Endian-independent binary IO utilities for C
1#ifndef __bini__
2#define __bini__
3
4/* License at end of file. */
5
6/* Used to add functions for version-specific types. */
7#define bini_c 1989
8#ifdef __STDC_VERSION__
9# undef bini_c
10# if __STDC_VERSION__ == 199901
11# define bini_c 1999
12# elif __STDC_VERSION__ == 201112
13# define bini_c 2011
14# elif __STDC_VERSION__ == 201710
15# define bini_c 2017
16# elif __STDC_VERSION__ == 202000 || __STDC_VERSION__ == 202311
17# define bini_c 2023
18# else
19# warning "bini.h: unknown C version. Falling back to C89."
20# define bini_c 1989
21# endif
22#endif
23
24
25#include <stdint.h>
26#include <stddef.h>
27#include <stdarg.h>
28#include <stdio.h>
29#if bini_c >= 1999
30# include <stdbool.h>
31#endif
32
33
34#ifndef BINI_BUFSIZ
35/* Starting size for bini_stream buffers. */
36# define BINI_BUFSIZ 8192
37#endif
38
39#ifndef BINI_STRBUFSIZ
40/* Starting size for bini_*print buffers. */
41# define BINI_STRBUFSIZ 1024
42#endif
43
44
45/* Execute the `x` macro for each type that bini uses.
46 Excludes string types (char *). */
47#ifndef BINI_X
48# if bini_c >= 1999
49# define BINI_X(x) \
50 x(char, c) \
51 x(short, s) \
52 x(int, i) \
53 x(long, l) \
54 x(long long, ll) \
55 x(unsigned char, cu) \
56 x(unsigned short, su) \
57 x(unsigned int, iu) \
58 x(unsigned long, lu) \
59 x(unsigned long long, llu) \
60 x(float, f) \
61 x(double, d) \
62 x(bool, b)
63# else
64# define BINI_X(x) \
65 x(char, c) \
66 x(short, s) \
67 x(int, i) \
68 x(long, l) \
69 x(unsigned char, cu) \
70 x(unsigned short, su) \
71 x(unsigned int, iu) \
72 x(unsigned long, lu) \
73 x(float, f) \
74 x(double, d)
75# endif
76#endif
77
78
79enum { BINI_ERR, BINI_OK };
80enum { BINI_BIGENDIAN, BINI_LITTLEENDIAN };
81enum
82{
83 /* Read values from the end of the buffer. */
84 BINI_STACK,
85 /* Read values from the start of the buffer. */
86 BINI_STREAM
87};
88
89
90/* Represents a binary IO stream. */
91struct bini_stream
92{
93 /* Read mode for the stream. Should be either BINI_STACK or BINI_STREAM. */
94 int mode;
95 size_t cap;
96 size_t len;
97 uint8_t *buffer;
98};
99
100
101int bini_getendian(void);
102
103
104/* Initialize a bini_stream, allocating a buffer and
105 setting each field to its respective defaults. Also
106 see bini_new(). Free data using bini_end(). */
107int bini_init(struct bini_stream *);
108/* Create a new bini_stream, allocating a starting buffer
109 of BINI_BUFSIZ bytes. To use your own buffer, initialize
110 your own bini_stream and call bini_init() to fill in the
111 necessary fields. Free data using bini_close(). */
112struct bini_stream *bini_new(void);
113/* End a bini_stream, flushing it if need be and freeing
114 its buffer. */
115void bini_end(struct bini_stream *);
116/* End a bini_stream, flushing it if need be, freeing its
117 buffer, and frees the provided variable as well. */
118void bini_close(struct bini_stream *bs);
119
120/* Flush a binary stream, clearing its buffer. */
121int bini_flush(struct bini_stream *);
122/* Dump the binary stream's buffer to fp. */
123void bini_dump(FILE *fp, struct bini_stream *bs);
124
125/* Read data from a FILE into a binary stream. */
126size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp);
127/* Write data to a FILE into a binary stream. This will
128 NOT consume data from the stream! Stack/stream mode does
129 not affect this function.
130 To consume data, use `bs->len -= bini_fwrite(bs, ...);` */
131size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp);
132
133
134#define _bini_rwx(_type, _name) \
135 /* Write a `typeof(v)` to the stream. */ \
136 void bini_w##_name(struct bini_stream *bs, _type v); \
137 /* Read a `typeof(v)` from the stream. */ \
138 _type bini_r##_name(struct bini_stream *bs);
139BINI_X(_bini_rwx)
140#undef _bini_rwx
141
142void bini_wstrn(struct bini_stream *, const char *, size_t);
143void bini_wstr(struct bini_stream *, const char *);
144
145size_t bini_rstr(struct bini_stream *, char *);
146
147#if 0 /* unimplemented */
148/* Write to a binary stream. */
149int bini_vprint(struct bini_stream *bs, char *fmt, va_list va);
150/* Write to a binary stream. */
151int bini_print(struct bini_stream *bs, char *fmt, ...);
152
153/* Read from a binary stream. */
154int bini_vscan(struct bini_stream *bs, char *fmt, va_list va);
155/* Read from a binary stream. */
156int bini_scan(struct bini_stream *bs, char *fmt, ...);
157#endif
158
159
160/* Aliased names that are more akin to libc names. */
161#if defined(bini_libc_names)
162
163typedef struct bini_stream bstream;
164
165# define bnew bini_new
166# define binit bini_init
167# define bend bini_end
168# define bclose bini_close
169
170# define bflush bini_flush
171# define bdump bini_dump
172
173# define bputc bini_wc
174# define bputs bini_ws
175# define bputi bini_wi
176# define bputl bini_wl
177# define bputll bini_wll
178# define bputcu bini_wcu
179# define bputsu bini_wsu
180# define bputiu bini_wiu
181# define bputlu bini_wlu
182# define bputllu bini_wllu
183# define bputf bini_wf
184# define bputd bini_wd
185# define bputb bini_wb
186# define bputstr bini_wstr
187# define bputstrn bini_wstrn
188
189# define breadc bini_rc
190# define breads bini_rs
191# define breadi bini_ri
192# define breadl bini_rl
193# define breadll bini_rll
194# define breadcu bini_rcu
195# define breadsu bini_rsu
196# define breadiu bini_riu
197# define breadlu bini_rlu
198# define breadllu bini_rllu
199# define breadf bini_rf
200# define breadd bini_rd
201# define breadb bini_rb
202# define breadstr bini_rstr
203# define breadstrn bini_rstrn
204
205#if 0
206# define bfprint bini_vprint
207# define bprint bini_print
208# define bvscan bini_vscan
209# define bscan bini_scan
210#endif
211
212#endif
213
214
215#if defined(bini_impl)
216
217
218#include <stdlib.h>
219#include <string.h>
220
221
222static
223void _bini_dbg(char *fmt, ...)
224{
225# if bini_dbg
226 va_list ap;
227 va_start(ap, fmt);
228 vfprintf(stderr, fmt, ap);
229 va_end(ap);
230# else
231 (void)fmt;
232# endif
233}
234
235
236int bini_getendian(void)
237{
238 const int n = 1;
239 if (*(char *)&n == 1)
240 return BINI_LITTLEENDIAN;
241 return BINI_BIGENDIAN;
242}
243
244
245int bini_init(struct bini_stream *bs)
246{
247 bs->buffer = calloc(BINI_BUFSIZ, 1);
248 if (!bs->buffer)
249 return BINI_ERR;
250 bs->len = 0;
251 bs->cap = BINI_BUFSIZ;
252 bs->mode = BINI_STREAM;
253 return BINI_OK;
254}
255
256struct bini_stream *bini_new(void)
257{
258 struct bini_stream *bs = malloc(sizeof(struct bini_stream));
259 bini_init(bs);
260 return bs;
261}
262
263void bini_end(struct bini_stream *bs)
264{
265 bini_flush(bs);
266 free(bs->buffer);
267}
268
269void bini_close(struct bini_stream *bs)
270{
271 bini_end(bs);
272 free(bs);
273}
274
275int bini_flush(struct bini_stream *bs)
276{
277 size_t i;
278 for (i = 0 ; i < bs->len ; i++)
279 bs->buffer[i] = 0;
280 bs->len = 0;
281 return BINI_OK;
282}
283
284void bini_dump(FILE *fp, struct bini_stream *bs)
285{
286 size_t i;
287 fprintf(fp, "bini buffer dump:\n");
288 fprintf(fp, "buffer: %lu/%lu bytes\n", bs->len, bs->cap);
289 fprintf(fp, "---\n");
290 for (i = 0 ; i < bs->len ; i++)
291 fprintf(fp, ". %02x '%c'\n", bs->buffer[i], bs->buffer[i]);
292 fprintf(fp, "---\n");
293}
294
295static
296#if bini_c >= 1999
297inline
298#endif
299int _bini_grow(struct bini_stream *bs, size_t n)
300{
301 uint8_t *newbuf;
302 if (bs->len + n < bs->cap)
303 return BINI_OK;
304 while (bs->len + n >= bs->cap)
305 {
306 newbuf = realloc(bs->buffer, bs->cap * 2);
307 if (!newbuf)
308 return BINI_ERR;
309 bs->buffer = newbuf;
310 bs->cap *= 2;
311 }
312 return BINI_OK;
313}
314
315size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp)
316{
317 size_t nread;
318 _bini_grow(bs, size*n);
319 nread = fread(bs->buffer, size, n, fp);
320 bs->len += nread;
321 return nread;
322}
323
324size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp)
325{
326 return fwrite(bs->buffer, size, n, fp);
327}
328
329static
330#if bini_c >= 1999
331inline
332#endif
333void _bini_shiftleft(struct bini_stream *bs, size_t n)
334{
335 size_t i;
336 _bini_dbg("shift %d: (len=%lu)\n", n, bs->len);
337 for (i = 0 ; i < bs->len ; i++)
338 {
339 if (i+n >= bs->len)
340 {
341 _bini_dbg(" %d: [%d] (out of bounds) to [%d]\n", n, i+n, i);
342 bs->buffer[i] = 0;
343 continue;
344 }
345 _bini_dbg(" %d: [%d] (%02x '%c') to [%d]\n",
346 n,
347 i+n,
348 bs->buffer[i+n],
349 bs->buffer[i+n],
350 i);
351 bs->buffer[i] = bs->buffer[i+n];
352 }
353}
354
355#define _bini_rwx(_type, _name) \
356 void bini_w##_name(struct bini_stream *bs, _type v) \
357 { \
358 size_t i; \
359 uint8_t *bytes; \
360 int l = bini_getendian() == BINI_LITTLEENDIAN; \
361 _bini_dbg("%s: %d (%lu bytes)\n", __FUNCTION__, *(int *)&v, sizeof(v)); \
362 _bini_grow(bs, sizeof(v)); \
363 bytes = (uint8_t *)&v; \
364 for (i = 0 ; i < sizeof(v) ; i++) \
365 { \
366 _bini_dbg("%s: byte index [%lu] (= %02x '%c') to buffer index %lu\n", \
367 __FUNCTION__, \
368 l ? sizeof(v)-i-1 : i, \
369 bytes[l ? sizeof(v)-i-1 : i], \
370 bytes[l ? sizeof(v)-i-1 : i], \
371 bs->len+i); \
372 bs->buffer[bs->len+i] = bytes[l ? sizeof(v)-i-1 : i]; \
373 } \
374 bs->len += i; \
375 _bini_dbg("-> %s: %d (%lu bytes)\n", __FUNCTION__, *(int *)bytes, sizeof(v)); \
376 } \
377 _type bini_r##_name(struct bini_stream *bs) \
378 { \
379 size_t i; \
380 _type v; \
381 uint8_t *bytes = (uint8_t *)&v; \
382 int l = bini_getendian() == BINI_LITTLEENDIAN; \
383 int m = bs->mode == BINI_STREAM; \
384 for (i = 0 ; i < sizeof(v) ; i++) \
385 { \
386 _bini_dbg("%s: byte index [%lu] from buffer index %lu (= %02x '%c')\n", \
387 __FUNCTION__, \
388 l ? sizeof(v)-i-1 : i, \
389 m ? i : (bs->len-sizeof(v)+i), \
390 bs->buffer[m ? i : (bs->len-sizeof(v)+i)], \
391 bs->buffer[m ? i : (bs->len-sizeof(v)+i)]); \
392 bytes[l ? sizeof(v)-i-1 : i] = bs->buffer[m ? i : (bs->len-sizeof(v)+i)]; \
393 } \
394 if (m) \
395 _bini_shiftleft(bs, i); \
396 bs->len -= i; \
397 return v; \
398 }
399BINI_X(_bini_rwx)
400#undef _bini_rwx
401
402
403void bini_wstrn(struct bini_stream *bs, const char *str, size_t n)
404{
405 bini_wlu(bs, n);
406 _bini_grow(bs, n);
407 while (str)
408 {
409 bini_wc(bs, *str);
410 str++;
411 }
412}
413
414void bini_wstr(struct bini_stream *bs, const char *str)
415{
416 size_t i, n = strlen(str);
417 char *s = (char *)str;
418 bini_wlu(bs, n);
419 _bini_grow(bs, n);
420 for (i = 0 ; i < n ; i++)
421 bini_wc(bs, s[i]);
422}
423
424size_t bini_rstr(struct bini_stream *bs, char *str)
425{
426 size_t i, n = bini_rlu(bs);
427 for (i = 0 ; i < n ; i++)
428 str[i] = bini_rc(bs);
429 return n;
430}
431
432/*
433Bini license: BSD 3-Clause.
434
435Copyright 2025 Emmeline Coats
436
437Redistribution and use in source and binary forms, with or without
438modification, are permitted provided that the following conditions are met:
439
4401. Redistributions of source code must retain the above copyright notice, this
441 list of conditions and the following disclaimer.
442
4432. Redistributions in binary form must reproduce the above copyright notice,
444 this list of conditions and the following disclaimer in the documentation
445 and/or other materials provided with the distribution.
446
4473. Neither the name of the copyright holder nor the names of its contributors
448 may be used to endorse or promote products derived from this software
449 without specific prior written permission.
450
451THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND
452ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
453WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
454DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
455FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
456DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
457SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
458CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
459OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
460OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
461*/
462
463#endif /* bini_impl */
464#endif /* __bini__ */