From 67f7be04bb36adb459d46fa7d235133f785218b4 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 16 Nov 2018 13:40:48 -0600 Subject: [PATCH 1/8] All Current unittests pass --- src/xserial/attribute.d | 24 +++- src/xserial/serial.d | 254 ++++++++++++++++++---------------------- 2 files changed, 132 insertions(+), 146 deletions(-) diff --git a/src/xserial/attribute.d b/src/xserial/attribute.d index 808271c..7594e40 100644 --- a/src/xserial/attribute.d +++ b/src/xserial/attribute.d @@ -1,4 +1,4 @@ -module xserial.attribute; +module xserial.attribute; import std.system : Endian; import std.traits : isIntegral; @@ -14,19 +14,35 @@ enum Exclude; /** * Includes this even if it would otherwise be excluded. - * If Exclude (or other UDA(@)) and Include are present value will be included. + * If Exclude (or other UDA(@)) and Include are present the last one is used, except when excluded with `@EncodeOnly` or `@DecodeOnly`. * Can also be used on @property methods to include them. (Be sure both the setter and getter exist!) * If used on a value of a base class value will be included. */ - enum Include; +enum Include; + +/** + * Used for advanced addedUDAs + */ +template Excluder(UDA_) { + alias UDA = UDA_; +} + +/** + * Used for advanced addedUDAs + */ +template Includer(UDA_) { + alias UDA = UDA_; +} /** * Excludes the field from decoding, encode only. + * Field with be excluded in decoding regardles of any other UDA. */ enum EncodeOnly; /** * Excludes the field from encoding, decode only. + * Field with be excluded in encoding regardles of any other UDA. */ enum DecodeOnly; @@ -73,3 +89,5 @@ unittest { // for code coverage EndianLength!uint(Endian.bigEndian); } + + diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 1c6913e..25eb2f2 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -1,9 +1,10 @@ module xserial.serial; import std.system : Endian, endian; -import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs; +import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs, TemplateOf; import std.typecons : isTuple; import std.algorithm.searching : canFind; +import std.meta : AliasSeq; import xbuffer.buffer : canSwapEndianness, Buffer, BufferOverflowException; import xbuffer.memory : xalloc, xfree; @@ -14,91 +15,51 @@ import xserial.attribute; /** * Serializes some data. */ -ubyte[] serialize(Endian endianness, L, Endian lengthEndianness, T)(T value, Buffer buffer) { - serializeImpl!(endianness, L, lengthEndianness, T)(buffer, value); - return buffer.data!ubyte; +ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { + return Grouped!().serialize!(endianness, L, lengthEndianness)(value,buffer); } - -/// ditto -ubyte[] serialize(Endian endianness, L, Endian lengthEndianness, T)(T value) { - Buffer buffer = xalloc!Buffer(64); - scope(exit) xfree(buffer); - return serialize!(endianness, L, lengthEndianness, T)(value, buffer).dup; -} - -/// ditto -ubyte[] serialize(Endian endianness, L, T)(T value, Buffer buffer) { - return serialize!(endianness, L, endianness, T)(value, buffer); -} - -/// ditto -ubyte[] serialize(Endian endianness, L, T)(T value) { - return serialize!(endianness, L, endianness, T)(value); -} - /// ditto -ubyte[] serialize(Endian endianness, T)(T value, Buffer buffer) { - return serialize!(endianness, uint)(value, buffer); +ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value) { + return Grouped!().serialize!(endianness, L, lengthEndianness)(value); } - -/// ditto -ubyte[] serialize(Endian endianness, T)(T value) { - return serialize!(endianness, uint)(value); -} - -/// ditto -ubyte[] serialize(T)(T value, Buffer buffer) { - return serialize!(endian, uint, T)(value, buffer); -} - -/// ditto -ubyte[] serialize(T)(T value) { - return serialize!(endian, uint, T)(value); -} - /** * Deserializes some data. */ -T deserialize(T, Endian endianness, L, Endian lengthEndianness)(Buffer buffer) { - return deserializeImpl!(endianness, L, lengthEndianness, T)(buffer); +T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { + return Grouped!().deserialize!(T, endianness, L, lengthEndianness)(buffer); } - /// ditto -T deserialize(T, Endian endianness, L, Endian lengthEndianness)(in ubyte[] data) { - Buffer buffer = xalloc!Buffer(data); - scope(exit) xfree(buffer); - return deserialize!(T, endianness, L, lengthEndianness)(buffer); -} - -/// ditto -T deserialize(T, Endian endianness, L)(Buffer buffer) { - return deserialize!(T, endianness, L, endianness)(buffer); -} - -/// ditto -T deserialize(T, Endian endianness, L)(in ubyte[] data) { - return deserialize!(T, endianness, L, endianness)(data); -} - -/// ditto -T deserialize(T, Endian endianness)(Buffer buffer) { - return deserialize!(T, endianness, uint)(buffer); -} - -/// ditto -T deserialize(T, Endian endianness)(in ubyte[] data) { - return deserialize!(T, endianness, uint)(data); -} - -/// ditto -T deserialize(T)(Buffer buffer) { - return deserialize!(T, endian, uint)(buffer); +T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(in ubyte[] data) { + return Grouped!().deserialize!(T, endianness, L, lengthEndianness)(data); +} + template Grouped(AddedUDAs...) { + /** + * Serializes some data. + */ + ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { + serializeImpl!(endianness, L, lengthEndianness, T, AddedUDAs)(buffer, value); + return buffer.data!ubyte; + } + /// ditto + ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value) { + Buffer buffer = xalloc!Buffer(64); + scope(exit) xfree(buffer); + return Grouped!AddedUDAs.serialize!(endianness, L, lengthEndianness, T)(value, buffer).dup; + } + /** + * Deserializes some data. + */ + T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { + return deserializeImpl!(endianness, L, lengthEndianness, T, AddedUDAs)(buffer); + } + /// ditto + T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(in ubyte[] data) { + Buffer buffer = xalloc!Buffer(data); + scope(exit) xfree(buffer); + return deserialize!(T, endianness, L, lengthEndianness, AddedUDAs)(buffer); + } } -/// ditto -T deserialize(T)(in ubyte[] data) { - return deserialize!(T, endian, uint)(data); -} // ----------- // common data @@ -112,35 +73,42 @@ enum EndianType { } -template Members(T, alias Only) { +template Members(T, Only, AddedUDAs...) { + alias UDAs = AliasSeq!(AddedUDAs,Includer!Include,Excluder!Exclude); import std.typetuple : TypeTuple; mixin({ - string ret = "alias Members = TypeTuple!("; foreach(member ; __traits(allMembers, T)) { - static if(is(typeof(mixin("T." ~ member)))) { - mixin("alias M = typeof(T." ~ member ~ ");"); - static if( - isType!M && - !isCallable!M && - ( - hasUDA!(__traits(getMember, T, member), Include) || - ( - [__traits(derivedMembers, T)].canFind(member) && - !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members - !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) && // properties - !hasUDA!(__traits(getMember, T, member), Exclude) && - !hasUDA!(__traits(getMember, T, member), Only) - ) - ) - ){ - ret ~= `"` ~ member ~ `",`; - + static if(!hasUDA!(__traits(getMember, T, member), Only)) { + static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { + static foreach(A;UDAs) { + static if(is(Attribute==A.UDA)) { + static if (__traits(isSame,TemplateOf!A,Includer)) { + ret ~= `"` ~ member ~ `",`; + } + else static assert(__traits(isSame,TemplateOf!A,Excluder), "AddedUDA is not a template of Include or Exclude"); + enum done = true; + } + } + } + static if (!is(typeof(done))) { + static if(is(typeof(mixin("T." ~ member)))) { + mixin("alias M = typeof(T." ~ member ~ ");"); + static if( + isType!M && + !isCallable!M && + !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members + !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties + ) { + ret ~= `"` ~ member ~ `",`; + + } + } } } - } + } return ret ~ ");"; }()); @@ -151,25 +119,25 @@ template Members(T, alias Only) { // serialization // ------------- -void serializeImpl(Endian endianness, L, Endian lengthEndianness, T)(Buffer buffer, T value) { - static if(isVar!L) serializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer, value); - else serializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T)(buffer, value); +void serializeImpl(Endian endianness, L, Endian lengthEndianness, T, AddedUDAs...)(Buffer buffer, T value) { + static if(isVar!L) serializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T, AddedUDAs)(buffer, value); + else serializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T, AddedUDAs)(buffer, value); } -void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer, T value) { +void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T, AddedUDAs...)(Buffer buffer, T value) { static if(isArray!T) { static if(isDynamicArray!T) serializeLength!(cle, CL)(buffer, value.length); - serializeArray!(endianness, OL, ole)(buffer, value); + serializeArray!(endianness, OL, ole, AddedUDAs)(buffer, value); } else static if(isAssociativeArray!T) { serializeLength!(cle, CL)(buffer, value.length); - serializeAssociativeArray!(endianness, OL, ole)(buffer, value); + serializeAssociativeArray!(endianness, OL, ole, AddedUDAs)(buffer, value); } else static if(isTuple!T) { serializeTuple!(endianness, OL, ole)(buffer, value); } else static if(is(T == class) || is(T == struct) || is(T == interface)) { static if(__traits(hasMember, T, "serialize") && __traits(compiles, value.serialize(buffer))) { value.serialize(buffer); } else { - serializeMembers!(endianness, OL, ole)(buffer, value); + serializeMembers!(endianness, OL, ole, AddedUDAs)(buffer, value); } } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { serializeNumber!endianness(buffer, value); @@ -194,31 +162,31 @@ void serializeLength(EndianType endianness, L)(Buffer buffer, size_t length) { else serializeNumber!(endianness, L)(buffer, length); } -void serializeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isArray!T) { +void serializeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T array) if(isArray!T) { static if(canSwapEndianness!(ForeachType!T) && !is(ForeachType!T == struct) && !is(ForeachType!T == class) && endianness != EndianType.var) { buffer.write!(cast(Endian)endianness)(array); } else { foreach(value ; array) { - serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value); + serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, value); } } } -void serializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T array) if(isAssociativeArray!T) { +void serializeAssociativeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T array) if(isAssociativeArray!T) { foreach(key, value; array) { - serializeImpl!(endianness, OL, ole, OL, ole)(buffer, key); - serializeImpl!(endianness, OL, ole, OL, ole)(buffer, value); + serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, key); + serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, value); } } -void serializeTuple(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, T tuple) if(isTuple!T) { +void serializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T tuple) if(isTuple!T) { static foreach(i ; 0..tuple.fieldNames.length) { serializeImpl!(endianness, OL, ole, OL, ole)(buffer, tuple[i]); } } -void serializeMembers(EndianType endianness, L, EndianType le, T)(Buffer __buffer, T __container) { - foreach(member ; Members!(T, DecodeOnly)) { +void serializeMembers(EndianType endianness, L, EndianType le, T, AddedUDAs...)(Buffer __buffer, T __container) { + foreach(member ; Members!(T, DecodeOnly, AddedUDAs)) { mixin("alias M = typeof(__container." ~ member ~ ");"); @@ -239,11 +207,11 @@ void serializeMembers(EndianType endianness, L, EndianType le, T)(Buffer __buffe immutable e = "L, le, L, le"; } - static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; - else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M)(__buffer, __container." ~ member ~ ");"; + static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; + else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; @@ -257,27 +225,27 @@ void serializeMembers(EndianType endianness, L, EndianType le, T)(Buffer __buffe // deserialization // --------------- -T deserializeImpl(Endian endianness, L, Endian lengthEndianness, T)(Buffer buffer) { - static if(isVar!L) return deserializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T)(buffer); - else return deserializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T)(buffer); +T deserializeImpl(Endian endianness, L, Endian lengthEndianness, T, AddedUDAs...)(Buffer buffer) { + static if(isVar!L) return deserializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T, AddedUDAs)(buffer); + else return deserializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T, AddedUDAs)(buffer); } -T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T)(Buffer buffer) { +T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T, AddedUDAs...)(Buffer buffer) { static if(isStaticArray!T) { - return deserializeStaticArray!(endianness, OL, ole, T)(buffer); + return deserializeStaticArray!(endianness, OL, ole, T, AddedUDAs)(buffer); } else static if(isDynamicArray!T) { - return deserializeDynamicArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer)); + return deserializeDynamicArray!(endianness, OL, ole, T, AddedUDAs)(buffer, deserializeLength!(cle, CL)(buffer)); } else static if(isAssociativeArray!T) { - return deserializeAssociativeArray!(endianness, OL, ole, T)(buffer, deserializeLength!(cle, CL)(buffer)); + return deserializeAssociativeArray!(endianness, OL, ole, T, AddedUDAs)(buffer, deserializeLength!(cle, CL)(buffer)); } else static if(isTuple!T) { - return deserializeTuple!(endianness, OL, ole, T)(buffer); + return deserializeTuple!(endianness, OL, ole, T, AddedUDAs)(buffer); } else static if(is(T == class) || is(T == struct)) { T ret; static if(is(T == class)) ret = new T(); static if(__traits(hasMember, T, "deserialize") && __traits(compiles, ret.deserialize(buffer))) { ret.deserialize(buffer); } else { - deserializeMembers!(endianness, OL, ole)(buffer, &ret); + deserializeMembers!(endianness, OL, ole, AddedUDAs)(buffer, &ret); } return ret; } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { @@ -303,50 +271,50 @@ size_t deserializeLength(EndianType endianness, L)(Buffer buffer) { else return deserializeNumber!(endianness, L)(buffer); } -T deserializeStaticArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isStaticArray!T) { +T deserializeStaticArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isStaticArray!T) { T ret; foreach(ref value ; ret) { - value = deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); + value = deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); } return ret; } -T deserializeDynamicArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isDynamicArray!T) { +T deserializeDynamicArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, size_t length) if(isDynamicArray!T) { T ret; foreach(i ; 0..length) { - ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); + ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); } return ret; } -T deserializeAssociativeArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer, size_t length) if(isAssociativeArray!T) { +T deserializeAssociativeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, size_t length) if(isAssociativeArray!T) { T ret; foreach(i ; 0..length) { - ret[deserializeImpl!(endianness, OL, ole, OL, ole, KeyType!T)(buffer)] = deserializeImpl!(endianness, OL, ole, OL, ole, ValueType!T)(buffer); + ret[deserializeImpl!(endianness, OL, ole, OL, ole, KeyType!T, AddedUDAs)(buffer)] = deserializeImpl!(endianness, OL, ole, OL, ole, ValueType!T)(buffer); } return ret; } -T deserializeNoLengthArray(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isDynamicArray!T) { +T deserializeNoLengthArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isDynamicArray!T) { T ret; try { - while(true) ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T)(buffer); + while(true) ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); } catch(BufferOverflowException) {} return ret; } -T deserializeTuple(EndianType endianness, OL, EndianType ole, T)(Buffer buffer) if(isTuple!T) { +T deserializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isTuple!T) { T ret; foreach(i, U; T.Types) { - ret[i] = deserializeImpl!(endianness, OL, ole, OL, ole, U)(buffer); + ret[i] = deserializeImpl!(endianness, OL, ole, OL, ole, U, AddedUDAs)(buffer); } return ret; } -void deserializeMembers(EndianType endianness, L, EndianType le, C)(Buffer __buffer, C __container) { +void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs...)(Buffer __buffer, C __container) { static if(isPointer!C) alias T = typeof(*__container); else alias T = C; - foreach(member ; Members!(T, EncodeOnly)) { + foreach(member ; Members!(T, EncodeOnly, AddedUDAs)) { mixin("alias M = typeof(__container." ~ member ~ ");"); @@ -367,11 +335,11 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C)(Buffer __buf immutable e = "L, le, L, le"; } - static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M)(__buffer);"; - else immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M)(__buffer);"; + static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer);"; static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; From 1aad5d6b4e6f45f2563e6e84fb0e8e11888865f4 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 17 Nov 2018 12:01:58 -0600 Subject: [PATCH 2/8] New unittests and many fixes. --- src/xserial/attribute.d | 4 +- src/xserial/serial.d | 285 ++++++++++++++++++++++++++++++---------- 2 files changed, 216 insertions(+), 73 deletions(-) diff --git a/src/xserial/attribute.d b/src/xserial/attribute.d index 7594e40..4dde80c 100644 --- a/src/xserial/attribute.d +++ b/src/xserial/attribute.d @@ -23,14 +23,14 @@ enum Include; /** * Used for advanced addedUDAs */ -template Excluder(UDA_) { +template Excluder(alias UDA_) { alias UDA = UDA_; } /** * Used for advanced addedUDAs */ -template Includer(UDA_) { +template Includer(alias UDA_) { alias UDA = UDA_; } diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 25eb2f2..4a83513 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -56,7 +56,7 @@ T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endia T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(in ubyte[] data) { Buffer buffer = xalloc!Buffer(data); scope(exit) xfree(buffer); - return deserialize!(T, endianness, L, lengthEndianness, AddedUDAs)(buffer); + return Grouped!AddedUDAs.deserialize!(T, endianness, L, lengthEndianness)(buffer); } } @@ -73,46 +73,80 @@ enum EndianType { } -template Members(T, Only, AddedUDAs...) { +/** + * Copied and slightly modified from Phobos `std.traits`. (dlang.org/phobos/std_traits.html) + */ +private template isDesiredUDA(alias attribute, alias toCheck) +{ + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDA = toCheck == attribute; + else + enum isDesiredUDA = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDA = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, toCheck); + else + enum isDesiredUDA = is(toCheck == attribute); +} + +auto getSerializeMembers(T, Only, AddedUDAs...)() { alias UDAs = AliasSeq!(AddedUDAs,Includer!Include,Excluder!Exclude); - import std.typetuple : TypeTuple; + struct Member{ + string name ; + string condition =""; + } - mixin({ - string ret = "alias Members = TypeTuple!("; - foreach(member ; __traits(allMembers, T)) { - static if(!hasUDA!(__traits(getMember, T, member), Only)) { - static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { - static foreach(A;UDAs) { - static if(is(Attribute==A.UDA)) { - static if (__traits(isSame,TemplateOf!A,Includer)) { - ret ~= `"` ~ member ~ `",`; + Member[] members; + + foreach(member; __traits(allMembers, T)) { + string condition = ""; + static if(!hasUDA!(__traits(getMember, T, member), Only)) { + static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { + static if (!is(typeof(done))) { + static if(is(typeof(Attribute)==Condition)) { + members ~= Member(member, Attribute.condition); + enum done = true; + } + else static foreach(A;UDAs) { + static if (!is(typeof(done))) { + static if(isDesiredUDA!(Attribute,A.UDA)) { + static if (__traits(isSame,TemplateOf!A,Includer)) { + members ~= Member(member); + } + else static assert(__traits(isSame,TemplateOf!A,Excluder), "AddedUDA is not a template of Include or Exclude"); + enum done = true; } - else static assert(__traits(isSame,TemplateOf!A,Excluder), "AddedUDA is not a template of Include or Exclude"); - enum done = true; } } } - static if (!is(typeof(done))) { - static if(is(typeof(mixin("T." ~ member)))) { - mixin("alias M = typeof(T." ~ member ~ ");"); - static if( - isType!M && - !isCallable!M && - !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members - !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties - ) { - ret ~= `"` ~ member ~ `",`; - - } + } + static if (!is(typeof(done))) { + static if(is(typeof(mixin("T." ~ member)))) { + mixin("alias M = typeof(T." ~ member ~ ");"); + static if( + isType!M && + !isCallable!M && + !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members + !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties + ) { + members ~= Member(member); } } } - } - return ret ~ ");"; - - }()); + } + } + return members; } // ------------- @@ -137,7 +171,7 @@ void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle static if(__traits(hasMember, T, "serialize") && __traits(compiles, value.serialize(buffer))) { value.serialize(buffer); } else { - serializeMembers!(endianness, OL, ole, AddedUDAs)(buffer, value); + serializeMembers!(endianness, OL, ole, T, AddedUDAs)(buffer, value); } } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { serializeNumber!endianness(buffer, value); @@ -186,39 +220,44 @@ void serializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)( } void serializeMembers(EndianType endianness, L, EndianType le, T, AddedUDAs...)(Buffer __buffer, T __container) { - foreach(member ; Members!(T, DecodeOnly, AddedUDAs)) { + enum serMems = getSerializeMembers!(T, DecodeOnly, AddedUDAs); + static foreach(member; serMems) {{ - mixin("alias M = typeof(__container." ~ member ~ ");"); + mixin("alias M = typeof(__container." ~ member.name ~ ");"); - static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) { + static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member.name))) { static if(is(uda : Custom!C, C)) { enum __custom = true; - uda.C.serialize(mixin("__container." ~ member), __buffer); + uda.C.serialize(mixin("__container." ~ member.name), __buffer); } } - static if(!is(typeof(__custom))) mixin({ + static if(!is(typeof(__custom))) + mixin( + { - static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) { + static if(hasUDA!(__traits(getMember, T, member.name), LengthImpl)) { import std.conv : to; - auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0]; + auto length = getUDAs!(__traits(getMember, T, member.name), LengthImpl)[0]; immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); } else { immutable e = "L, le, L, le"; } - static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; - else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member ~ ");"; + static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member.name), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member.name), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; + else static if(hasUDA!(__traits(getMember, T, member.name), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; + else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; - else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; + if (member.condition.length==0) return ret; + else return "with(__container){if(" ~ member.condition ~ "){" ~ ret ~ "}}"; - }()); + } + () + ); - } + }} } // --------------- @@ -245,7 +284,7 @@ T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, static if(__traits(hasMember, T, "deserialize") && __traits(compiles, ret.deserialize(buffer))) { ret.deserialize(buffer); } else { - deserializeMembers!(endianness, OL, ole, AddedUDAs)(buffer, &ret); + deserializeMembers!(endianness, OL, ole, T*, AddedUDAs)(buffer, &ret); } return ret; } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { @@ -314,39 +353,41 @@ T deserializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(B void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs...)(Buffer __buffer, C __container) { static if(isPointer!C) alias T = typeof(*__container); else alias T = C; - foreach(member ; Members!(T, EncodeOnly, AddedUDAs)) { + + enum serMems = getSerializeMembers!(T, EncodeOnly, AddedUDAs); + static foreach(member; serMems) {{ - mixin("alias M = typeof(__container." ~ member ~ ");"); + mixin("alias M = typeof(__container." ~ member.name ~ ");"); - static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member))) { + static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member.name))) { static if(is(uda : Custom!C, C)) { enum __custom = true; - mixin("__container." ~ member) = uda.C.deserialize(__buffer); + mixin("__container." ~ member.name) = uda.C.deserialize(__buffer); } } static if(!is(typeof(__custom))) mixin({ - static if(hasUDA!(__traits(getMember, T, member), LengthImpl)) { - import std.conv : to; - auto length = getUDAs!(__traits(getMember, T, member), LengthImpl)[0]; - immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); - } else { - immutable e = "L, le, L, le"; - } - - static if(hasUDA!(__traits(getMember, T, member), NoLength)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), Var)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), BigEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member), LittleEndian)) immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else immutable ret = "__container." ~ member ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - - static if(!hasUDA!(__traits(getMember, T, member), Condition)) return ret; - else return "with(__container){if(" ~ getUDAs!(__traits(getMember, T, member), Condition)[0].condition ~ "){" ~ ret ~ "}}"; - - }()); + static if(hasUDA!(__traits(getMember, T, member.name), LengthImpl)) { + import std.conv : to; + auto length = getUDAs!(__traits(getMember, T, member.name), LengthImpl)[0]; + immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); + } else { + immutable e = "L, le, L, le"; + } + + static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member.name), Var)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member.name), BigEndian)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else static if(hasUDA!(__traits(getMember, T, member.name), LittleEndian)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + else immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer);"; + + if (member.condition.length==0) return ret; + else return "with(__container){if(" ~ member.condition ~ "){" ~ ret ~ "}}"; + + }()); - } + }} } // --------- @@ -522,6 +563,108 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... } +@("nested Includes Excludes") unittest { + + struct Test1 { + ubyte a; + + @Exclude { + @EncodeOnly @Include ubyte b; + + @Condition("a==1") { + ubyte c; + @Include ubyte d; + @Exclude ubyte e; + } + + @Include @Exclude ubyte f; + ubyte g; + @Include ubyte h; + } + } + { + Test1 test1 = Test1(1, 2, 3, 4, 5, 6, 7, 8); + assert(test1.serialize() == [1, 2, 3, 4, 8]); + assert(deserialize!Test1([2, 1, 4]) == Test1(2, 0, 0, 1, 0, 0, 0, 4)); + } + { + Test1 test1 = Test1(128, 2, 3, 4, 5, 6, 7, 8); + assert(test1.serialize() == [128, 2, 4, 8]); + } + +} + +@("includer & excluder groups") unittest { + + enum G; + enum N; + + struct Test1 { + @Exclude: + @G ubyte a; + + @Include @N ubyte b; + + @Condition("a==1") @G ubyte c; + @G { + ubyte d; + @N ubyte e; + } + } + { + Test1 test1 = Test1(1, 2, 3, 4, 5); + assert(test1.serialize() == [2, 3]); + assert(Grouped!(Includer!G).serialize(test1) == [1, 2, 3, 4, 5]); + assert(Grouped!(Includer!G, Excluder!N).serialize(test1) == [1, 3, 4]); + assert(Grouped!(Includer!N).serialize(test1) == [2, 3, 5]); + + assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); + assert(Grouped!(Includer!G).deserialize!Test1([1, 2, 3, 4, 5]) == Test1(1, 2, 3, 4, 5)); + assert(Grouped!(Includer!G, Excluder!N).deserialize!Test1([1, 3, 4]) == Test1(1, 0, 3, 4, 0)); + assert(Grouped!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + } + { + Test1 test1 = Test1(6, 2, 3, 4, 5); + assert(test1.serialize() == [2]); + assert(Grouped!(Includer!G).serialize(test1) == [6, 2, 3, 4, 5]); + assert(Grouped!(Includer!G, Excluder!N).serialize(test1) == [6, 3, 4]); + assert(Grouped!(Includer!N).serialize(test1) == [2, 5]); + + assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); + assert(Grouped!(Includer!G).deserialize!Test1([6, 2, 3, 4, 5]) == Test1(6, 2, 3, 4, 5)); + assert(Grouped!(Includer!G, Excluder!N).deserialize!Test1([6, 3, 4]) == Test1(6, 0, 3, 4, 0)); + assert(Grouped!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + } + +} + +@("struct groups") unittest { + + struct G { int id; } + + struct Test1 { + @Exclude: + @G ubyte a; + @G(0) ubyte b; + @G(1) @G(2) ubyte c; + } + + Test1 test1 = Test1(1, 2, 3); + assert(test1.serialize() == []); + assert(Grouped!(Includer!G).serialize(test1) == [1]); + assert(Grouped!(Includer!(G(0))).serialize(test1) == [1, 2]); + assert(Grouped!(Includer!(G(1))).serialize(test1) == [1, 3]); + assert(Grouped!(Includer!(G(0)),Includer!(G(1))).serialize(test1) == [1, 2, 3]); + assert(Grouped!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).serialize(test1) == [1, 2]); + + assert(Grouped!(Includer!G).deserialize!Test1([1]) == Test1(1, 0, 0)); + assert(Grouped!(Includer!(G(0))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + assert(Grouped!(Includer!(G(1))).deserialize!Test1([1, 3]) == Test1(1, 0, 3)); + assert(Grouped!(Includer!(G(0)),Includer!(G(1))).deserialize!Test1([1, 2, 3]) == Test1(1, 2, 3)); + assert(Grouped!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + +} + @("using buffer") unittest { Buffer buffer = new Buffer(64); From b62c480a98f7db2ba035b6fd5b9e647d71797c96 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 17 Nov 2018 13:13:28 -0600 Subject: [PATCH 3/8] Added unittest for code coverage. Because code coverage reports do not necessarily understand CTFE. --- src/xserial/serial.d | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 4a83513..ecb46bc 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -677,3 +677,22 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... assert(buffer.data.length == 0); } + +@("because the coverage report does not understand CTFE") unittest{ + enum G; + enum N; + struct Test1 { + ubyte z; + @Exclude: + @G ubyte a; + + @Include @N ubyte b; + + @Condition("a==1") ubyte c; + @G { + ubyte d; + @N ubyte e; + } + } + auto test1 = getSerializeMembers!(Test1, EncodeOnly, Includer!G, Excluder!N); +} From 80a4cce54da5a263fd104a436065c1cbad2b69ef Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 22 Nov 2018 22:46:24 -0600 Subject: [PATCH 4/8] Refactored code enormously. Renamed Grouped to Group. Refactored code to greatly reduce redundant code. Compiled code should be the same (if not better). Added a bunch of unittests to test functionality. Code is fully backwards compatibility assuming no direct access to internal code was used. --- src/xserial/attribute.d | 61 ++- src/xserial/serial.d | 937 +++++++++++++++++++++------------------- 2 files changed, 553 insertions(+), 445 deletions(-) diff --git a/src/xserial/attribute.d b/src/xserial/attribute.d index 4dde80c..1183059 100644 --- a/src/xserial/attribute.d +++ b/src/xserial/attribute.d @@ -39,7 +39,6 @@ template Includer(alias UDA_) { * Field with be excluded in decoding regardles of any other UDA. */ enum EncodeOnly; - /** * Excludes the field from encoding, decode only. * Field with be excluded in encoding regardles of any other UDA. @@ -59,35 +58,75 @@ struct Condition { string condition; } * Indicates the endianness for the type and its subtypes. */ enum BigEndian; - /// ditto enum LittleEndian; - /** * Encodes and decodes as a Google varint. */ enum Var; +/** + * Indicates the endianness for length for the type and its subtypes. + */ +alias BigEndianLength = Length!(EndianType.bigEndian); +/// ditto +alias LittleEndianLength = Length!(EndianType.littleEndian); +/** + * Encodes and decodes length as a Google varint. + */ +alias VarLength = Length!(EndianType.var); + /** * Indicates that the array has no length. It should only be used * as last field in the class/struct. */ enum NoLength; -struct LengthImpl { string type; int endianness; } -template Length(T) if(isIntegral!T) { enum Length = LengthImpl(T.stringof, -1); } +struct Length(T) if(isIntegral!T) { + alias Type = T; + EndianType endianness = cast(EndianType)-1; + this(Endian e) { + endianness = cast(EndianType)e; + } + this(EndianType e) { + endianness = cast(EndianType)e; + } +} -template Length(T) if(isVar!T) { enum Length = LengthImpl(T.Base.stringof, EndianType.var); } +struct Length(Endian e) { + enum endianness = cast(EndianType)e; +} +struct Length(T, Endian e) if(isIntegral!T) { + alias Type = T; + enum endianness = cast(EndianType)e; +} +struct Length(Endian e, T) if(isIntegral!T) { + alias Type = T; + enum endianness = cast(EndianType)e; +} + +struct Length(EndianType e) { + enum endianness = cast(EndianType)e; +} +struct Length(T, EndianType e) if(isIntegral!T) { + alias Type = T; + enum endianness = cast(EndianType)e; +} +struct Length(EndianType e, T) if(isIntegral!T) { + alias Type = T; + enum endianness = cast(EndianType)e; +} -LengthImpl EndianLength(T)(Endian endianness) if(isIntegral!T) { return LengthImpl(T.stringof, endianness); } +struct Length(T) if(isVar!T) { + alias Type = T.Base; + enum endianness = EndianType.var; +} -struct Custom(T) if(is(T == struct) || is(T == class) || is(T == interface)) { alias C = T; } +alias EndianLength = Length; -unittest { // for code coverage - EndianLength!uint(Endian.bigEndian); +struct Custom(T) if(is(T == struct) || is(T == class) || is(T == interface)) { alias C = T; } -} diff --git a/src/xserial/serial.d b/src/xserial/serial.d index ecb46bc..0431fa0 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -1,6 +1,6 @@ module xserial.serial; -import std.system : Endian, endian; +import std.system : Endian, sysEndian = endian; import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs, TemplateOf; import std.typecons : isTuple; import std.algorithm.searching : canFind; @@ -12,392 +12,463 @@ import xbuffer.varint : isVar; import xserial.attribute; -/** - * Serializes some data. - */ -ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { - return Grouped!().serialize!(endianness, L, lengthEndianness)(value,buffer); -} -/// ditto -ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value) { - return Grouped!().serialize!(endianness, L, lengthEndianness)(value); -} -/** - * Deserializes some data. - */ -T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { - return Grouped!().deserialize!(T, endianness, L, lengthEndianness)(buffer); -} -/// ditto -T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(in ubyte[] data) { - return Grouped!().deserialize!(T, endianness, L, lengthEndianness)(data); -} - template Grouped(AddedUDAs...) { - /** - * Serializes some data. - */ - ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { - serializeImpl!(endianness, L, lengthEndianness, T, AddedUDAs)(buffer, value); - return buffer.data!ubyte; - } - /// ditto - ubyte[] serialize(Endian endianness=endian, L=uint, Endian lengthEndianness=endianness, T)(T value) { - Buffer buffer = xalloc!Buffer(64); - scope(exit) xfree(buffer); - return Grouped!AddedUDAs.serialize!(endianness, L, lengthEndianness, T)(value, buffer).dup; - } - /** - * Deserializes some data. - */ - T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { - return deserializeImpl!(endianness, L, lengthEndianness, T, AddedUDAs)(buffer); - } - /// ditto - T deserialize(T, Endian endianness=endian, L=uint, Endian lengthEndianness=endianness)(in ubyte[] data) { - Buffer buffer = xalloc!Buffer(data); - scope(exit) xfree(buffer); - return Grouped!AddedUDAs.deserialize!(T, endianness, L, lengthEndianness)(buffer); - } -} - -// ----------- -// common data -// ----------- enum EndianType { - - bigEndian = cast(int)Endian.bigEndian, - littleEndian = cast(int)Endian.littleEndian, - var, - + bigEndian = cast(int)Endian.bigEndian , + littleEndian = cast(int)Endian.littleEndian , + var , +} +unittest { + assert(EndianType.bigEndian!=EndianType.var); + assert(EndianType.bigEndian!=cast(EndianType)-1); } + /** * Copied and slightly modified from Phobos `std.traits`. (dlang.org/phobos/std_traits.html) */ private template isDesiredUDA(alias attribute, alias toCheck) { - static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) - { - static if (__traits(compiles, toCheck == attribute)) - enum isDesiredUDA = toCheck == attribute; - else - enum isDesiredUDA = false; - } - else static if (is(typeof(toCheck))) - { - static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); - else - enum isDesiredUDA = is(typeof(toCheck) == attribute); - } - else static if (__traits(isTemplate, attribute)) - enum isDesiredUDA = isInstanceOf!(attribute, toCheck); - else - enum isDesiredUDA = is(toCheck == attribute); + /*added*/ import std.traits; + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDA = toCheck == attribute; + else + enum isDesiredUDA = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDA = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, toCheck); + else + enum isDesiredUDA = is(toCheck == attribute); } -auto getSerializeMembers(T, Only, AddedUDAs...)() { - alias UDAs = AliasSeq!(AddedUDAs,Includer!Include,Excluder!Exclude); - - struct Member{ - string name ; - string condition =""; - } - - Member[] members; - - foreach(member; __traits(allMembers, T)) { - string condition = ""; - static if(!hasUDA!(__traits(getMember, T, member), Only)) { - static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { - static if (!is(typeof(done))) { - static if(is(typeof(Attribute)==Condition)) { - members ~= Member(member, Attribute.condition); - enum done = true; - } - else static foreach(A;UDAs) { - static if (!is(typeof(done))) { - static if(isDesiredUDA!(Attribute,A.UDA)) { - static if (__traits(isSame,TemplateOf!A,Includer)) { - members ~= Member(member); + +private bool isTemplateOf(alias UDA, alias Template)() { + return is(UDA==Template!U,U...); +} + + +template Group(AddedUDAs...) { + private auto getSerializeMembers(T, Only)() { + alias UDAs = AliasSeq!(AddedUDAs,Includer!Include,Excluder!Exclude); + + struct Member{ + string name ; + string condition =""; + } + + Member[] members; + + foreach(member; __traits(allMembers, T)) { + string condition = ""; + static if(!hasUDA!(__traits(getMember, T, member), Only)) { + static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { + static if (!is(typeof(done))) { + static if(is(typeof(Attribute)==Condition)) { + members ~= Member(member, Attribute.condition); + enum done = true; + } + else static foreach(A;UDAs) { + static if (!is(typeof(done))) { + static if(isDesiredUDA!(Attribute,A.UDA)) { + static if (__traits(isSame,TemplateOf!A,Includer)) { + members ~= Member(member); + } + else static assert(__traits(isSame,TemplateOf!A,Excluder), "AddedUDA is not a template of Include or Exclude"); + enum done = true; } - else static assert(__traits(isSame,TemplateOf!A,Excluder), "AddedUDA is not a template of Include or Exclude"); - enum done = true; } } } } - } - static if (!is(typeof(done))) { - static if(is(typeof(mixin("T." ~ member)))) { - mixin("alias M = typeof(T." ~ member ~ ");"); - static if( - isType!M && - !isCallable!M && - !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members - !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties - ) { - members ~= Member(member); + static if (!is(typeof(done))) { + static if(is(typeof(mixin("T." ~ member)))) { + mixin("alias M = typeof(T." ~ member ~ ");"); + static if( + isType!M && + !isCallable!M && + !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members + !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties + ) { + members ~= Member(member); + } } } } } + + return members; } - return members; -} - -// ------------- -// serialization -// ------------- - -void serializeImpl(Endian endianness, L, Endian lengthEndianness, T, AddedUDAs...)(Buffer buffer, T value) { - static if(isVar!L) serializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T, AddedUDAs)(buffer, value); - else serializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T, AddedUDAs)(buffer, value); -} - -void serializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T, AddedUDAs...)(Buffer buffer, T value) { - static if(isArray!T) { - static if(isDynamicArray!T) serializeLength!(cle, CL)(buffer, value.length); - serializeArray!(endianness, OL, ole, AddedUDAs)(buffer, value); - } else static if(isAssociativeArray!T) { - serializeLength!(cle, CL)(buffer, value.length); - serializeAssociativeArray!(endianness, OL, ole, AddedUDAs)(buffer, value); - } else static if(isTuple!T) { - serializeTuple!(endianness, OL, ole)(buffer, value); - } else static if(is(T == class) || is(T == struct) || is(T == interface)) { - static if(__traits(hasMember, T, "serialize") && __traits(compiles, value.serialize(buffer))) { - value.serialize(buffer); - } else { - serializeMembers!(endianness, OL, ole, T, AddedUDAs)(buffer, value); + alias Deserializer = Serializer; + template Serializer(Endian endianness, L=uint, Endian lengthEndianness=endianness) { + alias Serializer = Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness); + } + template Serializer(EndianType endianness=cast(EndianType)sysEndian, L=uint, EndianType lengthEndianness=endianness) { + // ------------- + // serialization + // ------------- + + /// Serialize Number + void serialize(T)(T value, Buffer buffer) if(is(T:bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { + static if(endianness == EndianType.var) { + static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); + buffer.writeVar!T(value); + } + else static if(endianness == EndianType.bigEndian) { + buffer.write!(Endian.bigEndian, T)(value); + } + else static if(endianness == EndianType.littleEndian) { + buffer.write!(Endian.littleEndian, T)(value); + } } - } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { - serializeNumber!endianness(buffer, value); - } else { - static assert(0, "Cannot serialize " ~ T.stringof); - } -} - -void serializeNumber(EndianType endianness, T)(Buffer buffer, T value) { - static if(endianness == EndianType.var) { - static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); - buffer.writeVar!T(value); - } else static if(endianness == EndianType.bigEndian) { - buffer.write!(Endian.bigEndian, T)(value); - } else static if(endianness == EndianType.littleEndian) { - buffer.write!(Endian.littleEndian, T)(value); - } -} - -void serializeLength(EndianType endianness, L)(Buffer buffer, size_t length) { - static if(L.sizeof < size_t.sizeof) serializeNumber!(endianness, L)(buffer, cast(L)length); - else serializeNumber!(endianness, L)(buffer, length); -} - -void serializeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T array) if(isArray!T) { - static if(canSwapEndianness!(ForeachType!T) && !is(ForeachType!T == struct) && !is(ForeachType!T == class) && endianness != EndianType.var) { - buffer.write!(cast(Endian)endianness)(array); - } else { - foreach(value ; array) { - serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, value); + /// Serialize Static Array + void serialize(T)(T value, Buffer buffer) if(isStaticArray!T) { + serializeArrayDataImpl(value, buffer); } - } -} - -void serializeAssociativeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T array) if(isAssociativeArray!T) { - foreach(key, value; array) { - serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, key); - serializeImpl!(endianness, OL, ole, OL, ole, AddedUDAs)(buffer, value); - } -} - -void serializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, T tuple) if(isTuple!T) { - static foreach(i ; 0..tuple.fieldNames.length) { - serializeImpl!(endianness, OL, ole, OL, ole)(buffer, tuple[i]); - } -} - -void serializeMembers(EndianType endianness, L, EndianType le, T, AddedUDAs...)(Buffer __buffer, T __container) { - enum serMems = getSerializeMembers!(T, DecodeOnly, AddedUDAs); - static foreach(member; serMems) {{ + /// Serialize Dynamic Array && Associative Array + void serialize(bool serializeLength=true, T)(T value, Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { + static if (serializeLength) { + static if(L.sizeof < size_t.sizeof) + Serializer!(lengthEndianness, L, lengthEndianness).serialize(cast(L)value.length, buffer); + else Serializer!(lengthEndianness, L, lengthEndianness).serialize(value.length, buffer); + } + // Dynamic Array + static if (isDynamicArray!T) { + serializeArrayDataImpl(value, buffer); + } + // AssociativeArray + else static if (isAssociativeArray!T){ + foreach(key, v; value) { + serialize(key, buffer); + serialize(v, buffer); + } + } + } + /// Serialize normal array excluding length (used for dynamic and static arrays) + private void serializeArrayDataImpl(T)(T value, Buffer buffer) { + static assert(isArray!T); + static if( canSwapEndianness!(ForeachType!T) + && !is(ForeachType!T == struct) + && endianness != EndianType.var ) + { + static assert(!is(ForeachType!T == class ), "internal error; submit a bug report"); + static assert(!is(ForeachType!T == interface ), "internal error; submit a bug report"); + buffer.write!(cast(Endian)endianness)(value); + } + else { + foreach(v ; value) { + serialize(v, buffer); + } + } + } + /// Serialize Tuple + void serialize(T)(T value, Buffer buffer) if(isTuple!T) { + static foreach(i ; 0..value.fieldNames.length) { + serialize(value[i], buffer); + } + } + /// Serialize Members (Class || Struct || Interface) + void serialize(T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + static if(__traits(hasMember, T, "serialize") && __traits(compiles, value.serialize(buffer))) { + value.serialize(buffer); + return; + } + else { + enum serMems = getSerializeMembers!(T, DecodeOnly); + static foreach(member; serMems) {{ + bool conditionGood = member.condition.length==0; + static if (member.condition.length!=0) { + mixin("with(value){if("~member.condition~"){conditionGood=true;}}"); + } + if (conditionGood) { + mixin("alias M = typeof(value."~member.name~");"); + + //---Custom UDA + static if (hasUDA!(__traits(getMember, T, member.name), Custom)) { + alias Found = getUDAs!(__traits(getMember, T, member.name), Custom); + Found[Found.length-1].C.serialize(mixin("value."~member.name), buffer); + } + //---Normal + else { + //---get new attributes + static foreach_reverse (uda; __traits(getAttributes, mixin("value."~member.name))) { + static if (isTemplateOf!(uda,Length) || (__traits(compiles, typeof(uda)) && !(is(typeof(uda)==void)) && isTemplateOf!(typeof(uda),Length))) { + static if (__traits(compiles, uda.Type) && !is(typeof(NewLengthGiven))) { + enum NewLengthGiven = true; + alias NewLength = uda.Type; + } + static if (__traits(compiles, uda.endianness==-1) && uda.endianness!=-1 && !is(typeof(newLengthEndianness))) { + enum newLengthEndianness = uda.endianness; + } + } + } + static if (!is(typeof(NewLengthGiven))) { + alias NewLength = L; + } + static if (!is(typeof(newLengthEndianness))) { + enum newLengthEndianness = lengthEndianness; + } + + static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) { + enum noLength = true; + } + + static foreach_reverse (uda; __traits(getAttributes, __traits(getMember, T, member.name))) { + static if (isDesiredUDA!(uda, Var)) { + enum newEndianness = EndianType.var; + } + else static if (isDesiredUDA!(uda, BigEndian)) { + enum newEndianness = EndianType.bigEndian; + } + else static if (isDesiredUDA!(uda, LittleEndian)) { + enum newEndianness = EndianType.littleEndian; + } + } + static if (!is(typeof(newEndianness))) { + enum newEndianness = endianness; + } + + //--do + static if (is(typeof(noLength))) { + static assert(isDynamicArray!M || isAssociativeArray!M); + Serializer!(newEndianness, NewLength, newLengthEndianness).serialize!false(mixin("value."~member.name), buffer); + } + else { + Serializer!(newEndianness, NewLength, newLengthEndianness).serialize(mixin("value."~member.name), buffer); + } + } + } + }} + } + } + + /// Without Buffer + ubyte[] serialize(T)(T value) { + Buffer buffer = xalloc!Buffer(64); + scope(exit) xfree(buffer); + serialize(value, buffer); + return buffer.data!ubyte.dup; + } + /// ditto + ubyte[] serialize(bool serializeLength, T)(T value) if(isDynamicArray!T || isAssociativeArray!T) { + Buffer buffer = xalloc!Buffer(64); + scope(exit) xfree(buffer); + serialize!serializeLength(value, buffer); + return buffer.data!ubyte.dup; + } + - mixin("alias M = typeof(__container." ~ member.name ~ ");"); + // --------------- + // deserialization + // --------------- - static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member.name))) { - static if(is(uda : Custom!C, C)) { - enum __custom = true; - uda.C.serialize(mixin("__container." ~ member.name), __buffer); + /// Deserialize Number + T deserialize(T)(Buffer buffer) if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { + static if(endianness == EndianType.var) { + static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); + return buffer.readVar!T(); + } + else static if(endianness == EndianType.bigEndian) { + return buffer.read!(Endian.bigEndian, T)(); + } + else static if(endianness == EndianType.littleEndian) { + return buffer.read!(Endian.littleEndian, T)(); } } - - static if(!is(typeof(__custom))) - mixin( - { - - static if(hasUDA!(__traits(getMember, T, member.name), LengthImpl)) { - import std.conv : to; - auto length = getUDAs!(__traits(getMember, T, member.name), LengthImpl)[0]; - immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); - } else { - immutable e = "L, le, L, le"; + /// Deserialize Static Array + T deserialize(T)(Buffer buffer) if(isStaticArray!T) { + T ret; + foreach(i ; 0..ret.length) { + ret[i] = deserialize!(ForeachType!T)(buffer); } - - static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) immutable ret = "xserial.serial.serializeArray!(endianness, L, le, M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member.name), Var)) immutable ret = "xserial.serial.serializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member.name), BigEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - else static if(hasUDA!(__traits(getMember, T, member.name), LittleEndian)) immutable ret = "xserial.serial.serializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - else immutable ret = "xserial.serial.serializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer, __container." ~ member.name ~ ");"; - - if (member.condition.length==0) return ret; - else return "with(__container){if(" ~ member.condition ~ "){" ~ ret ~ "}}"; - + return ret; } - () - ); - - }} -} - -// --------------- -// deserialization -// --------------- - -T deserializeImpl(Endian endianness, L, Endian lengthEndianness, T, AddedUDAs...)(Buffer buffer) { - static if(isVar!L) return deserializeImpl!(cast(EndianType)endianness, L.Type, EndianType.var, L.Type, EndianType.var, T, AddedUDAs)(buffer); - else return deserializeImpl!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness, L, cast(EndianType)lengthEndianness, T, AddedUDAs)(buffer); -} - -T deserializeImpl(EndianType endianness, OL, EndianType ole, CL, EndianType cle, T, AddedUDAs...)(Buffer buffer) { - static if(isStaticArray!T) { - return deserializeStaticArray!(endianness, OL, ole, T, AddedUDAs)(buffer); - } else static if(isDynamicArray!T) { - return deserializeDynamicArray!(endianness, OL, ole, T, AddedUDAs)(buffer, deserializeLength!(cle, CL)(buffer)); - } else static if(isAssociativeArray!T) { - return deserializeAssociativeArray!(endianness, OL, ole, T, AddedUDAs)(buffer, deserializeLength!(cle, CL)(buffer)); - } else static if(isTuple!T) { - return deserializeTuple!(endianness, OL, ole, T, AddedUDAs)(buffer); - } else static if(is(T == class) || is(T == struct)) { - T ret; - static if(is(T == class)) ret = new T(); - static if(__traits(hasMember, T, "deserialize") && __traits(compiles, ret.deserialize(buffer))) { - ret.deserialize(buffer); - } else { - deserializeMembers!(endianness, OL, ole, T*, AddedUDAs)(buffer, &ret); + /// Deserialize Dynamic Array && Associative Array + T deserialize(T, bool serializeLength=true)(Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { + static if (serializeLength) { + auto length = Serializer!(lengthEndianness, L, lengthEndianness).deserialize!L(buffer); + T value; + foreach(_ ; 0..length) { + // Dynamic Array + static if (isDynamicArray!T) { + value ~= deserialize!(ForeachType!T)(buffer); + } + // AssociativeArray + else static if (isAssociativeArray!T){ + value[deserialize!(KeyType!T)(buffer)] = deserialize!(ValueType!T)(buffer); + } + } + return value; + } + else { + T value; + try { + while(true) { + // Dynamic Array + static if (isDynamicArray!T) { + value ~= deserialize!(ForeachType!T)(buffer); + } + // AssociativeArray + else static if (isAssociativeArray!T){ + value[deserialize!(KeyType!T)(buffer)] = deserialize!(ValueType!T)(buffer); + } + } + } catch(BufferOverflowException) {} + return value; + } + } + /// Deerialize Tuple + T deserialize(T)(Buffer buffer) if(isTuple!T) { + T value; + static foreach(i, U; T.Types) { + value[i] = deserialize!U(buffer); + } + return value; + } + /// Deserialize Members (Class || Struct || Interface) + T deserialize(T)(Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + T value; + static if(is(T == class)) + value = new T(); + else static assert(!is(T==interface), "No way currently to deserialize an interface. I plan to implement a way to by giving an instance to serialize to."); + static if(__traits(hasMember, T, "deserialize") && __traits(compiles, value.deserialize(buffer))) { + value.deserialize(buffer); + return value; + } + else { + enum serMems = getSerializeMembers!(T, EncodeOnly); + static foreach(member; serMems) {{ + bool conditionGood = member.condition.length==0; + static if (member.condition.length!=0) { + mixin("with(value){if("~member.condition~"){conditionGood=true;}}"); + } + if (conditionGood) { + mixin("alias M = typeof(value."~member.name~");"); + + //---Custom UDA + static if (hasUDA!(__traits(getMember, T, member.name), Custom)) { + alias Found = getUDAs!(__traits(getMember, T, member.name), Custom); + mixin("value."~member.name) = Found[Found.length-1].C.deserialize(buffer); + } + //---Normal + else { + //---get new attributes + static foreach_reverse (uda; __traits(getAttributes, mixin("value."~member.name))) { + static if (isTemplateOf!(uda,Length) || (__traits(compiles, typeof(uda)) && !(is(typeof(uda)==void)) && isTemplateOf!(typeof(uda),Length))) { + static if (__traits(compiles, uda.Type) && !is(typeof(NewLengthGiven))) { + enum NewLengthGiven = true; + alias NewLength = uda.Type; + } + static if (__traits(compiles, uda.endianness==-1) && uda.endianness!=-1 && !is(typeof(newLengthEndianness))) { + enum newLengthEndianness = uda.endianness; + } + } + } + static if (!is(typeof(NewLengthGiven))) { + alias NewLength = L; + } + static if (!is(typeof(newLengthEndianness))) { + enum newLengthEndianness = lengthEndianness; + } + + static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) { + enum noLength = true; + } + + static foreach_reverse (uda; __traits(getAttributes, __traits(getMember, T, member.name))) { + static if (isDesiredUDA!(uda, Var)) { + enum newEndianness = EndianType.var; + } + else static if (isDesiredUDA!(uda, BigEndian)) { + enum newEndianness = EndianType.bigEndian; + } + else static if (isDesiredUDA!(uda, LittleEndian)) { + enum newEndianness = EndianType.littleEndian; + } + } + static if (!is(typeof(newEndianness))) { + enum newEndianness = endianness; + } + + //--do + static if (is(typeof(noLength))) { + static assert(isDynamicArray!M || isAssociativeArray!M); + mixin("value."~member.name) = Serializer!(newEndianness, NewLength, newLengthEndianness).deserialize!(M,false)(buffer); + } + else { + mixin("value."~member.name) = Serializer!(newEndianness, NewLength, newLengthEndianness).deserialize!M(buffer); + } + } + } + }} + return value; + } + } + /// Without Buffer + T deserialize(T)(ubyte[] data) { + Buffer buffer = xalloc!Buffer(data); + scope(exit) xfree(buffer); + return deserialize!T(buffer); + } + /// ditto + T deserialize(T, bool serializeLength)(ubyte[] data) if(isDynamicArray!T || isAssociativeArray!T) { + Buffer buffer = xalloc!Buffer(data); + scope(exit) xfree(buffer); + return deserialize!(T, serializeLength)(buffer); } - return ret; - } else static if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { - return deserializeNumber!(endianness, T)(buffer); - } else { - static assert(0, "Cannot deserialize " ~ T.stringof); } -} - -T deserializeNumber(EndianType endianness, T)(Buffer buffer) { - static if(endianness == EndianType.var) { - static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); - return buffer.readVar!T(); - } else static if(endianness == EndianType.bigEndian) { - return buffer.read!(Endian.bigEndian, T)(); - } else static if(endianness == EndianType.littleEndian) { - return buffer.read!(Endian.littleEndian, T)(); + + alias serialize = Serializer!().serialize ; + alias deserialize = Serializer!().deserialize ; + void serialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, Buffer buffer) { + Serializer!(endianness, L, lengthEndianness).serialize(value, buffer); } -} - -size_t deserializeLength(EndianType endianness, L)(Buffer buffer) { - static if(L.sizeof > size_t.sizeof) return cast(size_t)deserializeNumber!(endianness, L)(buffer); - else return deserializeNumber!(endianness, L)(buffer); -} - -T deserializeStaticArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isStaticArray!T) { - T ret; - foreach(ref value ; ret) { - value = deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); + ubyte[] serialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value) { + return Serializer!(endianness, L, lengthEndianness).serialize(value); } - return ret; -} - -T deserializeDynamicArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, size_t length) if(isDynamicArray!T) { - T ret; - foreach(i ; 0..length) { - ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); + void serialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { + Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).serialize(value, buffer); } - return ret; -} - -T deserializeAssociativeArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer, size_t length) if(isAssociativeArray!T) { - T ret; - foreach(i ; 0..length) { - ret[deserializeImpl!(endianness, OL, ole, OL, ole, KeyType!T, AddedUDAs)(buffer)] = deserializeImpl!(endianness, OL, ole, OL, ole, ValueType!T)(buffer); + ubyte[] serialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value) { + return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).serialize(value); + } + + T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(Buffer buffer) { + return Serializer!(endianness, L, lengthEndianness).deserialize!T(buffer); + } + T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(ubyte[] data) { + return Serializer!(endianness, L, lengthEndianness).deserialize!T(data); + } + T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { + return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(buffer); + } + T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(ubyte[] data) { + return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(data); } - return ret; } -T deserializeNoLengthArray(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isDynamicArray!T) { - T ret; - try { - while(true) ret ~= deserializeImpl!(endianness, OL, ole, OL, ole, ForeachType!T, AddedUDAs)(buffer); - } catch(BufferOverflowException) {} - return ret; -} +alias serialize = Group!().serialize ; +alias deserialize = Group!().deserialize ; +alias Serializer = Group!().Serializer ; +alias Deserializer = Group!().Deserializer ; -T deserializeTuple(EndianType endianness, OL, EndianType ole, T, AddedUDAs...)(Buffer buffer) if(isTuple!T) { - T ret; - foreach(i, U; T.Types) { - ret[i] = deserializeImpl!(endianness, OL, ole, OL, ole, U, AddedUDAs)(buffer); - } - return ret; -} -void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs...)(Buffer __buffer, C __container) { - static if(isPointer!C) alias T = typeof(*__container); - else alias T = C; - - enum serMems = getSerializeMembers!(T, EncodeOnly, AddedUDAs); - static foreach(member; serMems) {{ - - mixin("alias M = typeof(__container." ~ member.name ~ ");"); - - static foreach(uda ; __traits(getAttributes, __traits(getMember, T, member.name))) { - static if(is(uda : Custom!C, C)) { - enum __custom = true; - mixin("__container." ~ member.name) = uda.C.deserialize(__buffer); - } - } - - static if(!is(typeof(__custom))) mixin({ - - static if(hasUDA!(__traits(getMember, T, member.name), LengthImpl)) { - import std.conv : to; - auto length = getUDAs!(__traits(getMember, T, member.name), LengthImpl)[0]; - immutable e = "L, le, " ~ length.type ~ ", " ~ (length.endianness == -1 ? "endianness" : "EndianType." ~ (cast(EndianType)length.endianness).to!string); - } else { - immutable e = "L, le, L, le"; - } - - static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeNoLengthArray!(endianness, L, le, M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member.name), Var)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.var, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member.name), BigEndian)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.bigEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else static if(hasUDA!(__traits(getMember, T, member.name), LittleEndian)) immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(EndianType.littleEndian, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - else immutable ret = "__container." ~ member.name ~ "=xserial.serial.deserializeImpl!(endianness, " ~ e ~ ", M, AddedUDAs)(__buffer);"; - - if (member.condition.length==0) return ret; - else return "with(__container){if(" ~ member.condition ~ "){" ~ ret ~ "}}"; - - }()); - - }} -} // --------- // unittests // --------- @("numbers") unittest { - - // bools and numbers - assert(true.serialize() == [1]); assert(5.serialize!(Endian.bigEndian)() == [0, 0, 0, 5]); @@ -405,11 +476,9 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... version(LittleEndian) assert(12.serialize() == [12, 0, 0, 0]); version(BigEndian) assert(12.serialize() == [0, 0, 0, 12]); - } @("arrays") unittest { - assert([1, 2, 3].serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3]); assert([1, 2, 3].serialize().deserialize!(int[])() == [1, 2, 3]); @@ -417,146 +486,106 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... assert(test1.serialize!(Endian.bigEndian)() == [0, 1, 0, 2]); test1 = deserialize!(ushort[2], Endian.littleEndian)([2, 0, 1, 0]); assert(test1 == [2, 1]); - } @("associative arrays") unittest { - - // associative arrays - int[ushort] test; test[1] = 112; assert(test.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 1, 0, 1, 0, 0, 0, 112]); - + test = deserialize!(int[ushort], Endian.bigEndian, ubyte)([1, 0, 0, 0, 0, 0, 55]); assert(test == [ushort(0): 55]); - } @("tuples") unittest { - import std.typecons : Tuple, tuple; - + assert(tuple(1, "test").serialize!(Endian.bigEndian, ushort)() == [0, 0, 0, 1, 0, 4, 't', 'e', 's', 't']); - + Tuple!(ubyte, "a", uint[], "b") test; test.a = 12; assert(test.serialize!(Endian.littleEndian, uint)() == [12, 0, 0, 0, 0]); assert(deserialize!(typeof(test), Endian.bigEndian, ushort)([12, 0, 0]) == test); - } @("structs and classes") unittest { - struct Test1 { - byte a, b, c; - } - + Test1 test1 = Test1(1, 3, 55); assert(test1.serialize() == [1, 3, 55]); - assert(deserialize!Test1([1, 3, 55]) == test1); - + static struct Test2 { - int a; - void serialize(Buffer buffer) { buffer.write!(Endian.bigEndian)(this.a + 1); } - void deserialize(Buffer buffer) { this.a = buffer.read!(Endian.bigEndian, int)() - 1; } - } - + assert(serialize(Test2(5)) == [0, 0, 0, 6]); assert(deserialize!Test2([0, 0, 0, 6]) == Test2(5)); - + static class Test3 { - ubyte a; - void serialize() {} - void deserialize() {} - } - + Test3 test3 = new Test3(); assert(serialize(test3) == [0]); assert(deserialize!Test3([5]).a == 5); - } @("attributes") unittest { - struct Test1 { - @BigEndian int a; - @EncodeOnly @LittleEndian ushort b; - @Condition("a==1") @Var uint c; - @DecodeOnly @Var uint d; - @Exclude ubyte e; - } - + Test1 test1 = Test1(1, 2, 3, 4, 5); assert(test1.serialize() == [0, 0, 0, 1, 2, 0, 3]); assert(deserialize!Test1([0, 0, 0, 1, 4, 12]) == Test1(1, 0, 4, 12)); - + test1.a = 0; assert(test1.serialize() == [0, 0, 0, 0, 2, 0]); assert(deserialize!Test1([0, 0, 0, 0, 0, 0, 0, 0]) == Test1(0, 0, 0, 0)); - + struct Test2 { - ubyte[] a; - - @Length!ushort ushort[] b; - + @Length @Length!ushort ushort[] b; @NoLength uint[] c; - } - + Test2 test2 = Test2([1, 2], [3, 4], [5, 6]); assert(test2.serialize!(Endian.bigEndian, uint)() == [0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6]); - assert(deserialize!(Test2, Endian.bigEndian, uint)([0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6, 1]) == test2); - + assert(deserialize!(Test2, Endian.bigEndian, uint)([0, 0, 0, 2, 1, 2, 0, 2, 0, 3, 0, 4, 0, 0, 0, 5, 0, 0, 0, 6]) == test2); + struct Test3 { - @EndianLength!ushort(Endian.littleEndian) @LittleEndian ushort[] a; - @NoLength ushort[] b; - } - + Test3 test3 = Test3([1, 2], [3, 4]); assert(test3.serialize!(Endian.bigEndian)() == [2, 0, 1, 0, 2, 0, 0, 3, 0, 4]); - + struct Test4 { - ubyte a; - @LittleEndian uint b; - } - + struct Test5 { - @Length!ubyte Test4[] a; - @NoLength Test4[] b; - } - + Test5 test5 = Test5([Test4(1, 2)], [Test4(1, 2), Test4(3, 4)]); assert(test5.serialize() == [1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]); assert(deserialize!Test5([1, 1, 2, 0, 0, 0, 1, 2, 0, 0, 0, 3, 4, 0, 0, 0]) == test5); @@ -564,7 +593,6 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... } @("nested Includes Excludes") unittest { - struct Test1 { ubyte a; @@ -591,11 +619,9 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... Test1 test1 = Test1(128, 2, 3, 4, 5, 6, 7, 8); assert(test1.serialize() == [128, 2, 4, 8]); } - } @("includer & excluder groups") unittest { - enum G; enum N; @@ -614,32 +640,30 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... { Test1 test1 = Test1(1, 2, 3, 4, 5); assert(test1.serialize() == [2, 3]); - assert(Grouped!(Includer!G).serialize(test1) == [1, 2, 3, 4, 5]); - assert(Grouped!(Includer!G, Excluder!N).serialize(test1) == [1, 3, 4]); - assert(Grouped!(Includer!N).serialize(test1) == [2, 3, 5]); + assert(Group!(Includer!G).serialize(test1) == [1, 2, 3, 4, 5]); + assert(Group!(Includer!G, Excluder!N).serialize(test1) == [1, 3, 4]); + assert(Group!(Includer!N).serialize(test1) == [2, 3, 5]); assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); - assert(Grouped!(Includer!G).deserialize!Test1([1, 2, 3, 4, 5]) == Test1(1, 2, 3, 4, 5)); - assert(Grouped!(Includer!G, Excluder!N).deserialize!Test1([1, 3, 4]) == Test1(1, 0, 3, 4, 0)); - assert(Grouped!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + assert(Group!(Includer!G).deserialize!Test1([1, 2, 3, 4, 5]) == Test1(1, 2, 3, 4, 5)); + assert(Group!(Includer!G, Excluder!N).deserialize!Test1([1, 3, 4]) == Test1(1, 0, 3, 4, 0)); + assert(Group!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); } { Test1 test1 = Test1(6, 2, 3, 4, 5); assert(test1.serialize() == [2]); - assert(Grouped!(Includer!G).serialize(test1) == [6, 2, 3, 4, 5]); - assert(Grouped!(Includer!G, Excluder!N).serialize(test1) == [6, 3, 4]); - assert(Grouped!(Includer!N).serialize(test1) == [2, 5]); + assert(Group!(Includer!G).serialize(test1) == [6, 2, 3, 4, 5]); + assert(Group!(Includer!G, Excluder!N).serialize(test1) == [6, 3, 4]); + assert(Group!(Includer!N).serialize(test1) == [2, 5]); assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); - assert(Grouped!(Includer!G).deserialize!Test1([6, 2, 3, 4, 5]) == Test1(6, 2, 3, 4, 5)); - assert(Grouped!(Includer!G, Excluder!N).deserialize!Test1([6, 3, 4]) == Test1(6, 0, 3, 4, 0)); - assert(Grouped!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + assert(Group!(Includer!G).deserialize!Test1([6, 2, 3, 4, 5]) == Test1(6, 2, 3, 4, 5)); + assert(Group!(Includer!G, Excluder!N).deserialize!Test1([6, 3, 4]) == Test1(6, 0, 3, 4, 0)); + assert(Group!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); } - } @("struct groups") unittest { - struct G { int id; } struct Test1 { @@ -651,34 +675,79 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... Test1 test1 = Test1(1, 2, 3); assert(test1.serialize() == []); - assert(Grouped!(Includer!G).serialize(test1) == [1]); - assert(Grouped!(Includer!(G(0))).serialize(test1) == [1, 2]); - assert(Grouped!(Includer!(G(1))).serialize(test1) == [1, 3]); - assert(Grouped!(Includer!(G(0)),Includer!(G(1))).serialize(test1) == [1, 2, 3]); - assert(Grouped!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).serialize(test1) == [1, 2]); - - assert(Grouped!(Includer!G).deserialize!Test1([1]) == Test1(1, 0, 0)); - assert(Grouped!(Includer!(G(0))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); - assert(Grouped!(Includer!(G(1))).deserialize!Test1([1, 3]) == Test1(1, 0, 3)); - assert(Grouped!(Includer!(G(0)),Includer!(G(1))).deserialize!Test1([1, 2, 3]) == Test1(1, 2, 3)); - assert(Grouped!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + assert(Group!(Includer!G).serialize(test1) == [1]); + assert(Group!(Includer!(G(0))).serialize(test1) == [1, 2]); + assert(Group!(Includer!(G(1))).serialize(test1) == [1, 3]); + assert(Group!(Includer!(G(0)),Includer!(G(1))).serialize(test1) == [1, 2, 3]); + assert(Group!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).serialize(test1) == [1, 2]); + assert(Group!(Includer!G).deserialize!Test1([1]) == Test1(1, 0, 0)); + assert(Group!(Includer!(G(0))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + assert(Group!(Includer!(G(1))).deserialize!Test1([1, 3]) == Test1(1, 0, 3)); + assert(Group!(Includer!(G(0)),Includer!(G(1))).deserialize!Test1([1, 2, 3]) == Test1(1, 2, 3)); + assert(Group!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); } @("using buffer") unittest { - Buffer buffer = new Buffer(64); - + serialize(ubyte(55), buffer); assert(buffer.data.length == 1); assert(buffer.data!ubyte == [55]); - + assert(deserialize!ubyte(buffer) == 55); assert(buffer.data.length == 0); +} + +@("custom attrubute") unittest { + struct CusNo { + static void serialize(uint value, Buffer buffer) { + } + static uint deserialize(Buffer buffer) { + return 0; + } + } + struct Cus { + static void serialize(uint value, Buffer buffer) { + buffer.write((value+1).serialize!(Endian.bigEndian)); + } + static uint deserialize(Buffer buffer) { + return buffer.read!(Endian.bigEndian, int)-1; + } + } + struct Test { + @Custom!CusNo @Custom!Cus uint a; + } + + Test test = Test(5); + assert(test.serialize() == [0,0,0,6]); + assert(deserialize!Test([0,0,0,6]) == test); +} +@("override length uda") unittest { + { + struct Test { + @Length!ulong(EndianType.var) @Length!(ushort, EndianType.bigEndian) ubyte[] a; + } + + Test test = Test([1,2]); + assert(test.serialize!(EndianType.bigEndian)==[0,2,1,2]); + assert([0,2,1,2].deserialize!(Test,EndianType.bigEndian)==test); + } + { + struct Test2 { + @Length!ulong(EndianType.var) @Length!(uint) @EndianLength!ushort @LittleEndianLength ubyte[] a; + } + + Test2 test = Test2([1,2]); + import std.stdio; + assert(test.serialize!(EndianType.bigEndian)==[2,0,1,2]); + assert([2,0,1,2].deserialize!(Test2,EndianType.bigEndian)==test); + } } -@("because the coverage report does not understand CTFE") unittest{ +// for code coverage: because the coverage report does not understand CTFE +unittest{ enum G; enum N; struct Test1 { @@ -694,5 +763,5 @@ void deserializeMembers(EndianType endianness, L, EndianType le, C, AddedUDAs... @N ubyte e; } } - auto test1 = getSerializeMembers!(Test1, EncodeOnly, Includer!G, Excluder!N); + auto test1 = Group!(Includer!G, Excluder!N).getSerializeMembers!(Test1, EncodeOnly); } From d300ba6cfcc9ca1708002158ee23c2c20c404627 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 22 Nov 2018 23:46:18 -0600 Subject: [PATCH 5/8] Fix bug with serializing classes. --- src/xserial/serial.d | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 0431fa0..6089998 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -70,7 +70,7 @@ template Group(AddedUDAs...) { foreach(member; __traits(allMembers, T)) { string condition = ""; - static if(!hasUDA!(__traits(getMember, T, member), Only)) { + static if(is(typeof(mixin("T." ~ member))) && !hasUDA!(__traits(getMember, T, member), Only)) { static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { static if (!is(typeof(done))) { static if(is(typeof(Attribute)==Condition)) { @@ -91,16 +91,14 @@ template Group(AddedUDAs...) { } } static if (!is(typeof(done))) { - static if(is(typeof(mixin("T." ~ member)))) { - mixin("alias M = typeof(T." ~ member ~ ");"); - static if( - isType!M && - !isCallable!M && - !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members - !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties - ) { - members ~= Member(member); - } + mixin("alias M = typeof(T." ~ member ~ ");"); + static if( + isType!M && + !isCallable!M && + !__traits(compiles, { mixin("auto test=T." ~ member ~ ";"); }) && // static members + !__traits(compiles, { mixin("auto test=T.init." ~ member ~ "();"); }) // properties + ) { + members ~= Member(member); } } } @@ -327,7 +325,7 @@ template Group(AddedUDAs...) { return value; } } - /// Deerialize Tuple + /// Deserialize Tuple T deserialize(T)(Buffer buffer) if(isTuple!T) { T value; static foreach(i, U; T.Types) { @@ -412,7 +410,7 @@ template Group(AddedUDAs...) { } }} return value; - } + } } /// Without Buffer T deserialize(T)(ubyte[] data) { From b64c566ea00cf3f91f4735d4fd347e55a9f0bc70 Mon Sep 17 00:00:00 2001 From: Unknown Date: Thu, 22 Nov 2018 23:49:05 -0600 Subject: [PATCH 6/8] Implemented deserialize to. You can now deserialize data to a given instance with classes, structs, and interfaces. --- src/xserial/serial.d | 71 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 6089998..6252464 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -338,7 +338,11 @@ template Group(AddedUDAs...) { T value; static if(is(T == class)) value = new T(); - else static assert(!is(T==interface), "No way currently to deserialize an interface. I plan to implement a way to by giving an instance to serialize to."); + else static assert(!is(T==interface), "To deserialize an interface you must provide an instance to deserialize to. `deserialize(instance,data)`"); + return deserialize(value, buffer); + } + /// ditto + T deserialize(T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { static if(__traits(hasMember, T, "deserialize") && __traits(compiles, value.deserialize(buffer))) { value.deserialize(buffer); return value; @@ -424,6 +428,12 @@ template Group(AddedUDAs...) { scope(exit) xfree(buffer); return deserialize!(T, serializeLength)(buffer); } + /// ditto + T deserialize(T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + Buffer buffer = xalloc!Buffer(data); + scope(exit) xfree(buffer); + return deserialize(value, buffer); + } } alias serialize = Serializer!().serialize ; @@ -453,6 +463,19 @@ template Group(AddedUDAs...) { T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(ubyte[] data) { return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(data); } + + T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(endianness, L, lengthEndianness).deserialize(value, buffer); + } + T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(endianness, L, lengthEndianness).deserialize(value, data); + } + T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, buffer); + } + T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, data); + } } alias serialize = Group!().serialize ; @@ -744,6 +767,52 @@ alias Deserializer = Group!().Deserializer ; } } +@("interface and deserialize to instance") unittest { + interface ITest { + @LittleEndianLength @Include @property { + ubyte[] aa(); + void aa(ubyte[]); + } + @property { + ubyte cc(); + } + } + class Test : ITest { + @Length!ubyte ushort[] a; + @Exclude ubyte b; + ubyte c; + + this(ushort[] a, ubyte b, ubyte c) { + this.a = a; + this.b = b; + this.c = c; + } + + @property { + import std.algorithm, std.array; + ubyte[] aa() { + return a.map!((v){return cast(ubyte)v;}).array; + } + void aa(ubyte[] n) { + a = n.map!((v){return cast(ushort)v;}).array; + } + ubyte cc() { + return c; + } + } + } + + Test test = new Test([1,2], 3, 4); + test.deserialize!(EndianType.bigEndian)([5,0,1,0,2,0,3,0,4,0,5,6]); + assert(test.a==[1,2,3,4,5] && test.b==3 && test.c==6); + + ITest iTest = test; + iTest.deserialize!(EndianType.bigEndian)([2,0,0,0,1,2]); + assert(test.a==[1,2] && test.b==3 && test.c==6); + + assert(iTest.serialize!(Endian.bigEndian) == [2,0,0,0,1,2]); +} + // for code coverage: because the coverage report does not understand CTFE unittest{ enum G; From 40c4fc7c19d0b455df6a2173c9ab494a3d477bba Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 23 Nov 2018 22:03:38 -0600 Subject: [PATCH 7/8] Deserialize can now take immutable ubyte arrays. Also added unittest for such. --- src/xserial/serial.d | 59 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/src/xserial/serial.d b/src/xserial/serial.d index 6252464..ac9855b 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -417,19 +417,19 @@ template Group(AddedUDAs...) { } } /// Without Buffer - T deserialize(T)(ubyte[] data) { + T deserialize(T)(const(ubyte)[] data) { Buffer buffer = xalloc!Buffer(data); scope(exit) xfree(buffer); return deserialize!T(buffer); } /// ditto - T deserialize(T, bool serializeLength)(ubyte[] data) if(isDynamicArray!T || isAssociativeArray!T) { + T deserialize(T, bool serializeLength)(const(ubyte)[] data) if(isDynamicArray!T || isAssociativeArray!T) { Buffer buffer = xalloc!Buffer(data); scope(exit) xfree(buffer); return deserialize!(T, serializeLength)(buffer); } /// ditto - T deserialize(T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + T deserialize(T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { Buffer buffer = xalloc!Buffer(data); scope(exit) xfree(buffer); return deserialize(value, buffer); @@ -454,26 +454,26 @@ template Group(AddedUDAs...) { T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(Buffer buffer) { return Serializer!(endianness, L, lengthEndianness).deserialize!T(buffer); } - T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(ubyte[] data) { + T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(const(ubyte)[] data) { return Serializer!(endianness, L, lengthEndianness).deserialize!T(data); } T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(buffer); } - T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(ubyte[] data) { + T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(const(ubyte)[] data) { return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(data); } T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { return Serializer!(endianness, L, lengthEndianness).deserialize(value, buffer); } - T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { return Serializer!(endianness, L, lengthEndianness).deserialize(value, data); } T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, buffer); } - T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, ubyte[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, data); } } @@ -813,6 +813,51 @@ alias Deserializer = Group!().Deserializer ; assert(iTest.serialize!(Endian.bigEndian) == [2,0,0,0,1,2]); } +@("immutability") unittest { + { + ubyte[] test = [1,2,3,4]; + assert(test.serialize!(Endian.littleEndian, ubyte)==[4,1,2,3,4]); + assert(test.deserialize!(ubyte[4]) == [1,2,3,4]); + } + { + const(ubyte)[] test = [1,2,3,4]; + assert(test.serialize!(Endian.littleEndian, ubyte)==[4,1,2,3,4]); + assert(test.deserialize!(ubyte[4]) == [1,2,3,4]); + } + { + immutable(ubyte)[] test = [1,2,3,4]; + assert(test.serialize!(Endian.littleEndian, ubyte)==[4,1,2,3,4]); + assert(test.deserialize!(ubyte[4]) == [1,2,3,4]); + } + { + const(ubyte[]) test = [1,2,3,4]; + assert(test.serialize!(Endian.littleEndian, ubyte)==[4,1,2,3,4]); + assert(test.deserialize!(ubyte[4]) == [1,2,3,4]); + } + { + immutable(ubyte[]) test = [1,2,3,4]; + assert(test.serialize!(Endian.littleEndian, ubyte)==[4,1,2,3,4]); + assert(test.deserialize!(ubyte[4]) == [1,2,3,4]); + } + { + struct Test { + ubyte a; + } + { + Test test = Test(1); + assert(test.serialize==[1]); + } + { + const Test test = Test(1); + assert(test.serialize==[1]); + } + { + immutable Test test = Test(1); + assert(test.serialize==[1]); + } + } +} + // for code coverage: because the coverage report does not understand CTFE unittest{ enum G; From 10b0f8d2c932143cb09b0e3afdcbd148685736cd Mon Sep 17 00:00:00 2001 From: Unknown Date: Mon, 26 Nov 2018 15:01:24 -0600 Subject: [PATCH 8/8] Now Serializer Syntax and internal code. Endian handling is better. Fully allows either std.system.Endian or xserial.Endian. Endian enum is used as the UDA. Any UDA can be given to Serializer including things like Exclude to make exclude default. --- src/xserial/attribute.d | 236 ++++++++++----------- src/xserial/package.d | 6 +- src/xserial/serial.d | 440 +++++++++++++++++++++------------------- 3 files changed, 350 insertions(+), 332 deletions(-) diff --git a/src/xserial/attribute.d b/src/xserial/attribute.d index 1183059..487a32a 100644 --- a/src/xserial/attribute.d +++ b/src/xserial/attribute.d @@ -1,132 +1,134 @@ module xserial.attribute; -import std.system : Endian; +import std.system : SysEndian = Endian; import std.traits : isIntegral; import xbuffer.varint : isVar; -import xserial.serial : EndianType; -/** - * Excludes the field from both encoding and decoding. - */ -enum Exclude; - -/** - * Includes this even if it would otherwise be excluded. - * If Exclude (or other UDA(@)) and Include are present the last one is used, except when excluded with `@EncodeOnly` or `@DecodeOnly`. - * Can also be used on @property methods to include them. (Be sure both the setter and getter exist!) - * If used on a value of a base class value will be included. - */ -enum Include; - -/** - * Used for advanced addedUDAs - */ -template Excluder(alias UDA_) { - alias UDA = UDA_; -} - -/** - * Used for advanced addedUDAs - */ -template Includer(alias UDA_) { - alias UDA = UDA_; +// For Members +public { + import std.meta:AliasSeq; + alias _ForMembersUDAs = AliasSeq!(Exclude,Include,EncodeOnly,DecodeOnly,Condition,Custom); + /** + * Excludes the field from both encoding and decoding. + */ + enum Exclude; + /** + * Includes this even if it would otherwise be excluded. + * If Exclude (or other UDA(@)) and Include are present the last one is used, except when excluded with `@EncodeOnly` or `@DecodeOnly`. + * Can also be used on @property methods to include them. (Be sure both the setter and getter exist!) + * If used on a value of a base class value will be included. + */ + enum Include; + + /** + * Excludes the field from decoding, encode only. + * Field with be excluded in decoding regardles of any other UDA. + */ + enum EncodeOnly; + /** + * Excludes the field from encoding, decode only. + * Field with be excluded in encoding regardles of any other UDA. + */ + enum DecodeOnly; + + /** + * Only encode/decode the field when the condition is met. + * The condition is placed inside an if statement and can access + * the variables and functions of the class/struct (without `this`). + * + * This attribute can be used with EncodeOnly and DecodeOnly. + */ + struct Condition { string condition; } + + struct Custom(T) if(is(T == struct) || is(T == class) || is(T == interface)) { alias C = T; } } -/** - * Excludes the field from decoding, encode only. - * Field with be excluded in decoding regardles of any other UDA. - */ -enum EncodeOnly; -/** - * Excludes the field from encoding, decode only. - * Field with be excluded in encoding regardles of any other UDA. - */ -enum DecodeOnly; - -/** - * Only encode/decode the field when the condition is met. - * The condition is placed inside an if statement and can access - * the variables and functions of the class/struct (without `this`). - * - * This attribute can be used with EncodeOnly and DecodeOnly. - */ -struct Condition { string condition; } - -/** - * Indicates the endianness for the type and its subtypes. - */ -enum BigEndian; -/// ditto -enum LittleEndian; -/** - * Encodes and decodes as a Google varint. - */ -enum Var; - -/** - * Indicates the endianness for length for the type and its subtypes. - */ -alias BigEndianLength = Length!(EndianType.bigEndian); -/// ditto -alias LittleEndianLength = Length!(EndianType.littleEndian); -/** - * Encodes and decodes length as a Google varint. - */ -alias VarLength = Length!(EndianType.var); - -/** - * Indicates that the array has no length. It should only be used - * as last field in the class/struct. - */ -enum NoLength; - - -struct Length(T) if(isIntegral!T) { - alias Type = T; - EndianType endianness = cast(EndianType)-1; - this(Endian e) { - endianness = cast(EndianType)e; +// For Basic Values +public { + alias _ForBasicValuesUDAs = AliasSeq!(Endian,SysEndian,Length,NoLength); + /** + * Indicates the endianness for the type and its subtypes. + */ + alias BigEndian = Endian.big; + /// ditto + alias LittleEndian = Endian.little; + /** + * Encodes and decodes as a Google varint. + */ + alias Var = Endian.var; + + /** + * Indicates the endianness for length for the type and its subtypes. + */ + alias BigEndianLength = Length!(Endian.big); + /// ditto + alias LittleEndianLength = Length!(Endian.little); + /** + * Encodes and decodes length as a Google varint. + */ + alias VarLength = Length!(Endian.var); + + /** + * Indicates that the array has no length. It should only be used + * as last field in the class/struct. + */ + enum NoLength; + + + struct Length(T) if(isIntegral!T) { + static alias Type = T; + Endian endianness = cast(Endian)-1; + this(SysEndian e) { + endianness = cast(Endian)e; + } } - this(EndianType e) { - endianness = cast(EndianType)e; + struct Length(SysEndian e) { + static enum endianness = cast(Endian)e; + } + struct Length(T, SysEndian e) if(isIntegral!T) { + static alias Type = T; + static enum endianness = cast(Endian)e; + } + struct Length(SysEndian e, T) if(isIntegral!T) { + static alias Type = T; + static enum endianness = cast(Endian)e; + } + struct Length(T) if(isVar!T) { + static alias Type = T.Base; + static enum endianness = Endian.var; + } + + alias EndianLength = Length; // for backwords compatability + + enum Endian : SysEndian { + bigEndian = SysEndian.bigEndian , + littleEndian = SysEndian.littleEndian , + big = bigEndian , + little = littleEndian , + var = cast(Endian)(little+1) , + } + unittest { + assert(Endian.big!=Endian.var); + assert(Endian.big!=cast(Endian)-1); } } -struct Length(Endian e) { - enum endianness = cast(EndianType)e; -} -struct Length(T, Endian e) if(isIntegral!T) { - alias Type = T; - enum endianness = cast(EndianType)e; -} -struct Length(Endian e, T) if(isIntegral!T) { - alias Type = T; - enum endianness = cast(EndianType)e; -} - -struct Length(EndianType e) { - enum endianness = cast(EndianType)e; -} -struct Length(T, EndianType e) if(isIntegral!T) { - alias Type = T; - enum endianness = cast(EndianType)e; -} -struct Length(EndianType e, T) if(isIntegral!T) { - alias Type = T; - enum endianness = cast(EndianType)e; -} - -struct Length(T) if(isVar!T) { - alias Type = T.Base; - enum endianness = EndianType.var; +// For Objects +public { + alias _ForObjectsUDAs = AliasSeq!(Excluder,Includer); + /** + * Used for advanced addedUDAs + */ + struct Excluder(alias UDA_) { + static alias UDA = UDA_; + } + /** + * Used for advanced addedUDAs + */ + struct Includer(alias UDA_) { + static alias UDA = UDA_; + } } -alias EndianLength = Length; - - -struct Custom(T) if(is(T == struct) || is(T == class) || is(T == interface)) { alias C = T; } - - - diff --git a/src/xserial/package.d b/src/xserial/package.d index 606b760..bc13750 100644 --- a/src/xserial/package.d +++ b/src/xserial/package.d @@ -1,6 +1,6 @@ module xserial; -public import xbuffer; +public import xbuffer : BufferOverflowException; -public import xserial.attribute : Exclude, Include, EncodeOnly, DecodeOnly, Condition, BigEndian, LittleEndian, Var, NoLength, Length, Custom; -public import xserial.serial : serialize, deserialize; +public import xserial.attribute; +public import xserial.serial; diff --git a/src/xserial/serial.d b/src/xserial/serial.d index ac9855b..13e6d12 100644 --- a/src/xserial/serial.d +++ b/src/xserial/serial.d @@ -1,10 +1,10 @@ module xserial.serial; -import std.system : Endian, sysEndian = endian; -import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs, TemplateOf; +import std.system : SysEndian = Endian, sysEndian = endian; +import std.traits : isArray, isDynamicArray, isStaticArray, isAssociativeArray, ForeachType, KeyType, ValueType, isIntegral, isFloatingPoint, isSomeChar, isType, isCallable, isPointer, hasUDA, getUDAs, TemplateOf, isInstanceOf; import std.typecons : isTuple; import std.algorithm.searching : canFind; -import std.meta : AliasSeq; +import std.meta : AliasSeq, Filter, templateNot, templateOr, staticMap, Erase; import xbuffer.buffer : canSwapEndianness, Buffer, BufferOverflowException; import xbuffer.memory : xalloc, xfree; @@ -14,15 +14,6 @@ import xserial.attribute; -enum EndianType { - bigEndian = cast(int)Endian.bigEndian , - littleEndian = cast(int)Endian.littleEndian , - var , -} -unittest { - assert(EndianType.bigEndian!=EndianType.var); - assert(EndianType.bigEndian!=cast(EndianType)-1); -} /** @@ -50,16 +41,68 @@ private template isDesiredUDA(alias attribute, alias toCheck) else enum isDesiredUDA = is(toCheck == attribute); } - - -private bool isTemplateOf(alias UDA, alias Template)() { - return is(UDA==Template!U,U...); +/// ditto +private template isDesiredUDA(alias attribute) +{ + template isDesiredUDA(alias toCheck) + { + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDA = toCheck == attribute; + else + enum isDesiredUDA = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDA = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDA = isInstanceOf!(attribute, toCheck); + else + enum isDesiredUDA = is(toCheck == attribute); + } +} +/// ditto +private template isDesiredUDAFlipped(alias toCheck) +{ + template isDesiredUDAFlipped(alias attribute) + { + static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) + { + static if (__traits(compiles, toCheck == attribute)) + enum isDesiredUDAFlipped = toCheck == attribute; + else + enum isDesiredUDAFlipped = false; + } + else static if (is(typeof(toCheck))) + { + static if (__traits(isTemplate, attribute)) + enum isDesiredUDAFlipped = isInstanceOf!(attribute, typeof(toCheck)); + else + enum isDesiredUDAFlipped = is(typeof(toCheck) == attribute); + } + else static if (__traits(isTemplate, attribute)) + enum isDesiredUDAFlipped = isInstanceOf!(attribute, toCheck); + else + enum isDesiredUDAFlipped = is(toCheck == attribute); + } } -template Group(AddedUDAs...) { +private template isTemplateOf(alias UDA, alias Template) { + static if (__traits(compiles,TemplateOf!UDA)) + enum isTemplateOf = is(UDA==Template!U,U...) || __traits(isSame,TemplateOf!UDA,Template); + else + enum isTemplateOf = is(UDA==Template!U,U...); +} +alias Deserializer = Serializer; +template Serializer(MainAttributes...) { private auto getSerializeMembers(T, Only)() { - alias UDAs = AliasSeq!(AddedUDAs,Includer!Include,Excluder!Exclude); + alias UDAs = AliasSeq!(MainAttributes,Includer!Include,Excluder!Exclude); struct Member{ string name ; @@ -71,14 +114,14 @@ template Group(AddedUDAs...) { foreach(member; __traits(allMembers, T)) { string condition = ""; static if(is(typeof(mixin("T." ~ member))) && !hasUDA!(__traits(getMember, T, member), Only)) { - static foreach_reverse (Attribute; __traits(getAttributes, __traits(getMember, T, member))) { + static foreach_reverse (Attribute; AliasSeq!(MainAttributes,__traits(getAttributes, __traits(getMember, T, member)))) { static if (!is(typeof(done))) { static if(is(typeof(Attribute)==Condition)) { members ~= Member(member, Attribute.condition); enum done = true; } else static foreach(A;UDAs) { - static if (!is(typeof(done))) { + static if (!is(typeof(done)) && (isTemplateOf!(A,Includer)||isTemplateOf!(A,Excluder))) { static if(isDesiredUDA!(Attribute,A.UDA)) { static if (__traits(isSame,TemplateOf!A,Includer)) { members ~= Member(member); @@ -107,26 +150,69 @@ template Group(AddedUDAs...) { return members; } - alias Deserializer = Serializer; - template Serializer(Endian endianness, L=uint, Endian lengthEndianness=endianness) { - alias Serializer = Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness); - } - template Serializer(EndianType endianness=cast(EndianType)sysEndian, L=uint, EndianType lengthEndianness=endianness) { + template Shallow(MoreAttributes...) { + private alias Attributes = AliasSeq!(MainAttributes,MoreAttributes); + + private { + //---endianness + static foreach_reverse(Attribute; Attributes) { + static if (!is(typeof(endianness))) { + static if (__traits(compiles,typeof(Attribute) && is(typeof(Attribute):SysEndian))) + enum endianness = cast(Endian)Attribute; + else static if (is(Attribute) && isVar!Attribute) + enum endianness = Endian.var; + } + } + static if(!is(typeof(endianness))) { + enum endianness = cast(Endian)sysEndian; + } + //---lengthEndianness + static foreach_reverse(Attribute; Attributes) { + static if(!is(typeof(lengthEndianness))) { + static if (isTemplateOf!(Attribute,Length) || (__traits(compiles, typeof(Attribute)) && !(is(typeof(Attribute)==void)) && isTemplateOf!(typeof(Attribute),Length))) { + static if (__traits(compiles, Attribute.endianness!=-1) && is(typeof(Attribute.endianness):SysEndian) && Attribute.endianness!=-1) { + enum lengthEndianness = Attribute.endianness; + } + } + } + } + static if(!is(typeof(lengthEndianness))) { + enum lengthEndianness = endianness; + } + //---L (length type) + static foreach_reverse(Attribute; Attributes) { + static if(!is(L)) { + static if (isTemplateOf!(Attribute,Length) || (__traits(compiles, typeof(Attribute)) && !(is(typeof(Attribute)==void)) && isTemplateOf!(typeof(Attribute),Length))) { + static if (__traits(compiles, Attribute.Type)) { + alias L = Attribute.Type; + } + } + } + } + static if(!is(L)) { + alias L = uint; + } + //---noLength + enum noLength = Erase!(NoLength,Attributes).length 1, T.stringof ~ " cannot be annotated with @Var"); buffer.writeVar!T(value); } - else static if(endianness == EndianType.bigEndian) { - buffer.write!(Endian.bigEndian, T)(value); + else static if(endianness == Endian.bigEndian) { + buffer.write!(SysEndian.bigEndian, T)(value); + } + else static if(endianness == Endian.littleEndian) { + buffer.write!(SysEndian.littleEndian, T)(value); } - else static if(endianness == EndianType.littleEndian) { - buffer.write!(Endian.littleEndian, T)(value); + else { + assert(0,"endianness not reconized"); } } /// Serialize Static Array @@ -134,11 +220,11 @@ template Group(AddedUDAs...) { serializeArrayDataImpl(value, buffer); } /// Serialize Dynamic Array && Associative Array - void serialize(bool serializeLength=true, T)(T value, Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { - static if (serializeLength) { + void serialize(T)(T value, Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { + static if (!noLength) { static if(L.sizeof < size_t.sizeof) - Serializer!(lengthEndianness, L, lengthEndianness).serialize(cast(L)value.length, buffer); - else Serializer!(lengthEndianness, L, lengthEndianness).serialize(value.length, buffer); + Shallow!(MoreAttributes,lengthEndianness).serialize(cast(L)value.length, buffer); + else Shallow!(MoreAttributes,lengthEndianness).serialize(value.length, buffer); } // Dynamic Array static if (isDynamicArray!T) { @@ -157,7 +243,7 @@ template Group(AddedUDAs...) { static assert(isArray!T); static if( canSwapEndianness!(ForeachType!T) && !is(ForeachType!T == struct) - && endianness != EndianType.var ) + && endianness != Endian.var ) { static assert(!is(ForeachType!T == class ), "internal error; submit a bug report"); static assert(!is(ForeachType!T == interface ), "internal error; submit a bug report"); @@ -198,52 +284,10 @@ template Group(AddedUDAs...) { } //---Normal else { - //---get new attributes - static foreach_reverse (uda; __traits(getAttributes, mixin("value."~member.name))) { - static if (isTemplateOf!(uda,Length) || (__traits(compiles, typeof(uda)) && !(is(typeof(uda)==void)) && isTemplateOf!(typeof(uda),Length))) { - static if (__traits(compiles, uda.Type) && !is(typeof(NewLengthGiven))) { - enum NewLengthGiven = true; - alias NewLength = uda.Type; - } - static if (__traits(compiles, uda.endianness==-1) && uda.endianness!=-1 && !is(typeof(newLengthEndianness))) { - enum newLengthEndianness = uda.endianness; - } - } - } - static if (!is(typeof(NewLengthGiven))) { - alias NewLength = L; - } - static if (!is(typeof(newLengthEndianness))) { - enum newLengthEndianness = lengthEndianness; - } - - static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) { - enum noLength = true; - } - - static foreach_reverse (uda; __traits(getAttributes, __traits(getMember, T, member.name))) { - static if (isDesiredUDA!(uda, Var)) { - enum newEndianness = EndianType.var; - } - else static if (isDesiredUDA!(uda, BigEndian)) { - enum newEndianness = EndianType.bigEndian; - } - else static if (isDesiredUDA!(uda, LittleEndian)) { - enum newEndianness = EndianType.littleEndian; - } - } - static if (!is(typeof(newEndianness))) { - enum newEndianness = endianness; - } - - //--do - static if (is(typeof(noLength))) { - static assert(isDynamicArray!M || isAssociativeArray!M); - Serializer!(newEndianness, NewLength, newLengthEndianness).serialize!false(mixin("value."~member.name), buffer); - } - else { - Serializer!(newEndianness, NewLength, newLengthEndianness).serialize(mixin("value."~member.name), buffer); - } + // This magic little code creates a new Serializer with filtered UDAs from value. It also gives shallow all UDAs. + Serializer!(MainAttributes,Filter!(templateOr!(staticMap!(isDesiredUDA,_ForBasicValuesUDAs,_ForObjectsUDAs)),__traits(getAttributes,mixin("value."~member.name)))) + .Shallow!(__traits(getAttributes,mixin("value."~member.name))) + .serialize(mixin("value."~member.name), buffer); } } }} @@ -272,14 +316,14 @@ template Group(AddedUDAs...) { /// Deserialize Number T deserialize(T)(Buffer buffer) if(is(T : bool) || isIntegral!T || isFloatingPoint!T || isSomeChar!T) { - static if(endianness == EndianType.var) { + static if(endianness == Endian.var) { static assert(isIntegral!T && T.sizeof > 1, T.stringof ~ " cannot be annotated with @Var"); return buffer.readVar!T(); } - else static if(endianness == EndianType.bigEndian) { + else static if(endianness == Endian.bigEndian) { return buffer.read!(Endian.bigEndian, T)(); } - else static if(endianness == EndianType.littleEndian) { + else static if(endianness == Endian.littleEndian) { return buffer.read!(Endian.littleEndian, T)(); } } @@ -292,9 +336,9 @@ template Group(AddedUDAs...) { return ret; } /// Deserialize Dynamic Array && Associative Array - T deserialize(T, bool serializeLength=true)(Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { - static if (serializeLength) { - auto length = Serializer!(lengthEndianness, L, lengthEndianness).deserialize!L(buffer); + T deserialize(T)(Buffer buffer) if(isDynamicArray!T || isAssociativeArray!T) { + static if (!noLength) { + auto length = Shallow!(MoreAttributes, lengthEndianness).deserialize!L(buffer); T value; foreach(_ ; 0..length) { // Dynamic Array @@ -364,52 +408,10 @@ template Group(AddedUDAs...) { } //---Normal else { - //---get new attributes - static foreach_reverse (uda; __traits(getAttributes, mixin("value."~member.name))) { - static if (isTemplateOf!(uda,Length) || (__traits(compiles, typeof(uda)) && !(is(typeof(uda)==void)) && isTemplateOf!(typeof(uda),Length))) { - static if (__traits(compiles, uda.Type) && !is(typeof(NewLengthGiven))) { - enum NewLengthGiven = true; - alias NewLength = uda.Type; - } - static if (__traits(compiles, uda.endianness==-1) && uda.endianness!=-1 && !is(typeof(newLengthEndianness))) { - enum newLengthEndianness = uda.endianness; - } - } - } - static if (!is(typeof(NewLengthGiven))) { - alias NewLength = L; - } - static if (!is(typeof(newLengthEndianness))) { - enum newLengthEndianness = lengthEndianness; - } - - static if(hasUDA!(__traits(getMember, T, member.name), NoLength)) { - enum noLength = true; - } - - static foreach_reverse (uda; __traits(getAttributes, __traits(getMember, T, member.name))) { - static if (isDesiredUDA!(uda, Var)) { - enum newEndianness = EndianType.var; - } - else static if (isDesiredUDA!(uda, BigEndian)) { - enum newEndianness = EndianType.bigEndian; - } - else static if (isDesiredUDA!(uda, LittleEndian)) { - enum newEndianness = EndianType.littleEndian; - } - } - static if (!is(typeof(newEndianness))) { - enum newEndianness = endianness; - } - - //--do - static if (is(typeof(noLength))) { - static assert(isDynamicArray!M || isAssociativeArray!M); - mixin("value."~member.name) = Serializer!(newEndianness, NewLength, newLengthEndianness).deserialize!(M,false)(buffer); - } - else { - mixin("value."~member.name) = Serializer!(newEndianness, NewLength, newLengthEndianness).deserialize!M(buffer); - } + // This magic little code creates a new Serializer with filtered UDAs from value. It also gives shallow all UDAs. + mixin("value."~member.name) = Serializer!(MainAttributes,Filter!(templateOr!(staticMap!(isDesiredUDA,_ForBasicValuesUDAs,_ForObjectsUDAs)),__traits(getAttributes,mixin("value."~member.name)))) + .Shallow!(__traits(getAttributes,mixin("value."~member.name))) + .deserialize!M(buffer); } } }} @@ -435,53 +437,32 @@ template Group(AddedUDAs...) { return deserialize(value, buffer); } } - - alias serialize = Serializer!().serialize ; - alias deserialize = Serializer!().deserialize ; - void serialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, Buffer buffer) { - Serializer!(endianness, L, lengthEndianness).serialize(value, buffer); - } - ubyte[] serialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value) { - return Serializer!(endianness, L, lengthEndianness).serialize(value); - } - void serialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) { - Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).serialize(value, buffer); - } - ubyte[] serialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value) { - return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).serialize(value); - } - - T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(Buffer buffer) { - return Serializer!(endianness, L, lengthEndianness).deserialize!T(buffer); - } - T deserialize(T, EndianType endianness, L=uint, EndianType lengthEndianness=endianness)(const(ubyte)[] data) { - return Serializer!(endianness, L, lengthEndianness).deserialize!T(data); - } - T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(Buffer buffer) { - return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(buffer); - } - T deserialize(T, Endian endianness, L=uint, Endian lengthEndianness=endianness)(const(ubyte)[] data) { - return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize!T(data); - } - - T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { - return Serializer!(endianness, L, lengthEndianness).deserialize(value, buffer); - } - T deserialize(EndianType endianness, L=uint, EndianType lengthEndianness=endianness, T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { - return Serializer!(endianness, L, lengthEndianness).deserialize(value, data); - } - T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { - return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, buffer); - } - T deserialize(Endian endianness, L=uint, Endian lengthEndianness=endianness, T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { - return Serializer!(cast(EndianType)endianness, L, cast(EndianType)lengthEndianness).deserialize(value, data); - } + alias serialize = Shallow!().serialize; + alias deserialize = Shallow!().deserialize; } -alias serialize = Group!().serialize ; -alias deserialize = Group!().deserialize ; -alias Serializer = Group!().Serializer ; -alias Deserializer = Group!().Deserializer ; +alias serialize = Serializer!().serialize ; +alias deserialize = Serializer!().deserialize ; +void serialize(SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness, T)(T value, Buffer buffer) { + Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).serialize(value, buffer); +} +ubyte[] serialize(SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness, T)(T value) { + return Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).serialize(value); +} + +T deserialize(T, SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness)(Buffer buffer) { + return Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).deserialize!T(buffer); +} +T deserialize(T, SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness)(const(ubyte)[] data) { + return Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).deserialize!T(data); +} + +T deserialize(SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness, T)(T value, Buffer buffer) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).deserialize(value, buffer); +} +T deserialize(SysEndian endianness, L=uint, SysEndian lengthEndianness=endianness, T)(T value, const(ubyte)[] data) if(!isTuple!T && (is(T == class) || is(T == struct) || is(T == interface))) { + return Serializer!(cast(Endian)endianness, Length!(L,lengthEndianness)).deserialize(value, data); +} @@ -581,7 +562,7 @@ alias Deserializer = Group!().Deserializer ; struct Test2 { ubyte[] a; - @Length @Length!ushort ushort[] b; + @Length!ushort ushort[] b; @NoLength uint[] c; } @@ -661,26 +642,26 @@ alias Deserializer = Group!().Deserializer ; { Test1 test1 = Test1(1, 2, 3, 4, 5); assert(test1.serialize() == [2, 3]); - assert(Group!(Includer!G).serialize(test1) == [1, 2, 3, 4, 5]); - assert(Group!(Includer!G, Excluder!N).serialize(test1) == [1, 3, 4]); - assert(Group!(Includer!N).serialize(test1) == [2, 3, 5]); + assert(Serializer!(Includer!G).serialize(test1) == [1, 2, 3, 4, 5]); + assert(Serializer!(Includer!G, Excluder!N).serialize(test1) == [1, 3, 4]); + assert(Serializer!(Includer!N).serialize(test1) == [2, 3, 5]); assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); - assert(Group!(Includer!G).deserialize!Test1([1, 2, 3, 4, 5]) == Test1(1, 2, 3, 4, 5)); - assert(Group!(Includer!G, Excluder!N).deserialize!Test1([1, 3, 4]) == Test1(1, 0, 3, 4, 0)); - assert(Group!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + assert(Serializer!(Includer!G).deserialize!Test1([1, 2, 3, 4, 5]) == Test1(1, 2, 3, 4, 5)); + assert(Serializer!(Includer!G, Excluder!N).deserialize!Test1([1, 3, 4]) == Test1(1, 0, 3, 4, 0)); + assert(Serializer!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); } { Test1 test1 = Test1(6, 2, 3, 4, 5); assert(test1.serialize() == [2]); - assert(Group!(Includer!G).serialize(test1) == [6, 2, 3, 4, 5]); - assert(Group!(Includer!G, Excluder!N).serialize(test1) == [6, 3, 4]); - assert(Group!(Includer!N).serialize(test1) == [2, 5]); + assert(Serializer!(Includer!G).serialize(test1) == [6, 2, 3, 4, 5]); + assert(Serializer!(Includer!G, Excluder!N).serialize(test1) == [6, 3, 4]); + assert(Serializer!(Includer!N).serialize(test1) == [2, 5]); assert(deserialize!Test1([2]) == Test1(0, 2, 0, 0, 0)); - assert(Group!(Includer!G).deserialize!Test1([6, 2, 3, 4, 5]) == Test1(6, 2, 3, 4, 5)); - assert(Group!(Includer!G, Excluder!N).deserialize!Test1([6, 3, 4]) == Test1(6, 0, 3, 4, 0)); - assert(Group!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); + assert(Serializer!(Includer!G).deserialize!Test1([6, 2, 3, 4, 5]) == Test1(6, 2, 3, 4, 5)); + assert(Serializer!(Includer!G, Excluder!N).deserialize!Test1([6, 3, 4]) == Test1(6, 0, 3, 4, 0)); + assert(Serializer!(Includer!N).deserialize!Test1([2, 5]) == Test1(0, 2, 0, 0, 5)); } } @@ -696,17 +677,17 @@ alias Deserializer = Group!().Deserializer ; Test1 test1 = Test1(1, 2, 3); assert(test1.serialize() == []); - assert(Group!(Includer!G).serialize(test1) == [1]); - assert(Group!(Includer!(G(0))).serialize(test1) == [1, 2]); - assert(Group!(Includer!(G(1))).serialize(test1) == [1, 3]); - assert(Group!(Includer!(G(0)),Includer!(G(1))).serialize(test1) == [1, 2, 3]); - assert(Group!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).serialize(test1) == [1, 2]); - - assert(Group!(Includer!G).deserialize!Test1([1]) == Test1(1, 0, 0)); - assert(Group!(Includer!(G(0))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); - assert(Group!(Includer!(G(1))).deserialize!Test1([1, 3]) == Test1(1, 0, 3)); - assert(Group!(Includer!(G(0)),Includer!(G(1))).deserialize!Test1([1, 2, 3]) == Test1(1, 2, 3)); - assert(Group!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + assert(Serializer!(Includer!G).serialize(test1) == [1]); + assert(Serializer!(Includer!(G(0))).serialize(test1) == [1, 2]); + assert(Serializer!(Includer!(G(1))).serialize(test1) == [1, 3]); + assert(Serializer!(Includer!(G(0)),Includer!(G(1))).serialize(test1) == [1, 2, 3]); + assert(Serializer!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).serialize(test1) == [1, 2]); + + assert(Serializer!(Includer!G).deserialize!Test1([1]) == Test1(1, 0, 0)); + assert(Serializer!(Includer!(G(0))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); + assert(Serializer!(Includer!(G(1))).deserialize!Test1([1, 3]) == Test1(1, 0, 3)); + assert(Serializer!(Includer!(G(0)),Includer!(G(1))).deserialize!Test1([1, 2, 3]) == Test1(1, 2, 3)); + assert(Serializer!(Includer!(G(0)),Includer!(G(1)), Excluder!(G(2))).deserialize!Test1([1, 2]) == Test1(1, 2, 0)); } @("using buffer") unittest { @@ -748,22 +729,21 @@ alias Deserializer = Group!().Deserializer ; @("override length uda") unittest { { struct Test { - @Length!ulong(EndianType.var) @Length!(ushort, EndianType.bigEndian) ubyte[] a; + @Length!ulong(Endian.var) @Length!(ushort, Endian.bigEndian) ubyte[] a; } Test test = Test([1,2]); - assert(test.serialize!(EndianType.bigEndian)==[0,2,1,2]); - assert([0,2,1,2].deserialize!(Test,EndianType.bigEndian)==test); + assert(test.serialize!(Endian.bigEndian)==[0,2,1,2]); + assert([0,2,1,2].deserialize!(Test,Endian.bigEndian)==test); } { struct Test2 { - @Length!ulong(EndianType.var) @Length!(uint) @EndianLength!ushort @LittleEndianLength ubyte[] a; + @Length!ulong(Endian.var) @Length!(uint) @EndianLength!ushort @LittleEndianLength ubyte[] a; } Test2 test = Test2([1,2]); - import std.stdio; - assert(test.serialize!(EndianType.bigEndian)==[2,0,1,2]); - assert([2,0,1,2].deserialize!(Test2,EndianType.bigEndian)==test); + assert(test.serialize!(Endian.bigEndian)==[2,0,1,2]); + assert([2,0,1,2].deserialize!(Test2,Endian.bigEndian)==test); } } @@ -803,11 +783,11 @@ alias Deserializer = Group!().Deserializer ; } Test test = new Test([1,2], 3, 4); - test.deserialize!(EndianType.bigEndian)([5,0,1,0,2,0,3,0,4,0,5,6]); + test.deserialize!(Endian.bigEndian)([5,0,1,0,2,0,3,0,4,0,5,6]); assert(test.a==[1,2,3,4,5] && test.b==3 && test.c==6); ITest iTest = test; - iTest.deserialize!(EndianType.bigEndian)([2,0,0,0,1,2]); + iTest.deserialize!(Endian.bigEndian)([2,0,0,0,1,2]); assert(test.a==[1,2] && test.b==3 && test.c==6); assert(iTest.serialize!(Endian.bigEndian) == [2,0,0,0,1,2]); @@ -858,6 +838,42 @@ alias Deserializer = Group!().Deserializer ; } } +@("default exclude") unittest { + struct Test { + ubyte a; + @Include ubyte b; + } + Test test = Test(1,2); + assert(Serializer!(Exclude).serialize(test)==[2]); + assert(Serializer!(Exclude).deserialize!Test([2])==Test(0,2)); +} + +@("new endian syntax") unittest { + struct Test { + int a; + ushort[] b; + } + Test test = Test(1,[2,3,4]); + assert(Serializer!(Endian.big,Length!ushort,Length!(Endian.little)).serialize(test)==[0,0,0,1,3,0,0,2,0,3,0,4]); + assert(Serializer!(Endian.big,Length!ushort,Length!(Endian.little)).deserialize!Test([0,0,0,1,3,0,0,2,0,3,0,4])==test); +} + +@("passing attributes to sub instances") unittest { + enum C; + struct Inner { + @C int a; + int b; + } + struct Test { + @Includer!C @Include @BigEndian Inner c; + int d; + @Include int e; + } + Test test = Test(Inner(1,2),3,4); + assert(Serializer!(Exclude,LittleEndian).serialize(test)==[0,0,0,1,4,0,0,0]); + assert(Serializer!(Exclude,LittleEndian).deserialize!Test([0,0,0,1,4,0,0,0])==Test(Inner(1,0),0,4)); +} + // for code coverage: because the coverage report does not understand CTFE unittest{ enum G; @@ -875,5 +891,5 @@ unittest{ @N ubyte e; } } - auto test1 = Group!(Includer!G, Excluder!N).getSerializeMembers!(Test1, EncodeOnly); + auto test1 = Serializer!(Includer!G, Excluder!N).getSerializeMembers!(Test1, EncodeOnly); }