UNIXworkcode

1 --- 2 title: UCX 2.1 Modules 3 --- 4 5 UCX 2.1 provided several modules for data structures and algorithms. 6 You may choose to use specific modules by 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. 15 16 <div id="modules"> 17 18 ----------------------- ---------------------- -------------------------------- --------------------------- 19 [String](#string) [Buffer](#buffer) 20 [Allocator](#allocator) [Stack](#stack) [Memory&nbsp;Pool](#memory-pool) 21 [Array](#array) [List](#list) [Map](#map) [AVL&nbsp;Tree](#avl-tree) 22 [Logging](#logging) [Testing](#testing) [Utilities](#utilities) [Properties](#properties) 23 ----------------------- ---------------------- -------------------------------- --------------------------- 24 25 </div> 26 27 ## Allocator 28 29 *Header file:* [allocator.h](api-2.1/allocator_8h.html) 30 *Required modules:* None. 31 32 A UCX allocator consists of a pointer to the memory area / pool and four 33 function pointers to memory management functions operating on this memory 34 area / pool. These functions shall behave equivalent to the standard libc 35 functions `malloc`, `calloc`, `realloc` and `free`. 36 37 The signature of the memory management functions is based on the signature 38 of the respective libc function but each of them takes the pointer to the 39 memory area / pool as first argument. 40 41 As the pointer to the memory area / pool can be arbitrarily chosen, any data 42 can be provided to the memory management functions. One example is the 43 [UCX Memory Pool](#memory-pool). 44 45 ## Array 46 47 *Header file:* [array.h](api-2.1/array_8h.html) 48 *Required modules:* [Allocator](#allocator) 49 50 The UCX Array is an implementation of a dynamic array with automatic 51 reallocation. The array structure contains a capacity, the current size, 52 the size of each element, the raw pointer to the memory area and an allocator. 53 Arrays are in most cases much faster than linked list. 54 One can decide, whether to create a new array on the heap with `ucx_array_new()` 55 or to save one indirection by initializing a `UcxArray` structure on the stack 56 with `ucx_array_init()`. 57 58 ### Remove duplicates from an array of strings 59 60 The following example shows, how a `UcxArray` can be built with 61 a standard dynamic C array (pointer+length) as basis. 62 63 ```C 64 UcxArray* create_unique(sstr_t* array, size_t arrlen) { 65 // worst case is no duplicates, hence the capacity is set to arrlen 66 UcxArray* result = ucx_array_new(arrlen, sizeof(sstr_t)); 67 // only append elements, if they are not already present in the array 68 for (size_t i = 0 ; i < arrlen ; ++i) { 69 if (!ucx_array_contains(result, array+i, ucx_cmp_sstr, NULL)) { 70 ucx_array_append_from(result, array+i, 1); 71 } 72 } 73 // make the array as small as possible 74 ucx_array_shrink(result); 75 return result; 76 } 77 78 // ... 79 80 sstr_t* array = // some standard array of strings 81 size_t arrlen = // the length of the array 82 83 UcxArray* result = create_unique(array,arrlen); 84 85 // Iterate over the array and print the elements 86 sstr_t* unique = result->data; 87 for (size_t i = 0 ; i < result->size ; i++) { 88 printf("%" PRIsstr "\n", SFMT(unique[i])); 89 } 90 91 // Free the array. 92 ucx_array_free(result); 93 ``` 94 ### Preventing out of bounds writes 95 96 The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`, 97 and `ucx_array_shrink()` allow easy management of the array capacity. 98 Imagine you want to add `n` elements to an array. If your `n` elements are 99 already somewhere else consecutively in memory, you can use 100 `ucx_array_append_from()` and benefit from the autogrow facility in this family 101 of functions. Otherwise, you can ask the array to have enough capacity for 102 holding additional `n` elements. 103 104 ```C 105 size_t n = // ... elements to add 106 if (ucx_array_grow(array, n)) { 107 fprintf(stderr, "Cannot add %zu elements to the array.\n", n); 108 return 1; 109 } 110 for (size_t i = 0 ; i < n ; i++) { 111 ((int*)array->data)[array->size++] = 80; 112 } 113 ``` 114 115 ## AVL Tree 116 117 *Header file:* [avl.h](api-2.1/avl_8h.html) 118 *Required modules:* [Allocator](#allocator) 119 120 This binary search tree implementation allows average O(1) insertion and 121 removal of elements (excluding binary search time). 122 All common binary tree operations are implemented. Furthermore, this module 123 provides search functions via lower and upper bounds. 124 125 ### Filtering items with a time window 126 127 Suppose you have a list of items which contain a `time_t` value and your task 128 is to find all items within a time window `[t_start, t_end]`. 129 With AVL Trees this is easy: 130 ```C 131 // Somewhere in a header 132 typedef struct { 133 time_t ts; 134 // other important data 135 } MyObject; 136 137 // Source code 138 UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint); 139 // ... populate tree with objects, use '& MyObject.ts' as key ... 140 141 142 // Now find every item, with 30 <= ts <= 70 143 time_t ts_start = 30; 144 time_t ts_end = 70; 145 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 } 156 157 ucx_avl_free_content(tree, free); 158 ucx_avl_free(tree); 159 ``` 160 161 ## Buffer 162 163 *Header file:* [buffer.h](api-2.1/buffer_8h.html) 164 *Required modules:* None. 165 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. 171 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. 176 177 ### Add line numbers to a file 178 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> 192 193 int main(int argc, char** argv) { 194 195 if (argc != 2) { 196 fprintf(stderr, "Usage: %s <file>\n", argv[0]); 197 return 1; 198 } 199 200 FILE* input = fopen(argv[1], "r"); 201 if (!input) { 202 perror("Canno read input"); 203 return 1; 204 } 205 206 const size_t chunksize = 256; 207 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 213 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; 220 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; 226 227 size_t linelen = bufstr.length - nl.length; 228 sstr_t linestr = sstrsubsl(bufstr, 0, linelen); 229 230 printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr)); 231 232 // shift the buffer to the next line 233 ucx_buffer_shift_left(linebuf, linelen+1); 234 } while(1); 235 236 } while(1); 237 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 } 243 244 fclose(input); 245 ucx_buffer_free(linebuf); 246 247 return 0; 248 } 249 ``` 250 251 ## List 252 253 *Header file:* [list.h](api-2.1/list_8h.html) 254 *Required modules:* [Allocator](#allocator) 255 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. 259 260 ### Remove duplicates from an array of strings 261 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> 271 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 } 283 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 } 290 291 // ... 292 293 sstr_t* array = // some array of strings 294 size_t arrlen = // the length of the array 295 296 UcxList* list = remove_duplicates(array,arrlen); 297 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 } 303 304 // Use our free function to free the duplicated strings. 305 ucx_list_free_content(list, free_sstr); 306 ucx_list_free(list); 307 ``` 308 309 ## Logging 310 311 *Header file:* [logging.h](api-2.1/logging_8h.html) 312 *Required modules:* [Map](#map), [String](#string) 313 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. 340 341 ## Map 342 343 *Header file:* [map.h](api-2.1/map_8h.html) 344 *Required modules:* [Allocator](#allocator), [String](#string) 345 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. 349 350 ### Parsing command line options 351 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 = ""; 357 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. 413 414 ## Memory Pool 415 416 *Header file:* [mempool.h](api-2.1/mempool_8h.html) 417 *Required modules:* [Allocator](#allocator) 418 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. 425 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()`. 433 434 ### Read CSV data into a structure 435 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> 445 446 typedef struct { 447 sstr_t column_a; 448 sstr_t column_b; 449 sstr_t column_c; 450 } CSVData; 451 452 int main(int argc, char** argv) { 453 454 UcxMempool* pool = ucx_mempool_new(128); 455 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); 463 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); 467 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); 473 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 } 491 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 } 501 502 // cleanup everything, no manual free() needed 503 ucx_mempool_destroy(pool); 504 505 return 0; 506 } 507 ``` 508 509 ### Overriding the default destructor 510 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)); 516 517 // some special initialization with own resource management 518 my_object_init(obj); 519 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()`. 530 531 ## Properties 532 533 *Header file:* [properties.h](api-2.1/properties_8h.html) 534 *Required modules:* [Map](#map) 535 536 This module provides load and store function for `*.properties` files. 537 The key/value pairs are stored within an UCX Map. 538 539 ### Example: Loading properties from a file 540 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 } 548 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 } 557 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 } 564 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 ``` 570 571 ## Stack 572 573 *Header file:* [stack.h](api-2.1/stack_8h.html) 574 *Required modules:* [Allocator](#allocator) 575 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. 583 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); 591 592 int i = 42; 593 float f = 3.14f; 594 const char* str = "Hello!"; 595 size_t strn = 7; 596 597 // push the integer 598 ucx_stack_push(&stack, sizeof(int), &i); 599 600 // push the float and rember the address 601 float* remember = ucx_stack_push(&stack, sizeof(float), &f); 602 603 // push the string with zero terminator 604 ucx_stack_push(&stack, strn, str); 605 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); 608 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)); 615 616 // print the float directly from the stack and free it 617 printf("Float: %f\n", *remember); 618 ucx_stack_free(&stack, remember); 619 620 // the last element is the integer 621 int j; 622 ucx_stack_pop(&stack, &j); 623 printf("Integer: %d\n", j); 624 ``` 625 626 627 628 ## String 629 630 *Header file:* [string.h](api-2.1/string_8h.html) 631 *Required modules:* [Allocator](#allocator) 632 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. 639 640 ### Initialization 641 642 There are several ways to create an `sstr_t`: 643 644 ```C 645 // (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated 646 sstr_t a = sstr(cstr); 647 648 // (2) cstr does not need to be zero-terminated, if length is specified 649 sstr_t b = sstrn(cstr, len); 650 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"); 654 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"); 658 659 // (5) ST() macro creates sstr_t struct literal using sizeof() 660 sstr_t e = ST("hello"); 661 ``` 662 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. 667 668 ### Handling immutable strings 669 670 *(Since: UCX 2.0)* 671 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. 677 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. 685 686 687 ### Finding the position of a substring 688 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. 692 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 ``` 702 703 ### Spliting a string by a delimiter 704 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. 709 710 ```C 711 sstr_t test = ST("here::are::some::strings"); 712 sstr_t delim = ST("::"); 713 714 ssize_t count = 0; // no limit 715 UcxMempool* pool = ucx_mempool_new_default(); 716 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 } 722 723 ucx_mempool_destroy(pool); 724 ``` 725 The output is: 726 727 here 728 are 729 some 730 strings 731 732 The memory pool ensures, that all strings are freed. 733 734 ### Disabling convenience macros 735 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. 741 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. 746 747 ## Testing 748 749 *Header file:* [test.h](api-2.1/test_8h.html) 750 *Required modules:* None. 751 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. 756 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 763 764 765 // myunit.c 766 UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { 767 // ... reusable tests with UCX_TEST_ASSERT() ... 768 } 769 770 UCX_TEST(function_name) { 771 // ... resource allocation and other test preparation ... 772 773 // mandatory marker for the start of the tests 774 UCX_TEST_BEGIN 775 776 // ... verifications with UCX_TEST_ASSERT() ... 777 // (and/or calls with UCX_TEST_CALL_SUBROUTINE()) 778 779 // mandatory marker for the end of the tests 780 UCX_TEST_END 781 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. 790 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 ``` 799 800 ## Utilities 801 802 *Header file:* [utils.h](api-2.1/utils_8h.html) 803 *Required modules:* [Allocator](#allocator), [String](#string) 804 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. 809 810 ### A simple copy program 811 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> 819 820 int main(int argc, char** argv) { 821 822 if (argc != 3) { 823 fprintf(stderr, "Use %s <src> <dest>", argv[0]); 824 return 1; 825 } 826 827 FILE *srcf = fopen(argv[1], "r"); // insert error handling on your own 828 FILE *destf = fopen(argv[2], "w"); 829 830 size_t n = ucx_stream_copy(srcf, destf, fread, fwrite); 831 printf("%zu bytes copied.\n", n); 832 833 fclose(srcf); 834 fclose(destf); 835 836 837 return 0; 838 } 839 ``` 840 841 ### Automatic allocation for formatted strings 842 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); 856 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 } 861 862 // print the result to stdout 863 printf("%s", (char*)strbuffer->space); 864 865 ucx_buffer_free(strbuffer); 866 ``` 867