Sun, 09 Jul 2023 12:12:29 +0200
add release date for UCX 3.0
universe@556 | 1 | /* |
universe@556 | 2 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
universe@556 | 3 | * |
universe@556 | 4 | * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. |
universe@556 | 5 | * |
universe@556 | 6 | * Redistribution and use in source and binary forms, with or without |
universe@556 | 7 | * modification, are permitted provided that the following conditions are met: |
universe@556 | 8 | * |
universe@556 | 9 | * 1. Redistributions of source code must retain the above copyright |
universe@556 | 10 | * notice, this list of conditions and the following disclaimer. |
universe@556 | 11 | * |
universe@556 | 12 | * 2. Redistributions in binary form must reproduce the above copyright |
universe@556 | 13 | * notice, this list of conditions and the following disclaimer in the |
universe@556 | 14 | * documentation and/or other materials provided with the distribution. |
universe@556 | 15 | * |
universe@556 | 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
universe@556 | 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
universe@556 | 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
universe@556 | 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
universe@556 | 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
universe@556 | 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
universe@556 | 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
universe@556 | 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
universe@556 | 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
universe@556 | 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
universe@556 | 26 | * POSSIBILITY OF SUCH DAMAGE. |
universe@556 | 27 | */ |
universe@556 | 28 | |
universe@556 | 29 | #include "cx/hash_map.h" |
universe@556 | 30 | #include "cx/utils.h" |
universe@658 | 31 | #include "cx/string.h" |
universe@556 | 32 | #include "util_allocator.h" |
universe@691 | 33 | #include "test_map_generics.h" |
universe@556 | 34 | |
universe@556 | 35 | #include <gtest/gtest.h> |
universe@556 | 36 | #include <unordered_map> |
universe@556 | 37 | #include <unordered_set> |
universe@556 | 38 | |
universe@556 | 39 | struct map_operation { |
universe@556 | 40 | enum { |
universe@556 | 41 | put, rm |
universe@556 | 42 | } op; |
universe@556 | 43 | char const *key; |
universe@556 | 44 | char const *value; |
universe@556 | 45 | }; |
universe@556 | 46 | |
universe@556 | 47 | auto generate_map_operations() -> std::vector<map_operation> { |
universe@556 | 48 | return { |
universe@556 | 49 | {map_operation::put, "key 1", "test"}, |
universe@556 | 50 | {map_operation::put, "key 2", "blub"}, |
universe@556 | 51 | {map_operation::put, "key 3", "hallo"}, |
universe@556 | 52 | {map_operation::put, "key 2", "foobar"}, |
universe@556 | 53 | {map_operation::put, "key 4", "value 4"}, |
universe@556 | 54 | {map_operation::put, "key 5", "value 5"}, |
universe@556 | 55 | {map_operation::put, "key 6", "value 6"}, |
universe@556 | 56 | {map_operation::rm, "key 4", nullptr}, |
universe@556 | 57 | {map_operation::put, "key 7", "value 7"}, |
universe@556 | 58 | {map_operation::put, "key 8", "value 8"}, |
universe@556 | 59 | {map_operation::rm, "does not exist", nullptr}, |
universe@556 | 60 | {map_operation::put, "key 9", "value 9"}, |
universe@556 | 61 | {map_operation::put, "key 6", "other value"}, |
universe@556 | 62 | {map_operation::put, "key 7", "something else"}, |
universe@556 | 63 | {map_operation::rm, "key 8", nullptr}, |
universe@556 | 64 | {map_operation::rm, "key 2", nullptr}, |
universe@556 | 65 | {map_operation::put, "key 8", "new value"}, |
universe@556 | 66 | }; |
universe@556 | 67 | } |
universe@556 | 68 | |
universe@556 | 69 | static void verify_map_contents( |
universe@556 | 70 | CxMap *map, |
universe@556 | 71 | std::unordered_map<std::string, std::string> const &refmap |
universe@556 | 72 | ) { |
universe@556 | 73 | // verify key iterator |
universe@556 | 74 | { |
universe@556 | 75 | auto keyiter = cxMapIteratorKeys(map); |
universe@556 | 76 | std::unordered_set<std::string> keys; |
universe@563 | 77 | cx_foreach(CxHashKey*, elem, keyiter) { |
universe@691 | 78 | keys.insert(std::string(reinterpret_cast<char const *>(elem->data), elem->len)); |
universe@556 | 79 | } |
universe@561 | 80 | EXPECT_EQ(keyiter.index, map->size); |
universe@556 | 81 | ASSERT_EQ(keys.size(), map->size); |
universe@556 | 82 | for (auto &&k: keys) { |
universe@556 | 83 | EXPECT_NE(refmap.find(k), refmap.end()); |
universe@556 | 84 | } |
universe@556 | 85 | } |
universe@556 | 86 | |
universe@556 | 87 | // verify value iterator |
universe@556 | 88 | { |
universe@556 | 89 | auto valiter = cxMapIteratorValues(map); |
universe@556 | 90 | std::unordered_set<std::string> values; // we use that the values in our test data are unique strings |
universe@556 | 91 | cx_foreach(char const*, elem, valiter) { |
universe@556 | 92 | values.insert(std::string(elem)); |
universe@556 | 93 | } |
universe@561 | 94 | EXPECT_EQ(valiter.index, map->size); |
universe@556 | 95 | ASSERT_EQ(values.size(), map->size); |
universe@556 | 96 | for (auto &&v: values) { |
universe@556 | 97 | EXPECT_NE(std::find_if(refmap.begin(), refmap.end(), |
universe@556 | 98 | [v](auto const &e) { return e.second == v; }), refmap.end()); |
universe@556 | 99 | } |
universe@556 | 100 | } |
universe@556 | 101 | |
universe@556 | 102 | // verify pair iterator |
universe@556 | 103 | { |
universe@556 | 104 | auto pairiter = cxMapIterator(map); |
universe@556 | 105 | std::unordered_map<std::string, std::string> pairs; |
universe@556 | 106 | cx_foreach(CxMapEntry*, entry, pairiter) { |
universe@691 | 107 | pairs[std::string(reinterpret_cast<char const *>(entry->key->data), entry->key->len)] = std::string( |
universe@691 | 108 | (char *) entry->value); |
universe@556 | 109 | } |
universe@561 | 110 | EXPECT_EQ(pairiter.index, map->size); |
universe@556 | 111 | ASSERT_EQ(pairs.size(), refmap.size()); |
universe@556 | 112 | for (auto &&p: pairs) { |
universe@556 | 113 | ASSERT_EQ(p.second, refmap.at(p.first)); |
universe@556 | 114 | } |
universe@556 | 115 | } |
universe@556 | 116 | } |
universe@556 | 117 | |
universe@556 | 118 | TEST(CxHashMap, Create) { |
universe@556 | 119 | CxTestingAllocator allocator; |
universe@658 | 120 | auto map = cxHashMapCreate(&allocator, 1, 0); |
universe@556 | 121 | auto hmap = reinterpret_cast<struct cx_hash_map_s *>(map); |
universe@556 | 122 | EXPECT_GT(hmap->bucket_count, 0); |
universe@556 | 123 | cx_for_n(i, hmap->bucket_count) { |
universe@556 | 124 | EXPECT_EQ(hmap->buckets[i], nullptr); |
universe@556 | 125 | } |
universe@677 | 126 | EXPECT_EQ(map->item_size, 1); |
universe@556 | 127 | EXPECT_EQ(map->size, 0); |
universe@556 | 128 | EXPECT_EQ(map->allocator, &allocator); |
universe@685 | 129 | EXPECT_FALSE(map->store_pointer); |
universe@685 | 130 | EXPECT_EQ(map->cmpfunc, nullptr); |
universe@685 | 131 | EXPECT_EQ(map->simple_destructor, nullptr); |
universe@685 | 132 | EXPECT_EQ(map->advanced_destructor, nullptr); |
universe@685 | 133 | EXPECT_EQ(map->destructor_data, nullptr); |
universe@658 | 134 | cxMapStorePointers(map); |
universe@685 | 135 | EXPECT_TRUE(map->store_pointer); |
universe@677 | 136 | EXPECT_EQ(map->item_size, sizeof(void *)); |
universe@658 | 137 | cxMapStoreObjects(map); |
universe@685 | 138 | EXPECT_FALSE(map->store_pointer); |
universe@556 | 139 | |
universe@556 | 140 | cxMapDestroy(map); |
universe@556 | 141 | EXPECT_TRUE(allocator.verify()); |
universe@556 | 142 | } |
universe@556 | 143 | |
universe@668 | 144 | TEST(CxHashMap, CreateForStoringPointers) { |
universe@668 | 145 | CxTestingAllocator allocator; |
universe@668 | 146 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 0); |
universe@668 | 147 | auto hmap = reinterpret_cast<struct cx_hash_map_s *>(map); |
universe@668 | 148 | EXPECT_GT(hmap->bucket_count, 0); |
universe@668 | 149 | cx_for_n(i, hmap->bucket_count) { |
universe@668 | 150 | EXPECT_EQ(hmap->buckets[i], nullptr); |
universe@668 | 151 | } |
universe@668 | 152 | EXPECT_EQ(map->size, 0); |
universe@668 | 153 | EXPECT_EQ(map->allocator, &allocator); |
universe@685 | 154 | EXPECT_TRUE(map->store_pointer); |
universe@677 | 155 | EXPECT_EQ(map->item_size, sizeof(void *)); |
universe@668 | 156 | |
universe@668 | 157 | cxMapDestroy(map); |
universe@668 | 158 | EXPECT_TRUE(allocator.verify()); |
universe@668 | 159 | } |
universe@668 | 160 | |
universe@556 | 161 | TEST(CxHashMap, BasicOperations) { |
universe@556 | 162 | // create the map |
universe@556 | 163 | CxTestingAllocator allocator; |
universe@669 | 164 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 8); |
universe@556 | 165 | |
universe@556 | 166 | // create a reference map |
universe@556 | 167 | std::unordered_map<std::string, std::string> refmap; |
universe@556 | 168 | |
universe@556 | 169 | // generate operations |
universe@556 | 170 | auto ops = generate_map_operations(); |
universe@556 | 171 | |
universe@556 | 172 | // verify iterators for empty map |
universe@556 | 173 | verify_map_contents(map, refmap); |
universe@556 | 174 | |
universe@556 | 175 | // execute operations and verify results |
universe@556 | 176 | for (auto &&op: ops) { |
universe@563 | 177 | CxHashKey key = cx_hash_key_str(op.key); |
universe@563 | 178 | key.hash = 0; // force the hash map to compute the hash |
universe@556 | 179 | if (op.op == map_operation::put) { |
universe@556 | 180 | // execute a put operation and verify that the exact value can be read back |
universe@556 | 181 | refmap[std::string(op.key)] = std::string(op.value); |
universe@556 | 182 | int result = cxMapPut(map, key, (void *) op.value); |
universe@556 | 183 | EXPECT_EQ(result, 0); |
universe@556 | 184 | auto added = cxMapGet(map, key); |
universe@556 | 185 | EXPECT_EQ(memcmp(op.value, added, strlen(op.value)), 0); |
universe@556 | 186 | } else { |
universe@556 | 187 | // execute a remove and verify that the removed element was returned (or nullptr) |
universe@556 | 188 | auto found = refmap.find(op.key); |
universe@659 | 189 | auto removed = cxMapRemoveAndGet(map, key); |
universe@556 | 190 | if (found == refmap.end()) { |
universe@556 | 191 | EXPECT_EQ(removed, nullptr); |
universe@556 | 192 | } else { |
universe@556 | 193 | EXPECT_EQ(std::string((char *) removed), found->second); |
universe@556 | 194 | refmap.erase(found); |
universe@556 | 195 | } |
universe@556 | 196 | } |
universe@556 | 197 | // compare the current map state with the reference map |
universe@556 | 198 | verify_map_contents(map, refmap); |
universe@556 | 199 | } |
universe@556 | 200 | |
universe@556 | 201 | // destroy the map and verify the memory (de)allocations |
universe@556 | 202 | cxMapDestroy(map); |
universe@556 | 203 | EXPECT_TRUE(allocator.verify()); |
universe@556 | 204 | } |
universe@561 | 205 | |
universe@561 | 206 | TEST(CxHashMap, RemoveViaIterator) { |
universe@561 | 207 | CxTestingAllocator allocator; |
universe@669 | 208 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 4); |
universe@561 | 209 | |
universe@691 | 210 | cxMapPut(map, "key 1", (void *) "val 1"); |
universe@691 | 211 | cxMapPut(map, "key 2", (void *) "val 2"); |
universe@691 | 212 | cxMapPut(map, "key 3", (void *) "val 3"); |
universe@691 | 213 | cxMapPut(map, "key 4", (void *) "val 4"); |
universe@691 | 214 | cxMapPut(map, "key 5", (void *) "val 5"); |
universe@691 | 215 | cxMapPut(map, "key 6", (void *) "val 6"); |
universe@561 | 216 | |
universe@630 | 217 | auto iter = cxMapMutIterator(map); |
universe@561 | 218 | cx_foreach(CxMapEntry*, entry, iter) { |
universe@691 | 219 | if (reinterpret_cast<char const *>(entry->key->data)[4] % 2 == 1) cxIteratorFlagRemoval(iter); |
universe@561 | 220 | } |
universe@561 | 221 | EXPECT_EQ(map->size, 3); |
universe@561 | 222 | EXPECT_EQ(iter.index, map->size); |
universe@561 | 223 | |
universe@691 | 224 | EXPECT_EQ(cxMapGet(map, "key 1"), nullptr); |
universe@691 | 225 | EXPECT_NE(cxMapGet(map, "key 2"), nullptr); |
universe@691 | 226 | EXPECT_EQ(cxMapGet(map, "key 3"), nullptr); |
universe@691 | 227 | EXPECT_NE(cxMapGet(map, "key 4"), nullptr); |
universe@691 | 228 | EXPECT_EQ(cxMapGet(map, "key 5"), nullptr); |
universe@691 | 229 | EXPECT_NE(cxMapGet(map, "key 6"), nullptr); |
universe@561 | 230 | |
universe@561 | 231 | cxMapDestroy(map); |
universe@561 | 232 | EXPECT_TRUE(allocator.verify()); |
universe@561 | 233 | } |
universe@562 | 234 | |
universe@562 | 235 | TEST(CxHashMap, RehashNotRequired) { |
universe@562 | 236 | CxTestingAllocator allocator; |
universe@669 | 237 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 8); |
universe@562 | 238 | |
universe@691 | 239 | cxMapPut(map, "key 1", (void *) "val 1"); |
universe@691 | 240 | cxMapPut(map, "key 2", (void *) "val 2"); |
universe@691 | 241 | cxMapPut(map, "key 3", (void *) "val 3"); |
universe@691 | 242 | cxMapPut(map, "key 4", (void *) "val 4"); |
universe@691 | 243 | cxMapPut(map, "key 5", (void *) "val 5"); |
universe@691 | 244 | cxMapPut(map, "key 6", (void *) "val 6"); |
universe@562 | 245 | |
universe@562 | 246 | // 6/8 does not exceed 0.75, therefore the function should not rehash |
universe@562 | 247 | int result = cxMapRehash(map); |
universe@562 | 248 | EXPECT_EQ(result, 0); |
universe@562 | 249 | EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 8); |
universe@562 | 250 | |
universe@562 | 251 | cxMapDestroy(map); |
universe@562 | 252 | EXPECT_TRUE(allocator.verify()); |
universe@562 | 253 | } |
universe@562 | 254 | |
universe@562 | 255 | TEST(CxHashMap, Rehash) { |
universe@562 | 256 | CxTestingAllocator allocator; |
universe@687 | 257 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 7); |
universe@562 | 258 | |
universe@691 | 259 | cxMapPut(map, "key 1", (void *) "val 1"); |
universe@691 | 260 | cxMapPut(map, "key 2", (void *) "val 2"); |
universe@691 | 261 | cxMapPut(map, "key 3", (void *) "val 3"); |
universe@691 | 262 | cxMapPut(map, "foo 4", (void *) "val 4"); |
universe@691 | 263 | cxMapPut(map, "key 5", (void *) "val 5"); |
universe@691 | 264 | cxMapPut(map, "key 6", (void *) "val 6"); |
universe@691 | 265 | cxMapPut(map, "bar 7", (void *) "val 7"); |
universe@691 | 266 | cxMapPut(map, "key 8", (void *) "val 8"); |
universe@691 | 267 | cxMapPut(map, "key 9", (void *) "val 9"); |
universe@691 | 268 | cxMapPut(map, "key 10", (void *) "val 10"); |
universe@562 | 269 | |
universe@562 | 270 | int result = cxMapRehash(map); |
universe@562 | 271 | EXPECT_EQ(result, 0); |
universe@687 | 272 | EXPECT_EQ(reinterpret_cast<struct cx_hash_map_s *>(map)->bucket_count, 25); |
universe@687 | 273 | EXPECT_EQ(map->size, 10); |
universe@562 | 274 | |
universe@691 | 275 | EXPECT_STREQ((char *) cxMapGet(map, "key 1"), "val 1"); |
universe@691 | 276 | EXPECT_STREQ((char *) cxMapGet(map, "key 2"), "val 2"); |
universe@691 | 277 | EXPECT_STREQ((char *) cxMapGet(map, "key 3"), "val 3"); |
universe@691 | 278 | EXPECT_STREQ((char *) cxMapGet(map, "foo 4"), "val 4"); |
universe@691 | 279 | EXPECT_STREQ((char *) cxMapGet(map, "key 5"), "val 5"); |
universe@691 | 280 | EXPECT_STREQ((char *) cxMapGet(map, "key 6"), "val 6"); |
universe@691 | 281 | EXPECT_STREQ((char *) cxMapGet(map, "bar 7"), "val 7"); |
universe@691 | 282 | EXPECT_STREQ((char *) cxMapGet(map, "key 8"), "val 8"); |
universe@691 | 283 | EXPECT_STREQ((char *) cxMapGet(map, "key 9"), "val 9"); |
universe@691 | 284 | EXPECT_STREQ((char *) cxMapGet(map, "key 10"), "val 10"); |
universe@562 | 285 | |
universe@562 | 286 | cxMapDestroy(map); |
universe@562 | 287 | EXPECT_TRUE(allocator.verify()); |
universe@594 | 288 | } |
universe@594 | 289 | |
universe@594 | 290 | TEST(CxHashMap, Clear) { |
universe@594 | 291 | CxTestingAllocator allocator; |
universe@669 | 292 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 0); |
universe@686 | 293 | |
universe@691 | 294 | cxMapPut(map, "key 1", (void *) "val 1"); |
universe@691 | 295 | cxMapPut(map, "key 2", (void *) "val 2"); |
universe@691 | 296 | cxMapPut(map, "key 3", (void *) "val 3"); |
universe@594 | 297 | |
universe@594 | 298 | EXPECT_EQ(map->size, 3); |
universe@594 | 299 | |
universe@594 | 300 | cxMapClear(map); |
universe@594 | 301 | |
universe@594 | 302 | EXPECT_EQ(map->size, 0); |
universe@691 | 303 | EXPECT_EQ(cxMapGet(map, "key 1"), nullptr); |
universe@691 | 304 | EXPECT_EQ(cxMapGet(map, "key 2"), nullptr); |
universe@691 | 305 | EXPECT_EQ(cxMapGet(map, "key 3"), nullptr); |
universe@594 | 306 | |
universe@594 | 307 | cxMapDestroy(map); |
universe@594 | 308 | EXPECT_TRUE(allocator.verify()); |
universe@658 | 309 | } |
universe@658 | 310 | |
universe@658 | 311 | TEST(CxHashMap, StoreUcxStrings) { |
universe@658 | 312 | // create the map |
universe@658 | 313 | CxTestingAllocator allocator; |
universe@658 | 314 | auto map = cxHashMapCreate(&allocator, sizeof(cxstring), 8); |
universe@658 | 315 | |
universe@658 | 316 | // define some strings |
universe@685 | 317 | auto s1 = CX_STR("this"); |
universe@685 | 318 | auto s2 = CX_STR("is"); |
universe@685 | 319 | auto s3 = CX_STR("a"); |
universe@685 | 320 | auto s4 = CX_STR("test"); |
universe@685 | 321 | auto s5 = CX_STR("setup"); |
universe@658 | 322 | |
universe@658 | 323 | // put them into the map |
universe@691 | 324 | cxMapPut(map, "s1", &s1); |
universe@691 | 325 | cxMapPut(map, "s2", &s2); |
universe@691 | 326 | cxMapPut(map, "s3", &s3); |
universe@691 | 327 | cxMapPut(map, "s4", &s4); |
universe@658 | 328 | |
universe@658 | 329 | // overwrite a value |
universe@691 | 330 | cxMapPut(map, "s1", &s5); |
universe@658 | 331 | |
universe@658 | 332 | // look up a string |
universe@691 | 333 | auto s3p = reinterpret_cast<cxstring *>(cxMapGet(map, "s3")); |
universe@658 | 334 | EXPECT_EQ(s3p->length, s3.length); |
universe@658 | 335 | EXPECT_EQ(s3p->ptr, s3.ptr); |
universe@658 | 336 | EXPECT_NE(s3p, &s3); |
universe@658 | 337 | |
universe@658 | 338 | // remove a string |
universe@691 | 339 | cxMapRemove(map, "s2"); |
universe@658 | 340 | |
universe@658 | 341 | // iterate |
universe@658 | 342 | auto ref = std::vector{s5.ptr, s3.ptr, s4.ptr}; |
universe@658 | 343 | auto iter = cxMapIteratorValues(map); |
universe@658 | 344 | cx_foreach(cxstring*, s, iter) { |
universe@658 | 345 | auto found = std::find(ref.begin(), ref.end(), s->ptr); |
universe@658 | 346 | ASSERT_NE(found, ref.end()); |
universe@658 | 347 | ref.erase(found); |
universe@658 | 348 | } |
universe@658 | 349 | EXPECT_EQ(ref.size(), 0); |
universe@658 | 350 | |
universe@658 | 351 | cxMapDestroy(map); |
universe@658 | 352 | EXPECT_TRUE(allocator.verify()); |
universe@658 | 353 | } |
universe@685 | 354 | |
universe@686 | 355 | static void test_simple_destructor(void *data) { |
universe@686 | 356 | strcpy((char *) data, "OK"); |
universe@686 | 357 | } |
universe@686 | 358 | |
universe@686 | 359 | static void test_advanced_destructor( |
universe@686 | 360 | [[maybe_unused]] void *unused, |
universe@686 | 361 | void *data |
universe@686 | 362 | ) { |
universe@686 | 363 | strcpy((char *) data, "OK"); |
universe@686 | 364 | } |
universe@686 | 365 | |
universe@686 | 366 | static void verify_any_destructor(CxMap *map) { |
universe@686 | 367 | auto k1 = cx_hash_key_str("key 1"); |
universe@686 | 368 | auto k2 = cx_hash_key_str("key 2"); |
universe@686 | 369 | auto k3 = cx_hash_key_str("key 3"); |
universe@686 | 370 | auto k4 = cx_hash_key_str("key 4"); |
universe@686 | 371 | auto k5 = cx_hash_key_str("key 5"); |
universe@686 | 372 | |
universe@686 | 373 | char v1[] = "val 1"; |
universe@686 | 374 | char v2[] = "val 2"; |
universe@686 | 375 | char v3[] = "val 3"; |
universe@686 | 376 | char v4[] = "val 4"; |
universe@686 | 377 | char v5[] = "val 5"; |
universe@686 | 378 | |
universe@686 | 379 | cxMapPut(map, k1, (void *) v1); |
universe@686 | 380 | cxMapPut(map, k2, (void *) v2); |
universe@686 | 381 | cxMapPut(map, k3, (void *) v3); |
universe@686 | 382 | cxMapPut(map, k4, (void *) v4); |
universe@686 | 383 | |
universe@686 | 384 | cxMapRemove(map, k2); |
universe@686 | 385 | auto r = cxMapRemoveAndGet(map, k3); |
universe@686 | 386 | cxMapDetach(map, k1); |
universe@686 | 387 | |
universe@686 | 388 | EXPECT_STREQ(v1, "val 1"); |
universe@686 | 389 | EXPECT_STREQ(v2, "OK"); |
universe@686 | 390 | EXPECT_STREQ(v3, "val 3"); |
universe@686 | 391 | EXPECT_STREQ(v4, "val 4"); |
universe@686 | 392 | EXPECT_STREQ(v5, "val 5"); |
universe@686 | 393 | EXPECT_EQ(r, v3); |
universe@686 | 394 | |
universe@686 | 395 | cxMapClear(map); |
universe@686 | 396 | |
universe@686 | 397 | EXPECT_STREQ(v1, "val 1"); |
universe@686 | 398 | EXPECT_STREQ(v2, "OK"); |
universe@686 | 399 | EXPECT_STREQ(v3, "val 3"); |
universe@686 | 400 | EXPECT_STREQ(v4, "OK"); |
universe@686 | 401 | EXPECT_STREQ(v5, "val 5"); |
universe@686 | 402 | |
universe@686 | 403 | cxMapPut(map, k1, (void *) v1); |
universe@686 | 404 | cxMapPut(map, k3, (void *) v3); |
universe@686 | 405 | cxMapPut(map, k5, (void *) v5); |
universe@686 | 406 | |
universe@686 | 407 | { |
universe@686 | 408 | auto iter = cxMapMutIteratorKeys(map); |
universe@686 | 409 | cx_foreach(CxHashKey*, key, iter) { |
universe@691 | 410 | if (reinterpret_cast<char const *>(key->data)[4] == '1') cxIteratorFlagRemoval(iter); |
universe@686 | 411 | } |
universe@686 | 412 | } |
universe@686 | 413 | { |
universe@686 | 414 | auto iter = cxMapMutIteratorValues(map); |
universe@686 | 415 | cx_foreach(char*, v, iter) { |
universe@686 | 416 | if (v[4] == '5') cxIteratorFlagRemoval(iter); |
universe@686 | 417 | } |
universe@686 | 418 | } |
universe@686 | 419 | |
universe@686 | 420 | EXPECT_STREQ(v1, "OK"); |
universe@686 | 421 | EXPECT_STREQ(v2, "OK"); |
universe@686 | 422 | EXPECT_STREQ(v3, "val 3"); |
universe@686 | 423 | EXPECT_STREQ(v4, "OK"); |
universe@686 | 424 | EXPECT_STREQ(v5, "OK"); |
universe@686 | 425 | |
universe@686 | 426 | v1[0] = v2[0] = v4[0] = v5[0] = 'c'; |
universe@686 | 427 | |
universe@686 | 428 | cxMapDestroy(map); |
universe@686 | 429 | |
universe@686 | 430 | EXPECT_STREQ(v1, "cK"); |
universe@686 | 431 | EXPECT_STREQ(v2, "cK"); |
universe@686 | 432 | EXPECT_STREQ(v3, "OK"); |
universe@686 | 433 | EXPECT_STREQ(v4, "cK"); |
universe@686 | 434 | EXPECT_STREQ(v5, "cK"); |
universe@686 | 435 | } |
universe@686 | 436 | |
universe@686 | 437 | TEST(CxHashMap, SimpleDestructor) { |
universe@686 | 438 | CxTestingAllocator allocator; |
universe@686 | 439 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 0); |
universe@686 | 440 | map->simple_destructor = test_simple_destructor; |
universe@686 | 441 | verify_any_destructor(map); |
universe@686 | 442 | EXPECT_TRUE(allocator.verify()); |
universe@686 | 443 | } |
universe@686 | 444 | |
universe@686 | 445 | TEST(CxHashMap, AdvancedDestructor) { |
universe@686 | 446 | CxTestingAllocator allocator; |
universe@686 | 447 | auto map = cxHashMapCreate(&allocator, CX_STORE_POINTERS, 0); |
universe@686 | 448 | map->advanced_destructor = test_advanced_destructor; |
universe@686 | 449 | verify_any_destructor(map); |
universe@686 | 450 | EXPECT_TRUE(allocator.verify()); |
universe@686 | 451 | } |
universe@691 | 452 | |
universe@691 | 453 | TEST(CxHashMap, Generics) { |
universe@691 | 454 | CxTestingAllocator allocator; |
universe@691 | 455 | auto map = test_map_generics_step_1(&allocator); |
universe@691 | 456 | |
universe@691 | 457 | EXPECT_EQ(map->size, 3); |
universe@691 | 458 | EXPECT_STREQ((char *) cxMapGet(map, "test"), "test"); |
universe@691 | 459 | EXPECT_STREQ((char *) cxMapGet(map, "foo"), "bar"); |
universe@691 | 460 | EXPECT_STREQ((char *) cxMapGet(map, "hallo"), "welt"); |
universe@691 | 461 | |
universe@691 | 462 | test_map_generics_step_2(map); |
universe@691 | 463 | |
universe@691 | 464 | EXPECT_EQ(map->size, 2); |
universe@691 | 465 | EXPECT_STREQ((char *) cxMapGet(map, "key"), "value"); |
universe@691 | 466 | EXPECT_STREQ((char *) cxMapGet(map, "foo"), "bar"); |
universe@691 | 467 | |
universe@691 | 468 | test_map_generics_step_3(map); |
universe@691 | 469 | |
universe@691 | 470 | EXPECT_EQ(map->size, 0); |
universe@691 | 471 | |
universe@691 | 472 | cxMapDestroy(map); |
universe@691 | 473 | EXPECT_TRUE(allocator.verify()); |
universe@691 | 474 | } |
universe@706 | 475 | |
universe@706 | 476 | TEST(EmptyMap, Size) { |
universe@706 | 477 | auto map = cxEmptyMap; |
universe@706 | 478 | |
universe@706 | 479 | EXPECT_EQ(map->size, 0); |
universe@706 | 480 | } |
universe@706 | 481 | |
universe@706 | 482 | TEST(EmptyMap, Iterator) { |
universe@706 | 483 | auto map = cxEmptyMap; |
universe@706 | 484 | |
universe@706 | 485 | auto it1 = cxMapIterator(map); |
universe@706 | 486 | auto it2 = cxMapIteratorValues(map); |
universe@706 | 487 | auto it3 = cxMapIteratorKeys(map); |
universe@706 | 488 | auto it4 = cxMapMutIterator(map); |
universe@706 | 489 | auto it5 = cxMapMutIteratorValues(map); |
universe@706 | 490 | auto it6 = cxMapMutIteratorKeys(map); |
universe@706 | 491 | |
universe@706 | 492 | EXPECT_FALSE(cxIteratorValid(it1)); |
universe@706 | 493 | EXPECT_FALSE(cxIteratorValid(it2)); |
universe@706 | 494 | EXPECT_FALSE(cxIteratorValid(it3)); |
universe@706 | 495 | EXPECT_FALSE(cxIteratorValid(it4)); |
universe@706 | 496 | EXPECT_FALSE(cxIteratorValid(it5)); |
universe@706 | 497 | EXPECT_FALSE(cxIteratorValid(it6)); |
universe@706 | 498 | |
universe@706 | 499 | int c = 0; |
universe@706 | 500 | cx_foreach(void*, data, it1) c++; |
universe@706 | 501 | cx_foreach(void*, data, it2) c++; |
universe@706 | 502 | cx_foreach(void*, data, it3) c++; |
universe@706 | 503 | cx_foreach(void*, data, it4) c++; |
universe@706 | 504 | cx_foreach(void*, data, it5) c++; |
universe@706 | 505 | cx_foreach(void*, data, it6) c++; |
universe@706 | 506 | EXPECT_EQ(c, 0); |
universe@706 | 507 | } |
universe@706 | 508 | |
universe@706 | 509 | TEST(EmptyMap, NoOps) { |
universe@706 | 510 | auto map = cxEmptyMap; |
universe@706 | 511 | |
universe@706 | 512 | ASSERT_NO_FATAL_FAILURE(cxMapClear(map)); |
universe@706 | 513 | ASSERT_NO_FATAL_FAILURE(cxMapDestroy(map)); |
universe@706 | 514 | } |
universe@706 | 515 | |
universe@706 | 516 | TEST(EmptyMap, Get) { |
universe@706 | 517 | auto map = cxEmptyMap; |
universe@706 | 518 | |
universe@706 | 519 | CxHashKey key = cx_hash_key_str("test"); |
universe@706 | 520 | EXPECT_EQ(cxMapGet(map, key), nullptr); |
universe@706 | 521 | } |