ncser cleanup
authorKetmar Dark <[email protected]>
Tue, 9 Aug 2016 15:33:55 +0000 (9 18:33 +0300)
committerKetmar Dark <[email protected]>
Tue, 9 Aug 2016 15:33:55 +0000 (9 18:33 +0300)
ncserial.d

index 825ce47..d4f51e0 100644 (file)
@@ -39,40 +39,70 @@ enum NCEntryType : ubyte {
 
 // ////////////////////////////////////////////////////////////////////////// //
 public void ncser(T, ST) (auto ref ST fl, in ref T v) if (!is(T == class) && isWriteableStream!ST) {
+  import std.traits : Unqual;
+
   void writeTypeHeader(T) () {
-    static if (is(T == bool)) {
+    alias UT = Unqual!T;
+    static if (dimensionCount!UT > 0) {
+      static assert(dimensionCount!UT <= 255, "too many array dimenstions");
+      fl.writeNum!ubyte(NCEntryType.Array);
+      fl.writeNum!ubyte(cast(ubyte)dimensionCount!UT);
+      writeTypeHeader!(arrayElementType!UT);
+    } else static if (is(UT : K[V], K, V)) {
+      fl.writeNum!ubyte(NCEntryType.Dict);
+      writeTypeHeader!(Unqual!K);
+      writeTypeHeader!(Unqual!V);
+    } else static if (is(UT == bool)) {
       fl.writeNum!ubyte(NCEntryType.Bool);
-    } else static if (is(T == char) || is(T == wchar) || is(T == dchar)) {
-      fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Char|T.sizeof));
-    } else static if (__traits(isIntegral, T)) {
-      static if (__traits(isUnsigned, T)) {
-        fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Uint|T.sizeof));
+    } else static if (is(UT == char) || is(UT == wchar) || is(UT == dchar)) {
+      fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Char|UT.sizeof));
+    } else static if (__traits(isIntegral, UT)) {
+      static if (__traits(isUnsigned, UT)) {
+        fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Uint|UT.sizeof));
       } else {
-        fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Int|T.sizeof));
+        fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Int|UT.sizeof));
       }
-    } else static if (__traits(isFloating, T)) {
-      fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Float|T.sizeof));
-    } else static if (is(T == struct)) {
-      static assert(T.stringof.length <= 255, "struct name too long: "~T.stringof);
+    } else static if (__traits(isFloating, UT)) {
+      fl.writeNum!ubyte(cast(ubyte)(NCEntryType.Float|UT.sizeof));
+    } else static if (is(UT == struct)) {
+      static assert(UT.stringof.length <= 255, "struct name too long: "~UT.stringof);
       fl.writeNum!ubyte(NCEntryType.Struct);
-      fl.writeNum!ubyte(cast(ubyte)T.stringof.length);
-      fl.rawWriteExact(T.stringof[]);
+      fl.writeNum!ubyte(cast(ubyte)UT.stringof.length);
+      fl.rawWriteExact(UT.stringof[]);
     } else {
       static assert(0, "can't serialize type '"~T.stringof~"'");
     }
   }
 
