proof-read documentation

Wed, 31 Dec 2025 16:01:08 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 31 Dec 2025 16:01:08 +0100
changeset 1694
a2757c6427cc
parent 1693
c2d05cf1a062
child 1695
d21cec66facc

proof-read documentation

CHANGELOG file | annotate | diff | comparison | revisions
docs/Writerside/topics/about.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/allocator.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/array_list.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/buffer.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/collection.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/compare.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/hash_key.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/hash_map.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/json.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/linked_list.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/memory.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/mempool.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/printf.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/properties.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/streams.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/string.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/strings.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/test.h.md file | annotate | diff | comparison | revisions
docs/Writerside/topics/tree.h.md file | annotate | diff | comparison | revisions
tests/test_string.c file | annotate | diff | comparison | revisions
--- a/CHANGELOG	Wed Dec 31 15:25:30 2025 +0100
+++ b/CHANGELOG	Wed Dec 31 16:01:08 2025 +0100
@@ -87,7 +87,7 @@
  * changes grow strategy for the memory pool to reduce reallocations
  * changes grow strategy for CxBuffer, which does now take the page size into account
  * changes the implementation of cx_strreplacen() for improved efficiency
- * changes all cxListIterator() and cxMapIterator() family of functions to also accept NULL as argument
+ * changes all cxListIterator() and cxMapIterator() families of functions to also accept NULL as argument
  * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element
  * changes the compare function wrapper for pointer lists so that it no longer invokes the actual compare function for NULL pointers
  * changes struct cx_array_reallocator_s by replacing the four generic data members with two specifically named members
--- a/docs/Writerside/topics/about.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/about.md	Wed Dec 31 16:01:08 2025 +0100
@@ -113,7 +113,7 @@
 * changes grow strategy for the memory pool to reduce reallocations
 * changes grow strategy for CxBuffer, which does now take the page size into account
 * changes the implementation of cx_strreplacen() for improved efficiency
-* changes all cxListIterator() and cxMapIterator() family of functions to also accept NULL as argument
+* changes all cxListIterator() and cxMapIterator() families of functions to also accept NULL as argument
 * changes insert_element member function of CxList to accept NULL source and return a pointer to the inserted element
 * changes the compare function wrapper for pointer lists so that it no longer invokes the actual compare function for NULL pointers
 * changes struct cx_array_reallocator_s by replacing the four generic data members with two specifically named members
--- a/docs/Writerside/topics/allocator.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/allocator.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -103,7 +103,7 @@
 if (cx_reallocate(&mem, capacity + 32)) // ... do error handling
 ```
 
-> Please pay special attention to always use `cxFree()` and the  `cxRealloc()`-family of functions
+> Please pay special attention and always use `cxFree()` and the `cxRealloc()`-family of functions
 > with the **same** allocator that was used to allocate the memory.
 {style="warning"}
 
@@ -158,7 +158,7 @@
 typedef void (*cx_destructor_func2)(void *data, void *memory);
 ```
 
-The first one is called _simple_ destructor (e.g. in the context of [collections](collection.h.md)),
+The first one is called _simple_ destructor (e.g., in the context of [collections](collection.h.md)),
 and the second one is called _advanced_ destructor.
 The only difference is that you can pass additional custom `data` to an advanced destructor function.
 
@@ -226,7 +226,7 @@
 }
 ```
 
-Clone functions are, for example, used by the functions to clone [lists](list.h.md#clone) or [maps](map.h.md#clone).
+Clone functions are, for example, used by [lists](list.h.md#clone) or [maps](map.h.md#clone).
 
 <seealso>
 <category ref="apidoc">
--- a/docs/Writerside/topics/array_list.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/array_list.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -133,7 +133,7 @@
 ```
 
 The functions `cx_array_remove()` and `cx_array_remove_array()` remove one or more elements from the array by shifting the remaining elements by the number of removed elements.
-The functions `cx_array_remove_fast()` and `cx_array_remove_array_fast()` on the other hand copy elements from the end of the array into the gap to fill the removed elements.
+The functions `cx_array_remove_fast()` and `cx_array_remove_array_fast()` on the other hand, copy elements from the end of the array into the gap to fill the removed elements.
 Therefore, if the order of the elements does not matter, you can use the fast versions of these functions for better performance.
 
 > When you specify an `index` that is out-of-bounds or choose a number `n` of elements that would overflow the array,
@@ -157,9 +157,9 @@
 With simple `cx_compare_func` functions, arrays can always be sorted with standard `qsort()`.
 Sorting arrays with `cx_compare_func2` functions, however, need special support by either `qsort_r()` (GNU) or `qsort_s()` (ISO).
 However, both functions come with their own challenges.
-On the one hand, `qsort_r()` is not ISO standard, and on the other hand `qsort_s()` is only optional and has an incorrect parameter order in the Microsoft C library.
+On the one hand, `qsort_r()` is not ISO standard, and on the other hand, `qsort_s()` is only optional and has an incorrect parameter order in the Microsoft C library.
 
