/* Copyright (c) 2023 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "utilities/ChunkArray.h" #include "utilities/IOUtilities.h" #include #include #include #include #include ChunkArray ChunkArray_init(void) { ChunkArray array = {NULL, 0, NULL, 0, 8, 0, 0}; return array; } ChunkArray ChunkArray_copy(ChunkArray * array) { ChunkArray copy = ChunkArray_init(); copy.data = memdup(array->data, array->totalSize); copy.count = array->count; copy.offsets = memdup(array->offsets, array->count * sizeof(*array->offsets)); copy.totalSize = array->totalSize; copy.alignment = array->alignment; copy.allocatedOffsetCount = copy.count; copy.allocatedDataSize = copy.totalSize; return copy; } void ChunkArray_free(ChunkArray * array) { free(array->offsets); free(array->data); } void * ChunkArray_entryAtIndex(ChunkArray * array, size_t index) { if (index >= array->count) { return NULL; } return array->data + array->offsets[index]; } size_t ChunkArray_getEntrySize(ChunkArray * array, size_t index) { if (index >= array->count) { return 0; } size_t offset = array->offsets[index]; size_t bestNextOffset = array->totalSize; for (unsigned int searchIndex = 0; searchIndex < array->count; searchIndex++) { if (array->offsets[searchIndex] > offset && array->offsets[searchIndex] < bestNextOffset) { bestNextOffset = array->offsets[searchIndex]; } } return bestNextOffset - offset; } size_t ChunkArray_indexOfEntry(ChunkArray * array, void * entry) { if (entry < array->data || entry >= array->data + array->totalSize) { return SIZE_MAX; } size_t entryOffset = entry - array->data; size_t bestIndex = 0; size_t bestOffset = array->offsets[0]; for (size_t searchIndex = 1; searchIndex < array->count; searchIndex++) { if (array->offsets[searchIndex] <= entryOffset && array->offsets[searchIndex] > bestOffset) { bestOffset = array->offsets[searchIndex]; bestIndex = searchIndex; } } return bestIndex; } void * ChunkArray_append(ChunkArray * array, const void * data, size_t size) { return ChunkArray_insert(array, array->count, data, size); } void * ChunkArray_insert(ChunkArray * array, size_t index, const void * data, size_t size) { assert(index <= array->count); if (array->allocatedOffsetCount <= array->count) { array->allocatedOffsetCount = array->allocatedOffsetCount * 2 + (array->allocatedOffsetCount == 0) * 16; array->offsets = realloc(array->offsets, array->allocatedOffsetCount * sizeof(*array->offsets)); } for (size_t index2 = array->count; index2 > index; index2--) { array->offsets[index2] = array->offsets[index2 - 1]; } array->offsets[index] = array->totalSize; array->count++; size_t alignedSize = (size + array->alignment - 1) / array->alignment * array->alignment; if (array->allocatedDataSize <= array->totalSize + alignedSize) { array->allocatedDataSize = (array->allocatedDataSize * 2); if (array->allocatedDataSize < array->totalSize + alignedSize) { array->allocatedDataSize += alignedSize; } array->data = realloc(array->data, array->allocatedDataSize); } memset(array->data + array->totalSize, 0, alignedSize); if (data != NULL) { memcpy(array->data + array->totalSize, data, size); } array->totalSize += alignedSize; return array->data + array->totalSize - alignedSize; } void ChunkArray_remove(ChunkArray * array, size_t index) { assert(index < array->count); size_t entryOffset = array->offsets[index]; size_t bestNextEntryOffset = array->totalSize; for (size_t searchIndex = 0; searchIndex < array->count; searchIndex++) { if (array->offsets[searchIndex] > entryOffset && array->offsets[searchIndex] < bestNextEntryOffset) { bestNextEntryOffset = array->offsets[searchIndex]; } } size_t removedEntrySize = bestNextEntryOffset - entryOffset; if (bestNextEntryOffset != SIZE_MAX) { memmove(array->data + entryOffset, array->data + bestNextEntryOffset, array->totalSize - bestNextEntryOffset); for (unsigned int offsetIndex = 0; offsetIndex < array->count; offsetIndex++) { if (array->offsets[offsetIndex] > entryOffset) { array->offsets[offsetIndex] -= removedEntrySize; } } } array->totalSize -= removedEntrySize; array->count--; for (unsigned int offsetIndex = index; offsetIndex < array->count; offsetIndex++) { array->offsets[offsetIndex] = array->offsets[offsetIndex + 1]; } } void ChunkArray_removeAll(ChunkArray * array) { array->count = 0; array->totalSize = 0; }