mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 08:15:38 +07:00
Merge branch 'master' into feature/render
This commit is contained in:
@@ -524,3 +524,21 @@ namespace e2d { namespace stdex
|
|||||||
return !(l == r);
|
return !(l == r);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
namespace std
|
||||||
|
{
|
||||||
|
template < typename Char, typename Traits >
|
||||||
|
struct hash<e2d::stdex::basic_string_view<Char, Traits>>
|
||||||
|
: std::unary_function<e2d::stdex::basic_string_view<Char, Traits>, std::size_t>
|
||||||
|
{
|
||||||
|
// Inspired by:
|
||||||
|
// http://www.cse.yorku.ca/~oz/hash.html
|
||||||
|
std::size_t operator()(e2d::stdex::basic_string_view<Char, Traits> sv) const noexcept {
|
||||||
|
std::size_t hash = 0;
|
||||||
|
for ( Char c : sv ) {
|
||||||
|
hash = c + (hash << 6u) + (hash << 16u) - hash;
|
||||||
|
}
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ namespace e2d
|
|||||||
|
|
||||||
template < typename T >
|
template < typename T >
|
||||||
class module;
|
class module;
|
||||||
|
|
||||||
|
template < typename Char >
|
||||||
|
class basic_string_hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace e2d
|
namespace e2d
|
||||||
@@ -38,6 +41,11 @@ namespace e2d
|
|||||||
using str16_view = basic_string_view<char16_t>;
|
using str16_view = basic_string_view<char16_t>;
|
||||||
using str32_view = basic_string_view<char32_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 seconds_tag {};
|
||||||
struct milliseconds_tag {};
|
struct milliseconds_tag {};
|
||||||
struct microseconds_tag {};
|
struct microseconds_tag {};
|
||||||
@@ -53,10 +61,11 @@ namespace e2d
|
|||||||
namespace e2d
|
namespace e2d
|
||||||
{
|
{
|
||||||
class noncopyable {
|
class noncopyable {
|
||||||
|
public:
|
||||||
|
noncopyable(const noncopyable&) = delete;
|
||||||
|
noncopyable& operator=(const noncopyable&) = delete;
|
||||||
protected:
|
protected:
|
||||||
noncopyable() = default;
|
noncopyable() = default;
|
||||||
~noncopyable() = default;
|
~noncopyable() = default;
|
||||||
noncopyable(const noncopyable&) = delete;
|
|
||||||
noncopyable& operator=(const noncopyable&) = delete;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,61 @@
|
|||||||
* Copyright (C) 2018 Matvey Cherevko
|
* Copyright (C) 2018 Matvey Cherevko
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef E2D_INCLUDE_GUARD_4540A3A22AD942D8A6B0C77A3346E73A
|
||||||
|
#define E2D_INCLUDE_GUARD_4540A3A22AD942D8A6B0C77A3346E73A
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "_utils.hpp"
|
#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
|
namespace e2d
|
||||||
{
|
{
|
||||||
str make_utf8(str_view src);
|
str make_utf8(str_view src);
|
||||||
@@ -30,6 +81,11 @@ namespace e2d
|
|||||||
str32 make_utf32(str16_view src);
|
str32 make_utf32(str16_view src);
|
||||||
str32 make_utf32(str32_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
|
namespace strings
|
||||||
{
|
{
|
||||||
class format_error;
|
class format_error;
|
||||||
@@ -49,10 +105,22 @@ namespace e2d
|
|||||||
str_view fmt, Args&&... args);
|
str_view fmt, Args&&... args);
|
||||||
|
|
||||||
template < typename... Args >
|
template < typename... Args >
|
||||||
str rformat(str_view fmt, Args&&... args);
|
bool format_nothrow(
|
||||||
|
char* dst, std::size_t dst_size, std::size_t* length,
|
||||||
|
str_view fmt, Args&&... args) noexcept;
|
||||||
|
|
||||||
|
template < typename... Args >
|
||||||
|
str rformat(
|
||||||
|
str_view fmt, Args&&... args);
|
||||||
|
|
||||||
|
template < typename... Args >
|
||||||
|
bool rformat_nothrow(
|
||||||
|
str& dst,
|
||||||
|
str_view fmt, Args&&... args) noexcept;
|
||||||
|
|
||||||
bool wildcard_match(str_view string, str_view pattern);
|
bool wildcard_match(str_view string, str_view pattern);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "strings.inl"
|
#include "strings.inl"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -4,11 +4,183 @@
|
|||||||
* Copyright (C) 2018 Matvey Cherevko
|
* Copyright (C) 2018 Matvey Cherevko
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef E2D_INCLUDE_GUARD_5BC5A803A3694674845E9953209E8CBE
|
||||||
|
#define E2D_INCLUDE_GUARD_5BC5A803A3694674845E9953209E8CBE
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "_utils.hpp"
|
#include "_utils.hpp"
|
||||||
#include "strings.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
|
namespace e2d { namespace strings
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@@ -423,3 +595,5 @@ namespace e2d { namespace strings
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -238,6 +238,26 @@ namespace e2d
|
|||||||
? str32()
|
? str32()
|
||||||
: str32(src.cbegin(), src.cend());
|
: 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
|
namespace e2d { namespace strings
|
||||||
|
|||||||
@@ -18,6 +18,45 @@ namespace
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("strings") {
|
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("hello") == "hello");
|
||||||
REQUIRE(make_utf8(L"hello") == "hello");
|
REQUIRE(make_utf8(L"hello") == "hello");
|
||||||
|
|||||||
Reference in New Issue
Block a user