Branch data Line data Source code
1 : : /**
2 : : * @file bitstream.c
3 : : * @brief Reader/writer construction, destruction, and common result helpers.
4 : : */
5 : :
6 : : #include "bitstream_internal.h"
7 : : #include "triepack/triepack_version.h"
8 : :
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : :
12 : : /* ── Result string ───────────────────────────────────────────────────── */
13 : :
14 : 25 : const char *tp_result_str(tp_result result)
15 : : {
16 [ + + + + : 25 : switch (result) {
+ + + + +
+ + + + +
+ + + +
+ ]
17 : 3 : case TP_OK:
18 : 3 : return "OK";
19 : 2 : case TP_ERR_EOF:
20 : 2 : return "read past end of stream";
21 : 1 : case TP_ERR_ALLOC:
22 : 1 : return "memory allocation failed";
23 : 2 : case TP_ERR_INVALID_PARAM:
24 : 2 : return "invalid parameter";
25 : 1 : case TP_ERR_INVALID_POSITION:
26 : 1 : return "seek beyond bounds";
27 : 1 : case TP_ERR_NOT_ALIGNED:
28 : 1 : return "not byte-aligned";
29 : 1 : case TP_ERR_OVERFLOW:
30 : 1 : return "varint overflow";
31 : 1 : case TP_ERR_INVALID_UTF8:
32 : 1 : return "invalid UTF-8";
33 : 1 : case TP_ERR_BAD_MAGIC:
34 : 1 : return "bad magic bytes";
35 : 1 : case TP_ERR_VERSION:
36 : 1 : return "unsupported version";
37 : 1 : case TP_ERR_CORRUPT:
38 : 1 : return "data corrupt";
39 : 2 : case TP_ERR_NOT_FOUND:
40 : 2 : return "key not found";
41 : 1 : case TP_ERR_TRUNCATED:
42 : 1 : return "data truncated";
43 : 1 : case TP_ERR_ALPHABET:
44 : 1 : return "keys use too many distinct byte values";
45 : 1 : case TP_ERR_UNSUPPORTED:
46 : 1 : return "operation not implemented";
47 : 1 : case TP_ERR_JSON_SYNTAX:
48 : 1 : return "JSON syntax error";
49 : 1 : case TP_ERR_JSON_DEPTH:
50 : 1 : return "JSON nesting too deep";
51 : 1 : case TP_ERR_JSON_TYPE:
52 : 1 : return "JSON type mismatch";
53 : 2 : default:
54 : 2 : return "unknown error";
55 : : }
56 : : }
57 : :
58 : : /* ── Version metadata ────────────────────────────────────────────────── */
59 : :
60 : 3 : tp_version_info tp_version(void)
61 : : {
62 : : tp_version_info v;
63 : 3 : v.name = "triepack";
64 : 3 : v.implementation = "c";
65 : 3 : v.version = TP_VERSION_STRING;
66 : 3 : v.version_major = (uint8_t)TP_VERSION_MAJOR;
67 : 3 : v.version_minor = (uint8_t)TP_VERSION_MINOR;
68 : 3 : v.version_patch = (uint8_t)TP_VERSION_PATCH;
69 : 3 : v.format_version_major = (uint8_t)TP_FORMAT_VERSION_MAJOR;
70 : 3 : v.format_version_minor = (uint8_t)TP_FORMAT_VERSION_MINOR;
71 : 3 : v.max_alphabet_size = (uint16_t)TP_MAX_ALPHABET_SIZE;
72 : 3 : return v;
73 : : }
74 : :
75 : : /* ── Value helpers ───────────────────────────────────────────────────── */
76 : :
77 : 47943 : tp_value tp_value_null(void)
78 : : {
79 : : tp_value v;
80 : 47943 : memset(&v, 0, sizeof(v));
81 : 47943 : v.type = TP_NULL;
82 : 47943 : return v;
83 : : }
84 : :
85 : 31 : tp_value tp_value_bool(bool val)
86 : : {
87 : : tp_value v;
88 : 31 : memset(&v, 0, sizeof(v));
89 : 31 : v.type = TP_BOOL;
90 : 31 : v.data.bool_val = val;
91 : 31 : return v;
92 : : }
93 : :
94 : 878 : tp_value tp_value_int(int64_t val)
95 : : {
96 : : tp_value v;
97 : 878 : memset(&v, 0, sizeof(v));
98 : 878 : v.type = TP_INT;
99 : 878 : v.data.int_val = val;
100 : 878 : return v;
101 : : }
102 : :
103 : 11743 : tp_value tp_value_uint(uint64_t val)
104 : : {
105 : : tp_value v;
106 : 11743 : memset(&v, 0, sizeof(v));
107 : 11743 : v.type = TP_UINT;
108 : 11743 : v.data.uint_val = val;
109 : 11743 : return v;
110 : : }
111 : :
112 : 10 : tp_value tp_value_float32(float val)
113 : : {
114 : : tp_value v;
115 : 10 : memset(&v, 0, sizeof(v));
116 : 10 : v.type = TP_FLOAT32;
117 : 10 : v.data.float32_val = val;
118 : 10 : return v;
119 : : }
120 : :
121 : 41 : tp_value tp_value_float64(double val)
122 : : {
123 : : tp_value v;
124 : 41 : memset(&v, 0, sizeof(v));
125 : 41 : v.type = TP_FLOAT64;
126 : 41 : v.data.float64_val = val;
127 : 41 : return v;
128 : : }
129 : :
130 : 15 : tp_value tp_value_string(const char *str)
131 : : {
132 : : tp_value v;
133 : 15 : memset(&v, 0, sizeof(v));
134 : 15 : v.type = TP_STRING;
135 : 15 : v.data.string_val.str = str;
136 [ + + ]: 15 : v.data.string_val.str_len = str ? strlen(str) : 0;
137 : 15 : return v;
138 : : }
139 : :
140 : 59 : tp_value tp_value_string_n(const char *str, size_t len)
141 : : {
142 : : tp_value v;
143 : 59 : memset(&v, 0, sizeof(v));
144 : 59 : v.type = TP_STRING;
145 : 59 : v.data.string_val.str = str;
146 : 59 : v.data.string_val.str_len = len;
147 : 59 : return v;
148 : : }
149 : :
150 : 19 : tp_value tp_value_blob(const uint8_t *data, size_t len)
151 : : {
152 : : tp_value v;
153 : 19 : memset(&v, 0, sizeof(v));
154 : 19 : v.type = TP_BLOB;
155 : 19 : v.data.blob_val.data = data;
156 : 19 : v.data.blob_val.len = len;
157 : 19 : return v;
158 : : }
159 : :
160 : : /* ── Reader lifecycle ────────────────────────────────────────────────── */
161 : :
162 : : static const size_t DEFAULT_WRITER_CAP = 256;
163 : :
164 : 68664 : tp_result tp_bs_reader_create(tp_bitstream_reader **out, const uint8_t *buf, uint64_t bit_len)
165 : : {
166 [ + + ]: 68664 : if (!out)
167 : 3 : return TP_ERR_INVALID_PARAM;
168 : :
169 : : /* Allocation failure paths are excluded from coverage (LCOV_EXCL). */
170 : 68661 : tp_bitstream_reader *r = calloc(1, sizeof(*r));
171 : 68661 : if (!r) /* LCOV_EXCL_BR_LINE */
172 : : return TP_ERR_ALLOC; /* LCOV_EXCL_LINE */
173 : :
174 : 68661 : r->buf = buf;
175 : 68661 : r->bit_len = bit_len;
176 : 68661 : r->pos = 0;
177 : 68661 : r->order = TP_BIT_ORDER_MSB_FIRST;
178 : 68661 : r->owns_buf = false;
179 : 68661 : *out = r;
180 : 68661 : return TP_OK;
181 : : }
182 : :
183 : 9 : tp_result tp_bs_reader_create_copy(tp_bitstream_reader **out, const uint8_t *buf, uint64_t bit_len)
184 : : {
185 [ + + ]: 9 : if (!out)
186 : 3 : return TP_ERR_INVALID_PARAM;
187 : :
188 : 6 : size_t byte_len = (size_t)((bit_len + 7) / 8);
189 : : /* calloc, not malloc: a NULL source is a documented way to get a reader
190 : : over a zeroed buffer, and malloc would hand back uninitialised heap.
191 : : When there is a source it is copied over this anyway. */
192 : 6 : uint8_t *copy = calloc(1, byte_len);
193 [ - + - - ]: 6 : if (!copy && byte_len > 0)
194 : : return TP_ERR_ALLOC; /* LCOV_EXCL_LINE */
195 : :
196 [ + + + + ]: 6 : if (buf && byte_len > 0)
197 : 2 : memcpy(copy, buf, byte_len);
198 : :
199 : 6 : tp_bitstream_reader *r = calloc(1, sizeof(*r));
200 : 6 : if (!r) { /* LCOV_EXCL_BR_LINE */
201 : : /* LCOV_EXCL_START */
202 : : free(copy);
203 : : return TP_ERR_ALLOC;
204 : : /* LCOV_EXCL_STOP */
205 : : }
206 : :
207 : 6 : r->buf = copy;
208 : 6 : r->bit_len = bit_len;
209 : 6 : r->pos = 0;
210 : 6 : r->order = TP_BIT_ORDER_MSB_FIRST;
211 : 6 : r->owns_buf = true;
212 : 6 : *out = r;
213 : 6 : return TP_OK;
214 : : }
215 : :
216 : 68673 : tp_result tp_bs_reader_destroy(tp_bitstream_reader **reader)
217 : : {
218 [ + + ]: 68673 : if (!reader)
219 : 2 : return TP_ERR_INVALID_PARAM;
220 [ + + ]: 68671 : if (*reader) {
221 [ + + ]: 68667 : if ((*reader)->owns_buf)
222 : 6 : free((void *)(*reader)->buf);
223 : 68667 : free(*reader);
224 : 68667 : *reader = NULL;
225 : : }
226 : 68671 : return TP_OK;
227 : : }
228 : :
229 : 3 : tp_result tp_bs_reader_set_bit_order(tp_bitstream_reader *r, tp_bit_order order)
230 : : {
231 [ + + ]: 3 : if (!r)
232 : 2 : return TP_ERR_INVALID_PARAM;
233 : 1 : r->order = order;
234 : 1 : return TP_OK;
235 : : }
236 : :
237 : : /* ── Writer lifecycle ────────────────────────────────────────────────── */
238 : :
239 : 404 : tp_result tp_bs_writer_create(tp_bitstream_writer **out, size_t initial_cap, size_t growth)
240 : : {
241 [ + + ]: 404 : if (!out)
242 : 3 : return TP_ERR_INVALID_PARAM;
243 : :
244 : 401 : tp_bitstream_writer *w = calloc(1, sizeof(*w));
245 [ - + ]: 401 : if (!w)
246 : : return TP_ERR_ALLOC; /* LCOV_EXCL_LINE */
247 : :
248 [ + + ]: 401 : w->cap = initial_cap > 0 ? initial_cap : DEFAULT_WRITER_CAP;
249 : 401 : w->growth = growth;
250 : 401 : w->pos = 0;
251 : 401 : w->buf = calloc(1, w->cap);
252 : 401 : if (!w->buf) { /* LCOV_EXCL_BR_LINE */
253 : : /* LCOV_EXCL_START */
254 : : free(w);
255 : : return TP_ERR_ALLOC;
256 : : /* LCOV_EXCL_STOP */
257 : : }
258 : :
259 : 401 : *out = w;
260 : 401 : return TP_OK;
261 : : }
262 : :
263 : 407 : tp_result tp_bs_writer_destroy(tp_bitstream_writer **writer)
264 : : {
265 [ + + ]: 407 : if (!writer)
266 : 2 : return TP_ERR_INVALID_PARAM;
267 [ + + ]: 405 : if (*writer) {
268 : 401 : free((*writer)->buf);
269 : 401 : free(*writer);
270 : 401 : *writer = NULL;
271 : : }
272 : 405 : return TP_OK;
273 : : }
274 : :
275 : : /* ── Reader cursor ───────────────────────────────────────────────────── */
276 : :
277 : 85850 : uint64_t tp_bs_reader_position(const tp_bitstream_reader *r)
278 : : {
279 [ + + ]: 85850 : return r ? r->pos : 0;
280 : : }
281 : :
282 : 7 : uint64_t tp_bs_reader_remaining(const tp_bitstream_reader *r)
283 : : {
284 [ + + + + ]: 7 : if (!r || r->pos >= r->bit_len)
285 : 4 : return 0;
286 : 3 : return r->bit_len - r->pos;
287 : : }
288 : :
289 : 6 : uint64_t tp_bs_reader_length(const tp_bitstream_reader *r)
290 : : {
291 [ + + ]: 6 : return r ? r->bit_len : 0;
292 : : }
293 : :
294 : 123394 : tp_result tp_bs_reader_seek(tp_bitstream_reader *r, uint64_t bit_pos)
295 : : {
296 [ + + ]: 123394 : if (!r)
297 : 1 : return TP_ERR_INVALID_PARAM;
298 [ + + ]: 123393 : if (bit_pos > r->bit_len)
299 : 223 : return TP_ERR_INVALID_POSITION;
300 : 123170 : r->pos = bit_pos;
301 : 123170 : return TP_OK;
302 : : }
303 : :
304 : 586562 : tp_result tp_bs_reader_advance(tp_bitstream_reader *r, uint64_t n)
305 : : {
306 [ + + ]: 586562 : if (!r)
307 : 1 : return TP_ERR_INVALID_PARAM;
308 [ + + ]: 586561 : if (r->pos + n > r->bit_len)
309 : 416 : return TP_ERR_EOF;
310 : 586145 : r->pos += n;
311 : 586145 : return TP_OK;
312 : : }
313 : :
314 : 10455 : tp_result tp_bs_reader_align_to_byte(tp_bitstream_reader *r)
315 : : {
316 [ + + ]: 10455 : if (!r)
317 : 2 : return TP_ERR_INVALID_PARAM;
318 : 10453 : uint64_t rem = r->pos % 8;
319 [ + + ]: 10453 : if (rem != 0) {
320 : 7319 : uint64_t skip = 8 - rem;
321 [ + + ]: 7319 : if (r->pos + skip > r->bit_len)
322 : 3 : return TP_ERR_EOF;
323 : 7316 : r->pos += skip;
324 : : }
325 : 10450 : return TP_OK;
326 : : }
327 : :
328 : 10458 : bool tp_bs_reader_is_byte_aligned(const tp_bitstream_reader *r)
329 : : {
330 [ + + + + ]: 10458 : return r ? (r->pos % 8 == 0) : false;
331 : : }
332 : :
333 : : /* ── Writer position ─────────────────────────────────────────────────── */
334 : :
335 : 1387 : uint64_t tp_bs_writer_position(const tp_bitstream_writer *w)
336 : : {
337 [ + + ]: 1387 : return w ? w->pos : 0;
338 : : }
339 : :
340 : : /* ── Buffer access ───────────────────────────────────────────────────── */
341 : :
342 : 305 : tp_result tp_bs_writer_get_buffer(const tp_bitstream_writer *w, const uint8_t **buf,
343 : : uint64_t *bit_len)
344 : : {
345 [ + + + + : 305 : if (!w || !buf || !bit_len)
+ + ]
346 : 7 : return TP_ERR_INVALID_PARAM;
347 : 298 : *buf = w->buf;
348 : 298 : *bit_len = w->pos;
349 : 298 : return TP_OK;
350 : : }
351 : :
352 : 282 : tp_result tp_bs_writer_detach_buffer(tp_bitstream_writer *w, uint8_t **buf, size_t *byte_len,
353 : : uint64_t *bit_len)
354 : : {
355 [ + + + + : 282 : if (!w || !buf || !byte_len || !bit_len)
+ + + + ]
356 : 8 : return TP_ERR_INVALID_PARAM;
357 : 274 : *buf = w->buf;
358 : 274 : *byte_len = (size_t)((w->pos + 7) / 8);
359 : 274 : *bit_len = w->pos;
360 : :
361 : : /* Reset writer to empty state */
362 : 274 : w->buf = calloc(1, w->cap);
363 : 274 : if (!w->buf) { /* LCOV_EXCL_BR_LINE */
364 : : /* LCOV_EXCL_START */
365 : : w->cap = 0;
366 : : w->pos = 0;
367 : : return TP_ERR_ALLOC;
368 : : /* LCOV_EXCL_STOP */
369 : : }
370 : 274 : w->pos = 0;
371 : 274 : return TP_OK;
372 : : }
373 : :
374 : 14 : tp_result tp_bs_reader_get_buffer(const tp_bitstream_reader *r, const uint8_t **buf,
375 : : uint64_t *bit_len)
376 : : {
377 [ + + + + : 14 : if (!r || !buf || !bit_len)
+ + ]
378 : 4 : return TP_ERR_INVALID_PARAM;
379 : 10 : *buf = r->buf;
380 : 10 : *bit_len = r->bit_len;
381 : 10 : return TP_OK;
382 : : }
383 : :
384 : 70 : tp_result tp_bs_writer_to_reader(tp_bitstream_writer *w, tp_bitstream_reader **reader)
385 : : {
386 [ + + + + ]: 70 : if (!w || !reader)
387 : 4 : return TP_ERR_INVALID_PARAM;
388 : 66 : return tp_bs_reader_create(reader, w->buf, w->pos);
389 : : }
|