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 }