First
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "tl/platform.h"
|
||||
#include "tl/toolchain.h"
|
||||
#include "tl/debug.h"
|
||||
#include "tl/api.h"
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user