// This file is part of the Essence operating system. // It is released under the terms of the MIT license -- see LICENSE.md. // Written by: nakst. #ifndef ARRAY_IMPLEMENTATION_ONLY struct _ArrayHeader { size_t length, allocated; }; bool _ArrayEnsureAllocated(void **array, size_t minimumAllocated, size_t itemSize, uint8_t additionalHeaderBytes, EsHeap *heap); bool _ArraySetLength(void **array, size_t newLength, size_t itemSize, uint8_t additionalHeaderBytes, EsHeap *heap); void _ArrayDelete(void *array, uintptr_t position, size_t itemSize, size_t count); void _ArrayDeleteSwap(void *array, uintptr_t position, size_t itemSize); void *_ArrayInsert(void **array, const void *item, size_t itemSize, ptrdiff_t position, uint8_t additionalHeaderBytes, EsHeap *heap); void *_ArrayInsertMany(void **array, size_t itemSize, ptrdiff_t position, size_t count, EsHeap *heap); void _ArrayFree(void **array, size_t itemSize, EsHeap *heap); #define ArrayHeader(array) ((_ArrayHeader *) (array) - 1) #define ArrayLength(array) ((array) ? (ArrayHeader(array)->length) : 0) #ifdef KERNEL template #else template #endif struct Array { T *array; inline size_t Length() { return array ? ArrayHeader(array)->length : 0; } inline T &First() { return array[0]; } inline T &Last() { return array[Length() - 1]; } inline void Delete(uintptr_t position) { _ArrayDelete(array, position, sizeof(T), 1); } inline void DeleteSwap(uintptr_t position) { _ArrayDeleteSwap(array, position, sizeof(T)); } inline void DeleteMany(uintptr_t position, size_t count) { _ArrayDelete(array, position, sizeof(T), count); } inline T *Add(T item) { return (T *) _ArrayInsert((void **) &array, &item, sizeof(T), -1, 0, heap); } inline T *Add() { return (T *) _ArrayInsert((void **) &array, nullptr, sizeof(T), -1, 0, heap); } inline T *Insert(T item, uintptr_t position) { return (T *) _ArrayInsert((void **) &array, &item, sizeof(T), position, 0, heap); } inline T *AddPointer(const T *item) { return (T *) _ArrayInsert((void **) &(array), item, sizeof(T), -1, 0, heap); } inline T *InsertPointer(const T *item, uintptr_t position) { return (T *) _ArrayInsert((void **) &array, item, sizeof(T), position, 0, heap); } inline T *InsertMany(uintptr_t position, size_t count) { return (T *) _ArrayInsertMany((void **) &array, sizeof(T), position, count, heap); } inline bool SetLength(size_t length) { return _ArraySetLength((void **) &array, length, sizeof(T), 0, heap); } inline void Free() { _ArrayFree((void **) &array, sizeof(T), heap); } inline T Pop() { T t = Last(); Delete(Length() - 1); return t; } inline T &operator[](uintptr_t index) { return array[index]; } inline intptr_t Find(T item, bool failIfNotFound) { for (uintptr_t i = 0; i < Length(); i++) { if (array[i] == item) { return i; } } if (failIfNotFound) EsPanic("Array::Find - Item not found in %x.\n", this); return -1; } inline void FindAndDelete(T item, bool failIfNotFound) { intptr_t index = Find(item, failIfNotFound); if (index == -1) return; Delete(index); } inline void FindAndDeleteSwap(T item, bool failIfNotFound) { intptr_t index = Find(item, failIfNotFound); if (index == -1) return; DeleteSwap(index); } inline void AddFast(T item) { if (!array) { Add(item); return; } _ArrayHeader *header = ArrayHeader(array); if (header->length == header->allocated) { Add(item); return; } array[header->length++] = item; } }; #endif #ifndef ARRAY_DEFINITIONS_ONLY bool _ArrayMaybeInitialise(void **array, size_t itemSize, EsHeap *heap) { if (*array) return true; size_t newLength = 4; _ArrayHeader *header = (_ArrayHeader *) EsHeapAllocate(sizeof(_ArrayHeader) + itemSize * newLength, true, heap); if (!header) return false; header->length = 0; header->allocated = newLength; *array = header + 1; return true; } bool _ArrayEnsureAllocated(void **array, size_t minimumAllocated, size_t itemSize, uint8_t additionalHeaderBytes, EsHeap *heap) { if (!_ArrayMaybeInitialise(array, itemSize, heap)) { return false; } _ArrayHeader *oldHeader = ArrayHeader(*array); if (oldHeader->allocated >= minimumAllocated) { return true; } _ArrayHeader *newHeader = (_ArrayHeader *) EsHeapReallocate((uint8_t *) oldHeader - additionalHeaderBytes, sizeof(_ArrayHeader) + additionalHeaderBytes + itemSize * minimumAllocated, false, heap); if (!newHeader) { return false; } newHeader->allocated = minimumAllocated; *array = (uint8_t *) (newHeader + 1) + additionalHeaderBytes; return true; } bool _ArraySetLength(void **array, size_t newLength, size_t itemSize, uint8_t additionalHeaderBytes, EsHeap *heap) { if (!_ArrayMaybeInitialise(array, itemSize, heap)) { return false; } _ArrayHeader *header = ArrayHeader(*array); if (header->allocated >= newLength) { header->length = newLength; return true; } if (!_ArrayEnsureAllocated(array, header->allocated * 2 > newLength ? header->allocated * 2 : newLength + 16, itemSize, additionalHeaderBytes, heap)) { return false; } header = ArrayHeader(*array); header->length = newLength; return true; } void _ArrayDelete(void *array, uintptr_t position, size_t itemSize, size_t count) { if (!count) return; size_t oldArrayLength = ArrayLength(array); if (position >= oldArrayLength) EsPanic("_ArrayDelete - Position out of bounds (%d/%d).\n", position, oldArrayLength); if (count > oldArrayLength - position) EsPanic("_ArrayDelete - Count out of bounds (%d/%d/%d).\n", position, count, oldArrayLength); ArrayHeader(array)->length = oldArrayLength - count; uint8_t *data = (uint8_t *) array; EsMemoryMove(data + itemSize * (position + count), data + itemSize * oldArrayLength, ES_MEMORY_MOVE_BACKWARDS itemSize * count, false); } void _ArrayDeleteSwap(void *array, uintptr_t position, size_t itemSize) { size_t oldArrayLength = ArrayLength(array); if (position >= oldArrayLength) EsPanic("_ArrayDeleteSwap - Position out of bounds (%d/%d).\n", position, oldArrayLength); ArrayHeader(array)->length = oldArrayLength - 1; uint8_t *data = (uint8_t *) array; EsMemoryCopy(data + itemSize * position, data + itemSize * ArrayLength(array), itemSize); } void *_ArrayInsert(void **array, const void *item, size_t itemSize, ptrdiff_t position, uint8_t additionalHeaderBytes, EsHeap *heap) { size_t oldArrayLength = ArrayLength(*array); if (position == -1) position = oldArrayLength; if (position < 0 || (size_t) position > oldArrayLength) EsPanic("_ArrayInsert - Position out of bounds (%d/%d).\n", position, oldArrayLength); if (!_ArraySetLength(array, oldArrayLength + 1, itemSize, additionalHeaderBytes, heap)) return nullptr; uint8_t *data = (uint8_t *) *array; EsMemoryMove(data + itemSize * position, data + itemSize * oldArrayLength, itemSize, false); if (item) EsMemoryCopy(data + itemSize * position, item, itemSize); else EsMemoryZero(data + itemSize * position, itemSize); return data + itemSize * position; } void *_ArrayInsertMany(void **array, size_t itemSize, ptrdiff_t position, size_t insertCount, EsHeap *heap) { size_t oldArrayLength = ArrayLength(*array); if (position == -1) position = oldArrayLength; if (position < 0 || (size_t) position > oldArrayLength) EsPanic("_ArrayInsertMany - Position out of bounds (%d/%d).\n", position, oldArrayLength); if (!_ArraySetLength(array, oldArrayLength + insertCount, itemSize, 0, heap)) return nullptr; uint8_t *data = (uint8_t *) *array; EsMemoryMove(data + itemSize * position, data + itemSize * oldArrayLength, itemSize * insertCount, false); return data + itemSize * position; } void _ArrayFree(void **array, size_t itemSize, EsHeap *heap) { if (!(*array)) return; EsHeapFree(ArrayHeader(*array), sizeof(_ArrayHeader) + itemSize * ArrayHeader(*array)->allocated, heap); *array = nullptr; } #endif