/** * @file kMap.h * @brief Declares the kMap class. * * @internal * Copyright (C) 2012-2021 by LMI Technologies Inc. * Licensed under the MIT License. * Redistributed files must retain the above copyright notice. */ #ifndef K_API_MAP_H #define K_API_MAP_H #include /** * @class kMap * @extends kObject * @ingroup kApi-Data * @brief Represents a collection of key-value pairs stored in a hash table. * * The kMap class represents a hash table of key-value pairs. The kMap constructor accepts kType arguments that determine * the key type and value type. * * The map will automatically grow as new items are inserted. The size of the internal bucket array is always * a power of two and secondary hashing is not performed on keys. This places the burden on the caller to ensure * that keys have roughly uniform distribution, else clustering may occur. If in doubt, consider installing * a custom hash function (via kMap_SetHashFx) to ensure good key distribution. * * The following example illustrates how to construct a kMap instance, populate it, and access elements. * @code {.c} kStatus MapExample() { kMap map = kNULL; kText32 keys[] = { "one", "two", "three", "four", "five" }; k32s values[] = { 1, 2, 3, 4, 5 }; k32s result = 0; kMapItem resultIt = kNULL; //map iterator kSize i; kMapItem it = kNULL; //map iterator kTry { //create a map that can store small char-array keys and 32-bit integer values kTest(kMap_Construct(&map, kTypeOf(kText32), kTypeOf(k32s), 0, kNULL)); //add some initial items to the map for (i = 0; i < kCountOf(keys); ++i) { kTest(kMap_Add(map, &keys[i], &values[i])); } //perform some queries; //kMap_Find looks up a key and returns the value if (kSuccess(kMap_Find(map, "three", &result))) { printf("three: %d\n", result); } //kMap_FindItem looks up a key and returns an iterator to the key-value pair if (kSuccess(kMap_FindItem(map, "four", &resultIt))) { printf("four: %d\n", kMap_ValueAs_(map, resultIt, k32s)); } //print some information about the map printf("Key type: %s\n", kType_Name(kMap_KeyType(map))); printf("Value type: %s\n", kType_Name(kMap_ValueType(map))); printf("Count: %u\n", (k32u) kMap_Count(map)); //print the map items; note, the items will not be printed in sorted order //because kMap is not a sorted collection it = kMap_First(map); while (!kIsNull(it)) { const kChar* key = kMap_Key(map, it); k32s value = *(k32s*) kMap_Value(map, it); printf("key: %s, value: %d\n", key, value); it = kMap_Next(map, it); } } kFinally { kObject_Destroy(map); kEndFinally(); } return kOK; } @endcode * * For maps that contain key or value objects (e.g. kString) as opposed to values (e.g. kText32), * the objects are not automatically destroyed when the map is destroyed. To recursively destroy both the map and its * keys/values, use kObject_Dispose. Similarly, kMap does not automatically dispose key/value objects when they are * replaced or cleared from the map. * @code {.c} kStatus MapOfObjectsExample() { kMap map = kNULL; kString keyIn = kNULL; kImage valueIn = kNULL; kString keyOut = kNULL; kImage valueOut = kNULL; kImage result = kNULL; kMapItem resultIt = kNULL; //map iterator kMapItem it = kNULL; //map iterator kTry { //create a map that can store kString keys and kImage values kTest(kMap_Construct(&map, kTypeOf(kString), kTypeOf(kImage), 0, kNULL)); //add some initial items to the map kTest(kString_Construct(&keyIn, "one", kNULL)); kTest(kImage_Construct(&valueIn, kTypeOf(k8u), 100, 100, kNULL)); kTest(kMap_AddT(map, &keyIn, &valueIn)); keyIn = valueIn = kNULL; kTest(kString_Construct(&keyIn, "two", kNULL)); kTest(kImage_Construct(&valueIn, kTypeOf(k8u), 200, 200, kNULL)); kTest(kMap_AddT(map, &keyIn, &valueIn)); keyIn = valueIn = kNULL; kTest(kString_Construct(&keyIn, "three", kNULL)); kTest(kImage_Construct(&valueIn, kTypeOf(k8u), 300, 300, kNULL)); kTest(kMap_AddT(map, &keyIn, &valueIn)); keyIn = valueIn = kNULL; //perform some queries; //kMap_FindT looks up a key and returns the value if (kSuccess(kMap_FindT(map, "two", &result))) { printf("two: width(%u), height(%u)\n", (k32u)kImage_Width(result), (k32u)kImage_Height(result)); } //kMap_FindItemT looks up a key and returns an iterator to the key-value pair if (kSuccess(kMap_FindItemT(map, "one", &resultIt))) { kImage image = kMap_ValueAsT(map, resultIt, kImage); printf("one: width(%u), height(%u)\n", (k32u)kImage_Width(image), (k32u)kImage_Height(image)); } //there are several ways to replace a key/value pair, depending on whether it is already known //that the key is in the collection, and whether the key/value need to be destroyed/disposed; //the approach below first removes and destroys the key/value if they exist, then adds replacements if (kSuccess(kMap_RemoveT(map, "one", &keyOut, &valueOut))) { kObject_Destroy(keyOut); kObject_Destroy(valueOut); } kTest(kString_Construct(&keyIn, "one", kNULL)); kTest(kImage_Construct(&valueIn, kTypeOf(k8u), 1000, 1000, kNULL)); kTest(kMap_AddT(map, &keyIn, &valueIn)); keyIn = valueIn = kNULL; } kFinally { //clean up temp variables, in case of error above kObject_Destroy(keyIn); kObject_Destroy(valueIn); //dispose map; will destroy internal key/value objects kObject_Dispose(map); kEndFinally(); } return kOK; } @endcode * * kMap supports the kObject_Clone, kObject_Dispose, and kObject_Size methods. * * kMap supports the kdat6 serialization protocol. * */ //typedef kObject kMap; --forward-declared in kApiDef.x.h /** * Represents a key-value pair within a map. * @typedef kPointer kMapItem * @relates kMap */ typedef kPointer kMapItem; #include /** * Constructs a kMap object. * * @public @memberof kMap * @param map Map object. * @param keyType Type of map key. * @param valueType Type of map value. * @param initialCapacity Capacity initially reserved for map items. * @param allocator Memory allocator. * @return Operation status. */ kFx(kStatus) kMap_Construct(kMap* map, kType keyType, kType valueType, kSize initialCapacity, kAlloc allocator); /** * Reallocates the map. * * Existing items are discarded. * * @public @memberof kMap * @param map Map object. * @param keyType Type of map key. * @param valueType Type of map value. * @param initialCapacity Capacity initially reserved for map items. * @return Operation status. */ kFx(kStatus) kMap_Allocate(kMap map, kType keyType, kType valueType, kSize initialCapacity); /** * Performs a shallow copy of the source map. * * Source key-value pairs are copied by value; if the source map contains objects, the object * handles are copied but the objects are not cloned. * * @public @memberof kMap * @param map Map object. * @param source Map to be copied. * @return Operation status. */ kFx(kStatus) kMap_Assign(kMap map, kMap source); /** * Sets a custom key equality comparator. * * @public @memberof kMap * @param map Map object. * @param function Key equality function (or kNULL to unset). * @return Operation status. */ kInlineFx(kStatus) kMap_SetEqualsFx(kMap map, kEqualsFx function) { kObj(kMap, map); obj->equalsFx = function; return kOK; } /** * Sets a custom hash code generator. * * Calling this method will cause the existing map keys to be rehashed using * using the new hash function. If changing both the equals function and the hash function, * change equals first, then hash. * * @public @memberof kMap * @param map Map object. * @param function Hash code function (or kNULL to unset). * @return Operation status. */ kInlineFx(kStatus) kMap_SetHashFx(kMap map, kHashFx function) { kObj(kMap, map); obj->hashFx = function; kCheck(xkMap_Rehash(map)); return kOK; } /** * Returns the key type. * * @public @memberof kMap * @param map Map object. * @return Key type. */ kInlineFx(kType) kMap_KeyType(kMap map) { kObj(kMap, map); return obj->keyField.type; } /** * Returns the value type. * * @public @memberof kMap * @param map Map object. * @return Key type. */ kInlineFx(kType) kMap_ValueType(kMap map) { kObj(kMap, map); return obj->valueField.type; } /** * Returns the count of map elements. * * @public @memberof kMap * @param map Map object. * @return Count of elements. */ kInlineFx(kSize) kMap_Count(kMap map) { kObj(kMap, map); return obj->count; } /** * Returns the number of elements for which space has been allocated. * * @public @memberof kMap * @param map Map object. * @return Map capacity, in elements. */ kInlineFx(kSize) kMap_Capacity(kMap map) { kObj(kMap, map); return obj->capacity; } /** * Finds the value associated with the given key. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key to be found. * @param value Optionally receives value (can be kNULL). * @return kOK if found; kERROR_NOT_FOUND if not found. */ kFx(kStatus) kMap_Find(kMap map, const void* key, void* value); /** * Finds the value associated with the given key. * * A debug assertion will be raised if the size of the specified key or value type is not equal to the * size of the collection key or value type, respectively. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key that should be found. * @param VPtr_value Strongly-typed pointer to destination for found value. * @return kOK if found; kERROR_NOT_FOUND if not found. */ #define kMap_FindT(kMap_map, KPtr_key, VPtr_value) \ xkMap_FindT(kMap_map, KPtr_key, VPtr_value, sizeof(*(KPtr_key)), sizeof(*(VPtr_value))) /** * Reports whether the specified key is present in the map. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @return kTRUE if found. */ kInlineFx(kBool) kMap_Has(kMap map, const void* key) { return kSuccess(kMap_Find(map, key, kNULL)); } /** * Reports whether the specified key is present in the map. * * A debug assertion will be raised if the size of the specified key type is not equal to the * size of the collection key type. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key. * @return kTRUE if found. */ #define kMap_HasT(kMap_map, KPtr_key) \ xkMap_HasT(kMap_map, KPtr_key, sizeof(*(KPtr_key))) /** * Adds a new key-value pair. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @param value Pointer to value. * @return kOK if added; kERROR_ALREADY_EXISTS if key already present. */ kFx(kStatus) kMap_Add(kMap map, const void* key, const void* value); /** * Adds a new key-value pair. * * A debug assertion will be raised if the size of the specified key or value type is not equal to the * size of the collection key or value type, respectively. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key. * @param VPtr_value Strongly-typed pointer to value. * @return kOK if added; kERROR_ALREADY_EXISTS if key already present. */ #define kMap_AddT(kMap_map, KPtr_key, VPtr_value) \ xkMap_AddT(kMap_map, KPtr_key, VPtr_value, sizeof(*(KPtr_key)), sizeof(*(VPtr_value))) /** * Adds or replaces a key-value pair. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * consider using kMap_Remove to first remove and dispose the existing item, then kMap_Add * to add the new item. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @param value Pointer to value. * @return Operation status. */ kFx(kStatus) kMap_Replace(kMap map, const void* key, const void* value); /** * Adds or replaces a key-value pair. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * consider using kMap_Remove to first remove and dispose the existing item, then kMap_Add * to add the new item. * * A debug assertion will be raised if the size of the specified key or value type is not equal to the * size of the collection key or value type, respectively. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key. * @param VPtr_value Strongly-typed pointer to value. * @return Operation status. */ #define kMap_ReplaceT(kMap_map, KPtr_key, VPtr_value) \ xkMap_ReplaceT(kMap_map, KPtr_key, VPtr_value, sizeof(*(KPtr_key)), sizeof(*(VPtr_value))) /** * Removes a key-value pair from the map, optionally returning the old key and/or value. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * use the oldKey and/or oldValue parameters to receive the previous object(s) and then dispose * them. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @param oldKey Optionally receives key (can be kNULL). * @param oldValue Optionally receives value (can be kNULL). * @return kOK if removed; kERROR_NOT_FOUND if key not found. */ kFx(kStatus) kMap_Remove(kMap map, const void* key, void* oldKey, void* oldValue); /** * Removes a key-value pair from the map, returning the old key and/or value. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * use the oldKey and/or oldValue parameters to receive the previous object(s) and then dispose * them. * * A debug assertion will be raised if the size of the specified key or value type is not equal to the * size of the collection key or value type, respectively. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Pointer to key. * @param KPtr_oldKey Strongly-typed destination pointer to receive old key. * @param VPtr_oldValue Strongly-typed destination pointer to receive old value. * @return kOK if removed; kERROR_NOT_FOUND if key not found. */ #define kMap_RemoveT(kMap_map, KPtr_key, KPtr_oldKey, VPtr_oldValue) \ xkMap_RemoveT(kMap_map, KPtr_key, KPtr_oldKey, VPtr_oldValue, sizeof(*(KPtr_key)), sizeof(*(KPtr_oldKey)), sizeof(*(VPtr_oldValue))) /** * Removes a key-value pair from the map. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * kMap_Remove can be used instead to receive the old keys/values. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @return kOK if removed; kERROR_NOT_FOUND if key not found. */ kInlineFx(kStatus) kMap_Discard(kMap map, const void* key) { return kMap_Remove(map, key, kNULL, kNULL); } /** * Removes a key-value pair from the map. * * For object-based keys or values, the old objects are not automatically disposed. In this case, * use kMap_Remove instead to access the old keys/values. * * A debug assertion will be raised if the size of the specified key type is not equal to the * size of the collection key type. * * @relates @memberof kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key. * @return kOK if removed; kERROR_NOT_FOUND if key not found. */ #define kMap_DiscardT(kMap_map, KPtr_key) \ xkMap_DiscardT(kMap_map, KPtr_key, sizeof(*(KPtr_key))) /** * Ensures that capacity is reserved for at least the specified number of map items. * * @public @memberof kMap * @param map Map object. * @param capacity Map capacity, in items. * @return kOK if removed; kERROR_NOT_FOUND if key not found. */ kFx(kStatus) kMap_Reserve(kMap map, kSize capacity); /** * Sets the count of map items to zero. * * @public @memberof kMap * @param map Map object. * @return Operation status. */ kFx(kStatus) kMap_Clear(kMap map); /** * Disposes any elements in the map and sets the count of map items to zero. * * @public @memberof kMap * @param map Map object. * @return Operation status. */ kFx(kStatus) kMap_Purge(kMap map); /** * Gets a reference to the first map item (key-value pair). * * @public @memberof kMap * @param map Map object. * @return First map item, or kNULL. */ kFx(kMapItem) kMap_First(kMap map); /** * Given a map item, gets a reference to the next map item. * * @public @memberof kMap * @param map Map object. * @param item Map item. * @return Next map item, or kNULL. */ kFx(kMapItem) kMap_Next(kMap map, kMapItem item); /** * Finds the map item associated with the given key. * * @public @memberof kMap * @param map Map object. * @param key Pointer to key. * @param item Optionally receives map item (can be kNULL). * @return kOK if found; kERROR_NOT_FOUND if not found. */ kFx(kStatus) kMap_FindItem(kMap map, const void* key, kMapItem* item); /** * Finds the map item associated with the given key. * * A debug assertion will be raised if the size of the specified key type is not equal to the * size of the collection key type. * * @relates kMap * @param kMap_map Map object. * @param KPtr_key Strongly-typed pointer to key. * @param kMapItemPtr_item Optionally receives map item (can be kNULL). * @return kOK if found; kERROR_NOT_FOUND if not found. */ #define kMap_FindItemT(kMap_map, KPtr_key, kMapItemPtr_item) \ xkMap_FindItemT(kMap_map, KPtr_key, kMapItemPtr_item, sizeof(*(KPtr_key))) /** * Removes an item from the map. * * @public @memberof kMap * @param map Map object. * @param item Map item. * @return Operation status. */ kFx(kStatus) kMap_RemoveItem(kMap map, kMapItem item); /** * Returns a pointer to the key associated with a map item. * * @public @memberof kMap * @param map Map object. * @param item Map item. * @return Pointer to key. */ kInlineFx(const void*) kMap_Key(kMap map, kMapItem item) { kObj(kMap, map); return kPointer_ByteOffset(item, (kSSize)obj->keyField.offset); } /** * Returns a strongly-typed pointer to the key associated with a map item. * * A debug assertion will be raised if the size of the specified key type is not equal to the * size of the collection key type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param K Key type identifier (e.g., k32s). * @return Strongly-typed pointer to key. */ #define kMap_KeyT(kMap_map, kMapItem_item, K) \ kCast(K*, xkMap_KeyT(kMap_map, kMapItem_item, sizeof(K))) /** * Gets the key associated with a map item. * * A debug assertion will be raised if the size of the specified key type is not equal to the * size of the collection key type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param K Key type identifier (e.g., k32s). * @return Key content. */ #define kMap_KeyAsT(kMap_map, kMapItem_item, K) \ kPointer_ReadAs(xkMap_KeyAsT(kMap_map, kMapItem_item, sizeof(K)), K) /** * Returns a pointer to the value associated with a map item. * * @public @memberof kMap * @param map Map object. * @param item Map item. * @return Pointer to value. */ kInlineFx(void*) kMap_Value(kMap map, kMapItem item) { kObj(kMap, map); return kPointer_ByteOffset(item, (kSSize)obj->valueField.offset); } /** * Returns a strongly-typed pointer to the value associated with a map item. * * A debug assertion will be raised if the size of the specified value type is not equal to the * size of the collection value type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param V Value type identifier (e.g., k32s). * @return Strongly-typed pointer to value. */ #define kMap_ValueT(kMap_map, kMapItem_item, V) \ kCast(V*, xkMap_ValueT(kMap_map, kMapItem_item, sizeof(V))) /** * Sets the value associated with a map item. * * @public @memberof kMap * @param map Map object. * @param item Map item. * @param value Pointer to value to be copied into the map item. * @return Operation status. */ kInlineFx(kStatus) kMap_SetValue(kMap map, kMapItem item, const void* value) { kObj(kMap, map); void* valueSlot = kMap_Value(map, item); kValue_Import(obj->valueField.type, valueSlot, value); return kOK; } /** * Sets the value associated with a map item. * * A debug assertion will be raised if the size of the specified value type is not equal to the * size of the collection value type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param VPtr_value Strongly-typed pointer to value to be copied into the map item. * @return Operation status. */ #define kMap_SetValueT(kMap_map, kMapItem_item, VPtr_value) \ xkMap_SetValueT(kMap_map, kMapItem_item, VPtr_value, sizeof(*(VPtr_value))) /** * Sets the value associated with a map item. * * A debug assertion will be raised if the size of the specified value type is not equal to the * size of the collection value type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param V_value Item value. * @param V Value type identifier (e.g., k32s). * @return Value content. */ #define kMap_SetValueAsT(kMap_map, kMapItem_item, V_value, V) \ (kPointer_WriteAs(xkMap_ValueAsT(kMap_map, kMapItem_item, sizeof(V)), V_value, V), (void)0) /** * Gets the value associated with a map item. * * A debug assertion will be raised if the size of the specified value type is not equal to the * size of the collection value type. * * @relates kMap * @param kMap_map Map object. * @param kMapItem_item Map item. * @param V Value type identifier (e.g., k32s). * @return Value content. */ #define kMap_ValueAsT(kMap_map, kMapItem_item, V) \ kPointer_ReadAs(xkMap_ValueAsT(kMap_map, kMapItem_item, sizeof(V)), V) #endif