1 module mutils.serializer.common; 2 3 import std.algorithm : stripLeft; 4 import std.experimental.allocator; 5 import std.experimental.allocator.mallocator; 6 import std.stdio; 7 import std.string : indexOf; 8 import std.traits; 9 10 import mutils.serializer.lexer_utils; 11 12 /// Runtime check, throws nogc exception on error 13 static void check(string str="Parsing Error")(bool ok){ 14 enum hardCheck=false;// if true assert on error else throw Exception 15 static if(hardCheck){ 16 assert(ok,str); 17 }else{ 18 shared static immutable Exception e=new Exception(str); 19 if(!ok){ 20 throw e; 21 } 22 } 23 } 24 25 26 /// Enum to check if data is loaded or saved 27 enum Load { 28 no, 29 yes, 30 skip 31 } 32 33 /// Checks if type have to be allocated by serializer 34 auto isMallocType(T)(){ 35 static if(isDynamicArray!T || isPointer!T || is(T==class)){ 36 return true; 37 }else{ 38 return false; 39 } 40 } 41 42 /// Checks if in user defined attributes(UDA) there is malloc string 43 /// "malloc" string UDA indicates that data should be allocated by serializer 44 auto hasMallocUda(Args...)(){ 45 bool hasMalloc=false; 46 foreach(Arg;Args){ 47 static if(is(typeof(Arg)==string) && Arg=="malloc"){ 48 hasMalloc=true; 49 } 50 } 51 return hasMalloc; 52 } 53 54 /// Checks if in user defined attributes(UDA) there is noserialize string 55 /// "noserialize" string UDA indicates that data should not be serialzied by serializer 56 auto hasNoserializeUda(Args...)(){ 57 bool hasMalloc=false; 58 foreach(Arg;Args){ 59 static if(is(typeof(Arg)==string) && Arg=="noserialize"){ 60 hasMalloc=true; 61 } 62 } 63 return hasMalloc; 64 } 65 66 bool isStringVector(T)(){ 67 static if(is( Unqual!(ForeachType!T)==char )){ 68 return true; 69 }else{ 70 return false; 71 } 72 } 73 /// Checks if type can be treated as vector ex. replace int[] with MyArray!int 74 bool isCustomVector(T)(){ 75 static if(is(T==struct) && hasMember!(T,"opOpAssign") && hasMember!(T,"opIndex") && hasMember!(T,"length")){ 76 return true; 77 }else{ 78 return false; 79 } 80 } 81 /// Checks if type can be treated as map 82 bool isCustomMap(T)(){ 83 static if(is(T==struct) && hasMember!(T,"byKey") && hasMember!(T,"byValue") && hasMember!(T,"byKeyValue") && hasMember!(T,"add") && hasMember!(T,"isIn")){ 84 return true; 85 }else{ 86 return false; 87 } 88 } 89 ///Returns Load.yes when load is Load.yes or Load.skip 90 Load loadOrSkip(Load load)(){ 91 static if(load==Load.yes || load==Load.skip){ 92 return Load.yes; 93 }else{ 94 return load; 95 96 } 97 } 98 99 void commonSerialize(Load load,bool useMalloc=false, Serializer, T, ContainerOrSlice)(Serializer ser, ref T var,ref ContainerOrSlice con){ 100 static if (__traits(compiles,var.beforeSerialize!(load)(ser,con))) { 101 var.beforeSerialize!(load)(ser,con); 102 } 103 104 static if (hasMember!(T, "customSerialize" )) { 105 var.customSerialize!(load)(ser, con); 106 } else static if (isBasicType!T) { 107 ser.serializeBasicVar!(load)(var, con); 108 } else static if(isCustomVector!T){ 109 ser.serializeCustomVector!(load)(var, con); 110 } else static if(isCustomMap!T){ 111 ser.serializeCustomMap!(load)(var, con); 112 } else static if (is(T == struct)) { 113 ser.serializeStruct!(load)(var, con); 114 } else static if (isStaticArray!T) { 115 ser.serializeStaticArray!(load)(var, con); 116 }else static if (useMalloc && isMallocType!T) { 117 static if(isDynamicArray!T){ 118 ser.serializeDynamicArray!(load)(var, con); 119 }else static if(is(T==class)){ 120 ser.serializeClass!(load)(var, con); 121 }else static if(isPointer!T){ 122 ser.serializePointer!(load,useMalloc)(var, con); 123 }else{ 124 static assert(0); 125 } 126 } else static if (!useMalloc && isMallocType!T) { 127 //don't save, leave default value 128 }else static if (is(T==interface)) { 129 //don't save, leave default value 130 } else{ 131 static assert(0, "Type can not be serialized"); 132 } 133 134 static if (__traits(compiles,var.afterSerialize!(load)(ser,con))) { 135 var.afterSerialize!(load)(ser,con); 136 } 137 } 138 139 /// Struct to let BoundsChecking Without GC 140 struct NoGcSlice(T){ 141 shared static immutable Exception e=new Exception("BoundsChecking NoGcException"); 142 T slice; 143 alias slice this; 144 import std.traits; 145 146 //prevent NoGcSlice in NoGcSlice, NoGcSlice!(NoGcSlice!(T)) 147 static if( !hasMember!(T, "slice")){ 148 T opSlice(X,Y)(X start, Y end){ 149 if(start>=slice.length || end>slice.length){ 150 //assert(0); 151 throw e; 152 } 153 return slice[start..end]; 154 } 155 156 157 size_t opDollar() { return slice.length; } 158 } 159 } 160 161 162 void commonSerializePointer(Load load,bool useMalloc, Serializer, T, ContainerOrSlice)(Serializer ser, ref T var,ref ContainerOrSlice con){ 163 static assert(isPointer!T); 164 alias PointTo=typeof(*var); 165 bool exists=var !is null; 166 ser.serializeImpl!(loadOrSkip!load)(exists, con); 167 if(!exists){ 168 return; 169 } 170 static if(load==Load.yes){ 171 if(var is null)var=Mallocator.instance.make!(PointTo); 172 }else static if(load==Load.skip){ 173 __gshared static PointTo helperObj; 174 T beforeVar=var; 175 if(var is null)var=&helperObj; 176 } 177 ser.serializeImpl!(load,useMalloc)(*var,con); 178 static if(load==Load.skip){ 179 var=beforeVar; 180 } 181 182 } 183 184 185 void writelnTokens(TokenData[] tokens){ 186 writeln("--------"); 187 foreach(tk;tokens){ 188 writeln(tk); 189 } 190 } 191 192 193 void tokensToCharVectorPreatyPrint(Lexer,Vec)(TokenData[] tokens, ref Vec vec){ 194 __gshared static string spaces=" "; 195 int level=0; 196 void addSpaces(){ 197 vec~=cast(char[])spaces[0..level*4]; 198 } 199 200 foreach(tk;tokens){ 201 if(tk.isChar('{')){ 202 Lexer.toChars(tk,vec); 203 level++; 204 vec~='\n'; 205 addSpaces(); 206 }else if(tk.isChar('}')){ 207 level--; 208 vec~='\n'; 209 addSpaces(); 210 Lexer.toChars(tk,vec); 211 }else if(tk.isChar(',')){ 212 Lexer.toChars(tk,vec); 213 vec~='\n'; 214 addSpaces(); 215 }else{ 216 Lexer.toChars(tk,vec); 217 } 218 219 } 220 } 221 222 223 224 //----------------------------------------- 225 //--- Helper methods for string format 226 //----------------------------------------- 227 228 229 void serializeCustomVectorString(Load load, T, ContainerOrSlice)(ref T var, ref ContainerOrSlice con){ 230 alias ElementType=Unqual!(ForeachType!(T)); 231 static assert(isCustomVector!T); 232 static assert(is(ElementType==char)); 233 234 static if(load==Load.yes){ 235 var~=cast(char[])con[0].getUnescapedString; 236 con=con[1..$]; 237 }else{ 238 TokenData token; 239 token=cast(string)var[]; 240 token.type=StandardTokens.string_; 241 con ~= token; 242 } 243 } 244 245 void serializeCharToken(Load load, ContainerOrSlice)(char ch,ref ContainerOrSlice con){ 246 static if (load == Load.yes) { 247 //writelnTokens(con); 248 check(con[0].type==StandardTokens.character && con[0].isChar(ch)); 249 con=con[1..$]; 250 } else { 251 TokenData token; 252 token=ch; 253 con ~= token; 254 } 255 } 256 257 void ignoreBraces(Load load, ContainerOrSlice)(ref ContainerOrSlice con, char braceStart, char braceEnd){ 258 static assert(load==Load.yes ); 259 assert(con[0].isChar(braceStart)); 260 con=con[1..$]; 261 int nestageLevel=1; 262 while(con.length>0){ 263 TokenData token=con[0]; 264 con=con[1..$]; 265 if(token.type!=StandardTokens.character){ 266 continue; 267 } 268 if(token.isChar(braceStart)){ 269 nestageLevel++; 270 }else if(token.isChar(braceEnd)){ 271 nestageLevel--; 272 if(nestageLevel==0){ 273 break; 274 } 275 } 276 } 277 } 278 279 void ignoreToMatchingComma(Load load, ContainerOrSlice)(ref ContainerOrSlice con){ 280 static assert(load==Load.yes ); 281 int nestageLevel=0; 282 //writelnTokens(con); 283 //scope(exit)writelnTokens(con); 284 while(con.length>0){ 285 TokenData token=con[0]; 286 if(token.type!=StandardTokens.character){ 287 con=con[1..$]; 288 continue; 289 } 290 if(token.isChar('[') || token.isChar('{')){ 291 nestageLevel++; 292 }else if(token.isChar(']') || token.isChar('}')){ 293 nestageLevel--; 294 } 295 296 if(nestageLevel==0 && token.isChar(',')){ 297 break; 298 }else{ 299 con=con[1..$]; 300 } 301 } 302 }