annotate src/c-ringbuf/ringbuf.c @ 285:89fe52066db1 tip master

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