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 }