1 /// \file BinBuffer.d 2 3 module tagion.betterC.utils.BinBuffer; 4 5 @nogc: 6 import tagion.betterC.utils.platform; 7 8 //import core.stdc.stdlib : calloc, malloc, realloc, free; 9 version (WebAssembly) { 10 } 11 else { 12 import std.bitmanip : nativeToBigEndian, nativeToLittleEndian; 13 } 14 import std.traits : Unqual, isArray, isNumeric; 15 import tagion.betterC.utils.Memory; 16 import tagion.betterC.utils.sdt; 17 18 struct BinBuffer { 19 @nogc: 20 protected { 21 ubyte[] _data; 22 size_t _index; 23 } 24 25 enum DEFAULT_SIZE = 256; 26 this(const size_t size) { 27 if (size > 0) { 28 _data.create(size); 29 } 30 } 31 32 @trusted ~this() { 33 dispose; 34 } 35 36 void dispose() { 37 _data.dispose; 38 scope (exit) { 39 _index = 0; 40 } 41 } 42 43 void recreate(const size_t size) { 44 if (_data !is null) { 45 dispose; 46 } 47 if (size > 0) { 48 _data = create!(ubyte[])(size); 49 } 50 } 51 52 private void append(scope const(ubyte[]) add, size_t* index) { 53 if (_data is null) { 54 const new_size = (add.length < DEFAULT_SIZE) ? DEFAULT_SIZE : add.length; 55 _data = create!(ubyte[])(new_size); 56 } 57 scope (exit) { 58 *index += add.length; 59 _index = *index; 60 } 61 if (*index + add.length > _data.length) { 62 const new_size = _data.length + ((add.length < DEFAULT_SIZE) ? DEFAULT_SIZE : add 63 .length); 64 _data.resize(new_size); 65 } 66 _data[*index .. *index + add.length] = add[0 .. $]; 67 } 68 69 private void write(T)(const T x, size_t* index) if (isNumeric!T || is(Unqual!(T) == bool)) { 70 version (WebAssembly) { 71 auto res = (cast(ubyte*)&x)[0 .. T.sizeof]; 72 append(res, index); 73 } 74 else { 75 auto res = nativeToLittleEndian(x); 76 append(res, index); 77 } 78 } 79 80 private void write(const(ubyte[]) x, size_t* index) { 81 append(x, index); 82 } 83 84 private void write(T)(T x, size_t* index) if (isArray!T) { 85 append(cast(ubyte[]) x, index); 86 } 87 88 // private void write(sdt_t sdt, size_t* index) { 89 // write(utc.time, index); 90 // } 91 92 void write(T)(T x) { 93 write(x, &_index); 94 } 95 96 // version(none) 97 void write(T)(T x, const size_t index) { 98 size_t previous_index = _index; 99 size_t temp_index = index; 100 write(x, &temp_index); 101 if (temp_index > _index) { 102 _index = temp_index; 103 } 104 else { 105 _index = previous_index; 106 } 107 } 108 109 BinBuffer opSlice(const size_t from, const size_t to) const 110 in { 111 assert(from <= to); 112 assert(to <= _data.length); 113 } 114 do { 115 auto result = BinBuffer(to - from); 116 result.write(_data[from .. to]); 117 return result; 118 } 119 120 @property size_t opDollar(size_t dim : 0)() const pure { 121 return _index; 122 } 123 124 @property size_t length() const pure { 125 return _index; 126 } 127 128 immutable(ubyte[]) serialize() const { 129 return cast(immutable) _data[0 .. _index]; 130 } 131 } 132 133 unittest { 134 string text = "text"; 135 auto buf = BinBuffer(100); 136 137 buf.write(42); 138 size_t size = int.sizeof; 139 assert(buf.length == size); 140 buf.write(10.1); 141 size += double.sizeof; 142 assert(buf.length == size); 143 buf.write(text); 144 size += text.length; 145 assert(buf.length == size); 146 ubyte x = 7; 147 buf.write(x); 148 size += ubyte.sizeof; 149 assert(buf.length == size); 150 assert(size == 17); 151 152 ubyte[17] check; 153 154 size = 0; 155 check[size .. size + int.sizeof] = nativeToLittleEndian(42); 156 size += int.sizeof; 157 check[size .. size + double.sizeof] = nativeToLittleEndian(10.1); 158 size += double.sizeof; 159 check[size .. size + text.length] = cast(const(ubyte[])) text; 160 size += text.length; 161 check[size] = x; 162 163 foreach (i, a; buf.serialize) { 164 assert(a == check[i]); 165 } 166 assert(check == buf.serialize); 167 }