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