1 ---
2 title: UCX 2.1 Modules
3 ---
4
5 UCX 2.1 provided several modules for data structures and algorithms.
6 You may choose to use specific modules by including the corresponding header
7 file.
8 Please note, that some modules make use of other UCX 2.1 modules.
9 For instance, the [Allocator](#allocator) module is used by many other modules
10 to allow flexible memory allocation.
11 By default, the header files are placed into an `ucx` directory within your
12 systems include directory. In this case you can use a module by including it
13 via `#include <ucx/MODULENAME.h>`.
14 Required modules are included automatically.
15
16 <div id="modules">
17
18 ----------------------- ---------------------- -------------------------------- ---------------------------
19 [String](#string) [Buffer](#buffer)
20 [Allocator](#allocator) [Stack](#stack) [Memory Pool](#memory-pool)
21 [Array](#array) [List](#list) [Map](#map) [AVL Tree](#avl-tree)
22 [Logging](#logging) [Testing](#testing) [Utilities](#utilities) [Properties](#properties)
23 ----------------------- ---------------------- -------------------------------- ---------------------------
24
25 </div>
26
27 ## Allocator
28
29 *Header file:* [allocator.h](api-2.1/allocator_8h.html)
30 *Required modules:* None.
31
32 A UCX allocator consists of a pointer to the memory area / pool and four
33 function pointers to memory management functions operating on this memory
34 area / pool. These functions shall behave equivalent to the standard libc
35 functions `malloc`, `calloc`, `realloc` and `free`.
36
37 The signature of the memory management functions is based on the signature
38 of the respective libc function but each of them takes the pointer to the
39 memory area / pool as first argument.
40
41 As the pointer to the memory area / pool can be arbitrarily chosen, any data
42 can be provided to the memory management functions. One example is the
43 [UCX Memory Pool](#memory-pool).
44
45 ## Array
46
47 *Header file:* [array.h](api-2.1/array_8h.html)
48 *Required modules:* [Allocator](#allocator)
49
50 The UCX Array is an implementation of a dynamic array with automatic
51 reallocation. The array structure contains a capacity, the current size,
52 the size of each element, the raw pointer to the memory area and an allocator.
53 Arrays are in most cases much faster than linked list.
54 One can decide, whether to create a new array on the heap with `ucx_array_new()`
55 or to save one indirection by initializing a `UcxArray` structure on the stack
56 with `ucx_array_init()`.
57
58 ### Remove duplicates from an array of strings
59
60 The following example shows, how a `UcxArray` can be built with
61 a standard dynamic C array (pointer+length) as basis.
62
63 ```C
64 UcxArray* create_unique(sstr_t* array, size_t arrlen) {
65 // worst case is no duplicates, hence the capacity is set to arrlen
66 UcxArray* result = ucx_array_new(arrlen, sizeof(sstr_t));
67 // only append elements, if they are not already present in the array
68 for (size_t i = 0 ; i < arrlen ; ++i) {
69 if (!ucx_array_contains(result, array+i, ucx_cmp_sstr, NULL)) {
70 ucx_array_append_from(result, array+i, 1);
71 }
72 }
73 // make the array as small as possible
74 ucx_array_shrink(result);
75 return result;
76 }
77
78 // ...
79
80 sstr_t* array = // some standard array of strings
81 size_t arrlen = // the length of the array
82
83 UcxArray* result = create_unique(array,arrlen);
84
85 // Iterate over the array and print the elements
86 sstr_t* unique = result->data;
87 for (size_t i = 0 ; i < result->size ; i++) {
88 printf("%" PRIsstr "\n", SFMT(unique[i]));
89 }
90
91 // Free the array.
92 ucx_array_free(result);
93 ```
94 ### Preventing out of bounds writes
95
96 The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`,
97 and `ucx_array_shrink()` allow easy management of the array capacity.
98 Imagine you want to add `n` elements to an array. If your `n` elements are
99 already somewhere else consecutively in memory, you can use
100 `ucx_array_append_from()` and benefit from the autogrow facility in this family
101 of functions. Otherwise, you can ask the array to have enough capacity for
102 holding additional `n` elements.
103
104 ```C
105 size_t n = // ... elements to add
106 if (ucx_array_grow(array, n)) {
107 fprintf(stderr, "Cannot add %zu elements to the array.\n", n);
108 return 1;
109 }
110 for (size_t i = 0 ; i < n ; i++) {
111 ((int*)array->data)[array->size++] = 80;
112 }
113 ```
114
115 ## AVL Tree
116
117 *Header file:* [avl.h](api-2.1/avl_8h.html)
118 *Required modules:* [Allocator](#allocator)
119
120 This binary search tree implementation allows average O(1) insertion and
121 removal of elements (excluding binary search time).
122 All common binary tree operations are implemented. Furthermore, this module
123 provides search functions via lower and upper bounds.
124
125 ### Filtering items with a time window
126
127 Suppose you have a list of items which contain a `time_t` value and your task
128 is to find all items within a time window `[t_start, t_end]`.
129 With AVL Trees this is easy:
130 ```C
131 // Somewhere in a header
132 typedef struct {
133 time_t ts;
134 // other important data
135 } MyObject;
136
137 // Source code
138 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint);
139 // ... populate tree with objects, use '& MyObject.ts' as key ...
140
141
142 // Now find every item, with 30 <= ts <= 70
143 time_t ts_start = 30;
144 time_t ts_end = 70;
145
146 printf("Values in range:\n");
147 for (
148 UcxAVLNode* node = ucx_avl_find_node(
149 tree, (intptr_t) &ts_start,
150 ucx_dist_longint, UCX_AVL_FIND_LOWER_BOUNDED);
151 node && (*(time_t*)node->key) <= ts_end;
152 node = ucx_avl_succ(node)
153 ) {
154 printf(" ts: %ld\n", ((MyObject*)node->value)->ts);
155 }
156
157 ucx_avl_free_content(tree, free);
158 ucx_avl_free(tree);
159 ```
160
161 ## Buffer
162
163 *Header file:* [buffer.h](api-2.1/buffer_8h.html)
164 *Required modules:* None.
165
166 Instances of this buffer implementation can be used to read from or to write to
167 memory like you would do with a stream. This allows the use of
168 `ucx_stream_copy()` from the [Utilities](#utilities) module to copy contents
169 from one buffer to another or from file or network streams to the buffer and
170 vice-versa.
171
172 More features for convenient use of the buffer can be enabled, like automatic
173 memory management and automatic resizing of the buffer space.
174 See the documentation of the macro constants in the header file for more
175 information.
176
177 ### Add line numbers to a file
178
179 When reading a file line by line, you have three options: first, you could limit
180 the maximum supported line length.
181 Second, you allocate a god buffer large
182 enough for the most lines a text file could have.
183 And third, undoubtedly the best option, you start with a small buffer, which
184 adjusts on demand.
185 An `UcxBuffer` can be created to do just that for you.
186 Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function.
187 Here is a full working program, which adds line numbers to a file.
188 ```C
189 #include <stdio.h>
190 #include <ucx/buffer.h>
191 #include <ucx/utils.h>
192
193 int main(int argc, char** argv) {
194
195 if (argc != 2) {
196 fprintf(stderr, "Usage: %s <file>\n", argv[0]);
197 return 1;
198 }
199
200 FILE* input = fopen(argv[1], "r");
201 if (!input) {
202 perror("Canno read input");
203 return 1;
204 }
205
206 const size_t chunksize = 256;
207
208 UcxBuffer* linebuf =
209 ucx_buffer_new(
210 NULL, // the buffer should manage the memory area for us
211 2*chunksize, // initial size should be twice the chunk size
212 UCX_BUFFER_AUTOEXTEND); // the buffer will grow when necessary
213
214 size_t lineno = 1;
215 do {
216 // read line chunk
217 size_t read = ucx_stream_ncopy(
218 input, linebuf, fread, ucx_buffer_write, chunksize);
219 if (read == 0) break;
220
221 // handle line endings
222 do {
223 sstr_t bufstr = ucx_buffer_to_sstr(linebuf);
224 sstr_t nl = sstrchr(bufstr, '\n');
225 if (nl.length == 0) break;
226
227 size_t linelen = bufstr.length - nl.length;
228 sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
229
230 printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
231
232 // shift the buffer to the next line
233 ucx_buffer_shift_left(linebuf, linelen+1);
234 } while(1);
235
236 } while(1);
237
238 // print the 'noeol' line, if any
239 sstr_t lastline = ucx_buffer_to_sstr(linebuf);
240 if (lastline.length > 0) {
241 printf("%zu: %" PRIsstr, lineno, SFMT(lastline));
242 }
243
244 fclose(input);
245 ucx_buffer_free(linebuf);
246
247 return 0;
248 }
249 ```
250
251 ## List
252
253 *Header file:* [list.h](api-2.1/list_8h.html)
254 *Required modules:* [Allocator](#allocator)
255
256 This module provides the data structure and several functions for a doubly
257 linked list. Among the common operations like insert, remove, search and sort,
258 we allow convenient iteration via a special `UCX_FOREACH` macro.
259
260 ### Remove duplicates from an array of strings
261
262 Assume you are given an array of `sstr_t` and want to create a list of these
263 strings without duplicates.
264 This is a similar example to the one [above](#array), but here we are
265 using a `UcxList`.
266 ```C
267 #include <stdio.h>
268 #include <ucx/list.h>
269 #include <ucx/string.h>
270 #include <ucx/utils.h>
271
272 UcxList* remove_duplicates(sstr_t* array, size_t arrlen) {
273 UcxList* list = NULL;
274 for (size_t i = 0 ; i < arrlen ; ++i) {
275 if (ucx_list_find(list, array+i, ucx_cmp_sstr, NULL) == -1) {
276 sstr_t* s = malloc(sizeof(sstr_t));
277 *s = sstrdup(array[i]);
278 list = ucx_list_append(list, s);
279 }
280 }
281 return list;
282 }
283
284 // we will need this function to clean up the list contents later
285 void free_sstr(void* ptr) {
286 sstr_t* s = ptr;
287 free(s->ptr);
288 free(s);
289 }
290
291 // ...
292
293 sstr_t* array = // some array of strings
294 size_t arrlen = // the length of the array
295
296 UcxList* list = remove_duplicates(array,arrlen);
297
298 // Iterate over the list and print the elements
299 UCX_FOREACH(elem, list) {
300 sstr_t s = *((sstr_t*)elem->data);
301 printf("%" PRIsstr "\n", SFMT(s));
302 }
303
304 // Use our free function to free the duplicated strings.
305 ucx_list_free_content(list, free_sstr);
306 ucx_list_free(list);
307 ```
308
309 ## Logging
310
311 *Header file:* [logging.h](api-2.1/logging_8h.html)
312 *Required modules:* [Map](#map), [String](#string)
313
314 The logging module comes with some predefined log levels and allows some more
315 customization. You may choose if you want to get timestamps or source file and
316 line number logged automatically when outputting a message.
317 The following function call initializes a debug logger with all of the above
318 information:
319 ```C
320 log = ucx_logger_new(stdout, UCX_LOGGER_DEBUG,
321 UCX_LOGGER_LEVEL | UCX_LOGGER_TIMESTAMP | UCX_LOGGER_SOURCE);
322 ```
323 Afterwards you can use this logger with the predefined macros
324 ```C
325 ucx_logger_trace(log, "Verbose output");
326 ucx_logger_debug(log, "Debug message");
327 ucx_logger_info(log, "Information");
328 ucx_logger_warn(log, "Warning");
329 ucx_logger_error(log, "Error message");
330 ```
331 or you use
332 ```C
333 ucx_logger_log(log, CUSTOM_LEVEL, "Some message")
334 ```
335 When you use your custom log level, don't forget to register it with
336 ```C
337 ucx_logger_register_level(log, CUSTOM_LEVEL, "CUSTOM")
338 ```
339 where the last argument must be a string literal.
340
341 ## Map
342
343 *Header file:* [map.h](api-2.1/map_8h.html)
344 *Required modules:* [Allocator](#allocator), [String](#string)
345
346 This module provides a hash map implementation using murmur hash 2 and separate
347 chaining with linked lists. Similarly to the list module, we provide a
348 `UCX_MAP_FOREACH` macro to conveniently iterate through the key/value pairs.
349
350 ### Parsing command line options
351
352 Assume you want to parse command line options and record them within a map.
353 One way to do this is shown by the following code sample:
354 ```C
355 UcxMap* options = ucx_map_new(16);
356 const char *NOARG = "";
357
358 char *option = NULL;
359 char optchar = 0;
360 for(int i=1;i<argc;i++) {
361 char *arg = argv[i];
362 size_t len = strlen(arg);
363 if(len > 1 && arg[0] == '-') {
364 for(int c=1;c<len;c++) {
365 if(option) {
366 fprintf(stderr,
367 "Missing argument for option -%c\n", optchar);
368 return 1;
369 }
370 switch(arg[c]) {
371 default: {
372 fprintf(stderr, "Unknown option -%c\n\n", arg[c]);
373 return 1;
374 }
375 case 'v': {
376 ucx_map_cstr_put(options, "verbose", NOARG);
377 break;
378 }
379 case 'o': {
380 option = "output";
381 optchar = 'o';
382 break;
383 }
384 }
385 }
386 } else if(option) {
387 ucx_map_cstr_put(options, option, arg);
388 option = NULL;
389 } else {
390 // ... handle argument that is not an option ...
391 }
392 }
393 if(option) {
394 fprintf(stderr,
395 "Missing argument for option -%c\n", optchar);
396 return 1;
397 }
398 ```
399 With the following loop, you can access the previously recorded options:
400 ```C
401 UcxMapIterator iter = ucx_map_iterator(options);
402 char *arg;
403 UCX_MAP_FOREACH(optkey, arg, iter) {
404 char* opt = optkey.data;
405 if (*arg) {
406 printf("%s = %s\n", opt, arg);
407 } else {
408 printf("%s active\n", opt);
409 }
410 }
411 ```
412 Don't forget to call `ucx_map_free()`, when you are done with the map.
413
414 ## Memory Pool
415
416 *Header file:* [mempool.h](api-2.1/mempool_8h.html)
417 *Required modules:* [Allocator](#allocator)
418
419 Here we have a concrete allocator implementation in the sense of a memory pool.
420 This pool allows you to register destructor functions for the allocated memory,
421 which are automatically called on the destruction of the pool.
422 But you may also register *independent* destructor functions within a pool in
423 case some external library allocated memory for you, which should be
424 destroyed together with this pool.
425
426 Many UCX modules support the use of an allocator.
427 The [String Module](#string), for instance, provides the `sstrdup_a()` function,
428 which uses the specified allocator to allocate the memory for the duplicated
429 string.
430 This way, you can use a `UcxMempool` to keep track of the memory occupied by
431 duplicated strings and cleanup everything with just a single call to
432 `ucx_mempool_destroy()`.
433
434 ### Read CSV data into a structure
435
436 The following code example shows some of the basic memory pool functions and
437 how they can be used with other UCX modules.
438 ```C
439 #include <stdio.h>
440 #include <ucx/mempool.h>
441 #include <ucx/list.h>
442 #include <ucx/string.h>
443 #include <ucx/buffer.h>
444 #include <ucx/utils.h>
445
446 typedef struct {
447 sstr_t column_a;
448 sstr_t column_b;
449 sstr_t column_c;
450 } CSVData;
451
452 int main(int argc, char** argv) {
453
454 UcxMempool* pool = ucx_mempool_new(128);
455
456 FILE *f = fopen("test.csv", "r");
457 if (!f) {
458 perror("Cannot open file");
459 return 1;
460 }
461 // close the file automatically at pool destruction
462 ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose);
463
464 // create a buffer and register it at the memory pool for destruction
465 UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
466 ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free);
467
468 // read the file and split it by lines first
469 ucx_stream_copy(f, content, fread, ucx_buffer_write);
470 sstr_t contentstr = ucx_buffer_to_sstr(content);
471 ssize_t lc = 0;
472 sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc);
473
474 // skip the header and parse the remaining data
475 UcxList* datalist = NULL;
476 for (size_t i = 1 ; i < lc ; i++) {
477 if (lines[i].length == 0) continue;
478 ssize_t fc = 3;
479 sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc);
480 if (fc != 3) {
481 fprintf(stderr, "Syntax error in line %zu.\n", i);
482 ucx_mempool_destroy(pool);
483 return 1;
484 }
485 CSVData* data = ucx_mempool_malloc(pool, sizeof(CSVData));
486 data->column_a = fields[0];
487 data->column_b = fields[1];
488 data->column_c = fields[2];
489 datalist = ucx_list_append_a(pool->allocator, datalist, data);
490 }
491
492 // control output
493 UCX_FOREACH(elem, datalist) {
494 CSVData* data = elem->data;
495 printf("Column A: %" PRIsstr " | "
496 "Column B: %" PRIsstr " | "
497 "Column C: %" PRIsstr "\n",
498 SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c)
499 );
500 }
501
502 // cleanup everything, no manual free() needed
503 ucx_mempool_destroy(pool);
504
505 return 0;
506 }
507 ```
508
509 ### Overriding the default destructor
510
511 Sometimes you need to allocate memory with `ucx_mempool_malloc()`, but the
512 memory is not supposed to be freed with a simple call to `free()`.
513 In this case, you can overwrite the default destructor as follows:
514 ```C
515 MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject));
516
517 // some special initialization with own resource management
518 my_object_init(obj);
519
520 // register destructor function
521 ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy);
522 ```
523 Be aware, that your destructor function should not free any memory, that is
524 also managed by the pool.
525 Otherwise, you might be risking a double-free.
526 More precisely, a destructor function set with `ucx_mempool_set_destr()` MUST
527 NOT call `free()` on the specified pointer whereas a destructor function
528 registered with `ucx_mempool_reg_destr()` MAY (and in most cases will) call
529 `free()`.
530
531 ## Properties
532
533 *Header file:* [properties.h](api-2.1/properties_8h.html)
534 *Required modules:* [Map](#map)
535
536 This module provides load and store function for `*.properties` files.
537 The key/value pairs are stored within an UCX Map.
538
539 ### Example: Loading properties from a file
540
541 ```C
542 // Open the file as usual
543 FILE* file = fopen("myprops.properties", "r");
544 if (!file) {
545 // error handling
546 return 1;
547 }
548
549 // Load the properties from the file
550 UcxMap* myprops = ucx_map_new(16);
551 if (ucx_properties_load(myprops, file)) {
552 // ... error handling ...
553 fclose(file);
554 ucx_map_free(myprops);
555 return 1;
556 }
557
558 // Print out the key/value pairs
559 char* propval;
560 UcxMapIterator propiter = ucx_map_iterator(myprops);
561 UCX_MAP_FOREACH(key, propval, propiter) {
562 printf("%s = %s\n", (char*)key.data, propval);
563 }
564
565 // Don't forget to free the values before freeing the map
566 ucx_map_free_content(myprops, NULL);
567 ucx_map_free(myprops);
568 fclose(file);
569 ```
570
571 ## Stack
572
573 *Header file:* [stack.h](api-2.1/stack_8h.html)
574 *Required modules:* [Allocator](#allocator)
575
576 This concrete implementation of an UCX Allocator allows you to grab some amount
577 of memory which is then handled as a stack.
578 Please note, that the term *stack* only refers to the behavior of this
579 allocator. You may still choose to use either stack or heap memory
580 for the underlying space.
581 A typical use case is an algorithm where you need to allocate and free large
582 amounts of memory very frequently.
583
584 The following code sample shows how to initialize a stack and push and pop
585 simple data.
586 ```C
587 const size_t len = 1024;
588 char space[len];
589 UcxStack stack;
590 ucx_stack_init(&stack, space, len);
591
592 int i = 42;
593 float f = 3.14f;
594 const char* str = "Hello!";
595 size_t strn = 7;
596
597 // push the integer
598 ucx_stack_push(&stack, sizeof(int), &i);
599
600 // push the float and rember the address
601 float* remember = ucx_stack_push(&stack, sizeof(float), &f);
602
603 // push the string with zero terminator
604 ucx_stack_push(&stack, strn, str);
605
606 // if we forget, how big an element was, we can ask the stack
607 printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1);
608
609 // retrieve the string as sstr_t, without zero terminator!
610 sstr_t s;
611 s.length = ucx_stack_topsize(&stack)-1;
612 s.ptr = malloc(s.length);
613 ucx_stack_popn(&stack, s.ptr, s.length);
614 printf("%" PRIsstr "\n", SFMT(s));
615
616 // print the float directly from the stack and free it
617 printf("Float: %f\n", *remember);
618 ucx_stack_free(&stack, remember);
619
620 // the last element is the integer
621 int j;
622 ucx_stack_pop(&stack, &j);
623 printf("Integer: %d\n", j);
624 ```
625
626
627
628 ## String
629
630 *Header file:* [string.h](api-2.1/string_8h.html)
631 *Required modules:* [Allocator](#allocator)
632
633 This module provides a safe implementation of bounded string.
634 Usually C strings do not carry a length. While for zero-terminated strings you
635 can easily get the length with `strlen`, this is not generally possible for
636 arbitrary strings.
637 The `sstr_t` type of this module always carries the string and its length to
638 reduce the risk of buffer overflows dramatically.
639
640 ### Initialization
641
642 There are several ways to create an `sstr_t`:
643
644 ```C
645 // (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated
646 sstr_t a = sstr(cstr);
647
648 // (2) cstr does not need to be zero-terminated, if length is specified
649 sstr_t b = sstrn(cstr, len);
650
651 // (3) S() macro creates sstr_t from a string using sizeof() and using sstrn().
652 // This version is especially useful for function arguments
653 sstr_t c = S("hello");
654
655 // (4) SC() macro works like S(), but makes the string immutable using scstr_t.
656 // (available since UCX 2.0)
657 scstr_t d = SC("hello");
658
659 // (5) ST() macro creates sstr_t struct literal using sizeof()
660 sstr_t e = ST("hello");
661 ```
662
663 You should not use the `S()`, `SC()`, or `ST()` macro with string of unknown
664 origin, since the `sizeof()` call might not coincide with the string length in
665 those cases. If you know what you are doing, it can save you some performance,
666 because you do not need the `strlen()` call.
667
668 ### Handling immutable strings
669
670 *(Since: UCX 2.0)*
671
672 For immutable strings (i.e. `const char*` strings), UCX provides the `scstr_t`
673 type, which works exactly as the `sstr_t` type but with a pointer
674 to `const char`. All UCX string functions come in two flavors: one that enforces
675 the `scstr_t` type, and another that usually accepts both types and performs
676 a conversion automatically, if necessary.
677
678 There are some exceptions to this rule, as the return type may depend on the
679 argument type.
680 E.g. the `sstrchr()` function returns a substring starting at
681 the first occurrence of the specified character.
682 Since this substring points to the memory of the argument string, it does not
683 accept `scstr_t` as input argument, because the return type would break the
684 const-ness.
685
686
687 ### Finding the position of a substring
688
689 The `sstrstr()` function gives you a new `sstr_t` object starting with the
690 requested substring. Thus determining the position comes down to a simple
691 subtraction.
692
693 ```C
694 sstr_t haystack = ST("Here we go!");
695 sstr_t needle = ST("we");
696 sstr_t result = sstrstr(haystack, needle);
697 if (result.ptr)
698 printf("Found at position %zd.\n", haystack.length-result.length);
699 else
700 printf("Not found.\n");
701 ```
702
703 ### Spliting a string by a delimiter
704
705 The `sstrsplit()` function (and its allocator based version `sstrsplit_a()`) is
706 very powerful and might look a bit nasty at a first glance. But it is indeed
707 very simple to use. It is even more convenient in combination with a memory
708 pool.
709
710 ```C
711 sstr_t test = ST("here::are::some::strings");
712 sstr_t delim = ST("::");
713
714 ssize_t count = 0; // no limit
715 UcxMempool* pool = ucx_mempool_new_default();
716
717 sstr_t* result = sstrsplit_a(pool->allocator, test, delim, &count);
718 for (ssize_t i = 0 ; i < count ; i++) {
719 // don't forget to specify the length via the %*s format specifier
720 printf("%*s\n", result[i].length, result[i].ptr);
721 }
722
723 ucx_mempool_destroy(pool);
724 ```
725 The output is:
726
727 here
728 are
729 some
730 strings
731
732 The memory pool ensures, that all strings are freed.
733
734 ### Disabling convenience macros
735
736 If you are experiencing any troubles with the short convenience macros `S()`,
737 `SC()`, or `ST()`, you can disable them by setting the macro
738 `UCX_NO_SSTR_SHORTCUTS` before including the header (or via a compiler option).
739 For the formatting macros `SFMT()` and `PRIsstr` you can use the macro
740 `UCX_NO_SSTR_FORMAT_MACROS` to disable them.
741
742 Please keep in mind, that after disabling the macros, you cannot use them in
743 your code *and* foreign code that you might have included.
744 You should only disable the macros, if you are experiencing a nasty name clash
745 which cannot be otherwise resolved.
746
747 ## Testing
748
749 *Header file:* [test.h](api-2.1/test_8h.html)
750 *Required modules:* None.
751
752 This module provides a testing framework which allows you to execute test cases
753 within test suites.
754 To avoid code duplication within tests, we also provide the possibility to
755 define test subroutines.
756
757 You should declare test cases and subroutines in a header file per test unit
758 and implement them as you would implement normal functions.
759 ```C
760 // myunit.h
761 UCX_TEST(function_name);
762 UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
763
764
765 // myunit.c
766 UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
767 // ... reusable tests with UCX_TEST_ASSERT() ...
768 }
769
770 UCX_TEST(function_name) {
771 // ... resource allocation and other test preparation ...
772
773 // mandatory marker for the start of the tests
774 UCX_TEST_BEGIN
775
776 // ... verifications with UCX_TEST_ASSERT() ...
777 // (and/or calls with UCX_TEST_CALL_SUBROUTINE())
778
779 // mandatory marker for the end of the tests
780 UCX_TEST_END
781
782 // ... resource cleanup ...
783 // (all code after UCX_TEST_END is always executed)
784 }
785 ```
786 If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are
787 *required* to use a `UCX_TEST_SUBROUTINE`.
788 Otherwise, the testing framework does not know where to jump, when the assertion
789 fails.
790
791 After implementing the tests, you can easily build a test suite and execute it:
792 ```C
793 UcxTestSuite* suite = ucx_test_suite_new();
794 ucx_test_register(suite, testMyTestCase01);
795 ucx_test_register(suite, testMyTestCase02);
796 // ...
797 ucx_test_run(suite, stdout); // stdout, or any other FILE stream
798 ```
799
800 ## Utilities
801
802 *Header file:* [utils.h](api-2.1/utils_8h.html)
803 *Required modules:* [Allocator](#allocator), [String](#string)
804
805 In this module we provide very general utility function for copy and compare
806 operations.
807 We also provide several `printf` variants to conveniently print formatted data
808 to streams or strings.
809
810 ### A simple copy program
811
812 The utilities package provides several stream copy functions.
813 One of them has a very simple interface and can, for instance, be used to copy
814 whole files in a single call.
815 This is a minimal working example:
816 ```C
817 #include <stdio.h>
818 #include <ucx/utils.h>
819
820 int main(int argc, char** argv) {
821
822 if (argc != 3) {
823 fprintf(stderr, "Use %s <src> <dest>", argv[0]);
824 return 1;
825 }
826
827 FILE *srcf = fopen(argv[1], "r"); // insert error handling on your own
828 FILE *destf = fopen(argv[2], "w");
829
830 size_t n = ucx_stream_copy(srcf, destf, fread, fwrite);
831 printf("%zu bytes copied.\n", n);
832
833 fclose(srcf);
834 fclose(destf);
835
836
837 return 0;
838 }
839 ```
840
841 ### Automatic allocation for formatted strings
842
843 The UCX utility function `ucx_asprintf()` and it's convenient shortcut
844 `ucx_sprintf` allow easy formatting of strings, without ever having to worry
845 about the required space.
846 ```C
847 sstr_t mystring = ucx_sprintf("The answer is: %d!", 42);
848 ```
849 Still, you have to pass `mystring.ptr` to `free()` (or the free function of
850 your allocator, if you use `ucx_asprintf`).
851 If you don't have all the information ready to build your string, you can even
852 use a [UcxBuffer](#buffer) as a target with the utility function
853 `ucx_bprintf()`.
854 ```C
855 UcxBuffer* strbuffer = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
856
857 for (unsigned int i = 2 ; i < 100 ; i++) {
858 ucx_bprintf(strbuffer, "Integer %d is %s\n",
859 i, prime(i) ? "prime" : "not prime");
860 }
861
862 // print the result to stdout
863 printf("%s", (char*)strbuffer->space);
864
865 ucx_buffer_free(strbuffer);
866 ```
867