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 }
|