-To provide a platform independent solution, UCX detects if `qsort_r()` is available and implements a fallback when it is not.
+To provide a platform-independent solution, UCX detects if `qsort_r()` is available and implements a fallback when it is not.
 You can safely use `cx_array_qsort_c()` everywhere wehere you would use `qsort_r()`.
 
 The functions `cx_array_sort()` and `cx_array_sort_c()` are for convenient work with arrays declared with `CX_ARRAY()`.
--- a/docs/Writerside/topics/buffer.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/buffer.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -115,7 +115,7 @@
 
 The function `cxBufferDestroy()` is to be used when the buffer was initialized with `cxBufferInit()`,
 and the function `cxBufferFree()` is to be used when the buffer was created with `cxBufferCreate()`.
-The only difference is, that `cxBufferFree()` additionally deallocates the memory of the `CxBuffer` structure. 
+The only difference is that `cxBufferFree()` additionally deallocates the memory of the `CxBuffer` structure. 
 
 ## Capacity Management
 
@@ -152,7 +152,7 @@
 
 > If the buffer is in a copy-on-write state, `cxBufferMinimumCapacity()` will perform the copy-on-write action
 > before reallocating the space.
-> The function `cxBufferShrink()` on the other hand does _nothing_ when the buffer is in a copy-on-write state,
+> The function `cxBufferShrink()` on the other hand, does _nothing_ when the buffer is in a copy-on-write state,
 > because it makes little sense to copy the memory just to have it in a smaller memory region.
 
 ## Write
@@ -182,7 +182,7 @@
 items that do not fit into the buffer are discarded.
 The function then returns the actual number of items successfully written.
 This equals the number of bytes if and only if `size=1`.
