1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
|
utringbuffer: dynamic ring-buffer macros for C
==============================================
Arthur O'Dwyer <arthur.j.odwyer@gmail.com>
v2.1.0, December 2018
Here's a link back to the https://github.com/troydhanson/uthash[GitHub project page].
Introduction
------------
The functions in `utringbuffer.h` are based on the general-purpose array macros
provided in `utarray.h`, so before reading this page you should read
link:utarray.html[that page] first.
To use these macros in your own C program, copy both `utarray.h` and `utringbuffer.h`
into your source directory and use `utringbuffer.h` in your program.
#include "utringbuffer.h"
The provided <<operations,operations>> are based loosely on the C++ STL vector methods.
The ring-buffer data type supports construction (with a specified capacity),
destruction, iteration, and push, but not pop; once the ring-buffer reaches full
capacity, pushing a new element automatically pops and destroys the oldest element.
The elements contained in the ring-buffer can be any simple datatype or structure.
Internally the ring-buffer contains a pre-allocated memory region into which the
elements are copied, starting at position 0. When the ring-buffer reaches full
capacity, the next element to be pushed is pushed at position 0, overwriting the
oldest element, and the internal index representing the "start" of the ring-buffer
is incremented. A ring-buffer, once full, can never become un-full.
Download
~~~~~~~~
To download the `utringbuffer.h` header file,
follow the links on https://github.com/troydhanson/uthash to clone uthash or get a zip file,
then look in the src/ sub-directory.
BSD licensed
~~~~~~~~~~~~
This software is made available under the
link:license.html[revised BSD license].
It is free and open source.
Platforms
~~~~~~~~~
The 'utringbuffer' macros have been tested on:
* Linux,
* Mac OS X,
* Windows, using Visual Studio 2008 and Visual Studio 2010
Usage
-----
Declaration
~~~~~~~~~~~
The ring-buffer itself has the data type `UT_ringbuffer`, regardless of the type of
elements to be stored in it. It is declared like,
UT_ringbuffer *history;
New and free
~~~~~~~~~~~~
The next step is to create the ring-buffer using `utringbuffer_new`. Later when you're
done with the ring-buffer, `utringbuffer_free` will free it and all its elements.
Push, etc
~~~~~~~~~
The central features of the ring-buffer involve putting elements into it
and iterating over them. There are several <<operations,operations>>
that deal with either single elements or ranges of elements at a
time. In the examples below we will use only the push operation to insert
elements.
Elements
--------
Support for dynamic arrays of integers or strings is especially easy. These are
best shown by example:
Integers
~~~~~~~~
This example makes a ring-buffer of integers, pushes 0-9 into it, then prints it
two different ways. Lastly it frees it.
.Integer elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utringbuffer.h"
int main() {
UT_ringbuffer *history;
int i, *p;
utringbuffer_new(history, 7, &ut_int_icd);
for(i=0; i < 10; i++) utringbuffer_push_back(history, &i);
for (p = (int*)utringbuffer_front(history);
p != NULL;
p = (int*)utringbuffer_next(history, p)) {
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
}
for (i=0; i < utringbuffer_len(history); i++) {
p = utringbuffer_eltptr(history, i);
printf("%d\n", *p); /* prints "3 4 5 6 7 8 9" */
}
utringbuffer_free(history);
return 0;
}
-------------------------------------------------------------------------------
The second argument to `utringbuffer_push_back` is always a 'pointer' to the type
(so a literal cannot be used). So for integers, it is an `int*`.
Strings
~~~~~~~
In this example we make a ring-buffer of strings, push two strings into it, print
it and free it.
.String elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utringbuffer.h"
int main() {
UT_ringbuffer *strs;
char *s, **p;
utringbuffer_new(strs, 7, &ut_str_icd);
s = "hello"; utringbuffer_push_back(strs, &s);
s = "world"; utringbuffer_push_back(strs, &s);
p = NULL;
while ( (p=(char**)utringbuffer_next(strs,p))) {
printf("%s\n",*p);
}
utringbuffer_free(strs);
return 0;
}
-------------------------------------------------------------------------------
In this example, since the element is a `char*`, we pass a pointer to it
(`char**`) as the second argument to `utringbuffer_push_back`. Note that "push" makes
a copy of the source string and pushes that copy into the array.
About UT_icd
~~~~~~~~~~~~
Arrays can be made of any type of element, not just integers and strings. The
elements can be basic types or structures. Unless you're dealing with integers
and strings (which use pre-defined `ut_int_icd` and `ut_str_icd`), you'll need
to define a `UT_icd` helper structure. This structure contains everything that
utringbuffer (or utarray) needs to initialize, copy or destruct elements.
typedef struct {
size_t sz;
init_f *init;
ctor_f *copy;
dtor_f *dtor;
} UT_icd;
The three function pointers `init`, `copy`, and `dtor` have these prototypes:
typedef void (ctor_f)(void *dst, const void *src);
typedef void (dtor_f)(void *elt);
typedef void (init_f)(void *elt);
The `sz` is just the size of the element being stored in the array.
The `init` function is used by utarray but is never used by utringbuffer;
you may safely set it to any value you want.
The `copy` function is used whenever an element is copied into the buffer.
It is invoked during `utringbuffer_push_back`.
If `copy` is `NULL`, it defaults to a bitwise copy using memcpy.
The `dtor` function is used to clean up an element that is being removed from
the buffer. It may be invoked due to `utringbuffer_push_back` (on the oldest
element in the buffer), `utringbuffer_clear`, `utringbuffer_done`, or
`utringbuffer_free`.
If the elements need no cleanup upon destruction, `dtor` may be `NULL`.
Scalar types
~~~~~~~~~~~~
The next example uses `UT_icd` with all its defaults to make a ring-buffer of
`long` elements. This example pushes two longs into a buffer of capacity 1,
prints the contents of the buffer (which is to say, the most recent value
pushed), and then frees the buffer.
.long elements
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utringbuffer.h"
UT_icd long_icd = {sizeof(long), NULL, NULL, NULL };
int main() {
UT_ringbuffer *nums;
long l, *p;
utringbuffer_new(nums, 1, &long_icd);
l=1; utringbuffer_push_back(nums, &l);
l=2; utringbuffer_push_back(nums, &l);
p=NULL;
while((p = (long*)utringbuffer_next(nums,p))) printf("%ld\n", *p);
utringbuffer_free(nums);
return 0;
}
-------------------------------------------------------------------------------
Structures
~~~~~~~~~~
Structures can be used as utringbuffer elements. If the structure requires no
special effort to initialize, copy or destruct, we can use `UT_icd` with all
its defaults. This example shows a structure that consists of two integers. Here
we push two values, print them and free the buffer.
.Structure (simple)
-------------------------------------------------------------------------------
#include <stdio.h>
#include "utringbuffer.h"
typedef struct {
int a;
int b;
} intpair_t;
UT_icd intpair_icd = {sizeof(intpair_t), NULL, NULL, NULL};
int main() {
UT_ringbuffer *pairs;
intpair_t ip, *p;
utringbuffer_new(pairs, 7, &intpair_icd);
ip.a=1; ip.b=2; utringbuffer_push_back(pairs, &ip);
ip.a=10; ip.b=20; utringbuffer_push_back(pairs, &ip);
for(p=(intpair_t*)utringbuffer_front(pairs);
p!=NULL;
p=(intpair_t*)utringbuffer_next(pairs,p)) {
printf("%d %d\n", p->a, p->b);
}
utringbuffer_free(pairs);
return 0;
}
-------------------------------------------------------------------------------
The real utility of `UT_icd` is apparent when the elements stored in the
ring-buffer are structures that require special work to initialize, copy or
destruct.
For example, when a structure contains pointers to related memory areas that
need to be copied when the structure is copied (and freed when the structure is
freed), we can use custom `init`, `copy`, and `dtor` members in the `UT_icd`.
Here we take an example of a structure that contains an integer and a string.
When this element is copied (such as when an element is pushed),
we want to "deep copy" the `s` pointer (so the original element and the new
element point to their own copies of `s`). When an element is destructed, we
want to "deep free" its copy of `s`. Lastly, this example is written to work
even if `s` has the value `NULL`.
.Structure (complex)
-------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include "utringbuffer.h"
typedef struct {
int a;
char *s;
} intchar_t;
void intchar_copy(void *_dst, const void *_src) {
intchar_t *dst = (intchar_t*)_dst, *src = (intchar_t*)_src;
dst->a = src->a;
dst->s = src->s ? strdup(src->s) : NULL;
}
void intchar_dtor(void *_elt) {
intchar_t *elt = (intchar_t*)_elt;
free(elt->s);
}
UT_icd intchar_icd = {sizeof(intchar_t), NULL, intchar_copy, intchar_dtor};
int main() {
UT_ringbuffer *intchars;
intchar_t ic, *p;
utringbuffer_new(intchars, 2, &intchar_icd);
ic.a=1; ic.s="hello"; utringbuffer_push_back(intchars, &ic);
ic.a=2; ic.s="world"; utringbuffer_push_back(intchars, &ic);
ic.a=3; ic.s="peace"; utringbuffer_push_back(intchars, &ic);
p=NULL;
while( (p=(intchar_t*)utringbuffer_next(intchars,p))) {
printf("%d %s\n", p->a, (p->s ? p->s : "null"));
/* prints "2 world 3 peace" */
}
utringbuffer_free(intchars);
return 0;
}
-------------------------------------------------------------------------------
[[operations]]
Reference
---------
This table lists all the utringbuffer operations. These are loosely based on the C++
vector class.
Operations
~~~~~~~~~~
[width="100%",cols="50<m,40<",grid="none",options="none"]
|===============================================================================
| utringbuffer_new(UT_ringbuffer *a, int n, UT_icd *icd) | allocate a new ringbuffer
| utringbuffer_free(UT_ringbuffer *a) | free an allocated ringbuffer
| utringbuffer_init(UT_ringbuffer *a, int n, UT_icd *icd) | init a ringbuffer (non-alloc)
| utringbuffer_done(UT_ringbuffer *a) | dispose of a ringbuffer (non-alloc)
| utringbuffer_clear(UT_ringbuffer *a) | clear all elements from a, making it empty
| utringbuffer_push_back(UT_ringbuffer *a, element *p) | push element p onto a
| utringbuffer_len(UT_ringbuffer *a) | get length of a
| utringbuffer_empty(UT_ringbuffer *a) | get whether a is empty
| utringbuffer_full(UT_ringbuffer *a) | get whether a is full
| utringbuffer_eltptr(UT_ringbuffer *a, int j) | get pointer of element from index
| utringbuffer_eltidx(UT_ringbuffer *a, element *e) | get index of element from pointer
| utringbuffer_front(UT_ringbuffer *a) | get oldest element of a
| utringbuffer_next(UT_ringbuffer *a, element *e) | get element of a following e (front if e is NULL)
| utringbuffer_prev(UT_ringbuffer *a, element *e) | get element of a before e (back if e is NULL)
| utringbuffer_back(UT_ringbuffer *a) | get newest element of a
|===============================================================================
Notes
~~~~~
1. `utringbuffer_new` and `utringbuffer_free` are used to allocate a new ring-buffer
and to free it,
while `utringbuffer_init` and `utringbuffer_done` can be used if the UT_ringbuffer
is already allocated and just needs to be initialized or have its internal resources
freed.
2. Both `utringbuffer_new` and `utringbuffer_init` take a second parameter `n` indicating
the capacity of the ring-buffer, that is, the size at which the ring-buffer is considered
"full" and begins to overwrite old elements with newly pushed ones.
3. Once a ring-buffer has become full, it will never again become un-full except by
means of `utringbuffer_clear`. There is no way to "pop" a single old item from the
front of the ring-buffer. You can simulate this ability by maintaining a separate
integer count of the number of "logically popped elements", and starting your iteration
with `utringbuffer_eltptr(a, popped_count)` instead of with `utringbuffer_front(a)`.
4. Pointers to elements (obtained using `utringbuffer_eltptr`, `utringbuffer_front`,
`utringbuffer_next`, etc.) are not generally invalidated by `utringbuffer_push_back`,
because utringbuffer does not perform reallocation; however, a pointer to the oldest
element may suddenly turn into a pointer to the 'newest' element if
`utringbuffer_push_back` is called while the buffer is full.
5. The elements of a ring-buffer are stored in contiguous memory, but once the ring-buffer
has become full, it is no longer true that the elements are contiguously in order from
oldest to newest; i.e., `(element *)utringbuffer_front(a) + utringbuffer_len(a)-1` is
not generally equal to `(element *)utringbuffer_back(a)`.
// vim: set nowrap syntax=asciidoc:
|