mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 00:11:55 +07:00
basic_string_hash class
This commit is contained in:
@@ -24,6 +24,9 @@ namespace e2d
|
||||
|
||||
template < typename T >
|
||||
class module;
|
||||
|
||||
template < typename Char >
|
||||
class basic_string_hash;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
@@ -38,6 +41,11 @@ namespace e2d
|
||||
using str16_view = basic_string_view<char16_t>;
|
||||
using str32_view = basic_string_view<char32_t>;
|
||||
|
||||
using str_hash = basic_string_hash<char>;
|
||||
using wstr_hash = basic_string_hash<wchar_t>;
|
||||
using str16_hash = basic_string_hash<char16_t>;
|
||||
using str32_hash = basic_string_hash<char32_t>;
|
||||
|
||||
struct seconds_tag {};
|
||||
struct milliseconds_tag {};
|
||||
struct microseconds_tag {};
|
||||
@@ -53,10 +61,11 @@ namespace e2d
|
||||
namespace e2d
|
||||
{
|
||||
class noncopyable {
|
||||
public:
|
||||
noncopyable(const noncopyable&) = delete;
|
||||
noncopyable& operator=(const noncopyable&) = delete;
|
||||
protected:
|
||||
noncopyable() = default;
|
||||
~noncopyable() = default;
|
||||
noncopyable(const noncopyable&) = delete;
|
||||
noncopyable& operator=(const noncopyable&) = delete;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,55 @@
|
||||
|
||||
#include "_utils.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Char >
|
||||
class basic_string_hash final {
|
||||
public:
|
||||
static std::size_t empty_hash() noexcept;
|
||||
public:
|
||||
basic_string_hash() noexcept;
|
||||
~basic_string_hash() noexcept;
|
||||
|
||||
basic_string_hash(basic_string_hash&& other) noexcept;
|
||||
basic_string_hash& operator=(basic_string_hash&& other) noexcept;
|
||||
|
||||
basic_string_hash(const basic_string_hash& other) noexcept;
|
||||
basic_string_hash& operator=(const basic_string_hash& other) noexcept;
|
||||
|
||||
explicit basic_string_hash(basic_string_view<Char> str) noexcept;
|
||||
|
||||
basic_string_hash& assign(basic_string_hash&& other) noexcept;
|
||||
basic_string_hash& assign(const basic_string_hash& other) noexcept;
|
||||
basic_string_hash& assign(basic_string_view<Char> str) noexcept;
|
||||
|
||||
void swap(basic_string_hash& other) noexcept;
|
||||
void clear() noexcept;
|
||||
bool empty() const noexcept;
|
||||
|
||||
std::size_t hash() const noexcept;
|
||||
private:
|
||||
static std::size_t calculate_hash(
|
||||
basic_string_view<Char> str) noexcept;
|
||||
static void debug_check_collisions(
|
||||
std::size_t hash, basic_string_view<Char> str) noexcept;
|
||||
private:
|
||||
std::size_t hash_ = empty_hash();
|
||||
};
|
||||
|
||||
template < typename Char >
|
||||
void swap(basic_string_hash<Char>& l, basic_string_hash<Char>& r) noexcept;
|
||||
|
||||
template < typename Char >
|
||||
bool operator<(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept;
|
||||
|
||||
template < typename Char >
|
||||
bool operator==(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept;
|
||||
|
||||
template < typename Char >
|
||||
bool operator!=(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept;
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
str make_utf8(str_view src);
|
||||
@@ -32,6 +81,11 @@ namespace e2d
|
||||
str32 make_utf32(str16_view src);
|
||||
str32 make_utf32(str32_view src);
|
||||
|
||||
str_hash make_hash(str_view src);
|
||||
wstr_hash make_hash(wstr_view src);
|
||||
str16_hash make_hash(str16_view src);
|
||||
str32_hash make_hash(str32_view src);
|
||||
|
||||
namespace strings
|
||||
{
|
||||
class format_error;
|
||||
|
||||
@@ -11,6 +11,176 @@
|
||||
#include "_utils.hpp"
|
||||
#include "strings.hpp"
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Char >
|
||||
std::size_t basic_string_hash<Char>::empty_hash() noexcept {
|
||||
static std::size_t hash = calculate_hash(basic_string_view<Char>());
|
||||
return hash;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>::basic_string_hash() noexcept = default;
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>::~basic_string_hash() noexcept = default;
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>::basic_string_hash(
|
||||
basic_string_hash&& other) noexcept
|
||||
{
|
||||
assign(std::move(other));
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>& basic_string_hash<Char>::operator=(
|
||||
basic_string_hash&& other) noexcept
|
||||
{
|
||||
return assign(std::move(other));
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>::basic_string_hash(
|
||||
const basic_string_hash& other) noexcept
|
||||
{
|
||||
assign(other);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>& basic_string_hash<Char>::operator=(
|
||||
const basic_string_hash& other) noexcept
|
||||
{
|
||||
return assign(other);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>::basic_string_hash(
|
||||
basic_string_view<Char> str) noexcept
|
||||
{
|
||||
assign(str);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>& basic_string_hash<Char>::assign(
|
||||
basic_string_hash&& other) noexcept
|
||||
{
|
||||
if ( this != &other ) {
|
||||
swap(other);
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>& basic_string_hash<Char>::assign(
|
||||
const basic_string_hash& other) noexcept
|
||||
{
|
||||
if ( this != &other ) {
|
||||
hash_ = other.hash_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
basic_string_hash<Char>& basic_string_hash<Char>::assign(
|
||||
basic_string_view<Char> str) noexcept
|
||||
{
|
||||
hash_ = calculate_hash(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
void basic_string_hash<Char>::swap(basic_string_hash& other) noexcept {
|
||||
using std::swap;
|
||||
swap(hash_, other.hash_);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
void basic_string_hash<Char>::clear() noexcept {
|
||||
hash_ = empty_hash();
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
bool basic_string_hash<Char>::empty() const noexcept {
|
||||
return hash_ == empty_hash();
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::size_t basic_string_hash<Char>::hash() const noexcept {
|
||||
return hash_;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
std::size_t basic_string_hash<Char>::calculate_hash(
|
||||
basic_string_view<Char> str) noexcept
|
||||
{
|
||||
std::size_t hash = std::hash<basic_string_view<Char>>()(str);
|
||||
debug_check_collisions(hash, str);
|
||||
return hash;
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
void basic_string_hash<Char>::debug_check_collisions(
|
||||
std::size_t hash, basic_string_view<Char> str) noexcept
|
||||
{
|
||||
#if defined(E2D_BUILD_MODE) && E2D_BUILD_MODE == E2D_BUILD_MODE_DEBUG
|
||||
try {
|
||||
static std::mutex mutex;
|
||||
static hash_map<std::size_t, basic_string<Char>> table;
|
||||
std::lock_guard<std::mutex> guard(mutex);
|
||||
const auto iter = table.find(hash);
|
||||
if ( iter != table.end() ) {
|
||||
E2D_ASSERT_MSG(
|
||||
iter->second == str,
|
||||
"basic_string_hash: hash collision detected");
|
||||
} else {
|
||||
table.insert(std::make_pair(hash, str));
|
||||
}
|
||||
} catch (...) {
|
||||
E2D_ASSERT_MSG(false, "basic_string_hash: unexpected exception");
|
||||
throw;
|
||||
}
|
||||
#else
|
||||
E2D_UNUSED(hash, str);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d
|
||||
{
|
||||
template < typename Char >
|
||||
void swap(basic_string_hash<Char>& l, basic_string_hash<Char>& r) noexcept {
|
||||
l.swap(r);
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
bool operator<(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept {
|
||||
return l.hash() < r.hash();
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
bool operator==(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept {
|
||||
return l.hash() == r.hash();
|
||||
}
|
||||
|
||||
template < typename Char >
|
||||
bool operator!=(basic_string_hash<Char> l, basic_string_hash<Char> r) noexcept {
|
||||
return !(l == r);
|
||||
}
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template < typename Char >
|
||||
struct hash<e2d::basic_string_hash<Char>>
|
||||
: std::unary_function<e2d::basic_string_hash<Char>, std::size_t>
|
||||
{
|
||||
std::size_t operator()(e2d::basic_string_hash<Char> hs) const noexcept {
|
||||
return hs.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace e2d { namespace strings
|
||||
{
|
||||
//
|
||||
|
||||
@@ -238,6 +238,26 @@ namespace e2d
|
||||
? str32()
|
||||
: str32(src.cbegin(), src.cend());
|
||||
}
|
||||
|
||||
//
|
||||
// make_hash
|
||||
//
|
||||
|
||||
str_hash make_hash(str_view src) {
|
||||
return str_hash(src);
|
||||
}
|
||||
|
||||
wstr_hash make_hash(wstr_view src) {
|
||||
return wstr_hash(src);
|
||||
}
|
||||
|
||||
str16_hash make_hash(str16_view src) {
|
||||
return str16_hash(src);
|
||||
}
|
||||
|
||||
str32_hash make_hash(str32_view src) {
|
||||
return str32_hash(src);
|
||||
}
|
||||
}
|
||||
|
||||
namespace e2d { namespace strings
|
||||
|
||||
@@ -18,6 +18,45 @@ namespace
|
||||
}
|
||||
|
||||
TEST_CASE("strings") {
|
||||
{
|
||||
REQUIRE(str_hash().empty());
|
||||
REQUIRE(str_hash("").empty());
|
||||
REQUIRE_FALSE(str_hash("1").empty());
|
||||
|
||||
REQUIRE(str_hash().hash() == str_hash().hash());
|
||||
REQUIRE(str_hash().hash() == str_hash({null_utf8,0}).hash());
|
||||
REQUIRE(str_hash().hash() == wstr_hash({null_wide,0}).hash());
|
||||
REQUIRE(str_hash().hash() == str16_hash({null_utf16,0}).hash());
|
||||
REQUIRE(str_hash().hash() == str32_hash({null_utf32,0}).hash());
|
||||
REQUIRE(str_hash("hello").hash() == str_hash("hello").hash());
|
||||
REQUIRE(str_hash("world").hash() == str_hash("world").hash());
|
||||
REQUIRE(str_hash("hello").hash() != str_hash("world").hash());
|
||||
|
||||
REQUIRE(str_hash() == str_hash());
|
||||
REQUIRE(str_hash() == make_hash({null_utf8,0}));
|
||||
REQUIRE(wstr_hash() == make_hash({null_wide,0}));
|
||||
REQUIRE(str16_hash() == make_hash({null_utf16,0}));
|
||||
REQUIRE(str32_hash() == make_hash({null_utf32,0}));
|
||||
REQUIRE(str_hash("hello") == make_hash("hello"));
|
||||
REQUIRE(str_hash("world") == make_hash("world"));
|
||||
REQUIRE(str_hash("hello") != make_hash("world"));
|
||||
|
||||
REQUIRE(str_hash("hello").assign("world") == make_hash("world"));
|
||||
REQUIRE(str_hash("hello").assign(make_hash("world")) == make_hash("world"));
|
||||
{
|
||||
str_hash s("hello");
|
||||
s.clear();
|
||||
REQUIRE(s.empty());
|
||||
REQUIRE(s == str_hash());
|
||||
}
|
||||
{
|
||||
str_hash s1("hello");
|
||||
str_hash s2("world");
|
||||
s1.swap(s2);
|
||||
REQUIRE(s2 == str_hash("hello"));
|
||||
REQUIRE(s1 == make_hash("world"));
|
||||
}
|
||||
}
|
||||
{
|
||||
REQUIRE(make_utf8("hello") == "hello");
|
||||
REQUIRE(make_utf8(L"hello") == "hello");
|
||||
|
||||
Reference in New Issue
Block a user