1 module treeserial; 2 3 import std.traits; 4 import std.meta; 5 import std.bitmanip; 6 7 import std.algorithm; 8 import std.range; 9 import std.conv; 10 11 struct NoLength { 12 bool value = true; 13 } 14 struct LengthType(T) { 15 alias Type = T; 16 } 17 18 struct CastType(T) { 19 alias Type = T; 20 } 21 22 struct ElementAttributes(Attribs...) { 23 alias Attributes = Attribs; 24 } 25 26 struct Include { 27 bool value = true; 28 } 29 enum Exclude = Include(false); 30 31 alias DefaultAttributes = AliasSeq!(NoLength(false), LengthType!ushort); 32 33 34 template Serializer(Attributes...) { 35 36 template Subserializer(MoreAttributes...) { 37 alias Subserializer = Serializer!(Attributes, MoreAttributes); 38 } 39 alias Subdeserializer = Subserializer; 40 41 template isAttribute(alias T, AddedDefaults...) { 42 enum isAttribute = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes)).length > 0; 43 } 44 template GetAttributeType(alias T, AddedDefaults...) { 45 alias GetAttributeType = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1].Type; 46 } 47 template getAttributeValue(alias T, AddedDefaults...) { 48 static if (is(Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1]))// For if the struct is not created but referencing the type: instantiate it, and use the default. 49 enum getAttributeValue = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1]().value; 50 else 51 enum getAttributeValue = Filter!(isDesiredUDA!T, AliasSeq!(DefaultAttributes, AddedDefaults, Attributes))[$-1].value; 52 } 53 template GetAttributes(alias T) { 54 template Get(alias EA) { 55 alias Get = EA.Attributes; 56 } 57 alias GetAttributes = staticMap!(Get, Filter!(isDesiredUDA!T, Attributes)); 58 } 59 template FilterOut(alias T) { 60 alias FilterOut = Filter!(templateNot!(isDesiredUDA!T), Attributes); 61 } 62 63 static if (isAttribute!CastType) { 64 ubyte[] serialize(T)(T value) if (__traits(compiles, cast(GetAttributeType!CastType) rvalueOf!T) && serializable!(GetAttributeType!CastType)) { 65 return Serializer!(FilterOut!CastType).serialize(cast(GetAttributeType!CastType) value); 66 } 67 T deserialize(T)(ref const(ubyte)[] value) if (__traits(compiles, cast(T) rvalueOf!(GetAttributeType!CastType)) && deserializable!(GetAttributeType!CastType)) { 68 return cast(T) Serializer!(FilterOut!CastType).deserialize!(GetAttributeType!CastType)(value); 69 } 70 } 71 else { 72 ubyte[] serialize(T)(T value) if(isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) { 73 return nativeToLittleEndian(value).dup; 74 } 75 ubyte[] serialize(T)(T value) if(isDynamicArray!T || isStaticArray!T) { 76 alias Serializer_ = Serializer!(FilterOut!ElementAttributes, GetAttributes!ElementAttributes); 77 static if (isStaticArray!T || getAttributeValue!NoLength) 78 return value[].map!(Serializer_.serialize).joiner.array; 79 else 80 return serialize(cast(GetAttributeType!LengthType) value.length) ~ value.map!(Serializer_.serialize).joiner.array; 81 } 82 ubyte[] serialize(T)(T value) if(is(T == class) || is(T == struct)) { 83 static if(__traits(hasMember, T, "serialize")) { 84 return value.serialize; 85 } 86 else { 87 ubyte[] bytes; 88 foreach (memName; __traits(derivedMembers, T)) {{ 89 static if (memName != "this") { 90 alias mem = __traits(getMember, T, memName); 91 alias Mem = typeof(mem); 92 static if (Subserializer!(__traits(getAttributes, mem)).getAttributeValue!(Include, Include( 93 !isCallable!mem 94 && !__traits(compiles, { mixin("auto test=T." ~ memName ~ ";"); }) // static members 95 ))) { 96 bytes ~= Serializer!(Serializer!(FilterOut!ElementAttributes).FilterOut!Include, GetAttributes!ElementAttributes).serialize(mixin("value."~memName)); 97 } 98 } 99 }} 100 return bytes; 101 } 102 } 103 104 T deserialize(T)(ref const(ubyte)[] buffer) if (isIntegral!T || isSomeChar!T || isBoolean!T || isFloatOrDouble!T) { 105 scope (success) 106 buffer = buffer[T.sizeof..$]; 107 return littleEndianToNative!T(buffer[0..T.sizeof]); 108 } 109 T deserialize(T)(ref const(ubyte)[] buffer) if (isDynamicArray!T || isStaticArray!T) { 110 alias Serializer_ = Serializer!(FilterOut!ElementAttributes, GetAttributes!ElementAttributes); 111 static if (isStaticArray!T) 112 return repeat(null, T.length).map!(_=>Serializer_.deserialize!(ForeachType!T)(buffer)).array.to!T; 113 else static if (getAttributeValue!NoLength) 114 return repeat(null).map!(_=>Serializer_.deserialize!(ForeachType!T)(buffer)).until!((lazy _)=>buffer.length==0).array; 115 else 116 return repeat(null, deserialize!(GetAttributeType!LengthType)(buffer)).map!(_=>Serializer_.deserialize!(ForeachType!T)(buffer)).array; 117 } 118 T deserialize(T)(ref const(ubyte)[] buffer) if(is(T == class) || is(T == struct)) { 119 static if(__traits(hasMember, T, "deserialize")) { 120 return buffer.serialize; 121 } 122 else { 123 T value; 124 static if (is(T == class)) 125 value = new T(); 126 foreach (memName; __traits(derivedMembers, T)) {{ 127 static if (memName != "this") { 128 alias mem = __traits(getMember, T, memName); 129 alias Mem = typeof(mem); 130 static if(isCallable!Mem) 131 alias MemT = ReturnType!Mem; 132 else 133 alias MemT = Mem; 134 static if (Subserializer!(__traits(getAttributes, mem)).getAttributeValue!(Include, Include( 135 !isCallable!mem 136 && !__traits(compiles, { mixin("auto test=T." ~ memName ~ ";"); }) // static members 137 ))) { 138 mixin(q{value..}~memName) = Serializer!(Serializer!(FilterOut!ElementAttributes).FilterOut!Include, GetAttributes!ElementAttributes).deserialize!MemT(buffer); 139 } 140 } 141 }} 142 return value; 143 } 144 } 145 } 146 } 147 alias Deserializer = Serializer; 148 template serialize(Attributes...) { 149 alias serialize = Serializer!Attributes.serialize; 150 } 151 template deserialize(T, Attributes...) { 152 alias deserialize = Deserializer!Attributes.deserialize!T; 153 } 154 155 template serializable(T) { 156 enum serializable = __traits(compiles, rvalueOf!T.serialize); 157 } 158 template serializable(alias v) { 159 enum serializable = __traits(compiles, v.serialize); 160 } 161 template deserializable(T) { 162 enum deserializable = __traits(compiles, deserialize!T(lvalueOf!(const(ubyte)[]))); 163 } 164 165 /// Copied from phobas/std/bitmanip 166 private template isFloatOrDouble(T) 167 { 168 enum isFloatOrDouble = isFloatingPoint!T && 169 !is(Unqual!(FloatingPointTypeOf!T) == real); 170 } 171 172 173 @("number") 174 unittest { 175 { 176 int data = 5; 177 const(ubyte)[] bytes = serialize(data); 178 assert(bytes == [5,0,0,0] || bytes == [0,0,0,5]); 179 assert(bytes.deserialize!(typeof(data)) == data); 180 } 181 { 182 ushort data = 5; 183 const(ubyte)[] bytes = serialize(data); 184 assert(bytes == [5,0] || bytes == [0,5]); 185 assert(bytes.deserialize!(typeof(data)) == data); 186 } 187 } 188 @("cast type") 189 unittest { 190 { 191 int data = 5; 192 alias Serializer_ = Serializer!(CastType!byte); 193 const(ubyte)[] bytes = Serializer_.serialize(data); 194 assert(bytes == [5]); 195 assert(Serializer_.deserialize!(typeof(data))(bytes) == data); 196 } 197 } 198 @("array") 199 unittest { 200 { 201 int[] data = [1,2]; 202 const(ubyte)[] bytes = serialize(data); 203 assert(bytes == [2,0, 1,0,0,0, 2,0,0,0] || bytes == [0,2, 0,0,0,1, 0,0,0,2]); 204 assert(bytes.deserialize!(typeof(data)) == data); 205 } 206 // LengthType 207 { 208 int[] data = [1,2]; 209 alias Serializer_ = Serializer!(LengthType!uint); 210 const(ubyte)[] bytes = Serializer_.serialize(data); 211 assert(bytes == [2,0,0,0, 1,0,0,0, 2,0,0,0] || bytes == [0,0,0,2, 0,0,0,1, 0,0,0,2]); 212 assert(Serializer_.deserialize!(typeof(data))(bytes) == data); 213 } 214 // NoLength 215 { 216 int[] data = [1,2]; 217 alias Serializer_ = Serializer!(NoLength); 218 const(ubyte)[] bytes = Serializer_.serialize(data); 219 assert(bytes == [1,0,0,0, 2,0,0,0] || bytes == [0,0,0,1, 0,0,0,2]); 220 assert(Serializer_.deserialize!(typeof(data))(bytes) == data); 221 } 222 // ElementAttributes 223 { 224 int[] data = [1,2]; 225 alias Serializer_ = Serializer!(ElementAttributes!(CastType!ubyte)); 226 const(ubyte)[] bytes = Serializer_.serialize(data); 227 assert(bytes == [2,0, 1,2] || bytes == [0,2, 1,2]); 228 assert(Serializer_.deserialize!(typeof(data))(bytes) == data); 229 } 230 // Nested & ElementAttributes 231 { 232 int[][] data = [[1,2],[3,4]]; 233 alias Serializer_ = Serializer!(LengthType!ubyte, ElementAttributes!(ElementAttributes!(CastType!ubyte))); 234 const(ubyte)[] bytes = Serializer_.serialize(data); 235 assert(bytes == [2, 2,1,2, 2,3,4]); 236 assert(Serializer_.deserialize!(typeof(data))(bytes) == data); 237 } 238 // Static 239 { 240 int[2] data = [1,2]; 241 const(ubyte)[] bytes = serialize(data); 242 assert(bytes == [1,0,0,0, 2,0,0,0] || bytes == [0,0,0,1, 0,0,0,2]); 243 assert(bytes.deserialize!(typeof(data)) == data); 244 } 245 } 246 @("class") 247 unittest { 248 { 249 static class C { 250 ubyte a = 1; 251 ubyte b = 2; 252 @Exclude ubyte c = 3; 253 @property ubyte d() { return 4; } 254 @Include @property ubyte e() { return 5; } @Include @property void e(ubyte) {} 255 ubyte f() { return 6; } 256 @Include ubyte g() { return 7; } @Include void g(ubyte) {} 257 static ubyte h = 8; 258 @Include static ubyte i = 9; 259 static ubyte j() { return 10; } 260 @Include static ubyte k() { return 11; } @Include static void k(ubyte) {} 261 } 262 C data = new C; 263 const(ubyte)[] bytes = serialize(data); 264 assert(bytes == [1,2,5,7,9,11]); 265 266 const(ubyte)[] nb = [11,22,55,77,99,110]; 267 C nd = nb.deserialize!(typeof(data)); 268 assert(nd.a == 11); 269 assert(nd.b == 22); 270 assert(nd.c == 3); 271 assert(nd.d == 4); 272 assert(nd.e == 5); 273 assert(nd.f == 6); 274 assert(nd.g == 7); 275 assert(nd.h == 8); 276 assert(nd.i == 99); 277 assert(nd.j == 10); 278 assert(nd.k == 11); 279 } 280 } 281 282 /// Copied from std.traits 283 private template isDesiredUDA(alias attribute) 284 { 285 template isDesiredUDA(alias toCheck) 286 { 287 static if (is(typeof(attribute)) && !__traits(isTemplate, attribute)) 288 { 289 static if (__traits(compiles, toCheck == attribute)) 290 enum isDesiredUDA = toCheck == attribute; 291 else 292 enum isDesiredUDA = false; 293 } 294 else static if (is(typeof(toCheck))) 295 { 296 static if (__traits(isTemplate, attribute)) 297 enum isDesiredUDA = isInstanceOf!(attribute, typeof(toCheck)); 298 else 299 enum isDesiredUDA = is(typeof(toCheck) == attribute); 300 } 301 else static if (__traits(isTemplate, attribute)) 302 enum isDesiredUDA = isInstanceOf!(attribute, toCheck); 303 else 304 enum isDesiredUDA = is(toCheck == attribute); 305 } 306 } 307