merges constsstr branch

Mon, 14 May 2018 17:56:03 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 14 May 2018 17:56:03 +0200
changeset 306
90b6d69bb499
parent 305
353d71349e61 (diff)
parent 300
d1f814633049 (current diff)
child 308
d6f580621512

merges constsstr branch

configure.ac file | annotate | diff | comparison | revisions
src/string.c file | annotate | diff | comparison | revisions
src/ucx/string.h file | annotate | diff | comparison | revisions
src/ucx/ucx.h file | annotate | diff | comparison | revisions
test/string_tests.c file | annotate | diff | comparison | revisions
     1.1 --- a/.hgtags	Sun May 13 07:13:09 2018 +0200
     1.2 +++ b/.hgtags	Mon May 14 17:56:03 2018 +0200
     1.3 @@ -1,2 +1,3 @@
     1.4  a6184aff5108ba849759030fc75df9f4178f6e86 v1.0
     1.5  9c1591b3c4a4b259de601d1da6cf06e7c445595d v1.0.1
     1.6 +1f9237cfeb265dbb820e673fe5578fb02154766f v1.1
     2.1 --- a/configure.ac	Sun May 13 07:13:09 2018 +0200
     2.2 +++ b/configure.ac	Mon May 14 17:56:03 2018 +0200
     2.3 @@ -27,8 +27,9 @@
     2.4  #
     2.5  
     2.6  # the package version must match the macros in ucx.h
     2.7 -# if you change the package version, don't forget to adjust the library version
     2.8 -AC_INIT([ucx], [1.0.1], [olaf.wintermann@gmail.com])
     2.9 +# the lib version must follow the libtool versioning convention
    2.10 +AC_INIT([ucx], [2.0.0], [olaf.wintermann@gmail.com])
    2.11 +AC_SUBST([UCX_LIB_VERSION], [3:0:0])
    2.12  
    2.13  # don't place everything in the project root
    2.14  AC_CONFIG_AUX_DIR([build-aux])
     3.1 --- a/docs/src/header.html	Sun May 13 07:13:09 2018 +0200
     3.2 +++ b/docs/src/header.html	Mon May 14 17:56:03 2018 +0200
     3.3 @@ -21,17 +21,17 @@
     3.4                      <li><a href="modules.html">Modules</a>
     3.5                      <ul>
     3.6                          <li><a href="modules.html#allocator">Allocator</a></li>
     3.7 -                        <li><a href="modules.html#avl">AVL Tree</a></li>
     3.8 +                        <li><a href="modules.html#avl-tree">AVL Tree</a></li>
     3.9                          <li><a href="modules.html#buffer">Buffer</a></li>
    3.10                          <li><a href="modules.html#list">List</a></li>
    3.11                          <li><a href="modules.html#logging">Logging</a></li>
    3.12                          <li><a href="modules.html#map">Map</a></li>
    3.13 -                        <li><a href="modules.html#mempool">Memory Pool</a></li>
    3.14 +                        <li><a href="modules.html#memory-pool">Memory Pool</a></li>
    3.15                          <li><a href="modules.html#properties">Properties</a></li>
    3.16                          <li><a href="modules.html#stack">Stack</a></li>
    3.17                          <li><a href="modules.html#string">String</a></li>
    3.18 -                        <li><a href="modules.html#test">Testing</a></li>
    3.19 -                        <li><a href="modules.html#utils">Utilities</a></li>
    3.20 +                        <li><a href="modules.html#testing">Testing</a></li>
    3.21 +                        <li><a href="modules.html#utilities">Utilities</a></li>
    3.22                      </ul>
    3.23                      </li>
    3.24                      <li><a target="_blank" href="api/index.html">API Reference</a></li>
     4.1 --- a/docs/src/modules.md	Sun May 13 07:13:09 2018 +0200
     4.2 +++ b/docs/src/modules.md	Mon May 14 17:56:03 2018 +0200
     4.3 @@ -9,22 +9,20 @@
     4.4  For instance, the [Allocator](#allocator) module is used by many other modules
     4.5  to allow flexible memory allocation.
     4.6  By default the header files are placed into an `ucx` directory within your
     4.7 -systems include directory. In this case you can use an module by including it
     4.8 +systems include directory. In this case you can use a module by including it
     4.9  via `#include <ucx/MODULENAME.h>`.
    4.10  Required modules are included automatically.
    4.11  
    4.12  <div id="modules" align="center">
    4.13      
    4.14 ------------------------ ---------------------- ---------------------------- -------------------------
    4.15 -[Allocator](#allocator) [AVL&nbsp;Tree](#avl)  [Buffer](#buffer)            [List](#list)
    4.16 -[Logging](#logging)     [Map](#map)            [Memory&nbsp;Pool](#mempool) [Properties](#properties)
    4.17 -[Stack](#stack)         [String](#string)      [Testing](#test)             [Utilities](#utils)
    4.18 ------------------------ ---------------------- ---------------------------- -------------------------
    4.19 +----------------------- ----------------------      ----------------------------     -------------------------
    4.20 +[Allocator](#allocator) [AVL&nbsp;Tree](#avl-tree)  [Buffer](#buffer)                [List](#list)
    4.21 +[Logging](#logging)     [Map](#map)                 [Memory&nbsp;Pool](#memory-pool) [Properties](#properties)
    4.22 +[Stack](#stack)         [String](#string)           [Testing](#testing)              [Utilities](#utilities)
    4.23 +----------------------- ----------------------      ----------------------------     -------------------------
    4.24  
    4.25  </div>
    4.26  
    4.27 -<a name="allocator"></a>
    4.28 -
    4.29  ## Allocator
    4.30  
    4.31  *Header file:* [allocator.h](api/allocator_8h.html)  
    4.32 @@ -41,9 +39,7 @@
    4.33  
    4.34  As the pointer to the memory area / pool can be arbitrarily chosen, any data
    4.35  can be provided to the memory management functions. One example is the
    4.36 -[UCX Memory Pool](#mempool).
    4.37 -
    4.38 -<a name="avl"></a>
    4.39 +[UCX Memory Pool](#memory-pool).
    4.40  
    4.41  ## AVL Tree
    4.42  
    4.43 @@ -55,7 +51,46 @@
    4.44  All common binary tree operations are implemented. Furthermore, this module
    4.45  provides search functions via lower and upper bounds.
    4.46  
    4.47 -<a name="buffer"></a>
    4.48 +### Filtering items with a time window
    4.49 +
    4.50 +Suppose you have a list of items which contain a `time_t` value and your task
    4.51 +is to find all items within a time window `[t_start, t_end]`.
    4.52 +With AVL Trees this is easy:
    4.53 +```C
    4.54 +/* ---------------------
    4.55 + * Somewhere in a header
    4.56 + */
    4.57 +typedef struct {
    4.58 +    time_t ts;
    4.59 +    /* other important data */
    4.60 +} MyObject;
    4.61 +
    4.62 +/* -----------
    4.63 + * Source code
    4.64 + */
    4.65 +
    4.66 +UcxAVLTree* tree = ucx_avl_new(ucx_longintcmp);
    4.67 +/* ... populate tree with objects, use '& MyObject.ts' as key ... */
    4.68 +
    4.69 +
    4.70 +/* Now find every item, with 30 <= ts <= 70 */
    4.71 +time_t ts_start = 30;
    4.72 +time_t ts_end = 70;
    4.73 +
    4.74 +printf("Values in range:\n");
    4.75 +for (
    4.76 +        UcxAVLNode* node = ucx_avl_find_node(
    4.77 +            tree, (intptr_t) &ts_start,
    4.78 +            ucx_longintdist, UCX_AVL_FIND_LOWER_BOUNDED);
    4.79 +        node && (*(time_t*)node->key) <= ts_end;
    4.80 +        node = ucx_avl_succ(node)
    4.81 +    ) {
    4.82 +    printf(" ts: %ld\n", ((MyObject*)node->value)->ts);
    4.83 +}
    4.84 +
    4.85 +ucx_avl_free_content(tree, free);
    4.86 +ucx_avl_free(tree);
    4.87 +```
    4.88  
    4.89  ## Buffer
    4.90  
    4.91 @@ -64,8 +99,8 @@
    4.92  
    4.93  Instances of this buffer implementation can be used to read from or to write to
    4.94  memory like you would do with a stream. This allows the use of
    4.95 -`ucx_stream_copy` from the [Utilities](#utils) module to copy contents from one
    4.96 -buffer to another or from file or network streams to the buffer and
    4.97 +`ucx_stream_copy()` from the [Utilities](#utilities) module to copy contents
    4.98 +from one buffer to another or from file or network streams to the buffer and
    4.99  vice-versa.
   4.100  
   4.101  More features for convenient use of the buffer can be enabled, like automatic
   4.102 @@ -73,7 +108,79 @@
   4.103  See the documentation of the macro constants in the header file for more
   4.104  information.
   4.105  
   4.106 -<a name="list"></a>
   4.107 +### Add line numbers to a file
   4.108 +
   4.109 +When reading a file line by line, you have three options: first, you could limit
   4.110 +the maximum supported line length.
   4.111 +Second, you allocate a god buffer large
   4.112 +enough for the most lines a text file could have.
   4.113 +And third, undoubtedly the best option, you start with a small buffer, which
   4.114 +adjusts on demand.
   4.115 +An `UcxBuffer` can be created to do just that for you.
   4.116 +Just pass the `UCX_BUFFER_AUTOEXTEND` option to the initialization function.
   4.117 +Here is a full working program, which adds line numbers to a file.
   4.118 +```C
   4.119 +#include <stdio.h>
   4.120 +#include <ucx/buffer.h>
   4.121 +#include <ucx/utils.h>
   4.122 +
   4.123 +int main(int argc, char** argv) {
   4.124 +
   4.125 +    if (argc != 2) {
   4.126 +        fprintf(stderr, "Usage: %s <file>\n", argv[0]);
   4.127 +        return 1;
   4.128 +    }
   4.129 +
   4.130 +    FILE* input = fopen(argv[1], "r");
   4.131 +    if (!input) {
   4.132 +        perror("Canno read input");
   4.133 +        return 1;
   4.134 +    }
   4.135 +
   4.136 +    const size_t chunksize = 256;
   4.137 +
   4.138 +    UcxBuffer* linebuf =
   4.139 +        ucx_buffer_new(
   4.140 +            NULL,       /* the buffer should manage the memory area for us */
   4.141 +            2*chunksize,  /* initial size should be twice the chunk size */
   4.142 +            UCX_BUFFER_AUTOEXTEND); /* the buffer will grow when necessary */
   4.143 +
   4.144 +    size_t lineno = 1;
   4.145 +    do {
   4.146 +        /* read line chunk */
   4.147 +        size_t read = ucx_stream_ncopy(
   4.148 +                input, linebuf, fread, ucx_buffer_write, chunksize);
   4.149 +        if (read == 0) break;
   4.150 +        
   4.151 +        /* handle line endings */
   4.152 +        do {
   4.153 +            sstr_t bufstr = ucx_buffer_to_sstr(linebuf);
   4.154 +            sstr_t nl = sstrchr(bufstr, '\n');
   4.155 +            if (nl.length == 0) break;
   4.156 +
   4.157 +            size_t linelen = bufstr.length - nl.length;
   4.158 +            sstr_t linestr = sstrsubsl(bufstr, 0, linelen);
   4.159 +
   4.160 +            printf("%zu: %" PRIsstr "\n", lineno++, SFMT(linestr));
   4.161 +
   4.162 +            /* shift the buffer to the next line */
   4.163 +            ucx_buffer_shift_left(linebuf, linelen+1);
   4.164 +        } while(1);
   4.165 +
   4.166 +    } while(1);
   4.167 +
   4.168 +    /* print the 'noeol' line, if any */
   4.169 +    sstr_t lastline = ucx_buffer_to_sstr(linebuf);
   4.170 +    if (lastline.length > 0) {
   4.171 +        printf("%zu: %" PRIsstr, lineno, SFMT(lastline));
   4.172 +    }
   4.173 +
   4.174 +    fclose(input);
   4.175 +    ucx_buffer_free(linebuf);
   4.176 +
   4.177 +    return 0;
   4.178 +}
   4.179 +```
   4.180  
   4.181  ## List
   4.182  
   4.183 @@ -84,7 +191,52 @@
   4.184  linked list. Among the common operations like insert, remove, search and sort,
   4.185  we allow convenient iteration via a special `UCX_FOREACH` macro.
   4.186  
   4.187 -<a name="logging"></a>
   4.188 +### Remove duplicates from an array of strings
   4.189 +
   4.190 +Assume you are given an array of `sstr_t` and want to create a list of these
   4.191 +strings without duplicates.
   4.192 +```C
   4.193 +#include <stdio.h>
   4.194 +#include <ucx/list.h>
   4.195 +#include <ucx/string.h>
   4.196 +#include <ucx/utils.h>
   4.197 +
   4.198 +UcxList* remove_duplicates(sstr_t* array, size_t arrlen) {
   4.199 +    UcxList* list = NULL;
   4.200 +    for (size_t i = 0 ; i < arrlen ; ++i) {
   4.201 +        if (ucx_list_find(list, array+i, ucx_sstrcmp, NULL) == -1) {
   4.202 +            sstr_t* s = malloc(sizeof(sstr_t));
   4.203 +            *s = sstrdup(array[i]);
   4.204 +            list = ucx_list_append(list, s);
   4.205 +        }
   4.206 +    }
   4.207 +    return list;
   4.208 +}
   4.209 +
   4.210 +/* we will need this function to clean up the list contents later */
   4.211 +void free_sstr(void* ptr) {
   4.212 +    sstr_t* s = ptr;
   4.213 +    free(s->ptr);
   4.214 +    free(s);
   4.215 +}
   4.216 +
   4.217 +/* ... */
   4.218 +
   4.219 +sstr_t* array = /* some array of strings */
   4.220 +size_t arrlen = /* the length of the array */
   4.221 +
   4.222 +UcxList* list = remove_duplicates(array,arrlen);
   4.223 +
   4.224 +/* Iterate over the list and print the elements */
   4.225 +UCX_FOREACH(elem, list) {
   4.226 +    sstr_t s = *((sstr_t*)elem->data);
   4.227 +    printf("%" PRIsstr "\n", SFMT(s));
   4.228 +}
   4.229 +
   4.230 +/* Use our free function to free the duplicated strings. */
   4.231 +ucx_list_free_content(list, free_sstr);
   4.232 +ucx_list_free(list);
   4.233 +```
   4.234  
   4.235  ## Logging
   4.236  
   4.237 @@ -94,9 +246,29 @@
   4.238  The logging module comes with some predefined log levels and allows some more
   4.239  customization. You may choose if you want to get timestamps or source file and
   4.240  line number logged automatically when outputting a message.
   4.241 -
   4.242 -
   4.243 -<a name="map"></a>
   4.244 +The following function call initializes a debug logger with all of the above
   4.245 +information:
   4.246 +```C
   4.247 +    log = ucx_logger_new(stdout, UCX_LOGGER_DEBUG,
   4.248 +            UCX_LOGGER_LEVEL | UCX_LOGGER_TIMESTAMP | UCX_LOGGER_SOURCE);
   4.249 +```
   4.250 +Afterwards you can use this logger with the predefined macros
   4.251 +```C
   4.252 +    ucx_logger_trace(log, "Verbose output");
   4.253 +    ucx_logger_debug(log, "Debug message");
   4.254 +    ucx_logger_info(log, "Information");
   4.255 +    ucx_logger_warn(log, "Warning");
   4.256 +    ucx_logger_error(log, "Error message");
   4.257 +```
   4.258 +or you use
   4.259 +```C
   4.260 +    ucx_logger_log(log, CUSTOM_LEVEL, "Some message")
   4.261 +```
   4.262 +When you use your custom log level, don't forget to register it with
   4.263 +```C
   4.264 +    ucx_logger_register_level(log, CUSTOM_LEVEL, "CUSTOM")
   4.265 +```
   4.266 +where the last argument must be a string literal.
   4.267  
   4.268  ## Map
   4.269  
   4.270 @@ -107,7 +279,69 @@
   4.271  chaining with linked lists. Similarly to the list module, we provide a
   4.272  `UCX_MAP_FOREACH` macro to conveniently iterate through the key/value pairs.
   4.273  
   4.274 -<a name="mempool"></a>
   4.275 +### Parsing command line options
   4.276 +
   4.277 +Assume you want to parse command line options and record them within a map.
   4.278 +One way to do this is shown by the following code sample:
   4.279 +```C
   4.280 +    UcxMap* options = ucx_map_new(16);
   4.281 +    const char *NOARG = "";
   4.282 +    
   4.283 +    char *option = NULL;
   4.284 +    char optchar = 0;
   4.285 +    for(int i=1;i<argc;i++) {
   4.286 +        char *arg = argv[i];
   4.287 +        size_t len = strlen(arg);
   4.288 +        if(len > 1 && arg[0] == '-') {
   4.289 +            for(int c=1;c<len;c++) {
   4.290 +                if(option) {
   4.291 +                    fprintf(stderr,
   4.292 +                            "Missing argument for option -%c\n", optchar);
   4.293 +                    return 1;
   4.294 +                }
   4.295 +                switch(arg[c]) {
   4.296 +                    default: {
   4.297 +                        fprintf(stderr, "Unknown option -%c\n\n", arg[c]);
   4.298 +                        return 1;
   4.299 +                    }
   4.300 +                    case 'v': {
   4.301 +                        ucx_map_cstr_put(options, "verbose", NOARG);
   4.302 +                        break;
   4.303 +                    }
   4.304 +                    case 'o': {
   4.305 +                        option = "output";
   4.306 +                        optchar = 'o';
   4.307 +                        break;
   4.308 +                    }
   4.309 +                }
   4.310 +            }
   4.311 +        } else if(option) {
   4.312 +            ucx_map_cstr_put(options, option, arg);
   4.313 +            option = NULL;
   4.314 +        } else {
   4.315 +            /* ... handle argument that is not an option ... */
   4.316 +        }
   4.317 +    }
   4.318 +    if(option) {
   4.319 +        fprintf(stderr,
   4.320 +                "Missing argument for option -%c\n", optchar);
   4.321 +        return 1;
   4.322 +    }
   4.323 +```
   4.324 +With the following loop, you can access the previously recorded options:
   4.325 +```C
   4.326 +    UcxMapIterator iter = ucx_map_iterator(options);
   4.327 +    char *arg;
   4.328 +    UCX_MAP_FOREACH(optkey, arg, iter) {
   4.329 +        char* opt = optkey.data;
   4.330 +        if (*arg) {
   4.331 +            printf("%s = %s\n", opt, arg);
   4.332 +        } else {
   4.333 +            printf("%s active\n", opt);
   4.334 +        }
   4.335 +    }
   4.336 +```
   4.337 +Don't forget to call `ucx_map_free()`, when you are done with the map.
   4.338  
   4.339  ## Memory Pool
   4.340  
   4.341 @@ -118,10 +352,109 @@
   4.342  This pool allows you to register destructor functions for the allocated memory,
   4.343  which are automatically called on the destruction of the pool.
   4.344  But you may also register *independent* destructor functions within a pool in
   4.345 -case, some external library allocated memory for you, which you wish to be
   4.346 +case some external library allocated memory for you, which should be
   4.347  destroyed together with this pool.
   4.348  
   4.349 -<a name="properties"></a>
   4.350 +Many UCX modules support the use of an allocator.
   4.351 +The [String Module](#string), for instance, provides the `sstrdup_a()` function,
   4.352 +which uses the specified allocator to allocate the memory for the duplicated
   4.353 +string.
   4.354 +This way, you can use a `UcxMempool` to keep track of the memory occupied by
   4.355 +duplicated strings and cleanup everything with just a single call to
   4.356 +`ucx_mempool_destroy()`.
   4.357 +
   4.358 +### Read CSV data into a structure
   4.359 +
   4.360 +The following code example shows some of the basic memory pool functions and
   4.361 +how they can be used with other UCX modules.
   4.362 +```C
   4.363 +#include <stdio.h>
   4.364 +#include <ucx/mempool.h>
   4.365 +#include <ucx/list.h>
   4.366 +#include <ucx/string.h>
   4.367 +#include <ucx/buffer.h>
   4.368 +#include <ucx/utils.h>
   4.369 +
   4.370 +typedef struct {
   4.371 +    sstr_t column_a;
   4.372 +    sstr_t column_b;
   4.373 +    sstr_t column_c;
   4.374 +} CSVData;
   4.375 +
   4.376 +int main(int argc, char** argv) {
   4.377 +
   4.378 +    UcxMempool* pool = ucx_mempool_new(128);
   4.379 +
   4.380 +    FILE *f = fopen("test.csv", "r");
   4.381 +    if (!f) {
   4.382 +        perror("Cannot open file");
   4.383 +        return 1;
   4.384 +    }
   4.385 +    /* close the file automatically at pool destruction*/
   4.386 +    ucx_mempool_reg_destr(pool, f, (ucx_destructor) fclose);
   4.387 +
   4.388 +    /* create a buffer and register it at the memory pool for destruction */
   4.389 +    UcxBuffer* content = ucx_buffer_new(NULL, 256, UCX_BUFFER_AUTOEXTEND);
   4.390 +    ucx_mempool_reg_destr(pool, content, (ucx_destructor) ucx_buffer_free);
   4.391 +
   4.392 +    /* read the file and split it by lines first */
   4.393 +    ucx_stream_copy(f, content, fread, ucx_buffer_write);
   4.394 +    sstr_t contentstr = ucx_buffer_to_sstr(content);
   4.395 +    ssize_t lc = 0;
   4.396 +    sstr_t* lines = sstrsplit_a(pool->allocator, contentstr, S("\n"), &lc);
   4.397 +
   4.398 +    /* skip the header and parse the remaining data */
   4.399 +    UcxList* datalist = NULL;
   4.400 +    for (size_t i = 1 ; i < lc ; i++) {
   4.401 +        if (lines[i].length == 0) continue;
   4.402 +        ssize_t fc = 3;
   4.403 +        sstr_t* fields = sstrsplit_a(pool->allocator, lines[i], S(";"), &fc);
   4.404 +        if (fc != 3) {
   4.405 +            fprintf(stderr, "Syntax error in line %zu.\n", i);
   4.406 +            ucx_mempool_destroy(pool);
   4.407 +            return 1;
   4.408 +        }
   4.409 +        CSVData* data = ucx_mempool_malloc(pool, sizeof(CSVData));
   4.410 +        data->column_a = fields[0];
   4.411 +        data->column_b = fields[1];
   4.412 +        data->column_c = fields[2];
   4.413 +        datalist = ucx_list_append_a(pool->allocator, datalist, data);
   4.414 +    }
   4.415 +
   4.416 +    /* control output */
   4.417 +    UCX_FOREACH(elem, datalist) {
   4.418 +        CSVData* data = elem->data;
   4.419 +        printf("Column A: %" PRIsstr " | "
   4.420 +               "Column B: %" PRIsstr " | "
   4.421 +               "Column C: %" PRIsstr "\n",
   4.422 +               SFMT(data->column_a), SFMT(data->column_b), SFMT(data->column_c)
   4.423 +        );
   4.424 +    }
   4.425 +
   4.426 +    /* cleanup everything, no manual free() needed */
   4.427 +    ucx_mempool_destroy(pool);
   4.428 +
   4.429 +    return 0;
   4.430 +} 
   4.431 +```
   4.432 +
   4.433 +### Overriding the default destructor
   4.434 +
   4.435 +Sometimes you need to allocate memory with `ucx_mempool_malloc()`, but the
   4.436 +memory is not supposed to be freed with a simple call to `free()`.
   4.437 +In this case, you can overwrite the default destructor as follows:
   4.438 +```C
   4.439 +    MyObject* obj = ucx_mempool_malloc(pool, sizeof(MyObject));
   4.440 +
   4.441 +    /* some special initialization with own resource management */
   4.442 +    my_object_init(obj);
   4.443 +
   4.444 +    /* register destructor function */
   4.445 +    ucx_mempool_set_destr(obj, (ucx_destructor) my_object_destroy);
   4.446 +```
   4.447 +Be aware, that your destructor function should not free any memory, that is
   4.448 +also managed by the pool.
   4.449 +Otherwise you might be risking a double-free.
   4.450  
   4.451  ## Properties
   4.452  
   4.453 @@ -131,7 +464,37 @@
   4.454  This module provides load and store function for `*.properties` files.
   4.455  The key/value pairs are stored within an UCX Map.
   4.456  
   4.457 -<a name="stack"></a>
   4.458 +### Example: Loading properties from a file
   4.459 +
   4.460 +```C
   4.461 +/* Open the file as usual */
   4.462 +FILE* file = fopen("myprops.properties", "r");
   4.463 +if (!file) {
   4.464 +    // error handling
   4.465 +    return 1;
   4.466 +}
   4.467 +
   4.468 +/* Load the properties from the file */
   4.469 +UcxMap* myprops = ucx_map_new(16);
   4.470 +if (ucx_properties_load(myprops, file)) {
   4.471 +    /* ... error handling ... */
   4.472 +    fclose(file);
   4.473 +    ucx_map_free(myprops);
   4.474 +    return 1;
   4.475 +}
   4.476 +
   4.477 +/* Print out the key/value pairs */
   4.478 +char* propval;
   4.479 +UcxMapIterator propiter = ucx_map_iterator(myprops);
   4.480 +UCX_MAP_FOREACH(key, propval, propiter) {
   4.481 +    printf("%s = %s\n", (char*)key.data, propval);
   4.482 +}
   4.483 +
   4.484 +/* Don't forget to free the values before freeing the map */
   4.485 +ucx_map_free_content(myprops, NULL);
   4.486 +ucx_map_free(myprops);
   4.487 +fclose(file);
   4.488 +```
   4.489  
   4.490  ## Stack
   4.491  
   4.492 @@ -141,13 +504,54 @@
   4.493  This concrete implementation of an UCX Allocator allows you to grab some amount
   4.494  of memory which is then handled as a stack.
   4.495  Please note, that the term *stack* only refers to the behavior of this
   4.496 -allocator. You may still choose if you want to use stack or heap memory
   4.497 +allocator. You may still choose to use either stack or heap memory
   4.498  for the underlying space.
   4.499 -
   4.500  A typical use case is an algorithm where you need to allocate and free large
   4.501  amounts of memory very frequently.
   4.502  
   4.503 -<a name="string"></a>
   4.504 +The following code sample shows how to initialize a stack and push and pop
   4.505 +simple data.
   4.506 +```C
   4.507 +    const size_t len = 1024;
   4.508 +    char space[len];
   4.509 +    UcxStack stack;
   4.510 +    ucx_stack_init(&stack, space, len);
   4.511 +
   4.512 +    int i = 42;
   4.513 +    float f = 3.14f;
   4.514 +    const char* str = "Hello!";
   4.515 +    size_t strn = 7;
   4.516 +
   4.517 +    /* push the integer */
   4.518 +    ucx_stack_push(&stack, sizeof(int), &i);
   4.519 +
   4.520 +    /* push the float and rember the address */
   4.521 +    float* remember = ucx_stack_push(&stack, sizeof(float), &f);
   4.522 +
   4.523 +    /* push the string with zero terminator */
   4.524 +    ucx_stack_push(&stack, strn, str);
   4.525 +
   4.526 +    /* if we forget, how big an element was, we can ask the stack */
   4.527 +    printf("Length of string: %zu\n", ucx_stack_topsize(&stack)-1);
   4.528 +
   4.529 +    /* retrieve the string as sstr_t, without zero terminator! */
   4.530 +    sstr_t s;
   4.531 +    s.length = ucx_stack_topsize(&stack)-1;
   4.532 +    s.ptr = malloc(s.length);
   4.533 +    ucx_stack_popn(&stack, s.ptr, s.length);
   4.534 +    printf("%" PRIsstr "\n", SFMT(s));
   4.535 +
   4.536 +    /* print the float directly from the stack and free it */
   4.537 +    printf("Float: %f\n", *remember);
   4.538 +    ucx_stack_free(&stack, remember);
   4.539 +
   4.540 +    /* the last element is the integer */
   4.541 +    int j;
   4.542 +    ucx_stack_pop(&stack, &j);
   4.543 +    printf("Integer: %d\n", j);
   4.544 +```
   4.545 +
   4.546 +
   4.547  
   4.548  ## String
   4.549  
   4.550 @@ -232,8 +636,6 @@
   4.551  
   4.552  The memory pool ensures, that all strings are freed.
   4.553  
   4.554 -<a name="test"></a>
   4.555 -
   4.556  ## Testing
   4.557  
   4.558  *Header file:* [test.h](api/test_8h.html)  
   4.559 @@ -244,7 +646,50 @@
   4.560  To avoid code duplication within tests, we also provide the possibility to
   4.561  define test subroutines.
   4.562  
   4.563 -<a name="utils"></a>
   4.564 +You should declare test cases and subroutines in a header file per test unit
   4.565 +and implement them as you would implement normal functions.
   4.566 +```C
   4.567 +    /* myunit.h */
   4.568 +    UCX_TEST(function_name);
   4.569 +    UCX_TEST_SUBROUTINE(subroutine_name, paramlist); /* optional */
   4.570 +
   4.571 +
   4.572 +    /* myunit.c */
   4.573 +    UCX_TEST_SUBROUTINE(subroutine_name, paramlist) {
   4.574 +        /* ... reusable tests with UCX_TEST_ASSERT() ... */
   4.575 +    }
   4.576 +
   4.577 +    UCX_TEST(function_name) {
   4.578 +        /* ... resource allocation and other test preparation ... */
   4.579 +
   4.580 +        /* mandatory marker for the start of the tests */
   4.581 +        UCX_TEST_BEGIN
   4.582 +
   4.583 +        /*  ... verifications with UCX_TEST_ASSERT() ...
   4.584 +         * (and/or calls with UCX_TEST_CALL_SUBROUTINE())
   4.585 +         */
   4.586 +
   4.587 +        /* mandatory marker for the end of the tests */
   4.588 +        UCX_TEST_END
   4.589 +
   4.590 +        /* ... resource cleanup ...
   4.591 +         * (all code after UCX_TEST_END is always executed)
   4.592 +         */
   4.593 +    }
   4.594 +```
   4.595 +If you want to use the `UCX_TEST_ASSERT()` macro in a function, you are
   4.596 +*required* to use a `UCX_TEST_SUBROUTINE`.
   4.597 +Otherwise the testing framework does not know where to jump, when the assertion
   4.598 +fails.
   4.599 +
   4.600 +After implementing the tests, you can easily build a test suite and execute it:
   4.601 +```C
   4.602 +    UcxTestSuite* suite = ucx_test_suite_new();
   4.603 +    ucx_test_register(suite, testMyTestCase01);
   4.604 +    ucx_test_register(suite, testMyTestCase02);
   4.605 +    /* ... */
   4.606 +    ucx_test_run(suite, stdout); /* stdout, or any other FILE stream */
   4.607 +```
   4.608  
   4.609  ## Utilities
   4.610  
   4.611 @@ -256,3 +701,60 @@
   4.612  We also provide several `printf` variants to conveniently print formatted data
   4.613  to streams or strings.
   4.614  
   4.615 +### A simple copy program
   4.616 +
   4.617 +The utilities package provides several stream copy functions.
   4.618 +One of them has a very simple interface and can, for instance, be used to copy
   4.619 +whole files in a single call.
   4.620 +This is a minimal working example:
   4.621 +```C
   4.622 +#include <stdio.h>
   4.623 +#include <ucx/utils.h>
   4.624 +
   4.625 +int main(int argc, char** argv) {
   4.626 +
   4.627 +    if (argc != 3) {
   4.628 +        fprintf(stderr, "Use %s <src> <dest>", argv[0]);
   4.629 +        return 1;
   4.630 +    }
   4.631 +
   4.632 +    FILE *srcf = fopen(argv[1], "r");   /* insert error handling on your own */
   4.633 +    FILE *destf = fopen(argv[2], "w");
   4.634 +    
   4.635 +    size_t n =  ucx_stream_copy(srcf, destf, fread, fwrite);
   4.636 +    printf("%zu bytes copied.\n", n);
   4.637 +
   4.638 +    fclose(srcf);
   4.639 +    fclose(destf);
   4.640 +
   4.641 +
   4.642 +    return 0;
   4.643 +}
   4.644 +```
   4.645 +
   4.646 +### Automatic allocation for formatted strings
   4.647 +
   4.648 +The UCX utility function `ucx_asprintf()` and it's convenient shortcut
   4.649 +`ucx_sprintf` allow easy formatting of strings, without ever having to worry
   4.650 +about the required space.
   4.651 +```C
   4.652 +sstr_t mystring = ucx_sprintf("The answer is: %d!", 42);
   4.653 +```
   4.654 +Still, you have to pass `mystring.ptr` to `free()` (or the free function of
   4.655 +your allocator, if you use `ucx_asprintf`).
   4.656 +If you don't have all the information ready to build your string, you can even
   4.657 +use a [UcxBuffer](#buffer) as a target with the utility function
   4.658 +`ucx_bprintf()`.
   4.659 +```C
   4.660 +UcxBuffer* strbuffer = ucx_buffer_new(NULL, 512, UCX_BUFFER_AUTOEXTEND);
   4.661 +
   4.662 +for (unsigned int i = 2 ; i < 100 ; i++) {
   4.663 +        ucx_bprintf(strbuffer, "Integer %d is %s\n",
   4.664 +                        i, prime(i) ? "prime" : "not prime");
   4.665 +}
   4.666 +
   4.667 +/* print the result to stdout */
   4.668 +printf("%s", (char*)strbuffer->space);
   4.669 +
   4.670 +ucx_buffer_free(strbuffer);
   4.671 +```
     5.1 --- a/src/Makefile.am	Sun May 13 07:13:09 2018 +0200
     5.2 +++ b/src/Makefile.am	Mon May 14 17:56:03 2018 +0200
     5.3 @@ -27,7 +27,7 @@
     5.4  #
     5.5  
     5.6  lib_LTLIBRARIES = libucx.la
     5.7 -libucx_la_LDFLAGS = -version-info 1:0:0
     5.8 +libucx_la_LDFLAGS = -version-info $(UCX_LIB_VERSION)
     5.9  libucx_la_SOURCES = utils.c
    5.10  libucx_la_SOURCES += list.c
    5.11  libucx_la_SOURCES += map.c
     6.1 --- a/src/avl.c	Sun May 13 07:13:09 2018 +0200
     6.2 +++ b/src/avl.c	Mon May 14 17:56:03 2018 +0200
     6.3 @@ -136,6 +136,23 @@
     6.4      alfree(al, tree);
     6.5  }
     6.6  
     6.7 +static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node,
     6.8 +        ucx_destructor destr) {
     6.9 +    if (node) {
    6.10 +        ucx_avl_free_content_node(al, node->left, destr);
    6.11 +        ucx_avl_free_content_node(al, node->right, destr);
    6.12 +        if (destr) {
    6.13 +            destr(node->value);
    6.14 +        } else {
    6.15 +            alfree(al, node->value);
    6.16 +        }
    6.17 +    }
    6.18 +}
    6.19 +
    6.20 +void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) {
    6.21 +    ucx_avl_free_content_node(tree->allocator, tree->root, destr);
    6.22 +}
    6.23 +
    6.24  UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) {
    6.25      UcxAVLNode *n = tree->root;
    6.26      int cmpresult;
     7.1 --- a/src/buffer.c	Sun May 13 07:13:09 2018 +0200
     7.2 +++ b/src/buffer.c	Mon May 14 17:56:03 2018 +0200
     7.3 @@ -237,6 +237,61 @@
     7.4      }
     7.5  }
     7.6  
     7.7 -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str) {
     7.8 +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str) {
     7.9      return ucx_buffer_write((const void*)str, 1, strlen(str), buffer);
    7.10  }
    7.11 +
    7.12 +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift) {
    7.13 +    if (shift >= buffer->size) {
    7.14 +        buffer->pos = buffer->size = 0;
    7.15 +    } else {
    7.16 +        memmove(buffer->space, buffer->space + shift, buffer->size - shift);
    7.17 +        buffer->size -= shift;
    7.18 +        
    7.19 +        if (buffer->pos >= shift) {
    7.20 +            buffer->pos -= shift;
    7.21 +        } else {
    7.22 +            buffer->pos = 0;
    7.23 +        }
    7.24 +    }
    7.25 +    return 0;
    7.26 +}
    7.27 +
    7.28 +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift) {
    7.29 +    size_t req_capacity = buffer->size + shift;
    7.30 +    size_t movebytes;
    7.31 +    
    7.32 +    // auto extend buffer, if required and enabled
    7.33 +    if (buffer->capacity < req_capacity) {
    7.34 +        if ((buffer->flags & UCX_BUFFER_AUTOEXTEND) == UCX_BUFFER_AUTOEXTEND) {
    7.35 +            if (ucx_buffer_extend(buffer, req_capacity - buffer->capacity)) {
    7.36 +                return 1;
    7.37 +            }
    7.38 +            movebytes = buffer->size;
    7.39 +        } else {
    7.40 +            movebytes = buffer->capacity - shift;
    7.41 +        }
    7.42 +    } else {
    7.43 +        movebytes = buffer->size;
    7.44 +    }
    7.45 +    
    7.46 +    memmove(buffer->space + shift, buffer->space, movebytes);
    7.47 +    buffer->size = shift+movebytes;
    7.48 +    
    7.49 +    buffer->pos += shift;
    7.50 +    if (buffer->pos > buffer->size) {
    7.51 +        buffer->pos = buffer->size;
    7.52 +    }
    7.53 +    
    7.54 +    return 0;
    7.55 +}
    7.56 +
    7.57 +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift) {
    7.58 +    if (shift < 0) {
    7.59 +        return ucx_buffer_shift_left(buffer, (size_t) (-shift));
    7.60 +    } else if (shift > 0) {
    7.61 +        return ucx_buffer_shift_right(buffer, (size_t) shift);
    7.62 +    } else {
    7.63 +        return 0;
    7.64 +    }
    7.65 +}
     8.1 --- a/src/list.c	Sun May 13 07:13:09 2018 +0200
     8.2 +++ b/src/list.c	Mon May 14 17:56:03 2018 +0200
     8.3 @@ -77,6 +77,7 @@
     8.4  }
     8.5  
     8.6  void ucx_list_free_content(UcxList* list, ucx_destructor destr) {
     8.7 +    if (!destr) destr = free;
     8.8      while (list != NULL) {
     8.9          destr(list->data);
    8.10          list = list->next;
    8.11 @@ -141,6 +142,39 @@
    8.12      }
    8.13  }
    8.14  
    8.15 +UcxList *ucx_list_prepend_once(UcxList *l, void *data,
    8.16 +        cmp_func cmpfnc, void *cmpdata) {
    8.17 +    return ucx_list_prepend_once_a(ucx_default_allocator(), l,
    8.18 +            data, cmpfnc, cmpdata);
    8.19 +}
    8.20 +
    8.21 +UcxList *ucx_list_prepend_once_a(UcxAllocator *alloc, UcxList *l, void *data,
    8.22 +        cmp_func cmpfnc, void *cmpdata) {
    8.23 +
    8.24 +    UcxList* first = ucx_list_first(l);
    8.25 +    {
    8.26 +        UcxList *e = first;
    8.27 +        while (e) {
    8.28 +            if (cmpfnc(e->data, data, cmpdata) == 0) {
    8.29 +                return l;
    8.30 +            }
    8.31 +            e = e->next;
    8.32 +        }
    8.33 +    }
    8.34 +    
    8.35 +    UcxList *nl = ucx_list_append_a(alloc, NULL, data);
    8.36 +    if (!nl) {
    8.37 +        return NULL;
    8.38 +    }
    8.39 +
    8.40 +    if (first) {
    8.41 +        nl->next = first;
    8.42 +        first->prev = nl;
    8.43 +    }
    8.44 +    
    8.45 +    return nl;
    8.46 +}
    8.47 +
    8.48  UcxList *ucx_list_prepend(UcxList *l, void *data) {
    8.49      return ucx_list_prepend_a(ucx_default_allocator(), l, data);
    8.50  }
     9.1 --- a/src/logging.c	Sun May 13 07:13:09 2018 +0200
     9.2 +++ b/src/logging.c	Mon May 14 17:56:03 2018 +0200
     9.3 @@ -50,6 +50,8 @@
     9.4          ucx_map_int_put(logger->levels, l, (void*) "[WARNING]");
     9.5          l = UCX_LOGGER_INFO;
     9.6          ucx_map_int_put(logger->levels, l, (void*) "[INFO]");
     9.7 +        l = UCX_LOGGER_DEBUG;
     9.8 +        ucx_map_int_put(logger->levels, l, (void*) "[DEBUG]");
     9.9          l = UCX_LOGGER_TRACE;
    9.10          ucx_map_int_put(logger->levels, l, (void*) "[TRACE]");
    9.11      }
    9.12 @@ -75,6 +77,9 @@
    9.13          
    9.14          if ((logger->mask & UCX_LOGGER_LEVEL) > 0) {
    9.15              text = (char*) ucx_map_int_get(logger->levels, level);
    9.16 +            if (!text) {
    9.17 +                text = "[UNKNOWN]";
    9.18 +            }
    9.19              n = strlen(text);
    9.20              n = n > 256 ? 256 : n;
    9.21              memcpy(msg+k, text, n);
    10.1 --- a/src/map.c	Sun May 13 07:13:09 2018 +0200
    10.2 +++ b/src/map.c	Mon May 14 17:56:03 2018 +0200
    10.3 @@ -86,7 +86,11 @@
    10.4      UcxMapIterator iter = ucx_map_iterator(map);
    10.5      void *val;
    10.6      UCX_MAP_FOREACH(key, val, iter) {
    10.7 -        destr(val);
    10.8 +        if (destr) {
    10.9 +            destr(val);
   10.10 +        } else {
   10.11 +            alfree(map->allocator, val);
   10.12 +        }
   10.13      }
   10.14  }
   10.15  
    11.1 --- a/src/stack.c	Sun May 13 07:13:09 2018 +0200
    11.2 +++ b/src/stack.c	Mon May 14 17:56:03 2018 +0200
    11.3 @@ -120,13 +120,15 @@
    11.4          return;
    11.5      }
    11.6      
    11.7 -    size_t len = ucx_stack_topsize(stack);
    11.8 -    if (len > n) {
    11.9 -        len = n;
   11.10 +    if (dest) {
   11.11 +        size_t len = ucx_stack_topsize(stack);
   11.12 +        if (len > n) {
   11.13 +            len = n;
   11.14 +        }
   11.15 +
   11.16 +        memcpy(dest, stack->top, len);
   11.17      }
   11.18      
   11.19 -    memcpy(dest, stack->top, len);
   11.20 -    
   11.21      ucx_stack_free(stack, stack->top);
   11.22  }
   11.23  
   11.24 @@ -142,3 +144,22 @@
   11.25          return 0;
   11.26      }
   11.27  }
   11.28 +
   11.29 +void *ucx_stack_push(UcxStack *stack, size_t n, const void *data) {
   11.30 +    void *space = ucx_stack_malloc(stack, n);
   11.31 +    if (space) {
   11.32 +        memcpy(space, data, n);
   11.33 +    }
   11.34 +    return space;
   11.35 +}
   11.36 +
   11.37 +void *ucx_stack_pusharr(UcxStack *stack,
   11.38 +        size_t nelem, size_t elsize, const void *data) {
   11.39 +    
   11.40 +    // skip the memset by using malloc
   11.41 +    void *space = ucx_stack_malloc(stack, nelem*elsize);
   11.42 +    if (space) {
   11.43 +        memcpy(space, data, nelem*elsize);
   11.44 +    }
   11.45 +    return space;
   11.46 +}
    12.1 --- a/src/string.c	Sun May 13 07:13:09 2018 +0200
    12.2 +++ b/src/string.c	Mon May 14 17:56:03 2018 +0200
    12.3 @@ -227,9 +227,9 @@
    12.4  
    12.5  int ucx_strrchr(const char *string, size_t length, int chr, size_t *pos) {
    12.6      if(length > 0) {
    12.7 -        for(size_t i=length-1;i>=0;i--) {
    12.8 -            if(string[i] == chr) {
    12.9 -                *pos = i;
   12.10 +        for(size_t i=length ; i>0 ; i--) {
   12.11 +            if(string[i-1] == chr) {
   12.12 +                *pos = i-1;
   12.13                  return 1;
   12.14              }
   12.15          }
    13.1 --- a/src/ucx/avl.h	Sun May 13 07:13:09 2018 +0200
    13.2 +++ b/src/ucx/avl.h	Mon May 14 17:56:03 2018 +0200
    13.3 @@ -135,11 +135,36 @@
    13.4  
    13.5  /**
    13.6   * Destroys a UcxAVLTree.
    13.7 + * 
    13.8 + * Note, that the contents are not automatically freed.
    13.9 + * Use may use #ucx_avl_free_content() before calling this function.
   13.10 + * 
   13.11   * @param tree the tree to destroy
   13.12 + * @see ucx_avl_free_content()
   13.13   */
   13.14  void ucx_avl_free(UcxAVLTree *tree);
   13.15  
   13.16  /**
   13.17 + * Frees the contents of a UcxAVLTree.
   13.18 + * 
   13.19 + * This is a convenience function that iterates over the tree and passes all
   13.20 + * values to the specified destructor function.
   13.21 + * 
   13.22 + * If no destructor is specified (<code>NULL</code>), the free() function of
   13.23 + * the tree's own allocator is used.
   13.24 + * 
   13.25 + * You must ensure, that it is valid to pass each value in the map to the same
   13.26 + * destructor function.
   13.27 + * 
   13.28 + * You should free the entire tree afterwards, as the contents will be invalid.
   13.29 + * 
   13.30 + * @param tree for which the contents shall be freed
   13.31 + * @param destr optional pointer to a destructor function
   13.32 + * @see ucx_avl_free()
   13.33 + */
   13.34 +void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr);
   13.35 +
   13.36 +/**
   13.37   * Macro for initializing a new UcxAVLTree with the default allocator and a
   13.38   * ucx_ptrcmp() compare function.
   13.39   * 
    14.1 --- a/src/ucx/buffer.h	Sun May 13 07:13:09 2018 +0200
    14.2 +++ b/src/ucx/buffer.h	Mon May 14 17:56:03 2018 +0200
    14.3 @@ -137,6 +137,67 @@
    14.4  #define ucx_buffer_clone(src,flags) \
    14.5      ucx_buffer_extract(src, 0, (src)->capacity, flags)
    14.6  
    14.7 +
    14.8 +/**
    14.9 + * Shifts the contents of the buffer by the given offset.
   14.10 + * 
   14.11 + * If the offset is positive, the contents are shifted to the right.
   14.12 + * If auto extension is enabled, the buffer grows, if necessary.
   14.13 + * In case the auto extension fails, this function returns a non-zero value and
   14.14 + * no contents are changed.
   14.15 + * If auto extension is disabled, the contents that do not fit into the buffer
   14.16 + * are discarded.
   14.17 + * 
   14.18 + * If the offset is negative, the contents are shifted to the left where the
   14.19 + * first <code>shift</code> bytes are discarded.
   14.20 + * The new size of the buffer is the old size minus
   14.21 + * the absolute shift value.
   14.22 + * If this value is larger than the buffer size, the buffer is emptied (but
   14.23 + * not cleared, see the security note below).
   14.24 + * 
   14.25 + * The buffer position gets shifted alongside with the content but is kept
   14.26 + * within the boundaries of the buffer.
   14.27 + * 
   14.28 + * <b>Security note:</b> the shifting operation does <em>not</em> erase the
   14.29 + * previously occupied memory cells. You can easily do that manually, e.g. by
   14.30 + * calling <code>memset(buffer->space, 0, shift)</code> for a right shift or
   14.31 + * <code>memset(buffer->size, 0, buffer->capacity-buffer->size)</code>
   14.32 + * for a left shift.
   14.33 + * 
   14.34 + * @param buffer the buffer
   14.35 + * @param shift the shift offset (negative means left shift)
   14.36 + * @return 0 on success, non-zero if a required auto-extension fails
   14.37 + */
   14.38 +int ucx_buffer_shift(UcxBuffer* buffer, off_t shift);
   14.39 +
   14.40 +/**
   14.41 + * Shifts the buffer to the right.
   14.42 + * See ucx_buffer_shift() for details.
   14.43 + * 
   14.44 + * @param buffer the buffer
   14.45 + * @param shift the shift offset
   14.46 + * @return 0 on success, non-zero if a required auto-extension fails
   14.47 + * @see ucx_buffer_shift()
   14.48 + */
   14.49 +int ucx_buffer_shift_right(UcxBuffer* buffer, size_t shift);
   14.50 +
   14.51 +/**
   14.52 + * Shifts the buffer to the left.
   14.53 + * 
   14.54 + * See ucx_buffer_shift() for details. Note, however, that this method expects
   14.55 + * a positive shift offset.
   14.56 + * 
   14.57 + * Since a left shift cannot fail due to memory allocation problems, this
   14.58 + * function always returns zero.
   14.59 + * 
   14.60 + * @param buffer the buffer
   14.61 + * @param shift the shift offset
   14.62 + * @return always zero
   14.63 + * @see ucx_buffer_shift()
   14.64 + */
   14.65 +int ucx_buffer_shift_left(UcxBuffer* buffer, size_t shift);
   14.66 +
   14.67 +
   14.68  /**
   14.69   * Moves the position of the buffer.
   14.70   * 
   14.71 @@ -165,8 +226,8 @@
   14.72   * 
   14.73   * @param buffer the buffer to be cleared
   14.74   */
   14.75 -#define ucx_buffer_clear(buffer) memset(buffer->space, 0, buffer->size); \
   14.76 -        buffer->size = 0; buffer->pos = 0;
   14.77 +#define ucx_buffer_clear(buffer) memset((buffer)->space, 0, (buffer)->size); \
   14.78 +        (buffer)->size = 0; (buffer)->pos = 0;
   14.79  
   14.80  /**
   14.81   * Tests, if the buffer position has exceeded the buffer capacity.
   14.82 @@ -260,7 +321,15 @@
   14.83   * @param str the string
   14.84   * @return the number of bytes written
   14.85   */
   14.86 -size_t ucx_buffer_puts(UcxBuffer *buffer, char *str);
   14.87 +size_t ucx_buffer_puts(UcxBuffer *buffer, const char *str);
   14.88 +
   14.89 +/**
   14.90 + * Returns the complete buffer content as sstr_t.
   14.91 + * @param buffer the buffer
   14.92 + * @return the result of <code>sstrn()</code> with the buffer space and size
   14.93 + * as arguments
   14.94 + */
   14.95 +#define ucx_buffer_to_sstr(buffer) sstrn((buffer)->space, (buffer)->size)
   14.96  
   14.97  #ifdef	__cplusplus
   14.98  }
    15.1 --- a/src/ucx/list.h	Sun May 13 07:13:09 2018 +0200
    15.2 +++ b/src/ucx/list.h	Mon May 14 17:56:03 2018 +0200
    15.3 @@ -173,9 +173,11 @@
    15.4   * 
    15.5   * Note, that the contents are not usable afterwards and the list should be
    15.6   * destroyed with ucx_list_free().
    15.7 + *
    15.8 + * If no destructor is specified (<code>NULL</code>), stdlib's free() is used.
    15.9   * 
   15.10   * @param list the list for which the contents shall be freed
   15.11 - * @param destr the destructor function (e.g. stdlib free())
   15.12 + * @param destr optional destructor function
   15.13   * @see ucx_list_free()
   15.14   */
   15.15  void ucx_list_free_content(UcxList* list, ucx_destructor destr);
   15.16 @@ -213,6 +215,12 @@
   15.17  /**
   15.18   * Inserts an element at the end of the list, if it is not present in the list.
   15.19   * 
   15.20 + * <b>Note:</b> You should not try to store a freshly allocated object. Since
   15.21 + * it might be a duplicate, the memory allocated for that copy would be leaking
   15.22 + * afterwards.
   15.23 + * 
   15.24 + * <b>Deprecation notice:</b> This function is considered to do more harm than
   15.25 + * it adds usefulness and is going to be removed in a future UCX release.
   15.26   * 
   15.27   * @param list the list where to append the data, or <code>NULL</code> to
   15.28   * create a new list
   15.29 @@ -230,7 +238,12 @@
   15.30   * Inserts an element at the end of the list, if it is not present in the list,
   15.31   * using a UcxAllocator.
   15.32   * 
   15.33 - * See ucx_list_append() for details.
   15.34 + * <b>Note:</b> You should not try to store a freshly allocated object. Since
   15.35 + * it might be a duplicate, the memory allocated for that copy would be leaking
   15.36 + * afterwards.
   15.37 + * 
   15.38 + * <b>Deprecation notice:</b> This function is considered to do more harm than
   15.39 + * it adds usefulness and is going to be removed in a future UCX release.
   15.40   * 
   15.41   * @param allocator the allocator to use
   15.42   * @param list the list where to append the data, or <code>NULL</code> to
   15.43 @@ -246,6 +259,51 @@
   15.44          UcxList *list, void *data, cmp_func cmpfnc, void *cmpdata);
   15.45  
   15.46  /**
   15.47 + * Inserts an element at the beginning of the list, if it is not present
   15.48 + * in the list.
   15.49 + * 
   15.50 + * <b>Note:</b> You should not try to store a freshly allocated object. Since
   15.51 + * it might be a duplicate, the memory allocated for that copy would be leaking
   15.52 + * afterwards.
   15.53 + * 
   15.54 + * <b>Deprecation notice:</b> This function is considered to do more harm than
   15.55 + * it adds usefulness and is going to be removed in a future UCX release.
   15.56 + * 
   15.57 + * @param list the list where to prepend the data, or <code>NULL</code> to
   15.58 + * create a new list
   15.59 + * @param data the data to insert
   15.60 + * @param cmpfnc the compare function
   15.61 + * @param cmpdata additional data for the compare function
   15.62 + * @return a pointer to the new list head
   15.63 + * @see ucx_list_prepend()
   15.64 + */
   15.65 +UcxList *ucx_list_prepend_once(UcxList *list, void *data,
   15.66 +        cmp_func cmpfnc, void *cmpdata);
   15.67 +
   15.68 +/**
   15.69 + * Inserts an element at the beginning of the list, if it is not present in
   15.70 + * the list, using a UcxAllocator.
   15.71 + * 
   15.72 + * <b>Note:</b> You should not try to store a freshly allocated object. Since
   15.73 + * it might be a duplicate, the memory allocated for that copy would be leaking
   15.74 + * afterwards.
   15.75 + * 
   15.76 + * <b>Deprecation notice:</b> This function is considered to do more harm than
   15.77 + * it adds usefulness and is going to be removed in a future UCX release.
   15.78 + * 
   15.79 + * @param allocator the allocator to use
   15.80 + * @param list the list where to prepend the data, or <code>NULL</code> to
   15.81 + * create a new list
   15.82 + * @param data the data to insert
   15.83 + * @param cmpfnc the compare function
   15.84 + * @param cmpdata additional data for the compare function
   15.85 + * @return a pointer to the new list head
   15.86 + * @see ucx_list_prepend_a()
   15.87 + */
   15.88 +UcxList *ucx_list_prepend_once_a(UcxAllocator *allocator,
   15.89 +        UcxList *list, void *data, cmp_func cmpfnc, void *cmpdata);
   15.90 +
   15.91 +/**
   15.92   * Inserts an element at the beginning of the list.
   15.93   * 
   15.94   * You <i>should</i> overwrite the old list pointer by calling
    16.1 --- a/src/ucx/logging.h	Sun May 13 07:13:09 2018 +0200
    16.2 +++ b/src/ucx/logging.h	Mon May 14 17:56:03 2018 +0200
    16.3 @@ -177,6 +177,18 @@
    16.4          const unsigned int line, const char* format, ...);
    16.5  
    16.6  /**
    16.7 + * Registers a custom log level.
    16.8 + * @param logger the logger
    16.9 + * @param level the log level as unsigned integer
   16.10 + * @param name a string literal describing the level
   16.11 + */
   16.12 +#define ucx_logger_register_level(logger, level, name) {\
   16.13 +        unsigned int l; \
   16.14 +            l = level; \
   16.15 +            ucx_map_int_put(logger->levels, l, (void*) "[" name "]"); \
   16.16 +        } while (0);
   16.17 +
   16.18 +/**
   16.19   * Logs a message at the specified level.
   16.20   * @param logger the logger to use
   16.21   * @param level the level to log the message on
    17.1 --- a/src/ucx/map.h	Sun May 13 07:13:09 2018 +0200
    17.2 +++ b/src/ucx/map.h	Mon May 14 17:56:03 2018 +0200
    17.3 @@ -158,7 +158,10 @@
    17.4   * Frees the contents of a hash map.
    17.5   * 
    17.6   * This is a convenience function that iterates over the map and passes all
    17.7 - * values to the specified destructor function (e.g. stdlib free()).
    17.8 + * values to the specified destructor function.
    17.9 + * 
   17.10 + * If no destructor is specified (<code>NULL</code>), the free() function of
   17.11 + * the map's own allocator is used.
   17.12   * 
   17.13   * You must ensure, that it is valid to pass each value in the map to the same
   17.14   * destructor function.
   17.15 @@ -166,7 +169,7 @@
   17.16   * You should free or clear the map afterwards, as the contents will be invalid.
   17.17   * 
   17.18   * @param map for which the contents shall be freed
   17.19 - * @param destr pointer to the destructor function
   17.20 + * @param destr optional pointer to a destructor function
   17.21   * @see ucx_map_free()
   17.22   * @see ucx_map_clear()
   17.23   */
    18.1 --- a/src/ucx/properties.h	Sun May 13 07:13:09 2018 +0200
    18.2 +++ b/src/ucx/properties.h	Mon May 14 17:56:03 2018 +0200
    18.3 @@ -173,12 +173,15 @@
    18.4   * Retrieves all available key/value-pairs and puts them into a UcxMap.
    18.5   * 
    18.6   * This is done by successive calls to ucx_properties_next() until no more
    18.7 - * key/value-pairs can be retrieved. 
    18.8 + * key/value-pairs can be retrieved.
    18.9 + * 
   18.10 + * The memory for the map values is allocated by the map's own allocator.
   18.11   * 
   18.12   * @param prop the UcxProperties object
   18.13   * @param map the target map
   18.14   * @return The UcxProperties.error code (i.e. 0 on success).
   18.15   * @see ucx_properties_fill()
   18.16 + * @see UcxMap.allocator
   18.17   */
   18.18  int ucx_properties2map(UcxProperties *prop, UcxMap *map);
   18.19  
    19.1 --- a/src/ucx/stack.h	Sun May 13 07:13:09 2018 +0200
    19.2 +++ b/src/ucx/stack.h	Mon May 14 17:56:03 2018 +0200
    19.3 @@ -91,19 +91,23 @@
    19.4   * 
    19.5   * @param stack a pointer to the stack
    19.6   * @param n amount of memory to allocate
    19.7 - * @return a pointer to the allocated memory
    19.8 + * @return a pointer to the allocated memory or <code>NULL</code> on stack
    19.9 + * overflow
   19.10   * @see ucx_allocator_malloc()
   19.11   */
   19.12  void *ucx_stack_malloc(UcxStack *stack, size_t n);
   19.13  
   19.14  /**
   19.15 - * Alias for #ucx_stack_malloc().
   19.16 + * Allocates memory with #ucx_stack_malloc() and copies the specified data if
   19.17 + * the allocation was successful.
   19.18 + * 
   19.19   * @param stack a pointer to the stack
   19.20   * @param n amount of memory to allocate
   19.21 + * @param data a pointer to the data to copy
   19.22   * @return a pointer to the allocated memory
   19.23   * @see ucx_stack_malloc
   19.24   */
   19.25 -#define ucx_stack_push(stack, n) ucx_stack_malloc(stack, n)
   19.26 +void *ucx_stack_push(UcxStack *stack, size_t n, const void *data);
   19.27  
   19.28  /**
   19.29   * Allocates an array of stack memory
   19.30 @@ -119,15 +123,18 @@
   19.31  void *ucx_stack_calloc(UcxStack *stack, size_t nelem, size_t elsize);
   19.32  
   19.33  /**
   19.34 - * Alias for #ucx_stack_calloc().
   19.35 + * Allocates memory with #ucx_stack_calloc() and copies the specified data if
   19.36 + * the allocation was successful.
   19.37   * 
   19.38   * @param stack a pointer to the stack
   19.39 - * @param n amount of elements to allocate
   19.40 + * @param nelem amount of elements to allocate
   19.41   * @param elsize amount of memory per element
   19.42 + * @param data a pointer to the data
   19.43   * @return a pointer to the allocated memory
   19.44   * @see ucx_stack_calloc
   19.45   */
   19.46 -#define ucx_stack_pusharr(stack,n,elsize) ucx_stack_calloc(stack,n,elssize)
   19.47 +void *ucx_stack_pusharr(UcxStack *stack,
   19.48 +        size_t nelem, size_t elsize, const void *data);
   19.49  
   19.50  /**
   19.51   * Reallocates memory on the stack.
   19.52 @@ -184,12 +191,13 @@
   19.53   * Removes the top most element from the stack and copies the content to <code>
   19.54   * dest</code>.
   19.55   * 
   19.56 - * In contrast to #ucx_stack_pop() the <code>dest</code> pointer <code>MUST
   19.57 - * NOT</code> be <code>NULL</code>.
   19.58 + * This function copies at most <code>n</code> bytes to the destination, but
   19.59 + * the element is always freed as a whole.
   19.60 + * If the element was larger than <code>n</code>, the remaining data is lost.
   19.61   * 
   19.62   * @param stack a pointer to the stack
   19.63   * @param dest the location where the contents shall be written to
   19.64 - * @param n copies at most n elements to <code>dest</code>
   19.65 + * @param n copies at most n bytes to <code>dest</code>
   19.66   * @see ucx_stack_pop
   19.67   */
   19.68  void ucx_stack_popn(UcxStack *stack, void *dest, size_t n);
    20.1 --- a/src/ucx/string.h	Sun May 13 07:13:09 2018 +0200
    20.2 +++ b/src/ucx/string.h	Mon May 14 17:56:03 2018 +0200
    20.3 @@ -58,6 +58,12 @@
    20.4  /** Shortcut for the conversion of a C string to a <code>sstr_t</code>. */
    20.5  #define S(s) sstrn((char*)s, sizeof(s)-1)
    20.6  
    20.7 +/** Expands a sstr_t to printf arguments. */
    20.8 +#define SFMT(s) (int) (s).length, (s).ptr
    20.9 +
   20.10 +/** Format specifier for a sstr_t. */
   20.11 +#define PRIsstr ".*s"
   20.12 +
   20.13  #ifdef	__cplusplus
   20.14  extern "C" {
   20.15  #endif
    21.1 --- a/src/ucx/test.h	Sun May 13 07:13:09 2018 +0200
    21.2 +++ b/src/ucx/test.h	Mon May 14 17:56:03 2018 +0200
    21.3 @@ -36,8 +36,8 @@
    21.4   * **** IN HEADER FILE: ****
    21.5   *
    21.6   * <pre>
    21.7 - * UCX_TEST(function_name)
    21.8 - * UCX_TEST_SUBROUTINE(subroutine_name, paramlist) // optional
    21.9 + * UCX_TEST(function_name);
   21.10 + * UCX_TEST_SUBROUTINE(subroutine_name, paramlist); // optional
   21.11   * </pre>
   21.12   *
   21.13   * **** IN SOURCE FILE: ****
    22.1 --- a/src/ucx/ucx.h	Sun May 13 07:13:09 2018 +0200
    22.2 +++ b/src/ucx/ucx.h	Mon May 14 17:56:03 2018 +0200
    22.3 @@ -37,7 +37,7 @@
    22.4  #define	UCX_H
    22.5  
    22.6  /** Major UCX version as integer constant. */
    22.7 -#define UCX_VERSION_MAJOR   1
    22.8 +#define UCX_VERSION_MAJOR   2
    22.9  
   22.10  /** Minor UCX version as integer constant. */
   22.11  #define UCX_VERSION_MINOR   0
   22.12 @@ -135,9 +135,54 @@
   22.13  
   22.14  #if defined(__GNUC__) || defined(__clang__)
   22.15  #define UCX_MUL_BUILTIN
   22.16 +
   22.17 +#if __WORDSIZE == 32
   22.18 +/**
   22.19 + * Alias for <code>__builtin_umul_overflow</code>.
   22.20 + * 
   22.21 + * Performs a multiplication of size_t values and checks for overflow.
   22.22 + * 
   22.23 + * @param a first operand
   22.24 + * @param b second operand
   22.25 + * @param result a pointer to a size_t, where the result should
   22.26 + * be stored
   22.27 + * @return zero, if no overflow occurred and the result is correct, non-zero
   22.28 + * otherwise
   22.29 + */
   22.30 +#define ucx_szmul(a, b, result) __builtin_umul_overflow(a, b, result)
   22.31 +#else /* __WORDSIZE != 32 */
   22.32 +/**
   22.33 + * Alias for <code>__builtin_umull_overflow</code>.
   22.34 + * 
   22.35 + * Performs a multiplication of size_t values and checks for overflow.
   22.36 + * 
   22.37 + * @param a first operand
   22.38 + * @param b second operand
   22.39 + * @param result a pointer to a size_t, where the result should
   22.40 + * be stored
   22.41 + * @return zero, if no overflow occurred and the result is correct, non-zero
   22.42 + * otherwise
   22.43 + */
   22.44  #define ucx_szmul(a, b, result) __builtin_umull_overflow(a, b, result)
   22.45 -#else
   22.46 +#endif /* __WORDSIZE */
   22.47 +
   22.48 +#else /* no GNUC or clang bultin */
   22.49 +
   22.50 +/**
   22.51 + * Performs a multiplication of size_t values and checks for overflow.
   22.52 + * 
   22.53 + * This is a custom implementation in case there is no compiler builtin
   22.54 + * available.
   22.55 + * 
   22.56 + * @param a first operand
   22.57 + * @param b second operand
   22.58 + * @param result a pointer to a size_t, where the result should
   22.59 + * be stored
   22.60 + * @return zero, if no overflow occurred and the result is correct, non-zero
   22.61 + * otherwise
   22.62 + */
   22.63  int ucx_szmul(size_t a, size_t b, size_t *result);
   22.64 +
   22.65  #endif
   22.66  
   22.67  #ifdef	__cplusplus
    23.1 --- a/src/ucx/utils.h	Sun May 13 07:13:09 2018 +0200
    23.2 +++ b/src/ucx/utils.h	Mon May 14 17:56:03 2018 +0200
    23.3 @@ -156,6 +156,15 @@
    23.4  int ucx_strncmp(const void *s1, const void *s2, void *n);
    23.5  
    23.6  /**
    23.7 + * Wraps the sstrcmp function.
    23.8 + * @param s1 sstr one
    23.9 + * @param s2 sstr two
   23.10 + * @param data ignored
   23.11 + * @return the result of sstrcmp(s1, s2)
   23.12 + */
   23.13 +int ucx_sstrcmp(const void *s1, const void *s2, void *data);
   23.14 +
   23.15 +/**
   23.16   * Compares two integers of type int.
   23.17   * @param i1 pointer to integer one
   23.18   * @param i2 pointer to integer two
   23.19 @@ -166,6 +175,35 @@
   23.20  int ucx_intcmp(const void *i1, const void *i2, void *data);
   23.21  
   23.22  /**
   23.23 + * Compares two integers of type long int.
   23.24 + * @param i1 pointer to long integer one
   23.25 + * @param i2 pointer to long integer two
   23.26 + * @param data omitted
   23.27 + * @return -1, if *i1 is less than *i2, 0 if both are equal,
   23.28 + * 1 if *i1 is greater than *i2
   23.29 + */
   23.30 +int ucx_longintcmp(const void *i1, const void *i2, void *data);
   23.31 +
   23.32 +
   23.33 +/**
   23.34 + * Distance function for integers of type int.
   23.35 + * @param i1 pointer to integer one
   23.36 + * @param i2 pointer to integer two
   23.37 + * @param data omitted
   23.38 + * @return i1 minus i2
   23.39 + */
   23.40 +intmax_t ucx_intdist(const void *i1, const void *i2, void *data);
   23.41 +
   23.42 +/**
   23.43 + * Distance function for integers of type long int.
   23.44 + * @param i1 pointer to long integer one
   23.45 + * @param i2 pointer to long integer two
   23.46 + * @param data omitted
   23.47 + * @return i1 minus i2
   23.48 + */
   23.49 +intmax_t ucx_longintdist(const void *i1, const void *i2, void *data);
   23.50 +
   23.51 +/**
   23.52   * Compares two real numbers of type float.
   23.53   * @param f1 pointer to float one
   23.54   * @param f2 pointer to float two
    24.1 --- a/src/utils.c	Sun May 13 07:13:09 2018 +0200
    24.2 +++ b/src/utils.c	Mon May 14 17:56:03 2018 +0200
    24.3 @@ -96,6 +96,12 @@
    24.4      return strncmp((const char*)s1, (const char*)s2, *((size_t*) n));
    24.5  }
    24.6  
    24.7 +int ucx_sstrcmp(const void *s1, const void *s2, void *data) {
    24.8 +    sstr_t a = *(const sstr_t*) s1;
    24.9 +    sstr_t b = *(const sstr_t*) s2;
   24.10 +    return sstrcmp(a, b);
   24.11 +}
   24.12 +
   24.13  int ucx_intcmp(const void *i1, const void *i2, void *data) {
   24.14     int a = *((const int*) i1);
   24.15     int b = *((const int*) i2);
   24.16 @@ -106,6 +112,28 @@
   24.17     }
   24.18  }
   24.19  
   24.20 +int ucx_longintcmp(const void *i1, const void *i2, void *data) {
   24.21 +   int a = *((const long int*) i1);
   24.22 +   int b = *((const long int*) i2);
   24.23 +   if (a == b) {
   24.24 +       return 0;
   24.25 +   } else {
   24.26 +       return a < b ? -1 : 1;
   24.27 +   }
   24.28 +}
   24.29 +
   24.30 +intmax_t ucx_intdist(const void *i1, const void *i2, void *data) {
   24.31 +   intmax_t a = *((const int*) i1);
   24.32 +   intmax_t b = *((const int*) i2);
   24.33 +   return a - b;
   24.34 +}
   24.35 +
   24.36 +intmax_t ucx_longintdist(const void *i1, const void *i2, void *data) {
   24.37 +   intmax_t a = *((const long int*) i1);
   24.38 +   intmax_t b = *((const long int*) i2);
   24.39 +   return a - b;
   24.40 +}
   24.41 +
   24.42  int ucx_floatcmp(const void *f1, const void *f2, void *epsilon) {
   24.43     float a = *((const float*) f1);
   24.44     float b = *((const float*) f2);
    25.1 --- a/test/buffer_tests.c	Sun May 13 07:13:09 2018 +0200
    25.2 +++ b/test/buffer_tests.c	Mon May 14 17:56:03 2018 +0200
    25.3 @@ -625,3 +625,114 @@
    25.4      
    25.5      ucx_buffer_free(b);
    25.6  }
    25.7 +
    25.8 +UCX_TEST(test_ucx_buffer_shl) {
    25.9 +    
   25.10 +    const char* hw = "Shift the World!";
   25.11 +    
   25.12 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT);
   25.13 +    ucx_buffer_puts(b, hw);
   25.14 +    b->pos = 5;
   25.15 +    
   25.16 +    UCX_TEST_BEGIN
   25.17 +    char* expected;
   25.18 +    
   25.19 +    ucx_buffer_shift_left(b, 2);
   25.20 +    expected = "ift the World!";
   25.21 +    
   25.22 +    UCX_TEST_ASSERT(b->pos == 3, "position after normal shl wrong");
   25.23 +    UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shl wrong");
   25.24 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
   25.25 +            "contents after normal shl wrong");
   25.26 +    
   25.27 +    
   25.28 +    ucx_buffer_shift_left(b, 5);
   25.29 +    expected = "he World!";
   25.30 +    
   25.31 +    UCX_TEST_ASSERT(b->pos == 0, "position after overshift left wrong");
   25.32 +    UCX_TEST_ASSERT(b->size == strlen(expected),
   25.33 +            "size after overshift left wrong");
   25.34 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
   25.35 +            "contents after overshift left wrong");
   25.36 +    
   25.37 +    ucx_buffer_shift_left(b, 10);
   25.38 +    UCX_TEST_ASSERT(b->pos == 0, "position after 'shl everything away' wrong");
   25.39 +    UCX_TEST_ASSERT(b->size == 0, "size after 'shl everything away' wrong");
   25.40 +    
   25.41 +    UCX_TEST_END
   25.42 +    
   25.43 +    ucx_buffer_free(b);    
   25.44 +}
   25.45 +
   25.46 +UCX_TEST(test_ucx_buffer_shr) {
   25.47 +    
   25.48 +    const char* hw = "Shift the World!";
   25.49 +    
   25.50 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_DEFAULT);
   25.51 +    ucx_buffer_puts(b, hw);
   25.52 +    b->pos = 12;
   25.53 +    
   25.54 +    UCX_TEST_BEGIN
   25.55 +    char* expected;
   25.56 +    
   25.57 +    ucx_buffer_shift_right(b, 2);
   25.58 +    expected = "ShShift the World!";
   25.59 +    
   25.60 +    UCX_TEST_ASSERT(b->pos == 14, "position after normal shr wrong");
   25.61 +    UCX_TEST_ASSERT(b->size == strlen(expected), "size after normal shr wrong");
   25.62 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
   25.63 +            "contents after normal shr wrong");
   25.64 +    
   25.65 +    
   25.66 +    ucx_buffer_shift_right(b, 5);
   25.67 +    expected = "ShShiShShift the Wor";
   25.68 +    UCX_TEST_ASSERT(strlen(expected) == b->capacity,
   25.69 +            "Test data is wrong, please fix the test.");
   25.70 +    
   25.71 +    UCX_TEST_ASSERT(b->pos == 19,
   25.72 +            "position after overshift right w/o auto-extend wrong");
   25.73 +    UCX_TEST_ASSERT(b->size == 20,
   25.74 +            "size after overshift right w/o auto-extend wrong");
   25.75 +    UCX_TEST_ASSERT(b->capacity == 20,
   25.76 +            "capacity after overshift right w/o auto-extend wrong");
   25.77 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
   25.78 +            "contents after overshift right w/o auto-extend wrong");
   25.79 +    
   25.80 +    ucx_buffer_shift_right(b, 15);
   25.81 +    UCX_TEST_ASSERT(b->pos == b->capacity, "position after 'shr to eof' wrong");
   25.82 +    UCX_TEST_ASSERT(b->size == b->capacity, "size after 'shr to eof' wrong");
   25.83 +    UCX_TEST_ASSERT(ucx_buffer_eof(b), "buffer not eof after 'shr to eof'");
   25.84 +    
   25.85 +    UCX_TEST_END
   25.86 +    
   25.87 +    ucx_buffer_free(b);    
   25.88 +}
   25.89 +
   25.90 +UCX_TEST(test_ucx_buffer_shr_ax) {
   25.91 +    
   25.92 +    const char* hw = "Shift the World!";
   25.93 +    
   25.94 +    UcxBuffer *b = ucx_buffer_new(NULL, 20, UCX_BUFFER_AUTOEXTEND);
   25.95 +    ucx_buffer_puts(b, hw);
   25.96 +    b->pos = 12;
   25.97 +    
   25.98 +    UCX_TEST_BEGIN
   25.99 +    
  25.100 +    const char* expected = "Shift the Shift the World!";
  25.101 +    
  25.102 +    ucx_buffer_shift_right(b, 10);
  25.103 +    UCX_TEST_ASSERT(b->pos == 22, "position after shr w/ auto-extend wrong");
  25.104 +    UCX_TEST_ASSERT(b->size == strlen(expected),
  25.105 +            "size after shr w/ auto-extend wrong");
  25.106 +    UCX_TEST_ASSERT(b->capacity >= b->size,
  25.107 +            "auto-extension of capacity after shr w/ auto-extend failed");
  25.108 +    UCX_TEST_ASSERT(!ucx_buffer_eof(b),
  25.109 +            "buffer should not be eof after shr w/ auto-extend");
  25.110 +    UCX_TEST_ASSERT(!memcmp(b->space, expected, b->size),
  25.111 +            "contents wrong after shr w/ auto-extend");
  25.112 +    
  25.113 +    UCX_TEST_END
  25.114 +    
  25.115 +    ucx_buffer_free(b);    
  25.116 +}
  25.117 +
    26.1 --- a/test/buffer_tests.h	Sun May 13 07:13:09 2018 +0200
    26.2 +++ b/test/buffer_tests.h	Mon May 14 17:56:03 2018 +0200
    26.3 @@ -60,6 +60,9 @@
    26.4  UCX_TEST(test_ucx_buffer_write);
    26.5  UCX_TEST(test_ucx_buffer_write_oob);
    26.6  UCX_TEST(test_ucx_buffer_write_ax);
    26.7 +UCX_TEST(test_ucx_buffer_shl);
    26.8 +UCX_TEST(test_ucx_buffer_shr);
    26.9 +UCX_TEST(test_ucx_buffer_shr_ax);
   26.10  
   26.11  
   26.12  #ifdef	__cplusplus
    27.1 --- a/test/logging_tests.c	Sun May 13 07:13:09 2018 +0200
    27.2 +++ b/test/logging_tests.c	Mon May 14 17:56:03 2018 +0200
    27.3 @@ -46,7 +46,7 @@
    27.4      UCX_TEST_ASSERT(strcmp(logger->dateformat, "%F %T %z ") == 0,
    27.5          "date format not set to \"%F %T %z\"");
    27.6      
    27.7 -    UCX_TEST_ASSERT(logger->levels->count == 4,
    27.8 +    UCX_TEST_ASSERT(logger->levels->count == 5,
    27.9          "incorrect number of registered log levels");
   27.10  
   27.11      int level = UCX_LOGGER_ERROR;
   27.12 @@ -55,6 +55,9 @@
   27.13      level = UCX_LOGGER_WARN;
   27.14      UCX_TEST_ASSERT(strcmp((char*)ucx_map_int_get(logger->levels, level),
   27.15          "[WARNING]") == 0, "invalid warning level");
   27.16 +    level = UCX_LOGGER_DEBUG;
   27.17 +    UCX_TEST_ASSERT(strcmp((char*)ucx_map_int_get(logger->levels, level),
   27.18 +        "[DEBUG]") == 0, "invalid debug level");
   27.19      level = UCX_LOGGER_INFO;
   27.20      UCX_TEST_ASSERT(strcmp((char*)ucx_map_int_get(logger->levels, level),
   27.21          "[INFO]") == 0, "invalid info level");
    28.1 --- a/test/main.c	Sun May 13 07:13:09 2018 +0200
    28.2 +++ b/test/main.c	Mon May 14 17:56:03 2018 +0200
    28.3 @@ -125,6 +125,7 @@
    28.4          ucx_test_register(suite, test_ucx_default_allocator);
    28.5          
    28.6          /* sstring Tests */
    28.7 +        ucx_test_register(suite, test_sstr_macros);
    28.8          ucx_test_register(suite, test_sstr);
    28.9          ucx_test_register(suite, test_sstr_len);
   28.10          ucx_test_register(suite, test_sstrcmp);
   28.11 @@ -222,6 +223,9 @@
   28.12          ucx_test_register(suite, test_ucx_buffer_write);
   28.13          ucx_test_register(suite, test_ucx_buffer_write_oob);
   28.14          ucx_test_register(suite, test_ucx_buffer_write_ax);
   28.15 +        ucx_test_register(suite, test_ucx_buffer_shl);
   28.16 +        ucx_test_register(suite, test_ucx_buffer_shr);
   28.17 +        ucx_test_register(suite, test_ucx_buffer_shr_ax);
   28.18          
   28.19          /* Utils Tests*/
   28.20          ucx_test_register(suite, test_ucx_fprintf);
    29.1 --- a/test/string_tests.c	Sun May 13 07:13:09 2018 +0200
    29.2 +++ b/test/string_tests.c	Mon May 14 17:56:03 2018 +0200
    29.3 @@ -28,6 +28,19 @@
    29.4  
    29.5  #include "string_tests.h"
    29.6  
    29.7 +UCX_TEST(test_sstr_macros) {
    29.8 +    sstr_t hello = ST("Hello");
    29.9 +    sstr_t world = S("World");
   29.10 +    
   29.11 +    char buf[20];
   29.12 +    snprintf(buf, sizeof(buf), "%" PRIsstr ", %" PRIsstr "!", SFMT(hello), SFMT(world));
   29.13 +    
   29.14 +    UCX_TEST_BEGIN
   29.15 +    const char* cmp = "Hello, World!";
   29.16 +    UCX_TEST_ASSERT(!strcmp(cmp, buf), "Something weird happened.");
   29.17 +    UCX_TEST_END
   29.18 +}
   29.19 +
   29.20  UCX_TEST(test_sstr) {
   29.21      sstr_t s1 = sstr((char*)"1234");
   29.22      sstr_t s2 = sstrn((char*)"ab", 2);
   29.23 @@ -64,7 +77,12 @@
   29.24      UCX_TEST_BEGIN
   29.25      
   29.26      sstr_t notfound = sstrchr(str, 'x');
   29.27 -    UCX_TEST_ASSERT(notfound.length == 0, "string length not 0");
   29.28 +    UCX_TEST_ASSERT(notfound.length == 0,
   29.29 +            "string length not 0 after forward search w/o result");
   29.30 +    
   29.31 +    notfound = sstrrchr(str, 'x');
   29.32 +    UCX_TEST_ASSERT(notfound.length == 0,
   29.33 +            "string length not 0 after reverse search w/o result");
   29.34      
   29.35      sstr_t result = sstrchr(str, 'w');
   29.36      UCX_TEST_ASSERT(result.length == 35, "sstrchr returned wrong length");
    30.1 --- a/test/string_tests.h	Sun May 13 07:13:09 2018 +0200
    30.2 +++ b/test/string_tests.h	Mon May 14 17:56:03 2018 +0200
    30.3 @@ -36,6 +36,7 @@
    30.4  extern "C" {
    30.5  #endif
    30.6  
    30.7 +UCX_TEST(test_sstr_macros);
    30.8  UCX_TEST(test_sstr);
    30.9  UCX_TEST(test_sstr_len);
   30.10  UCX_TEST(test_sstrcmp);

mercurial