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 }