/** * @file kList.cpp * * @internal * Copyright (C) 2013-2021 by LMI Technologies Inc. * Licensed under the MIT License. * Redistributed files must retain the above copyright notice. */ #include #include #include kBeginClassEx(k, kList) //serialization versions kAddPrivateVersionEx(kList, "kdat6", "6.0.0.0", "kList-0", WriteDat6V0, ReadDat6V0) //special constructors kAddPrivateFrameworkConstructor(kList, ConstructFramework) //virtual methods kAddPrivateVMethod(kList, kObject, VRelease) kAddPrivateVMethod(kList, kObject, VDisposeItems) kAddPrivateVMethod(kList, kObject, VClone) kAddPrivateVMethod(kList, kObject, VHasShared) kAddPrivateVMethod(kList, kObject, VSize) kAddPrivateVMethod(kList, kObject, VAllocTraits) //kCollection interface kAddInterface(kList, kCollection); kAddPrivateIVMethod(kList, kCollection, VGetIterator, CollectionGetIterator) kAddPrivateIVMethod(kList, kCollection, VItemType, CollectionItemType) kAddIVMethod(kList, kCollection, VCount, Count) kAddPrivateIVMethod(kList, kCollection, VHasNext, CollectionHasNext) kAddPrivateIVMethod(kList, kCollection, VNext, CollectionNext) kEndClassEx() kFx(kStatus) kList_Construct(kList* list, kType itemType, kSize initialCapacity, kAlloc allocator) { kAlloc alloc = kAlloc_Fallback(allocator); kStatus status; kCheck(kAlloc_GetObject(alloc, kTypeOf(kList), list)); if (!kSuccess(status = xkList_Init(*list, kTypeOf(kList), itemType, initialCapacity, alloc))) { kAlloc_FreeRef(alloc, list); } return status; } kFx(kStatus) xkList_ConstructFramework(kList* list, kAlloc allocator) { return kList_Construct(list, kNULL, 0, allocator); } kFx(kStatus) xkList_Init(kList list, kType classType, kType itemType, kSize initialCapacity, kAlloc alloc) { kObjR(kList, list); kStatus status = kOK; kCheck(kObject_Init(list, classType, alloc)); obj->blocks = kNULL; obj->free = kNULL; obj->first = kNULL; obj->last = kNULL; obj->count = 0; obj->capacity = 0; kZero(obj->contentField); obj->itemSize = 0; kTry { kTest(xkList_Layout(list, itemType)); kTest(kList_Reserve(list, initialCapacity)); } kCatch(&status) { xkList_VRelease(list); kEndCatch(status); } return kOK; } kFx(kStatus) xkList_VClone(kList list, kList source, kAlloc valueAlloc, kObject context) { kObj(kList, list); kAlloc objectAlloc = kObject_Alloc(list); kObject contentClone = kNULL; kStatus status; kCheck(kList_Allocate(list, kList_ItemType(source), kList_Count(source))); kTry { kListItem item = kList_First(source); while (!kIsNull(item)) { const void* content = xkList_ItemContent(source, item); if (xkList_ItemIsRef(source)) { kTest(kObject_Clone(&contentClone, *(kObject*)content, objectAlloc, valueAlloc, context)); content = &contentClone; } kTest(kList_Add(list, content, kNULL)); contentClone = kNULL; item = kList_Next(source, item); } } kCatch(&status) { kObject_Dispose(contentClone); kEndCatch(status); } return kOK; } kFx(kStatus) xkList_VRelease(kList list) { kCheck(xkList_Deallocate(list)); kCheck(kObject_VRelease(list)); return kOK; } kFx(kStatus) kList_Allocate(kList list, kType itemType, kSize initialCapacity) { kObj(kList, list); kCheck(xkList_Deallocate(list)); obj->free = kNULL; obj->first = kNULL; obj->last = kNULL; obj->count = 0; obj->capacity = 0; obj->contentField.type = kNULL; kCheck(xkList_Layout(list, itemType)); kCheck(kList_Reserve(list, initialCapacity)); return kOK; } kFx(kStatus) kList_Assign(kList list, kList source) { kObjN(kList, sourceObj, source); kListItem item = kNULL; kCheck(kList_Allocate(list, sourceObj->contentField.type, sourceObj->count)); item = kList_First(source); while (!kIsNull(item)) { kCheck(kList_Add(list, xkList_ItemContent(source, item), kNULL)); item = kList_Next(source, item); } return kOK; } kFx(kStatus) xkList_Layout(kList list, kType itemType) { kObj(kList, list); xkStructField nextField, previousField; xkStructField* fields[3]; nextField.type = kTypeOf(kPointer); nextField.offset = offsetof(kListItemStruct, next); nextField.count = 1; fields[0] = &nextField; previousField.type = kTypeOf(kPointer); previousField.offset = offsetof(kListItemStruct, previous); previousField.count = 1; fields[1] = &previousField; obj->contentField.type = kIsNull(itemType) ? kTypeOf(kVoid) : itemType; obj->contentField.offset = kSIZE_NULL; obj->contentField.count = 1; fields[2] = &obj->contentField; kCheck(xkType_LayoutStruct(fields, kCountOf(fields), &obj->itemSize)); return kOK; } kFx(kStatus) xkList_Deallocate(kList list) { kObj(kList, list); while (!kIsNull(obj->blocks)) { kListItemBlock* next = obj->blocks->next; kCheck(kAlloc_Free(kObject_Alloc(list), obj->blocks)); obj->blocks = next; } return kOK; } kFx(kBool) xkList_VHasShared(kList list) { kObj(kList, list); if (kObject_IsShared(list)) { return kTRUE; } else if (xkList_ItemIsRef(list)) { kListItem item = kList_First(list); while (!kIsNull(item)) { kObject content = kList_AsT(list, item, kObject); if (!kIsNull(content) && kObject_HasShared(content)) { return kTRUE; } item = kList_Next(list, item); } } return kFALSE; } kFx(kSize) xkList_VSize(kList list) { kObj(kList, list); kSize size = sizeof(kListClass); kListItemBlock* blockObj = obj->blocks; while (!kIsNull(blockObj)) { size += (kByte*)blockObj->items - (kByte*)blockObj; size += obj->itemSize*blockObj->itemCount; blockObj = blockObj->next; } if (xkList_ItemIsRef(list)) { kListItem item = kList_First(list); while (!kIsNull(item)) { kObject content = kList_AsT(list, item, kObject); if (!kIsNull(content)) { size += kObject_Size(content); } item = kList_Next(list, item); } } return size; } kFx(kAllocTrait) xkList_VAllocTraits(kList list) { kObj(kList, list); kAllocTrait traits = kAlloc_Traits(kObject_Alloc(list)); if (xkList_ItemIsRef(list)) { kListItem item = kList_First(list); while (!kIsNull(item)) { kObject content = kList_AsT(list, item, kObject); if (!kIsNull(content)) { traits |= kObject_AllocTraits(content); } item = kList_Next(list, item); } } return traits; } kFx(kStatus) xkList_WriteDat6V0(kList list, kSerializer serializer) { kObj(kList, list); kListItem item = kList_First(list); kTypeVersion contentVersion; kCheck(kSerializer_WriteType(serializer, obj->contentField.type, &contentVersion)); kCheck(kSerializer_WriteSize(serializer, obj->count)); while (!kIsNull(item)) { kCheck(kSerializer_WriteItems(serializer, obj->contentField.type, contentVersion, xkList_ItemContent(list, item), 1)); item = kList_Next(list, item); } return kOK; } kFx(kStatus) xkList_ReadDat6V0(kList list, kSerializer serializer) { kObj(kList, list); kListItemStruct* itemObj = kNULL; kType contentType; kTypeVersion contentVersion; kSize count = 0; kCheck(kSerializer_ReadType(serializer, &contentType, &contentVersion)); kCheck(kSerializer_ReadSize(serializer, &count)); kCheck(kList_Allocate(list, contentType, count)); for (kSize i = 0; i < count; ++i) { itemObj = obj->free; obj->free = itemObj->next; itemObj->next = kNULL; itemObj->previous = obj->last; if (!kIsNull(itemObj->previous)) { itemObj->previous->next = itemObj; } obj->last = itemObj; if (kIsNull(obj->first)) { obj->first = itemObj; } kItemZero((void*)xkList_ItemContent(list, itemObj), obj->contentField.fieldSize); kCheck(kSerializer_ReadItems(serializer, obj->contentField.type, contentVersion, (void*)xkList_ItemContent(list, itemObj), 1)); } obj->count = count; return kOK; } kFx(kStatus) kList_Reserve(kList list, kSize capacity) { kObj(kList, list); if (capacity > obj->capacity) { kSize itemCount = kMax_(capacity - obj->capacity, xkLIST_MIN_ITEMS_PER_BLOCK); kSize itemOffset = kSize_Align(sizeof(kListItemBlock), kALIGN_ANY); kSize blockSize = kSize_Align(itemOffset + itemCount*obj->itemSize, kALIGN_ANY); kListItemBlock* block = kNULL; kSize i; kCheck(kAlloc_Get(kObject_Alloc(list), blockSize, &block)); block->itemCount = itemCount; block->items = (kListItemStruct*) kPointer_ByteOffset(block, (kSSize)itemOffset); block->next = obj->blocks; obj->blocks = block; for (i = 0; i < itemCount; ++i) { kListItemStruct* item = (kListItemStruct*) kPointer_ItemOffset(&block->items[0], (kSSize)i, obj->itemSize); item->next = obj->free; obj->free = item; } obj->capacity += itemCount; } return kOK; } kFx(kStatus) kList_Add(kList list, const void* itemContent, kListItem* item) { kObj(kList, list); kListItemStruct* itemObj = kNULL; void* contentSlot = kNULL; if (obj->count == obj->capacity) { kCheck(kList_Reserve(list, obj->capacity+1)); } itemObj = obj->free; obj->free = itemObj->next; itemObj->next = kNULL; itemObj->previous = obj->last; if (!kIsNull(itemObj->previous)) { itemObj->previous->next = itemObj; } obj->last = itemObj; if (kIsNull(obj->first)) { obj->first = itemObj; } obj->count++; contentSlot = (void*)xkList_ItemContent(list, itemObj); if (!kIsNull(itemContent)) { kValue_Import(obj->contentField.type, contentSlot, itemContent); } if (!kIsNull(item)) { *item = itemObj; } return kOK; } kFx(kStatus) kList_Insert(kList list, kListItem before, const void* itemContent, kListItem* item) { kObj(kList, list); kListItemStruct* itemObj = kNULL; kListItemStruct* beforeObj = (kListItemStruct*) before; void* contentSlot = kNULL; if (kIsNull(before)) { return kList_Add(list, itemContent, item); } if (obj->count == obj->capacity) { kCheck(kList_Reserve(list, obj->capacity+1)); } itemObj = obj->free; obj->free = itemObj->next; itemObj->next = beforeObj; itemObj->previous = beforeObj->previous; if (!kIsNull(itemObj->previous)) { itemObj->previous->next = itemObj; } beforeObj->previous = itemObj; if (obj->first == beforeObj) { obj->first = itemObj; } obj->count++; contentSlot = (void*)xkList_ItemContent(list, itemObj); if (!kIsNull(itemContent)) { kValue_Import(obj->contentField.type, contentSlot, itemContent); } if (!kIsNull(item)) { *item = itemObj; } return kOK; } kFx(kStatus) kList_Remove(kList list, kListItem item) { kObj(kList, list); kListItemStruct* itemObj = (kListItemStruct*) item; if (!kIsNull(itemObj->previous)) { itemObj->previous->next = itemObj->next; } if (!kIsNull(itemObj->next)) { itemObj->next->previous = itemObj->previous; } if (obj->first == itemObj) { obj->first = itemObj->next; } if (obj->last == itemObj) { obj->last = itemObj->previous; } itemObj->next = obj->free; itemObj->previous = kNULL; obj->free = itemObj; obj->count--; return kOK; } kFx(kStatus) kList_Clear(kList list) { kObj(kList, list); if (obj->count > 0) { obj->last->next = obj->free; obj->free = obj->first; obj->first = kNULL; obj->last = kNULL; obj->count = 0; } return kOK; } kFx(kStatus) kList_Purge(kList list) { kObj(kList, list); if (!kIsNull(obj->contentField.type) && xkList_ItemIsRef(list)) { kListItem item = kList_First(list); while (!kIsNull(item)) { kCheck(kObject_Dispose(kList_AsT(list, item, kObject))); item = kList_Next(list, item); } } kCheck(kList_Clear(list)); return kOK; } kFx(kStatus) kList_SetItem(kList list, kListItem item, const void* content) { kObj(kList, list); void* contentSlot = xkList_ItemContent(list, item); kValue_Import(obj->contentField.type, contentSlot, content); return kOK; } kFx(kStatus) kList_Item(kList list, kListItem item, void* content) { kObj(kList, list); void* contentSlot = xkList_ItemContent(list, item); kItemCopy(content, contentSlot, obj->contentField.fieldSize); return kOK; } kFx(kListItem) kList_FindIndex(kList list, kSize index) { kObj(kList, list); if (index > obj->count) { return kNULL; } else { kListItem it = kList_First(list); kSize i; for (i = 0; i < index; ++i) { it = kList_Next(list, it); } return it; } } kFx(void*) kList_AtIndex(kList list, kSize index) { kListItem item = kList_FindIndex(list, index); return kIsNull(item) ? kNULL : kList_At(list, item); }