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