// Copyright (c) 2012-2024 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: typedef uint64 ItemType; typedef typename AllocationType::template Data AllocationData; 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 a new instance of the class. /// FORCE_INLINE BitArray() : _count(0) , _capacity(0) { } /// /// Initializes a new instance of the class. /// /// The initial capacity. BitArray(const int32 capacity) : _count(0) , _capacity(capacity) { if (capacity > 0) _allocation.Allocate(ToItemCapacity(capacity)); } /// /// Initializes a new instance of the class. /// /// 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 = other._count; _capacity = other._capacity; other._count = 0; other._capacity = 0; _allocation.Swap(other._allocation); } /// /// 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 = other._count; _capacity = other._capacity; other._count = 0; other._capacity = 0; _allocation.Swap(other._allocation); } 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 = static_cast((int32)(1 << (index & (static_cast(sizeof(ItemType)) - 1)))); const ItemType item = static_cast(_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 = static_cast((int32)(1 << (index & (static_cast(sizeof(ItemType)) - 1)))); ItemType& item = reinterpret_cast(_allocation.Get())[offset]; if (value) item |= bitMask; else item &= ~bitMask; } 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(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) { ::Swap(_count, other._count); ::Swap(_capacity, other._capacity); _allocation.Swap(other._allocation); } 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); } };