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 }