comparison src/c-ringbuf/ringbuf.c @ 216:1f18f47e29eb

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