Mon, 14 May 2018 17:56:03 +0200
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 Tree](#avl) [Buffer](#buffer) [List](#list) 4.16 -[Logging](#logging) [Map](#map) [Memory Pool](#mempool) [Properties](#properties) 4.17 -[Stack](#stack) [String](#string) [Testing](#test) [Utilities](#utils) 4.18 ------------------------ ---------------------- ---------------------------- ------------------------- 4.19 +----------------------- ---------------------- ---------------------------- ------------------------- 4.20 +[Allocator](#allocator) [AVL Tree](#avl-tree) [Buffer](#buffer) [List](#list) 4.21 +[Logging](#logging) [Map](#map) [Memory 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);