docs/src/modules-ucx2.md

Sat, 06 Feb 2021 19:11:44 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 06 Feb 2021 19:11:44 +0100
changeset 390
d345541018fa
parent 370
docs/src/modules.md@07ac32b385e4
child 628
1e2be40f0cb5
permissions
-rw-r--r--

starts ucx 3.0 development

     1 ---
     2 title: UCX 2.1 Modules
     3 ---
     5 UCX 2.1 provided several modules for data structures and algorithms.
     6 You may choose to use specific modules by inclueding the corresponding header
     7 file.
     8 Please note, that some modules make use of other UCX 2.1 modules.
     9 For instance, the [Allocator](#allocator) module is used by many other modules
    10 to allow flexible memory allocation.
    11 By default the header files are placed into an `ucx` directory within your
    12 systems include directory. In this case you can use a module by including it
    13 via `#include <ucx/MODULENAME.h>`.
    14 Required modules are included automatically.
    16 <div id="modules" align="center">
    18 ----------------------- ----------------------  --------------------------------  ---------------------------
    19 [String](#string)       [Buffer](#buffer)
    20 [Allocator](#allocator) [Stack](#stack)         [Memory&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 /* ---------------------
   132  * Somewhere in a header
   133  */
   134 typedef struct {
   135     time_t ts;
   136     /* other important data */
   137 } MyObject;
   139 /* -----------
   140  * Source code
   141  */
   143 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint);
   144 /* ... populate tree with objects, use '& MyObject.ts' as key ... */
   147 /* Now find every item, with 30 <= ts <= 70 */
   148 time_t ts_start = 30;
   149 time_t ts_end = 70;
   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 }
   162 ucx_avl_free_content(tree, free);
   163 ucx_avl_free(tree);
   164 ```
   166 ## Buffer
   168 *Header file:* [buffer.h](api-2.1/buffer_8h.html)  
   169 *Required modules:* None.
   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.
   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.
   182 ### Add line numbers to a file
   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>
   198 int main(int argc, char** argv) {
   200     if (argc != 2) {
   201         fprintf(stderr, "Usage: %s <file>\n", argv[0]);
   202         return 1;
   203     }
   205     FILE* input = fopen(argv[1], "r");
   206     if (!input) {
   207         perror("Canno read input");
   208         return 1;
   209     }
   211     const size_t chunksize = 256;
   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 */
   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;
   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;
   232             size_t linelen = bufstr.length - nl.length;
   233             sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
   235             printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
   237             /* shift the buffer to the next line */
   238             ucx_buffer_shift_left(linebuf, linelen+1);
   239         } while(1);
   241     } while(1);
   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     }
   249     fclose(input);
   250     ucx_buffer_free(linebuf);
   252     return 0;
   253 }
   254 ```
   256 ## List
   258 *Header file:* [list.h](api-2.1/list_8h.html)  
   259 *Required modules:* [Allocator](#allocator)
   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.
   265 ### Remove duplicates from an array of strings
   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>
   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 }
   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 }
   296 /* ... */
   298 sstr_t* array = /* some array of strings */
   299 size_t arrlen = /* the length of the array */
   301 UcxList* list = remove_duplicates(array,arrlen);
   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 }
   309 /* Use our free function to free the duplicated strings. */
   310 ucx_list_free_content(list, free_sstr);
   311 ucx_list_free(list);
   312 ```
   314 ## Logging
   316 *Header file:* [logging.h](api-2.1/logging_8h.html)  
   317 *Required modules:* [Map](#map), [String](#string)
   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.
   346 ## Map
   348 *Header file:* [map.h](api-2.1/map_8h.html)  
   349 *Required modules:* [Allocator](#allocator), [String](#string)
   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.
   355 ### Parsing command line options
   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 = "";
   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.
   419 ## Memory Pool
   421 *Header file:* [mempool.h](api-2.1/mempool_8h.html)  
   422 *Required modules:* [Allocator](#allocator)
   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.
   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()`.
   439 ### Read CSV data into a structure
   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>
   451 typedef struct {
   452     sstr_t column_a;
   453     sstr_t column_b;
   454     sstr_t column_c;
   455 } CSVData;
   457 int main(int argc, char** argv) {
   459     UcxMempool* pool = ucx_mempool_new(128);
   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);
   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);
   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);
   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     }
   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     }
   507     /* cleanup everything, no manual free() needed */
   508     ucx_mempool_destroy(pool);
   510     return 0;
   511 } 
   512 ```
   514 ### Overriding the default destructor
   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));
   522     /* some special initialization with own resource management */
   523     my_object_init(obj);
   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()`.
   536 ## Properties
   538 *Header file:* [properties.h](api-2.1/properties_8h.html)  
   539 *Required modules:* [Map](#map)
   541 This module provides load and store function for `*.properties` files.
   542 The key/value pairs are stored within an UCX Map.
   544 ### Example: Loading properties from a file
   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 }
   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 }
   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 }
   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 ```
   576 ## Stack
   578 *Header file:* [stack.h](api-2.1/stack_8h.html)  
   579 *Required modules:* [Allocator](#allocator)
   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.
   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);
   597     int i = 42;
   598     float f = 3.14f;
   599     const char* str = "Hello!";
   600     size_t strn = 7;
   602     /* push the integer */
   603     ucx_stack_push(&stack, sizeof(int), &i);
   605     /* push the float and rember the address */
   606     float* remember = ucx_stack_push(&stack, sizeof(float), &f);
   608     /* push the string with zero terminator */
   609     ucx_stack_push(&stack, strn, str);
   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);
   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));
   621     /* print the float directly from the stack and free it */
   622     printf("Float: %f\n", *remember);
   623     ucx_stack_free(&stack, remember);
   625     /* the last element is the integer */
   626     int j;
   627     ucx_stack_pop(&stack, &j);
   628     printf("Integer: %d\n", j);
   629 ```
   633 ## String
   635 *Header file:* [string.h](api-2.1/string_8h.html)  
   636 *Required modules:* [Allocator](#allocator)
   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.
   645 ### Initialization
   647 There are several ways to create an `sstr_t`:
   649 ```C
   650 /* (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated */
   651 sstr_t a = sstr(cstr);
   653 /* (2) cstr does not need to be zero-terminated, if length is specified */
   654 sstr_t b = sstrn(cstr, len);
   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");
   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");
   664 /* (5) ST() macro creates sstr_t struct literal using sizeof() */
   665 sstr_t e = ST("hello");
   666 ```
   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.
   673 ### Handling immutable strings
   675 *(Since: UCX 2.0)*
   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.
   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.
   692 ### Finding the position of a substring
   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.
   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 ```
   708 ### Spliting a string by a delimiter
   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.
   715 ```C
   716 sstr_t test = ST("here::are::some::strings");
   717 sstr_t delim = ST("::");
   719 ssize_t count = 0; /* no limit */
   720 UcxMempool* pool = ucx_mempool_new_default();
   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 }
   728 ucx_mempool_destroy(pool);
   729 ```
   730 The output is:
   732     here
   733     are
   734     some
   735     strings
   737 The memory pool ensures, that all strings are freed.
   739 ### Disabling convenience macros
   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.
   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.
   752 ## Testing
   754 *Header file:* [test.h](api-2.1/test_8h.html)  
   755 *Required modules:* None.
   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.
   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 */
   770     /* myunit.c */
   771     UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
   772         /* ... reusable tests with UCX_TEST_ASSERT() ... */
   773     }
   775     UCX_TEST(function_name) {
   776         /* ... resource allocation and other test preparation ... */
   778         /* mandatory marker for the start of the tests */
   779         UCX_TEST_BEGIN
   781         /*  ... verifications with UCX_TEST_ASSERT() ...
   782          * (and/or calls with UCX_TEST_CALL_SUBROUTINE())
   783          */
   785         /* mandatory marker for the end of the tests */
   786         UCX_TEST_END
   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.
   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 ```
   807 ## Utilities
   809 *Header file:* [utils.h](api-2.1/utils_8h.html)  
   810 *Required modules:* [Allocator](#allocator), [String](#string)
   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.
   817 ### A simple copy program
   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>
   827 int main(int argc, char** argv) {
   829     if (argc != 3) {
   830         fprintf(stderr, "Use %s <src> <dest>", argv[0]);
   831         return 1;
   832     }
   834     FILE *srcf = fopen(argv[1], "r");   /* insert error handling on your own */
   835     FILE *destf = fopen(argv[2], "w");
   837     size_t n =  ucx_stream_copy(srcf, destf, fread, fwrite);
   838     printf("%zu bytes copied.\n", n);
   840     fclose(srcf);
   841     fclose(destf);
   844     return 0;
   845 }
   846 ```
   848 ### Automatic allocation for formatted strings
   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);
   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 }
   869 /* print the result to stdout */
   870 printf("%s", (char*)strbuffer->space);
   872 ucx_buffer_free(strbuffer);
   873 ```

mercurial