summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bazelrc3
-rw-r--r--MODULE.bazel7
-rw-r--r--MODULE.bazel.lock4
-rw-r--r--asl/handle_pool/BUILD.bazel2
-rw-r--r--asl/handle_pool/index_pool.hpp308
-rw-r--r--asl/handle_pool/index_pool_tests.cpp256
-rw-r--r--asl/testing/testing.cpp8
7 files changed, 532 insertions, 56 deletions
diff --git a/.bazelrc b/.bazelrc
index 1acd102..81e46ce 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,3 +1,6 @@
+common --registry=https://bcr.bazel.build/
+common --registry=https://bazel.stevenlr.com/registry/
+
startup --windows_enable_symlinks
build:windows --enable_runfiles=true
diff --git a/MODULE.bazel b/MODULE.bazel
index 6c224af..8d856b3 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -11,12 +11,7 @@ bazel_dep(name = "rules_cc", version = "0.1.1")
cc_configure = use_extension("@rules_cc//cc:extensions.bzl", "cc_configure_extension")
use_repo(cc_configure, "local_config_cc")
-bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
-git_override(
- module_name = "hedron_compile_commands",
- remote = "https://github.com/hedronvision/bazel-compile-commands-extractor.git",
- commit = "4f28899228fb3ad0126897876f147ca15026151e",
-)
+bazel_dep(name = "hedron_compile_commands", version = "0.1.0", dev_dependency = True)
bazel_dep(name = "rules_python", version = "1.3.0")
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index a47fe3f..55c4990 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -1,6 +1,9 @@
{
"lockFileVersion": 18,
"registryFileHashes": {
+ "https://bazel.stevenlr.com/registry/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
+ "https://bazel.stevenlr.com/registry/modules/hedron_compile_commands/0.1.0/MODULE.bazel": "5623ba8f732a01246c388bccebf924357e452314a178f179d3b375b623d5a359",
+ "https://bazel.stevenlr.com/registry/modules/hedron_compile_commands/0.1.0/source.json": "c55f6caa3eb9fb027af66949c23ca537214eb32b0316ae95bcc496f3cd8406b9",
"https://bcr.bazel.build/bazel_registry.json": "8a28e4aff06ee60aed2a8c281907fb8bcbf3b753c91fb5a5c57da3215d5b3497",
"https://bcr.bazel.build/modules/abseil-cpp/20210324.2/MODULE.bazel": "7cd0312e064fde87c8d1cd79ba06c876bd23630c83466e9500321be55c96ace2",
"https://bcr.bazel.build/modules/abseil-cpp/20211102.0/MODULE.bazel": "70390338f7a5106231d20620712f7cccb659cd0e9d073d1991c038eb9fc57589",
@@ -39,6 +42,7 @@
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
+ "https://bcr.bazel.build/modules/hedron_compile_commands/0.1.0/MODULE.bazel": "not found",
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
diff --git a/asl/handle_pool/BUILD.bazel b/asl/handle_pool/BUILD.bazel
index e5048d3..bdaa711 100644
--- a/asl/handle_pool/BUILD.bazel
+++ b/asl/handle_pool/BUILD.bazel
@@ -16,6 +16,7 @@ cc_library(
"//asl/memory:allocator",
"//asl/base",
"//asl/containers:chunked_buffer",
+ "//asl/types:option",
],
visibility = ["//visibility:public"],
)
@@ -27,6 +28,7 @@ cc_test(
],
deps = [
":index_pool",
+ "//asl/hashing",
"//asl/tests:utils",
"//asl/testing",
],
diff --git a/asl/handle_pool/index_pool.hpp b/asl/handle_pool/index_pool.hpp
index 377f6f4..56345e8 100644
--- a/asl/handle_pool/index_pool.hpp
+++ b/asl/handle_pool/index_pool.hpp
@@ -6,82 +6,296 @@
#include "asl/base/meta.hpp"
#include "asl/containers/chunked_buffer.hpp"
#include "asl/memory/allocator.hpp"
+#include "asl/types/option.hpp"
namespace asl
{
template<
- int kIndexSize_,
- int kGenSize_,
- int kUserSize_ = 0
+ int kIndexBits_,
+ int kGenBits_,
+ typename UserType_ = empty,
+ int kUserBits_ = 0
>
-requires (
- kUserSize_ >= 0
- && kGenSize_ > 0
- && kIndexSize_ > 0
- && (kUserSize_ + kGenSize_ + kIndexSize_ <= 63)
-)
+requires (kIndexBits_ > 0 && kGenBits_ > 0 && kUserBits_ >= 0)
struct index_pool_config
{
- static constexpr int kUserSize = kUserSize_;
- static constexpr int kGenSize = kGenSize_;
- static constexpr int kIndexSize = kIndexSize_;
+ static constexpr bool kHasUser = !same_as<UserType_, empty>;
- static constexpr bool kHasUser = kUserSize > 0;
+ using UserType = UserType_;
+ using PrimitiveUserType = smallest_unsigned_integer_type_for_width<size_of<UserType> * 8>;
- using handle_type = smallest_unsigned_integer_type_for_width<kGenSize + kIndexSize + kUserSize>;
- using user_type = select_t<kHasUser, smallest_unsigned_integer_type_for_width<kUserSize>, empty>;
-};
+ static_assert(trivially_copy_constructible<UserType>);
+ static_assert(trivially_destructible<UserType>);
+ static_assert(size_of<UserType> == size_of<PrimitiveUserType>, "UserType should be of size 1, 2 or 4");
+
+ static constexpr int kUserBits = []() static -> int {
+ if constexpr (!kHasUser) { return 0; };
+ return kUserBits_ == 0 ? size_of<UserType> * 8 : kUserBits_;
+ }();
+
+ static_assert(kUserBits <= size_of<UserType> * 8);
+
+ static constexpr int kIndexBits = kIndexBits_;
+ static constexpr int kGenBits = kGenBits_;
+
+ static_assert(kIndexBits + kGenBits + kUserBits <= 63);
+
+ using HandleType = smallest_unsigned_integer_type_for_width<kIndexBits + kGenBits + kUserBits + 1>;
+ static constexpr int kGenShift = kIndexBits;
+ static constexpr int kUserShift = kIndexBits + kGenBits;
+
+ static constexpr HandleType kValidMask = HandleType{1} << (size_of<HandleType> * 8 - 1);
+ static constexpr HandleType kIndexMask = (HandleType{1} << kIndexBits) - 1;
+ static constexpr HandleType kGenMask = ((HandleType{1} << kGenBits) - 1) << kGenShift;
+ static constexpr HandleType kUserMask = ((HandleType{1} << kUserBits) - 1) << kUserShift;
+ static constexpr HandleType kNiche = static_cast<HandleType>(~uint64_t{kValidMask});
+
+ static constexpr uint64_t kMaxGen = (uint64_t{1} << kGenBits) - 1;
+ static constexpr uint64_t kMaxIndex = (uint64_t{1} << kIndexBits) - 1;
+};
template<
- int kIndexSize_,
- int kGenSize_,
- typename UserType = empty,
- int kUserSize_ = 0
+ int kIndexBits_,
+ int kGenBits_,
+ typename UserType_ = empty,
+ int kUserBits_ = 0
>
-requires (
- same_as<UserType, empty> || (kUserSize_ <= size_of<UserType> * 8 && trivially_copy_constructible<UserType>)
-)
-class index_pool_handle_base
+class index_pool_handle
{
- static constexpr int kUserSizeComputed =
- same_as<UserType, empty> ? 0 : (kUserSize_ == 0 ? size_of<UserType> * 8 : kUserSize_); // NOLINT
-
public:
- using config = index_pool_config<kIndexSize_, kGenSize_, kUserSizeComputed>;
+ using config = index_pool_config<kIndexBits_, kGenBits_, UserType_, kUserBits_>;
private:
- using handle_type = config::handle_type;
- using user_type = config::user_type;
- using user_type_external = UserType;
-
- static constexpr handle_type kHasValueMask = ~(~handle_type{0} >> 1);
-
- static constexpr handle_type kIndexMask = (handle_type{1} << config::kIndexSize) - 1;
+ config::HandleType m_handle{};
- static constexpr int kGenShift = config::kIndexSize;
- static constexpr handle_type kGenMask = ((handle_type{1} << config::kGenSize) - 1) << kGenShift;
+public:
+ constexpr index_pool_handle() = default;
- static constexpr int kUserShift = config::kIndexSize + config::kGenSize;
- static constexpr handle_type kUserMask = ((handle_type{1} << config::kUserSize) - 1) << kUserShift;
-
- handle_type m_handle{};
+ constexpr explicit index_pool_handle(niche_t)
+ : m_handle{config::kNiche}
+ {}
-public:
- [[nodiscard]] constexpr bool has_value() const { return m_handle & kHasValueMask; }
+ constexpr index_pool_handle(uint64_t index, uint64_t gen)
+ requires (!config::kHasUser)
+ : m_handle{static_cast<config::HandleType>(
+ config::kValidMask |
+ (index & config::kIndexMask) |
+ ((gen << config::kGenShift) & config::kGenMask))}
+ {
+ ASL_ASSERT((index & uint64_t{config::kIndexMask}) == index);
+ ASL_ASSERT((gen & (uint64_t{config::kGenMask} >> config::kGenShift)) == gen);
+ }
- [[nodiscard]] constexpr user_type_external user() const
+ constexpr index_pool_handle(uint64_t index, uint64_t gen, config::UserType user)
requires config::kHasUser
+ : m_handle{static_cast<config::HandleType>(
+ config::kValidMask |
+ (index & config::kIndexMask) |
+ ((gen << config::kGenShift) & config::kGenMask) |
+ ((static_cast<config::HandleType>(bit_cast<typename config::PrimitiveUserType>(user)) << config::kUserShift) & config::kUserMask))}
+ {
+ ASL_ASSERT((index & uint64_t{config::kIndexMask}) == index);
+ ASL_ASSERT((gen & (uint64_t{config::kGenMask} >> config::kGenShift)) == gen);
+ ASL_ASSERT((bit_cast<typename config::PrimitiveUserType>(user) & (uint64_t{config::kUserMask} >> config::kUserShift)) == bit_cast<typename config::PrimitiveUserType>(user));
+ }
+
+ constexpr bool is_null(this index_pool_handle self)
{
- return bit_cast<user_type_external>(
- static_cast<user_type>((m_handle & kUserMask) >> kUserShift)
- );
+ return !(self.m_handle & config::kValidMask);
+ }
+
+ constexpr uint64_t index(this index_pool_handle self)
+ {
+ return self.m_handle & config::kIndexMask;
+ }
+
+ constexpr uint64_t gen(this index_pool_handle self)
+ {
+ return (self.m_handle & config::kGenMask) >> config::kGenShift;
+ }
+
+ constexpr config::UserType user(this index_pool_handle self)
+ {
+ return bit_cast<typename config::UserType>(static_cast<config::PrimitiveUserType>(
+ ((self.m_handle & config::kUserMask) >> config::kUserShift)));
+ }
+
+ constexpr bool operator==(this index_pool_handle self, index_pool_handle other) = default;
+
+ constexpr bool operator==(this index_pool_handle self, niche_t)
+ {
+ return self.m_handle == config::kNiche;
}
};
+template<
+ int kIndexBits_,
+ int kGenBits_,
+ typename UserType_,
+ int kUserBits_
+>
+struct is_uniquely_represented<index_pool_handle<kIndexBits_, kGenBits_, UserType_, kUserBits_>> : true_type {};
+
+template<
+ int kIndexBits_,
+ int kGenBits_,
+ typename UserType_ = empty,
+ int kUserBits_ = 0,
+ typename Payload = empty,
+ allocator Allocator = DefaultAllocator
+>
class IndexPool
{
+public:
+ using handle = index_pool_handle<kIndexBits_, kGenBits_, UserType_, kUserBits_>;
+
+private:
+ using config = handle::config;
+
+ static constexpr bool kHasPayload = !same_as<Payload, empty>;
+
+ // @Todo Remove need for default constructible & trivially destructible for payload
+ // Use maybe_uninit for it
+
+ // @Todo Use dummy user type with inner handle type -> no need to set user data when allocating handle
+
+ static_assert(default_constructible<Payload>);
+ static_assert(copy_constructible<Payload>);
+ static_assert(trivially_destructible<Payload>);
+
+ struct Slot
+ {
+ bool is_end_of_list : 1;
+ bool is_active : 1;
+
+ handle handle;
+
+ ASL_NO_UNIQUE_ADDRESS Payload payload;
+ };
+
+ chunked_buffer<Slot, 256, Allocator> m_slots;
+
+ // We only use the index, this is essentially the head of the linked
+ // list to the first available slot.
+ // Then the index of each slot points to the next available one.
+ handle m_first_available;
+
+ static constexpr handle make_handle(uint64_t index, uint64_t gen, [[maybe_unused]] config::UserType user_data)
+ {
+ if constexpr (config::kHasUser)
+ {
+ return handle(index, gen, user_data);
+ }
+ else
+ {
+ return handle(index, gen);
+ }
+ };
+
+ void allocate_new_slot(config::UserType user)
+ {
+ const auto new_index = static_cast<uint64_t>(m_slots.size());
+ if (new_index > config::kMaxIndex) { return; }
+
+ const handle new_handle = make_handle(new_index, 0, user);
+
+ m_slots.push(Slot{
+ .is_end_of_list = true,
+ .is_active = false,
+ .handle = new_handle,
+ .payload = Payload{}
+ });
+
+ m_first_available = new_handle;
+ }
+
+ option<handle> acquire_handle(const Payload& payload, config::UserType user)
+ {
+ if (m_first_available.is_null())
+ {
+ allocate_new_slot(user);
+ }
+
+ if (m_first_available.is_null())
+ {
+ return nullopt;
+ }
+
+ auto index = m_first_available.index();
+
+ Slot& slot = m_slots[static_cast<isize_t>(index)];
+ ASL_ASSERT(!slot.is_active);
+
+ m_first_available = slot.is_end_of_list ? handle{} : slot.handle;
+
+ slot.is_active = true;
+ slot.payload = payload;
+
+ return make_handle(index, slot.handle.gen(), user);
+ }
+
+ auto get_slot_if_valid(this auto&& self, handle h)
+ -> copy_const_t<decltype(self), Slot>*
+ {
+ auto index = static_cast<isize_t>(h.index());
+ if (index < 0 || index >= self.m_slots.size()) { return nullptr; }
+
+ auto& slot = self.m_slots[index];
+ if (!slot.is_active || slot.handle.gen() != h.gen())
+ {
+ return nullptr;
+ }
+
+ return &slot;
+ }
+
+public:
+ IndexPool() requires default_constructible<Allocator> = default;
+
+ explicit IndexPool(Allocator allocator) : m_slots{std::move(allocator)} {}
+
+ option<handle> acquire(const Payload& payload)
+ {
+ return acquire_handle(payload, {});
+ }
+
+ handle acquire_ensure(const Payload& payload)
+ {
+ auto opt = acquire(payload);
+ ASL_ASSERT_RELEASE(opt.has_value());
+ return opt.value();
+ }
+
+ // @Todo Add a policy to abandon slots that reached max generation
+ void release(handle h)
+ {
+ if (Slot* slot = get_slot_if_valid(h); slot != nullptr)
+ {
+ const uint64_t next_gen = h.gen() == config::kMaxGen ? 0 : h.gen() + 1;
+
+ slot->is_active = false;
+
+ if (m_first_available.is_null())
+ {
+ slot->is_end_of_list = true;
+ slot->handle = make_handle(h.index(), next_gen, {});
+ }
+ else
+ {
+ slot->is_end_of_list = false;
+ slot->handle = make_handle(m_first_available.index(), next_gen, {});
+ }
+
+ m_first_available = h;
+ }
+ }
+
+ bool is_valid(handle h) const
+ {
+ return get_slot_if_valid(h) != nullptr;
+ }
};
} // namespace asl
diff --git a/asl/handle_pool/index_pool_tests.cpp b/asl/handle_pool/index_pool_tests.cpp
index 1dd2816..329fb4c 100644
--- a/asl/handle_pool/index_pool_tests.cpp
+++ b/asl/handle_pool/index_pool_tests.cpp
@@ -3,8 +3,262 @@
// SPDX-License-Identifier: BSD-3-Clause
#include "asl/testing/testing.hpp"
+#include "asl/handle_pool/index_pool.hpp"
+#include "asl/hashing/hash.hpp"
-ASL_TEST(test)
+enum Flags: uint8_t {
+ kFlag0 = 0,
+ kFlag1 = 1,
+ kFlag2 = 2,
+};
+
+using Cfg1 = asl::index_pool_config<4, 3>;
+static_assert(!Cfg1::kHasUser);
+static_assert(Cfg1::kUserBits == 0);
+static_assert(asl::same_as<Cfg1::HandleType, uint8_t>);
+static_assert(Cfg1::kValidMask == uint8_t{0x80});
+static_assert(Cfg1::kIndexMask == uint8_t{0x0f});
+static_assert(Cfg1::kGenMask == uint8_t{0x70});
+static_assert(Cfg1::kGenShift == 4);
+static_assert(Cfg1::kMaxGen == 7);
+static_assert(Cfg1::kMaxIndex == 15);
+
+using Cfg2 = asl::index_pool_config<5, 5, Flags>;
+static_assert(Cfg2::kHasUser);
+static_assert(Cfg2::kUserBits == 8);
+static_assert(asl::same_as<Cfg2::PrimitiveUserType, uint8_t>);
+static_assert(asl::same_as<Cfg2::HandleType, uint32_t>);
+static_assert(Cfg2::kValidMask == uint32_t{0x8000'0000});
+static_assert(Cfg2::kIndexMask == uint32_t{0x0000'001f});
+static_assert(Cfg2::kGenMask == uint32_t{0x0000'03e0});
+static_assert(Cfg2::kUserMask == uint32_t{0x0003'fc00});
+static_assert(Cfg2::kGenShift == 5);
+static_assert(Cfg2::kUserShift == 10);
+static_assert(Cfg2::kMaxGen == 31);
+static_assert(Cfg2::kMaxIndex == 31);
+
+using Cfg3 = asl::index_pool_config<5, 6, Flags, 4>;
+static_assert(Cfg3::kHasUser);
+static_assert(Cfg3::kUserBits == 4);
+static_assert(asl::same_as<Cfg3::PrimitiveUserType, uint8_t>);
+static_assert(asl::same_as<Cfg3::HandleType, uint16_t>);
+static_assert(Cfg3::kValidMask == uint16_t{0x8000});
+static_assert(Cfg3::kIndexMask == uint16_t{0x001f});
+static_assert(Cfg3::kGenMask == uint16_t{0x07e0});
+static_assert(Cfg3::kUserMask == uint16_t{0x7800});
+static_assert(Cfg3::kGenShift == 5);
+static_assert(Cfg3::kUserShift == 11);
+static_assert(Cfg3::kMaxGen == 63);
+static_assert(Cfg3::kMaxIndex == 31);
+
+static_assert(asl::default_constructible<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::trivially_copy_constructible<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::trivially_move_constructible<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::trivially_copy_assignable<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::trivially_move_assignable<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::trivially_destructible<asl::index_pool_handle<5, 5, uint8_t>>);
+
+static_assert(asl::hashable<asl::index_pool_handle<5, 5, uint8_t>>);
+static_assert(asl::has_niche<asl::index_pool_handle<5, 5, uint8_t>>);
+
+ASL_TEST(default_is_invalid)
+{
+ const asl::index_pool_handle<5, 5, uint8_t> idx;
+ ASL_TEST_EXPECT(idx.is_null());
+}
+
+ASL_TEST(niche_is_invalid)
+{
+ const asl::index_pool_handle<5, 5, uint8_t> idx{asl::niche_t{}};
+ ASL_TEST_EXPECT(idx.is_null());
+}
+
+ASL_TEST(construct)
+{
+ const asl::index_pool_handle<5, 5> idx(9, 11);
+ ASL_TEST_EXPECT(!idx.is_null());
+ ASL_TEST_EXPECT(idx.index() == 9);
+ ASL_TEST_EXPECT(idx.gen() == 11);
+}
+
+ASL_TEST(construct_user)
+{
+ const asl::index_pool_handle<5, 5, Flags, 4> idx(9, 11, kFlag2);
+ ASL_TEST_EXPECT(!idx.is_null());
+ ASL_TEST_EXPECT(idx.index() == 9);
+ ASL_TEST_EXPECT(idx.gen() == 11);
+ ASL_TEST_EXPECT(idx.user() == kFlag2);
+ static_assert(asl::same_as<Flags, decltype(idx.user())>);
+}
+
+ASL_TEST(compare) // NOLINT
+{
+ const asl::index_pool_handle<5, 5, Flags, 4> idx_default;
+ const asl::index_pool_handle<5, 5, Flags, 4> idx0;
+ const asl::index_pool_handle<5, 5, Flags, 4> idx1(9, 11, kFlag2);
+ const asl::index_pool_handle<5, 5, Flags, 4> idx2(9, 11, kFlag1);
+ const asl::index_pool_handle<5, 5, Flags, 4> idx3(9, 11, kFlag1);
+ const asl::index_pool_handle<5, 5, Flags, 4> idx4(9, 10, kFlag2);
+ const asl::index_pool_handle<5, 5, Flags, 4> idx5(8, 11, kFlag2);
+
+ ASL_TEST_EXPECT(idx0 == idx_default);
+
+ ASL_TEST_EXPECT(idx0 != idx1);
+ ASL_TEST_EXPECT(idx0 != idx2);
+ ASL_TEST_EXPECT(idx0 != idx3);
+ ASL_TEST_EXPECT(idx0 != idx4);
+ ASL_TEST_EXPECT(idx0 != idx5);
+
+ ASL_TEST_EXPECT(idx1 != idx2);
+ ASL_TEST_EXPECT(idx1 != idx3);
+ ASL_TEST_EXPECT(idx1 != idx4);
+ ASL_TEST_EXPECT(idx1 != idx5);
+
+ ASL_TEST_EXPECT(idx2 == idx3);
+ ASL_TEST_EXPECT(idx2 != idx4);
+ ASL_TEST_EXPECT(idx2 != idx5);
+
+ ASL_TEST_EXPECT(idx3 != idx4);
+ ASL_TEST_EXPECT(idx3 != idx5);
+
+ ASL_TEST_EXPECT(idx4 != idx5);
+}
+
+ASL_TEST(hashing) // NOLINT
+{
+ const asl::index_pool_handle<4, 4> idx0(asl::niche_t{});
+ const asl::index_pool_handle<4, 4> idx1{};
+ const asl::index_pool_handle<4, 4> idx2(1, 1);
+ const asl::index_pool_handle<4, 4> idx3(1, 1);
+ const asl::index_pool_handle<4, 4> idx4(2, 1);
+ const asl::index_pool_handle<4, 4> idx5(1, 2);
+
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx1));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx2));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx2));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx2) == asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx2) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx2) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx3) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx3) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx4) != asl::hash_value(idx5));
+}
+
+ASL_TEST(hashing_in_option) // NOLINT
{
+ const asl::option<asl::index_pool_handle<4, 4>> idx0;
+ const asl::option<asl::index_pool_handle<4, 4>> idx1{asl::index_pool_handle<4, 4>()};
+ const asl::option<asl::index_pool_handle<4, 4>> idx2{asl::index_pool_handle<4, 4>(1, 1)};
+ const asl::option<asl::index_pool_handle<4, 4>> idx3{asl::index_pool_handle<4, 4>(1, 1)};
+ const asl::option<asl::index_pool_handle<4, 4>> idx4{asl::index_pool_handle<4, 4>(2, 1)};
+ const asl::option<asl::index_pool_handle<4, 4>> idx5{asl::index_pool_handle<4, 4>(1, 2)};
+
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx1));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx2));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx0) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx2));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx1) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx2) == asl::hash_value(idx3));
+ ASL_TEST_EXPECT(asl::hash_value(idx2) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx2) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx3) != asl::hash_value(idx4));
+ ASL_TEST_EXPECT(asl::hash_value(idx3) != asl::hash_value(idx5));
+
+ ASL_TEST_EXPECT(asl::hash_value(idx4) != asl::hash_value(idx5));
+}
+
+ASL_TEST(simple_pool) // NOLINT
+{
+ using Pool = asl::IndexPool<8, 8>;
+ Pool pool;
+
+ auto a = pool.acquire_ensure({});
+ auto b = pool.acquire_ensure({});
+
+ ASL_TEST_EXPECT(!a.is_null());
+ ASL_TEST_EXPECT(!b.is_null());
+ ASL_TEST_EXPECT(a.index() == 0);
+ ASL_TEST_EXPECT(b.index() == 1);
+ ASL_TEST_EXPECT(a.gen() == 0);
+ ASL_TEST_EXPECT(b.gen() == 0);
+
+ ASL_TEST_EXPECT(a != b);
+
+ ASL_TEST_EXPECT(pool.is_valid(a));
+ ASL_TEST_EXPECT(pool.is_valid(b));
+
+ pool.release(a);
+
+ ASL_TEST_EXPECT(!pool.is_valid(a));
+ ASL_TEST_EXPECT(pool.is_valid(b));
+
+ auto c = pool.acquire_ensure({});
+
+ ASL_TEST_EXPECT(!c.is_null());
+ ASL_TEST_EXPECT(c.index() == 0);
+ ASL_TEST_EXPECT(c.gen() == 1);
+
+ ASL_TEST_EXPECT(a != c);
+ ASL_TEST_EXPECT(b != c);
+
+ ASL_TEST_EXPECT(!pool.is_valid(a));
+ ASL_TEST_EXPECT(pool.is_valid(b));
+ ASL_TEST_EXPECT(pool.is_valid(c));
+
+ pool.release(b);
+
+ ASL_TEST_EXPECT(!pool.is_valid(a));
+ ASL_TEST_EXPECT(!pool.is_valid(b));
+ ASL_TEST_EXPECT(pool.is_valid(c));
+
+ pool.release(c);
+
+ ASL_TEST_EXPECT(!pool.is_valid(a));
+ ASL_TEST_EXPECT(!pool.is_valid(b));
+ ASL_TEST_EXPECT(!pool.is_valid(c));
+}
+
+ASL_TEST(pool_acquire_release_a_lot)
+{
+ using Pool = asl::IndexPool<3, 3>;
+ Pool pool;
+
+ for (int i = 0; i < 80; ++i)
+ {
+ pool.release(pool.acquire_ensure({}));
+ }
+}
+
+ASL_TEST(pool_acquire_past_capacity)
+{
+ using Pool = asl::IndexPool<3, 3>;
+ Pool pool;
+
+ for (int i = 0; i < 8; ++i)
+ {
+ ASL_TEST_EXPECT(pool.acquire({}).has_value());
+ }
+
+ for (int i = 0; i < 8; ++i)
+ {
+ ASL_TEST_EXPECT(!pool.acquire({}).has_value());
+ }
}
diff --git a/asl/testing/testing.cpp b/asl/testing/testing.cpp
index 53e2bb0..45035d3 100644
--- a/asl/testing/testing.cpp
+++ b/asl/testing/testing.cpp
@@ -69,7 +69,7 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
asl::testing::Test* failed_head = nullptr;
- for (auto* it = g_state.head; it != nullptr; it = it->m_next)
+ for (auto* it = g_state.head; it != nullptr;)
{
asl::eprint(GREEN("[ RUN ]") " {}\n", it->m_case_name);
@@ -80,13 +80,17 @@ int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{
asl::eprint(GREEN("[ OK ]") " {}\n", it->m_case_name);
pass += 1;
+ it = it->m_next;
}
else
{
asl::eprint(RED("[ FAILED ]") " {}\n", it->m_case_name);
fail += 1;
- it->m_next = asl::exchange(failed_head, it);
+ auto* this_test = it;
+ it = it->m_next;
+
+ this_test->m_next = asl::exchange(failed_head, this_test);
}
}