1 module cerealed.decerealiser;
2
3 public import cerealed.cereal;
4 public import cerealed.attrs;
5 public import cerealed.traits;
6 import std.traits;
7
8
9 auto decerealise(T)(in ubyte[] bytes) {
10 return Decerealiser(bytes).value!T;
11 }
12
13
14 struct Decerealiser {
15 //interface:
16 enum type = CerealType.ReadBytes;
17
18 void grainUByte(ref ubyte val) @safe {
19 val = _bytes[0];
20 _bytes = _bytes[1..$];
21 }
22
23 void grainBits(ref uint value, int bits) @safe {
24 value = readBits(bits);
25 }
26
27 void grainClass(T)(T val) @trusted if(is(T == class)) {
28 grainClassImpl(this, val);
29 }
30
31 auto grainRaw(size_t length) @safe {
32 auto res = _bytes[0..length];
33 _bytes = _bytes[length..$];
34 return res;
35 }
36
37 //specific:
38 this(T)(in T[] bytes) @safe if(isNumeric!T) {
39 setBytes(bytes);
40 }
41
42 const(ubyte[]) bytes() const nothrow @property @safe {
43 return _bytes;
44 }
45
46 ulong bytesLeft() const @safe { return bytes.length; }
47
48 @property @safe T value(T)() if(!isDynamicArray!T && !isAssociativeArray!T &&
49 !is(T == class)) {
50 T val;
51 grain(this, val);
52 return val;
53 }
54
55 @property @trusted T value(T, A...)(A args) if(is(T == class)) {
56 auto val = new T(args);
57 grain(this, val);
58 return val;
59 }
60
61 @property @safe T value(T, U = short)() if(isDynamicArray!T || isAssociativeArray!T) {
62 T val;
63 grain!(typeof(this), T, U)(this, val);
64 return val;
65 }
66
67 void reset() @safe {
68 /**resets the decerealiser to read from the beginning again*/
69 reset(_originalBytes);
70 }
71
72 void reset(T)(in T[] bytes) @safe if(isNumeric!T) {
73 /**resets the decerealiser to use the new slice*/
74 _bitIndex = 0;
75 _currentByte = 0;
76 setBytes(bytes);
77 }
78
79 void read(T)(ref T val) @trusted {
80 grain(this, val);
81 }
82
83 uint readBits(int bits) @safe {
84 if(_bitIndex == 0) {
85 _currentByte = this.value!ubyte;
86 }
87
88 return readBitsHelper(bits);
89 }
90
91 const(ubyte)[] originalBytes() @safe pure nothrow const {
92 return _originalBytes;
93 }
94
95 private:
96
97 const (ubyte)[] _originalBytes;
98 const (ubyte)[] _bytes;
99 ubyte _currentByte;
100 int _bitIndex;
101
102 uint readBitsHelper(int bits) @safe {
103 enum bitsInByte = 8;
104 if(_bitIndex + bits > bitsInByte) { //have to carry on to the next byte
105 immutable bits1stTime = bitsInByte - _bitIndex; //what's left of this byte
106 immutable bits2ndTime = (_bitIndex + bits) - bitsInByte; //bits to read from next byte
107 immutable value1 = readBitsHelper(bits1stTime);
108 _bitIndex = 0;
109 _currentByte = this.value!ubyte;
110 immutable value2 = readBitsHelper(bits2ndTime);
111 return (value1 << bits2ndTime) | value2;
112 }
113
114 _bitIndex += bits;
115
116 auto shift = _currentByte >> (bitsInByte - _bitIndex);
117 return shift & (0xff >> (bitsInByte - bits));
118 }
119
120 void setBytes(T)(in T[] bytes) @trusted if(isNumeric!T) {
121 static if(is(T == ubyte)) {
122 _bytes = bytes;
123 } else {
124 foreach(b; bytes) _bytes ~= cast(ubyte)b;
125 }
126
127 _originalBytes = _bytes;
128 }
129
130 static assert(isCereal!Decerealiser);
131 static assert(isDecerealiser!Decerealiser);
132 }