/* ========================================================================== * fifo.h - Simple byte FIFO with simple bit packing. * -------------------------------------------------------------------------- * Copyright (c) 2008-2013 William Ahern * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to permit * persons to whom the Software is furnished to do so, subject to the * following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * ========================================================================== */ #ifndef FIFO_H #define FIFO_H #include /* va_list va_start() va_end() */ #include /* size_t */ #include /* SIZE_MAX */ #include /* EOF FILE fputc(3) vsnprintf(3) */ #include /* realloc(3) free(3) */ #include /* memcpy(3) memmove(3) strlen(3) memchr(3) */ #include /* isgraph(3) */ #include /* ENOMEM EOVERFLOW errno */ #include /* assert(3) */ #include /* struct iovec */ /* * V E R S I O N I N T E R F A C E S * * Vendor: Entity for which versions numbers are relevant. (If forking * change FIFO_VENDOR to avoid confusion.) * * Three versions: * * REL Official "release"--bug fixes, new features, etc. * ABI Changes to existing object sizes or parameter types. * API Changes that might effect application source. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_VENDOR "william@25thandClement.com" #define FIFO_V_REL 0x20140424 /* 0x20130330 */ #define FIFO_V_ABI 0x20111113 /* 0x20100815 */ #define FIFO_V_API 0x20130325 /* 0x20111113 */ static inline const char *fifo_vendor(void) { return FIFO_VENDOR; } static inline int fifo_v_rel(void) { return FIFO_V_REL; } static inline int fifo_v_abi(void) { return FIFO_V_ABI; } static inline int fifo_v_api(void) { return FIFO_V_API; } /* * M I S C E L L A N E O U S M A C R O S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_MIN(a, b) (((a) < (b))? (a) : (b)) #define FIFO_NARG_(_15, _14, _13, _12, _11, _10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N #define FIFO_NARG(...) FIFO_NARG_(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) #define FIFO_PASTE(a, b) a ## b #define FIFO_XPASTE(a, b) FIFO_PASTE(a, b) #if __GNUC__ #define FIFO_NOTUSED __attribute__((unused)) #define FIFO_FORMAT(type, x, y) __attribute__((format (type, x, y))) #else #define FIFO_NOTUSED #define FIFO_FORMAT(...) #endif /* * O B J E C T I N I T I A L I Z A T I O N R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define FIFO_INITIALIZER { { 0 } } #define fifo_new(size) fifo_init(&(struct fifo)FIFO_INITIALIZER, ((size))? (unsigned char [(size) + !(size)]){ 0 } : (void *)0, (size)) struct fifo { /* static buffer */ struct iovec sbuf; unsigned char *base; size_t size, head, count; /* bit accumulators */ struct { unsigned char byte, count; } rbits, wbits; }; /* struct fifo */ #define FIFO_STATIC (1 << 0) #define FIFO_DYNAMIC (1 << 1) static inline int fifo_type(struct fifo *fifo) { return (1 << !fifo->sbuf.iov_base); } /* fifo_type() */ /* * Initialize fifo object as static or dynamic buffer. * * - Dynamic: Only pass the fifo object to initialize. DO NOT forget to * call fifo_reset() to release dynamic resources. * * - Static: Pass two additional arguments, pointer and size. Can safely * call fifo_reset(), but not required. */ static struct fifo *fifo_init(struct fifo *fifo, void *buf, size_t size) { fifo->sbuf = (struct iovec){ buf, size }; fifo->base = fifo->sbuf.iov_base; fifo->size = fifo->sbuf.iov_len; fifo->head = 0; fifo->count = 0; fifo->rbits.byte = 0; fifo->rbits.count = 0; fifo->wbits.byte = 0; fifo->wbits.count = 0; return fifo; } /* fifo_init() */ #define fifo_init3(...) (fifo_init)(__VA_ARGS__) #define fifo_init2(fifo, buf) fifo_init3((fifo), (buf), __builtin_object_size((buf), 3)) #define fifo_init1(fifo) fifo_init3((fifo), (void *)0, 0U) #define fifo_init(...) FIFO_XPASTE(fifo_init, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) static inline size_t fifo_update(struct fifo *, size_t); /* forward declaration */ static inline struct fifo *fifo_from(struct fifo *fifo, void *src, size_t size) { fifo_init(fifo, src, size); fifo_update(fifo, size); return fifo; } /* fifo_from() */ #define fifo_from3(...) (fifo_from)(__VA_ARGS__) #define fifo_from2(src, size) fifo_from3(&(struct fifo)FIFO_INITIALIZER, (src), (size)) #define fifo_from1(src) fifo_from3(&(struct fifo)FIFO_INITIALIZER, (src), __builtin_object_size((src), 3)) #define fifo_from(...) FIFO_XPASTE(fifo_from, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) #define fifo_into2(buf, size) fifo_init(&(struct fifo)FIFO_INITIALIZER, (buf), (size)) #define fifo_into1(buf) fifo_init(&(struct fifo)FIFO_INITIALIZER, (buf), __builtin_object_size((buf), 3)) #define fifo_into(...) FIFO_XPASTE(fifo_into, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) FIFO_NOTUSED static struct fifo *fifo_reset(struct fifo *fifo) { if (fifo->base != fifo->sbuf.iov_base) free(fifo->base); return fifo_init(fifo, fifo->sbuf.iov_base, fifo->sbuf.iov_len); } /* fifo_reset() */ FIFO_NOTUSED static struct fifo *fifo_purge(struct fifo *fifo) { fifo->head = 0; fifo->count = 0; fifo->rbits.byte = 0; fifo->rbits.count = 0; fifo->wbits.byte = 0; fifo->wbits.count = 0; return fifo; } /* fifo_purge() */ /* * M E M O R Y M A N A G E M E N T R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef FIFO_TMPBUFSIZ #define FIFO_TMPBUFSIZ 2048 #endif static void fifo_realign(struct fifo *fifo) { if (fifo->size - fifo->head >= fifo->count) { memmove(fifo->base, &fifo->base[fifo->head], fifo->count); fifo->head = 0; } else { unsigned char tmp[FIFO_TMPBUFSIZ]; unsigned n, m; while (fifo->head != 0) { n = FIFO_MIN(fifo->head, sizeof tmp); m = fifo->size - n; memcpy(tmp, fifo->base, n); memmove(fifo->base, &fifo->base[n], m); memcpy(&fifo->base[m], tmp, n); fifo->head -= n; } } } /* fifo_realign() */ static inline size_t fifo_power2(size_t i) { #if defined SIZE_MAX i--; i |= i >> 1; i |= i >> 2; i |= i >> 4; i |= i >> 8; i |= i >> 16; #if SIZE_MAX != 0xffffffffu i |= i >> 32; #endif return ++i; #else #error No SIZE_MAX defined #endif } /* fifo_power2() */ static inline size_t fifo_roundup(size_t i) { if (i > ~(((size_t)-1) >> 1u)) return (size_t)-1; else return fifo_power2(i); } /* fifo_roundup() */ static int fifo_realloc(struct fifo *fifo, size_t size) { void *tmp; if (fifo->size >= size) return 0; if (fifo_type(fifo) == FIFO_STATIC) return ENOMEM; fifo_realign(fifo); size = fifo_roundup(size); if (!(tmp = realloc(fifo->base, size))) return errno; fifo->base = tmp; fifo->size = size; return 0; } /* fifo_realloc() */ static inline int fifo_grow(struct fifo *fifo, size_t size) { if (fifo->size - fifo->count >= size) return 0; if (~fifo->count < size) return EOVERFLOW; return fifo_realloc(fifo, fifo->count + size); } /* fifo_grow() */ /* * V E C T O R R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline size_t fifo_rlen(struct fifo *fifo) { return fifo->count; } /* fifo_rlen() */ static inline size_t fifo_wlen(struct fifo *fifo) { return fifo->size - fifo->count; } /* fifo_wlen() */ static size_t fifo_rvec(struct fifo *fifo, struct iovec *iov, _Bool realign) { if (fifo->head + fifo->count > fifo->size && realign) fifo_realign(fifo); iov->iov_base = &fifo->base[fifo->head]; iov->iov_len = FIFO_MIN(fifo->size - fifo->head, fifo->count); return iov->iov_len; } /* fifo_rvec() */ #define fifo_rvec3(fifo, iov, realign, ...) (fifo_rvec)((fifo), (iov), (realign)) #define fifo_rvec(...) fifo_rvec3(__VA_ARGS__, 0, (char[-1]{ 0 })) static size_t fifo_wvec(struct fifo *fifo, struct iovec *iov, _Bool realign) { size_t tail, count; if (fifo->head + fifo->count < fifo->size && realign) fifo_realign(fifo); tail = (fifo->size)? ((fifo->head + fifo->count) % fifo->size) : 0; count = fifo_wlen(fifo); iov->iov_base = &fifo->base[tail]; iov->iov_len = FIFO_MIN(fifo->size - tail, count); return iov->iov_len; } /* fifo_wvec() */ #define fifo_wvec3(fifo, iov, realign, ...) (fifo_wvec)((fifo), (iov), (realign)) #define fifo_wvec(...) fifo_wvec3(__VA_ARGS__, 0, (char[-1]{ 0 })) FIFO_NOTUSED static size_t fifo_slice(struct fifo *fifo, struct iovec *iov, size_t p, size_t count) { size_t pe; if (p > fifo->count) { iov->iov_base = 0; iov->iov_len = 0; return 0; } count = FIFO_MIN(count, fifo->count - p); pe = p + count; if (fifo->head + p < fifo->size && fifo->head + pe > fifo->size) fifo_realign(fifo); iov->iov_base = &fifo->base[((fifo->head + p) % fifo->size)]; iov->iov_len = count; return count; } /* fifo_slice() */ static size_t fifo_tvec(struct fifo *fifo, struct iovec *iov, int ch) { unsigned char *p; if (fifo_rvec(fifo, iov)) { if ((p = memchr(iov->iov_base, ch, iov->iov_len))) return iov->iov_len = (p - (unsigned char *)iov->iov_base) + 1; if (fifo->count > iov->iov_len) { iov->iov_len = fifo->count - iov->iov_len; iov->iov_base = fifo->base; if ((p = memchr(iov->iov_base, ch, iov->iov_len))) { iov->iov_len = (p - fifo->base) + (fifo->size - fifo->head) + 1; iov->iov_base = fifo->base; fifo_realign(fifo); return iov->iov_len; } } iov->iov_len = 0; } return 0; } /* fifo_tvec() */ static inline size_t fifo_lvec(struct fifo *fifo, struct iovec *iov) { return fifo_tvec(fifo, iov, '\n'); } /* fifo_lvec() */ static int fifo_wbuf(struct fifo *fifo, struct iovec *iov, size_t size) { int error; if ((error = fifo_grow(fifo, size))) return error; /* try to avoid realigning buffer */ if (fifo_wvec(fifo, iov, 0) < size) fifo_wvec(fifo, iov, 1); return 0; } /* fifo_wbuf() */ /* * R E A D / W R I T E R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #ifndef FIFO_AUTOALIGN #define FIFO_AUTOALIGN 1 #endif static inline size_t fifo_discard(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->count); fifo->head = (fifo->head + count) % fifo->size; fifo->count -= count; #if FIFO_AUTOALIGN if (!fifo->count) fifo->head = 0; #endif return count; } /* fifo_discard() */ static inline size_t fifo_update(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->size - fifo->count); fifo->count += count; return count; } /* fifo_update() */ static inline size_t fifo_rewind(struct fifo *fifo, size_t count) { count = FIFO_MIN(count, fifo->size - fifo->count); fifo->head = (fifo->head + (fifo->size - count)) % fifo->size; fifo->count += count; return count; } /* fifo_rewind() */ FIFO_NOTUSED static int fifo_write(struct fifo *fifo, const void *src, size_t len) { const unsigned char *p = src, *pe = p + len; struct iovec iov; size_t n; int error = 0; do { while (fifo_wvec(fifo, &iov) && p < pe) { n = FIFO_MIN(iov.iov_len, (size_t)(pe - p)); memcpy(iov.iov_base, p, n); p += n; fifo_update(fifo, n); } } while (p < pe && !(error = fifo_grow(fifo, pe - p))); return error; } /* fifo_write() */ FIFO_NOTUSED static size_t fifo_read(struct fifo *fifo, void *dst, size_t lim) { unsigned char *p = dst, *pe = p + lim; struct iovec iov; size_t n; while (p < pe && fifo_rvec(fifo, &iov)) { n = FIFO_MIN(iov.iov_len, (size_t)(pe - p)); memcpy(p, iov.iov_base, n); p += n; fifo_discard(fifo, n); } return p - (unsigned char *)dst; } /* fifo_read() */ static int fifo_putc(struct fifo *fifo, int c) { int error; if (fifo->count >= fifo->size && (error = fifo_grow(fifo, 1))) return error; fifo->base[(fifo->head + fifo->count) % fifo->size] = (0xff & c); fifo_update(fifo, 1); return 0; } /* fifo_putc() */ static inline int fifo_puts(struct fifo *fifo, const void *src) { return fifo_write(fifo, src, strlen(src)); } /* fifo_puts() */ FIFO_NOTUSED FIFO_FORMAT(printf, 2, 3) static int fifo_printf(struct fifo *fifo, const char *fmt, ...) { va_list ap; struct iovec iov; int count = 0, error; do { if ((error = fifo_wbuf(fifo, &iov, (size_t)count + 1))) return error; va_start(ap, fmt); count = vsnprintf(iov.iov_base, iov.iov_len, fmt, ap); va_end(ap); if (count < 0) return (errno)? errno : ENOMEM; } while ((size_t)count >= iov.iov_len); fifo_update(fifo, count); return 0; } /* fifo_printf() */ static inline int fifo_ungetc(struct fifo *fifo, int c) { int error; if ((error = fifo_grow(fifo, 1))) return error; assert(1 == fifo_rewind(fifo, 1)); fifo->base[fifo->head] = c; return 0; } /* fifo_ungetc() */ static inline int fifo_getc(struct fifo *fifo) { int c; if (!fifo->count) return EOF; c = fifo->base[fifo->head]; fifo_discard(fifo, 1); return c; } /* fifo_getc() */ static inline int fifo_peek(struct fifo *fifo, size_t p) { if (p >= fifo->count) return EOF; return fifo->base[(fifo->head + p) % fifo->size]; } /* fifo_peek() */ static inline int fifo_scan(struct fifo *fifo, size_t *p) { return fifo_peek(fifo, (*p)++); } /* fifo_scan() */ #define FIFO_NOINFO 1 FIFO_NOTUSED static void fifo_dump(struct fifo *fifo, FILE *fp, int flags) { static const unsigned char hex[] = "0123456789abcdef"; size_t p, n; int ch; if (!(FIFO_NOINFO & flags)) fprintf(fp, "%zu of %zu bytes in FIFO; %u unread bits (0x%.2x); %u unflushed bits (0x%.2x)\n", fifo->count, fifo->size, fifo->rbits.count, (((1 << fifo->rbits.count) - 1) & fifo->rbits.byte), fifo->wbits.count, (((1 << fifo->wbits.count) - 1) & fifo->wbits.byte)); for (p = 0; p < fifo->count; p += 16) { fprintf(fp, " %.4x ", (unsigned)p); for (n = 0; n < 8 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc(hex[0x0f & (ch >> 4)], fp); fputc(hex[0x0f & (ch >> 0)], fp); fputc(' ', fp); } fputc(' ', fp); for (; n < 16 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc(hex[0x0f & (ch >> 4)], fp); fputc(hex[0x0f & (ch >> 0)], fp); fputc(' ', fp); } p -= n; for (; n < 16; n++) { fputc(' ', fp); fputc(' ', fp); fputc(' ', fp); } fputc(' ', fp); fputc('|', fp); for (n = 0; n < 16 && EOF != (ch = fifo_peek(fifo, p)); n++, p++) { fputc((isgraph(ch)? ch : '.'), fp); } p -= n; for (; n < 16; n++) fputc(' ', fp); fputc('|', fp); fputc('\n', fp); } } /* fifo_dump() */ #define fifo_dump3(...) fifo_dump(__VA_ARGS__) #define fifo_dump2(...) fifo_dump3(__VA_ARGS__, 0) #define fifo_dump1(...) fifo_dump3(__VA_ARGS__, stderr, 0) #define fifo_dump(...) FIFO_XPASTE(fifo_dump, FIFO_NARG(__VA_ARGS__))(__VA_ARGS__) /* * B I T P A C K I N G R O U T I N E S * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ static inline size_t fifo_rbits(struct fifo *fifo) { return fifo->rbits.count + (8 * fifo_rlen(fifo)); } /* fifo_rbits() */ static inline size_t fifo_wbits(struct fifo *fifo) { return (8 - fifo->wbits.count) + (8 * fifo_wlen(fifo)); } /* fifo_wbits() */ FIFO_NOTUSED static unsigned long long fifo_unpack(struct fifo *fifo, unsigned count) { unsigned long long bits = 0; unsigned mask, nbits; if (fifo_rbits(fifo) < count) return 0; while (count) { if (!fifo->rbits.count) { fifo->rbits.byte = fifo_getc(fifo); fifo->rbits.count = 8; } nbits = FIFO_MIN(count, fifo->rbits.count); mask = (1 << nbits) - 1; bits <<= nbits; bits |= mask & (fifo->rbits.byte >> (fifo->rbits.count - nbits)); fifo->rbits.count -= nbits; count -= nbits; } return bits; } /* fifo_unpack() */ FIFO_NOTUSED static int fifo_pack(struct fifo *fifo, unsigned long long bits, unsigned count) { unsigned mask, nbits; int error; if (fifo_wbits(fifo) < count && (error = fifo_grow(fifo, 8))) return error; while (count) { nbits = FIFO_MIN(count, 8U - fifo->wbits.count); mask = (1U << nbits) - 1; fifo->wbits.byte <<= nbits; fifo->wbits.byte |= mask & (bits >> (count - nbits)); fifo->wbits.count += nbits; count -= nbits; if (fifo->wbits.count >= 8) { fifo_putc(fifo, fifo->wbits.byte); fifo->wbits.byte = 0; fifo->wbits.count = 0; } } return 0; } /* fifo_pack() */ #endif /* FIFO_H */ #if FIFO_MAIN #include int main(void) { struct fifo *fifo = fifo_new(0); struct iovec iov; int ch; setlocale(LC_ALL, "C"); while (EOF != (ch = getchar())) fifo_putc(fifo, ch); puts("[stdin]"); fifo_dump(fifo, stdout); puts("[shifted 4 bits]"); fifo_pack(fifo, fifo_unpack(fifo, 4), 4); fifo_dump(fifo, stdout); puts("[shifted 1/2 count]"); for (size_t i = 0; i < fifo->count / 2; i++) fifo_putc(fifo, fifo_getc(fifo)); fifo_dump(fifo, stdout); puts("[slice of 17 bytes at 17]"); fifo_slice(fifo, &iov, 17, 17); fifo_dump(fifo_from(iov.iov_base, iov.iov_len), stdout); return 0; } /* main() */ #endif /* FIFO_MAIN */