1 module hunt.serialization.Specify; 2 3 import std.traits; 4 import std.range; 5 import hunt.serialization.BinarySerializer; 6 import hunt.serialization.BinaryDeserializer; 7 8 template PtrType(T) { 9 static if (is(T == bool) || is(T == char)) { 10 alias PtrType = ubyte*; 11 } else static if (is(T == float)) { 12 alias PtrType = uint*; 13 } else static if (is(T == double)) { 14 alias PtrType = ulong*; 15 } else { 16 alias PtrType = Unsigned!T*; 17 } 18 } 19 20 enum NULL = [110, 117, 108, 108]; 21 22 void specify(C, T)(auto ref C obj, ref T val) if (is(T == wchar)) { 23 specify(obj, *cast(ushort*)&val); 24 } 25 26 void specify(C, T)(auto ref C obj, ref T val) if (is(T == dchar)) { 27 specify(obj, *cast(uint*)&val); 28 } 29 30 void specify(C, T)(auto ref C obj, ref T val) if (is(T == ushort)) { 31 ubyte valh = (val >> 8); 32 ubyte vall = val & 0xff; 33 obj.putUbyte(valh); 34 obj.putUbyte(vall); 35 val = (valh << 8) + vall; 36 } 37 38 void specify(C, T)(auto ref C obj, ref T val) if (is(T == uint)) { 39 ubyte val0 = (val >> 24); 40 ubyte val1 = cast(ubyte)(val >> 16); 41 ubyte val2 = cast(ubyte)(val >> 8); 42 ubyte val3 = val & 0xff; 43 obj.putUbyte(val0); 44 obj.putUbyte(val1); 45 obj.putUbyte(val2); 46 obj.putUbyte(val3); 47 val = (val0 << 24) + (val1 << 16) + (val2 << 8) + val3; 48 } 49 50 void specify(C, T)(auto ref C obj, ref T val) if (is(T == ulong)) { 51 T newVal; 52 for (int i = 0; i < T.sizeof; ++i) { 53 immutable shiftBy = 64 - (i + 1) * T.sizeof; 54 ubyte byteVal = (val >> shiftBy) & 0xff; 55 obj.putUbyte(byteVal); 56 newVal |= (cast(T) byteVal << shiftBy); 57 } 58 val = newVal; 59 } 60 61 void specify(C, T)(auto ref C obj, ref T val) if (isStaticArray!T) { 62 static if (is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) { 63 obj.putRaw(cast(ubyte[]) val); 64 } else { 65 foreach (ref v; val) { 66 specify(obj, v); 67 } 68 } 69 } 70 71 void specify(C, T)(auto ref C obj, ref T val) if (is(T == string)) { 72 ushort len = cast(ushort) val.length; 73 specify(obj, len); 74 75 static if (is(C == BinarySerializer)) { 76 obj.putRaw(cast(ubyte[]) val); 77 } else { 78 val = cast(string) obj.putRaw(len).idup; 79 } 80 } 81 82 void specify(U, C, T)(auto ref C obj, ref T val) if(is(T == string)) { 83 U length = cast(U)val.length; 84 assert(length == val.length, "overflow"); 85 specify(obj,length); 86 87 static if(is(C == BinarySerializer)) 88 obj.putRaw(cast(ubyte[])val); 89 else 90 val = cast(string) obj.putRaw(length).idup; 91 } 92 93 void specify(C, T)(auto ref C obj, ref T val) if (isAssociativeArray!T) { 94 ushort length = cast(ushort) val.length; 95 specify(obj, length); 96 const keys = val.keys; 97 for (ushort i = 0; i < length; ++i) { 98 KeyType!T k = keys.length ? keys[i] : KeyType!T.init; 99 auto v = keys.length ? val[k] : ValueType!T.init; 100 101 specify(obj, k); 102 specify(obj, v); 103 val[k] = v; 104 } 105 } 106 107 void specify(C, T)(auto ref C obj, ref T val) if (isPointer!T) { 108 alias ValueType = PointerTarget!T; 109 specify(obj, *val); 110 } 111 112 //ubyte 113 void specify(C, T)(auto ref C obj, ref T val) if (is(T == ubyte)) { 114 obj.putUbyte(val); 115 } 116 117 void specify(C, T)(auto ref C obj, ref T val) 118 if (!is(T == enum) && (isSigned!T || isBoolean!T || is(T == char) || isFloatingPoint!T)) { 119 specifyPtr(obj, val); 120 } 121 122 //ENUM 123 void specify(C, T)(auto ref C obj, ref T val) if (is(T == enum)) { 124 specify(obj, cast(Unqual!(OriginalType!(T))) val); 125 } 126 127 void specify(C, T)(auto ref C obj, ref T val) 128 if (is(C == BinarySerializer) && isInputRange!T && !isInfinite!T 129 && !is(T == string) && !isStaticArray!T && !isAssociativeArray!T) { 130 enum hasLength = is(typeof(() { auto l = val.length; })); 131 ushort length = cast(ushort) val.length; 132 specify(obj, length); 133 134 static if (hasSlicing!(Unqual!T) && is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) { 135 obj.putRaw(cast(ubyte[]) val.array); 136 } else { 137 foreach (ref v; val) { 138 specify(obj, v); 139 } 140 } 141 } 142 143 void specify(C, T)(auto ref C obj, ref T val) 144 if (isAggregateType!T && !isInputRange!T && !isOutputRange!(T, ubyte)) { 145 loopMembers(obj, val); 146 } 147 148 void specify(C, T)(auto ref C obj, ref T val) 149 if (isDecerealiser!C && !isOutputRange!(T, ubyte) && isDynamicArray!T && !is(T == string)) { 150 ushort length; 151 152 specify(obj, length); 153 decerealiseArrayImpl(obj, val, length); 154 } 155 156 void decerealiseArrayImpl(C, T, U)(auto ref C obj, ref T val, U length) 157 if (is(T == E[], E) && isDecerealiser!C) { 158 159 ulong neededBytes(T)(ulong length) { 160 alias E = ElementType!T; 161 static if (isScalarType!E) 162 return length * E.sizeof; 163 else static if (isInputRange!E) 164 return neededBytes!E(length); 165 else 166 return 0; 167 } 168 169 immutable needed = neededBytes!T(length); 170 171 static if (is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) { 172 val = obj.putRaw(length).dup; 173 } else { 174 if (val.length != length) 175 val.length = cast(uint) length; 176 foreach (ref e; val) 177 obj.specify(e); 178 } 179 } 180 181 void specifyPtr(C, T)(auto ref C obj, ref T val) { 182 auto ptr = cast(PtrType!T)(&val); 183 specify(obj, *ptr); 184 } 185 186 void loopMembers(C, T)(auto ref C obj, ref T val) if (is(T == struct)) { 187 loopMembersImpl!T(obj, val); 188 } 189 190 void loopMembers(C, T)(auto ref C obj, ref T val) if (is(T == class)) { 191 192 static if (is(C == BinarySerializer)) { 193 if (val is null) { 194 obj.putRaw(NULL); 195 return; 196 } 197 //assert(val !is null, "null value cannot be serialised"); 198 } 199 200 static if (is(C == BinaryDeserializer)) { 201 if (obj.isNullObj) { 202 val = null; 203 return; 204 } 205 } 206 207 static if (is(typeof(() { val = new T; }))) { 208 if (val is null) 209 val = new T; 210 } else { 211 } 212 213 obj.putClass(val); 214 } 215 216 void loopMembersImpl(T, C, VT)(auto ref C obj, ref VT val) { 217 foreach (member; __traits(derivedMembers, T)) { 218 enum isMemberVariable = is(typeof(() { 219 __traits(getMember, val, member) = __traits(getMember, val, member).init; 220 })); 221 static if (isMemberVariable) { 222 specifyAggregateMember!member(obj, val); 223 } 224 } 225 } 226 227 void specifyAggregateMember(string member, C, T)(auto ref C obj, ref T val) { 228 import std.meta : staticIndexOf; 229 230 enum NoCereal; 231 enum noCerealIndex = staticIndexOf!(NoCereal, __traits(getAttributes, 232 __traits(getMember, val, member))); 233 static if (noCerealIndex == -1) { 234 specifyMember!member(obj, val); 235 } 236 } 237 238 void specifyMember(string member, C, T)(auto ref C obj, ref T val) { 239 specify(obj, __traits(getMember, val, member)); 240 } 241 242 void specifyBaseClass(C, T)(auto ref C obj, ref T val) if (is(T == class)) { 243 foreach (base; BaseTypeTuple!T) { 244 loopMembersImpl!base(obj, val); 245 } 246 } 247 248 void specifyClass(C, T)(auto ref C obj, ref T val) if (is(T == class)) { 249 specifyBaseClass(obj, val); 250 loopMembersImpl!T(obj, val); 251 } 252 253 void checkDecerealiser(T)() { 254 //static assert(T.type == CerealType.ReadBytes); 255 auto dec = T(); 256 ulong bl = dec.bytesLeft; 257 } 258 259 enum isDecerealiser(T) = (is(T == BinaryDeserializer) && is(typeof(checkDecerealiser!T)));