From 0fd9509556c4ef9893acb03c6f58668f0ed1abdf Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Thu, 2 Feb 2023 02:28:38 +0700 Subject: [PATCH] move fnv1a_hash to the personal header --- .vscode/settings.json | 6 + .../manuals/meta_manuals/inplace_manual.cpp | 4 +- develop/singles/headers/meta.hpp/meta_all.hpp | 166 +++++++++++------- .../untests/meta_base/fnv1a_hash_tests.cpp | 29 +++ .../untests/meta_base/hashed_string_tests.cpp | 2 +- .../untests/meta_base/memory_buffer_tests.cpp | 131 +++++++++++--- headers/meta.hpp/meta_base.hpp | 1 + headers/meta.hpp/meta_base/base.hpp | 2 + headers/meta.hpp/meta_base/fnv1a_hash.hpp | 48 +++++ headers/meta.hpp/meta_base/hashed_string.hpp | 53 +----- headers/meta.hpp/meta_base/intrusive_ptr.hpp | 8 +- headers/meta.hpp/meta_base/memory_buffer.hpp | 66 +++++-- 12 files changed, 350 insertions(+), 166 deletions(-) create mode 100644 develop/untests/meta_base/fnv1a_hash_tests.cpp create mode 100644 headers/meta.hpp/meta_base/fnv1a_hash.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json index c7e1c18..3c61139 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -17,6 +17,12 @@ "files.trimFinalNewlines": true, "files.trimTrailingWhitespace": true }, + "[yaml]": { + "files.encoding": "utf8", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true, + "files.trimTrailingWhitespace": true + }, "clangd.arguments": [ "--header-insertion=never" ], diff --git a/develop/manuals/meta_manuals/inplace_manual.cpp b/develop/manuals/meta_manuals/inplace_manual.cpp index 8c3a0b0..427de8c 100644 --- a/develop/manuals/meta_manuals/inplace_manual.cpp +++ b/develop/manuals/meta_manuals/inplace_manual.cpp @@ -54,7 +54,7 @@ TEST_CASE("meta/meta_manuals/inplace") { // alignas(ivec2) std::byte ivec2_buffer[sizeof(ivec2)]; // creates 'ivec2' on the memory buffer ('placement new' under the hood) - const meta::uvalue ivec2_ptr = ivec2_type.create_at(ivec2_buffer.get_memory(), 2, 3); + const meta::uvalue ivec2_ptr = ivec2_type.create_at(ivec2_buffer.get_data(), 2, 3); // 'create_at' returns a pointer to the constructed object CHECK(ivec2_ptr.get_type() == meta::resolve_type()); @@ -65,5 +65,5 @@ TEST_CASE("meta/meta_manuals/inplace") { CHECK(ivec2_length2(ivec2_ptr).get_as() == 13); // you must manually call the object's destructor - CHECK(ivec2_type.destroy_at(ivec2_buffer.get_memory())); + CHECK(ivec2_type.destroy_at(ivec2_buffer.get_data())); } diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index b39dbe8..fa4bc4b 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -8,10 +8,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -515,6 +517,45 @@ namespace meta_hpp::detail fixed_function(F) -> fixed_function; } +namespace meta_hpp::detail +{ + // REFERENCE: + // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + + template < std::size_t SizeBits = CHAR_BIT * sizeof(std::size_t) > + struct fnv1a_hash_traits; + + template <> + struct fnv1a_hash_traits<32> { // NOLINT(*-magic-numbers) + using underlying_type = std::uint32_t; + static inline constexpr underlying_type prime{16777619U}; + static inline constexpr underlying_type offset_basis{2166136261U}; + }; + + template <> + struct fnv1a_hash_traits<64> { // NOLINT(*-magic-numbers) + using underlying_type = std::uint64_t; + static inline constexpr underlying_type prime{1099511628211U}; + static inline constexpr underlying_type offset_basis{14695981039346656037U}; + }; + + template < typename T > + requires (std::is_same_v) || (std::is_integral_v && sizeof(T) == 1) + constexpr std::size_t fnv1a_hash(const T* mem, std::size_t size) noexcept { + using traits = fnv1a_hash_traits<>; + std::size_t hash{traits::offset_basis}; + for ( T byte : std::span(mem, size) ) { + hash ^= static_cast(byte); + hash *= traits::prime; + } + return hash; + } + + inline std::size_t fnv1a_hash(const void* mem, std::size_t size) noexcept { + return fnv1a_hash(static_cast(mem), size); + } +} + namespace meta_hpp::detail { struct hash_combiner { @@ -531,35 +572,6 @@ namespace meta_hpp::detail }; } -namespace meta_hpp::detail -{ - template < std::size_t SizeBytes = sizeof(std::size_t) > - struct fnv1a_hash_traits; - - template <> - struct fnv1a_hash_traits<4> { // NOLINT(*-magic-numbers) - using underlying_type = std::uint32_t; - static inline constexpr underlying_type prime{16777619U}; - static inline constexpr underlying_type offset{2166136261U}; - }; - - template <> - struct fnv1a_hash_traits<8> { // NOLINT(*-magic-numbers) - using underlying_type = std::uint64_t; - static inline constexpr underlying_type prime{1099511628211U}; - static inline constexpr underlying_type offset{14695981039346656037U}; - }; - - constexpr std::size_t fnv1a_hash(std::string_view str) noexcept { - std::size_t hash{fnv1a_hash_traits<>::offset}; - for ( char ch : str ) { - hash ^= static_cast(ch); - hash *= fnv1a_hash_traits<>::prime; - } - return hash; - } -} - namespace meta_hpp::detail { class hashed_string final { @@ -574,7 +586,7 @@ namespace meta_hpp::detail hashed_string& operator=(const hashed_string&) = default; constexpr hashed_string(std::string_view str) noexcept - : hash_{fnv1a_hash(str)} {} + : hash_{fnv1a_hash(str.data(), str.size())} {} constexpr void swap(hashed_string& other) noexcept { std::swap(hash_, other.hash_); @@ -584,24 +596,13 @@ namespace meta_hpp::detail return hash_; } private: - std::size_t hash_{fnv1a_hash({})}; + std::size_t hash_{fnv1a_hash("", 0)}; }; - constexpr void swap(hashed_string& l, hashed_string& r) noexcept { - l.swap(r); - } - - [[nodiscard]] constexpr bool operator<(hashed_string l, hashed_string r) noexcept { - return l.get_hash() < r.get_hash(); - } - - [[nodiscard]] constexpr bool operator==(hashed_string l, hashed_string r) noexcept { - return l.get_hash() == r.get_hash(); - } - - [[nodiscard]] constexpr bool operator!=(hashed_string l, hashed_string r) noexcept { - return l.get_hash() != r.get_hash(); - } + constexpr void swap(hashed_string& l, hashed_string& r) noexcept { l.swap(r); } + [[nodiscard]] constexpr bool operator<(hashed_string l, hashed_string r) noexcept { return l.get_hash() < r.get_hash(); } + [[nodiscard]] constexpr bool operator==(hashed_string l, hashed_string r) noexcept { return l.get_hash() == r.get_hash(); } + [[nodiscard]] constexpr bool operator!=(hashed_string l, hashed_string r) noexcept { return l.get_hash() != r.get_hash(); } } namespace std @@ -821,17 +822,15 @@ namespace meta_hpp::detail T* ptr_{}; }; - template < typename T > - void swap(intrusive_ptr& l, intrusive_ptr& r) noexcept { - return l.swap(r); - } - template < typename T, typename... Args > intrusive_ptr make_intrusive(Args&&... args) { // NOLINTNEXTLINE(*-owning-memory) return new T(std::forward(args)...); } + template < typename T > + void swap(intrusive_ptr& l, intrusive_ptr& r) noexcept { return l.swap(r); } + template < typename T > [[nodiscard]] bool operator==(const intrusive_ptr& l, const intrusive_ptr& r) noexcept { return l.get() == r.get(); } template < typename T > @@ -890,10 +889,10 @@ namespace meta_hpp::detail memory_buffer& operator=(const memory_buffer&) = delete; memory_buffer(memory_buffer&& other) noexcept - : memory_{other.memory_} + : data_{other.data_} , size_{other.size_} , align_{other.align_} { - other.memory_ = nullptr; + other.data_ = nullptr; other.size_ = 0; other.align_ = std::align_val_t{}; } @@ -906,16 +905,23 @@ namespace meta_hpp::detail } explicit memory_buffer(std::size_t size, std::align_val_t align) - : memory_{::operator new(size, align)} + : data_{::operator new(size, align)} , size_{size} , align_{align} {} + explicit memory_buffer(const void* mem, std::size_t size, std::align_val_t align) + : memory_buffer{size, align} { + if ( mem != nullptr && size > 0 ) { + std::memcpy(data_, mem, size); + } + } + ~memory_buffer() noexcept { reset(); } [[nodiscard]] bool is_valid() const noexcept { - return memory_ != nullptr; + return data_ != nullptr; } [[nodiscard]] explicit operator bool() const noexcept { @@ -923,26 +929,40 @@ namespace meta_hpp::detail } void reset() noexcept { - if ( memory_ != nullptr ) { - ::operator delete(memory_, align_); - memory_ = nullptr; + if ( data_ != nullptr ) { + ::operator delete(data_, align_); + data_ = nullptr; size_ = 0; align_ = std::align_val_t{}; } } void swap(memory_buffer& other) noexcept { - std::swap(memory_, other.memory_); + std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(align_, other.align_); } - [[nodiscard]] void* get_memory() noexcept { - return memory_; + [[nodiscard]] int compare(const memory_buffer& other) const noexcept { + if ( size_ < other.size_ ) { + return -1; + } + + if ( size_ > other.size_ ) { + return +1; + } + + return size_ != 0 + ? std::memcmp(data_, other.data_, size_) + : 0; } - [[nodiscard]] const void* get_memory() const noexcept { - return memory_; + [[nodiscard]] void* get_data() noexcept { + return data_; + } + + [[nodiscard]] const void* get_data() const noexcept { + return data_; } [[nodiscard]] std::size_t get_size() const noexcept { @@ -952,15 +972,27 @@ namespace meta_hpp::detail [[nodiscard]] std::align_val_t get_align() const noexcept { return align_; } + private: - void* memory_{}; + void* data_{}; std::size_t size_{}; std::align_val_t align_{}; }; - inline void swap(memory_buffer& l, memory_buffer& r) noexcept { - l.swap(r); - } + inline void swap(memory_buffer& l, memory_buffer& r) noexcept { l.swap(r); } + [[nodiscard]] inline bool operator<(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) < 0; } + [[nodiscard]] inline bool operator==(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) == 0; } + [[nodiscard]] inline bool operator!=(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) != 0; } +} + +namespace std +{ + template <> + struct hash { + size_t operator()(const meta_hpp::detail::memory_buffer& mb) const noexcept { + return meta_hpp::detail::fnv1a_hash(mb.get_data(), mb.get_size()); + } + }; } namespace meta_hpp::detail diff --git a/develop/untests/meta_base/fnv1a_hash_tests.cpp b/develop/untests/meta_base/fnv1a_hash_tests.cpp new file mode 100644 index 0000000..8b59681 --- /dev/null +++ b/develop/untests/meta_base/fnv1a_hash_tests.cpp @@ -0,0 +1,29 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include +#include + +TEST_CASE("meta/meta_base/fnv1a_hash") { + namespace meta = meta_hpp; + using meta::detail::fnv1a_hash; + + SUBCASE("const T*, size_t") { + constexpr const char* world = "world"; + constexpr const char* hello1 = "hello"; + constexpr const char* hello2 = "hello"; + static_assert(fnv1a_hash(world, 5) != fnv1a_hash(hello1, 5)); + static_assert(fnv1a_hash(hello1, 5) == fnv1a_hash(hello2, 5)); + } + + SUBCASE("const void*, size_t") { + constexpr const void* world = "world"; + constexpr const void* hello1 = "hello"; + constexpr const void* hello2 = "hello"; + CHECK(fnv1a_hash(world, 5) != fnv1a_hash(hello1, 5)); + CHECK(fnv1a_hash(hello1, 5) == fnv1a_hash(hello2, 5)); + } +} diff --git a/develop/untests/meta_base/hashed_string_tests.cpp b/develop/untests/meta_base/hashed_string_tests.cpp index f300cd2..3c1312f 100644 --- a/develop/untests/meta_base/hashed_string_tests.cpp +++ b/develop/untests/meta_base/hashed_string_tests.cpp @@ -22,7 +22,7 @@ TEST_CASE("meta/meta_base/hashed_string") { SUBCASE("ctor/1") { constexpr hashed_string hs{"hello"}; - static_assert(hs.get_hash() == meta::detail::fnv1a_hash("hello")); + static_assert(hs.get_hash() == meta::detail::fnv1a_hash("hello", 5)); CHECK(hs == hashed_string{std::string{"hello"}}); CHECK(hs == hashed_string{std::string_view{"hello"}}); diff --git a/develop/untests/meta_base/memory_buffer_tests.cpp b/develop/untests/meta_base/memory_buffer_tests.cpp index 5740549..feb746c 100644 --- a/develop/untests/meta_base/memory_buffer_tests.cpp +++ b/develop/untests/meta_base/memory_buffer_tests.cpp @@ -20,13 +20,13 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf.get_size() == 0); CHECK(buf.get_align() == std::align_val_t{}); - CHECK(buf.get_memory() == nullptr); - CHECK(std::as_const(buf).get_memory() == nullptr); + CHECK(buf.get_data() == nullptr); + CHECK(std::as_const(buf).get_data() == nullptr); } SUBCASE("ctor/1") { memory_buffer buf1{10, std::align_val_t{32}}; - const void* buf1_memory{buf1.get_memory()}; + const void* buf1_memory{buf1.get_data()}; memory_buffer buf2{std::move(buf1)}; @@ -37,8 +37,8 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf1.get_size() == 0); CHECK(buf1.get_align() == std::align_val_t{}); - CHECK(buf1.get_memory() == nullptr); - CHECK(std::as_const(buf1).get_memory() == nullptr); + CHECK(buf1.get_data() == nullptr); + CHECK(std::as_const(buf1).get_data() == nullptr); } { @@ -48,8 +48,8 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf2.get_size() == 10); CHECK(buf2.get_align() == std::align_val_t{32}); - CHECK(buf2.get_memory() == buf1_memory); - CHECK(std::as_const(buf2).get_memory() == buf1_memory); + CHECK(buf2.get_data() == buf1_memory); + CHECK(std::as_const(buf2).get_data() == buf1_memory); } } @@ -62,21 +62,20 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf.get_size() == 10); CHECK(buf.get_align() == std::align_val_t{32}); - CHECK(buf.get_memory()); - CHECK(std::as_const(buf).get_memory()); + CHECK(buf.get_data()); + CHECK(std::as_const(buf).get_data()); { - void* aligned_ptr{buf.get_memory()}; + void* aligned_ptr{buf.get_data()}; std::size_t aligned_size{buf.get_size()}; - CHECK(std::align( - meta::detail::to_underlying(buf.get_align()), buf.get_size(), - aligned_ptr, aligned_size) == buf.get_memory()); + std::size_t align{meta::detail::to_underlying(buf.get_align())}; + CHECK(std::align(align, buf.get_size(), aligned_ptr, aligned_size) == buf.get_data()); } } SUBCASE("operator=/0") { memory_buffer buf1{10, std::align_val_t{32}}; - const void* buf1_memory{buf1.get_memory()}; + const void* buf1_memory{buf1.get_data()}; memory_buffer buf2; buf2 = std::move(buf1); @@ -88,8 +87,8 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf1.get_size() == 0); CHECK(buf1.get_align() == std::align_val_t{}); - CHECK(buf1.get_memory() == nullptr); - CHECK(std::as_const(buf1).get_memory() == nullptr); + CHECK(buf1.get_data() == nullptr); + CHECK(std::as_const(buf1).get_data() == nullptr); } { @@ -99,14 +98,14 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf2.get_size() == 10); CHECK(buf2.get_align() == std::align_val_t{32}); - CHECK(buf2.get_memory() == buf1_memory); - CHECK(std::as_const(buf2).get_memory() == buf1_memory); + CHECK(buf2.get_data() == buf1_memory); + CHECK(std::as_const(buf2).get_data() == buf1_memory); } } SUBCASE("operator=/1") { memory_buffer buf1{10, std::align_val_t{32}}; - const void* buf1_memory{buf1.get_memory()}; + const void* buf1_memory{buf1.get_data()}; memory_buffer buf2{20, std::align_val_t{16}}; buf2 = std::move(buf1); @@ -118,8 +117,8 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf1.get_size() == 0); CHECK(buf1.get_align() == std::align_val_t{}); - CHECK(buf1.get_memory() == nullptr); - CHECK(std::as_const(buf1).get_memory() == nullptr); + CHECK(buf1.get_data() == nullptr); + CHECK(std::as_const(buf1).get_data() == nullptr); } { @@ -129,8 +128,8 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf2.get_size() == 10); CHECK(buf2.get_align() == std::align_val_t{32}); - CHECK(buf2.get_memory() == buf1_memory); - CHECK(std::as_const(buf2).get_memory() == buf1_memory); + CHECK(buf2.get_data() == buf1_memory); + CHECK(std::as_const(buf2).get_data() == buf1_memory); } } @@ -144,25 +143,99 @@ TEST_CASE("meta/meta_base/memory_buffer") { CHECK(buf.get_size() == 0); CHECK(buf.get_align() == std::align_val_t{}); - CHECK(buf.get_memory() == nullptr); - CHECK(std::as_const(buf).get_memory() == nullptr); + CHECK(buf.get_data() == nullptr); + CHECK(std::as_const(buf).get_data() == nullptr); } SUBCASE("swap") { memory_buffer buf1{10, std::align_val_t{32}}; memory_buffer buf2{15, std::align_val_t{16}}; - const void* buf1_memory{buf1.get_memory()}; - const void* buf2_memory{buf2.get_memory()}; + const void* buf1_memory{buf1.get_data()}; + const void* buf2_memory{buf2.get_data()}; meta::detail::swap(buf1, buf2); CHECK(buf1.get_size() == 15); CHECK(buf1.get_align() == std::align_val_t{16}); - CHECK(buf1.get_memory() == buf2_memory); + CHECK(buf1.get_data() == buf2_memory); CHECK(buf2.get_size() == 10); CHECK(buf2.get_align() == std::align_val_t{32}); - CHECK(buf2.get_memory() == buf1_memory); + CHECK(buf2.get_data() == buf1_memory); + } + + SUBCASE("operator<") { + memory_buffer buf1{5, std::align_val_t{1}}; + memory_buffer buf2{5, std::align_val_t{1}}; + memory_buffer buf3{6, std::align_val_t{1}}; + memory_buffer buf4{6, std::align_val_t{1}}; + + std::memcpy(buf1.get_data(), "hello", 5); + std::memcpy(buf2.get_data(), "world", 5); + std::memcpy(buf3.get_data(), "hello2", 6); + std::memcpy(buf4.get_data(), "hello2", 6); + + CHECK_FALSE(buf1 < buf1); + + CHECK(buf1 < buf2); + CHECK_FALSE(buf2 < buf1); + + CHECK(buf1 < buf3); + CHECK_FALSE(buf3 < buf1); + + CHECK_FALSE(buf3 < buf4); + CHECK_FALSE(buf4 < buf3); + } + + SUBCASE("operator==") { + memory_buffer buf1{5, std::align_val_t{1}}; + memory_buffer buf2{5, std::align_val_t{1}}; + memory_buffer buf3{6, std::align_val_t{1}}; + memory_buffer buf4{6, std::align_val_t{1}}; + + std::memcpy(buf1.get_data(), "hello", 5); + std::memcpy(buf2.get_data(), "world", 5); + std::memcpy(buf3.get_data(), "hello2", 6); + std::memcpy(buf4.get_data(), "hello2", 6); + + CHECK(buf1 == buf1); + + CHECK_FALSE(buf1 == buf2); + CHECK_FALSE(buf1 == buf3); + + CHECK(buf3 == buf4); + } + + SUBCASE("operator!=") { + memory_buffer buf1{5, std::align_val_t{1}}; + memory_buffer buf2{5, std::align_val_t{1}}; + memory_buffer buf3{6, std::align_val_t{1}}; + memory_buffer buf4{6, std::align_val_t{1}}; + + std::memcpy(buf1.get_data(), "hello", 5); + std::memcpy(buf2.get_data(), "world", 5); + std::memcpy(buf3.get_data(), "hello2", 6); + std::memcpy(buf4.get_data(), "hello2", 6); + + CHECK_FALSE(buf1 != buf1); + + CHECK(buf1 != buf2); + CHECK(buf1 != buf3); + + CHECK_FALSE(buf3 != buf4); + } + + SUBCASE("hash") { + memory_buffer buf1{"hello", 5, std::align_val_t{1}}; + memory_buffer buf2{"hello", 5, std::align_val_t{1}}; + memory_buffer buf3{"world", 5, std::align_val_t{1}}; + memory_buffer buf4{"worlX", 5, std::align_val_t{1}}; + memory_buffer buf5{"worlX2", 6, std::align_val_t{1}}; + + CHECK(std::hash{}(buf1) == std::hash{}(buf2)); + CHECK(std::hash{}(buf2) != std::hash{}(buf3)); + CHECK(std::hash{}(buf3) != std::hash{}(buf4)); + CHECK(std::hash{}(buf4) != std::hash{}(buf5)); } } diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index bc3d51d..00f5578 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -12,6 +12,7 @@ #include "meta_base/cvref_traits.hpp" #include "meta_base/exceptions.hpp" #include "meta_base/fixed_function.hpp" +#include "meta_base/fnv1a_hash.hpp" #include "meta_base/hash_combiner.hpp" #include "meta_base/hashed_string.hpp" #include "meta_base/insert_or_assign.hpp" diff --git a/headers/meta.hpp/meta_base/base.hpp b/headers/meta.hpp/meta_base/base.hpp index dc8484d..63386d2 100644 --- a/headers/meta.hpp/meta_base/base.hpp +++ b/headers/meta.hpp/meta_base/base.hpp @@ -15,9 +15,11 @@ #endif #include +#include #include #include #include +#include #include #include diff --git a/headers/meta.hpp/meta_base/fnv1a_hash.hpp b/headers/meta.hpp/meta_base/fnv1a_hash.hpp new file mode 100644 index 0000000..01d4781 --- /dev/null +++ b/headers/meta.hpp/meta_base/fnv1a_hash.hpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "base.hpp" + +namespace meta_hpp::detail +{ + // REFERENCE: + // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function + + template < std::size_t SizeBits = CHAR_BIT * sizeof(std::size_t) > + struct fnv1a_hash_traits; + + template <> + struct fnv1a_hash_traits<32> { // NOLINT(*-magic-numbers) + using underlying_type = std::uint32_t; + static inline constexpr underlying_type prime{16777619U}; + static inline constexpr underlying_type offset_basis{2166136261U}; + }; + + template <> + struct fnv1a_hash_traits<64> { // NOLINT(*-magic-numbers) + using underlying_type = std::uint64_t; + static inline constexpr underlying_type prime{1099511628211U}; + static inline constexpr underlying_type offset_basis{14695981039346656037U}; + }; + + template < typename T > + requires (std::is_same_v) || (std::is_integral_v && sizeof(T) == 1) + constexpr std::size_t fnv1a_hash(const T* mem, std::size_t size) noexcept { + using traits = fnv1a_hash_traits<>; + std::size_t hash{traits::offset_basis}; + for ( T byte : std::span(mem, size) ) { + hash ^= static_cast(byte); + hash *= traits::prime; + } + return hash; + } + + inline std::size_t fnv1a_hash(const void* mem, std::size_t size) noexcept { + return fnv1a_hash(static_cast(mem), size); + } +} diff --git a/headers/meta.hpp/meta_base/hashed_string.hpp b/headers/meta.hpp/meta_base/hashed_string.hpp index f612677..f514ee3 100644 --- a/headers/meta.hpp/meta_base/hashed_string.hpp +++ b/headers/meta.hpp/meta_base/hashed_string.hpp @@ -7,35 +7,7 @@ #pragma once #include "base.hpp" - -namespace meta_hpp::detail -{ - template < std::size_t SizeBytes = sizeof(std::size_t) > - struct fnv1a_hash_traits; - - template <> - struct fnv1a_hash_traits<4> { // NOLINT(*-magic-numbers) - using underlying_type = std::uint32_t; - static inline constexpr underlying_type prime{16777619U}; - static inline constexpr underlying_type offset{2166136261U}; - }; - - template <> - struct fnv1a_hash_traits<8> { // NOLINT(*-magic-numbers) - using underlying_type = std::uint64_t; - static inline constexpr underlying_type prime{1099511628211U}; - static inline constexpr underlying_type offset{14695981039346656037U}; - }; - - constexpr std::size_t fnv1a_hash(std::string_view str) noexcept { - std::size_t hash{fnv1a_hash_traits<>::offset}; - for ( char ch : str ) { - hash ^= static_cast(ch); - hash *= fnv1a_hash_traits<>::prime; - } - return hash; - } -} +#include "fnv1a_hash.hpp" namespace meta_hpp::detail { @@ -51,7 +23,7 @@ namespace meta_hpp::detail hashed_string& operator=(const hashed_string&) = default; constexpr hashed_string(std::string_view str) noexcept - : hash_{fnv1a_hash(str)} {} + : hash_{fnv1a_hash(str.data(), str.size())} {} constexpr void swap(hashed_string& other) noexcept { std::swap(hash_, other.hash_); @@ -61,24 +33,13 @@ namespace meta_hpp::detail return hash_; } private: - std::size_t hash_{fnv1a_hash({})}; + std::size_t hash_{fnv1a_hash("", 0)}; }; - constexpr void swap(hashed_string& l, hashed_string& r) noexcept { - l.swap(r); - } - - [[nodiscard]] constexpr bool operator<(hashed_string l, hashed_string r) noexcept { - return l.get_hash() < r.get_hash(); - } - - [[nodiscard]] constexpr bool operator==(hashed_string l, hashed_string r) noexcept { - return l.get_hash() == r.get_hash(); - } - - [[nodiscard]] constexpr bool operator!=(hashed_string l, hashed_string r) noexcept { - return l.get_hash() != r.get_hash(); - } + constexpr void swap(hashed_string& l, hashed_string& r) noexcept { l.swap(r); } + [[nodiscard]] constexpr bool operator<(hashed_string l, hashed_string r) noexcept { return l.get_hash() < r.get_hash(); } + [[nodiscard]] constexpr bool operator==(hashed_string l, hashed_string r) noexcept { return l.get_hash() == r.get_hash(); } + [[nodiscard]] constexpr bool operator!=(hashed_string l, hashed_string r) noexcept { return l.get_hash() != r.get_hash(); } } namespace std diff --git a/headers/meta.hpp/meta_base/intrusive_ptr.hpp b/headers/meta.hpp/meta_base/intrusive_ptr.hpp index d34282c..b01c89d 100644 --- a/headers/meta.hpp/meta_base/intrusive_ptr.hpp +++ b/headers/meta.hpp/meta_base/intrusive_ptr.hpp @@ -141,17 +141,15 @@ namespace meta_hpp::detail T* ptr_{}; }; - template < typename T > - void swap(intrusive_ptr& l, intrusive_ptr& r) noexcept { - return l.swap(r); - } - template < typename T, typename... Args > intrusive_ptr make_intrusive(Args&&... args) { // NOLINTNEXTLINE(*-owning-memory) return new T(std::forward(args)...); } + template < typename T > + void swap(intrusive_ptr& l, intrusive_ptr& r) noexcept { return l.swap(r); } + template < typename T > [[nodiscard]] bool operator==(const intrusive_ptr& l, const intrusive_ptr& r) noexcept { return l.get() == r.get(); } template < typename T > diff --git a/headers/meta.hpp/meta_base/memory_buffer.hpp b/headers/meta.hpp/meta_base/memory_buffer.hpp index 5ee51f8..8dcd52d 100644 --- a/headers/meta.hpp/meta_base/memory_buffer.hpp +++ b/headers/meta.hpp/meta_base/memory_buffer.hpp @@ -7,6 +7,7 @@ #pragma once #include "base.hpp" +#include "fnv1a_hash.hpp" namespace meta_hpp::detail { @@ -18,10 +19,10 @@ namespace meta_hpp::detail memory_buffer& operator=(const memory_buffer&) = delete; memory_buffer(memory_buffer&& other) noexcept - : memory_{other.memory_} + : data_{other.data_} , size_{other.size_} , align_{other.align_} { - other.memory_ = nullptr; + other.data_ = nullptr; other.size_ = 0; other.align_ = std::align_val_t{}; } @@ -34,16 +35,23 @@ namespace meta_hpp::detail } explicit memory_buffer(std::size_t size, std::align_val_t align) - : memory_{::operator new(size, align)} + : data_{::operator new(size, align)} , size_{size} , align_{align} {} + explicit memory_buffer(const void* mem, std::size_t size, std::align_val_t align) + : memory_buffer{size, align} { + if ( mem != nullptr && size > 0 ) { + std::memcpy(data_, mem, size); + } + } + ~memory_buffer() noexcept { reset(); } [[nodiscard]] bool is_valid() const noexcept { - return memory_ != nullptr; + return data_ != nullptr; } [[nodiscard]] explicit operator bool() const noexcept { @@ -51,26 +59,40 @@ namespace meta_hpp::detail } void reset() noexcept { - if ( memory_ != nullptr ) { - ::operator delete(memory_, align_); - memory_ = nullptr; + if ( data_ != nullptr ) { + ::operator delete(data_, align_); + data_ = nullptr; size_ = 0; align_ = std::align_val_t{}; } } void swap(memory_buffer& other) noexcept { - std::swap(memory_, other.memory_); + std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(align_, other.align_); } - [[nodiscard]] void* get_memory() noexcept { - return memory_; + [[nodiscard]] int compare(const memory_buffer& other) const noexcept { + if ( size_ < other.size_ ) { + return -1; + } + + if ( size_ > other.size_ ) { + return +1; + } + + return size_ != 0 + ? std::memcmp(data_, other.data_, size_) + : 0; } - [[nodiscard]] const void* get_memory() const noexcept { - return memory_; + [[nodiscard]] void* get_data() noexcept { + return data_; + } + + [[nodiscard]] const void* get_data() const noexcept { + return data_; } [[nodiscard]] std::size_t get_size() const noexcept { @@ -80,13 +102,25 @@ namespace meta_hpp::detail [[nodiscard]] std::align_val_t get_align() const noexcept { return align_; } + private: - void* memory_{}; + void* data_{}; std::size_t size_{}; std::align_val_t align_{}; }; - inline void swap(memory_buffer& l, memory_buffer& r) noexcept { - l.swap(r); - } + inline void swap(memory_buffer& l, memory_buffer& r) noexcept { l.swap(r); } + [[nodiscard]] inline bool operator<(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) < 0; } + [[nodiscard]] inline bool operator==(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) == 0; } + [[nodiscard]] inline bool operator!=(const memory_buffer& l, const memory_buffer& r) noexcept { return l.compare(r) != 0; } +} + +namespace std +{ + template <> + struct hash { + size_t operator()(const meta_hpp::detail::memory_buffer& mb) const noexcept { + return meta_hpp::detail::fnv1a_hash(mb.get_data(), mb.get_size()); + } + }; }