@@ -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) {