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 }