diff options
-rw-r--r-- | include/fud_hash_map.hpp | 16 | ||||
-rw-r--r-- | include/fud_hash_map_impl.hpp | 8 | ||||
-rw-r--r-- | include/fud_vector.hpp | 53 | ||||
-rw-r--r-- | test/test_hash_map.cpp | 194 |
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 |