This commit is contained in:
jeanlemotan
2024-07-02 18:06:33 +02:00
commit 8297b0b45f
157 changed files with 24865 additions and 0 deletions
+161
View File
@@ -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);
}
}
}
}
+59
View File
@@ -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;
}
}
}
+307
View File
@@ -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
+615
View File
@@ -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
View File
@@ -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
View File
@@ -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();
}
}
+304
View File
@@ -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);
}
//////////////////////////////////////////////////////////////////////////
}
}