// Copyright (c) Wojciech Figat. All rights reserved. #pragma once #include "Engine/Platform/Platform.h" #include "Engine/Core/Memory/Allocation.h" #include "Engine/Core/Math/Math.h" /// /// Template for dynamic array with variable capacity that stores the bit values. /// template API_CLASS(InBuild) class BitArray { friend BitArray; public: using ItemType = uint64; using AllocationData = typename AllocationType::template Data; private: int32 _count; int32 _capacity; AllocationData _allocation; FORCE_INLINE static int32 ToItemCount(const int32 size) { return Math::DivideAndRoundUp(size, sizeof(ItemType)); } FORCE_INLINE static int32 ToItemCapacity(const int32 size) { return Math::Max(Math::DivideAndRoundUp(size, sizeof(ItemType)), 1); } public: /// /// Initializes an empty without reserving any space. /// FORCE_INLINE BitArray() : _count(0) , _capacity(0) { } /// /// Initializes by reserving space. /// /// The number of elements that can be added without a need to allocate more memory. explicit BitArray(const int32 capacity) : _count(0) , _capacity(capacity) { if (capacity > 0) _allocation.Allocate(ToItemCapacity(capacity)); } /// /// Initializes by copying the elements from the other collection. /// /// The other collection to copy. BitArray(const BitArray& other) noexcept { _count = _capacity = other.Count(); if (_capacity > 0) { const int32 itemsCapacity = ToItemCapacity(_capacity); _allocation.Allocate(itemsCapacity); Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType)); } } /// /// Initializes a new instance of the class. /// /// The other collection to copy. template explicit BitArray(const BitArray& other) noexcept { _count = _capacity = other.Count(); if (_capacity > 0) { const int32 itemsCapacity = ToItemCapacity(_capacity); _allocation.Allocate(itemsCapacity); Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType)); } } /// /// Initializes a new instance of the class. /// /// The other collection to move. FORCE_INLINE BitArray(BitArray&& other) noexcept : _count(0) , _capacity(0) { Swap(other); } /// /// The assignment operator that deletes the current collection of items and the copies items from the other array. /// /// The other collection to copy. /// The reference to this. BitArray& operator=(const BitArray& other) noexcept { if (this != &other) { if (_capacity < other._count) { _allocation.Free(); _capacity = other._count; const int32 itemsCapacity = ToItemCapacity(_capacity); _allocation.Allocate(itemsCapacity); Platform::MemoryCopy(Get(), other.Get(), itemsCapacity * sizeof(ItemType)); } _count = other._count; } return *this; } /// /// The move assignment operator that deletes the current collection of items and the moves items from the other array. /// /// The other collection to move. /// The reference to this. BitArray& operator=(BitArray&& other) noexcept { if (this != &other) { _allocation.Free(); _count = 0; _capacity = 0; Swap(other); } return *this; } /// /// Finalizes an instance of the class. /// ~BitArray() { } public: /// /// Gets the pointer to the bits storage data (linear allocation). /// FORCE_INLINE ItemType* Get() { return _allocation.Get(); } /// /// Gets the pointer to the bits storage data (linear allocation). /// FORCE_INLINE const ItemType* Get() const { return _allocation.Get(); } /// /// Gets the amount of the items in the collection. /// FORCE_INLINE int32 Count() const { return _count; } /// /// Gets the amount of the items that can be contained by collection without resizing. /// FORCE_INLINE int32 Capacity() const { return _capacity; } /// /// Returns true if collection isn't empty. /// FORCE_INLINE bool HasItems() const { return _count != 0; } /// /// Returns true if collection is empty. /// FORCE_INLINE bool IsEmpty() const { return _count == 0; } /// /// Gets the item at the given index. /// /// The index of the item. /// The value of the item. FORCE_INLINE bool operator[](const int32 index) const { return Get(index); } /// /// Gets the item at the given index. /// /// The index of the item. /// The value of the item. bool Get(const int32 index) const { ASSERT(index >= 0 && index < _count); const ItemType offset = index / sizeof(ItemType); const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1))); const ItemType item = ((ItemType*)_allocation.Get())[offset]; return (item & bitMask) != 0; } /// /// Sets the item at the given index. /// /// The index of the item. /// The value to set. void Set(const int32 index, const bool value) { ASSERT(index >= 0 && index < _count); const ItemType offset = index / sizeof(ItemType); const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1))); ItemType& item = ((ItemType*)_allocation.Get())[offset]; if (value) item |= bitMask; else item &= ~bitMask; // Clear the bit } public: /// /// Clear the collection without changing its capacity. /// FORCE_INLINE void Clear() { _count = 0; } /// /// Changes the capacity of the collection. /// /// The new capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. void SetCapacity(const int32 capacity, const bool preserveContents = true) { if (capacity == _capacity) return; ASSERT(capacity >= 0); const int32 count = preserveContents ? (_count < capacity ? _count : capacity) : 0; _allocation.Relocate(ToItemCapacity(capacity), ToItemCount(_count), ToItemCount(count)); _capacity = capacity; _count = count; } /// /// Resizes the collection to the specified size. If the size is equal or less to the current capacity no additional memory reallocation in performed. /// /// The new collection size. /// True if preserve collection data when changing its size, otherwise collection after resize might not contain the previous data. void Resize(const int32 size, const bool preserveContents = true) { if (_count <= size) EnsureCapacity(size, preserveContents); _count = size; } /// /// Ensures the collection has given capacity (or more). /// /// The minimum capacity. /// True if preserve collection data when changing its size, otherwise collection after resize will be empty. void EnsureCapacity(const int32 minCapacity, const bool preserveContents = true) { if (_capacity < minCapacity) { const int32 capacity = _allocation.CalculateCapacityGrow(ToItemCapacity(_capacity), minCapacity); SetCapacity(capacity, preserveContents); } } /// /// Sets all items to the given value /// /// The value to assign to all the collection items. void SetAll(const bool value) { if (_count != 0) Platform::MemorySet(_allocation.Get(), ToItemCount(_count) * sizeof(ItemType), value ? MAX_uint32 : 0); } /// /// Adds the specified item to the collection. /// /// The item to add. void Add(const bool item) { EnsureCapacity(_count + 1); ++_count; Set(_count - 1, item); } /// /// Adds the specified item to the collection. /// /// The items to add. /// The items count. void Add(const bool* items, const int32 count) { EnsureCapacity(_count + count); for (int32 i = 0; i < count; ++i) Add(items[i]); } /// /// Adds the other collection to the collection. /// /// The other collection to add. void Add(const BitArray& other) { EnsureCapacity(_count, other.Count()); for (int32 i = 0; i < other.Count(); ++i) Add(other[i]); } /// /// Swaps the contents of collection with the other object without copy operation. Performs fast internal data exchange. /// /// The other collection. void Swap(BitArray& other) { if IF_CONSTEXPR (AllocationType::HasSwap) _allocation.Swap(other._allocation); else { // Move to temp const int32 oldItemsCapacity = ToItemCount(_capacity); const int32 otherItemsCapacity = ToItemCount(other._capacity); AllocationData oldAllocation; if (oldItemsCapacity) { oldAllocation.Allocate(oldItemsCapacity); Memory::MoveItems(oldAllocation.Get(), _allocation.Get(), oldItemsCapacity); _allocation.Free(); } // Move other to source if (otherItemsCapacity) { _allocation.Allocate(otherItemsCapacity); Memory::MoveItems(_allocation.Get(), other._allocation.Get(), otherItemsCapacity); other._allocation.Free(); } // Move temp to other if (oldItemsCapacity) { other._allocation.Allocate(oldItemsCapacity); Memory::MoveItems(other._allocation.Get(), oldAllocation.Get(), oldItemsCapacity); } } ::Swap(_count, other._count); ::Swap(_capacity, other._capacity); } public: template bool operator==(const BitArray& other) const { if (_count == other.Count()) { for (int32 i = 0; i < _count; i++) { if (!(Get(i) == other.Get(i))) return false; } return true; } return false; } template bool operator!=(const BitArray& other) const { return !operator==(other); } };