1 module cerealed.cerealiser; 2 3 4 public import cerealed.cereal; 5 public import cerealed.attrs; 6 public import cerealed.traits; 7 import cerealed.range; 8 import std.traits; 9 import std.exception; 10 import std.array; 11 12 13 alias AppenderCerealiser = CerealiserImpl!(Appender!(ubyte[])); 14 alias DynamicArrayCerealiser = CerealiserImpl!DynamicArrayRange; 15 alias ScopeBufferCerealiser = CerealiserImpl!ScopeBufferRange; 16 17 alias Cerealiser = AppenderCerealiser; //the default, easy option 18 19 /** 20 * Uses a ScopeBufferCerealiaser to write the bytes. The reason 21 * it takes a function as a template parameter is to be able 22 * to do something with the bytes. The bytes shouldn't be used 23 * directly because once the function exits that is no longer 24 * valid memory (it's been popped off the stack or freed). 25 */ 26 auto cerealise(alias F, ushort N = 32, T)(auto ref T val) @system { 27 static assert(N % 2 == 0, "cerealise must be passed an even number of bytes"); 28 ubyte[N] buf = void; 29 auto sbufRange = ScopeBufferRange(buf); 30 scope(exit) sbufRange.free(); 31 auto enc = ScopeBufferCerealiser(sbufRange); 32 enc ~= val; 33 static if(is(ReturnType!F == void)) { 34 F(enc.bytes); 35 } else { 36 return F(enc.bytes); 37 } 38 } 39 40 /** 41 * Slower version of $(D cerealise) that returns a ubyte slice. 42 * It's preferable to use the version with the lambda template alias 43 */ 44 ubyte[] cerealise(T)(auto ref T val) { 45 auto enc = Cerealiser(); 46 enc ~= val; 47 return enc.bytes.dup; 48 } 49 50 alias cerealize = cerealise; 51 52 struct CerealiserImpl(R) if(isCerealiserRange!R) { 53 //interface 54 enum type = CerealType.WriteBytes; 55 56 void grainUByte(ref ubyte val) @trusted { 57 _output.put(val); 58 } 59 60 void grainBits(ref uint value, int bits) @safe { 61 writeBits(value, bits); 62 } 63 64 void grainClass(T)(T val) @trusted if(is(T == class)) { 65 if(val.classinfo.name in _childCerealisers) { 66 _childCerealisers[val.classinfo.name](this, val); 67 } else { 68 grainClassImpl(this, val); 69 } 70 } 71 72 void grainRaw(ubyte[] val) @trusted { 73 _output.put(val); 74 } 75 76 //specific: 77 this(R r) { 78 _output = r; 79 } 80 81 const(ubyte[]) bytes() const nothrow @property @safe { 82 return _output.data; 83 } 84 85 ref CerealiserImpl opOpAssign(string op : "~", T)(T val) @safe { 86 write(val); 87 return this; 88 } 89 90 void write(T)(T val) @safe if(!isArray!T && !isAssociativeArray!T) { 91 Unqual!T lval = val; 92 grain(this, lval); 93 } 94 95 void write(T)(const ref T val) @safe if(!isDynamicArray!T && 96 !isAssociativeArray!T && 97 !isAggregateType!T) { 98 T lval = val; 99 grain(this, lval); 100 } 101 102 void write(T)(const(T)[] val) @trusted { 103 auto lval = cast(T[])val.dup; 104 grain(this, lval); 105 } 106 107 void write(K, V)(const(V[K]) val) @trusted { 108 auto lval = cast(V[K])val.dup; 109 grain(this, lval); 110 } 111 112 void writeBits(in int value, in int bits) @safe { 113 import std.conv; 114 enforce(value < (1 << bits), text("value ", value, " too big for ", bits, " bits")); 115 enum bitsInByte = 8; 116 if(_bitIndex + bits >= bitsInByte) { //carries over to next byte 117 const remainingBits = _bitIndex + bits - bitsInByte; 118 const thisByteValue = (value >> remainingBits); 119 _currentByte |= thisByteValue; 120 grainUByte(_currentByte); 121 _currentByte = 0; 122 _bitIndex = 0; 123 if(remainingBits > 0) { 124 ubyte remainingValue = value & (0xff >> (bitsInByte - remainingBits)); 125 writeBits(remainingValue, remainingBits); 126 } 127 return; 128 } 129 _currentByte |= (value << (bitsInByte - bits - _bitIndex)); 130 _bitIndex += bits; 131 } 132 133 void reset() @safe { 134 _output.clear(); 135 } 136 137 static void registerChildClass(T)() @safe { 138 _childCerealisers[T.classinfo.name] = (ref Cerealiser cereal, Object val) { 139 T child = cast(T)val; 140 cereal.grainClassImpl(child); 141 }; 142 } 143 144 private: 145 146 R _output; 147 ubyte _currentByte; 148 int _bitIndex; 149 alias ChildCerealiser = void function(ref CerealiserImpl cereal, Object val); 150 static ChildCerealiser[string] _childCerealisers; 151 152 static assert(isCereal!CerealiserImpl); 153 static assert(isCerealiser!CerealiserImpl); 154 }