Sun, 09 Apr 2023 19:37:00 +0200
add pointer array list tests
1 ---
2 title: UCX 2.1 Modules
3 ---
5 UCX 2.1 provided several modules for data structures and algorithms.
6 You may choose to use specific modules by inclueding 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.
16 <div id="modules" align="center">
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 ----------------------- ---------------------- -------------------------------- ---------------------------
25 </div>
27 ## Allocator
29 *Header file:* [allocator.h](api-2.1/allocator_8h.html)
30 *Required modules:* None.
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`.
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.
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).
45 ## Array
47 *Header file:* [array.h](api-2.1/array_8h.html)
48 *Required modules:* [Allocator](#allocator)
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()`.
58 ### Remove duplicates from an array of strings
60 The following example shows, how a `UcxArray` can be built with
61 a standard dynamic C array (pointer+length) as basis.
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 }
78 // ...
80 sstr_t* array = // some standard array of strings
81 size_t arrlen = // the length of the array
83 UcxArray* result = create_unique(array,arrlen);
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 }
91 // Free the array.
92 ucx_array_free(result);
93 ```
94 ### Preventing out of bounds writes
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.
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 ```
115 ## AVL Tree
117 *Header file:* [avl.h](api-2.1/avl_8h.html)
118 *Required modules:* [Allocator](#allocator)
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.
125 ### Filtering items with a time window
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;
137 // Source code
138 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint);
139 // ... populate tree with objects, use '& MyObject.ts' as key ...
142 // Now find every item, with 30 <= ts <= 70
143 time_t ts_start = 30;
144 time_t ts_end = 70;
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 }
157 ucx_avl_free_content(tree, free);
158 ucx_avl_free(tree);
159 ```
161 ## Buffer
163 *Header file:* [buffer.h](api-2.1/buffer_8h.html)
164 *Required modules:* None.
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.
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.
177 ### Add line numbers to a file
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>
193 int main(int argc, char** argv) {
195 if (argc != 2) {
196 fprintf(stderr, "Usage: %s <file>\n", argv[0]);
197 return 1;
198 }
200 FILE* input = fopen(argv[1], "r");
201 if (!input) {
202 perror("Canno read input");
203 return 1;
204 }
206 const size_t chunksize = 256;
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
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;
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;
227 size_t linelen = bufstr.length - nl.length;
228 sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
230 printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
232 // shift the buffer to the next line
233 ucx_buffer_shift_left(linebuf, linelen+1);
234 } while(1);
236 } while(1);
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 }
244 fclose(input);
245 ucx_buffer_free(linebuf);
247 return 0;
248 }
249 ```
251 ## List
253 *Header file:* [list.h](api-2.1/list_8h.html)
254 *Required modules:* [Allocator](#allocator)
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.
260 ### Remove duplicates from an array of strings
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>
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 }
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 }
291 // ...
293 sstr_t* array = // some array of strings
294 size_t arrlen = // the length of the array
296 UcxList* list = remove_duplicates(array,arrlen);
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 }
304 // Use our free function to free the duplicated strings.
305 ucx_list_free_content(list, free_sstr);
306 ucx_list_free(list);
307 ```
309 ## Logging
311 *Header file:* [logging.h](api-2.1/logging_8h.html)
312 *Required modules:* [Map](#map), [String](#string)
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.
341 ## Map
343 *Header file:* [map.h](api-2.1/map_8h.html)
344 *Required modules:* [Allocator](#allocator), [String](#string)
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.
350 ### Parsing command line options
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 = "";
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.
414 ## Memory Pool
416 *Header file:* [mempool.h](api-2.1/mempool_8h.html)
417 *Required modules:* [Allocator](#allocator)
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.
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()`.
434 ### Read CSV data into a structure
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>
446 typedef struct {
447 sstr_t column_a;
448 sstr_t column_b;
449 sstr_t column_c;
450 } CSVData;
452 int main(int argc, char** argv) {
454 UcxMempool* pool = ucx_mempool_new(128);
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);
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);
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);
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 }
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 }
502 // cleanup everything, no manual free() needed
503 ucx_mempool_destroy(pool);
505 return 0;
506 }
507 ```
509 ### Overriding the default destructor
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));
517 // some special initialization with own resource management
518 my_object_init(obj);
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 desructor function
528 registered with `ucx_mempool_reg_destr()` MAY (and in most cases will) call
529 `free()`.
531 ## Properties
533 *Header file:* [properties.h](api-2.1/properties_8h.html)
534 *Required modules:* [Map](#map)
536 This module provides load and store function for `*.properties` files.
537 The key/value pairs are stored within an UCX Map.
539 ### Example: Loading properties from a file
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 }
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 }
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 }
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 ```
571 ## Stack
573 *Header file:* [stack.h](api-2.1/stack_8h.html)
574 *Required modules:* [Allocator](#allocator)
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.
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);
592 int i = 42;
593 float f = 3.14f;
594 const char* str = "Hello!";
595 size_t strn = 7;
597 // push the integer
598 ucx_stack_push(&stack, sizeof(int), &i);
600 // push the float and rember the address
601 float* remember = ucx_stack_push(&stack, sizeof(float), &f);
603 // push the string with zero terminator
604 ucx_stack_push(&stack, strn, str);
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);
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));
616 // print the float directly from the stack and free it
617 printf("Float: %f\n", *remember);
618 ucx_stack_free(&stack, remember);
620 // the last element is the integer
621 int j;
622 ucx_stack_pop(&stack, &j);
623 printf("Integer: %d\n", j);
624 ```
628 ## String
630 *Header file:* [string.h](api-2.1/string_8h.html)
631 *Required modules:* [Allocator](#allocator)
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.
640 ### Initialization
642 There are several ways to create an `sstr_t`:
644 ```C
645 // (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated
646 sstr_t a = sstr(cstr);
648 // (2) cstr does not need to be zero-terminated, if length is specified
649 sstr_t b = sstrn(cstr, len);
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");
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");
659 // (5) ST() macro creates sstr_t struct literal using sizeof()
660 sstr_t e = ST("hello");
661 ```
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.
668 ### Handling immutable strings
670 *(Since: UCX 2.0)*
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.
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 constness.
687 ### Finding the position of a substring
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.
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 ```
703 ### Spliting a string by a delimiter
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.
710 ```C
711 sstr_t test = ST("here::are::some::strings");
712 sstr_t delim = ST("::");
714 ssize_t count = 0; // no limit
715 UcxMempool* pool = ucx_mempool_new_default();
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 }
723 ucx_mempool_destroy(pool);
724 ```
725 The output is:
727 here
728 are
729 some
730 strings
732 The memory pool ensures, that all strings are freed.
734 ### Disabling convenience macros
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.
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.
747 ## Testing
749 *Header file:* [test.h](api-2.1/test_8h.html)
750 *Required modules:* None.
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.
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
765 // myunit.c
766 UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
767 // ... reusable tests with UCX_TEST_ASSERT() ...
768 }
770 UCX_TEST(function_name) {
771 // ... resource allocation and other test preparation ...
773 // mandatory marker for the start of the tests
774 UCX_TEST_BEGIN
776 // ... verifications with UCX_TEST_ASSERT() ...
777 // (and/or calls with UCX_TEST_CALL_SUBROUTINE())
779 // mandatory marker for the end of the tests
780 UCX_TEST_END
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.
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 ```
800 ## Utilities
802 *Header file:* [utils.h](api-2.1/utils_8h.html)
803 *Required modules:* [Allocator](#allocator), [String](#string)
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.
810 ### A simple copy program
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>
820 int main(int argc, char** argv) {
822 if (argc != 3) {
823 fprintf(stderr, "Use %s <src> <dest>", argv[0]);
824 return 1;
825 }
827 FILE *srcf = fopen(argv[1], "r"); // insert error handling on your own
828 FILE *destf = fopen(argv[2], "w");
830 size_t n = ucx_stream_copy(srcf, destf, fread, fwrite);
831 printf("%zu bytes copied.\n", n);
833 fclose(srcf);
834 fclose(destf);
837 return 0;
838 }
839 ```
841 ### Automatic allocation for formatted strings
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);
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 }
862 // print the result to stdout
863 printf("%s", (char*)strbuffer->space);
865 ucx_buffer_free(strbuffer);
866 ```