--- a/docs/src/modules-ucx2.md Wed Jan 22 21:02:46 2025 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,866 +0,0 @@ ---- -title: UCX 2.1 Modules ---- - -UCX 2.1 provided several modules for data structures and algorithms. -You may choose to use specific modules by including the corresponding header -file. -Please note, that some modules make use of other UCX 2.1 modules. -For instance, the [Allocator](#allocator) module is used by many other modules -to allow flexible memory allocation. -By default, the header files are placed into an `ucx` directory within your -systems include directory. In this case you can use a module by including it -via `#include <ucx/MODULENAME.h>`. -Required modules are included automatically. - -<div id="modules"> - ------------------------ ---------------------- -------------------------------- --------------------------- -[String](#string) [Buffer](#buffer) -[Allocator](#allocator) [Stack](#stack) [Memory Pool](#memory-pool) -[Array](#array) [List](#list) [Map](#map) [AVL Tree](#avl-tree) -[Logging](#logging) [Testing](#testing) [Utilities](#utilities) [Properties](#properties) ------------------------ ---------------------- -------------------------------- --------------------------- - -</div> - -## Allocator - -*Header file:* [allocator.h](api-2.1/allocator_8h.html) -*Required modules:* None. - -A UCX allocator consists of a pointer to the memory area / pool and four -function pointers to memory management functions operating on this memory -area / pool. These functions shall behave equivalent to the standard libc -functions `malloc`, `calloc`, `realloc` and `free`. - -The signature of the memory management functions is based on the signature -of the respective libc function but each of them takes the pointer to the -memory area / pool as first argument. - -As the pointer to the memory area / pool can be arbitrarily chosen, any data -can be provided to the memory management functions. One example is the -[UCX Memory Pool](#memory-pool). - -## Array - -*Header file:* [array.h](api-2.1/array_8h.html) -*Required modules:* [Allocator](#allocator) - -The UCX Array is an implementation of a dynamic array with automatic -reallocation. The array structure contains a capacity, the current size, -the size of each element, the raw pointer to the memory area and an allocator. -Arrays are in most cases much faster than linked list. -One can decide, whether to create a new array on the heap with `ucx_array_new()` -or to save one indirection by initializing a `UcxArray` structure on the stack -with `ucx_array_init()`. - -### Remove duplicates from an array of strings - -The following example shows, how a `UcxArray` can be built with -a standard dynamic C array (pointer+length) as basis. - -```C -UcxArray* create_unique(sstr_t* array, size_t arrlen) { - // worst case is no duplicates, hence the capacity is set to arrlen - UcxArray* result = ucx_array_new(arrlen, sizeof(sstr_t)); - // only append elements, if they are not already present in the array - for (size_t i = 0 ; i < arrlen ; ++i) { - if (!ucx_array_contains(result, array+i, ucx_cmp_sstr, NULL)) { - ucx_array_append_from(result, array+i, 1); - } - } - // make the array as small as possible - ucx_array_shrink(result); - return result; -} - -// ... - -sstr_t* array = // some standard array of strings -size_t arrlen = // the length of the array - -UcxArray* result = create_unique(array,arrlen); - -// Iterate over the array and print the elements -sstr_t* unique = result->data; -for (size_t i = 0 ; i < result->size ; i++) { - printf("%" PRIsstr "\n", SFMT(unique[i])); -} - -// Free the array. -ucx_array_free(result); -``` -### Preventing out of bounds writes - -The functions `ucx_array_reserve()`, `ucx_array_resize()`, `ucx_array_grow()`, -and `ucx_array_shrink()` allow easy management of the array capacity. -Imagine you want to add `n` elements to an array. If your `n` elements are -already somewhere else consecutively in memory, you can use -`ucx_array_append_from()` and benefit from the autogrow facility in this family -of functions. Otherwise, you can ask the array to have enough capacity for -holding additional `n` elements. - -```C -size_t n = // ... elements to add -if (ucx_array_grow(array, n)) { - fprintf(stderr, "Cannot add %zu elements to the array.\n", n); - return 1; -} -for (size_t i = 0 ; i < n ; i++) { - ((int*)array->data)[array->size++] = 80; -} -``` - -## AVL Tree - -*Header file:* [avl.h](api-2.1/avl_8h.html) -*Required modules:* [Allocator](#allocator) - -This binary search tree implementation allows average O(1) insertion and -removal of elements (excluding binary search time). -All common binary tree operations are implemented. Furthermore, this module -provides search functions via lower and upper bounds. - -### Filtering items with a time window - -Suppose you have a list of items which contain a `time_t` value and your task -is to find all items within a time window `[t_start, t_end]`. -With AVL Trees this is easy: -```C -// Somewhere in a header -typedef struct { - time_t ts; - // other important data -} MyObject; - -// Source code -UcxAVLTree* tree = ucx_avl_new(ucx_cmp_longint); -// ... populate tree with objects, use '& MyObject.ts' as key ... - - -// Now find every item, with 30 <= ts <= 70 -time_t ts_start = 30; -time_t ts_end = 70; - -printf("Values in range:\n"); -for ( - UcxAVLNode* node = ucx_avl_find_node( - tree, (intptr_t) &ts_start, - ucx_dist_longint, UCX_AVL_FIND_LOWER_BOUNDED); - node && (*(time_t*)node->key) <= ts_end; - node = ucx_avl_succ(node) - ) { - printf(" ts: %ld\n", ((MyObject*)node->value)->ts); -} - -ucx_avl_free_content(tree, free); -ucx_avl_free(tree); -``` - -## Buffer - -*Header file:* [buffer.h](api-2.1/buffer_8h.html) -*Required modules:* None. - -Instances of this buffer implementation can be used to read from or to write to -memory like you would do with a stream. This allows the use of -`ucx_stream_copy()` from the [Utilities](#utilities) module to copy contents -from one buffer to another or from file or network streams to the buffer and -vice-versa. - -More features for convenient use of the buffer can be enabled, like automatic -memory management and automatic resizing of the buffer space. -See the documentation of the macro constants in the header file for more -information. - -### Add line numbers to a file - -When reading a file line by line, you have three options: first, you could limit -the maximum supported line length. -Second, you allocate a god buffer large -enough for the most lines a text file could have. -And third, undoubtedly the best option, you start with a small buffer, which -adjusts on demand. -An `UcxBuffer` can be created to do just that for you. -Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function. -Here is a full working program, which adds line numbers to a file. -```C -#include <stdio.h> -#include <ucx/buffer.h> -#include <ucx/utils.h> - -int main(int argc, char** argv) { - - if (argc != 2) { - fprintf(stderr, "Usage: %s <file>\n", argv[0]); - return 1; - } - - FILE* input = fopen(argv[1], "r"); - if (!input) { - perror("Canno read input"); - return 1; - } - - const size_t chunksize = 256; - - UcxBuffer* linebuf = - ucx_buffer_new( - NULL, // the buffer should manage the memory area for us - 2*chunksize, // initial size should be twice the chunk size - UCX_BUFFER_AUTOEXTEND); // the buffer will grow when necessary - - size_t lineno = 1; - do { - // read line chunk - size_t read = ucx_stream_ncopy( - input, linebuf, fread, ucx_buffer_write, chunksize); - if (read == 0) break; - - // handle line endings - do { - sstr_t bufstr = ucx_buffer_to_sstr(linebuf); - sstr_t nl = sstrchr(bufstr, '\n'); - if (nl.length == 0) break; - - size_t linelen = bufstr.length - nl.length; - sstr_t linestr = sstrsubsl(bufstr, 0, linelen); - - printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr)); - - // shift the buffer to the next line - ucx_buffer_shift_left(linebuf, linelen+1); - } while(1); - - } while(1); - - // print the 'noeol' line, if any - sstr_t lastline = ucx_buffer_to_sstr(linebuf); - if (lastline.length > 0) { - printf("%zu: %" PRIsstr, lineno, SFMT(lastline)); - } - - fclose(input); - ucx_buffer_free(linebuf); - - return 0; -} -``` - -## List - -*Header file:* [list.h](api-2.1/list_8h.html) -*Required modules:* [Allocator](#allocator) - -This module provides the data structure and several functions for a doubly -linked list. Among the common operations like insert, remove, search and sort, -we allow convenient iteration via a special `UCX_FOREACH` macro. - -### Remove duplicates from an array of strings - -Assume you are given an array of `sstr_t` and want to create a list of these -strings without duplicates. -This is a similar example to the one [above](#array), but here we are -using a `UcxList`. -```C -#include <stdio.h> -#include <ucx/list.h> -#include <ucx/string.h> -#include <ucx/utils.h> - -UcxList* remove_duplicates(sstr_t* array, size_t arrlen) { - UcxList* list = NULL; - for (size_t i = 0 ; i < arrlen ; ++i) { - if (ucx_list_find(list, array+i, ucx_cmp_sstr, NULL) == -1) { - sstr_t* s = malloc(sizeof(sstr_t)); - *s = sstrdup(array[i]); - list = ucx_list_append(list, s); - } - } - return list; -} - -// we will need this function to clean up the list contents later -void free_sstr(void* ptr) { - sstr_t* s = ptr; - free(s->ptr); - free(s); -} - -// ... - -sstr_t* array = // some array of strings -size_t arrlen = // the length of the array - -UcxList* list = remove_duplicates(array,arrlen); - -// Iterate over the list and print the elements -UCX_FOREACH(elem, list) { - sstr_t s = *((sstr_t*)elem->data); - printf("%" PRIsstr "\n", SFMT(s)); -} - -// Use our free function to free the duplicated strings. -ucx_list_free_content(list, free_sstr); -ucx_list_free(list); -``` - -## Logging - -*Header file:* [logging.h](api-2.1/logging_8h.html) -*Required modules:* [Map](#map), [String](#string) - -The logging module comes with some predefined log levels and allows some more -customization. You may choose if you want to get timestamps or source file and -line number logged automatically when outputting a message. -The following function call initializes a debug logger with all of the above -information: -```C - log = ucx_logger_new(stdout, UCX_LOGGER_DEBUG, - UCX_LOGGER_LEVEL | UCX_LOGGER_TIMESTAMP | UCX_LOGGER_SOURCE); -``` -Afterwards you can use this logger with the predefined macros -```C - ucx_logger_trace(log, "Verbose output"); - ucx_logger_debug(log, "Debug message"); - ucx_logger_info(log, "Information"); - ucx_logger_warn(log, "Warning"); - ucx_logger_error(log, "Error message"); -``` -or you use -```C - ucx_logger_log(log, CUSTOM_LEVEL, "Some message") -``` -When you use your custom log level, don't forget to register it with -```C - ucx_logger_register_level(log, CUSTOM_LEVEL, "CUSTOM") -``` -where the last argument must be a string literal. - -## Map - -*Header file:* [map.h](api-2.1/map_8h.html) -*Required modules:* [Allocator](#allocator), [String](#string) - -This module provides a hash map implementation using murmur hash 2 and separate -chaining with linked lists. Similarly to the list module, we provide a -`UCX_MAP_FOREACH` macro to conveniently iterate through the key/value pairs. - -### Parsing command line options - -Assume you want to parse command line options and record them within a map. -One way to do this is shown by the following code sample: -```C - UcxMap* options = ucx_map_new(16); - const char *NOARG = ""; - - char *option = NULL; - char optchar = 0; - for(int i=1;i<argc;i++) { - char *arg = argv[i]; - size_t len = strlen(arg); - if(len > 1 && arg[0] == '-') { - for(int c=1;c<len;c++) { - if(option) { - fprintf(stderr, - "Missing argument for option -%c\n", optchar); - return 1; - } - switch(arg[c]) { - default: { - fprintf(stderr, "Unknown option -%c\n\n", arg[c]); - return 1; - } - case 'v': { - ucx_map_cstr_put(options, "verbose", NOARG); - break; - } - case 'o': { - option = "output"; - optchar = 'o'; - break; - } - } - } - } else if(option) { - ucx_map_cstr_put(options, option, arg); - option = NULL; - } else { - // ... handle argument that is not an option ... - } - } - if(option) { - fprintf(stderr, - "Missing argument for option -%c\n", optchar); - return 1; - } -``` -With the following loop, you can access the previously recorded options: -```C - UcxMapIterator iter = ucx_map_iterator(options); - char *arg; - UCX_MAP_FOREACH(optkey, arg, iter) { - char* opt = optkey.data; - if (*arg) { - printf("%s = %s\n", opt, arg); - } else { - printf("%s active\n", opt); - } - } -``` -Don't forget to call `ucx_map_free()`, when you are done with the map. - -## Memory Pool - -*Header file:* [mempool.h](api-2.1/mempool_8h.html) -*Required modules:* [Allocator](#allocator) - -Here we have a concrete allocator implementation in the sense of a memory pool. -This pool allows you to register destructor functions for the allocated memory, -which are automatically called on the destruction of the pool. -But you may also register *independent* destructor functions within a pool in -case some external library allocated memory for you, which should be -destroyed together with this pool. - -Many UCX modules support the use of an allocator. -The [String Module](#string), for instance, provides the `sstrdup_a()` function, -which uses the specified allocator to allocate the memory for the duplicated -string. -This way, you can use a `UcxMempool` to keep track of the memory occupied by -duplicated strings and cleanup everything with just a single call to -`ucx_mempool_destroy()`. - -### Read CSV data into a structure - -The following code example shows some of the basic memory pool functions and -how they can be used with other UCX modules. -```C -#include <stdio.h> -#include <ucx/mempool.h> -#include <ucx/list.h> -#include <ucx/string.h> -#include <ucx/buffer.h> -#include <ucx/utils.h> - -typedef struct { - sstr_t column_a; - sstr_t column_b; - sstr_t column_c; -} CSVData; - -int main(int argc, char** argv) { - - UcxMempool* pool = ucx_mempool_new(128); - - FILE *f = fopen("test.csv", "r"); - if (!f) { - perror("Cannot open file"); - return 1; - } - // close the file automatically at pool destruction - ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose); - - // create a buffer and register it at the memory pool for destruction - UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND); - ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free); - - // read the file and split it by lines first - ucx_stream_copy(f, content, fread, ucx_buffer_write); - sstr_t contentstr = ucx_buffer_to_sstr(content); - ssize_t lc = 0; - sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc); - - // skip the header and parse the remaining data - UcxList* datalist = NULL; - for (size_t i = 1 ; i < lc ; i++) { - if (lines[i].length == 0) continue; - ssize_t fc = 3; - sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc); - if (fc != 3) { - fprintf(stderr, "Syntax error in line %zu.\n", i); - ucx_mempool_destroy(pool); - return 1; - } - CSVData* data = ucx_mempool_malloc(pool, sizeof(CSVData)); - data->column_a = fields[0]; - data->column_b = fields[1]; - data->column_c = fields[2]; - datalist = ucx_list_append_a(pool->allocator, datalist, data); - } - - // control output - UCX_FOREACH(elem, datalist) { - CSVData* data = elem->data; - printf("Column A: %" PRIsstr " | " - "Column B: %" PRIsstr " | " - "Column C: %" PRIsstr "\n", - SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c) - ); - } - - // cleanup everything, no manual free() needed - ucx_mempool_destroy(pool); - - return 0; -} -``` - -### Overriding the default destructor - -Sometimes you need to allocate memory with `ucx_mempool_malloc()`, but the -memory is not supposed to be freed with a simple call to `free()`. -In this case, you can overwrite the default destructor as follows: -```C - MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject)); - - // some special initialization with own resource management - my_object_init(obj); - - // register destructor function - ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy); -``` -Be aware, that your destructor function should not free any memory, that is -also managed by the pool. -Otherwise, you might be risking a double-free. -More precisely, a destructor function set with `ucx_mempool_set_destr()` MUST -NOT call `free()` on the specified pointer whereas a destructor function -registered with `ucx_mempool_reg_destr()` MAY (and in most cases will) call -`free()`. - -## Properties - -*Header file:* [properties.h](api-2.1/properties_8h.html) -*Required modules:* [Map](#map) - -This module provides load and store function for `*.properties` files. -The key/value pairs are stored within an UCX Map. - -### Example: Loading properties from a file - -```C -// Open the file as usual -FILE* file = fopen("myprops.properties", "r"); -if (!file) { - // error handling - return 1; -} - -// Load the properties from the file -UcxMap* myprops = ucx_map_new(16); -if (ucx_properties_load(myprops, file)) { - // ... error handling ... - fclose(file); - ucx_map_free(myprops); - return 1; -} - -// Print out the key/value pairs -char* propval; -UcxMapIterator propiter = ucx_map_iterator(myprops); -UCX_MAP_FOREACH(key, propval, propiter) { - printf("%s = %s\n", (char*)key.data, propval); -} - -// Don't forget to free the values before freeing the map -ucx_map_free_content(myprops, NULL); -ucx_map_free(myprops); -fclose(file); -``` - -## Stack - -*Header file:* [stack.h](api-2.1/stack_8h.html) -*Required modules:* [Allocator](#allocator) - -This concrete implementation of an UCX Allocator allows you to grab some amount -of memory which is then handled as a stack. -Please note, that the term *stack* only refers to the behavior of this -allocator. You may still choose to use either stack or heap memory -for the underlying space. -A typical use case is an algorithm where you need to allocate and free large -amounts of memory very frequently. - -The following code sample shows how to initialize a stack and push and pop -simple data. -```C - const size_t len = 1024; - char space[len]; - UcxStack stack; - ucx_stack_init(&stack, space, len); - - int i = 42; - float f = 3.14f; - const char* str = "Hello!"; - size_t strn = 7; - - // push the integer - ucx_stack_push(&stack, sizeof(int), &i); - - // push the float and rember the address - float* remember = ucx_stack_push(&stack, sizeof(float), &f); - - // push the string with zero terminator - ucx_stack_push(&stack, strn, str); - - // if we forget, how big an element was, we can ask the stack - printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1); - - // retrieve the string as sstr_t, without zero terminator! - sstr_t s; - s.length = ucx_stack_topsize(&stack)-1; - s.ptr = malloc(s.length); - ucx_stack_popn(&stack, s.ptr, s.length); - printf("%" PRIsstr "\n", SFMT(s)); - - // print the float directly from the stack and free it - printf("Float: %f\n", *remember); - ucx_stack_free(&stack, remember); - - // the last element is the integer - int j; - ucx_stack_pop(&stack, &j); - printf("Integer: %d\n", j); -``` - - - -## String - -*Header file:* [string.h](api-2.1/string_8h.html) -*Required modules:* [Allocator](#allocator) - -This module provides a safe implementation of bounded string. -Usually C strings do not carry a length. While for zero-terminated strings you -can easily get the length with `strlen`, this is not generally possible for -arbitrary strings. -The `sstr_t` type of this module always carries the string and its length to -reduce the risk of buffer overflows dramatically. - -### Initialization - -There are several ways to create an `sstr_t`: - -```C -// (1) sstr() uses strlen() internally, hence cstr MUST be zero-terminated -sstr_t a = sstr(cstr); - -// (2) cstr does not need to be zero-terminated, if length is specified -sstr_t b = sstrn(cstr, len); - -// (3) S() macro creates sstr_t from a string using sizeof() and using sstrn(). -// This version is especially useful for function arguments -sstr_t c = S("hello"); - -// (4) SC() macro works like S(), but makes the string immutable using scstr_t. -// (available since UCX 2.0) -scstr_t d = SC("hello"); - -// (5) ST() macro creates sstr_t struct literal using sizeof() -sstr_t e = ST("hello"); -``` - -You should not use the `S()`, `SC()`, or `ST()` macro with string of unknown -origin, since the `sizeof()` call might not coincide with the string length in -those cases. If you know what you are doing, it can save you some performance, -because you do not need the `strlen()` call. - -### Handling immutable strings - -*(Since: UCX 2.0)* - -For immutable strings (i.e. `const char*` strings), UCX provides the `scstr_t` -type, which works exactly as the `sstr_t` type but with a pointer -to `const char`. All UCX string functions come in two flavors: one that enforces -the `scstr_t` type, and another that usually accepts both types and performs -a conversion automatically, if necessary. - -There are some exceptions to this rule, as the return type may depend on the -argument type. -E.g. the `sstrchr()` function returns a substring starting at -the first occurrence of the specified character. -Since this substring points to the memory of the argument string, it does not -accept `scstr_t` as input argument, because the return type would break the -const-ness. - - -### Finding the position of a substring - -The `sstrstr()` function gives you a new `sstr_t` object starting with the -requested substring. Thus determining the position comes down to a simple -subtraction. - -```C -sstr_t haystack = ST("Here we go!"); -sstr_t needle = ST("we"); -sstr_t result = sstrstr(haystack, needle); -if (result.ptr) - printf("Found at position %zd.\n", haystack.length-result.length); -else - printf("Not found.\n"); -``` - -### Spliting a string by a delimiter - -The `sstrsplit()` function (and its allocator based version `sstrsplit_a()`) is -very powerful and might look a bit nasty at a first glance. But it is indeed -very simple to use. It is even more convenient in combination with a memory -pool. - -```C -sstr_t test = ST("here::are::some::strings"); -sstr_t delim = ST("::"); - -ssize_t count = 0; // no limit -UcxMempool* pool = ucx_mempool_new_default(); - -sstr_t* result = sstrsplit_a(pool->allocator, test, delim, &count); -for (ssize_t i = 0 ; i < count ; i++) { - // don't forget to specify the length via the %*s format specifier - printf("%*s\n", result[i].length, result[i].ptr); -} - -ucx_mempool_destroy(pool); -``` -The output is: - - here - are - some - strings - -The memory pool ensures, that all strings are freed. - -### Disabling convenience macros - -If you are experiencing any troubles with the short convenience macros `S()`, -`SC()`, or `ST()`, you can disable them by setting the macro -`UCX_NO_SSTR_SHORTCUTS` before including the header (or via a compiler option). -For the formatting macros `SFMT()` and `PRIsstr` you can use the macro -`UCX_NO_SSTR_FORMAT_MACROS` to disable them. - -Please keep in mind, that after disabling the macros, you cannot use them in -your code *and* foreign code that you might have included. -You should only disable the macros, if you are experiencing a nasty name clash -which cannot be otherwise resolved. - -## Testing - -*Header file:* [test.h](api-2.1/test_8h.html) -*Required modules:* None. - -This module provides a testing framework which allows you to execute test cases -within test suites. -To avoid code duplication within tests, we also provide the possibility to -define test subroutines. - -You should declare test cases and subroutines in a header file per test unit -and implement them as you would implement normal functions. -```C - // myunit.h - UCX_TEST(function_name); - UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional - - - // myunit.c - UCX_TEST_SUBROUTINE(subroutine_name, paramlist) { - // ... reusable tests with UCX_TEST_ASSERT() ... - } - - UCX_TEST(function_name) { - // ... resource allocation and other test preparation ... - - // mandatory marker for the start of the tests - UCX_TEST_BEGIN - - // ... verifications with UCX_TEST_ASSERT() ... - // (and/or calls with UCX_TEST_CALL_SUBROUTINE()) - - // mandatory marker for the end of the tests - UCX_TEST_END - - // ... resource cleanup ... - // (all code after UCX_TEST_END is always executed) - } -``` -If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are -*required* to use a `UCX_TEST_SUBROUTINE`. -Otherwise, the testing framework does not know where to jump, when the assertion -fails. - -After implementing the tests, you can easily build a test suite and execute it: -```C - UcxTestSuite* suite = ucx_test_suite_new(); - ucx_test_register(suite, testMyTestCase01); - ucx_test_register(suite, testMyTestCase02); - // ... - ucx_test_run(suite, stdout); // stdout, or any other FILE stream -``` - -## Utilities - -*Header file:* [utils.h](api-2.1/utils_8h.html) -*Required modules:* [Allocator](#allocator), [String](#string) - -In this module we provide very general utility function for copy and compare -operations. -We also provide several `printf` variants to conveniently print formatted data -to streams or strings. - -### A simple copy program - -The utilities package provides several stream copy functions. -One of them has a very simple interface and can, for instance, be used to copy -whole files in a single call. -This is a minimal working example: -```C -#include <stdio.h> -#include <ucx/utils.h> - -int main(int argc, char** argv) { - - if (argc != 3) { - fprintf(stderr, "Use %s <src> <dest>", argv[0]); - return 1; - } - - FILE *srcf = fopen(argv[1], "r"); // insert error handling on your own - FILE *destf = fopen(argv[2], "w"); - - size_t n = ucx_stream_copy(srcf, destf, fread, fwrite); - printf("%zu bytes copied.\n", n); - - fclose(srcf); - fclose(destf); - - - return 0; -} -``` - -### Automatic allocation for formatted strings - -The UCX utility function `ucx_asprintf()` and it's convenient shortcut -`ucx_sprintf` allow easy formatting of strings, without ever having to worry -about the required space. -```C -sstr_t mystring = ucx_sprintf("The answer is: %d!", 42); -``` -Still, you have to pass `mystring.ptr` to `free()` (or the free function of -your allocator, if you use `ucx_asprintf`). -If you don't have all the information ready to build your string, you can even -use a [UcxBuffer](#buffer) as a target with the utility function -`ucx_bprintf()`. -```C -UcxBuffer* strbuffer = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND); - -for (unsigned int i = 2 ; i < 100 ; i++) { - ucx_bprintf(strbuffer, "Integer %d is %s\n", - i, prime(i) ? "prime" : "not prime"); -} - -// print the result to stdout -printf("%s", (char*)strbuffer->space); - -ucx_buffer_free(strbuffer); -```