Files
handleaiuinew/env/LMI/include/kApi/Data/kMap.cpp
T
2024-05-18 23:53:26 +08:00

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;
}