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