First
This commit is contained in:
+161
@@ -0,0 +1,161 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "tl/format.h"
|
||||
#include "tl/murmur_hash.h"
|
||||
#include "tl/string.h"
|
||||
#include <set>
|
||||
|
||||
#if defined TL_PLATFORM_WINDOWS_DESKTOP_APP
|
||||
# include <windows.h>
|
||||
//# include <debugapi.h>
|
||||
#endif
|
||||
#if defined TL_PLATFORM_ANDROID_FAMILY
|
||||
# include <android/log.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning( disable : 4996)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace tl
|
||||
{
|
||||
namespace assert
|
||||
{
|
||||
|
||||
tl::assert::response default_handler(const char* condition, const char* file, int line, const char* msg) noexcept
|
||||
{
|
||||
file = file ? file : "N/A";
|
||||
condition = condition ? condition : "N/A";
|
||||
|
||||
eastl::string msgf;
|
||||
if (msg)
|
||||
msgf = tl::format<eastl::string>("{}({}): Assert '{}' Failed, Message: {}", file, line, condition, msg);
|
||||
else
|
||||
msgf = tl::format<eastl::string>("{}({}): Assert '{}' Failed", file, line, condition);
|
||||
|
||||
eastl::string symbolicatedStackTraceString;// = tl::format<eastl::string>("{}", this_thread::get_stack_trace<64>());
|
||||
if (!symbolicatedStackTraceString.empty())
|
||||
{
|
||||
msgf.append("\n");
|
||||
msgf.append("------------ assert stack -------------\n");
|
||||
msgf.append(symbolicatedStackTraceString.c_str());
|
||||
msgf.append("---------------------------------------\n");
|
||||
}
|
||||
|
||||
#if defined TL_PLATFORM_WINDOWS_DESKTOP_APP
|
||||
//On Windows Desktop output to the debugger window as well
|
||||
OutputDebugStringA(msgf.c_str());
|
||||
OutputDebugStringA("\n");
|
||||
#endif
|
||||
|
||||
#if defined TL_PLATFORM_ANDROID_FAMILY
|
||||
__android_log_print(ANDROID_LOG_ERROR, "ASSERT", "%s\n", msgf.c_str());
|
||||
#else
|
||||
std::puts(msgf.c_str()); //implicit new line
|
||||
std::fflush(stdout);
|
||||
#endif
|
||||
|
||||
return response::BREAK;
|
||||
}
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
assert::handler& get_static_handler() noexcept
|
||||
{
|
||||
static assert::handler s_handler = &default_handler;
|
||||
return s_handler;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
handler set_handler(handler new_handler) noexcept
|
||||
{
|
||||
handler& handler_storage = detail::get_static_handler();
|
||||
const handler old_handler = handler_storage;
|
||||
handler_storage = new_handler;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace detail
|
||||
{
|
||||
response do_assert(const char* _condition, const char* file, int line) noexcept
|
||||
{
|
||||
const assert::handler& handler = detail::get_static_handler();
|
||||
return handler ? handler(_condition, file, line, nullptr) : response::BREAK;
|
||||
}
|
||||
|
||||
// This function is needed for GCC. Don't remove it! (Looking at you CV! :P)
|
||||
response do_assert(const char* _condition, const char* file, int line, const char* msg) noexcept
|
||||
{
|
||||
assert::handler& handler = detail::get_static_handler();
|
||||
if (handler)
|
||||
return handler(_condition, file, line, msg);
|
||||
|
||||
return response::BREAK;
|
||||
}
|
||||
|
||||
|
||||
//the const char* is built using the __FILE__ macro so it's safe to compare the pointer
|
||||
//MS NO IT'S NOT ON 64 BIT !!!!
|
||||
|
||||
typedef std::pair<uint32_t, int> key_t;
|
||||
|
||||
struct key_compare
|
||||
{
|
||||
bool operator() (const key_t& x, const key_t& y) const noexcept
|
||||
{
|
||||
return x.first < y.first || (x.first == y.first && x.second < y.second);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<key_t, key_compare> disabled_asserts_map_t;
|
||||
disabled_asserts_map_t& get_static_disabled_asserts() noexcept
|
||||
{
|
||||
static disabled_asserts_map_t s_disabled_asserts;
|
||||
return s_disabled_asserts;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool is_assert_enabled(const char* file, int line) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(file);
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
uint32_t hash = tl::murmur32(file, 0);
|
||||
const key_t key_t(hash, line);
|
||||
const disabled_asserts_map_t& map = get_static_disabled_asserts();
|
||||
return !map.contains(key_t);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void set_assert_enabled(const char* file, int line, bool enabled) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(file);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
uint32_t hash = tl::murmur32(file, 0);
|
||||
const key_t key_t(hash, line);
|
||||
disabled_asserts_map_t& map = get_static_disabled_asserts();
|
||||
if (enabled)
|
||||
map.erase(key_t);
|
||||
else
|
||||
map.insert(key_t);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "tl/crash.h"
|
||||
#include "tl/debug.h"
|
||||
#include "tl/detail/internal_assert.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning( disable : 4996)
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace tl
|
||||
{
|
||||
namespace crash
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
TL_API handler& get_static_handler() noexcept
|
||||
{
|
||||
static handler s_handler = &default_handler;
|
||||
return s_handler;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace tl
|
||||
{
|
||||
namespace crash
|
||||
{
|
||||
|
||||
handler set_handler(handler new_handler) noexcept
|
||||
{
|
||||
if (!new_handler)
|
||||
default_handler(__FILE__, __LINE__, "You need to set a valid crash handler");
|
||||
|
||||
handler& handler_storage = detail::get_static_handler();
|
||||
const handler old_handler = handler_storage;
|
||||
handler_storage = new_handler;
|
||||
return old_handler;
|
||||
}
|
||||
|
||||
response default_handler(const char* file, int line, const char* msg) noexcept
|
||||
{
|
||||
const assert::handler& assert_handler = assert::detail::get_static_handler();
|
||||
if (assert_handler && assert_handler("Crash", file, line, msg) == assert::response::BREAK)
|
||||
TL_BREAK();
|
||||
|
||||
return response::CRASH;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,307 @@
|
||||
#include <stdio.h>
|
||||
#include "tl/memory_buffer.h"
|
||||
#include "tl/crash.h"
|
||||
|
||||
#define SIZE (*reinterpret_cast<uint32_t*>(m_buf))
|
||||
#define CAPACITY (*reinterpret_cast<uint32_t*>(m_buf + sizeof(uint32_t)))
|
||||
#define DATA (m_buf + sizeof(Header))
|
||||
#define DATA_X(x) ((x).m_buf + sizeof(Header))
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
TL_API uint8_t s_empty_data[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
size_t memory_buffer::get_grow_capacity(size_t needed, size_t capacity)
|
||||
{
|
||||
const size_t newCapacity = capacity + capacity / 2;
|
||||
if (newCapacity < needed) // still not enough so allocate as much as needed
|
||||
return needed;
|
||||
return newCapacity;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::memory_buffer()
|
||||
: m_buf(s_empty_data)
|
||||
{
|
||||
}
|
||||
|
||||
memory_buffer::memory_buffer(size_t size, uint8_t value)
|
||||
: m_buf(s_empty_data)
|
||||
{
|
||||
if (size > 0)
|
||||
{
|
||||
if (value == 0)
|
||||
m_buf = static_cast<uint8_t*>(calloc(1, size + sizeof(Header)));
|
||||
else
|
||||
{
|
||||
m_buf = static_cast<uint8_t*>(malloc(size + sizeof(Header)));
|
||||
memset(DATA, value, size);
|
||||
}
|
||||
CAPACITY = static_cast<uint32_t>(size);
|
||||
SIZE = static_cast<uint32_t>(size);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::memory_buffer(const uint8_t* start, const uint8_t* end)
|
||||
: m_buf(s_empty_data)
|
||||
{
|
||||
append(start, end);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::memory_buffer(const uint8_t* start, size_t size)
|
||||
: m_buf(s_empty_data)
|
||||
{
|
||||
append(start, size);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::memory_buffer(const memory_buffer& other)
|
||||
: m_buf(s_empty_data)
|
||||
{
|
||||
const uint32_t otherSize = static_cast<uint32_t>(other.size());
|
||||
if (otherSize > 0)
|
||||
{
|
||||
reserve(otherSize);
|
||||
SIZE = static_cast<uint32_t>(otherSize);
|
||||
memcpy(DATA, DATA_X(other), otherSize);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::memory_buffer(memory_buffer&& other) noexcept
|
||||
{
|
||||
m_buf = other.m_buf;
|
||||
other.m_buf = s_empty_data;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer::~memory_buffer()
|
||||
{
|
||||
if (m_buf != s_empty_data)
|
||||
free(m_buf);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer& memory_buffer::operator=(const memory_buffer& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
const uint32_t otherSize = static_cast<uint32_t>(other.size());
|
||||
if (otherSize > 0)
|
||||
{
|
||||
if (capacity() < otherSize)
|
||||
reserve(otherSize);
|
||||
|
||||
SIZE = otherSize;
|
||||
memcpy(DATA, DATA_X(other), otherSize);
|
||||
}
|
||||
else if (m_buf != s_empty_data)
|
||||
SIZE = 0;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
memory_buffer& memory_buffer::operator=(memory_buffer&& other) noexcept
|
||||
{
|
||||
std::swap(m_buf, other.m_buf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool memory_buffer::operator==(const memory_buffer& other) const
|
||||
{
|
||||
const uint32_t mySize = static_cast<uint32_t>(size());
|
||||
const uint32_t otherSize = static_cast<uint32_t>(other.size());
|
||||
return mySize == otherSize && (mySize == 0 || memcmp(DATA, DATA_X(other), mySize) == 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool memory_buffer::operator!=(const memory_buffer& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::assign(const uint8_t* start, const uint8_t* end)
|
||||
{
|
||||
const size_t needed = end - start;
|
||||
if (needed == 0)
|
||||
return;
|
||||
|
||||
const uint32_t curCapacity = static_cast<uint32_t>(capacity());
|
||||
if (needed > curCapacity)
|
||||
reserve(get_grow_capacity(needed, curCapacity));
|
||||
|
||||
memcpy(DATA, start, needed);
|
||||
SIZE = static_cast<uint32_t>(needed);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::append(const memory_buffer& other)
|
||||
{
|
||||
const uint32_t otherSize = static_cast<uint32_t>(other.size());
|
||||
if (otherSize > 0)
|
||||
append(DATA_X(other), otherSize);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::append(const uint8_t* start, size_t appendSize)
|
||||
{
|
||||
if (appendSize == 0)
|
||||
return;
|
||||
|
||||
const uint32_t size = SIZE;
|
||||
const uint32_t capacity = CAPACITY;
|
||||
const uint32_t newSize = size + static_cast<uint32_t>(appendSize);
|
||||
if (newSize > capacity)
|
||||
reserve(get_grow_capacity(newSize, capacity));
|
||||
|
||||
memcpy(DATA + size, start, appendSize);
|
||||
SIZE = newSize;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::append(const uint8_t* start, const uint8_t* end)
|
||||
{
|
||||
TL_PLAIN_ASSERT(end >= start);
|
||||
append(start, end - start);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::erase(iterator begin, iterator end)
|
||||
{
|
||||
const uint32_t size = SIZE;
|
||||
TL_PLAIN_ASSERT(m_buf == s_empty_data || begin >= DATA && end <= DATA + size);
|
||||
const size_t sizeToErase = end - begin;
|
||||
if (sizeToErase == 0)
|
||||
return;
|
||||
|
||||
const size_t right = size - (end - DATA);
|
||||
if (right == 0)
|
||||
{
|
||||
SIZE = static_cast<uint32_t>(begin - DATA);
|
||||
return;
|
||||
}
|
||||
|
||||
memmove(begin, end, right);
|
||||
SIZE -= static_cast<uint32_t>(sizeToErase);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::clear()
|
||||
{
|
||||
if (m_buf != s_empty_data)
|
||||
SIZE = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::shrink_to_fit()
|
||||
{
|
||||
const uint32_t size = SIZE;
|
||||
if (size == 0)
|
||||
{
|
||||
if (m_buf != s_empty_data)
|
||||
free(m_buf);
|
||||
|
||||
m_buf = s_empty_data;
|
||||
}
|
||||
else if (CAPACITY > size)
|
||||
{
|
||||
m_buf = static_cast<uint8_t*>(realloc(m_buf, size + sizeof(Header)));
|
||||
CAPACITY = static_cast<uint32_t>(size);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::resize(size_t size, uint8_t value)
|
||||
{
|
||||
const uint32_t crtSize = SIZE;
|
||||
if (size == crtSize)
|
||||
return;
|
||||
|
||||
if (size < crtSize)
|
||||
SIZE = static_cast<uint32_t>(size);
|
||||
else
|
||||
{
|
||||
const uint32_t crtCapacity = static_cast<uint32_t>(capacity());
|
||||
if (size > crtCapacity)
|
||||
reserve(get_grow_capacity(size, crtCapacity));
|
||||
|
||||
memset(DATA + crtSize, value, size - crtSize);
|
||||
SIZE = static_cast<uint32_t>(size);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::resize_uninitialized(size_t size)
|
||||
{
|
||||
const uint32_t crtSize = SIZE;
|
||||
if (size == crtSize)
|
||||
return;
|
||||
|
||||
if (size < crtSize)
|
||||
SIZE = static_cast<uint32_t>(size);
|
||||
else
|
||||
{
|
||||
const uint32_t crtCapacity = static_cast<uint32_t>(capacity());
|
||||
if (size > crtCapacity)
|
||||
reserve(get_grow_capacity(size, crtCapacity));
|
||||
|
||||
#ifdef TL_DEBUG
|
||||
constexpr uint8_t value = 0xCD;
|
||||
memset(DATA + size, value, size - size);
|
||||
#endif
|
||||
|
||||
SIZE = static_cast<uint32_t>(size);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void memory_buffer::reserve(size_t capacity)
|
||||
{
|
||||
if (capacity <= this->capacity())
|
||||
return;
|
||||
|
||||
if (capacity + sizeof(Header) >= tl::numeric_limits<uint32_t>::max())
|
||||
TL_CRASH("memory_buffer too big: {}", capacity);
|
||||
|
||||
if (m_buf == s_empty_data)
|
||||
{
|
||||
m_buf = static_cast<uint8_t*>(malloc(capacity + sizeof(Header)));
|
||||
SIZE = 0;
|
||||
}
|
||||
else
|
||||
m_buf = static_cast<uint8_t*>(realloc(m_buf, capacity + sizeof(Header)));
|
||||
|
||||
CAPACITY = static_cast<uint32_t>(capacity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef _size
|
||||
#undef _capacity
|
||||
@@ -0,0 +1,615 @@
|
||||
#include "tl/assert.h"
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace tl
|
||||
{
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash2, by Austin Appleby
|
||||
|
||||
// Note - This code makes a few assumptions about how your machine behaves -
|
||||
|
||||
// 1. We can read a 4-byte value from any address without crashing
|
||||
// 2. sizeof(int) == 4
|
||||
|
||||
// And it has a few limitations -
|
||||
|
||||
// 1. It will not work incrementally.
|
||||
// 2. It will not produce the same results on little-endian and big-endian
|
||||
// machines.
|
||||
//uint32_t murmur32_ci(const void* _buffer, size_t size, uint32_t seed)
|
||||
//{
|
||||
// TL_PLAIN_ASSERT(_buffer);
|
||||
// if (!_buffer)
|
||||
// {
|
||||
// return seed;
|
||||
// }
|
||||
// const uint8_t* buffer = reinterpret_cast<const uint8_t*>(_buffer);
|
||||
|
||||
// // 'm' and 'r' are mixing constants generated offline.
|
||||
// // They're not really 'magic', they just happen to work well.
|
||||
|
||||
// const uint32_t m = 0x5bd1e995;
|
||||
// const int r = 24;
|
||||
|
||||
// // Initialize the hash to a 'random' value
|
||||
|
||||
// uint32_t h = seed ^ static_cast<uint32_t>(size);
|
||||
|
||||
// // Mix 4 bytes at a time into the hash
|
||||
|
||||
// while (size >= 4)
|
||||
// {
|
||||
// uint32_t k = *(uint32_t*)buffer | 0x20202020;
|
||||
|
||||
// k *= m;
|
||||
// k ^= k >> r;
|
||||
// k *= m;
|
||||
|
||||
// h *= m;
|
||||
// h ^= k;
|
||||
|
||||
// buffer += 4;
|
||||
// size -= 4;
|
||||
// }
|
||||
|
||||
// // Handle the last few bytes of the input array
|
||||
|
||||
// switch (size)
|
||||
// {
|
||||
// case 3: h ^= (buffer[2] | 0x20) << 16;
|
||||
// case 2: h ^= (buffer[1] | 0x20) << 8;
|
||||
// case 1: h ^= (buffer[0] | 0x20);
|
||||
// h *= m;
|
||||
// };
|
||||
|
||||
// // Do a few final mixes of the hash to ensure the last few
|
||||
// // bytes are well-incorporated.
|
||||
|
||||
// h ^= h >> 13;
|
||||
// h *= m;
|
||||
// h ^= h >> 15;
|
||||
|
||||
// return h;
|
||||
//}
|
||||
|
||||
//uint32_t murmur32_ci(const char* c_str, uint32_t seed)
|
||||
//{
|
||||
// TL_PLAIN_ASSERT(c_str);
|
||||
// if (c_str)
|
||||
// {
|
||||
// return murmur32_ci(c_str, strlen(c_str), seed);
|
||||
// }
|
||||
// return seed;
|
||||
//}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash2, by Austin Appleby
|
||||
|
||||
// Note - This code makes a few assumptions about how your machine behaves -
|
||||
|
||||
// 1. We can read a 4-byte value from any address without crashing
|
||||
// 2. sizeof(int) == 4
|
||||
|
||||
// And it has a few limitations -
|
||||
|
||||
// 1. It will not work incrementally.
|
||||
// 2. It will not produce the same results on little-endian and big-endian
|
||||
// machines.
|
||||
uint32_t murmur32(const void* _buffer, size_t size, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(_buffer);
|
||||
if (!_buffer)
|
||||
return seed;
|
||||
|
||||
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(_buffer);
|
||||
|
||||
// 'm' and 'r' are mixing constants generated offline.
|
||||
// They're not really 'magic', they just happen to work well.
|
||||
|
||||
constexpr uint32_t m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
|
||||
// Initialize the hash to a 'random' value
|
||||
|
||||
uint32_t h = seed ^ static_cast<uint32_t>(size);
|
||||
|
||||
// Mix 4 bytes at a time into the hash
|
||||
|
||||
while (size >= 4)
|
||||
{
|
||||
uint32_t k = *(uint32_t*)buffer;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
buffer += 4;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
// Handle the last few bytes of the input array
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 3: h ^= (buffer[2]) << 16;
|
||||
case 2: h ^= (buffer[1]) << 8;
|
||||
case 1: h ^= (buffer[0]);
|
||||
h *= m;
|
||||
};
|
||||
|
||||
// Do a few final mixes of the hash to ensure the last few
|
||||
// bytes are well-incorporated.
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
uint32_t murmur32(const char* c_str, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(c_str);
|
||||
if (c_str)
|
||||
return murmur32(c_str, strlen(c_str), seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
uint64_t murmur64(const void* _buffer, size_t size, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(_buffer);
|
||||
if (!_buffer)
|
||||
return seed;
|
||||
|
||||
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(_buffer);
|
||||
|
||||
constexpr uint32_t m = 0x5bd1e995;
|
||||
constexpr int r = 24;
|
||||
|
||||
uint32_t h1 = seed ^ static_cast<uint32_t>(size);
|
||||
uint32_t h2 = 0;
|
||||
|
||||
const uint32_t* data = (const uint32_t*)buffer;
|
||||
|
||||
while (size >= 8)
|
||||
{
|
||||
uint32_t k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
size -= 4;
|
||||
|
||||
uint32_t k2 = *data++;
|
||||
k2 *= m; k2 ^= k2 >> r; k2 *= m;
|
||||
h2 *= m; h2 ^= k2;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
if (size >= 4)
|
||||
{
|
||||
uint32_t k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 3: h2 ^= ((uint8_t*)data)[2] << 16;
|
||||
case 2: h2 ^= ((uint8_t*)data)[1] << 8;
|
||||
case 1: h2 ^= ((uint8_t*)data)[0];
|
||||
h2 *= m;
|
||||
};
|
||||
|
||||
h1 ^= h2 >> 18; h1 *= m;
|
||||
h2 ^= h1 >> 22; h2 *= m;
|
||||
h1 ^= h2 >> 17; h1 *= m;
|
||||
h2 ^= h1 >> 19; h2 *= m;
|
||||
|
||||
uint64_t h = h1;
|
||||
|
||||
h = static_cast<uint64_t>(h << 32) | static_cast<uint64_t>(h2);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
uint64_t murmur64_seed64(const void* _buffer, size_t size, uint64_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(_buffer);
|
||||
if (!_buffer)
|
||||
return seed;
|
||||
|
||||
const uint8_t* buffer = reinterpret_cast<const uint8_t*>(_buffer);
|
||||
|
||||
constexpr uint32_t m = 0x5bd1e995;
|
||||
constexpr int r = 24;
|
||||
|
||||
uint32_t h1 = static_cast<uint32_t>((seed & 0xFFFFFFFF) ^ size);
|
||||
uint32_t h2 = static_cast<uint32_t>((seed >> 32) ^ size);
|
||||
|
||||
const uint32_t* data = (const uint32_t*)buffer;
|
||||
|
||||
while (size >= 8)
|
||||
{
|
||||
uint32_t k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
size -= 4;
|
||||
|
||||
uint32_t k2 = *data++;
|
||||
k2 *= m; k2 ^= k2 >> r; k2 *= m;
|
||||
h2 *= m; h2 ^= k2;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
if (size >= 4)
|
||||
{
|
||||
uint32_t k1 = *data++;
|
||||
k1 *= m; k1 ^= k1 >> r; k1 *= m;
|
||||
h1 *= m; h1 ^= k1;
|
||||
size -= 4;
|
||||
}
|
||||
|
||||
switch (size)
|
||||
{
|
||||
case 3: h2 ^= ((uint8_t*)data)[2] << 16;
|
||||
case 2: h2 ^= ((uint8_t*)data)[1] << 8;
|
||||
case 1: h2 ^= ((uint8_t*)data)[0];
|
||||
h2 *= m;
|
||||
};
|
||||
|
||||
h1 ^= h2 >> 18; h1 *= m;
|
||||
h2 ^= h1 >> 22; h2 *= m;
|
||||
h1 ^= h2 >> 17; h1 *= m;
|
||||
h2 ^= h1 >> 19; h2 *= m;
|
||||
|
||||
uint64_t h = h1;
|
||||
|
||||
h = static_cast<uint64_t>(h << 32) | static_cast<uint64_t>(h2);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
uint64_t murmur64(const char* c_str, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(c_str);
|
||||
if (c_str)
|
||||
return murmur64(c_str, strlen(c_str), seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
uint32_t murmur_unaligned32(const char* c_str, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(c_str);
|
||||
if (c_str)
|
||||
return murmur_unaligned32(c_str, strlen(c_str), seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
uint64_t murmur_unaligned64(const char* c_str, uint32_t seed)
|
||||
{
|
||||
TL_PLAIN_ASSERT(c_str);
|
||||
if (c_str)
|
||||
return murmur_unaligned64(c_str, strlen(c_str), seed);
|
||||
|
||||
return seed;
|
||||
}
|
||||
|
||||
#define MIX(h,k,m) { k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; }
|
||||
|
||||
uint32_t murmur_unaligned32(const void * i_buffer, size_t i_size, uint32_t i_seed)
|
||||
{
|
||||
constexpr uint32_t m = 0x5bd1e995;
|
||||
constexpr int32_t r = 24;
|
||||
|
||||
//const unsigned char * data = (const unsigned char *)key;
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(i_buffer);
|
||||
|
||||
size_t len = i_size;
|
||||
uint32_t h = static_cast<uint32_t>(i_seed ^ len);
|
||||
|
||||
const size_t align = (size_t)data & 3;
|
||||
|
||||
if (align && (len >= 4))
|
||||
{
|
||||
// Pre-load the temp registers
|
||||
|
||||
uint32_t t = 0, d = 0;
|
||||
|
||||
switch (align)
|
||||
{
|
||||
case 1: t |= data[2] << 16;
|
||||
case 2: t |= data[1] << 8;
|
||||
case 3: t |= data[0];
|
||||
}
|
||||
|
||||
t <<= (8 * align);
|
||||
|
||||
data += 4 - align;
|
||||
len -= 4 - align;
|
||||
|
||||
const size_t sl = 8 * (4 - align);
|
||||
const size_t sr = 8 * align;
|
||||
|
||||
// Mix
|
||||
|
||||
while (len >= 4)
|
||||
{
|
||||
d = *(uint32_t *)data;
|
||||
t = (t >> sr) | (d << sl);
|
||||
|
||||
uint32_t k = t;
|
||||
|
||||
MIX(h, k, m);
|
||||
|
||||
t = d;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
// Handle leftover data in temp registers
|
||||
|
||||
d = 0;
|
||||
|
||||
if (len >= align)
|
||||
{
|
||||
switch (align)
|
||||
{
|
||||
case 3: d |= data[2] << 16;
|
||||
case 2: d |= data[1] << 8;
|
||||
case 1: d |= data[0];
|
||||
}
|
||||
|
||||
uint32_t k = (t >> sr) | (d << sl);
|
||||
MIX(h, k, m);
|
||||
|
||||
data += align;
|
||||
len -= align;
|
||||
|
||||
//----------
|
||||
// Handle tail bytes
|
||||
|
||||
switch (len)
|
||||
{
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (len)
|
||||
{
|
||||
case 3: d |= data[2] << 16;
|
||||
case 2: d |= data[1] << 8;
|
||||
case 1: d |= data[0];
|
||||
case 0: h ^= (t >> sr) | (d << sl);
|
||||
h *= m;
|
||||
}
|
||||
}
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (len >= 4)
|
||||
{
|
||||
uint32_t k = *(uint32_t *)data;
|
||||
|
||||
MIX(h, k, m);
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
//----------
|
||||
// Handle tail bytes
|
||||
|
||||
switch (len)
|
||||
{
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t murmur_unaligned64(const void * i_buffer, size_t i_size, uint64_t i_seed)
|
||||
{
|
||||
constexpr uint32_t m = 0x5bd1e995;
|
||||
constexpr int r = 24;
|
||||
|
||||
size_t len = i_size;
|
||||
uint64_t h = i_seed ^ len;
|
||||
|
||||
const unsigned char * data = (const unsigned char *)i_buffer;
|
||||
|
||||
while (len >= 4)
|
||||
{
|
||||
uint64_t k;
|
||||
|
||||
k = data[0];
|
||||
k |= data[1] << 8;
|
||||
k |= data[2] << 16;
|
||||
k |= data[3] << 24;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
switch (len)
|
||||
{
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0];
|
||||
h *= m;
|
||||
};
|
||||
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
//uint64_t murmurAligned64(const void * _buffer, size_t len, uint64_t seed)
|
||||
//{
|
||||
// const uint32_t m = 0x5bd1e995;
|
||||
// const int32_t r = 24;
|
||||
|
||||
// //const unsigned char * data = (const unsigned char *)key;
|
||||
// const uint8_t* data = reinterpret_cast<const uint8_t*>(_buffer);
|
||||
|
||||
// uint64_t h = seed ^ static_cast<uint32_t>(len);
|
||||
|
||||
// int32_t align = (uint64_t)data & 3;
|
||||
|
||||
// if (align && (len >= 4))
|
||||
// {
|
||||
// // Pre-load the temp registers
|
||||
|
||||
// uint64_t t = 0, d = 0;
|
||||
|
||||
// switch (align)
|
||||
// {
|
||||
// case 1: t |= data[2] << 16;
|
||||
// case 2: t |= data[1] << 8;
|
||||
// case 3: t |= data[0];
|
||||
// }
|
||||
|
||||
// int32_t sr = 8 * align;
|
||||
|
||||
// t <<= sr;
|
||||
|
||||
// data += 4 - align;
|
||||
// len -= 4 - align;
|
||||
|
||||
// int32_t sl = 8 * (4 - align);
|
||||
//
|
||||
|
||||
// // Mix
|
||||
|
||||
// while (len >= 4)
|
||||
// {
|
||||
// d = *(uint32_t *)data;
|
||||
// uint64_t right = (t >> sr);
|
||||
// uint64_t left = (d << sl);
|
||||
// t = right | left;
|
||||
|
||||
// uint64_t k = t;
|
||||
|
||||
// MIX(h, k, m);
|
||||
|
||||
// t = d;
|
||||
|
||||
// data += 4;
|
||||
// len -= 4;
|
||||
// }
|
||||
|
||||
// // Handle leftover data in temp registers
|
||||
|
||||
// d = 0;
|
||||
|
||||
// if (len >= align)
|
||||
// {
|
||||
// switch (align)
|
||||
// {
|
||||
// case 3: d |= data[2] << 16;
|
||||
// case 2: d |= data[1] << 8;
|
||||
// case 1: d |= data[0];
|
||||
// }
|
||||
|
||||
// uint64_t k = (t >> sr) | (d << sl);
|
||||
// MIX(h, k, m);
|
||||
|
||||
// data += align;
|
||||
// len -= align;
|
||||
|
||||
// //----------
|
||||
// // Handle tail bytes
|
||||
|
||||
// switch (len)
|
||||
// {
|
||||
// case 3: h ^= data[2] << 16;
|
||||
// case 2: h ^= data[1] << 8;
|
||||
// case 1: h ^= data[0];
|
||||
// h *= m;
|
||||
// };
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// switch (len)
|
||||
// {
|
||||
// case 3: d |= data[2] << 16;
|
||||
// case 2: d |= data[1] << 8;
|
||||
// case 1: d |= data[0];
|
||||
// case 0: h ^= (t >> sr) | (d << sl);
|
||||
// h *= m;
|
||||
// }
|
||||
// }
|
||||
|
||||
// h ^= h >> 13;
|
||||
// h *= m;
|
||||
// h ^= h >> 15;
|
||||
|
||||
// return h;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// while (len >= 4)
|
||||
// {
|
||||
// uint64_t k = *(uint32_t *)data;
|
||||
|
||||
// MIX(h, k, m);
|
||||
|
||||
// data += 4;
|
||||
// len -= 4;
|
||||
// }
|
||||
|
||||
// //----------
|
||||
// // Handle tail bytes
|
||||
|
||||
// switch (len)
|
||||
// {
|
||||
// case 3: h ^= data[2] << 16;
|
||||
// case 2: h ^= data[1] << 8;
|
||||
// case 1: h ^= data[0];
|
||||
// h *= m;
|
||||
// };
|
||||
|
||||
// h ^= h >> 13;
|
||||
// h *= m;
|
||||
// h ^= h >> 15;
|
||||
|
||||
// return h;
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#include "tl/ptr.h"
|
||||
|
||||
namespace tl
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
thread_local int32_t s_ptr_checking_disabled = 0;
|
||||
}
|
||||
|
||||
|
||||
void enable_ptr_checking() noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(detail::s_ptr_checking_disabled > 0);
|
||||
detail::s_ptr_checking_disabled--;
|
||||
}
|
||||
void disable_ptr_checking() noexcept
|
||||
{
|
||||
detail::s_ptr_checking_disabled++;
|
||||
}
|
||||
void set_ptr_checking_enabled(bool v) noexcept
|
||||
{
|
||||
if (v)
|
||||
enable_ptr_checking();
|
||||
else
|
||||
disable_ptr_checking();
|
||||
}
|
||||
|
||||
}
|
||||
+437
@@ -0,0 +1,437 @@
|
||||
#include "tl/string.h"
|
||||
|
||||
template class std::basic_string<char, std::char_traits<char>, std::allocator<char>>;
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
string& string::append(char ch) noexcept
|
||||
{
|
||||
//first some early outs
|
||||
if (empty()) [[unlikely]]
|
||||
{
|
||||
const char buf[2] = { ch, '\0' };
|
||||
*this = buf;
|
||||
return *this;
|
||||
}
|
||||
|
||||
cell_t* old_cell = m_cell;
|
||||
detail::string_db& tldb = detail::string_db::get_instance();
|
||||
tldb.append_internalize(*old_cell, &ch, 1, m_cell);
|
||||
if (old_cell->dec_ref_counter())
|
||||
detail::string_db::get_instance_ptr()->free_cell(*old_cell);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(char const* cstr) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(cstr);
|
||||
//first some early outs
|
||||
if (!cstr || cstr[0] == '\0') [[unlikely]]
|
||||
return *this;
|
||||
|
||||
if (empty()) [[unlikely]]
|
||||
{
|
||||
*this = cstr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
detail::string_db& tldb = detail::string_db::get_instance();
|
||||
cell_t* old_cell = m_cell;
|
||||
tldb.append_internalize(*old_cell, cstr, strlen(cstr), m_cell);
|
||||
if (old_cell->dec_ref_counter())
|
||||
detail::string_db::get_instance_ptr()->free_cell(*old_cell);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(char const* from, char const* to) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(from);
|
||||
//first some early outs
|
||||
if (from[0] == '\0' || to == from) [[unlikely]]
|
||||
return *this;
|
||||
|
||||
if (empty()) [[unlikely]]
|
||||
{
|
||||
*this = string(from, to);
|
||||
return *this;
|
||||
}
|
||||
|
||||
detail::string_db& tldb = detail::string_db::get_instance();
|
||||
cell_t* old_cell = m_cell;
|
||||
tldb.append_internalize(*old_cell, from, to - from, m_cell);
|
||||
if (old_cell->dec_ref_counter())
|
||||
detail::string_db::get_instance_ptr()->free_cell(*old_cell);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(string const& str) noexcept
|
||||
{
|
||||
return append(str, 0, npos);
|
||||
}
|
||||
|
||||
string& string::append(std::string_view const& str) noexcept
|
||||
{
|
||||
return append(str, 0, npos);
|
||||
}
|
||||
string& string::append(eastl::string_view const& str) noexcept
|
||||
{
|
||||
return append(str, 0, npos);
|
||||
}
|
||||
|
||||
string& string::append(string const& str, size_t pos, size_t count) noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
const size_type strsz = count == npos ? str.size() - pos : count;
|
||||
|
||||
//first some early outs
|
||||
if (strsz == 0)
|
||||
return *this;
|
||||
|
||||
if (sz == 0)
|
||||
{
|
||||
if (pos == 0 && strsz == str.size())
|
||||
*this = str;
|
||||
else
|
||||
*this = string(str.c_str() + pos, strsz);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
detail::string_db& tldb = detail::string_db::get_instance();
|
||||
cell_t* old_cell = m_cell;
|
||||
tldb.append_internalize(*old_cell, str.c_str() + pos, strsz, m_cell);
|
||||
if (old_cell->dec_ref_counter())
|
||||
detail::string_db::get_instance_ptr()->free_cell(*old_cell);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
string& string::append(std::string_view const& str, size_t pos, size_t count) noexcept
|
||||
{
|
||||
return _append(str, pos, count);
|
||||
}
|
||||
|
||||
string& string::append(eastl::string_view const& str, size_t pos, size_t count) noexcept
|
||||
{
|
||||
return _append(str, pos, count);
|
||||
}
|
||||
string::size_type string::find_first_of(char ch, size_type off) const noexcept
|
||||
{
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
const size_type s = size();
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++)
|
||||
{
|
||||
if (string[i] == ch)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_first_of_ci(char ch, size_type off) const noexcept
|
||||
{
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
ch = ascii::toupper(ch);
|
||||
const size_type s = size();
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++)
|
||||
{
|
||||
if (ascii::toupper(string[i]) == ch)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_first_of(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
const size_type s = size();
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (::memchr(cstr, ch, l) != nullptr)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_first_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
const size_type s = size();
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (ascii::memichr(cstr, ch, l) != nullptr)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_first_not_of(char ch, size_type off) const noexcept
|
||||
{
|
||||
const size_type s = size();
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
if (string[i] != ch)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_first_not_of_ci(char ch, size_type off) const noexcept
|
||||
{
|
||||
ch = ascii::toupper(ch);
|
||||
const size_type s = size();
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
if (ascii::toupper(string[i]) != ch)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_first_not_of(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return off < size() ? off : npos;
|
||||
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
const size_type s = size();
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (::memchr(cstr, ch, l) == nullptr)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_first_not_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return off < size() ? off : npos;
|
||||
|
||||
if (off >= size()) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
const size_type s = size();
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
for (size_type i = off; i < s; i++) [[likely]]
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (ascii::memichr(cstr, ch, l) == nullptr)
|
||||
return i;
|
||||
}
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_first_not_of(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_first_not_of(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
string::size_type string::find_first_not_of_ci(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_first_not_of_ci(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_of(char ch, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
if (string[i] == ch)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_last_of_ci(char ch, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
ch = ascii::toupper(ch);
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
if (ascii::toupper(string[i]) == ch)
|
||||
return i;
|
||||
|
||||
} while (i-- > 0);
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_of(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (::memchr(cstr, ch, l) != nullptr)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_last_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (ascii::memichr(cstr, ch, l) != nullptr)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_of(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_last_of(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
string::size_type string::find_last_of_ci(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_last_of_ci(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_not_of(char ch, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
if (string[i] != ch)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_last_not_of_ci(char ch, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
ch = ascii::toupper(ch);
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
if (ascii::toupper(string[i]) != ch)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_not_of(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return i;
|
||||
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (::memchr(cstr, ch, l) == nullptr)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
return npos;
|
||||
}
|
||||
string::size_type string::find_last_not_of_ci(char const* cstr, size_type cstr_size, size_type off) const noexcept
|
||||
{
|
||||
const size_type sz = size();
|
||||
if (sz == 0) [[unlikely]]
|
||||
return npos;
|
||||
|
||||
size_type i = (off >= sz) ? sz - 1 : off;
|
||||
if (cstr_size == 0) [[unlikely]]
|
||||
return i;
|
||||
|
||||
const size_type l = cstr_size;
|
||||
char const* string = c_str();
|
||||
do
|
||||
{
|
||||
const char ch = string[i];
|
||||
if (ascii::memichr(cstr, ch, l) == nullptr)
|
||||
return i;
|
||||
} while (i-- > 0);
|
||||
return npos;
|
||||
}
|
||||
|
||||
string::size_type string::find_last_not_of(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_last_not_of(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
string::size_type string::find_last_not_of_ci(char const* cstr, size_type off) const noexcept
|
||||
{
|
||||
return cstr ? find_last_not_of_ci(cstr, strlen(cstr), off) : off;
|
||||
}
|
||||
|
||||
void string::free_database() noexcept
|
||||
{
|
||||
detail::string_db::free_instance();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
#include "tl/string.h"
|
||||
#include "tl/detail/string_db.h"
|
||||
#include "tl/murmur_hash.h"
|
||||
#include "tl/format.h"
|
||||
#include "tl/fixed_vector.h"
|
||||
#include <mutex>
|
||||
|
||||
#define cell_data(c) (reinterpret_cast<char*>(c) + sizeof(tl::detail::string_cell))
|
||||
|
||||
namespace tl
|
||||
{
|
||||
|
||||
tl::detail::string_db*& get_shared_string_db_instance_ptr() noexcept
|
||||
{
|
||||
static auto* s_instance = new tl::detail::string_db;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
constexpr static inline uint32_t k_min_hashmap_size = 65536;
|
||||
|
||||
constexpr static inline size_t k_map_count = 32;
|
||||
//constexpr static inline size_t k_map_mask = k_map_count - 1;
|
||||
|
||||
|
||||
namespace detail
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db::string_db() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db::~string_db() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db::internalize(const char* str_start, const char* str_end, cell_t*& dst_cell) noexcept
|
||||
{
|
||||
if (str_start == str_end)
|
||||
{
|
||||
dst_cell = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto length = static_cast<cell_t::length_t>(str_end - str_start);
|
||||
const cell_t::hash_t hash = string::raw_hash(str_start, length);
|
||||
|
||||
{
|
||||
string_db_map& map = m_map;
|
||||
std::unique_lock lg(map);
|
||||
dst_cell = map.find_or_add(hash, length, str_start);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db::internalize(const char* str, cell_t*& dst_cell) noexcept
|
||||
{
|
||||
if (!str || *str == '\0')
|
||||
{
|
||||
dst_cell = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto length = static_cast<cell_t::length_t>(::strlen(str));
|
||||
const cell_t::hash_t hash = string::raw_hash(str, length);
|
||||
|
||||
{
|
||||
string_db_map& map = m_map;
|
||||
std::unique_lock lg(map);
|
||||
dst_cell = map.find_or_add(hash, length, str);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db::append_internalize(cell_t& src_cell, const char* str, size_t str_size, cell_t*& dst_cell) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(str && str_size);
|
||||
|
||||
const auto total_size = static_cast<cell_t::length_t>(src_cell.length + str_size);
|
||||
if (total_size == 0)
|
||||
{
|
||||
dst_cell = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
tl::fixed_vector<char, 1024> b(total_size);
|
||||
memcpy(b.data(), cell_data(&src_cell), src_cell.length);
|
||||
memcpy(b.data() + src_cell.length, str, str_size);
|
||||
|
||||
const cell_t::hash_t hash = string::raw_hash(b.data(), total_size);
|
||||
|
||||
{
|
||||
string_db_map& map = m_map;
|
||||
std::unique_lock lg(map);
|
||||
dst_cell = map.find_or_add(hash, total_size, b.data());
|
||||
}
|
||||
}
|
||||
|
||||
void string_db::free_cell(cell_t& cell) noexcept
|
||||
{
|
||||
string_db_map& map = m_map;
|
||||
map.mark_cell_as_unused(cell);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db::free_instance() noexcept
|
||||
{
|
||||
string_db*& instance = get_instance_ptr();
|
||||
delete instance;
|
||||
instance = nullptr;
|
||||
|
||||
get_shared_string_db_instance_ptr() = nullptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db_map::string_db_map() noexcept
|
||||
: m_hashmap_size(k_min_hashmap_size)
|
||||
, m_hashmap_size_mask(k_min_hashmap_size - 1)
|
||||
{
|
||||
m_buckets.resize(m_hashmap_size);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db_map::~string_db_map() noexcept
|
||||
{
|
||||
for (cell_t* cell : m_buckets)
|
||||
{
|
||||
if (!cell)
|
||||
continue;
|
||||
free(cell);
|
||||
}
|
||||
m_buckets.clear();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db_map::rehash(uint32_t new_size) noexcept
|
||||
{
|
||||
using cell_t = string_db_map::cell_t;
|
||||
|
||||
eastl::vector<cell_t*> cells = std::move(m_buckets);
|
||||
|
||||
m_hashmap_size = new_size;
|
||||
m_hashmap_size_mask = m_hashmap_size - 1;
|
||||
|
||||
m_buckets.clear();
|
||||
m_buckets.resize(new_size);
|
||||
|
||||
const uint32_t mask = m_hashmap_size_mask;
|
||||
for (cell_t* cell: cells)
|
||||
{
|
||||
if (!cell)
|
||||
continue;
|
||||
|
||||
uint32_t index = cell->hash & mask; //new index
|
||||
while (m_buckets[index])
|
||||
index = (index + 1) & mask;
|
||||
|
||||
m_buckets[index] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db_map::cell_t* string_db_map::find(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(string);
|
||||
|
||||
const uint32_t mask = m_hashmap_size_mask;
|
||||
uint32_t index = hash & mask;
|
||||
|
||||
//static size_t maxColl = 0;
|
||||
//static size_t totalColl = 0;
|
||||
//size_t coll = 0;
|
||||
while (cell_t* c = m_buckets[index])
|
||||
{
|
||||
if (((c->hash == hash) & (c->length == length)) && memeq(cell_data(c), string, length)) //found?
|
||||
{
|
||||
const int old_counter = c->counter.fetch_add(1, std::memory_order_relaxed);
|
||||
if (old_counter == 0) //we're reviving a tomb cell
|
||||
m_used_cell_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return c;
|
||||
}
|
||||
|
||||
index = (index + 1) & mask;
|
||||
//coll++;
|
||||
}
|
||||
//maxColl = std::max(maxColl, coll);
|
||||
//totalColl += coll;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db_map::cell_t* string_db_map::find_or_add(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept
|
||||
{
|
||||
TL_PLAIN_ASSERT(string);
|
||||
|
||||
static constexpr size_t k_overused_factor = 4;
|
||||
static constexpr size_t k_underused_factor = 10;
|
||||
|
||||
const int used_cell_count = m_used_cell_count.load(std::memory_order_relaxed);
|
||||
|
||||
if (used_cell_count * k_overused_factor > m_hashmap_size && m_hashmap_size < (1 << 24))
|
||||
rehash(m_hashmap_size << 1);
|
||||
else if (used_cell_count * k_underused_factor < m_hashmap_size && m_hashmap_size > k_min_hashmap_size)
|
||||
rehash(m_hashmap_size >> 1);
|
||||
|
||||
const uint32_t mask = m_hashmap_size_mask;
|
||||
uint32_t index = hash & mask;
|
||||
|
||||
//static size_t maxColl = 0;
|
||||
//static size_t totalColl = 0;
|
||||
//size_t coll = 0;
|
||||
while (cell_t* c = m_buckets[index])
|
||||
{
|
||||
if (((c->hash == hash) & (c->length == length)) && memeq(cell_data(c), string, length)) //found?
|
||||
{
|
||||
const int old_counter = c->counter.fetch_add(1, std::memory_order_relaxed);
|
||||
if (old_counter == 0) //we're reviving a tomb cell
|
||||
m_used_cell_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return c;
|
||||
}
|
||||
|
||||
if (c->counter.load(std::memory_order_relaxed) == 0) //tomb cell?
|
||||
{
|
||||
free_cell(c);
|
||||
break;
|
||||
}
|
||||
|
||||
index = (index + 1) & mask;
|
||||
//coll++;
|
||||
}
|
||||
//maxColl = std::max(maxColl, coll);
|
||||
//totalColl += coll;
|
||||
|
||||
//if we're here it means one of 2 things: the head is null or we didn't find our string in the collision group
|
||||
//in both cases - we allocate a new cell and link it
|
||||
cell_t* c = allocate_cell(string, length, hash);
|
||||
m_buckets[index] = c;
|
||||
m_used_cell_count.fetch_add(1, std::memory_order_relaxed);
|
||||
return c;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
string_db_map::cell_t* string_db_map::allocate_cell(const char* string, cell_t::length_t length, cell_t::hash_t hash) noexcept
|
||||
{
|
||||
const size_t data_size = sizeof(cell_t) + length + 1;
|
||||
cell_t* c = (cell_t*)malloc(data_size);
|
||||
|
||||
c->length = length;
|
||||
c->hash = hash;
|
||||
c->counter = 1;
|
||||
|
||||
//copy the string in
|
||||
char* cd = cell_data(c);
|
||||
memcpy(cd, string, length);
|
||||
cd[length] = 0; //terminating zero
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void string_db_map::free_cell(cell_t* cell) noexcept
|
||||
{
|
||||
const uint32_t mask = m_hashmap_size_mask;
|
||||
uint32_t index = cell->hash & mask;
|
||||
|
||||
while (cell_t* c = m_buckets[index])
|
||||
{
|
||||
if (c == cell) // head
|
||||
{
|
||||
m_buckets[index] = nullptr;
|
||||
free(cell);
|
||||
break;
|
||||
}
|
||||
index = (index + 1) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
void string_db_map::mark_cell_as_unused(cell_t& cell) noexcept
|
||||
{
|
||||
[[maybe_unused]] const int old = m_used_cell_count.fetch_sub(1, std::memory_order_relaxed);
|
||||
TL_PLAIN_ASSERT(old > 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user