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