1 /// 2 module sbin.deserialize; 3 4 import std.format; 5 6 import sbin.type; 7 import sbin.vluint; 8 import sbin.zigzag; 9 import sbin.exception; 10 import sbin.repr; 11 12 /++ Deserialize part of input ragne to `Target` value 13 14 Params: 15 range = input range with serialized data (not saved before work) 16 target = reference to result object 17 +/ 18 void sbinDeserializePart(RH=EmptyReprHandler, R, string file=__FILE__, size_t line=__LINE__, Target...) 19 (ref R range, ref Target target) if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte) && isReprHandler!RH) 20 { 21 import sbin.util.stack : Stack; 22 23 static struct FieldName 24 { 25 string name; 26 ptrdiff_t index = -1; 27 28 string toString() const 29 { 30 if (index != -1) return format!"%s[%d]"(name, index); 31 else return name; 32 } 33 } 34 35 alias FNStack = Stack!(FieldName, 8); 36 37 FNStack fNameStack; 38 39 static struct WrapRng 40 { 41 R* rng; 42 size_t* count; 43 FNStack* fstack; 44 45 string field() const 46 { 47 import std.algorithm: map, joiner; 48 import std.array : array; 49 import std.conv : to; 50 return fstack.getData().map!(a=>a.toString()).joiner(".").array.to!string; 51 } 52 53 string type; 54 size_t vcnt; 55 size_t vexp; 56 57 ubyte front() @property 58 { 59 enforce (!(*rng).empty, new SBinDeserializeEmptyRangeException( 60 Target.stringof, field(), type, vcnt, vexp, *count)); 61 return (*rng).front; 62 } 63 64 void popFront() 65 { 66 (*rng).popFront(); 67 (*count)++; 68 } 69 70 bool empty() @property { return (*rng).empty; } 71 } 72 73 void setRngFields(ref WrapRng rng, string type, size_t vcnt, size_t vexp) 74 { rng.type = type; rng.vcnt = vcnt; rng.vexp = vexp; } 75 76 struct StackHolder 77 { 78 this(string name, ptrdiff_t idx=-1) 79 { fNameStack.push(FieldName(name, idx)); } 80 ~this() { fNameStack.pop(); } 81 } 82 83 ubyte pop(ref WrapRng rng, string field, size_t idx, string type, size_t vcnt, size_t vexp) 84 { 85 const _ = StackHolder(field, idx); 86 setRngFields(rng, type, vcnt, vexp); 87 auto ret = rng.front(); 88 rng.popFront(); 89 return ret; 90 } 91 92 void impl(T)(ref WrapRng r, ref T trg, string field, ptrdiff_t idx=-1) 93 { 94 const __ = StackHolder(field, idx); 95 96 static if (hasRepr!(RH, T)) 97 { 98 serializeRepr!(RH, T) repr; 99 impl(r, repr, "repr"); 100 trg = RH.fromRepr(repr); 101 } 102 else static if (is(T == enum)) 103 { 104 immutable EM = [EnumMembers!T]; 105 alias ENT = EnumNumType!T; 106 ubyte[ENT.sizeof] tmp; 107 foreach (i, ref v; tmp) 108 v = pop(r, "byte", i, T.stringof, i, ENT.sizeof); 109 trg = EM[tmp.unpack!ENT]; 110 } 111 else static if (is(T == vluint)) 112 { 113 setRngFields(r, "vluint", 0, 10); 114 trg = vluint(readVLUInt(r)); 115 } 116 else static if (is(T == vlint)) 117 { 118 setRngFields(r, "vlint", 0, 10); 119 trg = vlint(zzDecode(readVLUInt(r))); 120 } 121 else static if (is(T : double) || is(T : long)) 122 { 123 ubyte[T.sizeof] tmp; 124 foreach (i, ref v; tmp) 125 v = pop(r, "byte", i, T.stringof, i, T.sizeof); 126 trg = tmp.unpack!T; 127 } 128 else static if (isVoidArray!T) 129 { 130 static if (isDynamicArray!T) 131 { 132 auto _ = StackHolder("length"); 133 setRngFields(r, "vluint", 0, 10); 134 const l = cast(size_t)readVLUInt(r); 135 if (trg.length != l) trg.length = l; 136 } 137 138 auto tmp = (() @trusted => cast(ubyte[])trg[])(); 139 foreach (i, ref v; tmp) impl(r, v, "elem", i); 140 } 141 else static if (isStaticArray!T) 142 { 143 foreach (i, ref v; trg) impl(r, v, "elem", i); 144 } 145 else static if (isSomeString!T) 146 { 147 const len = (() { 148 auto _ = StackHolder("length"); 149 setRngFields(r, "vluint", 0, 10); 150 return cast(size_t)readVLUInt(r); 151 })(); 152 auto tmp = new ubyte[](len); 153 foreach (i, ref v; tmp) 154 v = pop(r, "elem", i, T.stringof, i, len); 155 trg = (() @trusted => cast(T)tmp)(); 156 } 157 else static if (isDynamicArray!T) 158 { 159 const len = (() 160 { 161 auto _ = StackHolder("length"); 162 setRngFields(r, "vluint", 0, 10); 163 return cast(size_t)readVLUInt(r); 164 })(); 165 if (trg.length != len) trg.length = len; 166 foreach (i, ref v; trg) impl(r, v, "elem", i); 167 } 168 else static if (isAssociativeArray!T) 169 { 170 const len = ((){ 171 auto _ = StackHolder("length"); 172 setRngFields(r, "vluint", 0, 10); 173 return cast(size_t)readVLUInt(r); 174 })(); 175 176 (() @trusted => trg.clear())(); 177 178 foreach (i; 0 .. len) 179 { 180 KeyType!T k; 181 ValueType!T v; 182 impl(r, k, "key", i); 183 impl(r, v, "value", i); 184 trg[k] = v; 185 } 186 187 (() @trusted => trg.rehash())(); 188 } 189 else static if (isTagged!(T).any) 190 { 191 import std.algorithm.mutation : move; 192 193 TaggedTagType!T tag; 194 impl(r, tag, "tag"); 195 196 FS: final switch (tag) 197 { 198 static foreach (t; getTaggedAllTags!T) 199 { 200 case t: 201 TaggedTypeByTag!(T, t) tmp; 202 203 // do not try deserialize null for nullable type 204 static if (!is(typeof(tmp) == typeof(null))) 205 impl(r, tmp, "value"); 206 207 static if (__traits(compiles, trg = tmp)) trg = tmp; 208 else 209 { 210 static if (isTagged!(T).isSumType) 211 trg = T(move(tmp)); 212 else 213 trg.set!t(move(tmp)); 214 } 215 216 break FS; 217 } 218 } 219 } 220 else static if (hasCustomRepr!(T, RH)) 221 { 222 ReturnType!(trg.sbinCustomRepr) tmp; 223 impl(r, tmp, "customRepr"); 224 // for @safe sbinDeserialize sbinFromCustomRepr must be @trusted or @safe 225 trg = T.sbinFromCustomRepr(tmp); 226 } 227 else static if (is(T == struct)) 228 { 229 version (allowRawUnions) 230 { 231 import std : Nullable; 232 static if (is(T == Nullable!A, A)) 233 pragma(msg, file, "(", cast(int)line, "): ", "\033[33mWarning:\033[0m ", 234 T, " deserialize as union, use NullableAsSumTypeRH for proper deserialize!"); 235 } 236 237 import std.traits : hasUDA; 238 foreach (i, ref v; trg.tupleof) 239 static if (!hasUDA!(T.tupleof[i], sbinSkip)) 240 impl(r, v, __traits(identifier, trg.tupleof[i])); 241 } 242 else static if (is(T == union)) 243 { 244 version (allowRawUnions) 245 { 246 auto tmp = (() @trusted => cast(ubyte[])((cast(void*)&trg)[0..T.sizeof]))(); 247 foreach (i, ref v; tmp) 248 v = pop(r, "byte", i, T.stringof, i, T.sizeof); 249 } 250 else 251 static assert(0, "raw unions are not allowed, for allow build "~ 252 "with configuration 'allow-raw-unions'"); 253 } 254 else static assert(0, "unsupported type: " ~ T.stringof); 255 } 256 257 size_t cnt; 258 259 auto wr = (() @trusted => WrapRng(&range, &cnt, &fNameStack))(); 260 261 static if (Target.length == 1) 262 impl(wr, target[0], "root"); 263 else foreach (i, ref v; target) 264 impl(wr, v, "root", i); 265 } 266 267 /++ Deserialize part of input range to `Target` value 268 269 Params: 270 range = input range with serialized data (not saved before work) 271 272 Returns: 273 deserialized value 274 +/ 275 Target sbinDeserializePart(RH=EmptyReprHandler, Target, R, string file=__FILE__, size_t line=__LINE__) 276 (ref R range) if (isReprHandler!RH) 277 { 278 Unqual!Target ret; 279 sbinDeserializePart!(RH, R, file, line, Target)(range, ret); 280 return ret; 281 } 282 283 /// ditto 284 Target sbinDeserializePart(Target, R, string file=__FILE__, size_t line=__LINE__)(ref R range) 285 { 286 Unqual!Target ret; 287 sbinDeserializePart!(EmptyReprHandler, R, file, line, Target)(range, ret); 288 return ret; 289 } 290 291 /++ Deserialize `Target` value 292 293 Params: 294 range = input range with serialized data (not saved before work) 295 296 Returns: 297 deserialized value 298 +/ 299 Target sbinDeserialize(RH=EmptyReprHandler, Target, R, string file=__FILE__, size_t line=__LINE__) 300 (R range) if (isReprHandler!RH) 301 { 302 Unqual!Target ret; 303 sbinDeserialize!(RH)(range, ret); 304 return ret; 305 } 306 307 /// ditto 308 Target sbinDeserialize(Target, R, string file=__FILE__, size_t line=__LINE__)(R range) 309 { 310 Unqual!Target ret; 311 sbinDeserialize!(EmptyReprHandler, R, file, line)(range, ret); 312 return ret; 313 } 314 315 /++ Deserialize `Target` value 316 317 Params: 318 range = input range with serialized data (not saved before work) 319 target = reference to result object 320 321 Throws: 322 SBinDeserializeException if range isn't empty after deseriazlie 323 +/ 324 void sbinDeserialize(RH=EmptyReprHandler, R, string file=__FILE__, size_t line=__LINE__, Target...) 325 (R range, ref Target target) if (isReprHandler!RH) 326 { 327 sbinDeserializePart!(RH, R, file, line)(range, target); 328 329 enforce(range.empty, new SBinDeserializeException( 330 format("input range not empty after full '%s' deserialize", Target.stringof))); 331 }