-  void serSimple(T) (in ref T v) {
-    alias TP = arrayElementType!T;
-    static if (is(TP == bool)) {
+  void serData(T) (in ref T v) {
+    alias UT = arrayElementType!T;
+    static if (dimensionCount!T > 0) {
+      // array
+      void writeMArray(AT) (AT arr) {
+        fl.writeXInt(arr.length);
+        static if (dimensionCount!AT == 1) {
+          foreach (const ref it; arr) serData(it);
+        } else {
+          foreach (const a2; arr) writeMArray(a2);
+        }
+      }
+      writeMArray(v);
+    } else static if (is(T : V[K], K, V)) {
+      // associative array
+      fl.writeXInt(v.length);
+      foreach (const kv; v.byKeyValue) {
+        serData(kv.key);
+        serData(kv.value);
+      }
+    } else static if (is(UT == bool)) {
       fl.writeNum!ubyte(cast(ubyte)v);
-    } else static if (__traits(isIntegral, TP) || __traits(isFloating, TP)) {
-      fl.writeNum!TP(v);
-    } else static if (is(TP == struct)) {
+    } else static if (__traits(isIntegral, UT) || __traits(isFloating, UT)) {
+      fl.writeNum!UT(v);
+    } else static if (is(UT == struct)) {
       import std.traits : FieldNameTuple, hasUDA;
-      foreach (string fldname; FieldNameTuple!TP) {
-        static if (!hasUDA!(__traits(getMember, TP, fldname), NCIgnore)) {
-          static assert(fldname.length <= 255, "struct '"~TP.stringof~"': field name too long: "~fldname);
+      foreach (string fldname; FieldNameTuple!UT) {
+        static if (!hasUDA!(__traits(getMember, UT, fldname), NCIgnore)) {
+          static assert(fldname.length <= 255, "struct '"~UT.stringof~"': field name too long: "~fldname);
           fl.writeNum!ubyte(cast(ubyte)fldname.length);
           fl.rawWriteExact(fldname[]);
           fl.ncser(__traits(getMember, v, fldname));
@@ -84,71 +114,94 @@ public void ncser(T, ST) (auto ref ST fl, in ref T v) if (!is(T == class) && isW
     }
   }
 
-  enum dimc = dimensionCount!T;
-  alias TP = arrayElementType!T;
-  static if (dimc > 0) {
-    void writeMArray(AT) (AT arr) {
-      fl.writeXInt(arr.length);
-      static if (dimensionCount!AT == 1) {
-        foreach (const ref it; arr) serSimple(it);
-      } else {
-        foreach (const a2; arr) writeMArray(a2);
-      }
-    }
-    static assert(dimc <= 255, "too many array dimenstions");
-    fl.writeNum!ubyte(NCEntryType.Array);
-    fl.writeNum!ubyte(cast(ubyte)dimc);
-    writeTypeHeader!TP();
-    writeMArray(v);
-  } else {
-    writeTypeHeader!TP();
-    serSimple(v);
-  }
+  writeTypeHeader!T;
+  serData(v);
 }
 
 
 // ////////////////////////////////////////////////////////////////////////// //
 public void ncunser(T, ST) (auto ref ST fl, out T v) if (!is(T == class) && isReadableStream!ST) {
-  void checkTypeId(T) (ubyte bt) {
-    static if (is(T == bool)) {
-      if (bt != NCEntryType.Bool) throw new Exception(`invalid stream (bool expected)`);
-    } else static if (is(T == char) || is(T == wchar) || is(T == dchar)) {
-      if (bt != (NCEntryType.Char|T.sizeof)) throw new Exception(`invalid stream (char expected)`);
-    } else static if (__traits(isIntegral, T)) {
-      static if (__traits(isUnsigned, T)) {
-        if (bt != (NCEntryType.Uint|T.sizeof)) throw new Exception(`invalid stream (int expected)`);
+  import std.traits : Unqual;
+
+  void checkTypeId(T) () {
+    //alias UT = Unqual!T;
+    alias UT = T;
+    static if (dimensionCount!UT > 0) {
+      if (fl.readNum!ubyte != NCEntryType.Array) throw new Exception(`invalid stream (array expected)`);
+      if (fl.readNum!ubyte != dimensionCount!UT) throw new Exception(`invalid stream (dimension count)`);
+      checkTypeId!(arrayElementType!T);
+    } else static if (is(UT : K[V], K, V)) {
+      if (fl.readNum!ubyte != NCEntryType.Dict) throw new Exception(`invalid stream (dict expected)`);
+      checkTypeId!(Unqual!K);
+      checkTypeId!(Unqual!V);
+    } else static if (is(UT == bool)) {
+      if (fl.readNum!ubyte != NCEntryType.Bool) throw new Exception(`invalid stream (bool expected)`);
+    } else static if (is(UT == char) || is(UT == wchar) || is(UT == dchar)) {
+      if (fl.readNum!ubyte != (NCEntryType.Char|UT.sizeof)) throw new Exception(`invalid stream (char expected)`);
+    } else static if (__traits(isIntegral, UT)) {
+      static if (__traits(isUnsigned, UT)) {
+        if (fl.readNum!ubyte != (NCEntryType.Uint|UT.sizeof)) throw new Exception(`invalid stream (int expected)`);
       } else {
-        if (bt != (NCEntryType.Int|T.sizeof)) throw new Exception(`invalid stream (int expected)`);
+        if (fl.readNum!ubyte != (NCEntryType.Int|UT.sizeof)) throw new Exception(`invalid stream (int expected)`);
       }
-    } else static if (__traits(isFloating, T)) {
-      if (bt != (NCEntryType.Float|T.sizeof)) throw new Exception(`invalid stream (float expected)`);
-    } else static if (is(T == struct)) {
+    } else static if (__traits(isFloating, UT)) {
+      if (fl.readNum!ubyte != (NCEntryType.Float|UT.sizeof)) throw new Exception(`invalid stream (float expected)`);
+    } else static if (is(UT == struct)) {
       char[255] cbuf = void;
-      static assert(T.stringof.length <= 255, "struct name too long: "~T.stringof);
-      if (bt != NCEntryType.Struct) throw new Exception(`invalid stream (struct expected)`);
-      if (fl.readNum!ubyte != T.stringof.length) throw new Exception(`invalid stream (struct name length)`);
-      fl.rawReadExact(cbuf[0..T.stringof.length]);
-      if (cbuf[0..T.stringof.length] != T.stringof) throw new Exception(`invalid stream (struct name)`);
+      static assert(UT.stringof.length <= 255, "struct name too long: "~UT.stringof);
+      if (fl.readNum!ubyte != NCEntryType.Struct) throw new Exception(`invalid stream (struct expected)`);
+      if (fl.readNum!ubyte != UT.stringof.length) throw new Exception(`invalid stream (struct name length)`);
+      fl.rawReadExact(cbuf[0..UT.stringof.length]);
+      if (cbuf[0..UT.stringof.length] != UT.stringof) throw new Exception(`invalid stream (struct name)`);
     } else {
       static assert(0, "can't unserialize type '"~T.stringof~"'");
     }
   }
 
-  void unserSimple(T) (out T v) {
-    alias TP = arrayElementType!T;
-    static if (is(TP == bool)) {
+  void unserData(T) (out T v) {
+    static if (dimensionCount!T > 0) {
+      void readMArray(AT) (out AT arr) {
+        auto llen = fl.readXInt!usize;
+        if (llen == 0) return;
+        static if (__traits(isStaticArray, AT)) {
+          if (arr.length != llen) throw new Exception(`invalid stream (array size)`);
+          static if (dimensionCount!AT == 1) {
+            foreach (ref it; arr) unserData(it);
+          } else {
+            foreach (ref a2; arr) readMArray(a2);
+          }
+        } else {
+          static if (dimensionCount!AT == 1) {
+            auto narr = new arrayElementType!AT[](llen);
+            foreach (ref it; narr) unserData(it);
+            arr = cast(AT)narr;
+          } else {
+            foreach (ref a2; arr) readMArray(a2);
+          }
+        }
+      }
+      readMArray(v);
+    } else static if (is(T : V[K], K, V)) {
+      K key = void;
+      V value = void;
+      foreach (immutable _; 0..fl.readXInt!usize) {
+        unserData(key);
+        unserData(value);
+        v[key] = value;
+      }
+    } else static if (is(T == bool)) {
       v = fl.readNum!ubyte != 0;
-    } else static if (__traits(isIntegral, TP) || __traits(isFloating, TP)) {
-      v = fl.readNum!TP;
-    } else static if (is(TP == struct)) {
+    } else static if (__traits(isIntegral, T) || __traits(isFloating, T)) {
+      v = fl.readNum!T;
+    } else static if (is(T == struct)) {
       import std.traits : FieldNameTuple, hasUDA;
 
-      ulong[(FieldNameTuple!TP.length+ulong.sizeof-1)/ulong.sizeof] fldseen = 0;
+      ulong[(FieldNameTuple!T.length+ulong.sizeof-1)/ulong.sizeof] fldseen = 0;
 
       void tryField (const(char)[] name) {
         bool found = false;
-        foreach (immutable idx, string fldname; FieldNameTuple!TP) {
-          static if (!hasUDA!(__traits(getMember, TP, fldname), NCIgnore)) {
+        foreach (immutable idx, string fldname; FieldNameTuple!T) {
+          static if (!hasUDA!(__traits(getMember, T, fldname), NCIgnore)) {
             if (fldname == name) {
               if (fldseen[idx/8]&(1UL<<(idx%8))) throw new Exception(`duplicate field value for '`~fldname~`'`);
               fldseen[idx/8] |= 1UL<<(idx%8);
@@ -177,41 +230,8 @@ public void ncunser(T, ST) (auto ref ST fl, out T v) if (!is(T == class) && isRe
     }
   }
 
-  alias TP = arrayElementType!T;
-  enum dimc = dimensionCount!T;
-  ubyte tp = fl.readNum!ubyte;
-  static if (dimc > 0) {
-    void readMArray(AT) (out AT arr) {
-      auto llen = fl.readXInt!usize;
-      if (llen == 0) return;
-      static if (__traits(isStaticArray, AT)) {
-        if (arr.length != llen) throw new Exception(`invalid stream (array size)`);
-        static if (dimensionCount!AT == 1) {
-          foreach (ref it; arr) unserSimple(it);
-        } else {
-          foreach (ref a2; arr) readMArray(a2);
-        }
-      } else {
-        static if (dimensionCount!AT == 1) {
-          auto narr = new arrayElementType!AT[](llen);
-          foreach (ref it; narr) unserSimple(it);
-          arr = cast(AT)narr;
-        } else {
-          foreach (ref a2; arr) readMArray(a2);
-        }
-      }
-    }
-
-    if (tp != NCEntryType.Array) throw new Exception(`invalid stream (array expected)`);
-    auto dc = fl.readNum!ubyte;
-    if (dc != dimc) throw new Exception(`invalid stream (dimension count)`);
-    tp = fl.readNum!ubyte;
-    checkTypeId!TP(tp);
-    readMArray(v);
-  } else {
-    checkTypeId!TP(tp);
-    unserSimple(v);
-  }
+  checkTypeId!T;
+  unserData(v);
 }
 
 
@@ -231,6 +251,7 @@ template dimensionCount(T) {
   }
 }
 static assert(dimensionCount!string == 1);
+static assert(dimensionCount!(int[int]) == 0);
 
 
 template arrayElementType(T) {