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__ */