Endian-independent binary IO utilities for C

Initial commit

+3
.gitignore
··· 1 + /*.out 2 + /*.bin 3 + /out.txt
+4
bini.c
··· 1 + #ifndef bini_impl 2 + #define bini_impl 3 + #endif 4 + #include <bini.h>
+432
bini.h
··· 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 + 78 + enum { BINI_ERR, BINI_OK }; 79 + enum { BINI_BIGENDIAN, BINI_LITTLEENDIAN }; 80 + enum 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. */ 90 + struct 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 + 100 + int 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(). */ 106 + int 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(). */ 111 + struct bini_stream *bini_new(void); 112 + /* End a bini_stream, flushing it if need be and freeing 113 + its buffer. */ 114 + void 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. */ 117 + void bini_close(struct bini_stream *bs); 118 + 119 + /* Flush a binary stream, clearing its buffer. */ 120 + int bini_flush(struct bini_stream *); 121 + /* Dump the binary stream's buffer to fp. */ 122 + void bini_dump(FILE *fp, struct bini_stream *bs); 123 + 124 + /* Read data from a FILE into a binary stream. */ 125 + size_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, ...);` */ 130 + size_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); 138 + BINI_X(_bini_rwx) 139 + #undef _bini_rwx 140 + 141 + void bini_wstrn(struct bini_stream *, const char *, size_t); 142 + void bini_wstr(struct bini_stream *, const char *); 143 + 144 + size_t bini_rstr(struct bini_stream *, char *); 145 + 146 + #if 0 /* unimplemented */ 147 + /* Write to a binary stream. */ 148 + int bini_vprint(struct bini_stream *bs, char *fmt, va_list va); 149 + /* Write to a binary stream. */ 150 + int bini_print(struct bini_stream *bs, char *fmt, ...); 151 + 152 + /* Read from a binary stream. */ 153 + int bini_vscan(struct bini_stream *bs, char *fmt, va_list va); 154 + /* Read from a binary stream. */ 155 + int 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 + 162 + typedef 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 + 221 + static 222 + void _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 + 235 + int 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 + 244 + int 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 + 255 + struct 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 + 262 + void bini_end(struct bini_stream *bs) 263 + { 264 + bini_flush(bs); 265 + free(bs->buffer); 266 + } 267 + 268 + void bini_close(struct bini_stream *bs) 269 + { 270 + bini_end(bs); 271 + free(bs); 272 + } 273 + 274 + int 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 + 283 + void 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 + 294 + static 295 + #if bini_c >= 1999 296 + inline 297 + #endif 298 + int _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 + 314 + size_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 + 323 + size_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 + 328 + static 329 + #if bini_c >= 1999 330 + inline 331 + #endif 332 + void _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 + } 398 + BINI_X(_bini_rwx) 399 + #undef _bini_rwx 400 + 401 + 402 + void 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 + 413 + void 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 + 423 + size_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__ */
+10
compile_flags.txt
··· 1 + -Wall 2 + -Werror 3 + -Wextra 4 + -pedantic 5 + -pedantic-errors 6 + -ansi 7 + -xc-header 8 + -Dbini_impl 9 + -Dbini_dbg 10 + -I.
+63
examples/fileio.c
··· 1 + #include <bini.c> 2 + 3 + typedef struct bini_stream bstream; 4 + 5 + struct data { int life, max, min, zero; }; 6 + 7 + void write(struct data d) 8 + { 9 + FILE *fp; 10 + bstream *bs = bini_new(); 11 + 12 + bini_wi(bs, d.life); 13 + bini_wi(bs, d.max); 14 + bini_wi(bs, d.min); 15 + bini_wi(bs, d.zero); 16 + 17 + fp = fopen("fileio.bin", "wb"); 18 + if (!fp) 19 + { 20 + fprintf(stderr, "failed to open fileio.bin\n"); 21 + exit(1); 22 + } 23 + bini_fwrite(bs, 1, bs->len, fp); 24 + fclose(fp); 25 + 26 + bini_close(bs); 27 + } 28 + 29 + void read(struct data *d) 30 + { 31 + FILE *fp; 32 + bstream *bs = bini_new(); 33 + 34 + fp = fopen("fileio.bin", "rb"); 35 + if (!fp) 36 + { 37 + fprintf(stderr, "failed to open fileio.bin\n"); 38 + exit(1); 39 + } 40 + bini_fread(bs, 1, sizeof(*d), fp); 41 + fclose(fp); 42 + 43 + bini_dump(stderr, bs); 44 + 45 + d->life = bini_ri(bs); 46 + d->max = bini_ri(bs); 47 + d->min = bini_ri(bs); 48 + d->zero = bini_ri(bs); 49 + 50 + printf("life=%d\n", d->life); 51 + printf("max i32=%d\n", d->max); 52 + printf("min i32=%d\n", d->min); 53 + printf("zero=%d\n", d->zero); 54 + 55 + bini_close(bs); 56 + } 57 + 58 + int main(void) 59 + { 60 + struct data d = { 42, INT32_MAX, INT32_MIN, 0 }; 61 + write(d); 62 + read(&d); 63 + }
+34
examples/integers.c
··· 1 + #define bini_libc_names 2 + #include <bini.c> 3 + 4 + void write(bstream *bs) 5 + { 6 + bputi(bs, 42); 7 + bputi(bs, INT32_MAX); 8 + bputi(bs, INT32_MIN); 9 + bputi(bs, 0); 10 + } 11 + 12 + void read(bstream *bs) 13 + { 14 + printf("life=%d\n", breadi(bs)); 15 + printf("max i32=%d\n", breadi(bs)); 16 + printf("min i32=%d\n", breadi(bs)); 17 + printf("zero=%d\n", breadi(bs)); 18 + } 19 + 20 + int main(void) 21 + { 22 + bstream *bs = bnew(); 23 + write(bs); 24 + # ifdef x_writebin 25 + { 26 + FILE *fp = fopen("out.bin", "wb"); 27 + fwrite(bs->buffer, 1, bs->len, fp); 28 + fclose(fp); 29 + } 30 + # endif 31 + bdump(stderr, bs); 32 + read(bs); 33 + bclose(bs); 34 + }
+36
examples/strings.c
··· 1 + #define bini_libc_names 2 + #include <bini.c> 3 + 4 + const char *msg1 = "Hello, World!"; 5 + const char *msg2 = "Ping pong!"; 6 + 7 + void write(bstream *bs) 8 + { 9 + bputstr(bs, msg1); 10 + bputstr(bs, msg2); 11 + } 12 + 13 + void read(bstream *bs) 14 + { 15 + char msg[BUFSIZ]; 16 + size_t n = breadstr(bs, msg); 17 + printf("msg1=%.*s\n", (int)n, msg); 18 + n = breadstr(bs, msg); 19 + printf("msg2=%.*s\n", (int)n, msg); 20 + } 21 + 22 + int main(void) 23 + { 24 + bstream *bs = bnew(); 25 + write(bs); 26 + # ifdef x_writebin 27 + { 28 + FILE *fp = fopen("out.bin", "wb"); 29 + fwrite(bs->buffer, 1, bs->len, fp); 30 + fclose(fp); 31 + } 32 + # endif 33 + bdump(stderr, bs); 34 + read(bs); 35 + bclose(bs); 36 + }
+44
readme
··· 1 + Bini 2 + ==== 3 + 4 + Endian-independent* binary IO utilities for C. 5 + 6 + *I only have access to little-endian machines, so it's 7 + currently untested on big endian, however it *should* 8 + work. If you have a big endian machine and are willing to 9 + test that Bini runs as expected, I'd be very grateful. 10 + 11 + Bini is written in plain ANSI C. If you're compiling in C99 12 + or later, functions for reading/writing long long and bool 13 + will be added automatically. You can override the C version 14 + Bini sees by passing -Dbini_c=<some year, i.e, 1989, 1999, 15 + 2011, etc>. 16 + 17 + Usage 18 + ----- 19 + 20 + Examples can be found in the </examples/> folder. 21 + 22 + I usually run examples with one of these commands: 23 + cc examples/<example>.c -I. -g -Dbini_dbg -Dx_writebin && gdb -q -ex=r --args a.out 24 + cc examples/<example>.c -I. -g -Dbini_dbg -Dx_writebin && ./a.out &> out.txt 25 + 26 + Sample: 27 + 28 + /* Only define this once! You can use the bini.c file to 29 + ensure you don't accidentally define bini_impl twice. */ 30 + #define bini_impl 31 + #include <bini.h> 32 + 33 + int main(void) 34 + { 35 + /* Allocate a new binary stream. */ 36 + struct bini_stream *bs = bini_new(); 37 + /* Write the meaning of life, the universe, and everything to our binary stream. */ 38 + bini_wi(bs, 42); 39 + /* Read the meaning of life, the universe, and everything from our binary stream. */ 40 + int life = bini_ri(bs); 41 + ASSERT(life == 42); 42 + /* Close our stream, flushing+freeing the buffer and the stream itself. */ 43 + bini_close(bs); 44 + }