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 }