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