Merge branch 'master' into feature/render

This commit is contained in:
2018-10-10 05:13:30 +07:00
6 changed files with 331 additions and 3 deletions

View File

@@ -524,3 +524,21 @@ namespace e2d { namespace stdex
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;
}
};
}

View File

@@ -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;
};
}

View File

@@ -4,10 +4,61 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#ifndef E2D_INCLUDE_GUARD_4540A3A22AD942D8A6B0C77A3346E73A
#define E2D_INCLUDE_GUARD_4540A3A22AD942D8A6B0C77A3346E73A
#pragma once
#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);
@@ -30,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;
@@ -49,10 +105,22 @@ namespace e2d
str_view fmt, Args&&... 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);
}
}
#include "strings.inl"
#endif

View File

@@ -4,11 +4,183 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#ifndef E2D_INCLUDE_GUARD_5BC5A803A3694674845E9953209E8CBE
#define E2D_INCLUDE_GUARD_5BC5A803A3694674845E9953209E8CBE
#pragma once
#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
{
//
@@ -423,3 +595,5 @@ namespace e2d { namespace strings
}
}
}}
#endif

View File

@@ -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

View File

@@ -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");