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