1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/docs/src/modules-ucx2.md Sat Feb 06 19:11:44 2021 +0100 1.3 @@ -0,0 +1,873 @@ 1.4 +--- 1.5 +title: UCX 2.1 Modules 1.6 +--- 1.7 + 1.8 +UCX 2.1 provided several modules for data structures and algorithms. 1.9 +You may choose to use specific modules by inclueding the corresponding header 1.10 +file. 1.11 +Please note, that some modules make use of other UCX 2.1 modules. 1.12 +For instance, the [Allocator](#allocator) module is used by many other modules 1.13 +to allow flexible memory allocation. 1.14 +By default the header files are placed into an `ucx` directory within your 1.15 +systems include directory. In this case you can use a module by including it 1.16 +via `#include <ucx/MODULENAME.h>`. 1.17 +Required modules are included automatically. 1.18 + 1.19 +<div id="modules" align="center"> 1.20 + 1.21 +----------------------- ---------------------- -------------------------------- --------------------------- 1.22 +[String](#string) [Buffer](#buffer) 1.23 +[Allocator](#allocator) [Stack](#stack) [Memory Pool](#memory-pool) 1.24 +[Array](#array) [List](#list) [Map](#map) [AVL Tree](#avl-tree) 1.25 +[Logging](#logging) [Testing](#testing) [Utilities](#utilities) [Properties](#properties) 1.26 +----------------------- ---------------------- -------------------------------- --------------------------- 1.27 + 1.28 +</div> 1.29 + 1.30 +## Allocator 1.31 + 1.32 +*Header file:* [allocator.h](api-2.1/allocator_8h.html) 1.33 +*Required modules:* None. 1.34 + 1.35 +A UCX allocator consists of a pointer to the memory area / pool and four 1.36 +function pointers to memory management functions operating on this memory 1.37 +area / pool. These functions shall behave equivalent to the standard libc 1.38 +functions `malloc`, `calloc`, `realloc` and `free`. 1.39 + 1.40 +The signature of the memory management functions is based on the signature 1.41 +of the respective libc function but each of them takes the pointer to the 1.42 +memory area / pool as first argument. 1.43 + 1.44 +As the pointer to the memory area / pool can be arbitrarily chosen, any data 1.45 +can be provided to the memory management functions. One example is the 1.46 +[UCX Memory Pool](#memory-pool). 1.47 + 1.48 +## Array 1.49 + 1.50 +*Header file:* [array.h](api-2.1/array_8h.html) 1.51 +*Required modules:* [Allocator](#allocator) 1.52 + 1.53 +The UCX Array is an implementation of a dynamic array with automatic 1.54 +reallocation. The array structure contains a capacity, the current size, 1.55 +the size of each element, the raw pointer to the memory area and an allocator. 1.56 +Arrays are in most cases much faster than linked list. 1.57 +One can decide, whether to create a new array on the heap with `ucx_array_new()` 1.58 +or to save one indirection by initializing a `UcxArray` structure on the stack 1.59 +with `ucx_array_init()`. 1.60 + 1.61 +### Remove duplicates from an array of strings 1.62 + 1.63 +The following example shows, how a `UcxArray` can be built with 1.64 +a standard dynamic C array (pointer+length) as basis. 1.65 + 1.66 +```C 1.67 +UcxArray* create_unique(sstr_t* array, size_t arrlen) { 1.68 + // worst case is no duplicates, hence the capacity is set to arrlen 1.69 + UcxArray* result = ucx_array_new(arrlen, sizeof(sstr_t)); 1.70 + // only append elements, if they are not already present in the array 1.71 + for (size_t i = 0 ; i < arrlen ; ++i) { 1.72 + if (!ucx_array_contains(result, array+i, ucx_cmp_sstr, NULL)) { 1.73 + ucx_array_append_from(result, array+i, 1); 1.74 + } 1.75 + } 1.76 + // make the array as small as possible 1.77 + ucx_array_shrink(result); 1.78 + return result; 1.79 +} 1.80 + 1.81 +/* ... */ 1.82 + 1.83 +sstr_t* array = /* some standard array of strings */ 1.84 +size_t arrlen = /* the length of the array */ 1.85 + 1.86 +UcxArray* result = create_unique(array,arrlen); 1.87 + 1.88 +/* Iterate over the array and print the elements */ 1.89 +sstr_t* unique = result->data; 1.90 +for (size_t i = 0 ; i < result->size ; i++) { 1.91 + printf("%" PRIsstr "\n", SFMT(unique[i])); 1.92 +} 1.93 + 1.94 +/* Free the array. */ 1.95 +ucx_array_free(result); 1.96 +``` 1.97 +### Preventing out of bounds writes 1.98 + 1.99 +The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`, 1.100 +and `ucx_array_shrink()` allow easy management of the array capacity. 1.101 +Imagine you want to add `n` elements to an array. If your `n` elements are 1.102 +already somewhere else consecutively in memory, you can use 1.103 +`ucx_array_append_from()` and benefit from the autogrow facility in this family 1.104 +of functions. Otherwise, you can ask the array to have enough capacity for 1.105 +holding additional `n` elements. 1.106 + 1.107 +```C 1.108 +size_t n = // ... elements to add 1.109 +if (ucx_array_grow(array, n)) { 1.110 + fprintf(stderr, "Cannot add %zu elements to the array.\n", n); 1.111 + return 1; 1.112 +} 1.113 +for (size_t i = 0 ; i < n ; i++) { 1.114 + ((int*)array->data)[array->size++] = 80; 1.115 +} 1.116 +``` 1.117 + 1.118 +## AVL Tree 1.119 + 1.120 +*Header file:* [avl.h](api-2.1/avl_8h.html) 1.121 +*Required modules:* [Allocator](#allocator) 1.122 + 1.123 +This binary search tree implementation allows average O(1) insertion and 1.124 +removal of elements (excluding binary search time). 1.125 +All common binary tree operations are implemented. Furthermore, this module 1.126 +provides search functions via lower and upper bounds. 1.127 + 1.128 +### Filtering items with a time window 1.129 + 1.130 +Suppose you have a list of items which contain a `time_t` value and your task 1.131 +is to find all items within a time window `[t_start, t_end]`. 1.132 +With AVL Trees this is easy: 1.133 +```C 1.134 +/* --------------------- 1.135 + * Somewhere in a header 1.136 + */ 1.137 +typedef struct { 1.138 + time_t ts; 1.139 + /* other important data */ 1.140 +} MyObject; 1.141 + 1.142 +/* ----------- 1.143 + * Source code 1.144 + */ 1.145 + 1.146 +UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint); 1.147 +/* ... populate tree with objects, use '& MyObject.ts' as key ... */ 1.148 + 1.149 + 1.150 +/* Now find every item, with 30 <= ts <= 70 */ 1.151 +time_t ts_start = 30; 1.152 +time_t ts_end = 70; 1.153 + 1.154 +printf("Values in range:\n"); 1.155 +for ( 1.156 + UcxAVLNode* node = ucx_avl_find_node( 1.157 + tree, (intptr_t) &ts_start, 1.158 + ucx_dist_longint, UCX_AVL_FIND_LOWER_BOUNDED); 1.159 + node && (*(time_t*)node->key) <= ts_end; 1.160 + node = ucx_avl_succ(node) 1.161 + ) { 1.162 + printf(" ts: %ld\n", ((MyObject*)node->value)->ts); 1.163 +} 1.164 + 1.165 +ucx_avl_free_content(tree, free); 1.166 +ucx_avl_free(tree); 1.167 +``` 1.168 + 1.169 +## Buffer 1.170 + 1.171 +*Header file:* [buffer.h](api-2.1/buffer_8h.html) 1.172 +*Required modules:* None. 1.173 + 1.174 +Instances of this buffer implementation can be used to read from or to write to 1.175 +memory like you would do with a stream. This allows the use of 1.176 +`ucx_stream_copy()` from the [Utilities](#utilities) module to copy contents 1.177 +from one buffer to another or from file or network streams to the buffer and 1.178 +vice-versa. 1.179 + 1.180 +More features for convenient use of the buffer can be enabled, like automatic 1.181 +memory management and automatic resizing of the buffer space. 1.182 +See the documentation of the macro constants in the header file for more 1.183 +information. 1.184 + 1.185 +### Add line numbers to a file 1.186 + 1.187 +When reading a file line by line, you have three options: first, you could limit 1.188 +the maximum supported line length. 1.189 +Second, you allocate a god buffer large 1.190 +enough for the most lines a text file could have. 1.191 +And third, undoubtedly the best option, you start with a small buffer, which 1.192 +adjusts on demand. 1.193 +An `UcxBuffer` can be created to do just that for you. 1.194 +Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function. 1.195 +Here is a full working program, which adds line numbers to a file. 1.196 +```C 1.197 +#include <stdio.h> 1.198 +#include <ucx/buffer.h> 1.199 +#include <ucx/utils.h> 1.200 + 1.201 +int main(int argc, char** argv) { 1.202 + 1.203 + if (argc != 2) { 1.204 + fprintf(stderr, "Usage: %s <file>\n", argv[0]); 1.205 + return 1; 1.206 + } 1.207 + 1.208 + FILE* input = fopen(argv[1], "r"); 1.209 + if (!input) { 1.210 + perror("Canno read input"); 1.211 + return 1; 1.212 + } 1.213 + 1.214 + const size_t chunksize = 256; 1.215 + 1.216 + UcxBuffer* linebuf = 1.217 + ucx_buffer_new( 1.218 + NULL, /* the buffer should manage the memory area for us */ 1.219 + 2*chunksize, /* initial size should be twice the chunk size */ 1.220 + UCX_BUFFER_AUTOEXTEND); /* the buffer will grow when necessary */ 1.221 + 1.222 + size_t lineno = 1; 1.223 + do { 1.224 + /* read line chunk */ 1.225 + size_t read = ucx_stream_ncopy( 1.226 + input, linebuf, fread, ucx_buffer_write, chunksize); 1.227 + if (read == 0) break; 1.228 + 1.229 + /* handle line endings */ 1.230 + do { 1.231 + sstr_t bufstr = ucx_buffer_to_sstr(linebuf); 1.232 + sstr_t nl = sstrchr(bufstr, '\n'); 1.233 + if (nl.length == 0) break; 1.234 + 1.235 + size_t linelen = bufstr.length - nl.length; 1.236 + sstr_t linestr = sstrsubsl(bufstr, 0, linelen); 1.237 + 1.238 + printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr)); 1.239 + 1.240 + /* shift the buffer to the next line */ 1.241 + ucx_buffer_shift_left(linebuf, linelen+1); 1.242 + } while(1); 1.243 + 1.244 + } while(1); 1.245 + 1.246 + /* print the 'noeol' line, if any */ 1.247 + sstr_t lastline = ucx_buffer_to_sstr(linebuf); 1.248 + if (lastline.length > 0) { 1.249 + printf("%zu: %" PRIsstr, lineno, SFMT(lastline)); 1.250 + } 1.251 + 1.252 + fclose(input); 1.253 + ucx_buffer_free(linebuf); 1.254 + 1.255 + return 0; 1.256 +} 1.257 +``` 1.258 + 1.259 +## List 1.260 + 1.261 +*Header file:* [list.h](api-2.1/list_8h.html) 1.262 +*Required modules:* [Allocator](#allocator) 1.263 + 1.264 +This module provides the data structure and several functions for a doubly 1.265 +linked list. Among the common operations like insert, remove, search and sort, 1.266 +we allow convenient iteration via a special `UCX_FOREACH` macro. 1.267 + 1.268 +### Remove duplicates from an array of strings 1.269 + 1.270 +Assume you are given an array of `sstr_t` and want to create a list of these 1.271 +strings without duplicates. 1.272 +This is a similar example to the one [above](#array), but here we are 1.273 +using a `UcxList`. 1.274 +```C 1.275 +#include <stdio.h> 1.276 +#include <ucx/list.h> 1.277 +#include <ucx/string.h> 1.278 +#include <ucx/utils.h> 1.279 + 1.280 +UcxList* remove_duplicates(sstr_t* array, size_t arrlen) { 1.281 + UcxList* list = NULL; 1.282 + for (size_t i = 0 ; i < arrlen ; ++i) { 1.283 + if (ucx_list_find(list, array+i, ucx_cmp_sstr, NULL) == -1) { 1.284 + sstr_t* s = malloc(sizeof(sstr_t)); 1.285 + *s = sstrdup(array[i]); 1.286 + list = ucx_list_append(list, s); 1.287 + } 1.288 + } 1.289 + return list; 1.290 +} 1.291 + 1.292 +/* we will need this function to clean up the list contents later */ 1.293 +void free_sstr(void* ptr) { 1.294 + sstr_t* s = ptr; 1.295 + free(s->ptr); 1.296 + free(s); 1.297 +} 1.298 + 1.299 +/* ... */ 1.300 + 1.301 +sstr_t* array = /* some array of strings */ 1.302 +size_t arrlen = /* the length of the array */ 1.303 + 1.304 +UcxList* list = remove_duplicates(array,arrlen); 1.305 + 1.306 +/* Iterate over the list and print the elements */ 1.307 +UCX_FOREACH(elem, list) { 1.308 + sstr_t s = *((sstr_t*)elem->data); 1.309 + printf("%" PRIsstr "\n", SFMT(s)); 1.310 +} 1.311 + 1.312 +/* Use our free function to free the duplicated strings. */ 1.313 +ucx_list_free_content(list, free_sstr); 1.314 +ucx_list_free(list); 1.315 +``` 1.316 + 1.317 +## Logging 1.318 + 1.319 +*Header file:* [logging.h](api-2.1/logging_8h.html) 1.320 +*Required modules:* [Map](#map), [String](#string) 1.321 + 1.322 +The logging module comes with some predefined log levels and allows some more 1.323 +customization. You may choose if you want to get timestamps or source file and 1.324 +line number logged automatically when outputting a message. 1.325 +The following function call initializes a debug logger with all of the above 1.326 +information: 1.327 +```C 1.328 + log = ucx_logger_new(stdout, UCX_LOGGER_DEBUG, 1.329 + UCX_LOGGER_LEVEL | UCX_LOGGER_TIMESTAMP | UCX_LOGGER_SOURCE); 1.330 +``` 1.331 +Afterwards you can use this logger with the predefined macros 1.332 +```C 1.333 + ucx_logger_trace(log, "Verbose output"); 1.334 + ucx_logger_debug(log, "Debug message"); 1.335 + ucx_logger_info(log, "Information"); 1.336 + ucx_logger_warn(log, "Warning"); 1.337 + ucx_logger_error(log, "Error message"); 1.338 +``` 1.339 +or you use 1.340 +```C 1.341 + ucx_logger_log(log, CUSTOM_LEVEL, "Some message") 1.342 +``` 1.343 +When you use your custom log level, don't forget to register it with 1.344 +```C 1.345 + ucx_logger_register_level(log, CUSTOM_LEVEL, "CUSTOM") 1.346 +``` 1.347 +where the last argument must be a string literal. 1.348 + 1.349 +## Map 1.350 + 1.351 +*Header file:* [map.h](api-2.1/map_8h.html) 1.352 +*Required modules:* [Allocator](#allocator), [String](#string) 1.353 + 1.354 +This module provides a hash map implementation using murmur hash 2 and separate 1.355 +chaining with linked lists. Similarly to the list module, we provide a 1.356 +`UCX_MAP_FOREACH` macro to conveniently iterate through the key/value pairs. 1.357 + 1.358 +### Parsing command line options 1.359 + 1.360 +Assume you want to parse command line options and record them within a map. 1.361 +One way to do this is shown by the following code sample: 1.362 +```C 1.363 + UcxMap* options = ucx_map_new(16); 1.364 + const char *NOARG = ""; 1.365 + 1.366 + char *option = NULL; 1.367 + char optchar = 0; 1.368 + for(int i=1;i<argc;i++) { 1.369 + char *arg = argv[i]; 1.370 + size_t len = strlen(arg); 1.371 + if(len > 1 && arg[0] == '-') { 1.372 + for(int c=1;c<len;c++) { 1.373 + if(option) { 1.374 + fprintf(stderr, 1.375 + "Missing argument for option -%c\n", optchar); 1.376 + return 1; 1.377 + } 1.378 + switch(arg[c]) { 1.379 + default: { 1.380 + fprintf(stderr, "Unknown option -%c\n\n", arg[c]); 1.381 + return 1; 1.382 + } 1.383 + case 'v': { 1.384 + ucx_map_cstr_put(options, "verbose", NOARG); 1.385 + break; 1.386 + } 1.387 + case 'o': { 1.388 + option = "output"; 1.389 + optchar = 'o'; 1.390 + break; 1.391 + } 1.392 + } 1.393 + } 1.394 + } else if(option) { 1.395 + ucx_map_cstr_put(options, option, arg); 1.396 + option = NULL; 1.397 + } else { 1.398 + /* ... handle argument that is not an option ... */ 1.399 + } 1.400 + } 1.401 + if(option) { 1.402 + fprintf(stderr, 1.403 + "Missing argument for option -%c\n", optchar); 1.404 + return 1; 1.405 + } 1.406 +``` 1.407 +With the following loop, you can access the previously recorded options: 1.408 +```C 1.409 + UcxMapIterator iter = ucx_map_iterator(options); 1.410 + char *arg; 1.411 + UCX_MAP_FOREACH(optkey, arg, iter) { 1.412 + char* opt = optkey.data; 1.413 + if (*arg) { 1.414 + printf("%s = %s\n", opt, arg); 1.415 + } else { 1.416 + printf("%s active\n", opt); 1.417 + } 1.418 + } 1.419 +``` 1.420 +Don't forget to call `ucx_map_free()`, when you are done with the map. 1.421 + 1.422 +## Memory Pool 1.423 + 1.424 +*Header file:* [mempool.h](api-2.1/mempool_8h.html) 1.425 +*Required modules:* [Allocator](#allocator) 1.426 + 1.427 +Here we have a concrete allocator implementation in the sense of a memory pool. 1.428 +This pool allows you to register destructor functions for the allocated memory, 1.429 +which are automatically called on the destruction of the pool. 1.430 +But you may also register *independent* destructor functions within a pool in 1.431 +case some external library allocated memory for you, which should be 1.432 +destroyed together with this pool. 1.433 + 1.434 +Many UCX modules support the use of an allocator. 1.435 +The [String Module](#string), for instance, provides the `sstrdup_a()` function, 1.436 +which uses the specified allocator to allocate the memory for the duplicated 1.437 +string. 1.438 +This way, you can use a `UcxMempool` to keep track of the memory occupied by 1.439 +duplicated strings and cleanup everything with just a single call to 1.440 +`ucx_mempool_destroy()`. 1.441 + 1.442 +### Read CSV data into a structure 1.443 + 1.444 +The following code example shows some of the basic memory pool functions and 1.445 +how they can be used with other UCX modules. 1.446 +```C 1.447 +#include <stdio.h> 1.448 +#include <ucx/mempool.h> 1.449 +#include <ucx/list.h> 1.450 +#include <ucx/string.h> 1.451 +#include <ucx/buffer.h> 1.452 +#include <ucx/utils.h> 1.453 + 1.454 +typedef struct { 1.455 + sstr_t column_a; 1.456 + sstr_t column_b; 1.457 + sstr_t column_c; 1.458 +} CSVData; 1.459 + 1.460 +int main(int argc, char** argv) { 1.461 + 1.462 + UcxMempool* pool = ucx_mempool_new(128); 1.463 + 1.464 + FILE *f = fopen("test.csv", "r"); 1.465 + if (!f) { 1.466 + perror("Cannot open file"); 1.467 + return 1; 1.468 + } 1.469 + /* close the file automatically at pool destruction*/ 1.470 + ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose); 1.471 + 1.472 + /* create a buffer and register it at the memory pool for destruction */ 1.473 + UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); 1.474 + ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free); 1.475 + 1.476 + /* read the file and split it by lines first */ 1.477 + ucx_stream_copy(f, content, fread, ucx_buffer_write); 1.478 + sstr_t contentstr = ucx_buffer_to_sstr(content); 1.479 + ssize_t lc = 0; 1.480 + sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc); 1.481 + 1.482 + /* skip the header and parse the remaining data */ 1.483 + UcxList* datalist = NULL; 1.484 + for (size_t i = 1 ; i < lc ; i++) { 1.485 + if (lines[i].length == 0) continue; 1.486 + ssize_t fc = 3; 1.487 + sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc); 1.488 + if (fc != 3) { 1.489 + fprintf(stderr, "Syntax error in line %zu.\n", i); 1.490 + ucx_mempool_destroy(pool); 1.491 + return 1; 1.492 + } 1.493 + CSVData* data = ucx_mempool_malloc(pool, sizeof(CSVData)); 1.494 + data->column_a = fields[0]; 1.495 + data->column_b = fields[1]; 1.496 + data->column_c = fields[2]; 1.497 + datalist = ucx_list_append_a(pool->allocator, datalist, data); 1.498 + } 1.499 + 1.500 + /* control output */ 1.501 + UCX_FOREACH(elem, datalist) { 1.502 + CSVData* data = elem->data; 1.503 + printf("Column A: %" PRIsstr " | " 1.504 + "Column B: %" PRIsstr " | " 1.505 + "Column C: %" PRIsstr "\n", 1.506 + SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c) 1.507 + ); 1.508 + } 1.509 + 1.510 + /* cleanup everything, no manual free() needed */ 1.511 + ucx_mempool_destroy(pool); 1.512 + 1.513 + return 0; 1.514 +} 1.515 +``` 1.516 + 1.517 +### Overriding the default destructor 1.518 + 1.519 +Sometimes you need to allocate memory with `ucx_mempool_malloc()`, but the 1.520 +memory is not supposed to be freed with a simple call to `free()`. 1.521 +In this case, you can overwrite the default destructor as follows: 1.522 +```C 1.523 + MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject)); 1.524 + 1.525 + /* some special initialization with own resource management */ 1.526 + my_object_init(obj); 1.527 + 1.528 + /* register destructor function */ 1.529 + ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy); 1.530 +``` 1.531 +Be aware, that your destructor function should not free any memory, that is 1.532 +also managed by the pool. 1.533 +Otherwise you might be risking a double-free. 1.534 +More precisely, a destructor function set with `ucx_mempool_set_destr()` MUST 1.535 +NOT call `free()` on the specified pointer whereas a desructor function 1.536 +registered with `ucx_mempool_reg_destr()` MAY (and in most cases will) call 1.537 +`free()`. 1.538 + 1.539 +## Properties 1.540 + 1.541 +*Header file:* [properties.h](api-2.1/properties_8h.html) 1.542 +*Required modules:* [Map](#map) 1.543 + 1.544 +This module provides load and store function for `*.properties` files. 1.545 +The key/value pairs are stored within an UCX Map. 1.546 + 1.547 +### Example: Loading properties from a file 1.548 + 1.549 +```C 1.550 +/* Open the file as usual */ 1.551 +FILE* file = fopen("myprops.properties", "r"); 1.552 +if (!file) { 1.553 + // error handling 1.554 + return 1; 1.555 +} 1.556 + 1.557 +/* Load the properties from the file */ 1.558 +UcxMap* myprops = ucx_map_new(16); 1.559 +if (ucx_properties_load(myprops, file)) { 1.560 + /* ... error handling ... */ 1.561 + fclose(file); 1.562 + ucx_map_free(myprops); 1.563 + return 1; 1.564 +} 1.565 + 1.566 +/* Print out the key/value pairs */ 1.567 +char* propval; 1.568 +UcxMapIterator propiter = ucx_map_iterator(myprops); 1.569 +UCX_MAP_FOREACH(key, propval, propiter) { 1.570 + printf("%s = %s\n", (char*)key.data, propval); 1.571 +} 1.572 + 1.573 +/* Don't forget to free the values before freeing the map */ 1.574 +ucx_map_free_content(myprops, NULL); 1.575 +ucx_map_free(myprops); 1.576 +fclose(file); 1.577 +``` 1.578 + 1.579 +## Stack 1.580 + 1.581 +*Header file:* [stack.h](api-2.1/stack_8h.html) 1.582 +*Required modules:* [Allocator](#allocator) 1.583 + 1.584 +This concrete implementation of an UCX Allocator allows you to grab some amount 1.585 +of memory which is then handled as a stack. 1.586 +Please note, that the term *stack* only refers to the behavior of this 1.587 +allocator. You may still choose to use either stack or heap memory 1.588 +for the underlying space. 1.589 +A typical use case is an algorithm where you need to allocate and free large 1.590 +amounts of memory very frequently. 1.591 + 1.592 +The following code sample shows how to initialize a stack and push and pop 1.593 +simple data. 1.594 +```C 1.595 + const size_t len = 1024; 1.596 + char space[len]; 1.597 + UcxStack stack; 1.598 + ucx_stack_init(&stack, space, len); 1.599 + 1.600 + int i = 42; 1.601 + float f = 3.14f; 1.602 + const char* str = "Hello!"; 1.603 + size_t strn = 7; 1.604 + 1.605 + /* push the integer */ 1.606 + ucx_stack_push(&stack, sizeof(int), &i); 1.607 + 1.608 + /* push the float and rember the address */ 1.609 + float* remember = ucx_stack_push(&stack, sizeof(float), &f); 1.610 + 1.611 + /* push the string with zero terminator */ 1.612 + ucx_stack_push(&stack, strn, str); 1.613 + 1.614 + /* if we forget, how big an element was, we can ask the stack */ 1.615 + printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1); 1.616 + 1.617 + /* retrieve the string as sstr_t, without zero terminator! */ 1.618 + sstr_t s; 1.619 + s.length = ucx_stack_topsize(&stack)-1; 1.620 + s.ptr = malloc(s.length); 1.621 + ucx_stack_popn(&stack, s.ptr, s.length); 1.622 + printf("%" PRIsstr "\n", SFMT(s)); 1.623 + 1.624 + /* print the float directly from the stack and free it */ 1.625 + printf("Float: %f\n", *remember); 1.626 + ucx_stack_free(&stack, remember); 1.627 + 1.628 + /* the last element is the integer */ 1.629 + int j; 1.630 + ucx_stack_pop(&stack, &j); 1.631 + printf("Integer: %d\n", j); 1.632 +``` 1.633 + 1.634 + 1.635 + 1.636 +## String 1.637 + 1.638 +*Header file:* [string.h](api-2.1/string_8h.html) 1.639 +*Required modules:* [Allocator](#allocator) 1.640 + 1.641 +This module provides a safe implementation of bounded string. 1.642 +Usually C strings do not carry a length. While for zero-terminated strings you 1.643 +can easily get the length with `strlen`, this is not generally possible for 1.644 +arbitrary strings. 1.645 +The `sstr_t` type of this module always carries the string and its length to 1.646 +reduce the risk of buffer overflows dramatically. 1.647 + 1.648 +### Initialization 1.649 + 1.650 +There are several ways to create an `sstr_t`: 1.651 + 1.652 +```C 1.653 +/* (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated */ 1.654 +sstr_t a = sstr(cstr); 1.655 + 1.656 +/* (2) cstr does not need to be zero-terminated, if length is specified */ 1.657 +sstr_t b = sstrn(cstr, len); 1.658 + 1.659 +/* (3) S() macro creates sstr_t from a string using sizeof() and using sstrn(). 1.660 + This version is especially useful for function arguments */ 1.661 +sstr_t c = S("hello"); 1.662 + 1.663 +/* (4) SC() macro works like S(), but makes the string immutable using scstr_t. 1.664 + (available since UCX 2.0) */ 1.665 +scstr_t d = SC("hello"); 1.666 + 1.667 +/* (5) ST() macro creates sstr_t struct literal using sizeof() */ 1.668 +sstr_t e = ST("hello"); 1.669 +``` 1.670 + 1.671 +You should not use the `S()`, `SC()`, or `ST()` macro with string of unknown 1.672 +origin, since the `sizeof()` call might not coincide with the string length in 1.673 +those cases. If you know what you are doing, it can save you some performance, 1.674 +because you do not need the `strlen()` call. 1.675 + 1.676 +### Handling immutable strings 1.677 + 1.678 +*(Since: UCX 2.0)* 1.679 + 1.680 +For immutable strings (i.e. `const char*` strings), UCX provides the `scstr_t` 1.681 +type, which works exactly as the `sstr_t` type but with a pointer 1.682 +to `const char`. All UCX string functions come in two flavors: one that enforces 1.683 +the `scstr_t` type, and another that usually accepts both types and performs 1.684 +a conversion automatically, if necessary. 1.685 + 1.686 +There are some exceptions to this rule, as the return type may depend on the 1.687 +argument type. 1.688 +E.g. the `sstrchr()` function returns a substring starting at 1.689 +the first occurrence of the specified character. 1.690 +Since this substring points to the memory of the argument string, it does not 1.691 +accept `scstr_t` as input argument, because the return type would break the 1.692 +constness. 1.693 + 1.694 + 1.695 +### Finding the position of a substring 1.696 + 1.697 +The `sstrstr()` function gives you a new `sstr_t` object starting with the 1.698 +requested substring. Thus determining the position comes down to a simple 1.699 +subtraction. 1.700 + 1.701 +```C 1.702 +sstr_t haystack = ST("Here we go!"); 1.703 +sstr_t needle = ST("we"); 1.704 +sstr_t result = sstrstr(haystack, needle); 1.705 +if (result.ptr) 1.706 + printf("Found at position %zd.\n", haystack.length-result.length); 1.707 +else 1.708 + printf("Not found.\n"); 1.709 +``` 1.710 + 1.711 +### Spliting a string by a delimiter 1.712 + 1.713 +The `sstrsplit()` function (and its allocator based version `sstrsplit_a()`) is 1.714 +very powerful and might look a bit nasty at a first glance. But it is indeed 1.715 +very simple to use. It is even more convenient in combination with a memory 1.716 +pool. 1.717 + 1.718 +```C 1.719 +sstr_t test = ST("here::are::some::strings"); 1.720 +sstr_t delim = ST("::"); 1.721 + 1.722 +ssize_t count = 0; /* no limit */ 1.723 +UcxMempool* pool = ucx_mempool_new_default(); 1.724 + 1.725 +sstr_t* result = sstrsplit_a(pool->allocator, test, delim, &count); 1.726 +for (ssize_t i = 0 ; i < count ; i++) { 1.727 + /* don't forget to specify the length via the %*s format specifier */ 1.728 + printf("%*s\n", result[i].length, result[i].ptr); 1.729 +} 1.730 + 1.731 +ucx_mempool_destroy(pool); 1.732 +``` 1.733 +The output is: 1.734 + 1.735 + here 1.736 + are 1.737 + some 1.738 + strings 1.739 + 1.740 +The memory pool ensures, that all strings are freed. 1.741 + 1.742 +### Disabling convenience macros 1.743 + 1.744 +If you are experiencing any troubles with the short convenience macros `S()`, 1.745 +`SC()`, or `ST()`, you can disable them by setting the macro 1.746 +`UCX_NO_SSTR_SHORTCUTS` before including the header (or via a compiler option). 1.747 +For the formatting macros `SFMT()` and `PRIsstr` you can use the macro 1.748 +`UCX_NO_SSTR_FORMAT_MACROS` to disable them. 1.749 + 1.750 +Please keep in mind, that after disabling the macros, you cannot use them in 1.751 +your code *and* foreign code that you might have included. 1.752 +You should only disable the macros, if you are experiencing a nasty name clash 1.753 +which cannot be otherwise resolved. 1.754 + 1.755 +## Testing 1.756 + 1.757 +*Header file:* [test.h](api-2.1/test_8h.html) 1.758 +*Required modules:* None. 1.759 + 1.760 +This module provides a testing framework which allows you to execute test cases 1.761 +within test suites. 1.762 +To avoid code duplication within tests, we also provide the possibility to 1.763 +define test subroutines. 1.764 + 1.765 +You should declare test cases and subroutines in a header file per test unit 1.766 +and implement them as you would implement normal functions. 1.767 +```C 1.768 + /* myunit.h */ 1.769 + UCX_TEST(function_name); 1.770 + UCX_TEST_SUBROUTINE(subroutine_name, paramlist); /* optional */ 1.771 + 1.772 + 1.773 + /* myunit.c */ 1.774 + UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { 1.775 + /* ... reusable tests with UCX_TEST_ASSERT() ... */ 1.776 + } 1.777 + 1.778 + UCX_TEST(function_name) { 1.779 + /* ... resource allocation and other test preparation ... */ 1.780 + 1.781 + /* mandatory marker for the start of the tests */ 1.782 + UCX_TEST_BEGIN 1.783 + 1.784 + /* ... verifications with UCX_TEST_ASSERT() ... 1.785 + * (and/or calls with UCX_TEST_CALL_SUBROUTINE()) 1.786 + */ 1.787 + 1.788 + /* mandatory marker for the end of the tests */ 1.789 + UCX_TEST_END 1.790 + 1.791 + /* ... resource cleanup ... 1.792 + * (all code after UCX_TEST_END is always executed) 1.793 + */ 1.794 + } 1.795 +``` 1.796 +If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are 1.797 +*required* to use a `UCX_TEST_SUBROUTINE`. 1.798 +Otherwise the testing framework does not know where to jump, when the assertion 1.799 +fails. 1.800 + 1.801 +After implementing the tests, you can easily build a test suite and execute it: 1.802 +```C 1.803 + UcxTestSuite* suite = ucx_test_suite_new(); 1.804 + ucx_test_register(suite, testMyTestCase01); 1.805 + ucx_test_register(suite, testMyTestCase02); 1.806 + /* ... */ 1.807 + ucx_test_run(suite, stdout); /* stdout, or any other FILE stream */ 1.808 +``` 1.809 + 1.810 +## Utilities 1.811 + 1.812 +*Header file:* [utils.h](api-2.1/utils_8h.html) 1.813 +*Required modules:* [Allocator](#allocator), [String](#string) 1.814 + 1.815 +In this module we provide very general utility function for copy and compare 1.816 +operations. 1.817 +We also provide several `printf` variants to conveniently print formatted data 1.818 +to streams or strings. 1.819 + 1.820 +### A simple copy program 1.821 + 1.822 +The utilities package provides several stream copy functions. 1.823 +One of them has a very simple interface and can, for instance, be used to copy 1.824 +whole files in a single call. 1.825 +This is a minimal working example: 1.826 +```C 1.827 +#include <stdio.h> 1.828 +#include <ucx/utils.h> 1.829 + 1.830 +int main(int argc, char** argv) { 1.831 + 1.832 + if (argc != 3) { 1.833 + fprintf(stderr, "Use %s <src> <dest>", argv[0]); 1.834 + return 1; 1.835 + } 1.836 + 1.837 + FILE *srcf = fopen(argv[1], "r"); /* insert error handling on your own */ 1.838 + FILE *destf = fopen(argv[2], "w"); 1.839 + 1.840 + size_t n = ucx_stream_copy(srcf, destf, fread, fwrite); 1.841 + printf("%zu bytes copied.\n", n); 1.842 + 1.843 + fclose(srcf); 1.844 + fclose(destf); 1.845 + 1.846 + 1.847 + return 0; 1.848 +} 1.849 +``` 1.850 + 1.851 +### Automatic allocation for formatted strings 1.852 + 1.853 +The UCX utility function `ucx_asprintf()` and it's convenient shortcut 1.854 +`ucx_sprintf` allow easy formatting of strings, without ever having to worry 1.855 +about the required space. 1.856 +```C 1.857 +sstr_t mystring = ucx_sprintf("The answer is: %d!", 42); 1.858 +``` 1.859 +Still, you have to pass `mystring.ptr` to `free()` (or the free function of 1.860 +your allocator, if you use `ucx_asprintf`). 1.861 +If you don't have all the information ready to build your string, you can even 1.862 +use a [UcxBuffer](#buffer) as a target with the utility function 1.863 +`ucx_bprintf()`. 1.864 +```C 1.865 +UcxBuffer* strbuffer = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); 1.866 + 1.867 +for (unsigned int i = 2 ; i < 100 ; i++) { 1.868 + ucx_bprintf(strbuffer, "Integer %d is %s\n", 1.869 + i, prime(i) ? "prime" : "not prime"); 1.870 +} 1.871 + 1.872 +/* print the result to stdout */ 1.873 +printf("%s", (char*)strbuffer->space); 1.874 + 1.875 +ucx_buffer_free(strbuffer); 1.876 +```