-If `CX_BUFFER_AUTO_EXTEND` is set, the buffer is grown to it's maximum capacity
+If `CX_BUFFER_AUTO_EXTEND` is set, the buffer is grown to its maximum capacity
 (see `cxBufferMaximumCapacity()` in [](#capacity-management)).
 In case the allocation for auto-extension fails, the function immediately returns zero and does not write any data.
 
@@ -200,7 +200,7 @@
 effectively creating a zero-terminated string whose size equals the buffer size.
 
 > If you use cxBufferTerminate() on a buffer with the `CX_BUFFER_COPY_ON_EXTEND` flag set, the shrink operation is skipped.
-> Using `cxBufferTerminate()` on a buffer with the `CX_BUFFER_COPY_ON_WRITE` flag set, will copy the entire memory just to add the zero-terminator.
+> Using `cxBufferTerminate()` on a buffer with the `CX_BUFFER_COPY_ON_WRITE` flag set will copy the entire memory just to add the zero-terminator.
 
 The function `cxBufferAppend()` writes the data to the end of the buffer (given by its size) regardless of the current position,
 and it also does _not_ advance the position.
@@ -222,15 +222,15 @@
 and stores them into the memory pointed to by `ptr`, which must be large enough to hold the contents.
 The function returns the actual bytes read, which might be lower if the desired number of items is not available.
 
-The function `cxBufferGet()` is a `fgetc()`-like function which returns the next byte in the buffer converted to an `int`.
+The function `cxBufferGet()` is an `fgetc()`-like function which returns the next byte in the buffer converted to an `int`.
 Similar to `fgetc()` it returns `EOF` when there are no more bytes in the buffer.
 
 When all bytes from the buffer have been read, the function `cxBufferEof()` returns true. 
 
 All read functions advance the position in the buffer by the number of read bytes.
 
-> When you want to read from a buffer that you previously used for writing, do not forget to set the position
-> in the buffer to zero, e.g. by calling `cxBufferSeek()`.
+> When you want to read from a buffer that you previously used for writing, remember to set the position
+> in the buffer to zero, e.g., by calling `cxBufferSeek()`.
 
 ## Reset and Clear
 
@@ -245,7 +245,7 @@
         size_t size, size_t nitems);
 ```
 
-The function `cxBufferReset()` sets both size and position of the buffer to zero,
+The function `cxBufferReset()` sets both the size and position of the buffer to zero,
 and `cxBufferClear()` additionally uses `memset()` to set every byte in the buffer to zero.
 
 > When clearing the buffer, only the "live" data, i.e., bytes with indices `[0..size)`, are cleared.
@@ -271,7 +271,7 @@
 relative to the start (when `whence` is `SEEK_SET`), the current position (when `whence` is `SEEK_CUR`),
 or end (when `whence` is `SEEK_END`) of the buffer.
 
-If the resulting position is negative, or larger than the size (i.e. the first unused byte),
+If the resulting position is negative, or larger than the size (i.e., the first unused byte),
 this function returns non-zero and sets `errno` to `EINVAL`.
 
 > Note that the behavior of `cxBufferSeek()` when seeking beyond the end of the buffer is not exactly the same as for POSIX `fseek()`.
@@ -295,7 +295,7 @@
 Data shifted to the left is always discarded when the new position of a byte would be smaller than zero.
 When bytes are discarded, the new position of the buffer is set to zero.
 
-When data is shift to the right, the behavior depends on the `CX_BUFFER_AUTO_EXTEND` flag.
+When data is shifted to the right, the behavior depends on the `CX_BUFFER_AUTO_EXTEND` flag.
 If set, the function extends the buffer's capacity before moving the data.
 Otherwise, the function discards all data that would exceed the buffer's capacity, and both the size and the position are equal to the capacity
 (which means, `cxBufferEof()` returns `true` after the operation). 
--- a/docs/Writerside/topics/collection.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/collection.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -35,7 +35,7 @@
 | `store_pointer`       | A `bool` indicating whether this collection stores pointers instead of the element's data.                                        |
 | `sorted`              | A `bool` indicating whether the elements are currently guaranteed sorted with respect to the compare function.                    |
 
-The attributes can be accessed directly via the `collection` member of your struct, or with the following convenience macros.
+The attributes can be accessed directly via the `collection` member of your struct or with the following convenience macros.
 
 ```C
 cxCollectionSize(c)
--- a/docs/Writerside/topics/compare.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/compare.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -8,7 +8,7 @@
 
 ## Examples
 
-In the following example we use `cx_cmp_int32` as compare function for a `CxList` of `int32_t` values.
+In the following example we use `cx_cmp_int32` as the compare function for a `CxList` of `int32_t` values.
 
 ```C
 CxList *list = cxArrayListCreate(
@@ -16,11 +16,12 @@
     sizeof(int32_t),    // the size of one element
     256                 // reseve space for 256 elements
 );
-cxSetCompareFunc(list, cx_cmp_int32); // the compare function for the elements
+// set the compare function for the elements
+cxSetCompareFunc(list, cx_cmp_int32);
 ```
 
 In the next example we simply want to compare two `double` values with rounding tolerance.
-Note how the use of the `cx_vcmp` flavour makes it unnecessary to store the literal in a variable just to be able to take an address.
+Note how the use of the `cx_vcmp` flavor makes it unnecessary to store the literal in a variable just to be able to take an address.
 ```C
 double x = ...
 
--- a/docs/Writerside/topics/hash_key.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/hash_key.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -27,7 +27,7 @@
 
 ## Description
 
-The primary function for creating a `CxHashKey` structure from non-integers is `cx_hash_key()`.
+The primary function for creating a `CxHashKey` structure from nonintegers is `cx_hash_key()`.
 The other functions effectively do the same, but
 
 * `cx_hash_key_bytes()` is strongly typed if you want to avoid passing `void*` 
--- a/docs/Writerside/topics/hash_map.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/hash_map.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -27,13 +27,12 @@
 If you pass zero for the number of `buckets`,
 the map is initialized with a default of 16 buckets; otherwise the specified number of buckets is allocated.
 
-The function `cxMapRehash()` allocates a new array of buckets and re-distributes all elements,
-if the number of elements exceeds ¾ of the number of buckets.
-Otherwise, no action is performed, and this function simply returns 0.
-After rehashing, the number of buckets is at least 2½ times the number of elements.
+The `cxMapRehash()` function creates a new bucket array and reassigns all elements when the element count surpasses three-quarters of the bucket count.
+If this condition isn't met, the function returns 0 without making any changes.
+Post-rehashing, the bucket count will be at least two and a half times the element count.
 
 > Advice if you want to create your own hash map structures:
-> Calling `cxMapRehash()` on a map is only defined, when the map is based on the
+> Calling `cxMapRehash()` on a map is only defined when the map is based on the
 > `struct cx_hash_map_s` structure.
 
 <seealso>
--- a/docs/Writerside/topics/json.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/json.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -3,7 +3,7 @@
 The UCX JSON API allows [parsing](#parser) and [formatting](#writer) of JSON data.
 
 The parser API is similar to the [properties](properties.h.md) parser,
-but - due to the nature of JSON - is not allocation-free.
+but - due to the nature of JSON - it is not allocation-free.
 
 ## Parser
 
@@ -41,7 +41,7 @@
 The actual parsing is an interleaving invocation of the `cxJsonFill()` (or `cxJsonFilln()`) and `cxJsonNext()` functions.
 The `cxJsonFill()` function is a convenience function that accepts UCX strings and normal zero-terminated C strings.
 
-Calling `cxJsonNext()` will return with `CX_JSON_NO_ERROR` (= zero) for each JSON value that is successfully parsed,
+Calling `cxJsonNext()` will return with `CX_JSON_NO_ERROR` (= zero) for each JSON value that is successfully parsed
 and stores the pointer to the allocated value in the variable pointed to by `value`.
 
 > The parser is capable of parsing multiple consecutive JSON values.
@@ -49,7 +49,7 @@
 
 When all the data from the input buffer was successfully consumed, `cxJsonNext()` returns `CX_JSON_NO_DATA`.
 
-If `cxJsonNext()` returns `CX_JSON_INCOMPLETE_DATA` it means that the input buffer is exhausted,
+If `cxJsonNext()` returns `CX_JSON_INCOMPLETE_DATA`, the input buffer is exhausted,
 but the parsed input does not constitute a complete JSON value. 
 In that case, you can call `cxJsonFill()` again to add more data and continue with `cxJsonNext()`.
 
@@ -144,7 +144,7 @@
 > `cxJsonIsFalse()` _only_ returns true, if the value is a literal `false`.
 >{style="note"}
 
-The `cxJsonAsXYZ()` family of functions return the value with its corresponding C type.
+The `cxJsonAsXYZ()` functions return the value with its corresponding plain type.
 
 The functions `cxJsonAsInteger()` and `cxJsonAsDouble()` can be used for any number value.
 For example, if `cxJsonAsInteger()` is used on a non-integral number, a double-to-int conversion is performed.
--- a/docs/Writerside/topics/linked_list.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/linked_list.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -47,16 +47,16 @@
 
 The functions usually expect a `loc_prev` and a `loc_next` offset.
 In the example structure from above you can obtain them with `offsetof(struct node, next)` and `offsetof(struct node, prev)`.
-In all functions, `loc_prev` is optional in the sense, that when you do not have a `prev` pointer, you can specify a negative value.
+In all functions, `loc_prev` is optional in the sense that when you do not have a `prev` pointer, you can specify a negative value.
 When a function expects a `loc_advance` offset, you can freely choose if you want to pass the offset of the `next` or the `prev` pointer,
 depending on what you want to do.
 
 If a function expects a `void** begin` and a `void** end` pointer, they are usually both optional, unless otherwise specified.
-If non-`NULL`, they point to the variables where the addresses of the first, or the last, node of a list are stored, respectively.
+If non-`NULL`, they point to the variables where the addresses of the first, or the last, node belonging to the list are stored, respectively.
 When a list operation results in a new first (or last) node, the addresses are overwritten.
 In simple scenarios, you usually keep a pointer to the beginning of a list, and hence you would usually pass `NULL` to the `end` argument.
 If you are designing a stack-like linked-list, it may happen that you only want to store the last node of a list (the top of stack),
-hence passing `NULL` to the `begin` argument and the address of your top-of-stack pointer to the `end` argument.
+hence passing `NULL` to the `begin` and the address of your top-of-stack pointer to the `end`.
 Either way, most functions allow you a big deal of flexibility — please still read the documentation of each function carefully to learn which combinations are allowed.
 
 When you are working with a singly linked list, it is still sometimes necessary to access the predecessor of a node.
@@ -91,7 +91,7 @@
         ptrdiff_t loc_prev, ptrdiff_t loc_next);
 ```
 
-When you have two nodes `left` and `right` you can link or unlink them with the functions shown above.
+When you have two nodes `left` and `right`, you can link or unlink them with the functions shown above.
 
 When linking `left` and `right` you should make sure that `left` as currently no successor and `right` has no predecessor,
 because the pointers will be overwritten without unlinking possible existing links, first.
@@ -184,7 +184,7 @@
 > it cannot take advantage of simply inserting the entire chain as-is, as the chain might need to be broken
 > to maintain the sort order.
 
-The functions with the `_c` suffix are equivalent, except that they accept a `cx_compare_func2` with additional `context`.
+The functions with the `_c` suffix are equivalent, except that they accept a `cx_compare_func2` with an additional `context`.
 
 ## Access and Find
 
@@ -215,7 +215,7 @@
 node in your list in case you are not keeping track of them separately.
 They can start at an arbitrary node within the list.
 
-The function `cx_linked_list_at()` starts at an arbitrary node `start` which is _specified_ to have the index `start_index`,
+The function `cx_linked_list_at()` starts at an arbitrary node `start`, which is _specified_ to have the index `start_index`,
 and finds the node at `index`.
 If `index` is larger than `start_index`, you must pass the offset of the `next` pointer to `loc_advanced`.
 On the other hand, if `index` is smaller than `start_index`, you must pass the offset of the `prev` pointer.
@@ -225,13 +225,13 @@
 The function `cx_linked_list_find()` starts a search at the `start` node and compares each element with `elem`.
 If `loc_data` is non-zero, the data-type of `elem` must be a pointer to data which is compatible to the data located at the specified offset in the node.
 If `loc_data` is zero, `elem` is expected to point to a node structure (which is usually artificially created for the sake of comparison and not contained in the list).
-When the searched element is found, a pointer to the node is returned and the index (assuming `start` has index zero) is written to the optional `found_index`, if non-`NULL`.
+When the searched element is found, a pointer to the node is returned, and the index (assuming `start` has index zero) is written to the optional `found_index`, if non-`NULL`.
 
 The function `cx_linked_list_find_c()` allows additional `context` for the compare function.
 
 The size of a list, or sub-list, can be determined with `cx_linked_list_size()` which may start at an arbitrary `node´ in the list.
 
-> A creative way of using `cx_linked_list_size()` in doubly-linked lists is to use the offset of the `prev` pointer
+> A creative way of using `cx_linked_list_size()` in doubly linked lists is to use the offset of the `prev` pointer
 > for `loc_next`, in which case the function will return the index of the node within the list plus one.
 
 ## Remove
@@ -257,11 +257,11 @@
 The `prev` and `next` pointers of _all_ removed nodes are kept completely intact, allowing traversal within the removed chain,
 as well as identifying the formerly adjacent nodes with the list from which the chain was removed.  
 
-> Both `begin` and `end` pointers are optional, if you specify both `loc_prev` and `loc_next`.
+> Both `begin` and `end` pointers are optional if you specify both `loc_prev` and `loc_next`.
 > In case your list does not have a `prev` pointer, specifying `begin` is mandatory (because there would be no other way to determine the predecessor of `node`).
 >{style="note"} 
 
-> While specifying _only_ `loc_prev` and `end` is technically illegal, you can simply swap roles
+> While specifying _only_ `loc_prev` and `end` is technically illegal, you can swap their roles
 > and use the offset of your `prev` pointer as `loc_next` and the address of your `end` pointer as `begin`.
 > The list is then traversed backwards, but otherwise everything works as expected. 
 
@@ -291,7 +291,7 @@
 
 The function `cx_linked_list_sort_c()` allows additional `context` for the compare function.
 
-> The `begin` pointer is required in all of the above functions while the `end` pointer is still optional.
+> The `begin` pointer is required in all the above functions, while the `end` pointer is still optional.
 > {style="note"}
 
 > Sorting uses [small buffer optimization](install.md#small-buffer-optimizations) for small list sizes.
@@ -323,7 +323,7 @@
 and `loc_advance` is the offset of the `next` pointer.
 But it is also possible to start with the _last_ node of both lists and use the `prev` pointer to compare them backwards.
 
-The `loc_data` offset is used to calculate the pointer that is passed to the `cmp_func`.
+The `loc_data` offset is used to calculate the pointer passed to the `cmp_func`.
 This can either be the offset of a specific field in the struct or simply zero, in which case the pointers to the nodes themselves are passed to the compare function.
 
 The function `cx_linked_list_compare_c()` allows additional `context` for the compare function.
--- a/docs/Writerside/topics/memory.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/memory.md	Wed Dec 31 16:01:08 2025 +0100
@@ -1,6 +1,6 @@
 # Memory Management
 
-With the `CxAllocator` interface UCX provides the possibility to use custom allocator functions for different purposes.
+With the `CxAllocator` interface, UCX provides the possibility to use custom allocator functions for different purposes.
 Many UCX functions support the use of specialized allocators or provide a second function suffixed with `_a`.
 
 For convenience, functions that are not explicitly requesting an allocator - like e.g. `cx_strdup_a()` - also accept
--- a/docs/Writerside/topics/mempool.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/mempool.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -118,7 +118,7 @@
 
 The function `cxMempoolTransfer()` transfers all memory managed and/or registered with the `source` pool to the `dest` pool.
 It also registers its allocator with the `dest` pool and creates a new allocator for the `source` pool.
-That means, that all references to the allocator of the `source` pool remain valid and continue to work with the `dest` pool.
+That means that all references to the allocator of the `source` pool remain valid and continue to work with the `dest` pool.
 The transferred allocator will be destroyed when the `dest` pool gets destroyed.
 
 The function `cxMempoolTransferObject()` transfers a _single_ object managed by the `source` pool to the `dest` pool.
@@ -131,7 +131,7 @@
 - the pools have a different type (simple, advanced, pure)
 - the pools have different base allocators (i.e. `cxDefaultAllocator` changed between creation of the pools)
 
-In case of an error, no memory is transferred and both pools remain unchanged and are in a valid state.
+In case of an error, no memory is transferred, and both pools remain unchanged and are in a valid state.
 
 
 ## Example
--- a/docs/Writerside/topics/printf.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/printf.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -53,8 +53,8 @@
         const char *fmt, va_list ap);
 ```
 
-The `cx_asprintf()` and `cx_asprintf_a()` functions print the formatted output directly to a freshly allocated
-string which is then returned from the function.
+The `cx_asprintf()` and `cx_asprintf_a()` functions print the formatted output directly to a freshly allocated string,
+which is then returned from the function.
 On platforms (or when using allocators) where allocation can fail,
 the returned string may be empty and the `ptr` field set to `NULL`. 
 
@@ -122,7 +122,7 @@
 
 All functions that allocate internal temporary memory use small buffer optimization to avoid a heap allocation
 if the expected string length is smaller than `cx_printf_sbo_size`.
-This size cannot be changed at runtime, but modified by defining the `CX_PRINTF_SBO_SIZE` macro when [building](install.md#small-buffer-optimizations) the library.
+This size cannot be changed at runtime but modified by defining the `CX_PRINTF_SBO_SIZE` macro when [building](install.md#small-buffer-optimizations) the library.
 
 <seealso>
 <category ref="apidoc">
--- a/docs/Writerside/topics/properties.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/properties.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -82,11 +82,11 @@
 Note that leading spaces of a continuing line are ignored.
 
 The actual parsing is an interleaving invocation of the `cxPropertiesFill()` (or `cxPropertiesFilln()`) and `cxPropertiesNext()` functions.
-The `cxPropertiesFill()` function is a convenience function, that accepts UCX strings and normal zero-terminated C strings and behaves otherwise like `cxPropertiesFilln()`.
+The `cxPropertiesFill()` function is a convenience function that accepts UCX strings and normal zero-terminated C strings and behaves otherwise like `cxPropertiesFilln()`.
 
 Filling the input buffer is cost-free if there is no data already in the input buffer.
 In that case, the input buffer only stores the pointer to the original data without creating a copy.
-Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed,
+Calling `cxPropertiesNext()` will return with `CX_PROPERTIES_NO_ERROR` (= zero) for each key/value-pair that is successfully parsed
 and stores the pointers and lengths for both the key and the value into the structures pointed to by the `key` and `value` arguments.
 
 When all the data from the input buffer was successfully consumed, `cxPropertiesNext()` returns `CX_PROPERTIES_NO_DATA`.
@@ -97,7 +97,7 @@
 > because you will otherwise soon end up with a dangling pointer.
 > {style="note"}
 
-If `cxPropertiesNext()` returns `CX_PROPERTIES_INCOMPLETE_DATA` it means that the input buffer is exhausted,
+If `cxPropertiesNext()` returns `CX_PROPERTIES_INCOMPLETE_DATA`, the input buffer is exhausted,
 but the last line did not contain a full key/value pair.
 In that case, you can call `cxPropertiesFill()` again to add more data and continue with `cxPropertiesNext()`.
 
--- a/docs/Writerside/topics/streams.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/streams.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -33,7 +33,7 @@
 The `cx_stream_bcopy()` function uses either a pre-initialized buffer `buf` of length `bufsize`
 or, if `buf` is `NULL`, an internal heap-allocated buffer.
 
-The `cx_stream_ncopy()` function behaves like `cx_stream_copy()` except, that it reads at most `n` bytes
+The `cx_stream_ncopy()` function behaves like `cx_stream_copy()` except that it reads at most `n` bytes
 (and the same is true for `cx_stream_bncopy()` and `cx_stream_bcopy()`).
 
 
@@ -54,7 +54,7 @@
 
 ## Example
 
-The following example shows, how to read the contents of a file into a buffer:
+The following example shows how to read the contents of a file into a buffer:
 ```c
 FILE *inputfile = fopen(infilename, "r");
 if (inputfile) {
--- a/docs/Writerside/topics/string.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/string.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -14,7 +14,7 @@
 > To simplify documentation, we introduce the pseudo-type `AnyStr` with the meaning that
 > any UCX string and any C string are supported.
 > The implementation is actually hidden behind a macro which uses `cx_strcast()` to guarantee compatibility.
-> Similarly we introduce `UcxStr` with the meaning that it is either a `cxstring` or a `cxmutstr`,
+> Similarly, we introduce `UcxStr` with the meaning that it is either a `cxstring` or a `cxmutstr`,
 > created by `cx_strcast_m()`.
 {style="note"}
 
@@ -66,11 +66,11 @@
 and guarantees that the result string is zero-terminated.
 The function `cx_strdup()` is equivalent to `cx_strdup_a()`, except that it uses the [default allocator](allocator.h.md#default-allocator).
 
-The functions `cx_strcpy_a()` and `cx_strcpy()` copy the contents of the `source` string to the `dest` string,
+The functions `cx_strcpy_a()` and `cx_strcpy()` copy the contents of the `source` string to the `dest` string
 and also guarantee zero-termination of the resulting string.
 The memory in `dest` is either freshly allocated or re-allocated to fit the size of the string plus the terminator.
 
-Allocated strings are always of type `cxmutstr` and can be deallocated by a call to `cx_strfree()` or `cx_strfree_a()`.
+Allocated strings always have the type `cxmutstr` and can be deallocated by a call to `cx_strfree()` or `cx_strfree_a()`.
 The caller must make sure to use the correct allocator for deallocating a string.
 It is safe to call these functions multiple times on a given string, as the pointer will be nulled and the length set to zero.
 It is also safe to call the functions with a `NULL`-pointer, just like any other `free()`-like function.
@@ -111,7 +111,7 @@
 The `cx_strcmp()` function compares two strings lexicographically
 and returns an integer greater than, equal to, or less than 0, if `s1` is greater than, equal to, or less than `s2`, respectively.
 
-The `cx_strcmp_p()` function takes pointers to UCX strings (i.e., only to `cxstring` and `cxmutstr`) and the signature is compatible with `cx_compare_func`.
+The `cx_strcmp_p()` function takes pointers to UCX strings (i.e., only to `cxstring` and `cxmutstr`), and the signature is compatible with `cx_compare_func`.
 Use this as a compare function for lists or other data structures.
 
 The functions `cx_strprefix()` and `cx_strsuffic()` check if `string` starts with `prefix` or ends with `suffix`, respectively.
@@ -140,7 +140,7 @@
 and appends the contents of the strings to `str`.
 `cx_strcat()` is equivalent, except that it uses the [default allocator](allocator.h.md#default-allocator).
 
-When there is no `str` where the other strings shall be appended to, you can pass `CX_NULLSTR` as first argument.
+When there is no `str` where the other strings shall be appended to, you can pass `CX_NULLSTR` as the first argument.
 In that case, a completely new string is allocated.
 
 Example usage:
@@ -239,14 +239,14 @@
 That means, at most `limit-1` splits are performed.
 The function returns the actual number of items written to `output`.
 
-On the other hand, `cx_strsplit_a()` uses the specified `allocator` to allocate the output array,
+On the other hand, `cx_strsplit_a()` uses the specified `allocator` to allocate the output array
 and writes the pointer to the allocated memory to `output`.
 
 > The type of the `UcxStr` must the same for `string` and `output` (i.e., either both `cxstring` or both `cxmutstr`). 
 > {style="note"}
 
 > The `allocator` in `cx_strsplit_a()` is _only_ used to allocate the output array.
-> The strings will always point into the original `string`
+> The strings will always point into the original `string`,
 > and you need to use `cx_strdup()` or `cx_strdup_a()` if you want copies or zero-terminated strings after performing the split.  
 {style="note"}
 
@@ -263,14 +263,14 @@
 bool cx_strtok_next(CxStrtokCtx *ctx, UcxStr* token);
 ```
 
-You can tokenize a string by creating a _tokenization_ context with `cx_strtok()`,
+You can tokenize a string by creating a _tokenization_ context with `cx_strtok()`
 and calling `cx_strtok_next()` as long as it returns `true`.
 
 The tokenization context is initialized with the string `str` to tokenize,
 one delimiter `delim`, and a `limit` for the maximum number of tokens.
 When `limit` is reached, the remaining part of `str` is returned as one single token.
 
-You can add additional delimiters to the context by calling `cx_strtok_delim()`, and
+You can add additional delimiters to the context by calling `cx_strtok_delim()` and
 specifying an array of delimiters to use.
 
 > Regardless of how the context was initialized, you can use `cx_strtok_next()`
--- a/docs/Writerside/topics/strings.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/strings.md	Wed Dec 31 16:01:08 2025 +0100
@@ -6,8 +6,8 @@
 
 Additionally, UCX offers several advanced [printf-like functions](printf.h.md) that also allow convenient work
 with strings of unknown length.
-For example, one the more advanced functions is `cx_sprintf_sa()` which lets you format a string into an existing
-pre-allocated buffer (e.g. on the stack) and automatically switches to a fresh buffer allocated by a custom allocator
+For example, one of the more advanced functions is `cx_sprintf_sa()` which lets you format a string into an existing
+pre-allocated buffer (e.g., on the stack) and automatically switches to a fresh buffer allocated by a custom allocator
 when the existing buffer is not large enough.
 
 The string API is designed to work with _both_ mutable and constant strings.
--- a/docs/Writerside/topics/test.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/test.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -16,7 +16,7 @@
 ```
 
 A test case needs to be defined with the `CX_TEST` macro.
-The general structure of a test case is 1) setup code, 2) the actual test, 3) tear down code.
+The general structure of a test case is 1) setup code, 2) the actual test, 3) tear-down code.
 In UCX this is realized by the `CX_TEST_DO` macro with which you can define a scope for the actual test.
 Whenever a test assertion fails, the scope is exited and the tear-down code is executed.
 
--- a/docs/Writerside/topics/tree.h.md	Wed Dec 31 15:25:30 2025 +0100
+++ b/docs/Writerside/topics/tree.h.md	Wed Dec 31 16:01:08 2025 +0100
@@ -1,7 +1,7 @@
 # Tree
 
 UCX provides several low-level functions for working with arbitrary tree structures,
-as well as some high-level functions for trees that induce a certain order on the data they store. 
+as well as some high-level functions for trees with a configurable node layout. 
 
 The following convenience macro allows you to declare and use simple tree structures.
 
@@ -139,17 +139,19 @@
 void *cxTreeSetRoot(CxTree *tree, void *root);
 ```
 
+The above functions are used to add nodes to a tree.
+
 The low-level function `cx_tree_add()` adds a `node` to a new `parent`, unlinking it from its previous parent, if required.
 
 When you have created a high-level tree that is still empty, you can create a root node with `cxTreeCreateRoot()` or `cxTreeCreateRootData()`.
 Both functions return an existing root node, if present, and `NULL` when the allocation of a new node failed.
 
 The functions `cxTreeAddData()`, `cxTreeAddNode()`, and `cxTreeSetParent()` are similar to `cx_tree_add()`.
-The difference between `cxTreeAddData()` and `cxTreeAddNode()` is,
-that `cxTreeAddData()` uses the allocator of the `CxTree` to create the node first, before adding it.
+The difference between `cxTreeAddData()` and `cxTreeAddNode()` is
+that `cxTreeAddData()` uses the allocator of the `CxTree` to create the node first before adding it.
 The function returns `NULL` in case the creation of the node fails, or the created node when creation was successful.
-The difference between `cxTreeSetParent()` and `cxTreeAddNode()` is,
-that `cxTreeSetParent()` does not increase the tree size, when `child` already had a parent.
+The difference between `cxTreeSetParent()` and `cxTreeAddNode()` is
+that `cxTreeSetParent()` does not increase the tree size when `child` already had a parent.
 
 When you want to use `cxTreeCreateRootData()` or `cxTreeAddData()`,
 the `loc_data` parameter of `cxTreeCreate()` must have been set to a non-negative value.
@@ -215,6 +217,10 @@
 
 void *cxTreeFindFast(CxTree *tree, const void *data,
         cx_tree_search_func sfunc);
+
+void *cxTreeFindFastInSubtree(CxTree *tree, const void *data,
+        cx_tree_search_func sfunc,
+        void *subtree_root, size_t max_depth);
         
 void *cxTreeFind(CxTree *tree, const void *data, bool use_dfs);
 
@@ -227,15 +233,15 @@
 The search proceeds as follows, starting with the `root` node:
 
 1. The current node is compared with `data` using the `sfunc`.
-2. If `sfunc` returns zero, a matching node has been found and the pointer to that node is stored at the location given by `result`.
+2. If `sfunc` returns zero, a matching node has been found, and the pointer to that node is stored at the location given by `result`.
 3. If `sfunc` returns a negative number, the entire subtree is skipped.
 4. If `sfunc` returns a positive number, the subtree is searched, unless the number is larger than the number of an already searched subtree. The best match will be stored at the location given by `result`. 
-5. The return value will be the return value of the `sfunc` for node that was stored in the `result`, or a negative number when no match was found
+5. The return value will be the return value of the `sfunc` for node, that was stored in the `result`, or a negative number when no match was found
 
 > If the `sfunc` implements some kind of metric, the search functions will return the best match in the tree with respect to that metric.
 > When a positive number is returned, it means that a new node with the searched data could be added as a child of the found node.
 
-The function `cxTreeFindFast()` uses the `sfunc` to find the `data` in the tree, and returns a pointer to the node when the data was found (or `NULL`, otherwise).
+The function `cxTreeFindFast()` uses the `sfunc` to find the `data` in the tree and returns a pointer to the node when the data was found (or `NULL`, otherwise).
 
 The function `cxTreeFind()` instead traverses the entire tree (either with breadth-first search or depth-first search)
 and compares the `data` with the collections [comparator function](collection.h.md#comparator-functions).
@@ -243,7 +249,7 @@
 
 The functions `cxTreeFindFastInSubtree()` and `cxTreeFindInSubtree()` are similar, except that they restrict the search to nodes
 in the subtree starting with (and including) `subtree_root`, and skipping all nodes below the `max_depth`.
-Note, that the `max_depth` is specified in relation to the `subtree_root` and not in relation to the entire tree.
+Note that the `max_depth` is specified in relation to the `subtree_root` and not in relation to the entire tree.
 
 ## Iterator and Visitor
 
@@ -290,7 +296,7 @@
 > In the best case it just does nothing, but calling them guarantees that no memory can be leaking, even when your code will change in the future.
 >{style="note"}
 
-The macro `cxTreeIteratorContinue()` instruct the iterator to skip the subtree below the currently inspected node.
+The macro `cxTreeIteratorContinue()` instructs the iterator to skip the subtree below the currently inspected node.
 
 > In contrast to iterators over [lists](list.h.md#iterators) or [maps](map.h.md#iterators),
 > a tree iterator is _always_ iterating over the nodes.
@@ -324,7 +330,7 @@
 The function `cxTreeRemoveNode()`, on the other hand, _only_ removes the `node` from the tree,
 links all children of `node` to the former parent of `node`, 
 and calls the optional `relink_func` for every former child of `node` which will be relinked to a new parent.
-Therefore, calling `cxTreeRemoveNode()` on the `root` node is an error and the function returns non-zero.
+Therefore, calling `cxTreeRemoveNode()` on the `root` node is an error, and the function returns non-zero.
 In all other cases, the function returns zero.
 
 > When your tree is storing a scene graph, for example, a possible use-case for the `relink_func` might be updating
@@ -357,7 +363,7 @@
 The function `cxTreeDestroySubtree()` performs the above actions for the entire subtree starting with `node`.
 The order in which the destructor functions for the nodes of the subtree are called is determined by a [tree iterator](#iterator-and-visitor).
 
-The function `cxTreeClear()` is a shorthand for invoking `cxTreeDestroySubtree()` on the root node of the tree.
+The function `cxTreeClear()` is short for invoking `cxTreeDestroySubtree()` on the root node of the tree if it exists.
 
 The function `cxTreeFree()` behaves like `cxTreeClear()` and then deallocates the memory for the `CxTree` structure.
 
--- a/tests/test_string.c	Wed Dec 31 15:25:30 2025 +0100
+++ b/tests/test_string.c	Wed Dec 31 16:01:08 2025 +0100
@@ -343,7 +343,7 @@
 
     CX_TEST_DO {
         // the entire string
-        for (off_t i = 0; i < str.length; i++) {
+        for (off_t i = 0; i < (off_t) str.length; i++) {
             CX_TEST_ASSERT(cx_strat(str, i) == str.ptr[i]);
         }
         // the entire string backwards

mercurial