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