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 }