diff --git a/dqn.h b/dqn.h index bd14449..33ec427 100644 --- a/dqn.h +++ b/dqn.h @@ -181,6 +181,7 @@ using f32 = float; } while (0) #define DQN_LOGE(msg, ...) DqnLog(__FILE__, __func__, __LINE__, msg, ##__VA_ARGS__); +#define DQN_LOGD(msg, ...) DqnLog(__FILE__, __func__, __LINE__, msg, ##__VA_ARGS__); // Use macro above. DQN_FILE_SCOPE void DqnLog(char *file, char const *const functionName, i32 const lineNum, char const *const msg, ...); @@ -518,20 +519,26 @@ public: // API // ============================================================================================= - bool Init (i64 const size, DqnMemStack *const stack); - bool Init (i64 const size, DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR); - bool Free (); - bool Resize (i64 const newMax); - bool Grow (); - T *Push (T const *item, i64 const num); + void Init (T *const data_, isize max_, isize count_ = 0); + void Init (DqnMemAPI *const memAPI_ = &DQN_DEFAULT_HEAP_ALLOCATOR); + void Init (DqnMemStack *const stack); + bool InitSize (isize size_, DqnMemAPI *const memAPI_ = &DQN_DEFAULT_HEAP_ALLOCATOR); + bool InitSize (isize size_, DqnMemStack *const stack); + + void Free (); + bool Resize (isize newMax); + bool Grow (isize multiplier = 2); + T *Push (T const *item, isize num); T *Push (T const item); - T *Insert (T const item, i64 index); + T *Insert (T const item, isize index); void Pop (); - T *Get (i64 const index); - void Clear (bool const zeroClear = false); - bool Remove (i64 const index); - bool RemoveStable(i64 const index); - void RemoveStable(i64 *indexList, const i64 numIndexes); + T *Get (isize index); + void Clear (bool zeroClear = false); + bool Remove (isize index); + bool RemoveStable(isize index); + + // indexList: Array of indexes to remove. This list gets sorted. + void RemoveStable(isize *indexList, isize numIndexes); // C++ Iterator // ============================================================================================= @@ -551,98 +558,106 @@ public: }; template -DQN_FILE_SCOPE DqnArray DqnArray_(i64 const size, - DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR) +class DqnSmartArray : public DqnArray { - DqnArray result; - bool init = result.Init(size, api); - DQN_ASSERT(init); + ~DqnSmartArray() { if (data && memAPI) memAPI->Free(data); } +}; + +template +void DqnArray::Init(DqnMemAPI *const memAPI_) +{ + memAPI = memAPI_; + count = max = 0; + data = nullptr; +} + +template +void DqnArray::Init(T *data_, isize max_, isize count_) +{ + DQN_ASSERT(data_); + memAPI = nullptr; + count = count_; + max = max_; + data = data_; +} + +template +void DqnArray::Init(DqnMemStack *const stack) +{ + this->Init(stack->myAPI); +} + +template +bool DqnArray::InitSize(isize size_, DqnMemAPI *const memAPI_) +{ + this->Init(memAPI_); + bool result = this->Resize(size_); return result; } template -DQN_FILE_SCOPE DqnArray DqnArray_(i64 const size, DqnMemStack *const stack) +bool DqnArray::InitSize(isize size_, DqnMemStack *const stack) { - DqnArray result = DqnArray_(size, &stack->myAPI); + bool result = this->InitSize(size_, &stack->myAPI); return result; } -template -DQN_FILE_SCOPE DqnArray DqnArray_(DqnMemAPI *const api = &DQN_DEFAULT_HEAP_ALLOCATOR) -{ - DqnArray result = DqnArray_(0, api); - return result; -} - -template -DQN_FILE_SCOPE DqnArray DqnArray_(DqnMemStack *const stack) -{ - DqnArray result = DqnArray_(0, stack); - return result; -} - -template -bool DqnArray::Init(i64 const size, DqnMemStack *const stack) -{ - bool result = false; - if (stack) - { - result = this->Init(size, &stack->myAPI); - } - - return result; -} - -template -bool DqnArray::Init(i64 const size, DqnMemAPI *const api) -{ - DQN_ASSERT(size >= 0); - - if (size > 0) - { - i64 allocateSize = size * sizeof(T); - this->data = (T *)api->Alloc(allocateSize, /*zeroClear*/ false); - if (!this->data) return false; - } - - this->memAPI = api; - this->count = 0; - this->max = size; - return true; -} - // Implementation taken from Milton, developed by Serge at // https://github.com/serge-rgb/milton#license template -bool DqnArray::Free() +void DqnArray::Free() { if (this->data) { - // TODO(doyle): Right now we assume free always works, and it probably should? - i64 sizeToFree = this->max * sizeof(T); - this->memAPI->Free(this->data, sizeToFree); - this->data = nullptr; + if (this->memAPI) + { + auto sizeToFree = this->max * sizeof(T); + this->memAPI->Free(this->data, sizeToFree); + } + this->data = nullptr; this->count = 0; this->max = 0; - return true; } - - return false; } template -bool DqnArray::Resize(i64 newMax) +bool DqnArray::Resize(isize newMax) { - if (!this->data) return false; - if (!this->memAPI->IsValid()) return false; + if (!this->memAPI) + { + DQN_LOGE("DqnArray has no memory api assigned. Resize to %d items failed.", newMax); + return false; + } - i64 oldSize = this->max * sizeof(T); - i64 newSize = newMax * sizeof(T); + if (newMax == 0) + { + DQN_LOGD( + "DqnArray tried to resize to 0 items. Not allowed? TODO(doyle): Maybe just free the " + "array then?"); + return false; + } + + + if (newMax < this->count) + { + DQN_LOGE( + "DqnArray has %d items but requested a resize to: %d and will destroy the remaining " + "items in the array!", this->count, newMax); + } + + auto oldSize = this->max * sizeof(T); + auto newSize = newMax * sizeof(T); T *result = nullptr; - if (oldSize == 0) result = (T *)this->memAPI->Alloc (newSize, /*zeroClear*/ false); - else result = (T *)this->memAPI->Realloc(this->data, oldSize, newSize); + if (this->data) + { + result = (T *)this->memAPI->Realloc(this->data, oldSize, newSize); + } + else + { + result = (T *)this->memAPI->Alloc(newSize, /*zeroClear*/ false); + } if (result) { @@ -652,36 +667,34 @@ bool DqnArray::Resize(i64 newMax) } else { + DQN_LOGE("DqnArray out of memory error. Failed to request: %d bytes.", newSize); return false; } } template -bool DqnArray::Grow() +bool DqnArray::Grow(isize multiplier) { - if (!this->data) return false; - - const f32 GROWTH_FACTOR = 2.0f; - i64 newMax = (i64)(this->max * GROWTH_FACTOR); - if (newMax == this->max) newMax++; - + isize newMax = this->max * multiplier; + newMax = (newMax == 0) ? 8 : newMax; bool result = this->Resize(newMax); return result; } template -T *DqnArray::Push(const T *item, const i64 num) +T *DqnArray::Push(T const *item, isize num) { - i64 newSize = this->count + num; - if (!this->data || newSize > this->max) + if (!this->data || (this->count + num) >= this->max) { - if (!this->Grow()) return nullptr; + if (!this->Grow()) + { + DQN_LOGE("DqnArray could not push %d item(s) onto array because growing failed.", num); + return nullptr; + } } for (auto i = 0; i < num; i++) - { this->data[this->count++] = item[i]; - } DQN_ASSERT(this->count <= this->max); T *result = this->data + (this->count - 1); @@ -689,33 +702,34 @@ T *DqnArray::Push(const T *item, const i64 num) } template -T *DqnArray::Push(const T item) +T *DqnArray::Push(T const item) { T* result = this->Push(&item, 1); return result; } template -T *DqnArray::Insert(const T item, i64 index) +T *DqnArray::Insert(T const item, isize index) { + index = DQN_MAX(index, 0); if (index >= this->count) { T *result = this->Push(item); return result; } - index = DQN_MAX(index, 0); - i64 newSize = this->count + 1; - if (!this->data || newSize > this->max) + if (!this->data || (this->count + 1) > this->max) { - if (!this->Grow()) return nullptr; + if (!this->Grow()) + { + DQN_LOGE("DqnArray could not insert at index %d because growing failed.", index); + return nullptr; + } } this->count++; for (auto i = this->count - 1; (i - 1) >= index; i--) - { this->data[i] = this->data[i - 1]; - } this->data[index] = item; T *result = this->data + index; @@ -731,7 +745,7 @@ void DqnArray::Pop() } template -T *DqnArray::Get(i64 const index) +T *DqnArray::Get(isize index) { T *result = nullptr; if (index >= 0 && index < this->count) result = &this->data[index]; @@ -739,51 +753,53 @@ T *DqnArray::Get(i64 const index) } template -void DqnArray::Clear(bool const zeroClear) +void DqnArray::Clear(bool zeroClear) { if (zeroClear) { - i64 sizeToClear = sizeof(T) * this->count; + isize sizeToClear = sizeof(T) * this->count; DqnMem_Clear(this->data, 0, sizeToClear); } this->count = 0; } template -bool DqnArray::Remove(i64 const index) +bool DqnArray::Remove(isize index) { - if (index >= this->count || index < 0) return false; + if (index >= this->count || index < 0) + return false; - bool firstElementAndOnlyElement = (index == 0 && this->count == 1); - bool isLastElement = (index == (this->count - 1)); - if (firstElementAndOnlyElement || isLastElement) + bool isLastElement = (index == (this->count - 1)); + if (isLastElement && this->count == 1) { this->count--; - return true; + } + else + { + this->data[index] = this->data[this->count - 1]; + this->count--; } - this->data[index] = this->data[this->count - 1]; - this->count--; return true; } template -bool DqnArray::RemoveStable(i64 const index) +bool DqnArray::RemoveStable(isize index) { - if (index >= this->count || index < 0) return false; + if (index >= this->count || index < 0) + return false; - bool firstElementAndOnlyElement = (index == 0 && this->count == 1); - bool isLastElement = (index == (this->count - 1)); - if (firstElementAndOnlyElement || isLastElement) + bool isLastElement = (index == (this->count - 1)); + if (isLastElement && this->count == 1) { this->count--; return true; } - i64 itemToRemoveByteOffset = index * sizeof(T); - i64 oneAfterItemToRemoveByteOffset = (index + 1) * sizeof(T); - i64 lastItemByteOffset = this->count * sizeof(T); - i64 numBytesToMove = lastItemByteOffset - oneAfterItemToRemoveByteOffset; + auto itemToRemoveByteOffset = index * sizeof(T); + auto oneAfterItemToRemoveByteOffset = (index + 1) * sizeof(T); + auto lastItemByteOffset = this->count * sizeof(T); + auto numBytesToMove = lastItemByteOffset - oneAfterItemToRemoveByteOffset; u8 *bytePtr = (u8 *)this->data; u8 *dest = &bytePtr[itemToRemoveByteOffset]; @@ -795,18 +811,18 @@ bool DqnArray::RemoveStable(i64 const index) } template -void DqnArray::RemoveStable(i64 *indexList, const i64 numIndexes) +void DqnArray::RemoveStable(isize *indexList, isize numIndexes) { if (numIndexes == 0 || !indexList) return; // NOTE: Sort the index list and ensure we only remove indexes up to the size of our array - Dqn_QuickSort(indexList, numIndexes); + Dqn_QuickSort(indexList, numIndexes); - i64 arrayHighestIndex = this->count - 1; - i64 realCount = numIndexes; + auto arrayHighestIndex = this->count - 1; + auto realCount = numIndexes; if (indexList[numIndexes - 1] > arrayHighestIndex) { - i64 realNumIndexes = + auto realNumIndexes = Dqn_BSearch(indexList, numIndexes, arrayHighestIndex, Dqn_BSearchBound_Lower); // NOTE: If -1, then there's no index in the indexlist that is within the range of our array // i.e. no index we can remove without out of array bounds access @@ -830,10 +846,10 @@ void DqnArray::RemoveStable(i64 *indexList, const i64 numIndexes) } else { - i64 indexListIndex = 0; - i64 indexToCopyTo = indexList[indexListIndex++]; - i64 indexToCopyFrom = indexToCopyTo + 1; - i64 deadIndex = indexList[indexListIndex++]; + auto indexListIndex = 0; + auto indexToCopyTo = indexList[indexListIndex++]; + auto indexToCopyFrom = indexToCopyTo + 1; + auto deadIndex = indexList[indexListIndex++]; bool breakLoop = false; for (; @@ -2813,7 +2829,7 @@ DQN_FILE_SCOPE void DqnLog(char *file, char const *const functionName, i32 const va_end(argList); } - char const *const formatStr = "DqnLog:%s:%s,%d: %s"; + char const *const formatStr = "DqnLog:%s:%s,%d: %s\n"; fprintf(stderr, formatStr, file, functionName, lineNum, userMsg); #if defined(DQN_WIN32_PLATFORM) @@ -2851,7 +2867,7 @@ DQN_FILE_SCOPE void DqnLog(char *file, char const *const functionName, i32 const va_end(argList); } - char const *const formatStr = "DqnLog:%s:%s,%d(%s): %s"; + char const *const formatStr = "DqnLog:%s:%s,%d(%s): %s\n"; fprintf(stderr, formatStr, file, functionName, lineNum, expr, userMsg); #if defined(DQN_WIN32_PLATFORM) diff --git a/dqn_unit_test.cpp b/dqn_unit_test.cpp index 3a4553b..5e0f640 100644 --- a/dqn_unit_test.cpp +++ b/dqn_unit_test.cpp @@ -1141,7 +1141,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) DqnArray array = {}; if (1) { - DQN_ASSERT(array.Init(1, memAPI)); + DQN_ASSERT(array.InitSize(1, memAPI)); DQN_ASSERT(array.max >= 1); DQN_ASSERT(array.count == 0); @@ -1219,16 +1219,16 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) LogSuccess("DqnArray(): Test resizing and free"); } } - DQN_ASSERT(array.Free()); + array.Free(); if (1) { - DQN_ASSERT(array.Init(1, memAPI)); + DQN_ASSERT(array.InitSize(1, memAPI)); DQN_ASSERT(array.max >= 1); DQN_ASSERT(array.count == 0); LogSuccess("DqnArray(): Empty array"); } - DQN_ASSERT(array.Free()); + array.Free(); if (1) { @@ -1237,7 +1237,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) DqnV2 c = DqnV2_(5, 6); DqnV2 d = DqnV2_(7, 8); - DQN_ASSERT(array.Init(16, memAPI)); + DQN_ASSERT(array.InitSize(16, memAPI)); DQN_ASSERT(array.Remove(0) == false); DQN_ASSERT(array.max >= 16); DQN_ASSERT(array.count == 0); @@ -1277,7 +1277,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) DQN_ASSERT(array.count == 0); LogSuccess("DqnArray(): Test removal"); } - DQN_ASSERT(array.Free()); + array.Free(); if (1) { @@ -1286,7 +1286,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) DqnV2 c = DqnV2_(5, 6); DqnV2 d = DqnV2_(7, 8); - DQN_ASSERT(array.Init(16, memAPI)); + DQN_ASSERT(array.InitSize(16, memAPI)); DQN_ASSERT(array.Push(a)); DQN_ASSERT(array.Push(b)); @@ -1315,7 +1315,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) LogSuccess("DqnArray(): Test stable removal"); } - DQN_ASSERT(array.Free()); + array.Free(); } if (1) @@ -1327,7 +1327,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 0); @@ -1341,7 +1341,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 4); @@ -1359,7 +1359,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 3); @@ -1376,7 +1376,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 4); @@ -1394,7 +1394,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 3); @@ -1411,7 +1411,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); DQN_ASSERT(array.count == 2); @@ -1427,7 +1427,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); @@ -1444,7 +1444,7 @@ void DqnArray_TestInternal(DqnMemAPI *const memAPI) i32 intList[] = {128, 32, 29, 31}; DqnArray array; - array.Init(DQN_ARRAY_COUNT(intList), memAPI); + array.InitSize(DQN_ARRAY_COUNT(intList), memAPI); array.Push(intList, DQN_ARRAY_COUNT(intList)); array.RemoveStable(indexesToFree, DQN_ARRAY_COUNT(indexesToFree)); @@ -1472,7 +1472,7 @@ void DqnArray_TestRealDataInternal(DqnArray *array) for (auto i = 0; i < array->count; i++) DQN_ASSERT(array->data[i] == buf[i]); - DQN_ASSERT(array->Free()); + array->Free(); free(buf); LogSuccess("DqnArray(): Testing real data"); @@ -1492,18 +1492,18 @@ void DqnArray_Test() { if (1) { - DqnArray array1 = DqnArray_(3); + DqnArray array1; + array1.InitSize(3); DQN_ASSERT(array1.count == 0); DQN_ASSERT(array1.max == 3); array1.Free(); - array1 = DqnArray_(); + array1.InitSize(0); DQN_ASSERT(array1.count == 0); DQN_ASSERT(array1.max == 0); array1.Push('c'); DQN_ASSERT(array1.count == 1); - DQN_ASSERT(array1.max == 1); array1.Free(); LogSuccess("DqnArray(): Testing faux-array constructors DqnArray_()"); @@ -1512,7 +1512,7 @@ void DqnArray_Test() if (1) { DqnArray array = {}; - DQN_ASSERT(array.Init(1)); + DQN_ASSERT(array.InitSize(1)); DqnArray_TestRealDataInternal(&array); } @@ -1525,7 +1525,7 @@ void DqnArray_Test() { auto memGuard0 = stack.TempRegionGuard(); DqnArray array = {}; - DQN_ASSERT(array.Init(1, &stack.myAPI)); + DQN_ASSERT(array.InitSize(1, &stack.myAPI)); DqnArray_TestRealDataInternal(&array); } @@ -1534,7 +1534,7 @@ void DqnArray_Test() { auto memGuard0 = stack.TempRegionGuard(); DqnArray array = {}; - DQN_ASSERT(array.Init(128, &stack.myAPI)); + DQN_ASSERT(array.InitSize(128, &stack.myAPI)); stack.Push(1024); DqnArray_TestRealDataInternal(&array); }