1 module mutils.serializer.lua_json_token; 2 3 import std.experimental.allocator; 4 import std.experimental.allocator.mallocator; 5 import std.meta; 6 import std.traits; 7 8 import mutils.container.vector_allocator; 9 import mutils.serializer.common; 10 import mutils.serializer.lexer_utils; 11 12 // COS==ContainerOrSlice 13 14 /** 15 * Serializer to save data in json|lua tokens format 16 * If serialized data have to be allocated it is not saved/loaded unless it has "malloc" UDA (@("malloc")) 17 */ 18 final class JSON_Lua_SerializerToken(bool isJson) { 19 alias SliceElementType = TokenData; 20 21 int beginObject(Load load, COS)(ref COS con) { 22 serializeCharToken!(load)('{', con); 23 return 0; // Just to satisfy interface 24 } 25 26 void endObject(Load load, COS)(ref COS con, int begin) { 27 serializeCharToken!(load)('}', con); 28 } 29 30 bool serializeWithName(Load load, string name, bool useMalloc = false, T, COS)(ref T var, 31 ref COS con) { 32 static if (load == Load.yes) { 33 auto conBegin = con; 34 scope (exit) 35 con = conBegin; //revert slice 36 37 foreach (iii; 0 .. 1000) { 38 string varNa; 39 serializeName!(load)(varNa, con); 40 bool loaded = false; 41 42 if (name == varNa) { 43 try { 44 auto tmpCon = con; 45 scope (failure) 46 con = tmpCon; //revert slice 47 serializeImpl!(load, useMalloc)(var, con); 48 loaded = true; 49 } 50 catch (Exception e) { 51 } 52 return true; 53 } 54 //scope(exit)Mallocator.instance.dispose(cast(string)varNa); 55 if (!loaded) { 56 ignoreToMatchingComma!(load)(con); 57 } 58 59 if (con[0].isChar(',')) { 60 con = con[1 .. $]; 61 } 62 if (con[0].isChar('}')) { 63 break; 64 } 65 } 66 return false; 67 68 } else { 69 70 if (!con[$ - 1].isChar('{')) { 71 serializeCharToken!(load)(',', con); 72 } 73 static string tmpName = name; 74 serializeName!(load)(tmpName, con); 75 assert(tmpName == name); 76 serialize!(load, useMalloc)(var, con); 77 return true; 78 79 } 80 81 } 82 /** 83 * Function loads and saves data depending on compile time variable load 84 * If useMalloc is true pointers, arrays, classes will be saved and loaded using Mallocator 85 * T is the serialized variable 86 * COS is string when load==Load.yes 87 * COS container supplied by user in which data is stored when load==Load.no(save) 88 */ 89 void serialize(Load load, bool useMalloc = false, T, COS)(ref T var, ref COS con) { 90 try { 91 static if (load == Load.yes) { 92 //pragma(msg, typeof(con)); 93 auto sss = NoGcSlice!(COS)(con); 94 serializeImpl!(load, useMalloc)(var, sss); 95 con = sss[0 .. $]; 96 } else { 97 serializeImpl!(load, useMalloc)(var, con); 98 } 99 } 100 catch (Exception e) { 101 } 102 } 103 104 //support for rvalues during load 105 void serialize(Load load, bool useMalloc = false, T, COS)(ref T var, COS con) { 106 static assert(load == Load.yes); 107 serialize!(load, useMalloc)(var, con); 108 } 109 110 __gshared static typeof(this) instance = new typeof(this); 111 112 package: 113 114 void serializeImpl(Load load, bool useMalloc = false, T, COS)(ref T var, ref COS con) { 115 static assert((load == Load.yes && is(ForeachType!(COS) == TokenData)) 116 || (load == Load.no && is(ForeachType!(COS) == TokenData))); 117 static assert(load != Load.skip, "Skip not supported"); 118 commonSerialize!(load, useMalloc)(this, var, con); 119 } 120 121 //----------------------------------------- 122 //--- Basic serializing methods 123 //----------------------------------------- 124 void serializeBasicVar(Load load, T, COS)(ref T var, ref COS con) { 125 static assert(isBasicType!T); 126 static if (is(T == char)) { 127 serializeChar!(load)(var, con); 128 } else static if (is(T == bool)) { 129 serializeBoolToken!(load)(var, con); 130 } else { 131 static if (load == Load.yes) { 132 check!("Wrong token type")(con[0].isAssignableTo!T); 133 var = con[0].get!T(); 134 con = con[1 .. $]; 135 } else { 136 TokenData token; 137 token = var; 138 con ~= token; 139 } 140 } 141 } 142 void serializeChar(Load load, T, COS)(ref T var, ref COS con) { 143 serializeCharToken!(load)('"', con); 144 static if (load == Load.yes) { 145 var = con[0].get!T(); 146 con = con[1 .. $]; 147 } else { 148 TokenData token; 149 token = var; 150 con ~= token; 151 } 152 serializeCharToken!(load)('"', con); 153 154 } 155 156 void serializeStruct(Load load, T, COS)(ref T var, ref COS con) { 157 static assert(is(T == struct)); 158 159 serializeCharToken!(load)('{', con); 160 static if (load == Load.yes) { 161 loadClassOrStruct!(load)(var, con); 162 } else { 163 saveClassOrStruct!(load)(var, con); 164 } 165 166 serializeCharToken!(load)('}', con); 167 168 } 169 170 void serializeClass(Load load, T, COS)(ref T var, ref COS con) { 171 static assert(is(T == class)); 172 173 serializeCharToken!(load)('{', con); 174 static if (load == Load.yes) { 175 var = Mallocator.instance.make!(T); 176 loadClassOrStruct!(load)(var, con); 177 } else { 178 if (var !is null) { 179 saveClassOrStruct!(load)(var, con); 180 } 181 } 182 serializeCharToken!(load)('}', con); 183 184 } 185 186 void serializeStaticArray(Load load, T, COS)(ref T var, ref COS con) { 187 static assert(isStaticArray!T); 188 serializeCharToken!(load)('[', con); 189 foreach (i, ref a; var) { 190 serializeImpl!(load)(a, con); 191 if (i != var.length - 1) { 192 serializeCharToken!(load)(',', con); 193 } 194 } 195 serializeCharToken!(load)(']', con); 196 197 } 198 199 void serializeDynamicArray(Load load, T, COS)(ref T var, ref COS con) { 200 static assert(isDynamicArray!T); 201 alias ElementType = Unqual!(ForeachType!(T)); 202 static if (is(ElementType == char)) { 203 static if (load == Load.yes) { 204 assert(con[0].type == StandardTokens.string_); 205 206 VectorAllocator!(ElementType, Mallocator) arrData; 207 serializeCustomVector!(load)(arrData, con); 208 var = cast(T) arrData[]; 209 210 //var=con[0].str; 211 //con=con[1..$]; 212 } else { 213 TokenData token; 214 token = var; 215 token.type = StandardTokens.string_; 216 con ~= token; 217 } 218 } else { 219 static if (load == Load.yes) { 220 221 VectorAllocator!(ElementType, Mallocator) arrData; 222 serializeCustomVector!(load)(arrData, con); 223 var = cast(T) arrData[]; 224 } else { 225 serializeCustomVector!(load)(var, con); 226 } 227 } 228 } 229 230 void serializeString(Load load, T, COS)(ref T var, ref COS con) { 231 serializeCustomVectorString!(load)(var, con); 232 } 233 234 void serializeCustomVector(Load load, T, COS)(ref T var, ref COS con) { 235 static if (is(Unqual!(ForeachType!(T)) == char)) { 236 serializeCustomVectorString!(load)(var, con); 237 } else { 238 alias ElementType = Unqual!(ForeachType!(T)); 239 uint dataLength = cast(uint)(var.length); 240 serializeCharToken!(load)('[', con); 241 242 static if (load == Load.yes) { 243 static if (hasMember!(T, "initialize")) { 244 var.initialize(); 245 } 246 247 static if (hasMember!(T, "reset")) { 248 var.reset(); 249 } 250 251 while (!con[0].isChar(']')) { 252 ElementType element; 253 serializeImpl!(load)(element, con); 254 var ~= element; 255 if (con[0].isChar(',')) { 256 serializeCharToken!(load)(',', con); 257 } else { 258 break; 259 } 260 } 261 262 } else { 263 int i; 264 foreach (ref d; var) { 265 serializeImpl!(load)(d, con); 266 if (i != var.length - 1) { 267 serializeCharToken!(load)(',', con); 268 } 269 i++; 270 } 271 272 } 273 serializeCharToken!(load)(']', con); 274 } 275 } 276 277 void serializeCustomMap(Load load, T, COS)(ref T var, ref COS con) { 278 serializeCharToken!(load)('{', con); 279 static if (load == Load.yes) { 280 bool ok = !con[0].isChar('}'); // false if no elements inside 281 while (ok) { 282 T.Key key; 283 T.Value value; 284 serializeKeyValue!(load)(key, value, con); 285 var.add(key, value); 286 287 if (con[0].isChar(',')) { 288 serializeCharToken!(load)(',', con); 289 ok = !con[0].isChar('}'); 290 } else { 291 break; 292 } 293 } 294 } else { 295 size_t i; 296 foreach (ref k, ref v; &var.byKeyValue) { 297 serializeKeyValue!(load)(k, v, con); 298 i++; 299 if (i != var.length) { 300 serializeCharToken!(load)(',', con); 301 } 302 } 303 } 304 serializeCharToken!(load)('}', con); 305 } 306 307 void serializeKeyValue(Load load, Key, Value, COS)(ref Key key, ref Value value, ref COS con) { 308 static assert(isStringVector!Key || isNumeric!Key, 309 "Map key has to be numeric or char vector."); 310 static if (isJson) { 311 static if (isStringVector!Key) { 312 serializeImpl!(load)(key, con); 313 } else static if (load == Load.yes) { 314 assert(con[0].type == StandardTokens.string_); 315 TokenData tk; 316 serializeNumberToken!(load)(tk, con[0].str); 317 if (tk.type == StandardTokens.double_) { 318 key = cast(Key) tk.double_; 319 } else if (tk.type == StandardTokens.long_) { 320 key = cast(Key) tk.long_; 321 } else { 322 assert(0); 323 } 324 con = con[1 .. $]; 325 } else { //save 326 serializeCharToken!(load)('"', con); 327 serializeImpl!(load)(key, con); 328 serializeCharToken!(load)('"', con); 329 } 330 serializeCharToken!(load)(':', con); 331 serializeImpl!(load)(value, con); 332 } else { 333 serializeCharToken!(load)('[', con); 334 serializeImpl!(load)(key, con); 335 serializeCharToken!(load)(']', con); 336 serializeCharToken!(load)('=', con); 337 serializeImpl!(load)(value, con); 338 339 } 340 } 341 342 void serializePointer(Load load, bool useMalloc, T, COS)(ref T var, ref COS con) { 343 commonSerializePointer!(load, useMalloc)(this, var, con); 344 } 345 346 //----------------------------------------- 347 //--- Helper methods for basic methods 348 //----------------------------------------- 349 350 void loadClassOrStruct(Load load, T, COS)(ref T var, ref COS con) { 351 static assert(load == Load.yes && (is(T == class) || is(T == struct))); 352 353 while (var.tupleof.length > 0) { 354 string varNa; 355 serializeName!(load)(varNa, con); 356 //scope(exit)Mallocator.instance.dispose(cast(string)varNa); 357 bool loaded = false; 358 foreach (i, ref a; var.tupleof) { 359 alias TP = AliasSeq!(__traits(getAttributes, var.tupleof[i])); 360 enum bool doSerialize = !hasNoserializeUda!(TP); 361 enum bool useMalloc = hasMallocUda!(TP); 362 enum string varName = __traits(identifier, var.tupleof[i]); 363 static if (doSerialize) { 364 if (varName == varNa) { 365 try { 366 auto tmpCon = con; 367 scope (failure) 368 con = tmpCon; //revert slice 369 serializeImpl!(load, useMalloc)(a, con); 370 loaded = true; 371 break; 372 } 373 catch (Exception e) { 374 } 375 } 376 } 377 378 } 379 if (!loaded) { 380 ignoreToMatchingComma!(load)(con); 381 } 382 383 if (con[0].isChar(',')) { 384 con = con[1 .. $]; 385 } 386 if (con[0].isChar('}')) { 387 break; 388 } 389 390 } 391 } 392 393 void saveClassOrStruct(Load load, T, COS)(ref T var, ref COS con) { 394 static assert(load == Load.no && (is(T == class) || is(T == struct))); 395 foreach (i, ref a; var.tupleof) { 396 alias TP = AliasSeq!(__traits(getAttributes, var.tupleof[i])); 397 enum bool doSerialize = !hasNoserializeUda!(TP); 398 enum bool useMalloc = hasMallocUda!(TP); 399 enum string varNameTmp = __traits(identifier, var.tupleof[i]); 400 static if (doSerialize && (useMalloc || !isMallocType!(typeof(a)))) { 401 string varName = cast(string) varNameTmp; 402 serializeName!(load)(varName, con); 403 serializeImpl!(load, useMalloc)(a, con); 404 405 if (i != var.tupleof.length - 1) { 406 serializeCharToken!(load)(',', con); 407 } 408 } else { 409 // hack remove comma if last tuple element was not serializable 410 if (i == var.tupleof.length - 1 && var.tupleof.length > 1) { // ERROR: if all elements are noserialize 411 con.remove(con.length - 1); 412 } 413 } 414 } 415 } 416 417 static if (isJson) { 418 419 void serializeName(Load load, COS)(ref string name, ref COS con) { 420 421 static if (load == Load.yes) { 422 if (con[0].type != StandardTokens.string_) { 423 //writelnTokens(con[0..10]); 424 //assert(con[0].type == StandardTokens.string_, "Wrong token, there should be key"); 425 name = null; 426 return; 427 } 428 name = con[0].getUnescapedString; 429 con = con[1 .. $]; 430 } else { 431 TokenData token; 432 token = name; 433 token.type = StandardTokens.string_; 434 con ~= token; 435 } 436 serializeCharToken!(load)(':', con); 437 } 438 } else { 439 440 void serializeName(Load load, COS)(ref string name, ref COS con) { 441 442 static if (load == Load.yes) { 443 assert(con[0].type == StandardTokens.identifier); 444 name = con[0].str; 445 con = con[1 .. $]; 446 } else { 447 TokenData token; 448 token = name; 449 token.type = StandardTokens.identifier; 450 con ~= token; 451 } 452 serializeCharToken!(load)('=', con); 453 } 454 } 455 }