mirror of https://gitlab.com/nakst/essence
193 lines
7.7 KiB
C++
193 lines
7.7 KiB
C++
// 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.
|
|
|
|
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 <class T, EsHeap *heap>
|
|
#else
|
|
template <class T, EsHeap *heap = nullptr>
|
|
#endif
|
|
struct Array {
|
|
T *array;
|
|
|
|
T &First() { return array[0]; }
|
|
T &Last() { return array[Length() - 1]; }
|
|
void Delete(uintptr_t position) { _ArrayDelete(array, position, sizeof(T), 1); }
|
|
void DeleteSwap(uintptr_t position) { _ArrayDeleteSwap(array, position, sizeof(T)); }
|
|
void DeleteMany(uintptr_t position, size_t count) { _ArrayDelete(array, position, sizeof(T), count); }
|
|
T *Add(T item) { return (T *) _ArrayInsert((void **) &array, &item, sizeof(T), -1, 0, heap); }
|
|
T *Add() { return (T *) _ArrayInsert((void **) &array, nullptr, sizeof(T), -1, 0, heap); }
|
|
T *Insert(T item, uintptr_t position) { return (T *) _ArrayInsert((void **) &array, &item, sizeof(T), position, 0, heap); }
|
|
T *AddPointer(const T *item) { return (T *) _ArrayInsert((void **) &(array), item, sizeof(T), -1, 0, heap); }
|
|
T *InsertPointer(const T *item, uintptr_t position) { return (T *) _ArrayInsert((void **) &array, item, sizeof(T), position, 0, heap); }
|
|
T *InsertMany(uintptr_t position, size_t count) { return (T *) _ArrayInsertMany((void **) &array, sizeof(T), position, count, heap); }
|
|
bool SetLength(size_t length) { return _ArraySetLength((void **) &array, length, sizeof(T), 0, heap); }
|
|
void Free() { _ArrayFree((void **) &array, sizeof(T), heap); }
|
|
T Pop() { T t = Last(); Delete(Length() - 1); return t; }
|
|
|
|
__attribute__((no_instrument_function))
|
|
size_t Length() {
|
|
return array ? ArrayHeader(array)->length : 0;
|
|
}
|
|
|
|
__attribute__((no_instrument_function))
|
|
T &operator[](uintptr_t index) {
|
|
#ifdef DEBUG_BUILD
|
|
EsAssert(index < Length());
|
|
#endif
|
|
return array[index];
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool FindAndDelete(T item, bool failIfNotFound) {
|
|
intptr_t index = Find(item, failIfNotFound);
|
|
if (index == -1) return false;
|
|
Delete(index);
|
|
return true;
|
|
}
|
|
|
|
bool FindAndDeleteSwap(T item, bool failIfNotFound) {
|
|
intptr_t index = Find(item, failIfNotFound);
|
|
if (index == -1) return false;
|
|
DeleteSwap(index);
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
};
|
|
|
|
#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
|