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