Mercurial > hg > libxtract
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 } |