annotate src/c-ringbuf/ringbuf.c @ 279:6ee836d79500

Add missing MIN() macro define
author Jamie Bullock <jamie@jamiebullock.com>
date Fri, 19 Dec 2014 17:46:10 +0000
parents dee3b3f352ed
children 730dfd7d613d
rev   line source
jamie@216 1 /*
jamie@216 2 * ringbuf.c - C ring buffer (FIFO) implementation.
jamie@216 3 *
jamie@216 4 * Written in 2011 by Drew Hess <dhess-src@bothan.net>.
jamie@216 5 *
jamie@216 6 * To the extent possible under law, the author(s) have dedicated all
jamie@216 7 * copyright and related and neighboring rights to this software to
jamie@216 8 * the public domain worldwide. This software is distributed without
jamie@216 9 * any warranty.
jamie@216 10 *
jamie@216 11 * You should have received a copy of the CC0 Public Domain Dedication
jamie@216 12 * along with this software. If not, see
jamie@216 13 * <http://creativecommons.org/publicdomain/zero/1.0/>.
jamie@216 14 */
jamie@216 15
jamie@216 16 #include "ringbuf.h"
jamie@216 17
jamie@216 18 #include <stdint.h>
jamie@216 19 #include <stdlib.h>
jamie@216 20 #include <string.h>
jamie@216 21 #include <sys/types.h>
jamie@216 22 #include <sys/uio.h>
jamie@216 23 #include <unistd.h>
jamie@216 24 #include <sys/param.h>
jamie@216 25 #include <assert.h>
jamie@216 26
jamie@279 27 #ifndef MIN
jamie@279 28 #define MIN(x,y) ((x)>(y)?(y):(x))
jamie@279 29 #endif
jamie@279 30
jamie@216 31 /*
jamie@216 32 * The code is written for clarity, not cleverness or performance, and
jamie@216 33 * contains many assert()s to enforce invariant assumptions and catch
jamie@216 34 * bugs. Feel free to optimize the code and to remove asserts for use
jamie@216 35 * in your own projects, once you're comfortable that it functions as
jamie@216 36 * intended.
jamie@216 37 */
jamie@216 38
jamie@216 39 struct ringbuf_t
jamie@216 40 {
jamie@216 41 uint8_t *buf;
jamie@216 42 uint8_t *head, *tail;
jamie@216 43 size_t size;
jamie@216 44 };
jamie@216 45
jamie@216 46 ringbuf_t
jamie@216 47 ringbuf_new(size_t capacity)
jamie@216 48 {
jamie@216 49 ringbuf_t rb = malloc(sizeof(struct ringbuf_t));
jamie@216 50 if (rb) {
jamie@216 51
jamie@216 52 /* One byte is used for detecting the full condition. */
jamie@216 53 rb->size = capacity + 1;
jamie@216 54 rb->buf = malloc(rb->size);
jamie@216 55 if (rb->buf)
jamie@216 56 ringbuf_reset(rb);
jamie@216 57 else {
jamie@216 58 free(rb);
jamie@216 59 return 0;
jamie@216 60 }
jamie@216 61 }
jamie@216 62 return rb;
jamie@216 63 }
jamie@216 64
jamie@216 65 size_t
jamie@216 66 ringbuf_buffer_size(const struct ringbuf_t *rb)
jamie@216 67 {
jamie@216 68 return rb->size;
jamie@216 69 }
jamie@216 70
jamie@216 71 void
jamie@216 72 ringbuf_reset(ringbuf_t rb)
jamie@216 73 {
jamie@216 74 rb->head = rb->tail = rb->buf;
jamie@216 75 }
jamie@216 76
jamie@216 77 void
jamie@216 78 ringbuf_free(ringbuf_t *rb)
jamie@216 79 {
jamie@216 80 assert(rb && *rb);
jamie@216 81 free((*rb)->buf);
jamie@216 82 free(*rb);
jamie@216 83 *rb = 0;
jamie@216 84 }
jamie@216 85
jamie@216 86 size_t
jamie@216 87 ringbuf_capacity(const struct ringbuf_t *rb)
jamie@216 88 {
jamie@216 89 return ringbuf_buffer_size(rb) - 1;
jamie@216 90 }
jamie@216 91
jamie@216 92 /*
jamie@216 93 * Return a pointer to one-past-the-end of the ring buffer's
jamie@216 94 * contiguous buffer. You shouldn't normally need to use this function
jamie@216 95 * unless you're writing a new ringbuf_* function.
jamie@216 96 */
jamie@216 97 static const uint8_t *
jamie@216 98 ringbuf_end(const struct ringbuf_t *rb)
jamie@216 99 {
jamie@216 100 return rb->buf + ringbuf_buffer_size(rb);
jamie@216 101 }
jamie@216 102
jamie@216 103 size_t
jamie@216 104 ringbuf_bytes_free(const struct ringbuf_t *rb)
jamie@216 105 {
jamie@216 106 if (rb->head >= rb->tail)
jamie@216 107 return ringbuf_capacity(rb) - (rb->head - rb->tail);
jamie@216 108 else
jamie@216 109 return rb->tail - rb->head - 1;
jamie@216 110 }
jamie@216 111
jamie@216 112 size_t
jamie@216 113 ringbuf_bytes_used(const struct ringbuf_t *rb)
jamie@216 114 {
jamie@216 115 return ringbuf_capacity(rb) - ringbuf_bytes_free(rb);
jamie@216 116 }
jamie@216 117
jamie@216 118 int
jamie@216 119 ringbuf_is_full(const struct ringbuf_t *rb)
jamie@216 120 {
jamie@216 121 return ringbuf_bytes_free(rb) == 0;
jamie@216 122 }
jamie@216 123
jamie@216 124 int
jamie@216 125 ringbuf_is_empty(const struct ringbuf_t *rb)
jamie@216 126 {
jamie@216 127 return ringbuf_bytes_free(rb) == ringbuf_capacity(rb);
jamie@216 128 }
jamie@216 129
jamie@216 130 const void *
jamie@216 131 ringbuf_tail(const struct ringbuf_t *rb)
jamie@216 132 {
jamie@216 133 return rb->tail;
jamie@216 134 }
jamie@216 135
jamie@216 136 const void *
jamie@216 137 ringbuf_head(const struct ringbuf_t *rb)
jamie@216 138 {
jamie@216 139 return rb->head;
jamie@216 140 }
jamie@216 141
jamie@216 142 /*
jamie@216 143 * Given a ring buffer rb and a pointer to a location within its
jamie@216 144 * contiguous buffer, return the a pointer to the next logical
jamie@216 145 * location in the ring buffer.
jamie@216 146 */
jamie@216 147 static uint8_t *
jamie@216 148 ringbuf_nextp(ringbuf_t rb, const uint8_t *p)
jamie@216 149 {
jamie@216 150 /*
jamie@216 151 * The assert guarantees the expression (++p - rb->buf) is
jamie@216 152 * non-negative; therefore, the modulus operation is safe and
jamie@216 153 * portable.
jamie@216 154 */
jamie@216 155 assert((p >= rb->buf) && (p < ringbuf_end(rb)));
jamie@216 156 return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb));
jamie@216 157 }
jamie@216 158
jamie@216 159 size_t
jamie@216 160 ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset)
jamie@216 161 {
jamie@216 162 const uint8_t *bufend = ringbuf_end(rb);
jamie@216 163 size_t bytes_used = ringbuf_bytes_used(rb);
jamie@216 164 if (offset >= bytes_used)
jamie@216 165 return bytes_used;
jamie@216 166
jamie@216 167 const uint8_t *start = rb->buf +
jamie@216 168 (((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb));
jamie@216 169 assert(bufend > start);
jamie@216 170 size_t n = MIN(bufend - start, bytes_used - offset);
jamie@216 171 const uint8_t *found = memchr(start, c, n);
jamie@216 172 if (found)
jamie@216 173 return offset + (found - start);
jamie@216 174 else
jamie@216 175 return ringbuf_findchr(rb, c, offset + n);
jamie@216 176 }
jamie@216 177
jamie@216 178 size_t
jamie@216 179 ringbuf_memset(ringbuf_t dst, int c, size_t len)
jamie@216 180 {
jamie@216 181 const uint8_t *bufend = ringbuf_end(dst);
jamie@216 182 size_t nwritten = 0;
jamie@216 183 size_t count = MIN(len, ringbuf_buffer_size(dst));
jamie@216 184 int overflow = count > ringbuf_bytes_free(dst);
jamie@216 185
jamie@216 186 while (nwritten != count) {
jamie@216 187
jamie@216 188 /* don't copy beyond the end of the buffer */
jamie@216 189 assert(bufend > dst->head);
jamie@216 190 size_t n = MIN(bufend - dst->head, count - nwritten);
jamie@216 191 memset(dst->head, c, n);
jamie@216 192 dst->head += n;
jamie@216 193 nwritten += n;
jamie@216 194
jamie@216 195 /* wrap? */
jamie@216 196 if (dst->head == bufend)
jamie@216 197 dst->head = dst->buf;
jamie@216 198 }
jamie@216 199
jamie@216 200 if (overflow) {
jamie@216 201 dst->tail = ringbuf_nextp(dst, dst->head);
jamie@216 202 assert(ringbuf_is_full(dst));
jamie@216 203 }
jamie@216 204
jamie@216 205 return nwritten;
jamie@216 206 }
jamie@216 207
jamie@216 208 void *
jamie@216 209 ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count)
jamie@216 210 {
jamie@216 211 const uint8_t *u8src = src;
jamie@216 212 const uint8_t *bufend = ringbuf_end(dst);
jamie@216 213 int overflow = count > ringbuf_bytes_free(dst);
jamie@216 214 size_t nread = 0;
jamie@216 215
jamie@216 216 while (nread != count) {
jamie@216 217 /* don't copy beyond the end of the buffer */
jamie@216 218 assert(bufend > dst->head);
jamie@216 219 size_t n = MIN(bufend - dst->head, count - nread);
jamie@216 220 memcpy(dst->head, u8src + nread, n);
jamie@216 221 dst->head += n;
jamie@216 222 nread += n;
jamie@216 223
jamie@216 224 /* wrap? */
jamie@216 225 if (dst->head == bufend)
jamie@216 226 dst->head = dst->buf;
jamie@216 227 }
jamie@216 228
jamie@216 229 if (overflow) {
jamie@216 230 dst->tail = ringbuf_nextp(dst, dst->head);
jamie@216 231 assert(ringbuf_is_full(dst));
jamie@216 232 }
jamie@216 233
jamie@216 234 return dst->head;
jamie@216 235 }
jamie@216 236
jamie@216 237 ssize_t
jamie@216 238 ringbuf_read(int fd, ringbuf_t rb, size_t count)
jamie@216 239 {
jamie@216 240 const uint8_t *bufend = ringbuf_end(rb);
jamie@216 241 size_t nfree = ringbuf_bytes_free(rb);
jamie@216 242
jamie@216 243 /* don't write beyond the end of the buffer */
jamie@216 244 assert(bufend > rb->head);
jamie@216 245 count = MIN(bufend - rb->head, count);
jamie@216 246 ssize_t n = read(fd, rb->head, count);
jamie@216 247 if (n > 0) {
jamie@216 248 assert(rb->head + n <= bufend);
jamie@216 249 rb->head += n;
jamie@216 250
jamie@216 251 /* wrap? */
jamie@216 252 if (rb->head == bufend)
jamie@216 253 rb->head = rb->buf;
jamie@216 254
jamie@216 255 /* fix up the tail pointer if an overflow occurred */
jamie@216 256 if (n > nfree) {
jamie@216 257 rb->tail = ringbuf_nextp(rb, rb->head);
jamie@216 258 assert(ringbuf_is_full(rb));
jamie@216 259 }
jamie@216 260 }
jamie@216 261
jamie@216 262 return n;
jamie@216 263 }
jamie@216 264
jamie@216 265 void *
jamie@216 266 ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count, bool destroy)
jamie@216 267 {
jamie@216 268 size_t bytes_used = ringbuf_bytes_used(src);
jamie@216 269 if (count > bytes_used)
jamie@216 270 return 0;
jamie@216 271
jamie@216 272 uint8_t *u8dst = dst;
jamie@216 273 const uint8_t *bufend = ringbuf_end(src);
jamie@216 274 uint8_t *tail = src->tail;
jamie@216 275 size_t nwritten = 0;
jamie@216 276 while (nwritten != count) {
jamie@216 277 assert(bufend > src->tail);
jamie@216 278 size_t n = MIN(bufend - src->tail, count - nwritten);
jamie@216 279 memcpy(u8dst + nwritten, src->tail, n);
jamie@216 280 src->tail += n;
jamie@216 281 nwritten += n;
jamie@216 282
jamie@216 283 /* wrap ? */
jamie@216 284 if (src->tail == bufend)
jamie@216 285 src->tail = src->buf;
jamie@216 286 }
jamie@216 287
jamie@216 288 if (!destroy)
jamie@216 289 {
jamie@216 290 src->tail = tail;
jamie@237 291 assert(ringbuf_bytes_used(src) == bytes_used);
jamie@216 292 }
jamie@237 293 else
jamie@237 294 {
jamie@237 295 assert(count + ringbuf_bytes_used(src) == bytes_used);
jamie@237 296 }
jamie@216 297 return src->tail;
jamie@216 298 }
jamie@216 299
jamie@216 300 ssize_t
jamie@216 301 ringbuf_write(int fd, ringbuf_t rb, size_t count)
jamie@216 302 {
jamie@216 303 size_t bytes_used = ringbuf_bytes_used(rb);
jamie@216 304 if (count > bytes_used)
jamie@216 305 return 0;
jamie@216 306
jamie@216 307 const uint8_t *bufend = ringbuf_end(rb);
jamie@216 308 assert(bufend > rb->head);
jamie@216 309 count = MIN(bufend - rb->tail, count);
jamie@216 310 ssize_t n = write(fd, rb->tail, count);
jamie@216 311 if (n > 0) {
jamie@216 312 assert(rb->tail + n <= bufend);
jamie@216 313 rb->tail += n;
jamie@216 314
jamie@216 315 /* wrap? */
jamie@216 316 if (rb->tail == bufend)
jamie@216 317 rb->tail = rb->buf;
jamie@216 318
jamie@216 319 assert(n + ringbuf_bytes_used(rb) == bytes_used);
jamie@216 320 }
jamie@216 321
jamie@216 322 return n;
jamie@216 323 }
jamie@216 324
jamie@216 325 void *
jamie@216 326 ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count)
jamie@216 327 {
jamie@216 328 size_t src_bytes_used = ringbuf_bytes_used(src);
jamie@216 329 if (count > src_bytes_used)
jamie@216 330 return 0;
jamie@216 331 int overflow = count > ringbuf_bytes_free(dst);
jamie@216 332
jamie@216 333 const uint8_t *src_bufend = ringbuf_end(src);
jamie@216 334 const uint8_t *dst_bufend = ringbuf_end(dst);
jamie@216 335 size_t ncopied = 0;
jamie@216 336 while (ncopied != count) {
jamie@216 337 assert(src_bufend > src->tail);
jamie@216 338 size_t nsrc = MIN(src_bufend - src->tail, count - ncopied);
jamie@216 339 assert(dst_bufend > dst->head);
jamie@216 340 size_t n = MIN(dst_bufend - dst->head, nsrc);
jamie@216 341 memcpy(dst->head, src->tail, n);
jamie@216 342 src->tail += n;
jamie@216 343 dst->head += n;
jamie@216 344 ncopied += n;
jamie@216 345
jamie@216 346 /* wrap ? */
jamie@216 347 if (src->tail == src_bufend)
jamie@216 348 src->tail = src->buf;
jamie@216 349 if (dst->head == dst_bufend)
jamie@216 350 dst->head = dst->buf;
jamie@216 351 }
jamie@216 352
jamie@216 353 assert(count + ringbuf_bytes_used(src) == src_bytes_used);
jamie@216 354
jamie@216 355 if (overflow) {
jamie@216 356 dst->tail = ringbuf_nextp(dst, dst->head);
jamie@216 357 assert(ringbuf_is_full(dst));
jamie@216 358 }
jamie@216 359
jamie@216 360 return dst->head;
jamie@216 361 }