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.ConsoleLogger; 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)(auto ref C obj, ref T val) 155 if (isDecerealiser!C && !isOutputRange!(T, ubyte) && isDynamicArray!T && !is(T == string)) { 156 ushort length; 157 158 specify!(options)(obj, length); 159 decerealiseArrayImpl!(options)(obj, val, length); 160 } 161 162 void decerealiseArrayImpl(SerializationOptions options, C, T : E[], E, U)(auto ref C obj, ref T val, U length) 163 if (is(T == E[], E) && isDecerealiser!C) { 164 165 ulong neededBytes(T)(ulong length) { 166 alias E = ElementType!T; 167 static if (isScalarType!E) 168 return length * E.sizeof; 169 else static if (isInputRange!E) 170 return neededBytes!E(length); 171 else 172 return 0; 173 } 174 175 immutable needed = neededBytes!T(length); 176 177 static if (is(Unqual!(ElementType!T) : ubyte) && T.sizeof == 1) { 178 val = obj.putRaw(length).dup; 179 } else { 180 if (val.length != length) 181 val.length = cast(uint) length; 182 183 foreach (ref e; val) { 184 obj.specify!(options)(e); 185 } 186 } 187 } 188 189 void specifyPtr(SerializationOptions options, C, T)(auto ref C obj, ref T val) { 190 auto ptr = cast(PtrType!T)(&val); 191 specify!(options)(obj, *ptr); 192 } 193 194 void loopMembers(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == struct)) { 195 loopMembersImpl!(T, options)(obj, val); 196 } 197 198 void loopMembers(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) { 199 200 debug(HUNT_DEBUG_MORE) tracef("Setting: %s, value: %s", T.stringof, val); 201 202 static if (is(C == BinarySerializer)) { 203 if (val is null) { 204 obj.putRaw(NULL); 205 return; 206 } 207 //assert(val !is null, "null value cannot be serialised"); 208 } 209 210 static if (is(C == BinaryDeserializer)) { 211 if (obj.isNullObj) { 212 obj.take(NULL.length); 213 val = null; 214 return; 215 } 216 } 217 218 static if (is(typeof(() { val = new T; }))) { 219 if (val is null) 220 val = new T; 221 } 222 223 obj.putClass!(options, T)(val); 224 } 225 226 227 void loopMembersImpl(T, SerializationOptions options, C, VT) 228 (auto ref C obj, ref VT val) { 229 // foreach (member; __traits(derivedMembers, T)) { 230 // enum isMemberVariable = is(typeof(() { 231 // __traits(getMember, val, member) = __traits(getMember, val, member).init; 232 // })); 233 // static if (isMemberVariable) { 234 // specifyAggregateMember!member(obj, val); 235 // } 236 // } 237 238 debug(HUNT_DEBUG_MORE) pragma(msg, "T=> " ~ T.stringof); 239 static foreach (string member; FieldNameTuple!T) { 240 debug(HUNT_DEBUG_MORE) pragma(msg, "Field member: " ~ member); 241 static if(!member.empty()) 242 {{ 243 alias currentMember = __traits(getMember, T, member); 244 alias memberType = typeof(currentMember); 245 246 static if(hasUDA!(currentMember, Ignore)) { 247 enum canDeserialize = false; 248 version(HUNT_DEBUG) { 249 infof("Ignore a member: %s %s", memberType.stringof, member); 250 } 251 } else static if(options.onlyPublic) { 252 static if (__traits(getProtection, currentMember) == "public") { 253 enum canDeserialize = true; 254 } else { 255 enum canDeserialize = false; 256 } 257 } else { 258 enum canDeserialize = true; 259 } 260 261 static if(canDeserialize) { 262 debug(HUNT_DEBUG_MORE) tracef("name: %s", member); 263 specify!(options)(obj, __traits(getMember, val, member)); 264 debug(HUNT_DEBUG_MORE) infof("value: %s", __traits(getMember, val, member)); 265 } 266 }} 267 } 268 } 269 270 271 // void specifyAggregateMember(string member, SerializationOptions options, C, T)(auto ref C obj, ref T val) { 272 // import std.meta : staticIndexOf; 273 274 // enum NoCereal; 275 // enum noCerealIndex = staticIndexOf!(NoCereal, __traits(getAttributes, 276 // __traits(getMember, val, member))); 277 // static if (noCerealIndex == -1) { 278 // specifyMember!(member, options)(obj, val); 279 // } 280 // } 281 282 // void specifyMember(string member, SerializationOptions options, C, T)(auto ref C obj, ref T val) { 283 // // alias currentMember = __traits(getMember, val, member); 284 // // static if(isAggregateType!(typeof(currentMember))) { 285 // // specify!(options)(obj, __traits(getMember, val, member)); 286 // // } else { 287 // // specify(obj, __traits(getMember, val, member)); 288 // // } 289 // debug(HUNT_DEBUG_MORE) tracef("name: %s", member); 290 // specify!(options)(obj, __traits(getMember, val, member)); 291 // debug(HUNT_DEBUG_MORE) infof("value: %s", __traits(getMember, val, member)); 292 // } 293 294 void specifyBaseClass(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) { 295 foreach (base; BaseTypeTuple!T) { 296 loopMembersImpl!(base, options)(obj, val); 297 } 298 } 299 300 void specifyClass(SerializationOptions options, C, T)(auto ref C obj, ref T val) if (is(T == class)) { 301 static if(options.traverseBase) { 302 specifyBaseClass!(options)(obj, val); 303 } 304 loopMembersImpl!(T, options)(obj, val); 305 } 306 307 void checkDecerealiser(T)() { 308 //static assert(T.type == CerealType.ReadBytes); 309 auto dec = T(); 310 ulong bl = dec.bytesLeft; 311 } 312 313 enum isDecerealiser(T) = (is(T == BinaryDeserializer) && is(typeof(checkDecerealiser!T)));