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