Research portable Memory game | Исследовать портируемую игру Память
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
13KB

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