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.stdio : writeln; 7 import std.traits; 8 9 import mutils.serializer.common; 10 import mutils.serializer.lexer_utils; 11 /** 12 * Serializer to save data in json|lua tokens format 13 * If serialized data have to be allocated it is not saved/loaded unless it has "malloc" UDA (@("malloc")) 14 */ 15 class JSON_Lua_SerializerToken(bool isJson){ 16 /** 17 * Function loads and saves data depending on compile time variable load 18 * If useMalloc is true pointers, arrays, classes will be saved and loaded using Mallocator 19 * T is the serialized variable 20 * ContainerOrSlice is string when load==Load.yes 21 * ContainerOrSlice container supplied by user in which data is stored when load==Load.no(save) 22 */ 23 void serialize(Load load,bool useMalloc=false, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 24 try{ 25 static if(load==Load.yes){ 26 //pragma(msg, typeof(con)); 27 auto sss=NoGcSlice!(ContainerOrSlice)(con); 28 serializeImpl!(load,useMalloc)(var, sss); 29 con=sss[0..$]; 30 }else{ 31 serializeImpl!(load,useMalloc)(var, con); 32 } 33 }catch(Exception e){} 34 } 35 36 //support for rvalues during load 37 void serialize(Load load,bool useMalloc=false, T, ContainerOrSlice)(ref T var,ContainerOrSlice con){ 38 static assert(load==Load.yes); 39 serialize!(load,useMalloc)(var,con); 40 } 41 42 __gshared static typeof(this) instance=new typeof(this); 43 44 package: 45 46 void serializeImpl(Load load,bool useMalloc=false, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 47 static assert( 48 (load==Load.yes && is(ForeachType!(ContainerOrSlice)==TokenData)) || 49 (load==Load.no && is(ForeachType!(ContainerOrSlice)==TokenData)) 50 ); 51 static assert(load!=Load.skip,"Skip not supported"); 52 commonSerialize!(load,useMalloc)(this,var,con); 53 } 54 55 //----------------------------------------- 56 //--- Basic serializing methods 57 //----------------------------------------- 58 void serializeBasicVar(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 59 static assert(isBasicType!T); 60 static if (is(T==char)) { 61 ser.serializeChar!(load)(var, con); 62 }else{ 63 static if (load == Load.yes) { 64 check!("Wrong token type")(con[0].isType!T); 65 var = con[0].get!T(); 66 con=con[1..$]; 67 } else { 68 TokenData token; 69 token=var; 70 con ~= token; 71 } 72 } 73 } 74 75 void serializeStruct(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 76 static assert(is(T == struct)); 77 78 79 serializeCharToken!(load)('{' ,con); 80 static if(load==Load.yes){ 81 loadClassOrStruct!(load)(var,con); 82 }else{ 83 saveClassOrStruct!(load)(var,con); 84 } 85 86 serializeCharToken!(load)('}' ,con); 87 88 } 89 90 91 void serializeClass(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 92 static assert(is(T==class)); 93 94 serializeCharToken!(load)('{' ,con); 95 static if(load==Load.yes){ 96 var=Mallocator.instance.make!(T); 97 loadClassOrStruct!(load)(var,con); 98 }else{ 99 if(var !is null){ 100 saveClassOrStruct!(load)(var,con); 101 } 102 } 103 serializeCharToken!(load)('}' ,con); 104 105 } 106 107 108 void serializeStaticArray(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 109 static assert(isStaticArray!T); 110 serializeCharToken!(load)('[',con); 111 foreach (i, ref a; var) { 112 serializeImpl!(load)(a,con); 113 if(i!=var.length-1){ 114 serializeCharToken!(load)(',',con); 115 } 116 } 117 serializeCharToken!(load)(']',con); 118 119 } 120 121 122 void serializeDynamicArray(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 123 static assert(isDynamicArray!T); 124 alias ElementType=Unqual!(ForeachType!(T)); 125 static if(is(ElementType==char)){ 126 static if (load == Load.yes) { 127 assert(con[0].type==StandardTokens.string_); 128 var=con[0].str; 129 con=con[1..$]; 130 } else { 131 TokenData token; 132 token=var; 133 token.type=StandardTokens.string_; 134 con ~= token; 135 } 136 }else{ 137 static if(load==Load.yes){ 138 import mutils.container.vector_allocator; 139 VectorAllocator!(ElementType, Mallocator) arrData; 140 serializeCustomVector!(load)(arrData, con); 141 var=cast(T)arrData[]; 142 }else{ 143 serializeCustomVector!(load)(var, con); 144 } 145 } 146 } 147 148 149 150 151 void serializeCustomVector(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 152 static if(is(Unqual!(ForeachType!(T))==char)){ 153 serializeCustomVectorString!(load)(var, con); 154 }else{ 155 alias ElementType=Unqual!(ForeachType!(T)); 156 uint dataLength=cast(uint)(var.length); 157 serializeCharToken!(load)('[',con); 158 159 static if(load==Load.yes){ 160 static if(hasMember!(T,"initialize")){ 161 var.initialize(); 162 } 163 164 while(!con[0].isChar(']')){ 165 ElementType element; 166 serializeImpl!(load)(element,con); 167 var~=element; 168 if(con[0].isChar(',')){ 169 serializeCharToken!(load)(',',con); 170 }else{ 171 break; 172 } 173 } 174 175 }else{ 176 foreach(i,ref d;var){ 177 serializeImpl!(load)(d,con); 178 if(i!=var.length-1){ 179 serializeCharToken!(load)(',',con); 180 } 181 } 182 183 } 184 serializeCharToken!(load)(']',con); 185 } 186 } 187 void serializeCustomMap(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 188 serializeCharToken!(load)('{' ,con); 189 static if(load==Load.yes){ 190 bool ok=!con[0].isChar('}');// false if no elements inside 191 while(ok){ 192 T.KeyType key; 193 T.ValueType value; 194 serializeKeyValue!(load)(key, value, con ); 195 var.add(key, value); 196 197 if(con[0].isChar(',')){ 198 serializeCharToken!(load)(',',con); 199 }else{ 200 break; 201 } 202 } 203 }else{ 204 size_t i; 205 foreach(ref keyValue; &var.byKeyValue){ 206 serializeKeyValue!(load)(keyValue.key, keyValue.value, con ); 207 i++; 208 if(i!=var.length){ 209 serializeCharToken!(load)(',',con); 210 } 211 } 212 } 213 serializeCharToken!(load)('}' ,con); 214 } 215 void serializeKeyValue(Load load, Key, Value, ContainerOrSlice)(ref Key key, ref Value value, ref ContainerOrSlice con){ 216 static assert(isStringVector!Key || isNumeric!Key, "Map key has to be numeric."); 217 static if(isJson){ 218 static if(isStringVector!Key){ 219 serializeImpl!(load)(key, con); 220 }else static if(load==Load.yes){ 221 assert(con[0].type==StandardTokens.string_); 222 TokenData tk; 223 serializeNumberToken!(load)(tk, con[0].str); 224 if(tk.type==StandardTokens.double_){ 225 key=cast(Key)tk.double_; 226 }else if(tk.type==StandardTokens.long_){ 227 key=cast(Key)tk.long_; 228 }else{ 229 assert(0); 230 } 231 con=con[1..$]; 232 }else{//save 233 serializeCharToken!(load)('"' ,con); 234 serializeImpl!(load)(key, con); 235 serializeCharToken!(load)('"' ,con); 236 } 237 serializeCharToken!(load)(':' ,con); 238 serializeImpl!(load)(value, con); 239 }else{ 240 serializeCharToken!(load)('[' ,con); 241 serializeImpl!(load)(key, con); 242 serializeCharToken!(load)(']' ,con); 243 serializeCharToken!(load)('=' ,con); 244 serializeImpl!(load)(value, con); 245 246 } 247 } 248 249 250 251 void serializePointer(Load load,bool useMalloc, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 252 commonSerializePointer!(load,useMalloc)(this,var,con); 253 } 254 255 //----------------------------------------- 256 //--- Helper methods for basic methods 257 //----------------------------------------- 258 259 260 void loadClassOrStruct(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 261 static assert(load==Load.yes && (is(T==class) || is(T==struct)) ); 262 263 while(var.tupleof.length>0){ 264 string varNa; 265 serializeName!(load)(varNa,con); 266 //scope(exit)Mallocator.instance.dispose(cast(string)varNa); 267 bool loaded=false; 268 foreach (i, ref a; var.tupleof) { 269 alias TP = AliasSeq!(__traits(getAttributes, var.tupleof[i])); 270 enum bool doSerialize=!hasNoserializeUda!(TP); 271 enum bool useMalloc=hasMallocUda!(TP); 272 enum string varName =__traits(identifier, var.tupleof[i]); 273 static if(doSerialize){ 274 if(varName==varNa){ 275 try{ 276 auto tmpCon=con; 277 scope(failure)con=tmpCon;//revert slice 278 serializeImpl!(load,useMalloc)(a,con); 279 loaded=true; 280 break; 281 }catch(Exception e){} 282 } 283 } 284 285 } 286 if(!loaded){ 287 ignoreToMatchingComma!(load)(con); 288 } 289 290 if(con[0].isChar(',')){ 291 con=con[1..$]; 292 }else{ 293 break; 294 } 295 296 297 298 } 299 } 300 301 302 void saveClassOrStruct(Load load, T, ContainerOrSlice)(ref T var,ref ContainerOrSlice con){ 303 static assert(load==Load.no && (is(T==class) || is(T==struct)) ); 304 foreach (i, ref a; var.tupleof) { 305 alias TP = AliasSeq!(__traits(getAttributes, var.tupleof[i])); 306 enum bool doSerialize=!hasNoserializeUda!(TP); 307 enum bool useMalloc=hasMallocUda!(TP); 308 enum string varNameTmp =__traits(identifier, var.tupleof[i]); 309 static if(doSerialize && (useMalloc || !isMallocType!(typeof(a))) ){ 310 string varName=cast(string)varNameTmp; 311 serializeName!(load)(varName,con); 312 serializeImpl!(load, useMalloc)(a,con); 313 314 if(i!=var.tupleof.length-1){ 315 serializeCharToken!(load)(',' ,con); 316 } 317 }else{ 318 // hack remove comma if last tuple element was not serializable 319 if(i==var.tupleof.length-1){ 320 con.remove(con.length-1); 321 } 322 } 323 } 324 } 325 326 static if(isJson){ 327 328 void serializeName(Load load, ContainerOrSlice)(ref string name,ref ContainerOrSlice con){ 329 330 static if (load == Load.yes) { 331 assert(con[0].type==StandardTokens.string_); 332 name=con[0].getUnescapedString; 333 con=con[1..$]; 334 } else { 335 TokenData token; 336 token=name; 337 token.type=StandardTokens.string_; 338 con ~= token; 339 } 340 serializeCharToken!(load)(':' ,con); 341 } 342 }else{ 343 344 void serializeName(Load load, ContainerOrSlice)(ref string name,ref ContainerOrSlice con){ 345 346 static if (load == Load.yes) { 347 assert(con[0].type==StandardTokens.identifier); 348 name=con[0].str; 349 con=con[1..$]; 350 } else { 351 TokenData token; 352 token=name; 353 token.type=StandardTokens.identifier; 354 con ~= token; 355 } 356 serializeCharToken!(load)('=' ,con); 357 } 358 } 359 }