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