UNIXworkcode

1 # Map Interface 2 3 Similar to the list interface, the map interface provides a common API for implementing maps. 4 5 UCX is shipped with a [hash map](hash_map.h.md) implementation of this interface. 6 7 ```C 8 #include <cx/hash_map.h> 9 10 CxMap *cxHashMapCreate(const CxAllocator *allocator, 11 size_t itemsize, size_t buckets); 12 ``` 13 14 The function `cxHashMapCreate()` creates a new map where both the map structure 15 and the contained buckets are allocated by the specified `allocator`. 16 17 The map will store items of size `itemsize`. 18 You can use the `CX_STORE_POINTERS` macro for `itemsize` to indicate that the map shall store 19 pointers instead of actual items. 20 21 If you pass zero for the number of `buckets`, 22 the map is initialized with a default of 16 buckets; otherwise the specified number of buckets is allocated. 23 24 > If you want to lazy-initialize maps, you can use the global `cxEmptyMap` symbol as a placeholder instead of using a `NULL`-pointer. 25 > While you *must not* insert elements into that map, you can safely access this map or create iterators. 26 > This allows you to write clean code without checking for `NULL`-pointer everywhere. 27 > You still need to make sure that the placeholder is replaced with an actual map before inserting elements. 28 29 > All functions working with keys are implemented as generics, so that the following types are supported: 30 > `CxHashKey`, `cxstring`, `cxmutstr`, `const char*`, and `char*`. 31 > When we write `KeyType`, we mean any of these types. 32 > {style="note"} 33 34 ## Examples 35 36 ```C 37 #include <stdio.h> 38 #include <cx/hash_map.h> 39 40 int main() { 41 CxMap *contacts = cxHashMapCreate(NULL, CX_STORE_POINTERS, 0); 42 43 // Build a small phone book 44 cxMapPut(contacts, "John", "123-0815"); 45 cxMapPut(contacts, "Jane", "987-4711"); 46 cxMapPut(contacts, "Michelle", "555-3141"); 47 cxMapPut(contacts, "Oliver", "000-9999"); 48 49 // Retrieve a phone number 50 const char *janes_phone = cxMapGet(contacts, "Jane"); 51 printf("The number of Jane is: %s\n", janes_phone); 52 53 // Update number 54 cxMapPut(contacts, "Jane", "987-1337"); 55 56 // Remove and retrieve number 57 cxMapRemoveAndGet(contacts, "Jane", &janes_phone); 58 printf("The number of Jane was: %s\n", janes_phone); 59 janes_phone = cxMapGet(contacts, "Jane"); 60 if (janes_phone == NULL) printf("Jane's number was deleted.\n"); 61 62 // Iterate through the contact list 63 CxMapIterator iter = cxMapIterator(contacts); 64 cx_foreach(CxMapEntry *, entry, iter) { 65 cxstring name = cx_strn(entry->key->data, entry->key->len); 66 const char *phone = entry->value; 67 printf("%" CX_PRIstr ": %s\n", CX_SFMT(name), phone); 68 } 69 70 cxMapFree(contacts); 71 72 return 0; 73 } 74 ``` 75 76 The above example illustrates basic operations with a map. 77 78 In the first part we add several entries to the map. 79 Then the example shows retrieval, updating, and removal of information. 80 The last part shows how to iterate over the pairs inside the map and how to recover the string from the key. 81 82 In real-world situations, however, it is quite unlikely that you will use a map to store string literals. 83 The next example shows a more realistic program, where it is necessary to store strings based on user input. 84 85 ```C 86 #include <stdio.h> 87 #include <string.h> 88 #include <cx/hash_map.h> 89 90 int main() { 91 // store strings in the map... 92 CxMap *contacts = cxHashMapCreate(NULL, sizeof(cxmutstr), 0); 93 // ...which are automatically freed when removed 94 cxSetDestructor(contacts, cx_strfree); 95 96 // build a small interactive program 97 const unsigned buffer_size = 256; 98 char input[buffer_size]; 99 bool running = true; 100 101 while (running) { 102 puts("\n*** Contacts - Menu ***"); 103 puts("1) Add entry"); 104 puts("2) Look up entry"); 105 puts("3) Remove entry"); 106 puts("4) Show all entries"); 107 puts("0) Exit"); 108 fputs("> ", stdout); 109 110 if (fgets(input, buffer_size, stdin)) { 111 if (input[1] != '\n') { 112 puts("Please select a menu item.\n"); 113 } else if (input[0] == '1') { 114 fputs("Name > ", stdout); 115 if (fgets(input, buffer_size, stdin)) { 116 // Copy the name (alternative: use 2nd input buf) 117 cxmutstr name = cx_strdup( 118 cx_strn(input, strcspn(input, "\n"))); 119 fputs("Phone > ", stdout); 120 if (fgets(input, buffer_size, stdin)) { 121 // add the entry, note that only the contents 122 // of the cxmutstr are copied, not the string 123 // data - therefore we have to call cx_strdup 124 cxmutstr phone = cx_strdup( 125 cx_strn(input, strcspn(input, "\n"))); 126 // note, that we can simply use the cxmutstr 127 // for the name as key, because cxMapPut uses 128 // generic selection 129 cxMapPut(contacts, name, &phone); 130 } 131 cx_strfree(&name); 132 } 133 } else if (input[0] == '2') { 134 fputs("Name > ", stdout); 135 if (fgets(input, buffer_size, stdin)) { 136 // Look up the name 137 input[strcspn(input, "\n")] = '\0'; 138 cxmutstr *phone = cxMapGet(contacts, input); 139 if (phone == NULL) { 140 puts("No record."); 141 } else { 142 printf("Result: %" CX_PRIstr "\n", 143 CX_SFMT(*phone)); 144 } 145 } 146 } else if (input[0] == '3') { 147 fputs("Name > ", stdout); 148 if (fgets(input, buffer_size, stdin)) { 149 // Remove the entry 150 // Since we registered cx_strfree as destructor, 151 // the memory is automatically freed 152 input[strcspn(input, "\n")] = '\0'; 153 if (cxMapRemove(contacts, input)) { 154 puts("No such record."); 155 } else { 156 puts("Removed."); 157 } 158 } 159 } else if (input[0] == '4') { 160 // Almost the same iteration loop as above ... 161 CxMapIterator iter = cxMapIterator(contacts); 162 cx_foreach(CxMapEntry *, entry, iter) { 163 cxstring name = cx_strn(entry->key->data, 164 entry->key->len); 165 // ... except that here we get cxmutstr* 166 cxmutstr *phone = entry->value; 167 printf("%" CX_PRIstr ": %" CX_PRIstr "\n", 168 CX_SFMT(name), CX_SFMT(*phone)); 169 } 170 } else if (input[0] == '0') { 171 running = false; 172 } else { 173 puts("Please select a menu item.\n"); 174 } 175 } else { 176 running = false; 177 } 178 } 179 180 // this will free all remaining strings that are in the map 181 cxMapFree(contacts); 182 183 return 0; 184 } 185 ``` 186 187 ## Insert 188 189 ```C 190 #include <cx/map.h> 191 192 int cxMapPut(CxMap *map, KeyType key, void *value); 193 194 void *cxMapEmplace(CxMap *map, KeyType key); 195 ``` 196 197 The function `cxMapPut()` stores the specified `key` / `value` pair 198 and returns zero if the element was successfully put into the map and non-zero otherwise. 199 200 The key is always copied. 201 The behavior for the value is dependent on whether the map is storing pointers. 202 If it is storing pointers, the `value` pointer is directly stored in the map. 203 Otherwise, the memory pointed to by `value` is copied, using the element size of the collection. 204 205 The function `cxMapEmplace()` is similar to `cxMapPut()`, but instead of adding an existing value to the map, 206 it returns a pointer to newly allocated memory for the value. 207 The memory is allocated using the allocator of the map. 208 If the map is storing pointers, the allocated memory will only be that for a pointer. 209 Otherwise, the memory is allocated using the known element size. 210 211 Both functions, if an element is already associated with the specified key, replace the existing element. 212 If [destructor functions](collection.h.md#destructor-functions) are registered, 213 they are invoked for the old element before it is replaced. 214 215 ## Access 216 217 ```C 218 #include <cx/map.h> 219 220 void *cxMapGet(CxMap *map, KeyType key); 221 222 bool cxMapContains(CxMap *map, KeyType key); 223 224 size_t cxMapSize(const CxMap *map); 225 ``` 226 227 With the function `cxMapGet()` you can retrieve a value stored under the specified `key`. 228 If there is no such value, this function returns `NULL`. 229 230 The function `cxMapContains()` returns `true` if the map contains an element with the specified `key`, 231 and `false` otherwise. 232 233 The function `cxMapSize()` returns how many key/value-pairs are currently stored in the map. 234 235 ## Remove 236 237 ```C 238 #include <cx/map.h> 239 240 int cxMapRemove(CxMap *map, KeyType key); 241 242 int cxMapRemoveAndGet(CxMap *map, KeyType key, void* targetbuf); 243 244 void cxMapClear(CxMap *map); 245 ``` 246 247 The function `cxMapRemove()` retrieves and removes a value stored under the specified `key`. 248 If [destructor functions](collection.h.md#destructor-functions) are registered, they are called before removal. 249 250 On the other hand, `cxMapRemoveAndGet()` does not invoke the destructor functions 251 and instead copies the value into the `targetbuf`, which must be large enough to hold the value. 252 253 In either case, the functions return zero when an element was found under the specified key, and non-zero otherwise. 254 255 The function `cxMapClear()` removes all elements from the map, invoking the destructor functions for each of them. 256 257 ## Iterators 258 259 ```C 260 #include <cx/map.h> 261 262 CxMapIterator cxMapIterator(const CxMap *map); 263 264 CxMapIterator cxMapIteratorKeys(const CxMap *map); 265 266 CxMapIterator cxMapIteratorValues(const CxMap *map); 267 ``` 268 269 The above functions create iterators over the 270 pairs, keys, or values of the specified `map`, respectively. 271 272 Iterators over pairs yield elements of the type `CxMapEntry*` which is a struct containing a pointer to the `key` and the value, respectively. 273 274 Iterators over keys yield elements of the type `const CxHashKey*` (cf. [CxHashKey documentation](hash_key.h.md)). 275 276 The behavior of iterators over values depends on the concrete implementation. 277 Implementations are encouraged to support `CX_STORE_POINTERS`. 278 If used, the `void*` elements the iterator yields shall be directly the stored pointers. 279 Otherwise, the iterator shall yield pointers to the map's memory where the value is stored. 280 281 It is always safe to call the above functions on a `NULL`-pointer. 282 In that case, the returned iterator will behave like an iterator over an empty map. 283 284 ## Compare 285 286 ```C 287 #include <cx/map.h> 288 289 int cxMapCompare(const CxMap *map, const CxMap *other); 290 ``` 291 292 Arbitrary maps can be compared with `cxMapCompare()`. 293 That means, for example, you can compare a [hash map](hash_map.h.md) with the map aspect of a [key/value list](kv_list.h.md). 294 295 The return value of `cxMapCompare()` is zero if the lists contain the same keys and their values are pairwise equivalent, according to the map's compare function. 296 If the `map` contains fewer keys than the `other` map, the function returns a negative value, and if it contains more values, it returns a positive value, respectively. 297 When both maps contain the same number of keys but differ in either a key or a value, the return value is non-zero, but otherwise unspecified. 298 299 ## Clone 300 301 ```C 302 #include <cx/allocator.h> 303 304 typedef void*(cx_clone_func)( 305 void *target, const void *source, 306 const CxAllocator *allocator, 307 void *data); 308 309 #include <cx/map.h> 310 311 int cxMapClone(CxMap *dst, const CxMap *src, 312 cx_clone_func clone_func, 313 const CxAllocator *clone_allocator, 314 void *data); 315 316 int cxMapCloneShallow(CxMap *dst, const CxMap *src); 317 318 int cxMapDifference(CxMap *dst, 319 const CxMap *minuend, const CxMap *subtrahend, 320 cx_clone_func clone_func, 321 const CxAllocator *clone_allocator, 322 void *data); 323 324 int cxMapDifferenceShallow(CxMap *dst, 325 const CxMap *minuend, const CxMap *subtrahend); 326 327 int cxMapListDifference(CxMap *dst, 328 const CxMap *src, const CxList *keys, 329 cx_clone_func clone_func, 330 const CxAllocator *clone_allocator, 331 void *data); 332 333 int cxMapListDifferenceShallow(CxMap *dst, 334 const CxMap *src, const CxList *keys); 335 336 int cxMapIntersection(CxMap *dst, 337 const CxMap *src, const CxMap *other, 338 cx_clone_func clone_func, 339 const CxAllocator *clone_allocator, 340 void *data); 341 342 int cxMapIntersectionShallow(CxMap *dst, 343 const CxMap *src, const CxMap *other); 344 345 int cxMapListIntersection(CxMap *dst, 346 const CxMap *src, const CxList *keys, 347 cx_clone_func clone_func, 348 const CxAllocator *clone_allocator, 349 void *data); 350 351 int cxMapListIntersectionShallow(CxMap *dst, 352 const CxMap *src, const CxList *keys); 353 354 int cxMapUnion(CxMap *dst, const CxMap *src, 355 cx_clone_func clone_func, 356 const CxAllocator *clone_allocator, 357 void *data); 358 359 int cxMapUnionShallow(CxMap *dst, const CxMap *src); 360 ``` 361 362 With `cxMapClone()` you can create deep copies of the values in one map and insert them into another map. 363 The destination map does not need to be empty. 364 But when a key already exists in the destination map, the value is overwritten with the clone from the source map. 365 If the destination map has destructors defined, they are called for the replaced element. 366 367 The functions `cxMapDifference()` and `cxMapListDifference()` are similar to `cxMapClone()` 368 except that they only clone an element from the source map, when the key is _not_ contained in the 369 other map (or list, respectively). 370 This is equivalent to computing the set difference for the set of keys. 371 Likewise, `cxMapIntersection()` and `cxMapListIntersection()` only clone an element from the source map 372 when the key is contained in _both_ collections. 373 The function `cxMapUnion()` only clones elements from `src` to `dst` when their key is not present in `dst`. 374 375 > The function `cxMapUnion()` does not operate on three maps for the sake of simplicity. 376 > If you want to combine two maps without modifying either of them, you can first use `cxMapClone()` 377 > to clone the first map into a new, empty, map, and then use `cxMapUnion()`. 378 379 Refer to the documentation of the [clone-function callback](allocator.h.md#clone-function) to learn how to implement it. 380 381 The _simple_ versions of the above functions use an internal shallow clone function 382 which uses `memcpy()` to copy the values. 383 If the destination map is storing pointers, this internal clone function uses the current default allocator to allocate the memory. 384 385 The functions return zero if and only if all clone operations were successful. 386 387 > It is perfectly possible to clone items into a map of a different type. 388 > For example, you can clone entries from a map that is just storing pointers (`CX_STORE_POINTERS`) to a map that 389 > allocates the memory for the objects (and vice versa). 390 391 > The maps passed to the above functions must all be different. 392 > Passing the same pointer for different map arguments may result in erroneous behavior. 393 >{style="warning"} 394 395 ## Dispose 396 397 ```C 398 #include <cx/map.h> 399 400 void cxMapFree(CxMap *map); 401 ``` 402 403 The function `cxMapFree()` invokes the [destructor functions](collection.h.md#destructor-functions) for all elements 404 and then deallocates the entire memory for the map. 405 406 ## Implement own Map Structures 407 408 If the UCX [hash map](hash_map.h.md) implementation does not suit your needs, 409 you can also implement your own map. 410 To be compatible with the `CxMap` interface, it needs to store key/value pairs of the following form. 411 412 ```C 413 typedef struct { 414 const CxHashKey *key; 415 void *value; 416 } CxMapEntry; 417 ``` 418 419 When you are declaring your map structure, a `CxMap` member must be embedded as the first member of this structure. 420 Secondly, you need to implement the `cx_map_class` and assign it when allocating your map. 421 422 ```C 423 #include <cx/map.h> 424 425 typedef struct { 426 CxMap base; 427 // ... data members for storing CxMapEntry elements go here ... 428 } MyMap; 429 430 // declare the class - implement the functions somewhere 431 static cx_map_class my_map_class = { 432 my_map_destructor, 433 my_map_clear, 434 my_map_put, 435 my_map_get, 436 my_map_remove, 437 my_map_iterator, 438 }; 439 440 // this function will create the map 441 CxMap *myMapCreate(const CxAllocator *allocator, size_t itemsize) { 442 if (allocator == NULL) { 443 allocator = cxDefaultAllocator; 444 } 445 446 // allocate memory 447 MyMap *map = cxCalloc(allocator, 1, sizeof(MyMap)); 448 if (map == NULL) return NULL; 449 450 // initialize base members 451 map->base.cl = &my_map_class; // <--- assign class here 452 map->base.collection.allocator = allocator; 453 if (itemsize == CX_STORE_POINTERS) { 454 map->base.collection.elem_size = sizeof(void *); 455 map->base.collection.store_pointer = true; 456 } else { 457 map->base.collection.elem_size = itemsize; 458 } 459 460 // ... initialization of data members go here ... 461 462 return (CxMap *) map; 463 } 464 ``` 465 466 The required behavior for the implementations is described in the following table. 467 You can always look at the source code of the UCX hash map to get inspiration. 468 469 | Function | Description | 470 |--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 471 | `clear` | Invoke [destructor functions](collection.h.md#destructor-functions) on all elements and remove them from the map. | 472 | `deallocate` | Invoke destructor functions on all elements and deallocate the entire map memory. | 473 | `put` | Store an element in the map. If an element is already stored, invoke the destructor functions on that element and replace it with the new element. When the value pointer is `NULL`, only allocate memory without copying an existing value. Return `CxMapEntry` where the `key` is `NULL` when allocation fails. | 474 | `get` | Look up the specified key and return the associated value (or `NULL` if the key was not found). | 475 | `remove` | Remove an element from the map. If a target buffer is specified, copy the elements to that buffer. Otherwise, invoke the destructor functions for the element. If the key was not found in the map, return non-zero. | 476 | `iterator` | Return an iterator over the pairs, the keys, or the values, depending on the iterator type passed with the last argument. | 477 478 > In contrast to the list interface, there is no `cx_map_init()` function which automatically 479 > configures a wrapping mechanism when `CX_STORE_POINTERS` is used. 480 > That means, in the implementation of the above functions you must take care of distinguishing between 481 > maps that are storing pointers and that are storing elements yourself (if you want to support both). 482 >{style="note"} 483 484 <seealso> 485 <category ref="apidoc"> 486 <a href="https://ucx.sourceforge.io/api/map_8h.html">map.h</a> 487 </category> 488 </seealso> 489 490