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