docs/src/modules-ucx2.md

Fri, 12 Apr 2024 21:48:12 +0200

author
Mike Becker <universe@uap-core.de>
date
Fri, 12 Apr 2024 21:48:12 +0200
changeset 849
edb9f875b7f9
parent 719
034ec7abb83e
permissions
-rw-r--r--

improves interface of cx_sprintf() variants

     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 including the corresponding header
     7 file.
     8 Please note, that some modules make use of other UCX 2.1 modules.
     9 For instance, the [Allocator](#allocator) module is used by many other modules
    10 to allow flexible memory allocation.
    11 By default, the header files are placed into an `ucx` directory within your
    12 systems include directory. In this case you can use a module by including it
    13 via `#include <ucx/MODULENAME.h>`.
    14 Required modules are included automatically.
    16 <div id="modules">
    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 ----------------------- ----------------------  --------------------------------  ---------------------------
    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 destructor 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 const-ness.
   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 ```

mercurial