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