summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominick Allen <djallen@librehumanitas.org>2025-04-03 06:07:34 -0500
committerDominick Allen <djallen@librehumanitas.org>2025-04-03 06:07:34 -0500
commit090af1b8097fecf6b09f3048811a44e11ece3242 (patch)
tree9995adaa16751562d72173b8510b6336aa2d5be3
parente6d9330a978373ffc24209b5edf03eda6491f818 (diff)
Test hash map extract pair, fix bugs.
-rw-r--r--include/fud_hash_map.hpp16
-rw-r--r--include/fud_hash_map_impl.hpp8
-rw-r--r--include/fud_vector.hpp53
-rw-r--r--test/test_hash_map.cpp194
4 files changed, 249 insertions, 22 deletions
diff --git a/include/fud_hash_map.hpp b/include/fud_hash_map.hpp
index 9220876..ba9e0c2 100644
--- a/include/fud_hash_map.hpp
+++ b/include/fud_hash_map.hpp
@@ -200,7 +200,7 @@ class HashMap {
if (hashIndexOption.isNone()) {
return insert(key, value);
}
- auto hashIndex{hashIndexOption.take()};
+ auto hashIndex{hashIndexOption.value()};
m_data[hashIndex].value() = value;
return FudStatus::Success;
}
@@ -211,7 +211,7 @@ class HashMap {
if (hashIndexOption.isNone()) {
return insert(key, std::move(value));
}
- auto hashIndex{hashIndexOption.take()};
+ auto hashIndex{hashIndexOption.value()};
m_data[hashIndex].value() = std::move(value);
return FudStatus::Success;
}
@@ -222,7 +222,7 @@ class HashMap {
if (hashIndexOption.isNone()) {
return insert(std::move(key), value);
}
- auto hashIndex{hashIndexOption.take()};
+ auto hashIndex{hashIndexOption.value()};
m_data[hashIndex].value() = value;
return FudStatus::Success;
}
@@ -233,7 +233,7 @@ class HashMap {
if (hashIndexOption.isNone()) {
return insert(std::move(key), std::move(value));
}
- auto hashIndex{hashIndexOption.take()};
+ auto hashIndex{hashIndexOption.value()};
m_data[hashIndex].value() = std::move(value);
return FudStatus::Success;
}
@@ -290,7 +290,7 @@ class HashMap {
return value;
}
- KeyValuePair extractPair(const Key& key)
+ Option<KeyValuePair> extractPair(const Key& key)
{
auto hashIndexOption = lookup(key);
if (hashIndexOption.isNone()) {
@@ -298,13 +298,13 @@ class HashMap {
}
auto hashIndex{hashIndexOption.value()};
- KeyValuePair kvPair{key, m_data[hashIndex].takeValue()};
+ KeyValuePair kvPair{std::move(m_data[hashIndex].takeKey()), std::move(m_data[hashIndex].takeValue())};
m_data[hashIndex].tombstone();
return kvPair;
}
- KeyValuePair extractPair(Key&& key)
+ Option<KeyValuePair> extractPair(Key&& key)
{
auto hashIndexOption = lookup(key);
if (hashIndexOption.isNone()) {
@@ -312,7 +312,7 @@ class HashMap {
}
auto hashIndex{hashIndexOption.value()};
- KeyValuePair kvPair{std::move(key), m_data[hashIndex].takeValue()};
+ KeyValuePair kvPair{std::move(key), std::move(m_data[hashIndex].takeValue())};
m_data[hashIndex].tombstone();
return kvPair;
diff --git a/include/fud_hash_map_impl.hpp b/include/fud_hash_map_impl.hpp
index 6e224f9..23f5653 100644
--- a/include/fud_hash_map_impl.hpp
+++ b/include/fud_hash_map_impl.hpp
@@ -151,7 +151,13 @@ struct MapEntry {
return *std::bit_cast<Value*>(m_valueData.data());
}
- [[nodiscard]] Value&& takeValue() &
+ [[nodiscard]] constexpr Key&& takeKey() &
+ {
+ fudAssert(hasValue());
+ return std::move(*std::bit_cast<Key*>(m_keyData.data()));
+ }
+
+ [[nodiscard]] constexpr Value&& takeValue() &
{
fudAssert(hasValue());
return std::move(*std::bit_cast<Value*>(m_valueData.data()));
diff --git a/include/fud_vector.hpp b/include/fud_vector.hpp
index 1ba2abf..b2827b4 100644
--- a/include/fud_vector.hpp
+++ b/include/fud_vector.hpp
@@ -181,9 +181,6 @@ class Vector {
Builder&& builder,
Allocator* allocator = &globalFudAllocator)
{
- // using BuilderResult = decltype(std::forward<Builder>(builder)(T{}));
- // static_assert(std::is_same_v<BuilderResult, FudStatus>);
-
auto status = Vector::initializeWithCapacity(output, count, allocator);
if (status != FudStatus::Success) {
return status;
@@ -201,7 +198,7 @@ class Vector {
return FudStatus::Success;
}
- static Result<Vector<T>, FudStatus> from(const Vector<T>& rhs, Option<Allocator*> allocatorOption = NullOpt)
+ static Result<Vector<T>, FudStatus> copy(const Vector<T>& rhs, Option<Allocator*> allocatorOption = NullOpt)
{
Allocator* allocator = nullptr;
if (allocatorOption.hasValue()) {
@@ -223,7 +220,7 @@ class Vector {
return spanResult.takeError();
}
Vector<T> output{};
- auto status = Vector::initializeFromSpan(output, rhs.m_length, allocator);
+ auto status = Vector::initializeFromSpan(output, spanResult.takeOkay(), allocator);
if (status != FudStatus::Success) {
return status;
}
@@ -231,7 +228,7 @@ class Vector {
}
template <size_t Size>
- static Result<Vector<T>, FudStatus> from(Span<const T, Size>& rhs, Allocator* allocator)
+ static Result<Vector<T>, FudStatus> from(Span<const T, Size> rhs, Allocator* allocator)
{
Vector<T> output{};
auto status = initializeFromSpan(output, rhs, allocator);
@@ -242,7 +239,7 @@ class Vector {
}
template <size_t Size>
- static FudStatus initializeFromSpan(Vector<T>& output, Span<const T, Size>& rhs, Allocator* allocator)
+ static FudStatus initializeFromSpan(Vector<T>& output, Span<const T, Size> rhs, Allocator* allocator)
{
auto status = Vector::initializeWithCapacity(output, rhs.size(), allocator);
if (status != FudStatus::Success) {
@@ -259,7 +256,38 @@ class Vector {
return Vector<T>{std::move(rhs)};
}
- FudStatus copy(const Vector<T>& rhs);
+ FudStatus clone(const Vector<T>& rhs)
+ {
+ fudAssert(&rhs != this);
+ auto cleanupStatus = cleanup();
+ if (cleanupStatus != FudStatus::Success) {
+ return cleanupStatus;
+ }
+
+ auto spanResult = rhs.span();
+ if (spanResult.isError()) {
+ return spanResult.takeError();
+ }
+
+ return initializeFromSpan(*this, spanResult.takeOkay(), rhs.m_allocator);
+ }
+
+ FudStatus copy(const Vector<T>& rhs)
+ {
+ fudAssert(&rhs != this);
+ auto* allocator = m_allocator;
+ auto cleanupStatus = cleanup();
+ if (cleanupStatus != FudStatus::Success) {
+ return cleanupStatus;
+ }
+
+ auto spanResult = rhs.span();
+ if (spanResult.isError()) {
+ return spanResult.takeError();
+ }
+
+ return initializeFromSpan(*this, spanResult.takeOkay(), allocator);
+ }
FudStatus take(Vector<T>&& rhs);
@@ -280,20 +308,19 @@ class Vector {
Result<Span<const T>, FudStatus> span() const
{
- using RetType = Result<Span<const T>, FudStatus>;
if (m_data == nullptr) {
- return RetType::error(FudStatus::ObjectInvalid);
+ return Error{FudStatus::ObjectInvalid};
}
- return RetType::okay(Span{m_data, m_length});
+ return Okay{Span{m_data, m_length}};
}
Result<Span<T>, FudStatus> span()
{
using RetType = Result<Span<T>, FudStatus>;
if (m_data == nullptr) {
- return RetType::error(FudStatus::ObjectInvalid);
+ return Error{FudStatus::ObjectInvalid};
}
- return RetType::okay(Span{m_data, m_length});
+ return Okay{Span{m_data, m_length}};
}
Result<Span<const T>, FudStatus> span(size_t count) const
diff --git a/test/test_hash_map.cpp b/test/test_hash_map.cpp
index 9f01766..e8842fa 100644
--- a/test/test_hash_map.cpp
+++ b/test/test_hash_map.cpp
@@ -155,6 +155,131 @@ TEST(FudHash, InsertCopyKeyCopyValue)
}
}
+TEST(FudHash, UpdateMoveKeyMoveValue)
+{
+ auto stringList{testStrings()};
+ HashMap<int, String> mapInt2String{};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto updateStatus = mapInt2String.update(index * 1, String::from(stringList[index]).takeOkay());
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapInt2String.size(), stringList.size());
+ EXPECT_GT(mapInt2String.capacity(), mapInt2String.size());
+
+ const String invalid{String::makeFromCString("Invalid").takeOkay()};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto updateStatus = mapInt2String.update(
+ index * 1,
+ String::from(stringList[stringList.size() - (index + 1)]).takeOkay());
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapInt2String.size(), stringList.size());
+ EXPECT_GT(mapInt2String.capacity(), mapInt2String.size());
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[stringList.size() - (index + 1)]);
+ }
+}
+
+TEST(FudHash, UpdateMoveKeyCopyValue)
+{
+ auto stringList{testStrings()};
+ HashMap<String, int> mapString2Int{};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto updateStatus = mapString2Int.update(String::from(stringList[index]).takeOkay(), index);
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapString2Int.size(), stringList.size());
+ EXPECT_GT(mapString2Int.capacity(), mapString2Int.size());
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ const int invalid = -1;
+ EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto value = index * 2;
+ auto updateStatus = mapString2Int.update(String::from(stringList[index]).takeOkay(), value);
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ const int invalid = -1;
+ EXPECT_EQ(mapString2Int.getConstRef(stringList[index]).valueOr(invalid), index * 2);
+ }
+}
+
+TEST(FudHash, UpdateCopyKeyMoveValue)
+{
+ auto stringList{testStrings()};
+ HashMap<int, String> mapInt2String{};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto updateStatus = mapInt2String.update(index, String::from(stringList[index]).takeOkay());
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapInt2String.size(), stringList.size());
+ EXPECT_GT(mapInt2String.capacity(), mapInt2String.size());
+
+ const String invalid{String::makeFromCString("Invalid").takeOkay()};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto updateStatus = mapInt2String.update(
+ index,
+ String::from(stringList[stringList.size() - (index + 1)]).takeOkay());
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[stringList.size() - (index + 1)]);
+ }
+}
+
+TEST(FudHash, UpdateCopyKeyCopyValue)
+{
+ auto stringViewList{testStringViews()};
+
+ HashMap<StringView, int> mapView2Int{};
+ for (int index = 0; index < static_cast<int>(stringViewList.size()); ++index) {
+ EXPECT_FALSE(mapView2Int.contains(stringViewList[index]));
+ auto updateStatus = mapView2Int.update(stringViewList[index], index);
+ EXPECT_TRUE(mapView2Int.contains(stringViewList[index]));
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapView2Int.size(), stringViewList.size());
+ EXPECT_GT(mapView2Int.capacity(), mapView2Int.size());
+
+ for (int index = 0; index < static_cast<int>(stringViewList.size()); ++index) {
+ const int invalid = -1;
+ EXPECT_EQ(mapView2Int.getConstRef(stringViewList[index]).valueOr(invalid), index);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringViewList.size()); ++index) {
+ int updateValue = index * 2;
+ auto updateStatus = mapView2Int.update(stringViewList[index], updateValue);
+ EXPECT_EQ(updateStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapView2Int.size(), stringViewList.size());
+ EXPECT_GT(mapView2Int.capacity(), mapView2Int.size());
+
+ for (int index = 0; index < static_cast<int>(stringViewList.size()); ++index) {
+ const int invalid = -1;
+ EXPECT_EQ(mapView2Int.getConstRef(stringViewList[index]).valueOr(invalid), index * 2);
+ }
+}
+
TEST(FudHash, RemoveKeyConstRef)
{
auto stringList{testStrings()};
@@ -173,7 +298,9 @@ TEST(FudHash, RemoveKeyConstRef)
}
for (const auto& entry : stringList) {
+ EXPECT_TRUE(mapString2Int.contains(entry));
EXPECT_EQ(mapString2Int.remove(entry), FudStatus::Success);
+ EXPECT_FALSE(mapString2Int.contains(entry));
}
for (const auto& entry : stringList) {
@@ -281,4 +408,71 @@ TEST(FudHash, ExtractMoveKey)
}
}
+TEST(FudHash, ExtractPairConstKey)
+{
+ auto stringList{testStrings()};
+ HashMap<String, int> mapString2Int{};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto insertStatus = mapString2Int.insert(String::from(stringList[index]).takeOkay(), index);
+ EXPECT_EQ(insertStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapString2Int.size(), stringList.size());
+ EXPECT_GT(mapString2Int.capacity(), mapString2Int.size());
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ const int invalid = -1;
+ EXPECT_EQ(mapString2Int.get(stringList[index]).valueOr(invalid), index);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto valueOpt{mapString2Int.extractPair(stringList[index])};
+ ASSERT_TRUE(valueOpt.hasValue());
+ EXPECT_EQ(valueOpt.value().m_key, stringList[index]);
+ EXPECT_EQ(valueOpt.value().m_value, index);
+ }
+
+ for (const auto& entry : stringList) {
+ EXPECT_TRUE(mapString2Int.extract(entry).isNone());
+ }
+
+ for (const auto& entry : stringList) {
+ const int invalid = -1;
+ EXPECT_EQ(mapString2Int.get(entry).valueOr(invalid), invalid);
+ }
+}
+
+TEST(FudHash, ExtractPairMoveKey)
+{
+ auto stringList{testStrings()};
+ HashMap<int, String> mapInt2String{};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto insertStatus = mapInt2String.insert(index, String::from(stringList[index]).takeOkay());
+ EXPECT_EQ(insertStatus, FudStatus::Success);
+ }
+
+ EXPECT_EQ(mapInt2String.size(), stringList.size());
+ EXPECT_GT(mapInt2String.capacity(), mapInt2String.size());
+
+ const String invalid{String::makeFromCString("Invalid").takeOkay()};
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), stringList[index]);
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ auto valueOpt{mapInt2String.extractPair(index * 1)};
+ ASSERT_TRUE(valueOpt.hasValue());
+ EXPECT_EQ(valueOpt.value().m_value, stringList[index]);
+ EXPECT_EQ(valueOpt.value().m_key, index);
+ }
+
+ for (int index = 0; index < stringList.size(); ++index) {
+ EXPECT_TRUE(mapInt2String.extract(index * 1).isNone());
+ }
+
+ for (int index = 0; index < static_cast<int>(stringList.size()); ++index) {
+ EXPECT_EQ(mapInt2String.getConstRef(index).valueOr(invalid), invalid);
+ }
+}
+
} // namespace fud