+432
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
+10
compile_flags.txt
+63
examples/fileio.c
+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
+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
+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
+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
+
}