1 module utils.binary;
2 
3 import
4 		std.conv,
5 		std.range,
6 		std.traits,
7 		std.typecons,
8 		std.algorithm,
9 		std.typetuple,
10 
11 		utils.misc,
12 		utils.except,
13 		utils.binary.helpers;
14 
15 public import
16 				utils.binary.funcs,
17 				utils.binary.readers;
18 
19 
20 struct BinaryReader(Reader)
21 {
22 	this(A...)(auto ref A args)
23 	{
24 		reader = Reader(args);
25 	}
26 
27 	auto read(T)(string f = __FILE__, uint l = __LINE__) if(is(T == struct))
28 	{
29 		_l = l;
30 		_f = f;
31 		_info = T.stringof;
32 
33 		T t;
34 		process(t, t, t);
35 		return t;
36 	}
37 
38 	ref write(T)(auto ref in T t, string f = __FILE__, uint l = __LINE__) if(is(T == struct))
39 	{
40 		_l = l;
41 		_f = f;
42 		_info = T.stringof;
43 
44 		process!true(t, t, t);
45 		return this;
46 	}
47 
48 	Reader reader;
49 private:
50 	debug
51 	{
52 		enum errorRead = `throwError!"can't read %s.%s variable"(_f, _l, _info, name)`;
53 		enum errorWrite = `throwError!"can't write %s.%s variable"(_f, _l, _info, name)`;
54 		enum errorRSkip = `throwError!"can't skip when reading %s.%s variable"(_f, _l, _info, name)`;
55 		enum errorWSkip = `throwError!"can't skip when writing %s.%s variable"(_f, _l, _info, name)`;
56 		enum errorCheck = `throwError!"variable %s.%s mismatch(%s when %s expected)"(_f, _l, _info, name, tmp, *p)`;
57 		enum errorValid = `throwError!"variable %s.%s has invalid value %s"(_f, _l, _info, name, *p)`;
58 	}
59 	else
60 	{
61 		enum errorRead = `throwError!"can't read %s"(_f, _l, _info)`;
62 		enum errorWrite = `throwError!"can't write %s"(_f, _l, _info)`;
63 		enum errorRSkip = errorRead;
64 		enum errorWSkip = errorWrite;
65 		enum errorCheck = errorRead;
66 		enum errorValid = errorRead;
67 	}
68 
69 	enum checkLength = `E.sizeof * elemsCnt < 512 * 1024 * 1024 || throwError!"length of %s.%s variable is too big(%u)"(_f, _l, _info, name, elemsCnt);`;
70 
71 	void process(bool isWrite = false, T, S, P)(ref T data, ref S st, ref P parent)
72 	{
73 		foreach(name; aliasSeqOf!(fieldsToProcess!T()))
74 		{
75 			enum Elem = T.stringof ~ `.` ~ name;
76 
77 			alias attrs = TypeTuple!(__traits(getAttributes, __traits(getMember, T, name)));
78 
79 			debug
80 			{
81 				enum att = checkAttrs(attrs);
82 
83 				static assert(!att.length, Elem ~ ` has invalid attribute ` ~ att);
84 			}
85 
86 			auto p = &__traits(getMember, data, name);
87 			alias R = typeof(*p);
88 
89 			{
90 				enum idx = staticIndexOf!(`skip`, attrs);
91 
92 				static if(idx >= 0)
93 				{
94 					size_t cnt = StructExecuter!(attrs[idx + 1])(data, st, parent, reader);
95 
96 					static if(isWrite)
97 					{
98 						reader.wskip(cnt) || mixin(errorWSkip);
99 					}
100 					else
101 					{
102 						reader.rskip(cnt) || mixin(errorRSkip);
103 					}
104 				}
105 			}
106 
107 			{
108 				enum idx = staticIndexOf!(`ignoreif`, attrs);
109 
110 				static if(idx >= 0)
111 				{
112 					auto v = StructExecuter!(attrs[idx + 1])(data, st, parent, reader);
113 
114 					if(v)
115 					{
116 						static if(!isWrite)
117 						{
118 							enum def = staticIndexOf!(`default`, attrs);
119 
120 							static if(def >= 0)
121 							{
122 								*p = StructExecuter!(attrs[def + 1])(data, st, parent, reader);
123 							}
124 						}
125 
126 						continue;
127 					}
128 				}
129 			}
130 
131 			static if(!isWrite)
132 			{
133 				static if(is(R == immutable))
134 				{
135 					Unqual!R tmp;
136 					auto varPtr = &tmp;
137 				}
138 				else
139 				{
140 					alias varPtr = p;
141 				}
142 			}
143 
144 			static if(isDataSimple!R)
145 			{
146 				static if(isWrite)
147 				{
148 					reader.write(toByte(*p)) || mixin(errorWrite);
149 				}
150 				else
151 				{
152 					reader.read(toByte(*varPtr)) || mixin(errorRead);
153 				}
154 			}
155 			else static if(isAssociativeArray!R)
156 			{
157 				struct Pair
158 				{
159 					Unqual!(KeyType!R) key;
160 					Unqual!(ValueType!R) value;
161 				}
162 
163 				struct AA
164 				{
165 					mixin(`@(` ~ [ attrs ].to!string[1..$ - 1] ~ `) Pair[] ` ~ name ~ `;`);
166 				}
167 
168 				AA aa;
169 				auto arr = &aa.tupleof[0];
170 
171 				static if(isWrite)
172 				{
173 					*arr = p.byKeyValue.map!(a => Pair(a.key, a.value)).array;
174 				}
175 
176 				process!isWrite(aa, st, data);
177 
178 				static if(!isWrite)
179 				{
180 					*p = map!(a => tuple(a.tupleof))(*arr).assocArray;
181 				}
182 			}
183 			else static if(isArray!R)
184 			{
185 				alias E = ElementEncodingType!R;
186 
187 				enum isElemSimple = isDataSimple!E;
188 				enum lenIdx = staticIndexOf!(`length`, attrs);
189 
190 				static assert(isElemSimple || is(E == struct), `can't serialize ` ~ Elem);
191 
192 				static if(lenIdx >= 0)
193 				{
194 					uint elemsCnt = StructExecuter!(attrs[lenIdx + 1])(data, st, parent, reader);
195 
196 					static if(isWrite)
197 					{
198 						assert(p.length == elemsCnt);
199 					}
200 
201 					enum isRest = false;
202 				}
203 				else
204 				{
205 					static if(staticIndexOf!(`ubyte`, attrs) >= 0)			alias L = ubyte;
206 					else static if(staticIndexOf!(`ushort`, attrs) >= 0)	alias L = ushort;
207 					else static if(staticIndexOf!(`uint`, attrs) >= 0)		alias L = uint;
208 					else static if(staticIndexOf!(`ulong`, attrs) >= 0)		alias L = ulong;
209 
210 					static if(is(L))
211 					{
212 						L elemsCnt;
213 
214 						static if(isWrite)
215 						{
216 							assert(p.length <= L.max);
217 
218 							elemsCnt = cast(L)p.length;
219 							reader.write(elemsCnt.toByte) || mixin(errorWrite);
220 						}
221 						else
222 						{
223 							reader.read(elemsCnt.toByte) || mixin(errorRead);
224 						}
225 
226 						enum isRest = false;
227 					}
228 					else
229 					{
230 						enum isRest = staticIndexOf!(`rest`, attrs) >= 0;
231 					}
232 				}
233 
234 				enum isStr = is(R : string);
235 				enum isLen = is(typeof(elemsCnt));
236 				enum isDyn = isDynamicArray!R;
237 
238 				static if(isDyn)
239 				{
240 					static assert(isStr || isLen || isRest, `length of ` ~ Elem ~ ` is unknown`);
241 				}
242 				else
243 				{
244 					static assert(!(isLen || isRest), `static array ` ~ Elem ~ ` can't have a length`);
245 				}
246 
247 				static if(isElemSimple)
248 				{
249 					static if(isWrite)
250 					{
251 						reader.write(toByte(*p)) || mixin(errorWrite);
252 
253 						static if(isStr && !isLen)
254 						{
255 							reader.wskip(1) || mixin(errorWSkip);
256 						}
257 					}
258 					else
259 					{
260 						static if(isStr && !isLen)
261 						{
262 							reader.readstr(*varPtr) || mixin(errorRead);
263 						}
264 						else
265 						{
266 							ubyte[] arr;
267 
268 							static if(isRest)
269 							{
270 								!(reader.length % E.sizeof) && reader.read(arr, cast(uint)reader.length) || mixin(errorRead);
271 							}
272 							else
273 							{
274 								mixin(checkLength);
275 
276 								reader.read(arr, elemsCnt * cast(uint)E.sizeof) || mixin(errorRead);
277 							}
278 
279 							*varPtr = (cast(E *)arr.ptr)[0..arr.length / E.sizeof];
280 						}
281 					}
282 				}
283 				else
284 				{
285 					debug
286 					{
287 						auto old = _info;
288 						_info ~= `.` ~ name;
289 					}
290 
291 					static if(isWrite)
292 					{
293 						foreach(ref v; *p)
294 						{
295 							process!isWrite(v, st, data);
296 						}
297 					}
298 					else
299 					{
300 						static if(isRest)
301 						{
302 							while(reader.length)
303 							{
304 								E v;
305 								process!isWrite(v, st, data);
306 
307 								*varPtr ~= v;
308 							}
309 						}
310 						else
311 						{
312 							static if(isDyn)
313 							{
314 								mixin(checkLength);
315 
316 								*varPtr = new E[elemsCnt];
317 							}
318 
319 							foreach(ref v; *varPtr)
320 							{
321 								process!isWrite(v, st, data);
322 							}
323 						}
324 					}
325 
326 					debug
327 					{
328 						_info = old;
329 					}
330 				}
331 			}
332 			else
333 			{
334 				debug
335 				{
336 					auto old = _info;
337 					_info ~= `.` ~ name;
338 				}
339 
340 				process!isWrite(*p, st, data);
341 
342 				debug
343 				{
344 					_info = old;
345 				}
346 			}
347 
348 			static if(!isWrite)
349 			{
350 				static if(is(typeof(tmp)))
351 				{
352 					tmp == *p || mixin(errorCheck);
353 				}
354 
355 				enum idx = staticIndexOf!(`validif`, attrs);
356 
357 				static if(idx >= 0)
358 				{
359 					StructExecuter!(attrs[idx + 1])(data, st, parent, reader) || mixin(errorValid);
360 				}
361 			}
362 		}
363 	}
364 
365 	uint _l;
366 
367 	string
368 			_f,
369 			_info;
370 }