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