docs/src/features.md

Thu, 25 Jan 2024 22:05:48 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 25 Jan 2024 22:05:48 +0100
changeset 819
5da2ead43077
parent 746
b4bd0155f03f
child 831
7970eac1c598
permissions
-rw-r--r--

rename cx_array_copy_result to just cx_array_result

     1 ---
     2 title: UCX Features
     3 ---
     5 <div id="modules">
     7 ------------------------ -------------------------  -------------------  ---------------------------------
     8 [Allocator](#allocator)  [String](#string)          [Buffer](#buffer)    [Memory&nbsp;Pool](#memory-pool)
     9 [Iterator](#iterator)    [Collection](#collection)  [List](#list)        [Map](#map)
    10 [Utilities](#utilities)
    11 ------------------------ -------------------------  -------------------  ---------------------------------
    13 </div>
    15 ## Allocator
    17 *Header file:* [allocator.h](api/allocator_8h.html)  
    19 The UCX allocator provides an interface for implementing an own memory allocation mechanism.
    20 Various function in UCX provide an additional alternative signature that takes an allocator as
    21 argument. A default allocator implementation using the stdlib memory management functions is
    22 available via the global symbol `cxDefaultAllocator`.
    24 If you want to define your own allocator, you need to initialize the `CxAllocator` structure
    25 with a pointer to an allocator class (containing function pointers for the memory management
    26 functions) and an optional pointer to an arbitrary memory region that can be used to store
    27 state information for the allocator. An example is shown below:
    29 ```c
    30 struct my_allocator_state {
    31     size_t total;
    32     size_t avail;
    33     char mem[];
    34 };
    36 static cx_allocator_class my_allocator_class = {
    37         my_malloc_impl,
    38         my_realloc_impl,   // all these functions are somewhere defined
    39         my_calloc_impl,
    40         my_free_impl
    41 };
    43 CxAllocator create_my_allocator(size_t n) {
    44     CxAllocator alloc;
    45     alloc.cl = &my_allocator_class;
    46     alloc.data = calloc(1, sizeof(struct my_allocator_state) + n);
    47     return alloc;
    48 }
    49 ```
    51 ## String
    53 *Header file:* [string.h](api/string_8h.html)
    55 UCX strings come in two variants: immutable (`cxstring`) and mutable (`cxmutstr`).
    56 The functions of UCX are designed to work with immutable strings by default but in situations where it is necessary,
    57 the API also provides alternative functions that work directly with mutable strings.
    58 Functions that change a string in-place are, of course, only accepting mutable strings.
    60 When you are using UCX functions, or defining your own functions, you are sometimes facing the "problem",
    61 that the function only accepts arguments of type `cxstring` but you only have a `cxmutstr` at hand.
    62 In this case you _should not_ introduce a wrapper function that accepts the `cxmutstr`,
    63 but instead you should use the `cx_strcast()` function to cast the argument to the correct type.
    65 In general, UCX strings are **not** necessarily zero-terminated. If a function guarantees to return zero-terminated
    66 string, it is explicitly mentioned in the documentation of the respective function.
    67 As a rule of thumb, you _should not_ pass the strings of a UCX string structure to another API without explicitly
    68 ensuring that the string is zero-terminated.
    70 ## Buffer
    72 *Header file:* [buffer.h](api/buffer_8h.html)
    74 Instances of this buffer implementation can be used to read from or write to memory like you would do with a stream.
    75 This allows the use of `cx_stream_copy()` (see [Utilities](#utilities)) to copy contents from one buffer to another,
    76 or from a file or network streams to the buffer and vice-versa.
    78 More features for convenient use of the buffer can be enabled, like automatic memory management and automatic
    79 resizing of the buffer space.
    81 Since UCX 3.0, the buffer also supports automatic flushing of contents to another stream (or buffer) as an alternative
    82 to automatically resizing the buffer space.
    83 Please refer to the API doc for the fields prefixed with `flush_` to learn more. 
    85 ## Memory Pool
    87 *Header file:* [mempool.h](api/mempool_8h.html)
    89 A memory pool is providing an allocator implementation that automatically deallocates the memory upon its destruction. 
    90 It also allows you to register destructor functions for the allocated memory, which are automatically called before
    91 the memory is deallocated.
    92 Additionally, you may also register _independent_ destructor functions within a pool in case some external library
    93 allocated memory for you, which should be destroyed together with this pool.
    95 Many UCX features support the use of an allocator.
    96 The [strings](#string), for instance, provide several functions suffixed with `_a` that allow specifying an allocator.
    97 You can use this to keep track of the memory occupied by dynamically allocated strings and cleanup everything with
    98 just a single call to `cxMempoolDestroy()`.
   100 The following code illustrates this on the example of reading a CSV file into memory. 
   101 ```C
   102 #include <stdio.h>
   103 #include <cx/mempool.h>
   104 #include <cx/linked_list.h>
   105 #include <cx/string.h>
   106 #include <cx/buffer.h>
   107 #include <cx/utils.h>
   109 typedef struct {
   110     cxstring column_a;
   111     cxstring column_b;
   112     cxstring column_c;
   113 } CSVData;
   115 int main(void) {
   116     CxMempool* pool = cxBasicMempoolCreate(128);
   118     FILE *f = fopen("test.csv", "r");
   119     if (!f) {
   120         perror("Cannot open file");
   121         return 1;
   122     }
   123     // close the file automatically at pool destruction
   124     cxMempoolRegister(pool, f, (cx_destructor_func) fclose);
   126     // create a buffer using the memory pool for destruction
   127     CxBuffer *content = cxBufferCreate(NULL, 256, pool->allocator, CX_BUFFER_AUTO_EXTEND);
   129     // read the file into the buffer and turn it into a string
   130     cx_stream_copy(f, content, (cx_read_func) fread, (cx_write_func) cxBufferWrite);
   131     fclose(f);
   132     cxstring contentstr = cx_strn(content->space, content->size);
   134     // split the string into lines - use the mempool for allocating the target array
   135     cxstring* lines;
   136     size_t lc = cx_strsplit_a(pool->allocator, contentstr,
   137                               CX_STR("\n"), SIZE_MAX, &lines);
   139     // skip the header and parse the remaining data into a linked list
   140     // the nodes of the linked list shall also be allocated by the mempool
   141     CxList* datalist = cxLinkedListCreate(pool->allocator, NULL, sizeof(CSVData));
   142     for (size_t i = 1 ; i < lc ; i++) {
   143         if (lines[i].length == 0) continue;
   144         cxstring fields[3];
   145         size_t fc = cx_strsplit(lines[i], CX_STR(";"), 3, fields);
   146         if (fc != 3) {
   147             fprintf(stderr, "Syntax error in line %zu.\n", i);
   148             cxMempoolDestroy(pool);
   149             return 1;
   150         }
   151         CSVData data;
   152         data.column_a = fields[0];
   153         data.column_b = fields[1];
   154         data.column_c = fields[2];
   155         cxListAdd(datalist, &data);
   156     }
   158     // iterate through the list and output the data
   159     CxIterator iter = cxListIterator(datalist);
   160     cx_foreach(CSVData*, data, iter) {
   161         printf("Column A: %.*s | "
   162                "Column B: %.*s | "
   163                "Column C: %.*s\n",
   164                (int)data->column_a.length, data->column_a.ptr,
   165                (int)data->column_b.length, data->column_b.ptr,
   166                (int)data->column_c.length, data->column_c.ptr
   167         );
   168     }
   170     // cleanup everything, no manual free() needed 
   171     cxMempoolDestroy(pool);
   173     return 0;
   174 } 
   175 ```
   177 ## Iterator
   179 *Header file:* [iterator.h](api/iterator_8h.html)
   181 In UCX 3 a new feature has been introduced to write own iterators, that work with the `cx_foreach` macro.
   182 In previous UCX releases there were different hard-coded foreach macros for lists and maps that were not customizable.
   183 Now, creating an iterator is as simple as creating a `CxIterator` struct and setting the fields in a meaningful way.
   185 You do not always need all fields in the iterator structure, depending on your use case.
   186 Sometimes you only need the `index` (for example when iterating over simple lists), and other times you will need the
   187 `slot` and `kv_data` fields (for example when iterating over maps).
   189 Usually an iterator is not mutating the collection it is iterating over.
   190 In some programming languages it is even disallowed to change the collection while iterating with foreach.
   191 But sometimes it is desirable to remove an element from the collection while iterating over it.
   192 This is, what the `CxMutIterator` is for.
   193 The only differences are, that the `mutating` flag is `true` and the `src_handle` is not const.
   194 On mutating iterators it is allowed to call the `cxFlagForRemoval()` function, which instructs the iterator to remove
   195 the current element from the collection on the next call to `cxIteratorNext()` and clear the flag afterward.
   196 If you are implementing your own iterator, it is up to you to implement this behavior in your `next` method, or
   197 make the implementation of the `flag_removal` method always return `false`.
   199 ## Collection
   201 *Header file:* [collection.h](api/collection_8h.html)
   203 Collections in UCX 3 have several common features.
   204 If you want to implement an own collection data type that uses the same features, you can use the
   205 `CX_COLLECTION_MEMBERS` macro at the beginning of your struct to roll out all members a usual UCX collection has.
   206 ```c
   207 struct my_fancy_collection_s {
   208     CX_COLLECTION_MEMBERS
   209     struct my_collection_data_s *data;
   210 };
   211 ```
   212 Based on this structure, this header provides some convenience macros for invoking the destructor functions
   213 that are part of the basic collection members.
   214 The idea of having destructor functions within a collection is that you can destroy the collection _and_ the
   215 contents with one single function call.
   216 When you are implementing a collection, you are responsible for invoking the destructors at the right places, e.g.
   217 when removing (and deleting) elements in the collection, clearing the collection, or - the most prominent case -
   218 destroying the collection.
   220 You can always look at the UCX list and map implementations if you need some inspiration.
   222 ## List
   224 *Header file:* [list.h](api/list_8h.html)
   226 This header defines a common interface for all list implementations, which is basically as simple as the following
   227 structure.
   228 ```c
   229 struct cx_list_s {
   230     CX_COLLECTION_MEMBERS       // size, capacity, etc.
   231     cx_list_class const *cl;    // The list class definition
   232 };
   233 ```
   234 The actual structure contains one more class pointer that is used when wrapping a list into a pointer-aware list
   235 with `cxListStorePointers()`. What this means, is that - if you want to implement your own list structure - you
   236 only need to cover the case where the list is storing copies of your objects.
   238 UCX comes with two common list implementations (linked list and array list) that should cover most use cases.
   239 But if you feel the need to implement an own list, the only thing you need to do is to define a struct where
   240 `struct cx_list_s`, and set an appropriate list class that implements the functionality.
   241 It is strongly recommended that this class is shared among all instances of the same list type, because otherwise
   242 the `cxListCompare` function cannot use the optimized implementation of your class and will instead fall back to
   243 using iterators to compare the contents element-wise.
   245 ### Linked List
   247 *Header file:* [linked_list.h](api/linked__list_8h.html)
   249 On top of implementing the list interface, this header also defines several low-level functions that
   250 work with arbitrary structures. 
   251 Low-level functions, in contrast to the high-level list interface, can easily be recognized by their snake-casing.
   252 The function `cx_linked_list_at`, for example, implements a similar functionality like `cxListAt`, but operates
   253 on arbitrary structures.
   254 The following snippet shows how it is used.
   255 All other low-level functions work similarly.
   256 ```c
   257 struct node {
   258     node *next;
   259     node *prev;
   260     int data;
   261 };
   263 const ptrdiff_t loc_prev = offsetof(struct node, prev);
   264 const ptrdiff_t loc_next = offsetof(struct node, next);
   265 const ptrdiff_t loc_data = offsetof(struct node, data);
   267 struct node a = {0}, b = {0}, c = {0}, d = {0};
   268 cx_linked_list_link(&a, &b, loc_prev, loc_next);
   269 cx_linked_list_link(&b, &c, loc_prev, loc_next);
   270 cx_linked_list_link(&c, &d, loc_prev, loc_next);
   272 cx_linked_list_at(&a, 0, loc_next, 2); // returns pointer to c
   273 ```
   275 ### Array List
   277 *Header file:* [array_list.h](api/array__list_8h.html)
   279 Since low-level array lists are just plain arrays, there is no need for such many low-level functions as for linked
   280 lists.
   281 However, there is one extremely powerful function that can be used for several complex tasks: `cx_array_copy`.
   282 The full signature is shown below:
   283 ```c
   284 enum cx_array_result cx_array_copy(
   285         void **target,
   286         size_t *size,
   287         size_t *capacity,  // optional
   288         size_t index,
   289         void const *src,
   290         size_t elem_size,
   291         size_t elem_count,
   292         struct cx_array_reallocator_s *reallocator // optional
   293 );
   294 ```
   295 The `target` argument is a pointer to the target array pointer.
   296 The reason for this additional indirection is that - given that you provide a `reallocator` - this function writes
   297 back the pointer to the possibly reallocated array.
   298 The next two arguments are pointers to the `size` and `capacity` of the target array.
   299 Tracking the capacity is optional.
   300 If you do not specify a pointer for the capacity, automatic reallocation of the array is entirely disabled (i.e. it
   301 does not make sense to specify a `reallocator` then).
   302 In this case, the function cannot copy more than `size-index` elements and if you try, it will return
   303 `CX_ARRAY_REALLOC_NOT_SUPPORTED` and do nothing.
   305 On a successful invocation, the function copies `elem_count` number of elements, each of size `elem_size` from
   306 `src` to `*target` and uses the `reallocator` to extend the array when necessary.
   307 Finally, the size, capacity, and the pointer to the array are all updated and the function returns
   308 `CX_ARRAY_SUCCESS`.
   310 The third, but extremely rare, return code is `CX_ARRAY_REALLOC_FAILED` and speaks for itself.
   312 A few things to note:
   313 * `*target` and `src` can point to the same memory region, effectively copying elements within the array with `memmove`
   314 * `*target` does not need to point to the start of the array, but `size` and `capacity` always start counting from the
   315   position, `*target` points to - in this scenario, specifying a `reallocator` is forbidden for obvious reasons
   316 * `index` does not need to be within size of the current array, if `capacity` is specified
   317 * `index` does not even need to be within the capacity of the array, if `reallocator` is specified 
   319 If you just want to add one single element to an existing array, you can use the macro `cx_array_add()`.
   320 In that case, since the element is added to the end of the array, the `capacity` argument is mandatory.
   322 ## Map
   324 *Header file:* [map.h](api/map_8h.html)
   326 Similar to the list interface, the map interface provides a common API for implementing maps.
   327 There are some minor subtle differences, though.
   329 First, the `remove` method is not always a destructive removal.
   330 Instead, the last argument is a Boolean that indicates whether the element shall be destroyed or returned.
   331 ```c
   332 void *(*remove)(CxMap *map, CxHashKey key, bool destroy);
   333 ```
   334 When you implement this method, you are either supposed to invoke the destructors and return `NULL`,
   335 or just remove the element from the map and return it.
   337 Secondly, the iterator method is a bit more complete. The signature is as follows:
   338 ```c
   339 CxIterator (*iterator)(CxMap const *map, enum cx_map_iterator_type type);
   340 ```
   341 There are three map iterator types: for values, for keys, for pairs.
   342 Depending on the iterator type requested, you need to create an iterator with the correct methods that
   343 return the requested thing.
   344 There are no automatic checks to enforce this - it's completely up to you.
   345 If you need inspiration on how to do that, check the hash map implementation that comes with UCX.
   347 ### Hash Map
   349 *Header file:* [hash_map.h](api/hash__map_8h.html)
   351 UCX provides a basic hash map implementation with a configurable amount of buckets.
   352 If you do not specify the number of buckets, a default of 16 buckets will be used.
   353 You can always rehash the map with `cxMapRehash()` to change the number of buckets to something more efficient,
   354 but you need to be careful, because when you use this function you are effectively locking into using this
   355 specific hash map implementation, and you would need to remove all calls to this function when you want to
   356 exchange the concrete map implementation with something different.
   358 ## Utilities
   360 *Header file:* [utils.h](api/utils_8h.html)
   362 UCX provides some utilities for routine tasks. Most of them are simple macros, like e.g. the `cx_for_n()` macro,
   363 creating a `for` loop counting from zero to (n-1) which is extremely useful to traverse the indices of
   364 an array.
   366 But the most useful utilities are the *stream copy* functions, which provide a simple way to copy all - or a
   367 bounded amount of - data from one stream to another. Since the read/write functions of a UCX buffer are
   368 fully compatible with stream read/write functions, you can easily transfer data from file or network streams to
   369 a UCX buffer or vice-versa.
   371 The following example shows, how easy it is to read the contents of a file into a buffer:
   372 ```c
   373 FILE *inputfile = fopen(infilename, "r");
   374 if (inputfile) {
   375     CxBuffer fbuf;
   376     cxBufferInit(&fbuf, NULL, 4096, NULL, CX_BUFFER_AUTO_EXTEND);
   377     cx_stream_copy(inputfile, &fbuf,
   378                    (cx_read_func) fread,
   379                    (cx_write_func) cxBufferWrite);
   380     fclose(inputfile);
   382     // ... do something meaningful with the contents ...
   384     cxBufferDestroy(&fbuf);
   385 } else {
   386     perror("Error opening input file");
   387     if (fout != stdout) {
   388         fclose(fout);
   389     }
   390 }
   391 ```
   393 ### Printf Functions
   395 *Header file:* [printf.h](api/printf_8h.html)
   397 In this utility header you can find `printf()`-like functions that can write the formatted output to an arbitrary
   398 stream (or UCX buffer, resp.), or to memory allocated by an allocator within a single function call.
   399 With the help of these convenience functions, you do not need to `snprintf` your string to a temporary buffer anymore,
   400 plus you do not need to worry about too small buffer sizes, because the functions will automatically allocate enough
   401 memory to contain the entire formatted string.
   403 ### Compare Functions
   405 *Header file:* [compare.h](api/compare_8h.html)
   407 This header file contains a collection of compare functions for various data types.
   408 Their signatures are designed to be compatible with the `cx_compare_func` function pointer type.

mercurial