925 lines
23 KiB
C++
925 lines
23 KiB
C++
/**
|
|
* @file kMap.cpp
|
|
*
|
|
* @internal
|
|
* Copyright (C) 2012-2021 by LMI Technologies Inc.
|
|
* Licensed under the MIT License.
|
|
* Redistributed files must retain the above copyright notice.
|
|
*/
|
|
#include <kApi/Data/kMap.h>
|
|
#include <kApi/Data/kCollection.h>
|
|
#include <kApi/Io/kSerializer.h>
|
|
|
|
kBeginClassEx(k, kMap)
|
|
|
|
//serialization versions
|
|
kAddPrivateVersionEx(kMap, "kdat6", "5.7.1.0", "kMap-0", WriteDat6V0, ReadDat6V0)
|
|
|
|
//special constructors
|
|
kAddPrivateFrameworkConstructor(kMap, ConstructFramework)
|
|
|
|
//virtual methods
|
|
kAddPrivateVMethod(kMap, kObject, VRelease)
|
|
kAddPrivateVMethod(kMap, kObject, VDisposeItems)
|
|
kAddPrivateVMethod(kMap, kObject, VClone)
|
|
kAddPrivateVMethod(kMap, kObject, VHasShared)
|
|
kAddPrivateVMethod(kMap, kObject, VSize)
|
|
kAddPrivateVMethod(kMap, kObject, VAllocTraits)
|
|
|
|
kEndClassEx()
|
|
|
|
kFx(kStatus) kMap_Construct(kMap* map, kType keyType, kType valueType, kSize initialCapacity, kAlloc allocator)
|
|
{
|
|
kAlloc alloc = kAlloc_Fallback(allocator);
|
|
kStatus status;
|
|
|
|
kCheck(kAlloc_GetObject(alloc, kTypeOf(kMap), map));
|
|
|
|
if (!kSuccess(status = xkMap_Init(*map, kTypeOf(kMap), keyType, valueType, initialCapacity, alloc)))
|
|
{
|
|
kAlloc_FreeRef(alloc, map);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_ConstructFramework(kMap* map, kAlloc allocator)
|
|
{
|
|
return kMap_Construct(map, kNULL, kNULL, 0, allocator);
|
|
}
|
|
|
|
kFx(kStatus) xkMap_Init(kMap map, kType classType, kType keyType, kType valueType, kSize initialCapacity, kAlloc alloc)
|
|
{
|
|
kObjR(kMap, map);
|
|
kStatus status = kOK;
|
|
|
|
kCheck(kObject_Init(map, classType, alloc));
|
|
|
|
obj->blocks = kNULL;
|
|
obj->free = kNULL;
|
|
obj->buckets = kNULL;
|
|
obj->bucketCount = 0;
|
|
obj->count = 0;
|
|
obj->capacity = 0;
|
|
kZero(obj->keyField);
|
|
kZero(obj->valueField);
|
|
obj->collectionItem = kNULL;
|
|
obj->hashFx = kNULL;
|
|
obj->equalsFx = kNULL;
|
|
|
|
kTry
|
|
{
|
|
kTest(xkMap_Layout(map, keyType, valueType));
|
|
kTest(kMap_Reserve(map, initialCapacity));
|
|
}
|
|
kCatch(&status)
|
|
{
|
|
xkMap_VRelease(map);
|
|
kEndCatch(status);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_VClone(kMap map, kMap source, kAlloc valueAlloc, kObject context)
|
|
{
|
|
kObj(kMap, map);
|
|
kObjN(kMap, sourceObj, source);
|
|
kAlloc objectAlloc = kObject_Alloc(map);
|
|
kObject keyClone = kNULL;
|
|
kObject valueClone = kNULL;
|
|
kStatus status = kOK;
|
|
|
|
kCheck(kMap_Allocate(map, sourceObj->keyField.type, sourceObj->valueField.type, sourceObj->count));
|
|
|
|
obj->hashFx = sourceObj->hashFx;
|
|
obj->equalsFx = sourceObj->equalsFx;
|
|
|
|
kTry
|
|
{
|
|
kMapItem item = kMap_First(source);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
const void* key = kMap_Key(source, item);
|
|
void* value = kMap_Value(source, item);
|
|
|
|
if (xkMap_KeyIsRef(source))
|
|
{
|
|
kTest(kObject_Clone(&keyClone, *(kObject*)key, objectAlloc, valueAlloc, context));
|
|
key = &keyClone;
|
|
}
|
|
if (xkMap_ValueIsRef(source))
|
|
{
|
|
kTest(kObject_Clone(&valueClone, *(kObject*)value, objectAlloc, valueAlloc, context));
|
|
value = &valueClone;
|
|
}
|
|
|
|
kTest(kMap_Add(map, key, value));
|
|
keyClone = valueClone = kNULL;
|
|
|
|
item = kMap_Next(source, item);
|
|
}
|
|
}
|
|
kCatch(&status)
|
|
{
|
|
kObject_Dispose(keyClone);
|
|
kObject_Dispose(valueClone);
|
|
kEndCatch(status);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_VRelease(kMap map)
|
|
{
|
|
kCheck(xkMap_Deallocate(map));
|
|
|
|
kCheck(kObject_VRelease(map));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Allocate(kMap map, kType keyType, kType valueType, kSize initialCapacity)
|
|
{
|
|
kObj(kMap, map);
|
|
kHashFx hashFx = obj->hashFx;
|
|
kEqualsFx equalsFx = obj->equalsFx;
|
|
|
|
kCheck(xkMap_Deallocate(map));
|
|
|
|
obj->free = kNULL;
|
|
obj->bucketCount = 0;
|
|
obj->count = 0;
|
|
obj->capacity = 0;
|
|
obj->keyField.type = kNULL;
|
|
obj->valueField.type = kNULL;
|
|
obj->collectionItem = kNULL;
|
|
obj->hashFx = kNULL;
|
|
obj->equalsFx = kNULL;
|
|
|
|
kCheck(xkMap_Layout(map, keyType, valueType));
|
|
kCheck(kMap_Reserve(map, initialCapacity));
|
|
|
|
if (obj->keyField.type == keyType)
|
|
{
|
|
obj->hashFx = hashFx;
|
|
obj->equalsFx = equalsFx;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Assign(kMap map, kMap source)
|
|
{
|
|
kObj(kMap, map);
|
|
kObjN(kMap, sourceObj, source);
|
|
kMapItem item = kNULL;
|
|
|
|
kCheck(kMap_Allocate(map, sourceObj->keyField.type, sourceObj->valueField.type, sourceObj->count));
|
|
|
|
obj->hashFx = sourceObj->hashFx;
|
|
obj->equalsFx = sourceObj->equalsFx;
|
|
|
|
item = kMap_First(source);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
kCheck(kMap_Add(map, kMap_Key(source, item), kMap_Value(source, item)));
|
|
|
|
item = kMap_Next(source, item);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_Layout(kMap map, kType keyType, kType valueType)
|
|
{
|
|
kObj(kMap, map);
|
|
xkStructField nextField, hashCodeField;
|
|
xkStructField* fields[4];
|
|
|
|
nextField.type = kTypeOf(kPointer);
|
|
nextField.offset = offsetof(kMapItemStruct, next);
|
|
nextField.count = 1;
|
|
fields[0] = &nextField;
|
|
|
|
hashCodeField.type = kTypeOf(kSize);
|
|
hashCodeField.offset = offsetof(kMapItemStruct, hashCode);
|
|
hashCodeField.count = 1;
|
|
fields[1] = &hashCodeField;
|
|
|
|
obj->keyField.type = kIsNull(keyType) ? kTypeOf(kVoid) : keyType;
|
|
obj->keyField.offset = kSIZE_NULL;
|
|
obj->keyField.count = 1;
|
|
fields[2] = &obj->keyField;
|
|
|
|
obj->valueField.type = kIsNull(valueType) ? kTypeOf(kVoid) : valueType;
|
|
obj->valueField.offset = kSIZE_NULL;
|
|
obj->valueField.count = 1;
|
|
fields[3] = &obj->valueField;
|
|
|
|
kCheck(xkType_LayoutStruct(fields, kCountOf(fields), &obj->itemSize));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_Deallocate(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
|
|
while (!kIsNull(obj->blocks))
|
|
{
|
|
kMapItemBlock* next = obj->blocks->next;
|
|
|
|
kCheck(kAlloc_Free(kObject_Alloc(map), obj->blocks));
|
|
|
|
obj->blocks = next;
|
|
}
|
|
|
|
//note: buckets are considered to be part of "control" rather than "data" memory
|
|
kCheck(kObject_FreeMemRef(map, &obj->buckets));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kBool) xkMap_VHasShared(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
|
|
if (kObject_IsShared(map))
|
|
{
|
|
return kTRUE;
|
|
}
|
|
else
|
|
{
|
|
kBool keyIsRef = kType_IsReference(obj->keyField.type);
|
|
kBool valIsRef = kType_IsReference(obj->valueField.type);
|
|
|
|
if (keyIsRef || valIsRef)
|
|
{
|
|
kMapItem item = kMap_First(map);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
if (keyIsRef)
|
|
{
|
|
kObject key = kMap_KeyAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(key) && kObject_HasShared(key))
|
|
{
|
|
return kTRUE;
|
|
}
|
|
}
|
|
|
|
if (valIsRef)
|
|
{
|
|
kObject value = kMap_ValueAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(value) && kObject_HasShared(value))
|
|
{
|
|
return kTRUE;
|
|
}
|
|
}
|
|
|
|
item = kMap_Next(map, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
return kFALSE;
|
|
}
|
|
|
|
kFx(kSize) xkMap_VSize(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize size = sizeof(kMapClass);
|
|
kMapItemBlock* blockObj = obj->blocks;
|
|
kBool keyIsRef = kType_IsReference(obj->keyField.type);
|
|
kBool valIsRef = kType_IsReference(obj->valueField.type);
|
|
|
|
while (!kIsNull(blockObj))
|
|
{
|
|
size += (kByte*)blockObj->items - (kByte*)blockObj;
|
|
size += obj->itemSize*blockObj->itemCount;
|
|
|
|
blockObj = blockObj->next;
|
|
}
|
|
|
|
size += sizeof(kPointer) * obj->bucketCount;
|
|
|
|
if (keyIsRef || valIsRef)
|
|
{
|
|
kMapItem item = kMap_First(map);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
if (keyIsRef)
|
|
{
|
|
kObject key = kMap_KeyAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(key))
|
|
{
|
|
size += kObject_Size(key);
|
|
}
|
|
}
|
|
|
|
if (valIsRef)
|
|
{
|
|
kObject value = kMap_ValueAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(value))
|
|
{
|
|
size += kObject_Size(value);
|
|
}
|
|
}
|
|
|
|
item = kMap_Next(map, item);
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
kFx(kAllocTrait) xkMap_VAllocTraits(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kAllocTrait traits = kAlloc_Traits(kObject_Alloc(map));
|
|
kBool keyIsRef = kType_IsReference(obj->keyField.type);
|
|
kBool valIsRef = kType_IsReference(obj->valueField.type);
|
|
|
|
if (keyIsRef || valIsRef)
|
|
{
|
|
kMapItem item = kMap_First(map);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
if (keyIsRef)
|
|
{
|
|
kObject key = kMap_KeyAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(key))
|
|
{
|
|
traits |= kObject_AllocTraits(key);
|
|
}
|
|
}
|
|
|
|
if (valIsRef)
|
|
{
|
|
kObject value = kMap_ValueAsT(map, item, kObject);
|
|
|
|
if (!kIsNull(value))
|
|
{
|
|
traits |= kObject_AllocTraits(value);
|
|
}
|
|
}
|
|
|
|
item = kMap_Next(map, item);
|
|
}
|
|
}
|
|
|
|
return traits;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_WriteDat6V0(kMap map, kSerializer serializer)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItem item = kMap_First(map);
|
|
kTypeVersion keyVersion, valueVersion;
|
|
|
|
kCheck(kSerializer_WriteType(serializer, obj->keyField.type, &keyVersion));
|
|
kCheck(kSerializer_WriteType(serializer, obj->valueField.type, &valueVersion));
|
|
kCheck(kSerializer_WriteSize(serializer, obj->count));
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
kCheck(kSerializer_WriteItems(serializer, obj->keyField.type, keyVersion, kMap_Key(map, item), 1));
|
|
kCheck(kSerializer_WriteItems(serializer, obj->valueField.type, valueVersion, kMap_Value(map, item), 1));
|
|
|
|
item = kMap_Next(map, item);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_ReadDat6V0(kMap map, kSerializer serializer)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kType keyType, valueType;
|
|
kTypeVersion keyVersion, valueVersion;
|
|
kSize count = 0;
|
|
|
|
kCheck(kSerializer_ReadType(serializer, &keyType, &keyVersion));
|
|
kCheck(kSerializer_ReadType(serializer, &valueType, &valueVersion));
|
|
kCheck(kSerializer_ReadSize(serializer, &count));
|
|
|
|
kCheck(kMap_Allocate(map, keyType, valueType, count));
|
|
|
|
for (kSize i = 0; i < count; ++i)
|
|
{
|
|
itemObj = obj->free;
|
|
obj->free = itemObj->next;
|
|
itemObj->next = obj->buckets[0];
|
|
obj->buckets[0] = itemObj;
|
|
|
|
itemObj->hashCode = 0;
|
|
kItemZero((void*)kMap_Key(map, itemObj), obj->keyField.fieldSize);
|
|
kItemZero(kMap_Value(map, itemObj), obj->valueField.fieldSize);
|
|
|
|
kCheck(kSerializer_ReadItems(serializer, obj->keyField.type, keyVersion, (void*)kMap_Key(map, itemObj), 1));
|
|
kCheck(kSerializer_ReadItems(serializer, obj->valueField.type, valueVersion, kMap_Value(map, itemObj), 1));
|
|
}
|
|
|
|
obj->count = count;
|
|
|
|
kCheck(xkMap_Rehash(map));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Reserve(kMap map, kSize capacity)
|
|
{
|
|
kObj(kMap, map);
|
|
|
|
if (capacity > obj->capacity)
|
|
{
|
|
kCheck(xkMap_ReserveItems(map, capacity));
|
|
kCheck(xkMap_ReserveBuckets(map));
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_ReserveItems(kMap map, kSize capacity)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize itemCount = kMax_(capacity - obj->capacity, xkMAP_MIN_ITEMS_PER_BLOCK);
|
|
kSize itemOffset = kSize_Align(sizeof(kMapItemBlock), kALIGN_ANY);
|
|
kSize blockSize = kSize_Align(itemOffset + itemCount*obj->itemSize, kALIGN_ANY);
|
|
kMapItemBlock* block = kNULL;
|
|
kSize i;
|
|
|
|
kCheck(kAlloc_Get(kObject_Alloc(map), blockSize, &block));
|
|
|
|
block->itemCount = itemCount;
|
|
block->items = (kMapItemStruct*) kPointer_ByteOffset(block, (kSSize)itemOffset);
|
|
|
|
block->next = obj->blocks;
|
|
obj->blocks = block;
|
|
|
|
for (i = 0; i < itemCount; ++i)
|
|
{
|
|
kMapItemStruct* item = (kMapItemStruct*) kPointer_ItemOffset(&block->items[0], (kSSize)i, obj->itemSize);
|
|
|
|
item->next = obj->free;
|
|
obj->free = item;
|
|
}
|
|
|
|
obj->capacity += itemCount;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_ReserveBuckets(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize minBucketCount = xkMAP_CAPACITY_TO_BUCKETS(obj->capacity);
|
|
kSize bucketCount = 1;
|
|
kMapItemStruct** buckets = kNULL;
|
|
kMapItemStruct** tempBuckets = kNULL;
|
|
kSize i;
|
|
|
|
kCheckArgs(minBucketCount < (1U << 31));
|
|
|
|
while (bucketCount < minBucketCount)
|
|
{
|
|
bucketCount *= 2;
|
|
}
|
|
|
|
//note: buckets are considered to be part of "control" rather than "data" memory
|
|
kCheck(kObject_GetMemZero(map, bucketCount*sizeof(kPointer), &buckets));
|
|
|
|
for (i = 0; i < obj->bucketCount; ++i)
|
|
{
|
|
kMapItemStruct* itemObj = obj->buckets[i];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
kMapItemStruct* nextObj = itemObj->next;
|
|
kSize newBucketIndex = xkMap_HashToIndex(itemObj->hashCode, bucketCount);
|
|
|
|
itemObj->next = buckets[newBucketIndex];
|
|
buckets[newBucketIndex] = itemObj;
|
|
|
|
itemObj = nextObj;
|
|
}
|
|
}
|
|
|
|
tempBuckets = obj->buckets;
|
|
obj->buckets = buckets;
|
|
obj->bucketCount = bucketCount;
|
|
|
|
kCheck(kObject_FreeMem(map, tempBuckets));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) xkMap_Rehash(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* items = kNULL;
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kSize i;
|
|
|
|
for (i = 0; i < obj->bucketCount; ++i)
|
|
{
|
|
itemObj = obj->buckets[i];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
kMapItemStruct* nextObj = itemObj->next;
|
|
|
|
itemObj->hashCode = xkMap_HashKey(map, kMap_Key(map, itemObj));
|
|
itemObj->next = items;
|
|
items = itemObj;
|
|
|
|
itemObj = nextObj;
|
|
}
|
|
|
|
obj->buckets[i] = kNULL;
|
|
}
|
|
|
|
itemObj = items;
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
kMapItemStruct* nextObj = itemObj->next;
|
|
kSize bucketIndex = xkMap_BucketIndex(map, itemObj->hashCode);
|
|
|
|
itemObj->next = obj->buckets[bucketIndex];
|
|
obj->buckets[bucketIndex] = itemObj;
|
|
|
|
itemObj = nextObj;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Find(kMap map, const void* key, void* value)
|
|
{
|
|
kObj(kMap, map);
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
kSize hashCode = xkMap_HashKey(map, key);
|
|
kMapItemStruct* itemObj = obj->buckets[xkMap_BucketIndex(map, hashCode)];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (xkMap_KeyEquals(map, key, kMap_Key(map, itemObj)))
|
|
{
|
|
//if the assertion below is encountered, then the map item has been corrupted
|
|
kAssert(hashCode == itemObj->hashCode);
|
|
|
|
if (!kIsNull(value))
|
|
{
|
|
kItemCopy(value, kMap_Value(map, itemObj), obj->valueField.fieldSize);
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Add(kMap map, const void* key, const void* value)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kSize hashCode = xkMap_HashKey(map, key);
|
|
kSize bucketIndex = 0;
|
|
void* keySlot = kNULL;
|
|
void* valueSlot = kNULL;
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
itemObj = obj->buckets[bucketIndex];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (xkMap_KeyEquals(map, key, kMap_Key(map, itemObj)))
|
|
{
|
|
return kERROR_ALREADY_EXISTS;
|
|
}
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
if (obj->count == obj->capacity)
|
|
{
|
|
kCheck(kMap_Reserve(map, obj->capacity+1));
|
|
}
|
|
|
|
itemObj = obj->free;
|
|
obj->free = itemObj->next;
|
|
itemObj->next = kNULL;
|
|
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
|
|
itemObj->next = obj->buckets[bucketIndex];
|
|
obj->buckets[bucketIndex] = itemObj;
|
|
|
|
itemObj->hashCode = hashCode;
|
|
|
|
obj->count++;
|
|
|
|
keySlot = (void*)kMap_Key(map, itemObj);
|
|
valueSlot = kMap_Value(map, itemObj);
|
|
|
|
kValue_Import(obj->keyField.type, keySlot, key);
|
|
kValue_Import(obj->valueField.type, valueSlot, value);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Replace(kMap map, const void* key, const void* value)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kSize hashCode = xkMap_HashKey(map, key);
|
|
kSize bucketIndex = 0;
|
|
void* keySlot = kNULL;
|
|
void* valueSlot = kNULL;
|
|
kBool found = kFALSE;
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
itemObj = obj->buckets[bucketIndex];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (xkMap_KeyEquals(map, key, kMap_Key(map, itemObj)))
|
|
{
|
|
found = kTRUE;
|
|
break;
|
|
}
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
{
|
|
if (obj->count >= obj->capacity)
|
|
{
|
|
kCheck(kMap_Reserve(map, obj->capacity+1));
|
|
}
|
|
|
|
itemObj = obj->free;
|
|
obj->free = itemObj->next;
|
|
itemObj->next = kNULL;
|
|
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
|
|
itemObj->next = obj->buckets[bucketIndex];
|
|
obj->buckets[bucketIndex] = itemObj;
|
|
|
|
itemObj->hashCode = hashCode;
|
|
|
|
obj->count++;
|
|
}
|
|
|
|
keySlot = (void*)kMap_Key(map, itemObj);
|
|
valueSlot = kMap_Value(map, itemObj);
|
|
|
|
kValue_Import(obj->keyField.type, keySlot, key);
|
|
kValue_Import(obj->valueField.type, valueSlot, value);
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Remove(kMap map, const void* key, void* oldKey, void* oldValue)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* previousObj = kNULL;
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kSize hashCode = xkMap_HashKey(map, key);
|
|
kSize bucketIndex = 0;
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
itemObj = obj->buckets[bucketIndex];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (xkMap_KeyEquals(map, key, kMap_Key(map, itemObj)))
|
|
{
|
|
//if the assertion below is encountered, then the map item has been corrupted
|
|
kAssert(hashCode == itemObj->hashCode);
|
|
|
|
if (oldKey) kItemCopy(oldKey, kMap_Key(map, itemObj), obj->keyField.fieldSize);
|
|
if (oldValue) kItemCopy(oldValue, kMap_Value(map, itemObj), obj->valueField.fieldSize);
|
|
|
|
if (kIsNull(previousObj))
|
|
{
|
|
obj->buckets[bucketIndex] = itemObj->next;
|
|
}
|
|
else
|
|
{
|
|
previousObj->next = itemObj->next;
|
|
}
|
|
|
|
itemObj->next = obj->free;
|
|
obj->free = itemObj;
|
|
|
|
obj->count--;
|
|
|
|
return kOK;
|
|
}
|
|
|
|
previousObj = itemObj;
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Clear(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize i;
|
|
|
|
if (obj->count > 0)
|
|
{
|
|
for (i = 0; i < obj->bucketCount; ++i)
|
|
{
|
|
kMapItemStruct* itemObj = obj->buckets[i];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
kMapItemStruct* nextObj = itemObj->next;
|
|
|
|
itemObj->next = obj->free;
|
|
obj->free = itemObj;
|
|
|
|
itemObj = nextObj;
|
|
}
|
|
|
|
obj->buckets[i] = kNULL;
|
|
}
|
|
|
|
obj->count = 0;
|
|
}
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kStatus) kMap_Purge(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
|
|
if (!kIsNull(obj->keyField.type) && !kIsNull(obj->valueField.type))
|
|
{
|
|
kBool keyIsRef = kType_IsReference(obj->keyField.type);
|
|
kBool valIsRef = kType_IsReference(obj->valueField.type);
|
|
|
|
if (keyIsRef || valIsRef)
|
|
{
|
|
kMapItem item = kMap_First(map);
|
|
|
|
while (!kIsNull(item))
|
|
{
|
|
if (keyIsRef) kCheck(kObject_Dispose(kMap_KeyAsT(map, item, kObject)));
|
|
if (valIsRef) kCheck(kObject_Dispose(kMap_ValueAsT(map, item, kObject)));
|
|
|
|
item = kMap_Next(map, item);
|
|
}
|
|
}
|
|
}
|
|
|
|
kCheck(kMap_Clear(map));
|
|
|
|
return kOK;
|
|
}
|
|
|
|
kFx(kMapItem) kMap_First(kMap map)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize i;
|
|
|
|
for (i = 0; i < obj->bucketCount; ++i)
|
|
{
|
|
if (!kIsNull(obj->buckets[i]))
|
|
{
|
|
return obj->buckets[i];
|
|
}
|
|
}
|
|
|
|
return kNULL;
|
|
}
|
|
|
|
kFx(kMapItem) kMap_Next(kMap map, kMapItem item)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* itemObj = (kMapItemStruct*) item;
|
|
|
|
if (!kIsNull(itemObj->next))
|
|
{
|
|
return itemObj->next;
|
|
}
|
|
else
|
|
{
|
|
kSize hashCode = xkMapItem_HashCode(map, item);
|
|
kSize bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
kSize i;
|
|
|
|
for (i = bucketIndex+1; i < obj->bucketCount; ++i)
|
|
{
|
|
if (!kIsNull(obj->buckets[i]))
|
|
{
|
|
return obj->buckets[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
return kNULL;
|
|
}
|
|
|
|
kFx(kStatus) kMap_FindItem(kMap map, const void* key, kMapItem* item)
|
|
{
|
|
kObj(kMap, map);
|
|
kSize hashCode = xkMap_HashKey(map, key);
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
kMapItemStruct* itemObj = obj->buckets[xkMap_BucketIndex(map, hashCode)];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (xkMap_KeyEquals(map, key, kMap_Key(map, itemObj)))
|
|
{
|
|
//if the assertion below is encountered, then the map item has been corrupted
|
|
kAssert(hashCode == itemObj->hashCode);
|
|
|
|
*item = itemObj;
|
|
return kOK;
|
|
}
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
return kERROR_NOT_FOUND;
|
|
}
|
|
|
|
kFx(kStatus) kMap_RemoveItem(kMap map, kMapItem item)
|
|
{
|
|
kObj(kMap, map);
|
|
kMapItemStruct* previousObj = kNULL;
|
|
kMapItemStruct* itemObj = kNULL;
|
|
kSize hashCode = xkMapItem_HashCode(map, item);
|
|
kSize bucketIndex = 0;
|
|
kBool found = kFALSE;
|
|
|
|
if (obj->bucketCount > 0)
|
|
{
|
|
bucketIndex = xkMap_BucketIndex(map, hashCode);
|
|
itemObj = obj->buckets[bucketIndex];
|
|
|
|
while (!kIsNull(itemObj))
|
|
{
|
|
if (itemObj == item)
|
|
{
|
|
found = kTRUE;
|
|
|
|
if (kIsNull(previousObj))
|
|
{
|
|
obj->buckets[bucketIndex] = itemObj->next;
|
|
}
|
|
else
|
|
{
|
|
previousObj->next = itemObj->next;
|
|
}
|
|
|
|
itemObj->next = obj->free;
|
|
obj->free = itemObj;
|
|
|
|
obj->count--;
|
|
|
|
break;
|
|
}
|
|
|
|
previousObj = itemObj;
|
|
itemObj = itemObj->next;
|
|
}
|
|
}
|
|
|
|
return (found) ? kOK : kERROR_NOT_FOUND;
|
|
}
|