docs/src/modules-ucx2.md

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

mercurial