123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- /*
- * ringbuf.c - C ring buffer (FIFO) implementation.
- *
- * Written in 2011 by Drew Hess <dhess-src@bothan.net>.
- *
- * To the extent possible under law, the author(s) have dedicated all
- * copyright and related and neighboring rights to this software to
- * the public domain worldwide. This software is distributed without
- * any warranty.
- *
- * You should have received a copy of the CC0 Public Domain Dedication
- * along with this software. If not, see
- * <http://creativecommons.org/publicdomain/zero/1.0/>.
- */
- #ifndef KITTY_DEBUG_BUILD
- #define NDEBUG 1
- #endif
- #include "ringbuf.h"
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <sys/param.h>
- #include <assert.h>
- static size_t
- size_t_min(size_t x, size_t y) {
- return x > y ? y : x;
- }
- /*
- * The code is written for clarity, not cleverness or performance, and
- * contains many assert()s to enforce invariant assumptions and catch
- * bugs. Feel free to optimize the code and to remove asserts for use
- * in your own projects, once you're comfortable that it functions as
- * intended.
- */
- struct ringbuf_t
- {
- uint8_t *buf;
- uint8_t *head, *tail;
- size_t size;
- };
- ringbuf_t
- ringbuf_new(size_t capacity)
- {
- ringbuf_t rb = malloc(sizeof(struct ringbuf_t));
- if (rb) {
- /* One byte is used for detecting the full condition. */
- rb->size = capacity + 1;
- rb->buf = malloc(rb->size);
- if (rb->buf)
- ringbuf_reset(rb);
- else {
- free(rb);
- return 0;
- }
- }
- return rb;
- }
- size_t
- ringbuf_buffer_size(const struct ringbuf_t *rb)
- {
- return rb->size;
- }
- void
- ringbuf_reset(ringbuf_t rb)
- {
- rb->head = rb->tail = rb->buf;
- }
- void
- ringbuf_free(ringbuf_t *rb)
- {
- assert(rb && *rb);
- free((*rb)->buf);
- free(*rb);
- *rb = 0;
- }
- size_t
- ringbuf_capacity(const struct ringbuf_t *rb)
- {
- return ringbuf_buffer_size(rb) - 1;
- }
- /*
- * Return a pointer to one-past-the-end of the ring buffer's
- * contiguous buffer. You shouldn't normally need to use this function
- * unless you're writing a new ringbuf_* function.
- */
- static const uint8_t *
- ringbuf_end(const struct ringbuf_t *rb)
- {
- return rb->buf + ringbuf_buffer_size(rb);
- }
- size_t
- ringbuf_bytes_free(const struct ringbuf_t *rb)
- {
- if (rb->head >= rb->tail)
- return ringbuf_capacity(rb) - (rb->head - rb->tail);
- else
- return rb->tail - rb->head - 1;
- }
- size_t
- ringbuf_bytes_used(const struct ringbuf_t *rb)
- {
- return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
- }
- int
- ringbuf_is_full(const struct ringbuf_t *rb)
- {
- return ringbuf_bytes_free(rb) == 0;
- }
- int
- ringbuf_is_empty(const struct ringbuf_t *rb)
- {
- return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
- }
- const void *
- ringbuf_tail(const struct ringbuf_t *rb)
- {
- return rb->tail;
- }
- const void *
- ringbuf_head(const struct ringbuf_t *rb)
- {
- return rb->head;
- }
- /*
- * Given a ring buffer rb and a pointer to a location within its
- * contiguous buffer, return the a pointer to the next logical
- * location in the ring buffer.
- */
- static uint8_t *
- ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
- {
- /*
- * The assert guarantees the expression (++p - rb->buf) is
- * non-negative; therefore, the modulus operation is safe and
- * portable.
- */
- assert((p >= rb->buf) && (p < ringbuf_end(rb)));
- return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
- }
- size_t
- ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset)
- {
- const uint8_t *bufend = ringbuf_end(rb);
- size_t bytes_used = ringbuf_bytes_used(rb);
- if (offset >= bytes_used)
- return bytes_used;
- const uint8_t *start = rb->buf +
- (((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb));
- assert(bufend > start);
- size_t n = size_t_min(bufend - start, bytes_used - offset);
- const uint8_t *found = memchr(start, c, n);
- if (found)
- return offset + (found - start);
- else
- return ringbuf_findchr(rb, c, offset + n);
- }
- size_t
- ringbuf_memset(ringbuf_t dst, int c, size_t len)
- {
- const uint8_t *bufend = ringbuf_end(dst);
- size_t nwritten = 0;
- size_t count = size_t_min(len, ringbuf_buffer_size(dst));
- int overflow = count > ringbuf_bytes_free(dst);
- while (nwritten != count) {
- /* don't copy beyond the end of the buffer */
- assert(bufend > dst->head);
- size_t n = size_t_min(bufend - dst->head, count - nwritten);
- memset(dst->head, c, n);
- dst->head += n;
- nwritten += n;
- /* wrap? */
- if (dst->head == bufend)
- dst->head = dst->buf;
- }
- if (overflow) {
- dst->tail = ringbuf_nextp(dst, dst->head);
- assert(ringbuf_is_full(dst));
- }
- return nwritten;
- }
- void *
- ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count)
- {
- const uint8_t *u8src = src;
- const uint8_t *bufend = ringbuf_end(dst);
- int overflow = count > ringbuf_bytes_free(dst);
- size_t nread = 0;
- while (nread != count) {
- /* don't copy beyond the end of the buffer */
- assert(bufend > dst->head);
- size_t n = size_t_min(bufend - dst->head, count - nread);
- memcpy(dst->head, u8src + nread, n);
- dst->head += n;
- nread += n;
- /* wrap? */
- if (dst->head == bufend)
- dst->head = dst->buf;
- }
- if (overflow) {
- dst->tail = ringbuf_nextp(dst, dst->head);
- assert(ringbuf_is_full(dst));
- }
- return dst->head;
- }
- ssize_t
- ringbuf_read(int fd, ringbuf_t rb, size_t count)
- {
- const uint8_t *bufend = ringbuf_end(rb);
- size_t nfree = ringbuf_bytes_free(rb);
- /* don't write beyond the end of the buffer */
- assert(bufend > rb->head);
- count = size_t_min(bufend - rb->head, count);
- ssize_t n = read(fd, rb->head, count);
- if (n > 0) {
- assert(rb->head + n <= bufend);
- rb->head += n;
- /* wrap? */
- if (rb->head == bufend)
- rb->head = rb->buf;
- /* fix up the tail pointer if an overflow occurred */
- if ((size_t)n > nfree) {
- rb->tail = ringbuf_nextp(rb, rb->head);
- assert(ringbuf_is_full(rb));
- }
- }
- return n;
- }
- void *
- ringbuf_memmove_from(void *dst, ringbuf_t src, size_t count)
- {
- size_t bytes_used = ringbuf_bytes_used(src);
- if (count > bytes_used)
- return 0;
- uint8_t *u8dst = dst;
- const uint8_t *bufend = ringbuf_end(src);
- size_t nwritten = 0;
- while (nwritten != count) {
- assert(bufend > src->tail);
- size_t n = size_t_min(bufend - src->tail, count - nwritten);
- memcpy(u8dst + nwritten, src->tail, n);
- src->tail += n;
- nwritten += n;
- /* wrap ? */
- if (src->tail == bufend)
- src->tail = src->buf;
- }
- assert(count + ringbuf_bytes_used(src) == bytes_used);
- return src->tail;
- }
- unsigned char
- ringbuf_move_char(ringbuf_t src) {
- assert(!ringbuf_is_empty(src));
- const uint8_t *bufend = ringbuf_end(src);
- assert(bufend > src->tail);
- uint8_t ans = *src->tail;
- src->tail += 1;
- if (src->tail == bufend)
- src->tail = src->buf;
- return ans;
- }
- size_t
- ringbuf_memcpy_from(void *dst, const ringbuf_t src, size_t count)
- {
- size_t bytes_used = ringbuf_bytes_used(src);
- if (count > bytes_used) count = bytes_used;
- uint8_t *u8dst = dst;
- const uint8_t *bufend = ringbuf_end(src);
- size_t nwritten = 0;
- const uint8_t* tail = src->tail;
- while (nwritten != count) {
- assert(bufend > tail);
- size_t n = size_t_min(bufend - tail, count - nwritten);
- memcpy(u8dst + nwritten, tail, n);
- tail += n;
- nwritten += n;
- /* wrap ? */
- if (tail == bufend)
- tail = src->buf;
- }
- assert(ringbuf_bytes_used(src) == bytes_used);
- return count;
- }
- ssize_t
- ringbuf_write(int fd, ringbuf_t rb, size_t count)
- {
- size_t bytes_used = ringbuf_bytes_used(rb);
- if (count > bytes_used)
- return 0;
- const uint8_t *bufend = ringbuf_end(rb);
- assert(bufend > rb->head);
- count = size_t_min(bufend - rb->tail, count);
- ssize_t n = write(fd, rb->tail, count);
- if (n > 0) {
- assert(rb->tail + n <= bufend);
- rb->tail += n;
- /* wrap? */
- if (rb->tail == bufend)
- rb->tail = rb->buf;
- assert(n + ringbuf_bytes_used(rb) == bytes_used);
- }
- return n;
- }
- void *
- ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count)
- {
- size_t src_bytes_used = ringbuf_bytes_used(src);
- if (count > src_bytes_used)
- return 0;
- int overflow = count > ringbuf_bytes_free(dst);
- const uint8_t *src_bufend = ringbuf_end(src);
- const uint8_t *dst_bufend = ringbuf_end(dst);
- size_t ncopied = 0;
- while (ncopied != count) {
- assert(src_bufend > src->tail);
- size_t nsrc = size_t_min(src_bufend - src->tail, count - ncopied);
- assert(dst_bufend > dst->head);
- size_t n = size_t_min(dst_bufend - dst->head, nsrc);
- memcpy(dst->head, src->tail, n);
- src->tail += n;
- dst->head += n;
- ncopied += n;
- /* wrap ? */
- if (src->tail == src_bufend)
- src->tail = src->buf;
- if (dst->head == dst_bufend)
- dst->head = dst->buf;
- }
- assert(count + ringbuf_bytes_used(src) == src_bytes_used);
- if (overflow) {
- dst->tail = ringbuf_nextp(dst, dst->head);
- assert(ringbuf_is_full(dst));
- }
- return dst->head;
- }
|