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