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
+278
View File
@@ -0,0 +1,278 @@
#pragma once
#include "tl/detail/prologue.h"
#include <type_traits>
///////////////////////////////////////////////////////////////////////////////
namespace tl
{
namespace assert
{
//The assert handler is called whenever an assert fails and should present a message to the user.
//If it returns BREAK, the code will dbgbreak and allow you to connect with the debugger. Returning CONTINUE will continue the code normally
// condition contains a stringified condition that failed
// msg is any user supplied message. Can be NULL
// file is the name of the file the assert failed
// line is the line where the assert failed
enum class response
{
BREAK,
CONTINUE
};
using handler = response (*)(const char *, const char *, int, const char *);
TL_API handler set_handler(handler) noexcept;
TL_API response default_handler(const char* condition, const char* file, int line, const char* msg) noexcept;
inline response null_handler(const char*, const char*, int, const char*) noexcept
{
return response::CONTINUE;
}
//Use this to temporarily change the assert handler for the current scope
class handler_guard
{
public:
explicit handler_guard(handler new_handler) noexcept
{
m_prev_handler = set_handler(new_handler);
}
~handler_guard() noexcept
{
set_handler(m_prev_handler);
}
private:
handler m_prev_handler;
};
///////////////////////////////////////////////////////////////////////
//for internal use only
namespace detail
{
//helper for checking if a description is used instead of an assert condition
template<typename Cond> struct _description_in_condition { static const bool value = false; };
template<> struct _description_in_condition<char> { static const bool value = true; };
template<int N> struct _description_in_condition<char[N]> { static const bool value = true; };
template<typename T>
struct description_in_condition
{
static const bool value = _description_in_condition<std::remove_cv_t<std::remove_reference_t<T>>>::value;
};
//this is responsible of formatting the messages and calling the handler
TL_API response do_assert(const char* _condition, const char* _file, int _line, const char* msg) noexcept;
TL_API response do_assert(const char* _condition, const char* _file, int _line) noexcept;
//used to mark/test once asserts as being taken or not
TL_API bool is_assert_enabled(const char* _file, int _line) noexcept;
TL_API void set_assert_enabled(const char* _file, int _line, bool _enabled) noexcept;
inline const char* get_plain_message() noexcept { return ""; }
inline const char* get_plain_message(const char* msg) noexcept { return msg; }
TL_API handler& get_static_handler() noexcept;
}
}
}
#define __TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition) \
const auto& __cond_result__ = (condition); \
(void)(__cond_result__); \
static_assert(tl::assert::detail::description_in_condition<decltype(__cond_result__)>::value == false, \
"It seems you used the assert description instead of the assert condition.")
//ASSERT
#define __TL_ASSERT_ENABLED_IMPL__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
if (!(__cond_result__)) [[unlikely]] \
{ \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
/* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \
if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//PLAIN ASSERT
#define __TL_PLAIN_ASSERT_ENABLED_IMPL__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
if (!(__cond_result__)) [[unlikely]] \
{ \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__(condition, ...) \
do \
{ \
__TL_ASSERT_TEST_DESCRIPTION_IN_CONDITION__(condition); \
/* checking the condition first and then the GetAssertOnce, as chances are that the condition is cheaper */ \
if (!(__cond_result__) && tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[unlikely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
if (tl::assert::detail::do_assert(#condition, __FILE__, __LINE__, ##__VA_ARGS__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//FAIL
#define __TL_FAIL_ENABLED_IMPL__(...) \
do \
{ \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} while(false)
#define __TL_FAIL_ENABLED_IMPL_ONCE__(...) \
do \
{ \
if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
eastl::string __msg__ = tl::format<eastl::string>(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__.data()) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
//PLAIN FAIL
#define __TL_PLAIN_FAIL_ENABLED_IMPL__(...) \
do \
{ \
const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} while(false)
#define __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__(...) \
do \
{ \
if (tl::assert::detail::is_assert_enabled(__FILE__, __LINE__)) [[likely]] \
{ \
tl::assert::detail::set_assert_enabled(__FILE__, __LINE__, false); \
const char* __msg__ = tl::assert::detail::get_plain_message(__VA_ARGS__); \
if (tl::assert::detail::do_assert("fail", __FILE__, __LINE__, __msg__) == tl::assert::response::BREAK) \
TL_BREAK(); \
} \
} while(false)
#define __TL_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false)
#define __TL_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false)
#define __TL_PLAIN_ASSERT_DISABLED_IMPL__(condition, ...) do {} while (false)
#define __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__(condition, ...) do {} while (false)
#define __TL_FAIL_DISABLED_IMPL__(...) do {} while (false)
#define __TL_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false)
#define __TL_PLAIN_FAIL_DISABLED_IMPL__(...) do {} while (false)
#define __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__(...) do {} while (false)
#ifdef TL_DEBUG
//assert
#define TL_ASSERT __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
#define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
//plain assert
#define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
//fail
#define TL_FAIL __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
//plain fail
#define TL_PLAIN_FAIL __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#else
//assert
#define TL_ASSERT __TL_ASSERT_DISABLED_IMPL__
#define TL_ASSERT_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#ifdef TL_RETAIL
#define TL_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_ASSERT_DISABLED_IMPL_ONCE__
#else
#define TL_ASSERT_RELEASE __TL_ASSERT_ENABLED_IMPL__
#define TL_ASSERT_RELEASE_ONCE __TL_ASSERT_ENABLED_IMPL_ONCE__
#define TL_PLAIN_ASSERT_RELEASE __TL_PLAIN_ASSERT_ENABLED_IMPL__
#define TL_PLAIN_ASSERT_RELEASE_ONCE __TL_PLAIN_ASSERT_ENABLED_IMPL_ONCE__
#define TL_FAIL_RELEASE __TL_FAIL_ENABLED_IMPL__
#define TL_FAIL_RELEASE_ONCE __TL_FAIL_ENABLED_IMPL_ONCE__
#define TL_PLAIN_FAIL_RELEASE __TL_PLAIN_FAIL_ENABLED_IMPL__
#define TL_PLAIN_FAIL_RELEASE_ONCE __TL_PLAIN_FAIL_ENABLED_IMPL_ONCE__
#endif
//plain assert
#define TL_PLAIN_ASSERT __TL_PLAIN_ASSERT_DISABLED_IMPL__
#define TL_PLAIN_ASSERT_ONCE __TL_PLAIN_ASSERT_DISABLED_IMPL_ONCE__
//fail
#define TL_FAIL __TL_FAIL_DISABLED_IMPL__
#define TL_FAIL_ONCE __TL_FAIL_DISABLED_IMPL_ONCE__
//plain fail
#define TL_PLAIN_FAIL __TL_PLAIN_FAIL_DISABLED_IMPL__
#define TL_PLAIN_FAIL_ONCE __TL_PLAIN_FAIL_DISABLED_IMPL_ONCE__
#endif
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include "tl/detail/prologue.h"
///////////////////////////////////////////////////////////////////////////////
//
namespace tl
{
namespace crash
{
//use this to specify a function to be called when a crash is requested.
//Note!!! File and msg can be NULL
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// It's very risky to implement this function so do it only if you REALLY REALLY have to do something before the crash.
// This might be called because the system is out of memory - so any allocation will fail. Same for file handles or any other resources.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
enum class response
{
CRASH,
CONTINUE
};
using handler = response (*)(const char *, int, const char *);
TL_API handler set_handler(handler) noexcept;
TL_API response default_handler(const char* file, int line, const char* msg) noexcept;
//Use this to temporarily change the assert handler for the current scope
class handler_guard
{
public:
explicit handler_guard(handler new_handler) noexcept
{
m_prev_handler = set_handler(new_handler);
}
~handler_guard() noexcept
{
set_handler(m_prev_handler);
}
private:
handler m_prev_handler;
};
///////////////////////////////////////////////////////////////////////
//for internal use only
namespace detail
{
TL_API handler& get_static_handler() noexcept;
//this is responsible of formatting the messages and calling the handler
inline response invoke_crash_handler(const char* file, int line, const char* msg) noexcept
{
const handler& handler = get_static_handler();
return handler(file, line, msg);
}
}
}
}
#include "tl/detail/internal_assert.h"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+272
View File
@@ -0,0 +1,272 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/vector.h"
#include "tl/string.h"
#include "tl/hash_and_combine.h"
namespace tl
{
namespace detail
{
namespace path_system
{
template <typename PathSystems> struct path_key_less;
//////////////////////////////////////////////////////////////////////////
// inner path_base definition
template<typename PathSystems>
class path_base
{
public:
virtual ~path_base() noexcept = default;
using iterator = tl::vector<string>::iterator;
using const_iterator = tl::vector<string>::const_iterator;
using const_reverse_iterator = tl::vector<string>::const_reverse_iterator;
using key_less = path_key_less<PathSystems>;
using value_type = std::string;
//checked indexing
const string& at(size_t index) const noexcept;
void shrink(size_t size) noexcept;
bool empty() const noexcept;
size_t size() const noexcept;
void reserve(size_t capacity) noexcept;
// if the path is empty, these operations have no effect. They return the removed element as a string.
string pop_back() noexcept;
virtual bool push_back(string element) noexcept;
virtual void take_elements(tl::vector<string>&& elements) noexcept;
iterator begin() noexcept;
iterator end() noexcept;
const_iterator begin() const noexcept;
const_iterator end() const noexcept;
const_reverse_iterator rbegin() const noexcept;
const_reverse_iterator rend() const noexcept;
uint32_t hash() const noexcept;
protected:
bool validate_element(const string& element) noexcept;
void push_element(string element) noexcept;
tl::vector<string> m_elements;
mutable uint32_t m_hash = 0;
friend bool path_key_less<PathSystems>::operator()(const path_base<PathSystems>& a1, const path_base<PathSystems>& a2) const noexcept;
};
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
struct path_key_less
{
bool operator()(const path_base<PathSystems>& a1, const path_base<PathSystems>& a2) const noexcept;
};
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
namespace detail
{
namespace path_system
{
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
const string& path_base<PathSystems>::at(size_t index) const noexcept
{
return m_elements[index];
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void path_base<PathSystems>::shrink(size_t size) noexcept
{
if (size < this->size())
{
m_elements.resize(size);
m_hash = 0;
}
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
uint32_t path_base<PathSystems>::hash() const noexcept
{
if (m_hash == 0)
{
for (const string& e : m_elements)
hash_and_combine(m_hash, e);
}
return m_hash;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
size_t path_base<PathSystems>::size() const noexcept
{
return m_elements.size();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool path_base<PathSystems>::empty() const noexcept
{
return m_elements.empty();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void path_base<PathSystems>::reserve(size_t capacity) noexcept
{
m_elements.reserve(capacity);
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
string path_base<PathSystems>::pop_back() noexcept
{
if (empty())
{
TL_PLAIN_FAIL();
return string();
}
m_hash = 0;
auto res = m_elements.back();
m_elements.pop_back();
return res;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool path_base<PathSystems>::push_back(string element) noexcept
{
if (!validate_element(element))
{
TL_PLAIN_FAIL("Pushed an invalid path element.");
return false;
}
push_element(std::move(element));
return true;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool path_base<PathSystems>::validate_element(const string& /*element*/) noexcept
{
// validate element to be added here
// if not return false
return true;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void path_base<PathSystems>::push_element(string element) noexcept
{
m_hash = 0;
m_elements.push_back(std::move(element));
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void path_base<PathSystems>::take_elements(tl::vector<string>&& elements) noexcept
{
m_hash = 0;
m_elements = std::move(elements);
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::iterator path_base<PathSystems>::begin() noexcept
{
return m_elements.begin();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::iterator path_base<PathSystems>::end() noexcept
{
return m_elements.end();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::const_iterator path_base<PathSystems>::begin() const noexcept
{
return m_elements.cbegin();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::const_iterator path_base<PathSystems>::end() const noexcept
{
return m_elements.cend();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::const_reverse_iterator path_base<PathSystems>::rbegin() const noexcept
{
return m_elements.crbegin();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
typename path_base<PathSystems>::const_reverse_iterator path_base<PathSystems>::rend() const noexcept
{
return m_elements.crend();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool path_key_less<PathSystems>::operator()(const path_base<PathSystems>& a1, const path_base<PathSystems>& a2) const noexcept
{
const size_t sz1 = a1.m_elements.size();
const size_t sz2 = a2.m_elements.size();
for (size_t i = 0; i < sz1 && i < sz2; ++i)
{
if (a1.m_elements[i] != a2.m_elements[i])
return a1.m_elements[i].unique_key() < a2.m_elements[i].unique_key();
}
return sz1 < sz2;
}
}
}
}
+383
View File
@@ -0,0 +1,383 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/fixed_vector.h>
#include "tl/string.h"
#include "tl/string.h"
namespace tl
{
namespace detail
{
namespace path_system
{
//////////////////////////////////////////////////////////////////////////
template<typename T>
void simple_format_rel_path(T& o_buffer, const tl::vector<string>& members, const char* separator) noexcept;
template<typename T>
void simple_format_abs_path(T& o_buffer, const tl::vector<string>& members, const char* root_tag, const char* separator) noexcept;
inline bool find_next_separator(const char* const* separators_begin, const char* const* separators_end, const char*& io_startIt, const char* end, const char*& io_endIt) noexcept;
inline tl::vector<string> simple_parse_path(const char* path, const char* root_tag, const tl::vector<const char*>& separators) noexcept;
inline bool simple_match_prefix(const char* path, const char* prefix) noexcept;
//////////////////////////////////////////////////////////////////////////
template<int Index, typename... Tail>
struct path_system_getter
{
template<typename T>
static T format_absolute(int type_idx, const tl::vector<string>& members) noexcept;
static void parse_absolute(tl::vector<string>& o_elements, int type_idx, const char* path, size_t size) noexcept;
static int deduce_system_type(const char* path, size_t size) noexcept;
static bool validate_abs_path(int type_idx, const tl::vector<string>& members) noexcept;
};
// specialization if Head element exists
template<int Index, typename Head, typename... Tail>
struct path_system_getter<Index, Head, Tail...>
{
template<typename T>
static T format_absolute(int type_idx, const tl::vector<string>& members) noexcept;
static void parse_absolute(tl::vector<string>& o_elements, int type_idx, const char* path, size_t size) noexcept;
static int deduce_system_type(const char* path, size_t size) noexcept;
static bool validate_abs_path(int type_idx, const tl::vector<string>& members) noexcept;
};
//////////////////////////////////////////////////////////////////////////
template<typename... Tail>
struct path_systems_getter
{
static tl::vector<tl::vector<const char*>> get_all_parse_separators() noexcept;
};
template<typename Head, typename... Tail>
struct path_systems_getter<Head, Tail...>
{
static tl::vector<tl::vector<const char*>> get_all_parse_separators() noexcept;
};
//////////////////////////////////////////////////////////////////////////
template<int Index, typename TypeToCheck, typename... Tail>
struct path_system_type_checker
{
static bool is(int type_idx) noexcept;
};
// specialization if Head element exists
template<int Index, typename TypeToCheck, typename Head, typename... Tail>
struct path_system_type_checker<Index, TypeToCheck, Head, Tail...>
{
static bool is(int type_idx) noexcept;
};
//////////////////////////////////////////////////////////////////////////
template<typename parse_separators, typename format_separator>
struct base_path_system
{
static const tl::vector<const char*>& get_parse_separators() noexcept;
static const char* get_format_separator() noexcept;
static string format_relative(const tl::vector<string>& members) noexcept;
};
//////////////////////////////////////////////////////////////////////////
}
}
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
namespace detail
{
namespace path_system
{
//////////////////////////////////////////////////////////////////////////
template<typename T>
void simple_format_rel_path(T& o_buffer, const tl::vector<string>& members, const char* separator) noexcept
{
//calculate the size to reserve
size_t totalSize = 0;
for (const string& el: members)
totalSize += el.size() + 1; //+1 for the separator
totalSize -= members.empty() ? 0 : 1;//remove the last separator
o_buffer.reserve(totalSize);
//append the elements and separator
const size_t pathSize = members.size();
for (size_t j = 0; j < pathSize; j++)
{
const string& member = members[j];
o_buffer.append(member.data(), member.size());
if (j < pathSize - 1)
o_buffer.append(separator);
}
}
//////////////////////////////////////////////////////////////////////////
template<typename T>
void simple_format_abs_path(T& o_buffer, const tl::vector<string>& members, const char* root_tag, const char* separator) noexcept
{
o_buffer.append(root_tag);
detail::path_system::simple_format_rel_path(o_buffer, members, separator);
}
//////////////////////////////////////////////////////////////////////////
inline bool find_next_separator(const char* const* separators_begin, const char* const* separators_end, const char*& io_startIt, const char* end, const char*& io_endIt) noexcept
{
const size_t str_size = end - io_startIt;
const char* bestStartIt = nullptr;
const char* bestSeparator = nullptr;
for (const char* const* separator_ptr = separators_begin; separator_ptr != separators_end; separator_ptr++)
{
const char* it = ascii::strnstr(io_startIt, *separator_ptr, str_size);
if (it != nullptr && (bestStartIt == nullptr || it < bestStartIt))
{
bestStartIt = it;
bestSeparator = *separator_ptr;
}
}
if (bestStartIt)
{
io_startIt = bestStartIt;
io_endIt = io_startIt + strlen(bestSeparator);
return true;
}
return false;
}
//////////////////////////////////////////////////////////////////////////
inline void simple_parse_path(tl::vector<string>& o_elements, const char* path, size_t size, const char* root_tag, const tl::vector<const char*>& separators) noexcept
{
const char* pathIt = path;
const char* start = path;
const char* end = start + size;
// parse root
if (root_tag)
{
const char* tagIt = root_tag;
for (; *tagIt; ++tagIt, ++pathIt)
{
if (*tagIt != *pathIt)
break;
}
// matched root
if (!(*tagIt))
start = pathIt;
// if not start over again
else
{
pathIt = path;
start = path;
}
}
const char* const* separators_begin = separators.data();
const char* const* separators_end = separators_begin + separators.size();
// parse the rest of the path
tl::fixed_vector<string, 64> elements;
while (*start != 0)
{
const char* sepStart = start;
const char* sepEnd = nullptr;
bool matchedSeparator = detail::path_system::find_next_separator(separators_begin, separators_end, sepStart, end, sepEnd);
// if has matched separator
if (matchedSeparator)
{
if (sepStart > start)
elements.emplace_back(start, sepStart);
start = sepEnd;
}
else
{
//get the remainder of the string
if (*start != 0)
elements.emplace_back(start);
break;
}
}
//now reserve the space and move into final place
o_elements.clear();
o_elements.reserve(elements.size());
for (string& element : elements)
o_elements.emplace_back(std::move(element));
}
//////////////////////////////////////////////////////////////////////////
inline bool simple_match_prefix(const char* path, size_t size, const char* prefix) noexcept
{
const size_t prefix_length = strlen(prefix);
if (prefix_length > size)
return false;
return strncmp(path, prefix, prefix_length) == 0;
}
//////////////////////////////////////////////////////////////////////////
template<int Index, typename... Tail>
template<typename T>
T path_system_getter<Index, Tail...>::format_absolute(int /*type_idx*/, const tl::vector<string>& /*members*/) noexcept
{
TL_PLAIN_FAIL("Failed to format path. Invalid type.");
return {};
}
template<int Index, typename... Tail>
void path_system_getter<Index, Tail...>::parse_absolute(tl::vector<string>& /*o_elements*/, int /*type_idx*/, const char* /*path*/, size_t /* size */) noexcept
{
TL_PLAIN_FAIL("Failed parsing absolute path. Invalid type.");
}
template<int Index, typename... Tail>
int path_system_getter<Index, Tail...>::deduce_system_type(const char* /*path*/, size_t /* size */) noexcept
{
return -1;
}
template<int Index, typename... Tail>
bool path_system_getter<Index, Tail...>::validate_abs_path(int /*type_idx*/, const tl::vector<string>& /*members*/) noexcept
{
TL_PLAIN_FAIL("Failed validating path system. Invalid type.");
return false;
}
//////////////////////////////////////////////////////////////////////////
template<int Index, typename Head, typename... Tail>
template<typename T>
T path_system_getter<Index, Head, Tail...>::format_absolute(int type_idx, const tl::vector<string>& members) noexcept
{
if (type_idx == Index)
return Head::template format_absolute<T>(members);
return path_system_getter<Index + 1, Tail...>::template format_absolute<T>(type_idx, members);
}
template<int Index, typename Head, typename... Tail>
void path_system_getter<Index, Head, Tail...>::parse_absolute(tl::vector<string>& o_elements, int type_idx, const char* path, size_t size) noexcept
{
if (type_idx == Index)
{
Head::parse_absolute(o_elements, path, size);
return;
}
path_system_getter<Index + 1, Tail...>::parse_absolute(o_elements, type_idx, path, size);
}
template<int Index, typename Head, typename... Tail>
int path_system_getter<Index, Head, Tail...>::deduce_system_type(const char* path, size_t size) noexcept
{
if (Head::match(path, size))
return Index;
return path_system_getter<Index + 1, Tail...>::deduce_system_type(path, size);
}
template<int Index, typename Head, typename... Tail>
bool path_system_getter<Index, Head, Tail...>::validate_abs_path(int type_idx, const tl::vector<string>& members) noexcept
{
if (type_idx == Index)
return Head::validate_abs_path(members);
return path_system_getter<Index + 1, Tail...>::validate_abs_path(type_idx, members);
}
//////////////////////////////////////////////////////////////////////////
template<typename... Tail>
tl::vector<tl::vector<const char*>> path_systems_getter<Tail...>::get_all_parse_separators() noexcept
{
return tl::vector<tl::vector<const char*>>();
}
template<typename Head, typename... Tail>
tl::vector<tl::vector<const char*>> path_systems_getter<Head, Tail...>::get_all_parse_separators() noexcept
{
tl::vector<tl::vector<const char*>> separators_array;
separators_array.reserve(32);
separators_array.push_back(Head::get_parse_separators());
tl::vector<tl::vector<const char*>> tail_separators_array = path_systems_getter<Tail...>::get_all_parse_separators();
std::copy(tail_separators_array.begin(), tail_separators_array.end(), std::back_inserter(separators_array));
return separators_array;
}
//////////////////////////////////////////////////////////////////////////
template<int Index, typename TypeToCheck, typename... Tail>
bool path_system_type_checker<Index, TypeToCheck, Tail...>::is(int type_idx) noexcept
{
TL_PLAIN_FAIL("Failed checking path type.");
return false;
}
//////////////////////////////////////////////////////////////////////////
template<int Index, typename TypeToCheck, typename Head, typename... Tail>
bool path_system_type_checker<Index, TypeToCheck, Head, Tail...>::is(int type_idx) noexcept
{
if (type_idx == Index)
return std::is_same_v<Head, TypeToCheck>;
return path_system_type_checker<Index + 1, TypeToCheck, Tail...>::is(type_idx);
}
//////////////////////////////////////////////////////////////////////////
template<typename parse_separators, typename format_separator>
const tl::vector<const char*>& base_path_system<parse_separators, format_separator>::get_parse_separators() noexcept
{
return parse_separators::vector();
}
//////////////////////////////////////////////////////////////////////////
template<typename parse_separators, typename format_separator>
string base_path_system<parse_separators, format_separator>::format_relative(const tl::vector<string>& members) noexcept
{
eastl::string buffer;
buffer.reserve(8192);
detail::path_system::simple_format_rel_path(buffer, members, format_separator::value());
if (buffer.data())
return string(buffer.data(), buffer.size());
return string();
}
//////////////////////////////////////////////////////////////////////////
}
}
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "tl/platform.h"
#include "tl/toolchain.h"
#include "tl/debug.h"
#include "tl/api.h"
+128
View File
@@ -0,0 +1,128 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/vector.h>
#include <EASTL/array.h>
#include <EASTL/unique_ptr.h>
#include <atomic>
#include <shared_mutex>
#include "tl/plain_crash.h"
//disable the 'strdup' warning
#if defined(_MSC_VER)
# pragma warning( disable : 4996 )
#endif
#ifndef _SCL_SECURE_NO_WARNINGS
# define _SCL_SECURE_NO_WARNINGS
#endif
namespace tl
{
namespace detail
{
class string_db;
}
TL_API detail::string_db*& get_shared_string_db_instance_ptr() noexcept;
}
namespace tl
{
class string;
namespace detail
{
struct TL_API string_cell
{
friend class string_db_map;
using length_t = uint32_t; //uint32_t was chosen to avoid the cell to gain 4 bytes in 64 bit configs. We'll never have strings bigger than 4 billion chars (for the foreseeable future)
using hash_t = uint32_t;
length_t length;
hash_t hash; //hash for key
std::atomic_int counter;
// uint32_t padding;
//this increments the ref_count and returns true IF the cell was not resurrected. So true means the cell was alive before the increment
void inc_ref_counter() noexcept { counter.fetch_add(1, std::memory_order_relaxed); }
//this decrements the ref_count and returns true if the cell is still alive
bool dec_ref_counter() noexcept { const int oldRef = counter.fetch_sub(1, std::memory_order_relaxed); TL_PLAIN_ASSERT(oldRef > 0); return oldRef == 1; }
};
static_assert(sizeof(string_cell) % sizeof(uint32_t) == 0);
class TL_API string_db_map
{
public:
using cell_t = string_cell;
string_db_map() noexcept;
~string_db_map() noexcept;
//this returns a new or existing cell with the string in the argument
cell_t* find(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept;
cell_t* find_or_add(cell_t::hash_t hash, cell_t::length_t length, const char* string) noexcept;
cell_t* allocate_cell(const char* string, cell_t::length_t length, cell_t::hash_t hash) noexcept;
void free_cell(cell_t* cell) noexcept;
void mark_cell_as_unused(cell_t& cell) noexcept;
void lock() noexcept { m_mutex.lock(); }
void unlock() noexcept { m_mutex.unlock(); }
private:
std::mutex m_mutex;
uint32_t m_hashmap_size = 0;
uint32_t m_hashmap_size_mask = m_hashmap_size - 1;
eastl::vector<cell_t*> m_buckets;
std::atomic_int m_used_cell_count = { 0 };
void rehash(uint32_t new_size) noexcept;
//size_t m_garbage_size = 0;
};
class TL_API string_db
{
public:
string_db() noexcept;
~string_db() noexcept;
using cell_t = string_cell;
static string_db*& get_instance_ptr() noexcept;
static string_db& get_instance() noexcept;
static void free_instance() noexcept;
void internalize(const char* str_start, const char* str_end, cell_t*& dst_cell) noexcept;
void internalize(const char* str, cell_t*& dst_cell) noexcept;
void append_internalize(cell_t& src_cell, const char* str, size_t str_size, cell_t*& dst_cell) noexcept;
void free_cell(cell_t& cell) noexcept;
private:
string_db_map m_map;
};
inline string_db*& string_db::get_instance_ptr() noexcept
{
static string_db*& s_instance = get_shared_string_db_instance_ptr();
return s_instance;
}
inline string_db& string_db::get_instance() noexcept
{
string_db* db = get_instance_ptr();
if (!db)
TL_PLAIN_CRASH("String DB was deleted");
return *db;
}
}
}