#ifndef __kf_bini__ #define __kf_bini__ /* bini.h: endian-independent binary IO. License: BSD 3-Clause. See EOF for license text. Version: 1.1 Changelog: 1.0 (Dec 28, 2025): Initial version. 1.1 (Dec 30, 2025): Use an offset instead of shifting the byte buffer so carelessly. _bini_grow will shift the buffer on-demand instead so as to conserve CPU time. */ /* Used to add functions for version-specific types. */ #define bini_c 1989 #ifdef __STDC_VERSION__ # undef bini_c # if __STDC_VERSION__ == 199901 # define bini_c 1999 # elif __STDC_VERSION__ == 201112 # define bini_c 2011 # elif __STDC_VERSION__ == 201710 # define bini_c 2017 # elif __STDC_VERSION__ == 202000 || __STDC_VERSION__ == 202311 # define bini_c 2023 # else # warning "bini.h: unknown C version. Falling back to C89." # define bini_c 1989 # endif #endif #include #include #include #include #if bini_c >= 1999 # include #endif #ifndef BINI_BUFSIZ /* Starting size for bini_stream buffers. */ # define BINI_BUFSIZ 8192 #endif #ifndef BINI_STRBUFSIZ /* Starting size for bini_*print buffers. */ # define BINI_STRBUFSIZ 1024 #endif /* Execute the `x` macro for each type that bini uses. Excludes string types (char *). */ #ifndef BINI_X # if bini_c >= 1999 # define BINI_X(x) \ x(char, c) \ x(short, s) \ x(int, i) \ x(long, l) \ x(long long, ll) \ x(unsigned char, cu) \ x(unsigned short, su) \ x(unsigned int, iu) \ x(unsigned long, lu) \ x(unsigned long long, llu) \ x(float, f) \ x(double, d) \ x(bool, b) # else # define BINI_X(x) \ x(char, c) \ x(short, s) \ x(int, i) \ x(long, l) \ x(unsigned char, cu) \ x(unsigned short, su) \ x(unsigned int, iu) \ x(unsigned long, lu) \ x(float, f) \ x(double, d) # endif #endif enum { BINI_ERR, BINI_OK }; enum { BINI_BIGENDIAN, BINI_LITTLEENDIAN }; enum { /* Read values from the end of the buffer. */ BINI_STACK, /* Read values from the start of the buffer. */ BINI_STREAM }; /* Represents a binary IO stream. */ struct bini_stream { /* Read mode for the stream. Should be either BINI_STACK or BINI_STREAM. */ int mode; /* Offset from the start of the buffer in bytes. Only used for BINI_STREAM. */ size_t off; size_t cap; size_t len; uint8_t *buffer; }; int bini_getendian(void); /* Initialize a bini_stream, allocating a buffer and setting each field to its respective defaults. Also see bini_new(). Free data using bini_end(). */ int bini_init(struct bini_stream *); /* Create a new bini_stream, allocating a starting buffer of BINI_BUFSIZ bytes. To use your own buffer, initialize your own bini_stream and call bini_init() to fill in the necessary fields. Free data using bini_close(). */ struct bini_stream *bini_new(void); /* End a bini_stream, flushing it if need be and freeing its buffer. */ void bini_end(struct bini_stream *); /* End a bini_stream, flushing it if need be, freeing its buffer, and frees the provided variable as well. */ void bini_close(struct bini_stream *bs); /* Shift a binary stream, resetting the offset and shifting the array. */ void bini_shift(struct bini_stream *); /* Flush a binary stream, clearing its buffer. */ int bini_flush(struct bini_stream *); /* Dump the binary stream's buffer to fp. */ void bini_dump(FILE *fp, struct bini_stream *bs); /* Read data from a FILE into a binary stream. */ size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp); /* Write data to a FILE into a binary stream. This will NOT consume data from the stream! Stack/stream mode does not affect this function. To consume data, use `bs->len -= bini_fwrite(bs, ...);` */ size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp); #define _bini_rwx(_type, _name) \ /* Write a `typeof(v)` to the stream. */ \ void bini_w##_name(struct bini_stream *bs, _type v); \ /* Read a `typeof(v)` from the stream. */ \ _type bini_r##_name(struct bini_stream *bs); BINI_X(_bini_rwx) #undef _bini_rwx void bini_wstrn(struct bini_stream *, const char *, size_t); void bini_wstr(struct bini_stream *, const char *); size_t bini_rstr(struct bini_stream *, char *); #if 0 /* unimplemented */ /* Write to a binary stream. */ int bini_vprint(struct bini_stream *bs, char *fmt, va_list va); /* Write to a binary stream. */ int bini_print(struct bini_stream *bs, char *fmt, ...); /* Read from a binary stream. */ int bini_vscan(struct bini_stream *bs, char *fmt, va_list va); /* Read from a binary stream. */ int bini_scan(struct bini_stream *bs, char *fmt, ...); #endif /* Aliased names that are more akin to libc names. */ #if defined(bini_libc_names) typedef struct bini_stream bstream; # define bnew bini_new # define binit bini_init # define bend bini_end # define bclose bini_close # define bflush bini_flush # define bdump bini_dump # define bputc bini_wc # define bputs bini_ws # define bputi bini_wi # define bputl bini_wl # define bputll bini_wll # define bputcu bini_wcu # define bputsu bini_wsu # define bputiu bini_wiu # define bputlu bini_wlu # define bputllu bini_wllu # define bputf bini_wf # define bputd bini_wd # define bputb bini_wb # define bputstr bini_wstr # define bputstrn bini_wstrn # define breadc bini_rc # define breads bini_rs # define breadi bini_ri # define breadl bini_rl # define breadll bini_rll # define breadcu bini_rcu # define breadsu bini_rsu # define breadiu bini_riu # define breadlu bini_rlu # define breadllu bini_rllu # define breadf bini_rf # define breadd bini_rd # define breadb bini_rb # define breadstr bini_rstr # define breadstrn bini_rstrn #if 0 # define bfprint bini_vprint # define bprint bini_print # define bvscan bini_vscan # define bscan bini_scan #endif #endif #if defined(bini_impl) #include #include static void _bini_dbg(char *fmt, ...) { # if bini_dbg va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); # else (void)fmt; # endif } int bini_getendian(void) { const int n = 1; if (*(char *)&n == 1) return BINI_LITTLEENDIAN; return BINI_BIGENDIAN; } int bini_init(struct bini_stream *bs) { bs->buffer = calloc(BINI_BUFSIZ, 1); if (!bs->buffer) return BINI_ERR; bs->off = 0; bs->len = 0; bs->cap = BINI_BUFSIZ; bs->mode = BINI_STREAM; return BINI_OK; } struct bini_stream *bini_new(void) { struct bini_stream *bs = malloc(sizeof(struct bini_stream)); bini_init(bs); return bs; } void bini_end(struct bini_stream *bs) { bini_flush(bs); free(bs->buffer); } void bini_close(struct bini_stream *bs) { bini_end(bs); free(bs); } static #if bini_c >= 1999 inline #endif void _bini_shiftleft(struct bini_stream *bs, size_t n) { size_t i; _bini_dbg("shift %d: (len=%lu)\n", n, bs->len); for (i = 0 ; i < bs->len ; i++) { if (i+n >= bs->len) { _bini_dbg(" %d: [%d] (out of bounds) to [%d]\n", n, i+n, i); bs->buffer[i] = 0; continue; } _bini_dbg(" %d: [%d] (%02x '%c') to [%d]\n", n, i+n, bs->buffer[i+n], bs->buffer[i+n], i); bs->buffer[i] = bs->buffer[i+n]; } } void bini_shift(struct bini_stream *bs) { _bini_shiftleft(bs, bs->off); bs->off = 0; } int bini_flush(struct bini_stream *bs) { size_t i; for (i = 0 ; i < bs->len ; i++) bs->buffer[i] = 0; bs->len = 0; return BINI_OK; } void bini_dump(FILE *fp, struct bini_stream *bs) { size_t i; fprintf(fp, "bini buffer dump:\n"); fprintf(fp, "buffer: %lu/%lu bytes\n", bs->len, bs->cap); fprintf(fp, "---\n"); for (i = 0 ; i < bs->len ; i++) fprintf(fp, ". %02x '%c'\n", bs->buffer[i], bs->buffer[i]); fprintf(fp, "---\n"); } static #if bini_c >= 1999 inline #endif int _bini_grow(struct bini_stream *bs, size_t n) { uint8_t *newbuf; if (bs->off + bs->len + n < bs->cap) return BINI_OK; /* Shifting the array could give enough space to fit `n`, avoiding the need to realloc. */ bini_shift(bs); if (bs->len + n < bs->cap) return BINI_OK; while (bs->len + n >= bs->cap) { newbuf = realloc(bs->buffer, bs->cap * 2); if (!newbuf) return BINI_ERR; bs->buffer = newbuf; bs->cap *= 2; } return BINI_OK; } size_t bini_fread(struct bini_stream *bs, size_t size, size_t n, FILE *fp) { size_t nread; _bini_grow(bs, size*n); nread = fread(bs->buffer, size, n, fp); bs->len += nread; return nread; } size_t bini_fwrite(struct bini_stream *bs, size_t size, size_t n, FILE *fp) { return fwrite(bs->buffer, size, n, fp); } #define _bini_rwx(_type, _name) \ void bini_w##_name(struct bini_stream *bs, _type v) \ { \ size_t i; \ uint8_t *bytes; \ int l = bini_getendian() == BINI_LITTLEENDIAN; \ _bini_dbg("%s: %d (%lu bytes)\n", __FUNCTION__, *(int *)&v, sizeof(v)); \ _bini_grow(bs, sizeof(v)); \ bytes = (uint8_t *)&v; \ for (i = 0 ; i < sizeof(v) ; i++) \ { \ _bini_dbg("%s: byte index [%lu] (= %02x '%c') to buffer index %lu\n", \ __FUNCTION__, \ l ? sizeof(v)-i-1 : i, \ bytes[l ? sizeof(v)-i-1 : i], \ bytes[l ? sizeof(v)-i-1 : i], \ bs->off+bs->len+i); \ bs->buffer[bs->off+bs->len+i] = bytes[l ? sizeof(v)-i-1 : i]; \ } \ bs->len += i; \ _bini_dbg("-> %s: %d (%lu bytes)\n", __FUNCTION__, *(int *)bytes, sizeof(v)); \ } \ _type bini_r##_name(struct bini_stream *bs) \ { \ size_t i; \ _type v; \ uint8_t *bytes = (uint8_t *)&v; \ int l = bini_getendian() == BINI_LITTLEENDIAN; \ int m = bs->mode == BINI_STREAM; \ for (i = 0 ; i < sizeof(v) ; i++) \ { \ const size_t p = bs->off + (m ? i : (bs->len-sizeof(v)+i)); \ _bini_dbg("%s: byte index [%lu] from buffer index %lu (= %02x '%c')\n", \ __FUNCTION__, \ l ? sizeof(v)-i-1 : i, \ p, \ bs->buffer[p], \ bs->buffer[p]); \ bytes[l ? sizeof(v)-i-1 : i] = bs->buffer[p]; \ } \ if (m) \ { \ bs->off += i; \ /* _bini_shiftleft(bs, i); */ \ } \ bs->len -= i; \ return v; \ } BINI_X(_bini_rwx) #undef _bini_rwx void bini_wstrn(struct bini_stream *bs, const char *str, size_t n) { bini_wlu(bs, n); _bini_grow(bs, n); while (str) { bini_wc(bs, *str); str++; } } void bini_wstr(struct bini_stream *bs, const char *str) { size_t i, n = strlen(str); char *s = (char *)str; bini_wlu(bs, n); _bini_grow(bs, n); for (i = 0 ; i < n ; i++) bini_wc(bs, s[i]); } size_t bini_rstr(struct bini_stream *bs, char *str) { size_t i, n = bini_rlu(bs); for (i = 0 ; i < n ; i++) str[i] = bini_rc(bs); return n; } /* Bini license: BSD 3-Clause. Copyright 2025 Emmeline Coats Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #endif /* bini_impl */ #endif /* __bini__ */