Merge branch 'fix/collections-maintenance' of https://github.com/mtszkarbowiak/FlaxEngine into mtszkarbowiak-fix/collections-maintenance

This commit is contained in:
Wojtek Figat
2024-11-05 11:12:21 +01:00
8 changed files with 215 additions and 206 deletions

View File

@@ -17,15 +17,15 @@ API_CLASS(InBuild) class Array
{
friend Array;
public:
typedef T ItemType;
typedef typename AllocationType::template Data<T> AllocationData;
using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>;
private:
int32 _count;
int32 _capacity;
AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromCount, int32 fromCapacity)
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromCount, const int32 fromCapacity)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
@@ -52,7 +52,7 @@ public:
/// Initializes a new instance of the <see cref="Array"/> class.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
Array(int32 capacity)
explicit Array(const int32 capacity)
: _count(0)
, _capacity(capacity)
{
@@ -66,7 +66,7 @@ public:
/// <param name="initList">The initial values defined in the array.</param>
Array(std::initializer_list<T> initList)
{
_count = _capacity = (int32)initList.size();
_count = _capacity = static_cast<int32>(initList.size());
if (_count > 0)
{
_allocation.Allocate(_count);
@@ -79,7 +79,7 @@ public:
/// </summary>
/// <param name="data">The initial data.</param>
/// <param name="length">The amount of items.</param>
Array(const T* data, int32 length)
Array(const T* data, const int32 length)
{
ASSERT(length >= 0);
_count = _capacity = length;
@@ -160,8 +160,8 @@ public:
Clear();
if (initList.size() > 0)
{
EnsureCapacity((int32)initList.size());
_count = (int32)initList.size();
EnsureCapacity(static_cast<int32>(initList.size()));
_count = static_cast<int32>(initList.size());
Memory::ConstructItems(_allocation.Get(), initList.begin(), _count);
}
return *this;
@@ -255,7 +255,7 @@ public:
/// </summary>
/// <param name="index">The index.</param>
/// <returns><c>true</c> if is valid a index; otherwise, <c>false</c>.</returns>
bool IsValidIndex(int32 index) const
bool IsValidIndex(const int32 index) const
{
return index < _count && index >= 0;
}
@@ -280,7 +280,7 @@ public:
/// Gets item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE T& At(int32 index)
FORCE_INLINE T& At(const int32 index)
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[index];
@@ -290,7 +290,7 @@ public:
/// Gets item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE const T& At(int32 index) const
FORCE_INLINE const T& At(const int32 index) const
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[index];
@@ -300,7 +300,7 @@ public:
/// Gets or sets the item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE T& operator[](int32 index)
FORCE_INLINE T& operator[](const int32 index)
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[index];
@@ -310,7 +310,7 @@ public:
/// Gets the item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE const T& operator[](int32 index) const
FORCE_INLINE const T& operator[](const int32 index) const
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[index];
@@ -406,7 +406,7 @@ public:
/// </summary>
/// <param name="capacity">The new capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void SetCapacity(const int32 capacity, bool preserveContents = true)
void SetCapacity(const int32 capacity, const bool preserveContents = true)
{
if (capacity == _capacity)
return;
@@ -422,7 +422,7 @@ public:
/// </summary>
/// <param name="size">The new collection size.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data.</param>
void Resize(int32 size, bool preserveContents = true)
void Resize(const int32 size, const bool preserveContents = true)
{
if (_count > size)
{
@@ -441,7 +441,7 @@ public:
/// </summary>
/// <param name="minCapacity">The minimum capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true)
{
if (_capacity < minCapacity)
{
@@ -466,7 +466,7 @@ public:
/// </summary>
/// <param name="data">The data.</param>
/// <param name="count">The amount of items.</param>
void Set(const T* data, int32 count)
void Set(const T* data, const int32 count)
{
EnsureCapacity(count, false);
Memory::DestructItems(_allocation.Get(), _count);
@@ -482,7 +482,7 @@ public:
{
EnsureCapacity(_count + 1);
Memory::ConstructItems(_allocation.Get() + _count, &item, 1);
_count++;
++_count;
}
/// <summary>
@@ -493,7 +493,7 @@ public:
{
EnsureCapacity(_count + 1);
Memory::MoveItems(_allocation.Get() + _count, &item, 1);
_count++;
++_count;
}
/// <summary>
@@ -501,7 +501,7 @@ public:
/// </summary>
/// <param name="items">The items to add.</param>
/// <param name="count">The items count.</param>
void Add(const T* items, int32 count)
void Add(const T* items, const int32 count)
{
EnsureCapacity(_count + count);
Memory::ConstructItems(_allocation.Get() + _count, items, count);
@@ -532,7 +532,7 @@ public:
/// Adds the given amount of items to the collection.
/// </summary>
/// <param name="count">The items count.</param>
FORCE_INLINE void AddDefault(int32 count = 1)
FORCE_INLINE void AddDefault(const int32 count = 1)
{
EnsureCapacity(_count + count);
Memory::ConstructItems(_allocation.Get() + _count, count);
@@ -543,7 +543,7 @@ public:
/// Adds the given amount of uninitialized items to the collection without calling the constructor.
/// </summary>
/// <param name="count">The items count.</param>
FORCE_INLINE void AddUninitialized(int32 count = 1)
FORCE_INLINE void AddUninitialized(const int32 count = 1)
{
EnsureCapacity(_count + count);
_count += count;
@@ -557,7 +557,7 @@ public:
{
EnsureCapacity(_count + 1);
Memory::ConstructItems(_allocation.Get() + _count, 1);
_count++;
++_count;
return _allocation.Get()[_count - 1];
}
@@ -568,7 +568,7 @@ public:
/// Warning! AddZeroed() will create items without calling the constructor and this is not appropriate for item types that require a constructor to function properly.
/// </remarks>
/// <param name="count">The number of new items to add.</param>
void AddZeroed(int32 count = 1)
void AddZeroed(const int32 count = 1)
{
EnsureCapacity(_count + count);
Platform::MemoryClear(_allocation.Get() + _count, count * sizeof(T));
@@ -580,7 +580,7 @@ public:
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The item to be inserted by copying.</param>
void Insert(int32 index, const T& item)
void Insert(const int32 index, const T& item)
{
ASSERT(index >= 0 && index <= _count);
EnsureCapacity(_count + 1);
@@ -597,7 +597,7 @@ public:
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
/// <param name="item">The item to inserted by moving.</param>
void Insert(int32 index, T&& item)
void Insert(const int32 index, T&& item)
{
ASSERT(index >= 0 && index <= _count);
EnsureCapacity(_count + 1);
@@ -605,7 +605,7 @@ public:
Memory::ConstructItems(data + _count, 1);
for (int32 i = _count - 1; i >= index; i--)
data[i + 1] = MoveTemp(data[i]);
_count++;
++_count;
data[index] = MoveTemp(item);
}
@@ -613,7 +613,7 @@ public:
/// Insert the given item at specified index with keeping items order.
/// </summary>
/// <param name="index">The zero-based index at which item should be inserted.</param>
void Insert(int32 index)
void Insert(const int32 index)
{
ASSERT(index >= 0 && index <= _count);
EnsureCapacity(_count + 1);
@@ -621,7 +621,7 @@ public:
Memory::ConstructItems(data + _count, 1);
for (int32 i = _count - 1; i >= index; i--)
data[i + 1] = data[i];
_count++;
++_count;
}
/// <summary>
@@ -661,7 +661,7 @@ public:
/// <param name="item">The item to remove.</param>
void RemoveAllKeepOrder(const T& item)
{
for (int32 i = Count() - 1; i >= 0; i--)
for (int32 i = Count() - 1; i >= 0; --i)
{
if (_allocation.Get()[i] == item)
{
@@ -679,14 +679,14 @@ public:
void RemoveAtKeepOrder(const int32 index)
{
ASSERT(index < _count && index >= 0);
_count--;
--_count;
T* data = _allocation.Get();
if (index < _count)
{
T* dst = data + index;
T* src = data + (index + 1);
const int32 count = _count - index;
for (int32 i = 0; i < count; i++)
for (int32 i = 0; i < count; ++i)
dst[i] = MoveTemp(src[i]);
}
Memory::DestructItems(data + _count, 1);
@@ -712,7 +712,7 @@ public:
/// <param name="item">The item to remove.</param>
void RemoveAll(const T& item)
{
for (int32 i = Count() - 1; i >= 0; i--)
for (int32 i = Count() - 1; i >= 0; --i)
{
if (_allocation.Get()[i] == item)
{
@@ -730,7 +730,7 @@ public:
void RemoveAt(const int32 index)
{
ASSERT(index < _count && index >= 0);
_count--;
--_count;
T* data = _allocation.Get();
if (_count)
data[index] = data[_count];
@@ -743,7 +743,7 @@ public:
void RemoveLast()
{
ASSERT(_count > 0);
_count--;
--_count;
Memory::DestructItems(_allocation.Get() + _count, 1);
}
@@ -772,7 +772,7 @@ public:
{
T* data = _allocation.Get();
const int32 count = _count / 2;
for (int32 i = 0; i < count; i++)
for (int32 i = 0; i < count; ++i)
::Swap(data[i], data[_count - i - 1]);
}
@@ -954,7 +954,7 @@ public:
{
}
Iterator(Array const* array, const int32 index)
Iterator(const Array* array, const int32 index)
: _array(const_cast<Array*>(array))
, _index(index)
{
@@ -1052,7 +1052,7 @@ public:
Iterator& operator--()
{
if (_index > 0)
_index--;
--_index;
return *this;
}
@@ -1060,7 +1060,7 @@ public:
{
Iterator temp = *this;
if (_index > 0)
_index--;
--_index;
return temp;
}
};
@@ -1084,7 +1084,7 @@ public:
};
template<typename T, typename AllocationType>
void* operator new(size_t size, Array<T, AllocationType>& array)
void* operator new(const size_t size, Array<T, AllocationType>& array)
{
ASSERT(size == sizeof(T));
const int32 index = array.Count();
@@ -1093,7 +1093,7 @@ void* operator new(size_t size, Array<T, AllocationType>& array)
}
template<typename T, typename AllocationType>
void* operator new(size_t size, Array<T, AllocationType>& array, int32 index)
void* operator new(const size_t size, Array<T, AllocationType>& array, const int32 index)
{
ASSERT(size == sizeof(T));
array.Insert(index);

View File

@@ -14,20 +14,20 @@ API_CLASS(InBuild) class BitArray
{
friend BitArray;
public:
typedef uint64 ItemType;
typedef typename AllocationType::template Data<ItemType> AllocationData;
using ItemType = uint64;
using AllocationData = typename AllocationType::template Data<ItemType>;
private:
int32 _count;
int32 _capacity;
AllocationData _allocation;
FORCE_INLINE static int32 ToItemCount(int32 size)
FORCE_INLINE static int32 ToItemCount(const int32 size)
{
return Math::DivideAndRoundUp<int32>(size, sizeof(ItemType));
}
FORCE_INLINE static int32 ToItemCapacity(int32 size)
FORCE_INLINE static int32 ToItemCapacity(const int32 size)
{
return Math::Max<int32>(Math::DivideAndRoundUp<int32>(size, sizeof(ItemType)), 1);
}
@@ -46,7 +46,7 @@ public:
/// Initializes a new instance of the <see cref="BitArray"/> class.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
BitArray(int32 capacity)
explicit BitArray(const int32 capacity)
: _count(0)
, _capacity(capacity)
{
@@ -200,7 +200,7 @@ public:
/// </summary>
/// <param name="index">The index of the item.</param>
/// <returns>The value of the item.</returns>
FORCE_INLINE bool operator[](int32 index) const
FORCE_INLINE bool operator[](const int32 index) const
{
return Get(index);
}
@@ -210,7 +210,7 @@ public:
/// </summary>
/// <param name="index">The index of the item.</param>
/// <returns>The value of the item.</returns>
bool Get(int32 index) const
bool Get(const int32 index) const
{
ASSERT(index >= 0 && index < _count);
const ItemType offset = index / sizeof(ItemType);
@@ -224,7 +224,7 @@ public:
/// </summary>
/// <param name="index">The index of the item.</param>
/// <param name="value">The value to set.</param>
void Set(int32 index, bool value)
void Set(const int32 index, const bool value)
{
ASSERT(index >= 0 && index < _count);
const ItemType offset = index / sizeof(ItemType);
@@ -233,7 +233,7 @@ public:
if (value)
item |= bitMask;
else
item &= ~bitMask;
item &= ~bitMask; // Clear the bit
}
public:
@@ -250,7 +250,7 @@ public:
/// </summary>
/// <param name="capacity">The new capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void SetCapacity(const int32 capacity, bool preserveContents = true)
void SetCapacity(const int32 capacity, const bool preserveContents = true)
{
if (capacity == _capacity)
return;
@@ -266,7 +266,7 @@ public:
/// </summary>
/// <param name="size">The new collection size.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data.</param>
void Resize(int32 size, bool preserveContents = true)
void Resize(const int32 size, const bool preserveContents = true)
{
if (_count <= size)
EnsureCapacity(size, preserveContents);
@@ -278,7 +278,7 @@ public:
/// </summary>
/// <param name="minCapacity">The minimum capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true)
{
if (_capacity < minCapacity)
{
@@ -304,7 +304,7 @@ public:
void Add(const bool item)
{
EnsureCapacity(_count + 1);
_count++;
++_count;
Set(_count - 1, item);
}
@@ -313,10 +313,10 @@ public:
/// </summary>
/// <param name="items">The items to add.</param>
/// <param name="count">The items count.</param>
void Add(const bool* items, int32 count)
void Add(const bool* items, const int32 count)
{
EnsureCapacity(_count + count);
for (int32 i = 0; i < count; i++)
for (int32 i = 0; i < count; ++i)
Add(items[i]);
}
@@ -327,7 +327,7 @@ public:
void Add(const BitArray& other)
{
EnsureCapacity(_count, other.Count());
for (int32 i = 0; i < other.Count(); i++)
for (int32 i = 0; i < other.Count(); ++i)
Add(other[i]);
}

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Core/Types/BaseTypes.h"
/// <summary>
/// Tells if the object is occupied, and if not, if the bucket is a subject of compaction.
/// </summary>
enum class BucketState : byte
{
Empty = 0,
Deleted = 1,
Occupied = 2,
};

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Core/Memory/Allocation.h"
#include "Engine/Core/Collections/BucketState.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Collections/Config.h"
@@ -25,34 +26,27 @@ public:
{
friend Dictionary;
enum State : byte
{
Empty = 0,
Deleted = 1,
Occupied = 2,
};
/// <summary>The key.</summary>
KeyType Key;
/// <summary>The value.</summary>
ValueType Value;
private:
State _state;
BucketState _state;
FORCE_INLINE void Free()
{
if (_state == Occupied)
if (_state == BucketState::Occupied)
{
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
_state = Empty;
_state = BucketState::Empty;
}
FORCE_INLINE void Delete()
{
_state = Deleted;
_state = BucketState::Deleted;
Memory::DestructItem(&Key);
Memory::DestructItem(&Value);
}
@@ -62,7 +56,7 @@ public:
{
Memory::ConstructItems(&Key, &key, 1);
Memory::ConstructItem(&Value);
_state = Occupied;
_state = BucketState::Occupied;
}
template<typename KeyComparableType>
@@ -70,7 +64,7 @@ public:
{
Memory::ConstructItems(&Key, &key, 1);
Memory::ConstructItems(&Value, &value, 1);
_state = Occupied;
_state = BucketState::Occupied;
}
template<typename KeyComparableType>
@@ -78,31 +72,31 @@ public:
{
Memory::ConstructItems(&Key, &key, 1);
Memory::MoveItems(&Value, &value, 1);
_state = Occupied;
_state = BucketState::Occupied;
}
FORCE_INLINE bool IsEmpty() const
{
return _state == Empty;
return _state == BucketState::Empty;
}
FORCE_INLINE bool IsDeleted() const
{
return _state == Deleted;
return _state == BucketState::Deleted;
}
FORCE_INLINE bool IsOccupied() const
{
return _state == Occupied;
return _state == BucketState::Occupied;
}
FORCE_INLINE bool IsNotOccupied() const
{
return _state != Occupied;
return _state != BucketState::Occupied;
}
};
typedef typename AllocationType::template Data<Bucket> AllocationData;
using AllocationData = typename AllocationType::template Data<Bucket>;
private:
int32 _elementsCount = 0;
@@ -110,7 +104,7 @@ private:
int32 _size = 0;
AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromSize)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
@@ -127,10 +121,10 @@ private:
Bucket& toBucket = toData[i];
Memory::MoveItems(&toBucket.Key, &fromBucket.Key, 1);
Memory::MoveItems(&toBucket.Value, &fromBucket.Value, 1);
toBucket._state = Bucket::Occupied;
toBucket._state = BucketState::Occupied;
Memory::DestructItem(&fromBucket.Key);
Memory::DestructItem(&fromBucket.Value);
fromBucket._state = Bucket::Empty;
fromBucket._state = BucketState::Empty;
}
}
from.Free();
@@ -149,7 +143,7 @@ public:
/// Initializes a new instance of the <see cref="Dictionary"/> class.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
Dictionary(int32 capacity)
explicit Dictionary(const int32 capacity)
{
SetCapacity(capacity);
}
@@ -289,7 +283,7 @@ public:
{
}
Iterator(Iterator&& i)
Iterator(Iterator&& i) noexcept
: _collection(i._collection)
, _index(i._index)
{
@@ -348,7 +342,7 @@ public:
return *this;
}
Iterator& operator=(Iterator&& v)
Iterator& operator=(Iterator&& v) noexcept
{
_collection = v._collection;
_index = v._index;
@@ -363,8 +357,9 @@ public:
const Bucket* data = _collection->_allocation.Get();
do
{
_index++;
} while (_index != capacity && data[_index].IsNotOccupied());
++_index;
}
while (_index != capacity && data[_index].IsNotOccupied());
}
return *this;
}
@@ -383,8 +378,9 @@ public:
const Bucket* data = _collection->_allocation.Get();
do
{
_index--;
} while (_index > 0 && data[_index].IsNotOccupied());
--_index;
}
while (_index > 0 && data[_index].IsNotOccupied());
}
return *this;
}
@@ -423,7 +419,7 @@ public:
// Insert
ASSERT(pos.FreeSlotIndex != -1);
_elementsCount++;
++_elementsCount;
Bucket& bucket = _allocation.Get()[pos.FreeSlotIndex];
bucket.Occupy(key);
return bucket.Value;
@@ -498,7 +494,7 @@ public:
FindPosition(key, pos);
if (pos.ObjectIndex == -1)
return nullptr;
return (ValueType*)&_allocation.Get()[pos.ObjectIndex].Value;
return const_cast<ValueType*>(&_allocation.Get()[pos.ObjectIndex].Value); //TODO This one is problematic. I think this entire method should be removed.
}
public:
@@ -538,7 +534,7 @@ public:
/// </summary>
/// <param name="capacity">The new capacity.</param>
/// <param name="preserveContents">Enables preserving collection contents during resizing.</param>
void SetCapacity(int32 capacity, bool preserveContents = true)
void SetCapacity(int32 capacity, const bool preserveContents = true)
{
if (capacity == Capacity())
return;
@@ -564,7 +560,7 @@ public:
_allocation.Allocate(capacity);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < capacity; i++)
data[i]._state = Bucket::Empty;
data[i]._state = BucketState::Empty;
}
_size = capacity;
Bucket* oldData = oldAllocation.Get();
@@ -581,8 +577,8 @@ public:
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
bucket->_state = Bucket::Occupied;
_elementsCount++;
bucket->_state = BucketState::Occupied;
++_elementsCount;
}
}
}
@@ -598,7 +594,7 @@ public:
/// </summary>
/// <param name="minCapacity">The minimum required capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
void EnsureCapacity(int32 minCapacity, const bool preserveContents = true)
{
if (_size >= minCapacity)
return;
@@ -682,8 +678,8 @@ public:
if (pos.ObjectIndex != -1)
{
_allocation.Get()[pos.ObjectIndex].Delete();
_elementsCount--;
_deletedCount++;
--_elementsCount;
++_deletedCount;
return true;
}
return false;
@@ -701,8 +697,8 @@ public:
{
ASSERT(_allocation.Get()[i._index].IsOccupied());
_allocation.Get()[i._index].Delete();
_elementsCount--;
_deletedCount++;
--_elementsCount;
++_deletedCount;
return true;
}
return false;
@@ -721,7 +717,7 @@ public:
if (i->Value == value)
{
Remove(i);
result++;
++result;
}
}
return result;
@@ -768,7 +764,7 @@ public:
if (HasItems())
{
const Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
for (int32 i = 0; i < _size; ++i)
{
if (data[i].IsOccupied() && data[i].Value == value)
return true;
@@ -788,7 +784,7 @@ public:
if (HasItems())
{
const Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
for (int32 i = 0; i < _size; ++i)
{
if (data[i].IsOccupied() && data[i].Value == value)
{
@@ -862,14 +858,14 @@ public:
return Iterator(this, _size);
}
const Iterator begin() const
Iterator begin() const
{
Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE const Iterator end() const
FORCE_INLINE Iterator end() const
{
return Iterator(this, _size);
}
@@ -928,7 +924,7 @@ private:
result.ObjectIndex = bucketIndex;
return;
}
checksCount++;
++checksCount;
bucketIndex = (bucketIndex + DICTIONARY_PROB_FUNC(_size, checksCount)) & tableSizeMinusOne;
}
result.ObjectIndex = -1;
@@ -965,7 +961,7 @@ private:
// Fast path if it's empty
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
data[i]._state = Bucket::Empty;
data[i]._state = BucketState::Empty;
}
else
{
@@ -975,7 +971,7 @@ private:
_allocation.Allocate(_size);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
data[i]._state = Bucket::Empty;
data[i]._state = BucketState::Empty;
Bucket* oldData = oldAllocation.Get();
FindPositionResult pos;
for (int32 i = 0; i < _size; i++)
@@ -988,7 +984,7 @@ private:
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
Memory::MoveItems(&bucket->Key, &oldBucket.Key, 1);
Memory::MoveItems(&bucket->Value, &oldBucket.Value, 1);
bucket->_state = Bucket::Occupied;
bucket->_state = BucketState::Occupied;
}
}
for (int32 i = 0; i < _size; i++)

View File

@@ -4,6 +4,7 @@
#include "Engine/Core/Memory/Memory.h"
#include "Engine/Core/Memory/Allocation.h"
#include "Engine/Core/Collections/BucketState.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Collections/Config.h"
@@ -24,29 +25,22 @@ public:
{
friend HashSet;
enum State : byte
{
Empty,
Deleted,
Occupied,
};
/// <summary>The item.</summary>
T Item;
private:
State _state;
BucketState _state;
FORCE_INLINE void Free()
{
if (_state == Occupied)
if (_state == BucketState::Occupied)
Memory::DestructItem(&Item);
_state = Empty;
_state = BucketState::Empty;
}
FORCE_INLINE void Delete()
{
_state = Deleted;
_state = BucketState::Deleted;
Memory::DestructItem(&Item);
}
@@ -54,38 +48,38 @@ public:
FORCE_INLINE void Occupy(const ItemType& item)
{
Memory::ConstructItems(&Item, &item, 1);
_state = Occupied;
_state = BucketState::Occupied;
}
template<typename ItemType>
FORCE_INLINE void Occupy(ItemType&& item)
{
Memory::MoveItems(&Item, &item, 1);
_state = Occupied;
_state = BucketState::Occupied;
}
FORCE_INLINE bool IsEmpty() const
{
return _state == Empty;
return _state == BucketState::Empty;
}
FORCE_INLINE bool IsDeleted() const
{
return _state == Deleted;
return _state == BucketState::Deleted;
}
FORCE_INLINE bool IsOccupied() const
{
return _state == Occupied;
return _state == BucketState::Occupied;
}
FORCE_INLINE bool IsNotOccupied() const
{
return _state != Occupied;
return _state != BucketState::Occupied;
}
};
typedef typename AllocationType::template Data<Bucket> AllocationData;
using AllocationData = typename AllocationType::template Data<Bucket>;
private:
int32 _elementsCount = 0;
@@ -93,7 +87,7 @@ private:
int32 _size = 0;
AllocationData _allocation;
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, int32 fromSize)
FORCE_INLINE static void MoveToEmpty(AllocationData& to, AllocationData& from, const int32 fromSize)
{
if IF_CONSTEXPR (AllocationType::HasSwap)
to.Swap(from);
@@ -102,16 +96,16 @@ private:
to.Allocate(fromSize);
Bucket* toData = to.Get();
Bucket* fromData = from.Get();
for (int32 i = 0; i < fromSize; i++)
for (int32 i = 0; i < fromSize; ++i)
{
Bucket& fromBucket = fromData[i];
if (fromBucket.IsOccupied())
{
Bucket& toBucket = toData[i];
Memory::MoveItems(&toBucket.Item, &fromBucket.Item, 1);
toBucket._state = Bucket::Occupied;
toBucket._state = BucketState::Occupied;
Memory::DestructItem(&fromBucket.Item);
fromBucket._state = Bucket::Empty;
fromBucket._state = BucketState::Empty;
}
}
from.Free();
@@ -130,7 +124,7 @@ public:
/// Initializes a new instance of the <see cref="HashSet"/> class.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
HashSet(int32 capacity)
explicit HashSet(const int32 capacity)
{
SetCapacity(capacity);
}
@@ -252,7 +246,7 @@ public:
{
}
Iterator(HashSet const* collection, const int32 index)
Iterator(const HashSet* collection, const int32 index)
: _collection(const_cast<HashSet*>(collection))
, _index(index)
{
@@ -270,7 +264,7 @@ public:
{
}
Iterator(Iterator&& i)
Iterator(Iterator&& i) noexcept
: _collection(i._collection)
, _index(i._index)
{
@@ -307,7 +301,7 @@ public:
return _index >= 0 && _index < _collection->_size;
}
FORCE_INLINE bool operator !() const
FORCE_INLINE bool operator!() const
{
return !(bool)*this;
}
@@ -329,7 +323,7 @@ public:
return *this;
}
Iterator& operator=(Iterator&& v)
Iterator& operator=(Iterator&& v) noexcept
{
_collection = v._collection;
_index = v._index;
@@ -344,8 +338,9 @@ public:
const Bucket* data = _collection->_allocation.Get();
do
{
_index++;
} while (_index != capacity && data[_index].IsNotOccupied());
++_index;
}
while (_index != capacity && data[_index].IsNotOccupied());
}
return *this;
}
@@ -364,8 +359,9 @@ public:
const Bucket* data = _collection->_allocation.Get();
do
{
_index--;
} while (_index > 0 && data[_index].IsNotOccupied());
--_index;
}
while (_index > 0 && data[_index].IsNotOccupied());
}
return *this;
}
@@ -415,7 +411,7 @@ public:
/// </summary>
/// <param name="capacity">New capacity</param>
/// <param name="preserveContents">Enable/disable preserving collection contents during resizing</param>
void SetCapacity(int32 capacity, bool preserveContents = true)
void SetCapacity(int32 capacity, const bool preserveContents = true)
{
if (capacity == Capacity())
return;
@@ -441,7 +437,7 @@ public:
_allocation.Allocate(capacity);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < capacity; i++)
data[i]._state = Bucket::Empty;
data[i]._state = BucketState::Empty;
}
_size = capacity;
Bucket* oldData = oldAllocation.Get();
@@ -457,7 +453,7 @@ public:
ASSERT(pos.FreeSlotIndex != -1);
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
bucket->_state = Bucket::Occupied;
bucket->_state = BucketState::Occupied;
_elementsCount++;
}
}
@@ -474,7 +470,7 @@ public:
/// </summary>
/// <param name="minCapacity">The minimum required capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void EnsureCapacity(int32 minCapacity, bool preserveContents = true)
void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true)
{
if (_size >= minCapacity)
return;
@@ -559,8 +555,8 @@ public:
if (pos.ObjectIndex != -1)
{
_allocation.Get()[pos.ObjectIndex].Delete();
_elementsCount--;
_deletedCount++;
--_elementsCount;
++_deletedCount;
return true;
}
return false;
@@ -578,8 +574,8 @@ public:
{
ASSERT(_allocation.Get()[i._index].IsOccupied());
_allocation.Get()[i._index].Delete();
_elementsCount--;
_deletedCount++;
--_elementsCount;
++_deletedCount;
return true;
}
return false;
@@ -656,14 +652,14 @@ public:
return Iterator(this, _size);
}
const Iterator begin() const
Iterator begin() const
{
Iterator i(this, -1);
++i;
return i;
}
FORCE_INLINE const Iterator end() const
FORCE_INLINE Iterator end() const
{
return Iterator(this, _size);
}
@@ -750,7 +746,7 @@ private:
// Insert
ASSERT(pos.FreeSlotIndex != -1);
_elementsCount++;
++_elementsCount;
return &_allocation.Get()[pos.FreeSlotIndex];
}
@@ -760,8 +756,8 @@ private:
{
// Fast path if it's empty
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
data[i]._state = Bucket::Empty;
for (int32 i = 0; i < _size; ++i)
data[i]._state = BucketState::Empty;
}
else
{
@@ -770,11 +766,11 @@ private:
MoveToEmpty(oldAllocation, _allocation, _size);
_allocation.Allocate(_size);
Bucket* data = _allocation.Get();
for (int32 i = 0; i < _size; i++)
data[i]._state = Bucket::Empty;
for (int32 i = 0; i < _size; ++i)
data[i]._state = BucketState::Empty;
Bucket* oldData = oldAllocation.Get();
FindPositionResult pos;
for (int32 i = 0; i < _size; i++)
for (int32 i = 0; i < _size; ++i)
{
Bucket& oldBucket = oldData[i];
if (oldBucket.IsOccupied())
@@ -783,10 +779,10 @@ private:
ASSERT(pos.FreeSlotIndex != -1);
Bucket* bucket = &_allocation.Get()[pos.FreeSlotIndex];
Memory::MoveItems(&bucket->Item, &oldBucket.Item, 1);
bucket->_state = Bucket::Occupied;
bucket->_state = BucketState::Occupied;
}
}
for (int32 i = 0; i < _size; i++)
for (int32 i = 0; i < _size; ++i)
oldData[i].Free();
}
_deletedCount = 0;

View File

@@ -14,8 +14,8 @@ template<typename T, typename AllocationType = HeapAllocation>
class RingBuffer
{
public:
typedef T ItemType;
typedef typename AllocationType::template Data<T> AllocationData;
using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>;
private:
int32 _front = 0, _back = 0, _count = 0, _capacity = 0;
@@ -62,7 +62,7 @@ public:
}
Memory::ConstructItems(_allocation.Get() + _back, &data, 1);
_back = (_back + 1) % _capacity;
_count++;
++_count;
}
FORCE_INLINE T& PeekFront()
@@ -75,13 +75,13 @@ public:
return _allocation.Get()[_front];
}
FORCE_INLINE T& operator[](int32 index)
FORCE_INLINE T& operator[](const int32 index)
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[(_front + index) % _capacity];
}
FORCE_INLINE const T& operator[](int32 index) const
FORCE_INLINE const T& operator[](const int32 index) const
{
ASSERT(index >= 0 && index < _count);
return _allocation.Get()[(_front + index) % _capacity];
@@ -91,7 +91,7 @@ public:
{
Memory::DestructItems(_allocation.Get() + _front, 1);
_front = (_front + 1) % _capacity;
_count--;
--_count;
}
void Clear()

View File

@@ -31,28 +31,28 @@ public:
FORCE_INLINE T* Get()
{
return (T*)_data;
return reinterpret_cast<T*>(_data);
}
FORCE_INLINE const T* Get() const
{
return (T*)_data;
return reinterpret_cast<const T*>(_data);
}
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
ASSERT(minCapacity <= Capacity);
return Capacity;
}
FORCE_INLINE void Allocate(int32 capacity)
FORCE_INLINE void Allocate(const int32 capacity)
{
#if ENABLE_ASSERTION_LOW_LAYERS
ASSERT(capacity <= Capacity);
#endif
}
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount)
{
#if ENABLE_ASSERTION_LOW_LAYERS
ASSERT(capacity <= Capacity);
@@ -104,7 +104,7 @@ public:
return _data;
}
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, const int32 minCapacity) const
{
if (capacity < minCapacity)
capacity = minCapacity;
@@ -129,21 +129,21 @@ public:
return capacity;
}
FORCE_INLINE void Allocate(int32 capacity)
FORCE_INLINE void Allocate(const int32 capacity)
{
#if ENABLE_ASSERTION_LOW_LAYERS
ASSERT(!_data);
#endif
_data = (T*)Allocator::Allocate(capacity * sizeof(T));
_data = static_cast<T*>(Allocator::Allocate(capacity * sizeof(T)));
#if !BUILD_RELEASE
if (!_data)
OUT_OF_MEMORY;
#endif
}
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount)
{
T* newData = capacity != 0 ? (T*)Allocator::Allocate(capacity * sizeof(T)) : nullptr;
T* newData = capacity != 0 ? static_cast<T*>(Allocator::Allocate(capacity * sizeof(T))) : nullptr;
#if !BUILD_RELEASE
if (!newData && capacity != 0)
OUT_OF_MEMORY;
@@ -203,12 +203,12 @@ public:
FORCE_INLINE T* Get()
{
return _useOther ? _other.Get() : (T*)_data;
return _useOther ? _other.Get() : reinterpret_cast<T*>(_data);
}
FORCE_INLINE const T* Get() const
{
return _useOther ? _other.Get() : (T*)_data;
return _useOther ? _other.Get() : reinterpret_cast<const T*>(_data);
}
FORCE_INLINE int32 CalculateCapacityGrow(int32 capacity, int32 minCapacity) const
@@ -227,13 +227,15 @@ public:
FORCE_INLINE void Relocate(int32 capacity, int32 oldCount, int32 newCount)
{
T* data = reinterpret_cast<T*>(_data);
// Check if the new allocation will fit into inlined storage
if (capacity <= Capacity)
{
if (_useOther)
{
// Move the items from other allocation to the inlined storage
Memory::MoveItems((T*)_data, _other.Get(), newCount);
Memory::MoveItems(data, _other.Get(), newCount);
// Free the other allocation
Memory::DestructItems(_other.Get(), oldCount);
@@ -255,8 +257,8 @@ public:
_useOther = true;
// Move the items from the inlined storage to the other allocation
Memory::MoveItems(_other.Get(), (T*)_data, newCount);
Memory::DestructItems((T*)_data, oldCount);
Memory::MoveItems(_other.Get(), data, newCount);
Memory::DestructItems(data, oldCount);
}
}
}
@@ -277,4 +279,4 @@ public:
};
};
typedef HeapAllocation DefaultAllocation;
using DefaultAllocation = HeapAllocation;

View File

@@ -17,8 +17,8 @@ class RenderListBuffer
{
friend RenderListBuffer;
public:
typedef T ItemType;
typedef typename AllocationType::template Data<T> AllocationData;
using ItemType = T;
using AllocationData = typename AllocationType::template Data<T>;
private:
volatile int64 _count;
@@ -42,7 +42,7 @@ public:
/// Initializes a new instance of the <see cref="RenderListBuffer"/> class.
/// </summary>
/// <param name="capacity">The initial capacity.</param>
RenderListBuffer(int32 capacity)
explicit RenderListBuffer(const int32 capacity)
: _count(0)
, _capacity(capacity)
{
@@ -55,7 +55,7 @@ public:
/// </summary>
/// <param name="data">The initial data.</param>
/// <param name="length">The amount of items.</param>
RenderListBuffer(const T* data, int32 length)
RenderListBuffer(const T* data, const int32 length)
{
ASSERT(length >= 0);
_count = _capacity = length;
@@ -76,7 +76,7 @@ public:
if (_capacity > 0)
{
_allocation.Allocate(_capacity);
Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)other._count);
Memory::ConstructItems(_allocation.Get(), other.Get(), static_cast<int32>(other._count));
}
}
@@ -102,7 +102,7 @@ public:
{
if (this != &other)
{
Memory::DestructItems(_allocation.Get(), (int32)_count);
Memory::DestructItems(_allocation.Get(), static_cast<int32>(_count));
if (_capacity < other.Count())
{
_allocation.Free();
@@ -110,7 +110,7 @@ public:
_allocation.Allocate(_capacity);
}
_count = other.Count();
Memory::ConstructItems(_allocation.Get(), other.Get(), (int32)_count);
Memory::ConstructItems(_allocation.Get(), other.Get(), static_cast<int32>(_count));
}
return *this;
}
@@ -124,7 +124,7 @@ public:
{
if (this != &other)
{
Memory::DestructItems(_allocation.Get(), (int32)_count);
Memory::DestructItems(_allocation.Get(), static_cast<int32>(_count));
_allocation.Free();
_count = other._count;
_capacity = other._capacity;
@@ -140,7 +140,7 @@ public:
/// </summary>
~RenderListBuffer()
{
Memory::DestructItems(_allocation.Get(), (int32)_count);
Memory::DestructItems(_allocation.Get(), static_cast<int32>(_count));
}
public:
@@ -149,7 +149,7 @@ public:
/// </summary>
FORCE_INLINE int32 Count() const
{
return (int32)Platform::AtomicRead((volatile int64*)&_count);
return static_cast<int32>(Platform::AtomicRead((volatile int64*)&_count));
}
/// <summary>
@@ -157,7 +157,7 @@ public:
/// </summary>
FORCE_INLINE int32 Capacity() const
{
return (int32)Platform::AtomicRead((volatile int64*)&_capacity);
return static_cast<int32>(Platform::AtomicRead((volatile int64*)&_capacity));
}
/// <summary>
@@ -188,7 +188,7 @@ public:
/// Gets or sets the item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE T& operator[](int32 index)
FORCE_INLINE T& operator[](const int32 index)
{
ASSERT(index >= 0 && index < Count());
return _allocation.Get()[index];
@@ -198,7 +198,7 @@ public:
/// Gets the item at the given index.
/// </summary>
/// <returns>The reference to the item.</returns>
FORCE_INLINE const T& operator[](int32 index) const
FORCE_INLINE const T& operator[](const int32 index) const
{
ASSERT(index >= 0 && index < Count());
return _allocation.Get()[index];
@@ -232,7 +232,7 @@ public:
void Clear()
{
_locker.Lock();
Memory::DestructItems(_allocation.Get(), (int32)_count);
Memory::DestructItems(_allocation.Get(), static_cast<int32>(_count));
_count = 0;
_locker.Unlock();
}
@@ -242,14 +242,14 @@ public:
/// </summary>
/// <param name="capacity">The new capacity.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize will be empty.</param>
void SetCapacity(const int32 capacity, bool preserveContents = true)
void SetCapacity(const int32 capacity, const bool preserveContents = true)
{
if (capacity == Capacity())
return;
_locker.Lock();
ASSERT(capacity >= 0);
const int32 count = preserveContents ? ((int32)_count < capacity ? (int32)_count : capacity) : 0;
_allocation.Relocate(capacity, (int32)_count, count);
const int32 count = preserveContents ? (static_cast<int32>(_count) < capacity ? static_cast<int32>(_count) : capacity) : 0;
_allocation.Relocate(capacity, static_cast<int32>(_count), count);
Platform::AtomicStore(&_capacity, capacity);
Platform::AtomicStore(&_count, count);
_locker.Unlock();
@@ -260,17 +260,17 @@ public:
/// </summary>
/// <param name="size">The new collection size.</param>
/// <param name="preserveContents">True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data.</param>
void Resize(int32 size, bool preserveContents = true)
void Resize(const int32 size, const bool preserveContents = true)
{
_locker.Lock();
if (_count > size)
{
Memory::DestructItems(_allocation.Get() + size, (int32)_count - size);
Memory::DestructItems(_allocation.Get() + size, static_cast<int32>(_count) - size);
}
else
{
EnsureCapacity(size, preserveContents);
Memory::ConstructItems(_allocation.Get() + _count, size - (int32)_count);
Memory::ConstructItems(_allocation.Get() + _count, size - static_cast<int32>(_count));
}
_count = size;
_locker.Unlock();
@@ -280,14 +280,14 @@ public:
/// Ensures the collection has given capacity (or more).
/// </summary>
/// <param name="minCapacity">The minimum capacity.</param>
void EnsureCapacity(int32 minCapacity)
void EnsureCapacity(const int32 minCapacity)
{
_locker.Lock();
int32 capacity = (int32)Platform::AtomicRead(&_capacity);
int32 capacity = static_cast<int32>(Platform::AtomicRead(&_capacity));
if (capacity < minCapacity)
{
capacity = _allocation.CalculateCapacityGrow(capacity, minCapacity);
const int32 count = (int32)_count;
const int32 count = static_cast<int32>(_count);
_allocation.Relocate(capacity, count, count);
Platform::AtomicStore(&_capacity, capacity);
}
@@ -324,8 +324,8 @@ private:
int32 AddOne()
{
Platform::InterlockedIncrement(&_threadsAdding);
int32 count = (int32)Platform::AtomicRead(&_count);
int32 capacity = (int32)Platform::AtomicRead(&_capacity);
int32 count = static_cast<int32>(Platform::AtomicRead(&_count));
int32 capacity = static_cast<int32>(Platform::AtomicRead(&_capacity));
const int32 minCapacity = GetMinCapacity(count);
if (minCapacity > capacity || Platform::AtomicRead(&_threadsResizing)) // Resize if not enough space or someone else is already doing it (don't add mid-resizing)
{
@@ -340,7 +340,7 @@ private:
// Thread-safe resizing
_locker.Lock();
capacity = (int32)Platform::AtomicRead(&_capacity);
capacity = static_cast<int32>(Platform::AtomicRead(&_capacity));
if (capacity < minCapacity)
{
if (Platform::AtomicRead(&_threadsAdding))
@@ -350,7 +350,7 @@ private:
goto RETRY;
}
capacity = _allocation.CalculateCapacityGrow(capacity, minCapacity);
count = (int32)Platform::AtomicRead(&_count);
count = static_cast<int32>(Platform::AtomicRead(&_count));
_allocation.Relocate(capacity, count, count);
Platform::AtomicStore(&_capacity, capacity);
}
@@ -362,10 +362,10 @@ private:
// Let other thread enter resizing-area
_locker.Unlock();
}
return (int32)Platform::InterlockedIncrement(&_count) - 1;
return static_cast<int32>(Platform::InterlockedIncrement(&_count)) - 1;
}
FORCE_INLINE static int32 GetMinCapacity(int32 count)
FORCE_INLINE static int32 GetMinCapacity(const int32 count)
{
// Ensure there is a slack for others threads to reduce resize counts in highly multi-threaded environment
constexpr int32 slack = PLATFORM_THREADS_LIMIT * 8;