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
+75
View File
@@ -0,0 +1,75 @@
cmake_minimum_required(VERSION 3.16.5)
project(TL)
include(${PROJECT_SOURCE_DIR}/../build-utils/cmake/compiler_settings.cmake)
if (MSVC)
add_definitions(/bigobj)
set(LIB_FOLDER "vs2022")
elseif (ANDROID)
set(LIB_FOLDER "Android")
elseif (APPLE)
set(LIB_FOLDER "macos")
endif()
if(NOT TARGET EASTL)
set(EASTL_DIR "${PROJECT_SOURCE_DIR}/../EASTL")
set(EASTL_LIB_DIR "${EASTL_DIR}/release/lib/${LIB_FOLDER}")
add_library(EASTL STATIC IMPORTED)
list(APPEND EASTL_INCLUDE_DIRS "${EASTL_DIR}/include"
"${EASTL_DIR}/test/packages/EAAssert/include"
"${EASTL_DIR}/test/packages/EABase/include/Common"
"${EASTL_DIR}/test/packages/EAMain/include"
"${EASTL_DIR}/test/packages/EAStdC/include"
"${EASTL_DIR}/test/packages/EATest/include"
"${EASTL_DIR}/test/packages/EAThread/include")
list(APPEND LIB_INCLUDE_DIRS "dir2")
set_target_properties(EASTL PROPERTIES
IMPORTED_LOCATION "${EASTL_LIB_DIR}/Release/${LIB_PREFIX}EASTL${LIB_SUFFIX}"
IMPORTED_LOCATION_DEBUG "${EASTL_LIB_DIR}/Debug/${LIB_PREFIX}EASTL${LIB_SUFFIX}"
INTERFACE_INCLUDE_DIRECTORIES "${EASTL_INCLUDE_DIRS}")
endif()
file(GLOB_RECURSE SRC "src/*.cpp")
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.inl" "include/*.hpp" "include/*.tpp")
file(GLOB_RECURSE NATVIS "natvis/*.natjmc" "natvis/*.natstepfilter" "natvis/*.natvis")
foreach(_source IN ITEMS ${HEADERS})
get_filename_component(_source_path "${_source}" PATH)
file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}")
string(REPLACE "/" "\\" _group_path "${_source_path_rel}")
source_group("${_group_path}" FILES "${_source}")
endforeach()
foreach(_source IN ITEMS ${SRC})
get_filename_component(_source_path "${_source}" PATH)
file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}")
string(REPLACE "/" "\\" _group_path "${_source_path_rel}")
source_group("${_group_path}" FILES "${_source}")
endforeach()
add_library(TL STATIC ${SRC} ${HEADERS} ${NATVIS})
target_include_directories(TL PUBLIC "include")
target_link_libraries(TL EASTL)
#if(BUILD_SHARED_LIBS)
# target_compile_definitions(TL PUBLIC TL_BUILD_SHARED_LIB)
#endif()
if (0)
#//////////////////////////////////
set(Boost_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../boost")
set(Boost_LIBRARY_DIR "${PROJECT_SOURCE_DIR}/../boost/stage/lib")
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.69 COMPONENTS unit_test_framework)
file(GLOB_RECURSE TEST_SRC "test/*.cpp" "test/*.h")
add_executable(TL_Test ${TEST_SRC})
target_link_libraries(TL_Test TL)
target_link_libraries(TL_Test ${Boost_LIBRARIES})
target_include_directories(TL_Test PUBLIC ${Boost_INCLUDE_DIRS})
endif()
+879
View File
@@ -0,0 +1,879 @@
#pragma once
#include "tl/detail/prologue.h"
#include <cstddef>
#include "tl/path_system.h"
#include "tl/rel_path.h"
#include "tl/detail/path_base.h"
#include "tl/plain_crash.h"
#include "tl/format.h"
namespace tl
{
template<typename PathSystems> class abs_path;
template<typename PathSystems> struct path_key_less;
//////////////////////////////////////////////////////////////////////////
template<typename PathSystems>
class abs_path : public detail::path_system::path_base<PathSystems>
{
template<typename T> friend class rel_path;
public:
using rel_path_type = rel_path<PathSystems>;
abs_path() noexcept;
explicit abs_path(const char* path) noexcept;
abs_path(const char* path, size_t size) noexcept;
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
explicit abs_path(const Str& path) noexcept;
abs_path(const abs_path& other) noexcept;
abs_path(abs_path&& other) noexcept;
// operators
const string& operator[](size_t idx) const noexcept;
string& operator[](size_t idx) noexcept;
abs_path& operator=(const abs_path& other) noexcept;
abs_path& operator=(abs_path&& other) noexcept;
abs_path& operator=(const char* path) noexcept;
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path& operator=(const Str& path) noexcept;
abs_path operator+(const char* path) const noexcept;
abs_path operator+(const rel_path<PathSystems>& path) const noexcept;
abs_path operator+(rel_path<PathSystems>&& path) const noexcept;
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path operator+(const Str& path) const noexcept;
abs_path& operator+=(const char* path) noexcept;
abs_path& operator+=(const rel_path<PathSystems>& path) noexcept;
abs_path& operator+=(rel_path<PathSystems>&& path) noexcept;
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path& operator+=(const Str& path) noexcept;
bool operator==(const abs_path& other) const noexcept;
bool operator!=(const abs_path& other) const noexcept;
auto operator<=>(const abs_path& other) const noexcept;
void swap(abs_path& other) noexcept;
void clear() noexcept;
string str() const noexcept;
eastl::string eastl_str() const noexcept;
std::string std_str() const noexcept;
rel_path<PathSystems> subpath(size_t idx, int count = 0) const noexcept;
abs_path parent() const noexcept;
abs_path parent(size_t levels) const noexcept;
rel_path<PathSystems> path_to(const abs_path& to) const noexcept;
void path_to(rel_path<PathSystems>& dst, const abs_path& to) const noexcept;
bool is_prefix_of(const abs_path& path) const noexcept;
template <typename T>
bool is() const noexcept;
bool is_valid() const noexcept;
bool push_back(string element) noexcept override;
const string& front() const noexcept;
const string& back() const noexcept;
string& front() noexcept;
string& back() noexcept;
void take_elements(tl::vector<string>&& elements) noexcept override;
//Parses a string.
// 1st attempt is done to parse it as an absolute path. If it succeeds, it returns the path.
// 2nd attempt is done to parse it as a relative path and if it succeeds, it returns it as fallbackRoot + relative path. This happens ONLY if the fallbackRoot is valid
template<typename Str>
static abs_path from_string(const Str& string, const abs_path& fallbackRoot) noexcept;
//Parses a string as an absolute path. If it fails it returns an invalid path.
template<typename Str>
static abs_path from_string(const Str& string) noexcept;
template<typename Str>
static bool is_valid_string(const Str& string) noexcept;
template<typename Str>
static bool has_valid_tag(const Str& string) noexcept;
bool parse(const char* path, size_t size) noexcept;
private:
bool collapse_path() noexcept;
int m_pathSystemId = -1;
};
//////////////////////////////////////////////////////////////////////////
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>::abs_path() noexcept
{
m_pathSystemId = -1;
this->m_hash = 0;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>::abs_path(const char* path, size_t size) noexcept
{
if (!path)
TL_PLAIN_CRASH("null path in abs_path constructor");
if (parse(path, size) == false)
TL_PLAIN_FAIL("Failed to parse absolute path");
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>::abs_path(const char* path) noexcept
{
if (!path)
TL_PLAIN_CRASH("null path in abs_path constructor");
if (parse(path, strlen(path)) == false)
TL_PLAIN_FAIL("Failed to parse absolute path");
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
template <typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path<PathSystems>::abs_path(const Str& path) noexcept
{
if (parse(path.data(), path.size()) == false)
TL_PLAIN_FAIL("Failed to parse absolute path");
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>::abs_path(const abs_path& other) noexcept
: m_pathSystemId(other.m_pathSystemId)
{
this->m_elements = other.m_elements;
this->m_hash = other.m_hash;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>::abs_path(abs_path&& other) noexcept
: m_pathSystemId(other.m_pathSystemId)
{
this->m_elements = std::move(other.m_elements);
this->m_hash = other.m_hash;
other.m_hash = 0;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
const string& abs_path<PathSystems>::operator[](size_t idx) const noexcept
{
return this->m_elements[idx];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
string& abs_path<PathSystems>::operator[](size_t idx) noexcept
{
this->m_hash = 0;
return this->m_elements[idx];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator=(const abs_path& other) noexcept
{
this->m_elements = other.m_elements;
this->m_hash = other.m_hash;
m_pathSystemId = other.m_pathSystemId;
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator=(abs_path&& other) noexcept
{
std::swap(this->m_elements, other.m_elements);
std::swap(this->m_hash, other.m_hash);
std::swap(m_pathSystemId, other.m_pathSystemId);
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path<PathSystems>& abs_path<PathSystems>::operator=(const Str& path) noexcept
{
if (parse(path.data(), path.size()) == false)
TL_PLAIN_FAIL("Failed to parse absolute path");
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator=(const char* path) noexcept
{
if (!path)
TL_PLAIN_CRASH("null path in abs_path assignment");
if (parse(path, strlen(path)) == false)
TL_PLAIN_FAIL("Failed to parse absolute path");
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems> abs_path<PathSystems>::operator+(const char* path) const noexcept
{
if (path && path[0] != '\0')
{
abs_path p;
p.reserve(this->size() + 1);
p = *this;
p += path;
return p;
}
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path<PathSystems> abs_path<PathSystems>::operator+(const Str& path) const noexcept
{
if (!path.empty())
{
abs_path p;
p.reserve(this->size() + 1);
p = *this;
p += path;
return p;
}
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems> abs_path<PathSystems>::operator+(const rel_path<PathSystems>& path) const noexcept
{
if (!path.empty())
{
abs_path p;
p.reserve(this->size() + 1);
p = *this;
p += path;
return p;
}
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems> abs_path<PathSystems>::operator+(rel_path<PathSystems>&& path) const noexcept
{
if (!path.empty())
{
abs_path p;
p.reserve(this->size() + 1);
p = *this;
p += std::move(path);
return p;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator+=(const char* path) noexcept
{
if (m_pathSystemId < 0 && this->empty())
this->operator=(path);
else if (path && path[0] != '\0')
*this += rel_path<PathSystems>(path);
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
template<typename Str> requires (!std::is_same_v<Str, const char*> && !std::is_same_v<Str, char*>)
abs_path<PathSystems>& abs_path<PathSystems>::operator+=(const Str& path) noexcept
{
if (m_pathSystemId < 0 && this->empty())
this->operator=(path);
else if (!path.empty())
*this += rel_path<PathSystems>(path);
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator+=(const rel_path<PathSystems>& path) noexcept
{
if (!path.empty())
{
//use the move version
*this += rel_path<PathSystems>(path);
this->collapse_path();
}
return *this;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems>& abs_path<PathSystems>::operator+=(rel_path<PathSystems>&& path) noexcept
{
if (!path.empty())
{
this->m_elements.reserve(this->m_elements.size() + path.size());
for (auto& e : path)
{
if (!push_back(std::move(e)))
return *this;
}
this->m_hash = 0;
this->collapse_path();
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool abs_path<PathSystems>::operator==(const abs_path& other) const noexcept
{
return (!this->m_hash || !other.m_hash || this->m_hash == other.m_hash) &&
this->m_pathSystemId == other.m_pathSystemId &&
this->m_elements == other.m_elements;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool abs_path<PathSystems>::operator!=(const abs_path& other) const noexcept
{
return !(*this == other);
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
auto abs_path<PathSystems>::operator<=>(const abs_path& other) const noexcept
{
// return std::compare_three_way{}(this->m_elements, other.m_elements);
const size_t sz1 = this->m_elements.size();
const size_t sz2 = other.m_elements.size();
for (size_t i = 0; i < sz1 && i < sz2; ++i)
{
auto r = this->m_elements[i] <=> other.m_elements[i];
if (r != decltype(r)::equal)
return r;
}
return sz1 <=> sz2;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool abs_path<PathSystems>::parse(const char* path, size_t size) noexcept
{
this->clear();
if (size == 0 || !path || path[0] == 0)
return true;
m_pathSystemId = PathSystems::deduce_system_type(path, size);
if (m_pathSystemId < 0)
return false;
PathSystems::parse_absolute(this->m_elements, m_pathSystemId, path, size);
for (const string& element : this->m_elements)
{
if (!this->validate_element(element))
{
this->clear();
return false;
}
}
if (collapse_path() == false)
{
this->clear();
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
bool abs_path<PathSystems>::collapse_path() noexcept
{
if (this->empty())
return is_valid();
this->m_hash = 0;
for (size_t i = 0; i < this->m_elements.size();)
{
if (i >= 1 &&
this->m_elements[i] == path_system::back_t::value() &&
this->m_elements[i - 1] != path_system::back_t::value())
{
this->m_elements.erase(this->m_elements.begin() + i - 1, this->m_elements.begin() + i + 1); //remove the .. and the parent
i -= 1;
}
else if (this->m_elements[i] == path_system::current_t::value())
this->m_elements.erase(this->m_elements.begin() + i); //remove the .
else
i++;
}
if (!PathSystems::validate_abs_path(m_pathSystemId, this->m_elements))
return false;
// if the first element is ".." the collapse failed
if (!this->m_elements.empty() && this->m_elements[0] == path_system::back_t::value())
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void abs_path<PathSystems>::swap(abs_path& other) noexcept
{
std::swap(this->m_elements, other.m_elements);
std::swap(this->m_hash, other.m_hash);
std::swap(this->m_pathSystemId, other.m_pathSystemId);
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
void abs_path<PathSystems>::clear() noexcept
{
this->m_elements.resize(0);
this->m_hash = 0;
m_pathSystemId = -1;
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
string abs_path<PathSystems>::str() const noexcept
{
if (!is_valid() && this->empty())
return {};
return PathSystems::template format_absolute<string>(m_pathSystemId, this->m_elements);
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
eastl::string abs_path<PathSystems>::eastl_str() const noexcept
{
if (!is_valid() && this->empty())
return {};
return PathSystems::template format_absolute<eastl::string>(m_pathSystemId, this->m_elements);
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
std::string abs_path<PathSystems>::std_str() const noexcept
{
if (!is_valid() && this->empty())
return {};
return PathSystems::template format_absolute<std::string>(m_pathSystemId, this->m_elements);
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
rel_path<PathSystems> abs_path<PathSystems>::subpath(size_t idx, int count /* = 0 */) const noexcept
{
rel_path<PathSystems> dst;
if (count == 0)
count = (int)this->size() - (int)idx;
if (count < 0)
count = (int)this->size() - (int)idx + count;
if (count > 0 && idx < this->size())
{
dst.reserve(count);
for (int i = 0; i < count; i++)
dst.push_element(this->m_elements[idx + i]);
}
return dst;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems> abs_path<PathSystems>::parent() const noexcept
{
if (this->empty())
{
TL_PLAIN_FAIL("Invalid path");
return abs_path();
}
abs_path p = *this;
if (p.back() == path_system::back_t::value())
{
//do not do a pop_back here!!
//The parent of '/..' is '/../..'!!!
p.push_back(path_system::back_t::value());
}
else
p.pop_back();
if (!PathSystems::validate_abs_path(p.m_pathSystemId, p.m_elements))
{
TL_PLAIN_FAIL("Invalid path");
return abs_path();
}
return p;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
abs_path<PathSystems> abs_path<PathSystems>::parent(size_t levels) const noexcept
{
if (this->empty() || this->size() <= levels)
{
TL_PLAIN_FAIL("Invalid path");
return abs_path();
}
abs_path p = *this;
if (p.back() == path_system::back_t::value())
{
//The parent of '/..' is '/../..'!!!
while (levels > 0)
{
levels--;
p.push_back(path_system::back_t::value());
}
}
else
p.shrink(p.size() - levels);
if (!PathSystems::validate_abs_path(p.m_pathSystemId, p.m_elements))
{
TL_PLAIN_FAIL("Invalid path");
return abs_path();
}
return p;
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
template <typename T>
bool abs_path<PathSystems>::is() const noexcept
{
return PathSystems::template is<T>(m_pathSystemId);
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
inline bool abs_path<PathSystems>::is_valid() const noexcept
{
return m_pathSystemId >= 0;
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
rel_path<PathSystems> abs_path<PathSystems>::path_to(const abs_path& to) const noexcept
{
rel_path<PathSystems> path;
path_to(path, to);
return path;
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
void abs_path<PathSystems>::path_to(rel_path<PathSystems>& dst, const abs_path& to) const noexcept
{
dst.clear();
if (this->empty())
{
dst.reserve(to.size());
for (string const& element : to)
dst.push_element(element);
}
else if (to.empty())
{
//ms relative path from an absolute path to an empty path should be an empty path
}
else
{
const size_t count = std::min(this->size(), to.size());
size_t last = 0;
for (size_t i = 0; i < count; i++)
{
if (this->m_elements[i] != to.m_elements[i])
break;
last = i + 1;
}
dst.reserve((this->size() - last) + to.size() - last);
for (size_t i = last; i < this->size(); i++)
dst.push_element(path_system::back_t::value());
for (size_t i = last; i < to.size(); i++)
dst.push_element(to.m_elements[i]);
}
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
bool abs_path<PathSystems>::is_prefix_of(const abs_path& path) const noexcept
{
if (m_pathSystemId != path.m_pathSystemId)
return false;
if (this->size() > path.size())
return false;
for (size_t i = 0, _count = this->size(); i < _count; ++i)
{
if (this->m_elements[i] != path[i])
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
void abs_path<PathSystems>::take_elements(tl::vector<string>&& elements) noexcept
{
clear();
if (elements.empty())
return;
m_pathSystemId = PathSystems::deduce_system_type(elements.front().data(), elements.front().size());
if (m_pathSystemId < 0)
return;
detail::path_system::path_base<PathSystems>::take_elements(std::move(elements));
if (collapse_path() == false)
clear();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
bool abs_path<PathSystems>::push_back(string element) noexcept
{
if (m_pathSystemId < 0)
{
TL_PLAIN_ASSERT(this->empty());
m_pathSystemId = PathSystems::deduce_system_type(element.data(), element.size());
if (m_pathSystemId < 0)
return false;
}
const bool is_back_element = element == path_system::back_t::value();
if (!detail::path_system::path_base<PathSystems>::push_back(std::move(element)))
return false;
if (is_back_element && collapse_path() == false)
{
clear();
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
const tl::string& abs_path<PathSystems>::back() const noexcept
{
TL_PLAIN_ASSERT(!this->empty());
return this->m_elements.back();
}
//////////////////////////////////////////////////////////////////////////
template <typename PathSystems>
const tl::string& abs_path<PathSystems>::front() const noexcept
{
TL_PLAIN_ASSERT(!this->empty());
return this->m_elements.front();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
tl::string& abs_path<PathSystems>::front() noexcept
{
TL_PLAIN_ASSERT(!this->empty());
this->m_hash = 0;
return this->m_elements.front();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
tl::string& abs_path<PathSystems>::back() noexcept
{
TL_PLAIN_ASSERT(!this->empty());
this->m_hash = 0;
return this->m_elements.back();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
template<typename Str>
abs_path<PathSystems> abs_path<PathSystems>::from_string(const Str& string, const abs_path& fallbackRoot) noexcept
{
abs_path outPath;
if (outPath.parse(string.data(), string.size()))
return outPath;
else if (fallbackRoot.is_valid())
return fallbackRoot + string;
else
return abs_path();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
template<typename Str>
abs_path<PathSystems> abs_path<PathSystems>::from_string(const Str& string) noexcept
{
abs_path outPath;
if (outPath.parse(string.data(), string.size()))
return outPath;
return abs_path();
}
//////////////////////////////////////////////////////////////////////////
template<class PathSystems>
template<typename Str>
bool abs_path<PathSystems>::is_valid_string(const Str& string) noexcept
{
abs_path outPath;
return outPath.parse(string.data(), string.size());
}
template<class PathSystems>
template<typename Str>
bool abs_path<PathSystems>::has_valid_tag(const Str& string) noexcept
{
if (string.empty())
return false;
const int pathSystemId = PathSystems::deduce_system_type(string.data(), string.size());
return pathSystemId >= 0;
}
}
//////////////////////////////////////////////////////////////////////////
template <class PathSystems>
struct std::formatter<tl::abs_path<PathSystems>>
{
constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); }
auto format(const tl::abs_path<PathSystems>& p, std::format_context& ctx) const
{
return format_to(ctx.out(), "{}", p.str());
}
};
template <typename T>
void swap(tl::abs_path<T>& a, tl::abs_path<T>& b) noexcept
{
a.swap(b);
}
template <typename T>
struct std::hash<tl::abs_path<T>>
{
std::size_t operator()(const tl::abs_path<T>& p) const
{
return p.hash();
}
};
template <typename T>
struct eastl::hash<tl::abs_path<T>>
{
std::size_t operator()(const tl::abs_path<T>& p) const
{
return p.hash();
}
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/algorithm.h>
namespace tl
{
using namespace eastl;
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// adjacent_find()
//
// Container-based version of the <algorithm> `std::adjacent_find()` function to
// find equal adjacent elements within a container.
template <typename Squence>
auto adjacent_find(Squence& sequence) noexcept -> decltype(tl::begin(sequence));
// Overload of adjacent_find() for using a predicate evaluation other than `==` as
// the function's test condition.
template <typename Sequence, typename BinaryPredicate>
auto adjacent_find(Sequence& sequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence));
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
#include "tl/detail/algorithm/adjacent_find.inl"
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/algorithm.h"
#include "tl/iterator.h"
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename C, typename Pred>
bool all_of(const C& c, Pred&& pred) noexcept
{
return eastl::all_of(tl::begin(c), tl::end(c), std::forward<Pred>(pred));
}
template <typename C, typename Pred>
bool any_of(const C& c, Pred&& pred) noexcept
{
return eastl::any_of(tl::begin(c), tl::end(c), std::forward<Pred>(pred));
}
template <typename C, typename Pred>
bool none_of(const C& c, Pred&& pred) noexcept
{
return eastl::none_of(tl::begin(c), tl::end(c), std::forward<Pred>(pred));
}
//////////////////////////////////////////////////////////////////////////
}
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/type_traits.h"
#include <iterator>
#include <type_traits>
#include <forward_list>
namespace tl
{
namespace detail
{
//////////////////////////////////////////////////////////////////////////
// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...]
template <typename SrcContainer, typename DstContainer>
void append(std::true_type, DstContainer& dst, const SrcContainer& src) noexcept
{
dst.insert(std::begin(src), std::end(src));
}
//////////////////////////////////////////////////////////////////////////
// sequence containers [vector, dequeue, forward_list, list, ...]
template <typename SrcContainer, typename DstContainer>
void append(std::false_type, DstContainer& dst, const SrcContainer& src) noexcept
{
dst.insert(std::end(dst), std::begin(src), std::end(src));
}
//////////////////////////////////////////////////////////////////////////
// Obs: std::forward_list don't support fast traversal to the end
// thus going to std::prev(std::end(dst)) must be done manually
// Cost: O(N)
template <typename T, typename SrcContainer>
void append(std::false_type, std::forward_list<T>& dst, const SrcContainer& src) noexcept
{
auto before_end = dst.before_begin();
for (auto& _ : dst)
++before_end;
dst.insert_after(before_end, std::begin(src), std::end(src));
}
//////////////////////////////////////////////////////////////////////////
} // namespace detail
//////////////////////////////////////////////////////////////////////////
template <typename SrcContainer, typename DstContainer>
void append(DstContainer& dst, const SrcContainer& src) noexcept
{
detail::append(tl::is_associative_container<DstContainer>{}, dst, src);
}
//////////////////////////////////////////////////////////////////////////
}
+102
View File
@@ -0,0 +1,102 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/functional.h"
namespace tl
{
namespace algorithm
{
//////////////////////////////////////////////////////////////////////////
/**
* @brief Count the number of bit set for the given value.
*/
inline uint8_t bits_count(uint32_t i_value) noexcept
{
#if defined(_MSC_VER)
return uint8_t(__popcnt(i_value));
#elif defined(__clang__) || defined(__GNUC__)
return uint8_t(__builtin_popcount(i_value));
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler");
#endif
}
//////////////////////////////////////////////////////////////////////////
/**
* @brief Count the number of bit set for the given value.
*/
inline uint64_t bits_count(uint64_t i_value) noexcept
{
#if defined(_MSC_VER)
#if defined(TL_PLATFORM_64)
return uint8_t(__popcnt64(i_value));
#elif defined(TL_PLATFORM_32)
return bits_count(uint32_t(i_value >> 32)) + bits_count(uint32_t(i_value & 0xffffffff));
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler);");
#endif // TL_PLATFORM_XX
#elif defined(__clang__) || defined(__GNUC__)
return uint8_t(__builtin_popcountl(i_value));
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler");
#endif
}
//////////////////////////////////////////////////////////////////////////
/**
* @brief Position of the top bit set to 1.
* @param the given value
* @return -1 in case value is 0 otherwise the position.
*/
inline int8_t top_bit(uint32_t i_value) noexcept
{
#if defined(_MSC_VER)
unsigned long index = 0;
return _BitScanReverse(&index, (unsigned long)i_value) ? uint8_t(index) : -1;
#elif defined(__clang__) || defined(__GNUC__)
return i_value ? 31u - __builtin_clzl(i_value) : -1;
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler);");
#endif
}
//////////////////////////////////////////////////////////////////////////
/**
* @brief Position of the top bit set to 1.
* @param the given value
* @return -1 in case value is 0 otherwise the position.
*/
inline int8_t top_bit(uint64_t i_value) noexcept
{
#if defined(_MSC_VER)
unsigned long index = 0;
#if defined(TL_PLATFORM_64)
return _BitScanReverse64(&index, (unsigned long long)i_value) ? uint8_t(index) : -1;
#elif defined(TL_PLATFORM_32)
const int8_t position = top_bit(uint32_t(i_value >> 32));
if (position > -1)
{
return position + 32;
}
return top_bit(uint32_t(i_value & 0xffffffff));
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler);");
#endif // TL_PLATFORM_XX
#elif defined(__clang__) || defined(__GNUC__)
return i_value ? 63u - __builtin_clzll(i_value) : -1;
#else
static_assert(!std::is_same_v<T, T>, "no-builtin function found for this compiler);");
#endif
}
//////////////////////////////////////////////////////////////////////////
} // namespace algorithm
} // namespace tl
@@ -0,0 +1,20 @@
#pragma once
#include "tl/detail/prologue.h"
namespace tl
{
namespace algorithm
{
//////////////////////////////////////////////////////////////////////////
template <typename T, typename... Comparators> struct chained_sorting_predicate;
template <typename T, typename... Comparators>
chained_sorting_predicate<T, Comparators...> make_chained_sorting_predicate(const Comparators&... args) noexcept;
}
}
+36
View File
@@ -0,0 +1,36 @@
#pragma once
#include "tl/detail/prologue.h"
// see https://en.cppreference.com/w/cpp/algorithm
// -----------------------------------------------------------------------------
// Non-modifiying sequence operations (STL specializations)
// -----------------------------------------------------------------------------
#include "tl/algorithm/all_any_none_of.h"
#include "tl/algorithm/for_each.h"
#include "tl/algorithm/count.h"
#include "tl/algorithm/mismatch.h"
#include "tl/algorithm/find.h"
#include "tl/algorithm/find_end.h"
#include "tl/algorithm/find_first_of.h"
#include "tl/algorithm/adjacent_find.h"
#include "tl/algorithm/search.h"
// -----------------------------------------------------------------------------
// Non-modifiying sequence operations (our own versions)
// -----------------------------------------------------------------------------
#include "tl/algorithm/contains.h"
// -----------------------------------------------------------------------------
// Modifiying sequence operations
// -----------------------------------------------------------------------------
#include "tl/algorithm/copy.h"
#include "tl/algorithm/remove.h"
#include "tl/algorithm/unique.h"
// -----------------------------------------------------------------------------
// Modifiying sequence operations (our own versions)
// -----------------------------------------------------------------------------
#include "tl/algorithm/append.h"
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/algorithm/find.h"
#include <iterator>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename C, typename T>
bool contains(C& c, T&& value) noexcept
{
return tl::find(c, std::forward<T>(value)) != tl::end(c);
}
template <typename C, typename T>
bool contains(const C& c, T&& value) noexcept
{
return tl::cfind(c, std::forward<T>(value)) != tl::end(c);
}
template <typename C, typename UnaryPredicate>
bool contains_if(C& c, UnaryPredicate&& p) noexcept
{
return tl::find_if(c, std::forward<UnaryPredicate>(p)) != tl::end(c);
}
template <typename C, typename UnaryPredicate>
bool contains_if(const C& c, UnaryPredicate&& p) noexcept
{
return tl::cfind_if(c, std::forward<UnaryPredicate>(p)) != tl::end(c);
}
//////////////////////////////////////////////////////////////////////////
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "tl/detail/prologue.h"
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename OutputIt> auto copy(const Container& container, OutputIt d_first) noexcept -> OutputIt;
template <typename Container, typename OutputIt, typename UnaryPredicate> auto copy_if(const Container& container, OutputIt d_first, const UnaryPredicate& predicate) noexcept -> OutputIt;
//////////////////////////////////////////////////////////////////////////
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// count()
//
// Container-based version of the <algorithm> `std::count()` function to
// count values that match within a container
template <typename C, typename T>
auto count(const C& c, T&& value) noexcept -> typename std::iterator_traits<typename C::iterator>::difference_type;
// count_if()
//
// Container-based version of the <algorithm> `std::count()` function to
// count values that match within a container
template <typename C, typename UnaryPredicate>
auto count_if(const C& c, UnaryPredicate&& p) noexcept -> typename std::iterator_traits<typename C::iterator>::difference_type;
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
+155
View File
@@ -0,0 +1,155 @@
#pragma once
#include "tl/detail/prologue.h"
#include <tl/algorithm.h>
#include <tl/utility.h>
#include <tl/type_traits.h>
namespace tl
{
namespace detail
{
// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...]
template <typename C, typename Key>
auto find(std::true_type, C& c, const Key& key) noexcept -> decltype(c.begin())
{
return c.find(key);
}
// sequence containers [array, vector, dequeue, forward_list, list, ...]
template <typename C, typename T>
auto find(std::false_type, C& c, const T& value) noexcept -> decltype(c.begin())
{
return eastl::find(tl::begin(c), tl::end(c), value);
}
} // namespace detail
//////////////////////////////////////////////////////////////////////////
template <typename C, typename T>
auto find(C& c, const T& value) noexcept -> decltype(c.begin())
{
return detail::find(is_associative_container<C>{}, c, value);
}
//////////////////////////////////////////////////////////////////////////
namespace detail
{
// associative containers (ordered & unordered) [maps, unordered_maps, multimaps, unordered_multimaps ...]
template <typename C, typename Key>
auto cfind(std::true_type, const C& c, const Key& key) noexcept -> decltype(c.cbegin())
{
return c.find(key);
}
// sequence containers [array, vector, dequeue, forward_list, list, ...]
template <typename C, typename T>
auto cfind(std::false_type, const C& c, const T& value) noexcept -> decltype(c.cbegin())
{
return eastl::find(tl::cbegin(c), tl::cend(c), value);
}
} // namespace detail
template <typename C, typename T>
auto cfind(const C& c, const T& value) noexcept -> decltype(c.cbegin())
{
return detail::cfind(is_associative_container<C>{}, c, value);
}
//////////////////////////////////////////////////////////////////////////
namespace detail
{
// associative containers (ordered) [maps, multimaps ...]
template <typename C, typename Key>
auto rfind(std::true_type, C& c, const Key& key) noexcept -> decltype(c.rbegin())
{
auto forward_iterator = c.find(key);
return tl::make_reverse_iterator(forward_iterator);
}
// sequence containers [array, vector, dequeue, list, ...]
template <typename C, typename T>
auto rfind(std::false_type, C& c, const T& value) noexcept -> decltype(c.rbegin())
{
return eastl::find(tl::rbegin(c), tl::rend(c), value);
}
} // namespace detail
template <typename C, typename T>
auto rfind(C& c, const T& value) noexcept -> decltype(c.rbegin())
{
return detail::rfind(is_associative_container<C>{}, c, value);
}
//////////////////////////////////////////////////////////////////////////
namespace detail
{
// associative containers (ordered) [maps, multimaps ...]
template <typename C, typename Key>
auto crfind(std::true_type, const C& c, const Key& key) noexcept -> decltype(c.crbegin())
{
auto forward_iterator = c.find(key);
return tl::make_reverse_iterator(forward_iterator);
}
// sequence containers [array, vector, dequeue, forward_list, list, ...]
template <typename C, typename T>
auto crfind(std::false_type, const C& c, const T& value) noexcept -> decltype(c.crbegin())
{
return eastl::find(tl::rbegin(c), tl::rend(c), value);
}
} // namespace detail
template <typename C, typename T>
auto crfind(const C& c, const T& value) noexcept -> decltype(c.crbegin())
{
return detail::rfind(is_associative_container<C>{}, c, value);
}
//////////////////////////////////////////////////////////////////////////
template <typename C, typename UnaryPredicate>
auto find_if(C& c, UnaryPredicate&& p) noexcept -> decltype(c.begin())
{
return eastl::find_if(tl::begin(c), tl::end(c), std::forward<UnaryPredicate>(p));
}
//////////////////////////////////////////////////////////////////////////
template <typename C, typename UnaryPredicate>
auto cfind_if(const C& c, UnaryPredicate&& p) noexcept -> decltype(c.cbegin())
{
return eastl::find_if(tl::cbegin(c), tl::cend(c), std::forward<UnaryPredicate>(p));
}
//////////////////////////////////////////////////////////////////////////
template <typename C, typename UnaryPredicate>
auto rfind_if(C& c, UnaryPredicate&& p) noexcept -> decltype(c.rbegin())
{
return eastl::find_if(tl::rbegin(c), tl::rend(c), std::forward<UnaryPredicate>(p));
}
//////////////////////////////////////////////////////////////////////////
template <typename C, typename UnaryPredicate>
auto crfind_if(const C& c, UnaryPredicate&& p) noexcept -> decltype(c.crbegin())
{
return eastl::find_if(tl::crbegin(c), tl::crend(c), std::forward<UnaryPredicate>(p));
}
//////////////////////////////////////////////////////////////////////////
template <typename C, typename UnaryPredicate>
auto find_if_not(C& c, UnaryPredicate&& q) noexcept -> decltype(tl::begin(c))
{
return eastl::find_if(tl::begin(c), tl::end(c), std::forward<UnaryPredicate>(q));
}
//////////////////////////////////////////////////////////////////////////
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// find_end()
//
// Container-based version of the <algorithm> `std::find_end()` function to
// find the last subsequence within a container.
template <typename SquenceA, typename SequenceB>
auto find_end(SquenceA& sequence, SequenceB& subsequence) noexcept -> decltype(tl::begin(sequence));
// Overload of find_end() for using a predicate evaluation other than `==` as
// the function's test condition.
template <typename SequenceA, typename SequenceB, typename BinaryPredicate>
auto find_end(SequenceA& sequence, SequenceB& subsequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence));
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
#include "tl/detail/algorithm/find_end.inl"
+28
View File
@@ -0,0 +1,28 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// find_first_of()
//
// Container-based version of the <algorithm> `std::find_first_of()` function to
// find the first elements in an ordered set within a container.
template <typename SquenceA, typename SequenceB>
auto find_first_of(SquenceA& sequence, SequenceB& subsequence) noexcept -> decltype(tl::begin(sequence));
// Overload of find_first_of() for using a predicate evaluation other than `==` as
// the function's test condition.
template <typename SequenceA, typename SequenceB, typename BinaryPredicate>
auto find_first_of(SequenceA& sequence, SequenceB& subsequence, BinaryPredicate&& p) noexcept -> decltype(tl::begin(sequence));
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/type_traits.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// for_each()
//
// Container-based version of the <algorithm> `std::for_each()` function to
// apply a function to a container's elements.
template <typename C, typename Function>
decay_t<Function> for_each(C&& c, Function&& f) noexcept;
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
+27
View File
@@ -0,0 +1,27 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// mismatch()
//
// Container-based version of the <algorithm> `std::mismatch()` function to
// return the first element where two ordered containers differ
template <typename C1, typename C2>
auto mismatch(C1& c1, C2& c2) noexcept -> std::pair<decltype(tl::begin(c1)), decltype(tl::begin(c2))>;
// Overload of mismatch() for using a predicate evaluation other than `==` as
// the function's test condition.
template <typename C1, typename C2, typename BinaryPredicate>
auto mismatch(C1& c1, C2& c2, BinaryPredicate&& p) noexcept -> std::pair<decltype(tl::begin(c1)), decltype(tl::begin(c2))>;
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include "tl/detail/prologue.h"
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename OutputIt> auto move(Container& container, OutputIt d_first) noexcept -> OutputIt;
//////////////////////////////////////////////////////////////////////////
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/algorithm.h"
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename T>
auto remove(Container& container, const T& value) noexcept -> decltype(container.begin())
{
return eastl::remove(container.begin(), container.end(), value);
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename UnaryPredicate>
auto remove_if(Container& container, const UnaryPredicate& predicate) noexcept -> decltype(container.begin())
{
return eastl::remove_if(container.begin(), container.end(), predicate);
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename T>
auto erase(Container& container, const T& value) noexcept -> decltype(container.begin())
{
return container.erase(remove(container, value), container.end());
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename UnaryPredicate>
auto erase_if(Container& container, const UnaryPredicate& predicate) noexcept -> decltype(container.begin())
{
return container.erase(remove_if(container, predicate), container.end());
}
//////////////////////////////////////////////////////////////////////////
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/iterator.h"
#include <algorithm>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
// search()
//
// Container-based version of the <algorithm> `std::search()` function to search
// a container for a subsequence.
template <typename Sequence1, typename Sequence2>
auto search(Sequence1& sequence, Sequence2& subsequence) noexcept -> decltype(tl::begin(sequence));
// Overload of search() for using a predicate evaluation other than
// `==` as the function's test condition.
template <typename Sequence1, typename Sequence2, typename BinaryPredicate>
auto search(Sequence1& sequence, Sequence2& subsequence, BinaryPredicate&& pred) noexcept -> decltype(tl::begin(sequence));
// search_n()
//
// Container-based version of the <algorithm> `std::search_n()` function to
// search a container for the first sequence of N elements.
template <typename Sequence, typename Size, typename T>
auto search_n(Sequence& sequence, Size count, T&& value) noexcept -> decltype(tl::begin(sequence));
// Overload of search_n() for using a predicate evaluation other than
// `==` as the function's test condition.
template <typename Sequence, typename Size, typename T, typename BinaryPredicate>
auto search_n(Sequence& sequence, Size count, T&& value, BinaryPredicate&& pred) noexcept -> decltype(tl::begin(sequence));
//////////////////////////////////////////////////////////////////////////
} // end namespace tl
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/algorithm.h>
#include <EASTL/sort.h>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename Container>
void sort(Container& container) noexcept
{
eastl::sort(container.begin(), container.end());
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename Compare>
void sort(Container& container, const Compare& compare) noexcept
{
eastl::sort(container.begin(), container.end(), compare);
}
//////////////////////////////////////////////////////////////////////////
template <typename Container>
void stable_sort(Container& container) noexcept
{
eastl::stable_sort(container.begin(), container.end());
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename Compare>
void stable_sort(Container& container, const Compare& compare) noexcept
{
eastl::stable_sort(container.begin(), container.end(), compare);
}
//////////////////////////////////////////////////////////////////////////
}
+622
View File
@@ -0,0 +1,622 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/functional.h"
#include <EASTL/vector.h>
#include <tl/fixed_vector.h>
#include "tl/string.h"
#include "tl/optional.h"
namespace tl
{
namespace algorithm
{
enum class empty_token_policy
{
discard,
keep
};
template <class DstContainer, class SrcContainer>
DstContainer to_lower_ascii_copy(const SrcContainer& str) noexcept
{
if constexpr (tl::is_same_v<DstContainer, string>)
{
if (str.empty())
return string();
const string::size_type s = str.size();
fixed_vector<char, 512> b(s);
char const* string = str.data();
for (string::size_type i = 0; i < s; i++)
b[i] = (char)ascii::tolower(string[i]);
return { b.data(), s };
}
else
{
DstContainer dst;
dst.resize(str.size());
eastl::transform(tl::begin(str), tl::end(str), tl::begin(dst), ascii::tolower);
return dst;
}}
template <class Container>
Container to_lower_ascii_copy(const Container& str) noexcept
{
return to_lower_ascii_copy<Container, Container>(str);
}
template <class DstContainer, class SrcContainer>
DstContainer to_upper_ascii_copy(const SrcContainer& str) noexcept
{
if constexpr (tl::is_same_v<DstContainer, string>)
{
if (str.empty())
return string();
const string::size_type s = str.size();
fixed_vector<char, 512> b(s);
char const* string = str.data();
for (string::size_type i = 0; i < s; i++)
b[i] = (char)ascii::toupper(string[i]);
return { b.data(), s };
}
else
{
DstContainer dst;
dst.resize(str.size());
eastl::transform(tl::begin(str), tl::end(str), tl::begin(dst), ascii::toupper);
return dst;
}
}
template <class Container>
Container to_upper_ascii_copy(const Container& str) noexcept
{
return to_upper_ascii_copy<Container, Container>(str);
}
template <class Container>
void to_lower_ascii(Container& str) noexcept
{
eastl::transform(tl::begin(str), tl::end(str), tl::begin(str), ascii::tolower);
}
template <>
inline void to_lower_ascii(string& str) noexcept
{
str = to_lower_ascii_copy(str);
}
template <class Container>
void to_upper_ascii(Container& str) noexcept
{
eastl::transform(tl::begin(str), tl::end(str), tl::begin(str), ascii::toupper);
}
template <>
inline void to_upper_ascii(string& str) noexcept
{
str = to_upper_ascii_copy(str);
}
template <class String>
tl::vector<String> split_on_any(const String& str, const char* separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
return split_on_any(str, String(separators), token_policy);
}
template <class String, class Delim>
tl::vector<String> split_on_any(const String& str, const Delim& separators, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
tl::vector<String> dst;
dst.reserve(32);
split_on_any(str,
separators,
[&dst](const String& token)
{
dst.push_back(token);
},
token_policy);
return dst;
}
template <class String, class Delim>
tl::vector<String> split_on_all(const String& str, const Delim& separator, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
tl::vector<String> dst;
dst.reserve(32);
split_on_all(str,
separator,
[&dst](const String& token)
{
dst.push_back(token);
},
token_policy);
return dst;
}
template <class String, class Func>
void split_on_any(const String& str, const char* delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
return split_on_any(str, String(delimiters), functor, token_policy);
}
template <class String, class Func>
void split_on_any(const String& str, char delimiter, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
String token;
const char* cstr = str.data();
size_t offset = 0;
const size_t endOffset = str.size();
while (offset <= endOffset)
{
const char* nextCstr = static_cast<const char*>(::memchr(cstr + offset, delimiter, endOffset - offset));
const size_t nextOffset = nextCstr ? nextCstr - cstr : endOffset;
if (offset < nextOffset || token_policy == empty_token_policy::keep)
{
token.clear();
token.append(cstr + offset, cstr + nextOffset);
functor(token);
}
offset = nextOffset + 1;
}
}
template <class String, class Delim, class Func>
void split_on_any(const String& str, const Delim& delimiters, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
const size_t k_delims_size = delimiters.size();
if (k_delims_size == 1)
return split_on_any(str, delimiters[0], functor, token_policy);
if (k_delims_size == 0)
{
TL_PLAIN_FAIL("'delimiters' cannot be empty");
return;
}
String token;
const char* cstr = str.data();
const char* delimsStr = delimiters.data();
size_t offset = 0;
const size_t endOffset = str.size();
while (offset <= endOffset)
{
size_t nextOffset = offset;
for (; nextOffset < endOffset; ++nextOffset)
{
if (::memchr(delimsStr, cstr[nextOffset], k_delims_size) != nullptr)
break;
}
if (offset < nextOffset || token_policy == empty_token_policy::keep)
{
token.clear();
token.append(cstr + offset, cstr + nextOffset);
functor(token);
}
offset = nextOffset + 1;
}
}
template <class String, class Delim, class Func>
void split_on_all(const String& str, const Delim& sep, const Func& functor, empty_token_policy token_policy = empty_token_policy::discard) noexcept
{
String separator(sep);
if (separator.empty())
{
TL_PLAIN_FAIL("'delimiters' cannot be empty");
return;
}
size_t p0 = 0;
size_t p1 = 0;
String token;
while ((p1 = str.find(separator, p0)) != String::npos)
{
if (p1 > p0)
{
token.clear();
token.append(str, p0, p1 - p0);
functor(token);
}
else if (token_policy == empty_token_policy::keep)
functor(String{});
p0 = p1 + separator.size();
}
if (str.size() > p0)
{
token.clear();
token.append(str, p0, String::npos);
functor(token);
}
else if (token_policy == empty_token_policy::keep)
functor(String{});
}
template <class String>
String left(const String& text, size_t length) noexcept
{
size_t textSize = text.size();
return text.substr(0, length > textSize ? textSize : length);
}
template <class String>
String right(const String& text, size_t length) noexcept
{
const size_t textSize = text.size();
size_t startPos = textSize >= length ? textSize - length : 0;
return text.substr(startPos);
}
template <class String>
bool starts_with(const String& text, const String& prefix) noexcept
{
const size_t textSize = text.size();
const size_t prefixSize = prefix.size();
if (textSize < prefixSize)
return false;
return memcmp(text.data(), prefix.data(), prefixSize) == 0;
}
template <class String>
bool starts_with_ci(const String& text, const String& prefix) noexcept
{
const size_t textSize = text.size();
const size_t prefixSize = prefix.size();
if (textSize < prefixSize)
return false;
return ascii::memicmp(text.data(), prefix.data(), prefixSize) == 0;
}
template <class String>
bool ends_with(const String& text, const String& suffix) noexcept
{
const size_t textSize = text.size();
const size_t suffixSize = suffix.size();
if (textSize < suffixSize)
return false;
return memcmp(text.data() + (textSize - suffixSize), suffix.data(), suffixSize) == 0;
}
template <class String>
bool ends_with_ci(const String& text, const String& suffix) noexcept
{
const size_t textSize = text.size();
const size_t suffixSize = suffix.size();
if (textSize < suffixSize)
return false;
return ascii::memicmp(text.data() + (textSize - suffixSize), suffix.data(), suffixSize) == 0;
}
template <class String>
String remove_prefix(const String& text, const String& prefix) noexcept
{
if (starts_with(text, prefix))
return text.substr(prefix.size(), text.size() - prefix.size());
return text;
}
template <class String>
String remove_prefix_ci(const String& text, const String& prefix) noexcept
{
if (starts_with_ci(text, prefix))
return text.substr(prefix.size(), text.size() - prefix.size());
return text;
}
template <class String>
String remove_suffix(const String& text, const String& suffix) noexcept
{
if (ends_with(text, suffix))
return text.substr(0, text.size() - suffix.size());
return text;
}
template <class String>
String remove_suffix_ci(const String& text, const String& suffix) noexcept
{
if (ends_with_ci(text, suffix))
return text.substr(0, text.size() - suffix.size());
return text;
}
template <class String>
eastl::optional<size_t> contains(const String& text, const String& subText) noexcept
{
const size_t offset = text.find(subText);
if (offset != String::npos)
return offset;
return nullopt;
}
template <class String>
eastl::optional<size_t> contains_ci(const String& text, const String& subText) noexcept
{
const size_t offset = text.find_ci(subText);
if (offset != String::npos)
return offset;
return nullopt;
}
template <class String>
String quote(const String& text, const String& quote) noexcept
{
String result = quote;
result += text;
result += quote;
return result;
}
template <class String>
String unquote(const String& text, const String& quote) noexcept
{
if (starts_with(text, quote) && ends_with(text, quote) && text.size() >= quote.size() * 2)
return text.substr(quote.size(), text.size() - quote.size() * 2);
return text;
}
static const char* s_defaultWhiteSpaceCStr = " \t\f\v\n\r";
template <class String>
String trim_left_any_of(const String& text, const String& characters) noexcept
{
const typename String::size_type startPos = text.find_first_not_of(characters);
if (startPos == String::npos)
return String();
return text.substr(startPos);
}
template <class String>
String trim_left_any_of_ci(const String& text, const String& characters) noexcept
{
const typename String::size_type startPos = text.find_first_not_of_ci(characters);
if (startPos == String::npos)
return String();
return text.substr(startPos);
}
template <class String>
String trim_left(const String& text) noexcept
{
static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr);
return trim_left_any_of(text, s_defaultWhiteSpace);
}
template <class String>
String trim_left_ci(const String& text) noexcept
{
static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr);
return trim_left_any_of_ci(text, s_defaultWhiteSpace);
}
template <class String>
String trim_right_any_of(const String& text, const String& characters) noexcept
{
const typename String::size_type startPos = text.find_last_not_of(characters);
if (startPos == String::npos)
return String();
return text.substr(0, startPos + 1);
}
template <class String>
String trim_right_any_of_ci(const String& text, const String& characters) noexcept
{
const typename String::size_type startPos = text.find_last_not_of_ci(characters);
if (startPos == String::npos)
return String();
return text.substr(0, startPos + 1);
}
template <class String>
String trim_right(const String& text) noexcept
{
static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr);
return trim_right_any_of(text, String(s_defaultWhiteSpace));
}
template <class String>
String trim_right_ci(const String& text) noexcept
{
static String s_defaultWhiteSpace(s_defaultWhiteSpaceCStr);
return trim_right_any_of_ci(text, String(s_defaultWhiteSpace));
}
template <class String>
String trim(const String& text) noexcept
{
return trim_right(trim_left(text));
}
template <class String>
String trim_ci(const String& text) noexcept
{
return trim_right_ci(trim_left_ci(text));
}
template <class String>
String trim_any_of(const String& text, const String& characters) noexcept
{
return trim_right_any_of(trim_left_any_of(text, characters), characters);
}
template <class String>
String trim_any_of_ci(const String& text, const String& characters) noexcept
{
return trim_right_any_of_ci(trim_left_any_of_ci(text, characters), characters);
}
template <class String, class RepString>
String replace(const String& text, const String& target, const function<eastl::optional<RepString>(size_t, size_t)>& functor) noexcept
{
if (target.empty())
return text;
eastl::fixed_vector<size_t, 512> positions;
size_t position = 0;
while ((position = text.find(target, position)) != String::npos)
{
positions.push_back(position);
position += target.length();
}
String result;
result.reserve(text.size());
auto start = text.begin();
for (auto it = positions.begin(); it != positions.end(); ++it)
{
const size_t position = *it;
size_t index = eastl::distance(positions.begin(), it);
eastl::optional<RepString> replacementString = functor(index, positions.size());
if (!replacementString)
continue;
auto end = text.begin() + position;
result.append(start, end); //copy up to the result
result.append(*replacementString);
start = end + target.size();
}
if (start != text.end())
result.append(start, text.end()); //copy the end run
return result;
}
template <class String, typename Fun>
String replace(const String& text, const String& target, Fun f) noexcept
{
return replace(text, target, function<eastl::optional<String>(size_t, size_t)>(f));
}
template <class Func>
size_t replace_first(const string& text, const string& target, const string& replacement, size_t startingOffset, const Func& functor) noexcept
{
if (target.empty())
{
functor(text);
return string::npos;
}
const size_t startPos = text.find(target, startingOffset);
if (startPos == string::npos)
{
functor(text);
return string::npos;
}
eastl::string result(text.eastl_str());
string res2(result.replace(startPos, target.length(), eastl::string(replacement.eastl_str())));
functor(res2);
return startPos + replacement.length();
}
namespace detail
{
template <class Container, class String>
String join_worker(const Container& container, const String& delimiter) noexcept
{
String dst;
//reserve some space based on some heuristics:
// We'll have size - 1 delimiters
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
dst.reserve((delimiter.size() + 2) * container.size());
auto end = std::end(container);
auto it = std::begin(container);
while (true)
{
dst += *it;
++it;
if (it == end)
break;
dst += delimiter;
}
return dst;
}
template <class Container>
string join_worker(const Container& container, const string& delimiter) noexcept
{
eastl::string buffer;
//reserve some space based on some heuristics:
// We'll have size - 1 delimiters
// And an average string size of 2 chars. The small choice is made to help with the small string case only where the allocator overhead is significant.
buffer.reserve((delimiter.size() + 2) * container.size());
auto end = std::end(container);
auto it = std::begin(container);
while (true)
{
const string& s = *it;
buffer.append(s.data(), s.size());
++it;
if (it == end)
break;
buffer.append(delimiter.data(), delimiter.size());
}
return string(buffer);
}
//joiner used when the Delim and String are of the same type
template <class Container, class Delim, class String>
std::enable_if_t<
std::is_same_v<
std::remove_const_t<std::remove_reference_t<String>>,
std::remove_const_t<std::remove_reference_t<Delim>>>,
String> join(const Container& container, const Delim& delimiter) noexcept
{
return join_worker(container, delimiter);
}
//joiner used when the Delim and String are different types
template <class Container, class Delim, class String>
std::enable_if_t<
!std::is_same_v<
std::remove_const_t<std::remove_reference_t<String>>,
std::remove_const_t<std::remove_reference_t<Delim>>>,
String> join(const Container& container, const Delim& delimiter) noexcept
{
return join_worker(container, String(delimiter));
}
} //namespace detail
template <class Container, class Delim, class String>
String join(const Container& container, const Delim& delimiter) noexcept
{
if (container.empty())
return String();
return detail::join<Container, Delim, String>(container, delimiter);
}
}
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/algorithm.h>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename Container>
auto unique(Container& container) noexcept -> decltype(container.begin())
{
return eastl::unique(container.begin(), container.end());
}
//////////////////////////////////////////////////////////////////////////
template <typename Container, typename BinaryPredicate>
auto unique(Container& container, const BinaryPredicate& predicate) noexcept -> decltype(container.begin())
{
return eastl::unique(container.begin(), container.end(), predicate);
}
//////////////////////////////////////////////////////////////////////////
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include "tl/platform.h"
#include "tl/toolchain.h"
//////////////////////////////////////////////////////////////////////////
#if defined TL_BUILD_SHARED_LIB
#if defined TL_TOOLCHAIN_MSC
# define TL_API __declspec(dllexport)
#else
# define TL_API
#endif
#elif defined TL_USE_SHARED_LIB
#if defined TL_TOOLCHAIN_MSC
# define TL_API __declspec(dllimport)
#else
# define TL_API
#endif //
#else
# define TL_API
#endif
//////////////////////////////////////////////////////////////////////////
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/detail/internal_assert.h"
#include "tl/format.h"
+88
View File
@@ -0,0 +1,88 @@
#pragma once
#include <mutex>
#include <future>
#include <tl/deque.h>
#include <tl/functional.h>
namespace tl
{
class async_queue
{
public:
using Item = function<void()>;
void enqueue(Item func) noexcept
{
std::lock_guard lg(m_mutex);
m_items.push_back(std::move(func));
}
bool run_one() noexcept
{
std::unique_lock lg(m_mutex);
if (m_items.empty())
return false;
const Item item = std::move(m_items.front());
m_items.pop_front();
lg.unlock();
item();
return true;
}
bool run() noexcept
{
std::unique_lock lg(m_mutex);
if (m_items.empty())
return false;
do
{
Item item = std::move(m_items.front());
m_items.pop_front();
lg.unlock();
item();
lg.lock();
} while (!m_items.empty());
return true;
}
private:
std::mutex m_mutex;
deque<Item> m_items;
};
template<typename Res, typename Func>
std::future<Res> async(async_queue& queue, Func&& func) noexcept
{
shared_ptr<std::promise<Res>> promise = make_shared<std::promise<Res>>();
auto future = promise->get_future();
queue.enqueue([promise = std::move(promise), func = std::move(func)]()
{
if constexpr (tl::is_same_v<Res, void>)
{
func();
promise->set_value();
}
else
promise->set_value(func());
});
return future;
}
template<typename R>
bool is_ready(std::future<R> const& f) noexcept
{
return f.valid() && f.wait_until(std::chrono::system_clock::time_point::min()) == std::future_status::ready;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/atomic.h>
namespace tl
{
using namespace eastl;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/bitset.h>
namespace tl
{
using namespace eastl;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/bitvector.h>
namespace tl
{
using namespace eastl;
}
+471
View File
@@ -0,0 +1,471 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/result.h"
#include <tl/deque.h>
#include <mutex>
#include <condition_variable>
#include <cassert>
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename T>
class blocking_queue
{
public:
explicit blocking_queue(size_t max_size) noexcept;
~blocking_queue() noexcept;
void exit() noexcept; //stop all processing, returns Exited
void finish() noexcept; //once empty or full, stops all processing, returns Finished
enum class push_error_code
{
Full,
Timeout,
Finished,
Exited,
};
using push_result = result<void, push_error_code>;
push_result push(T const& t) noexcept;
push_result try_push(T const& t) noexcept;
push_result push_for(T const& t, chrono::system_clock::duration duration) noexcept;
push_result push_until(T const& t, chrono::system_clock::time_point time_point) noexcept;
push_result push(T&& t) noexcept;
push_result try_push(T&& t) noexcept;
push_result push_for(T&& t, chrono::system_clock::duration duration) noexcept;
push_result push_until(T&& t, chrono::system_clock::time_point time_point) noexcept;
enum class pop_error_code
{
Empty,
Timeout,
Exited,
Finished,
};
using pop_result = result<T, pop_error_code>;
pop_result pop() noexcept;
pop_result try_pop() noexcept;
pop_result pop_for(chrono::system_clock::duration duration) noexcept;
pop_result pop_until(chrono::system_clock::time_point time_point) noexcept;
template <typename Container>
using pop_many_result = result<Container, pop_error_code>;
template <typename Container>
pop_many_result<Container> pop_many(size_t max) noexcept;
template <typename Container>
pop_many_result<Container> try_pop_many(size_t max) noexcept;
template <typename Container>
pop_many_result<Container> pop_many_for(size_t max, chrono::system_clock::duration duration) noexcept;
template <typename Container>
pop_many_result<Container> pop_many_until(size_t max, chrono::system_clock::time_point time_point) noexcept;
using pop_many_emplace_result = result<void, pop_error_code>;
template <typename Container>
pop_many_emplace_result pop_many_emplace(Container& dst, size_t max) noexcept;
template <typename Container>
pop_many_emplace_result try_pop_many_emplace(Container& dst, size_t max) noexcept;
template <typename Container>
pop_many_emplace_result pop_many_emplace_for(Container& dst, size_t max, chrono::system_clock::duration duration) noexcept;
template <typename Container>
pop_many_emplace_result pop_many_emplace_until(Container& dst, size_t max, chrono::system_clock::time_point time_point) noexcept;
bool empty() const noexcept;
bool full() const noexcept;
size_t size() const noexcept;
size_t max_size() const noexcept;
size_t capacity() const noexcept;
private:
push_result _push(T&& t, bool block, chrono::system_clock::time_point* time_point) noexcept;
pop_result _pop(bool block, chrono::system_clock::time_point* time_point) noexcept;
template <typename Container>
pop_many_result<Container> _pop_many_emplace(Container& dst, size_t max, bool block, chrono::system_clock::time_point* time_point) noexcept;
mutable std::mutex m_mutex;
std::condition_variable m_cv;
deque<T> m_queue;
size_t m_max_size = 10;
bool m_exit = false;
bool m_finish = false;
};
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
template <class T>
blocking_queue<T>::blocking_queue(size_t max_size) noexcept
: m_max_size(max_size)
{
TL_ASSERT(m_max_size > 0);
}
template <class T>
blocking_queue<T>::~blocking_queue() noexcept
{
exit();
}
template <class T>
void blocking_queue<T>::exit() noexcept
{
{
std::unique_lock<std::mutex> lg(m_mutex);
m_exit = true;
}
m_cv.notify_all();
}
template <class T>
void blocking_queue<T>::finish() noexcept
{
{
std::unique_lock<std::mutex> lg(m_mutex);
m_finish = true;
}
m_cv.notify_all();
}
template <class T>
bool blocking_queue<T>::empty() const noexcept
{
std::unique_lock<std::mutex> lg(m_mutex);
return m_queue.empty();
}
template <class T>
bool blocking_queue<T>::full() const noexcept
{
std::unique_lock<std::mutex> lg(m_mutex);
return m_queue.size() == m_max_size;
}
template <class T>
size_t blocking_queue<T>::size() const noexcept
{
std::unique_lock<std::mutex> lg(m_mutex);
return m_queue.size();
}
template <class T>
size_t blocking_queue<T>::max_size() const noexcept
{
return capacity();
}
template <class T>
size_t blocking_queue<T>::capacity() const noexcept
{
return m_max_size;
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push(T const& t) noexcept
{
return _push(T(t), true, nullptr);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::try_push(T const& t) noexcept
{
return _push(T(t), false, nullptr);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push(T&& t) noexcept
{
return _push(std::move(t), true, nullptr);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::try_push(T&& t) noexcept
{
return _push(std::move(t), false, nullptr);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push_for(T const& t, chrono::system_clock::duration duration) noexcept
{
chrono::system_clock::time_point until = chrono::system_clock::now() + duration;
return _push(T(t), true, &until);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push_until(T const& t, chrono::system_clock::time_point time_point) noexcept
{
return _push(T(t), true, &time_point);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push_for(T&& t, chrono::system_clock::duration duration) noexcept
{
chrono::system_clock::time_point until = chrono::system_clock::now() + duration;
return _push(std::move(t), true, &until);
}
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::push_until(T&& t, chrono::system_clock::time_point time_point) noexcept
{
return _push(std::move(t), true, &time_point);
}
template <typename T>
typename blocking_queue<T>::pop_result blocking_queue<T>::pop() noexcept
{
return _pop(true, nullptr);
}
template <typename T>
typename blocking_queue<T>::pop_result blocking_queue<T>::try_pop() noexcept
{
return _pop(false, nullptr);
}
template <typename T>
typename blocking_queue<T>::pop_result blocking_queue<T>::pop_for(chrono::system_clock::duration duration) noexcept
{
chrono::system_clock::time_point until = chrono::system_clock::now() + duration;
return _pop(true, &until);
}
template <typename T>
typename blocking_queue<T>::pop_result blocking_queue<T>::pop_until(chrono::system_clock::time_point time_point) noexcept
{
return _pop(true, &time_point);
}
template <typename T>
template <typename Container>
result<Container, typename blocking_queue<T>::pop_error_code> blocking_queue<T>::pop_many(size_t max) noexcept
{
Container container;
return _pop_many_emplace(container, max, true, nullptr);
}
template <typename T>
template <typename Container>
result<Container, typename blocking_queue<T>::pop_error_code> blocking_queue<T>::try_pop_many(size_t max) noexcept
{
Container container;
return _pop_many_emplace(container, max, false, nullptr);
}
template <typename T>
template <typename Container>
result<Container, typename blocking_queue<T>::pop_error_code> blocking_queue<T>::pop_many_for(size_t max, chrono::system_clock::duration duration) noexcept
{
Container container;
chrono::system_clock::time_point until = chrono::system_clock::now() + duration;
return _pop_many_emplace(container, max, true, &until);
}
template <typename T>
template <typename Container>
result<Container, typename blocking_queue<T>::pop_error_code> blocking_queue<T>::pop_many_until(size_t max, chrono::system_clock::time_point time_point) noexcept
{
Container container;
return _pop_many_emplace(container, max, true, &time_point);
}
template <typename T>
template <typename Container>
typename blocking_queue<T>::pop_many_emplace_result blocking_queue<T>::pop_many_emplace(Container& dst, size_t max) noexcept
{
pop_many_result<Container> result = _pop_many_emplace(dst, max, true, nullptr);
return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code());
}
template <typename T>
template <typename Container>
typename blocking_queue<T>::pop_many_emplace_result blocking_queue<T>::try_pop_many_emplace(Container& dst, size_t max) noexcept
{
pop_many_result<Container> result = _pop_many_emplace(dst, max, false, nullptr);
return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code());
}
template <typename T>
template <typename Container>
typename blocking_queue<T>::pop_many_emplace_result blocking_queue<T>::pop_many_emplace_for(Container& dst, size_t max, chrono::system_clock::duration duration) noexcept
{
chrono::system_clock::time_point until = chrono::system_clock::now() + duration;
pop_many_result<Container> result = _pop_many_emplace(dst, max, true, &until);
return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code());
}
template <typename T>
template <typename Container>
typename blocking_queue<T>::pop_many_emplace_result blocking_queue<T>::pop_many_emplace_until(Container& dst, size_t max, chrono::system_clock::time_point time_point) noexcept
{
pop_many_result<Container> result = _pop_many_emplace(dst, max, true, &time_point);
return (result.has_value()) ? success() : pop_many_emplace_result(result.error().code());
}
//////////////////////////////////////////////////////////////////////////
template <class T>
typename blocking_queue<T>::push_result blocking_queue<T>::_push(T&& t, bool block, chrono::system_clock::time_point* time_point) noexcept
{
bool pushed_one = false;
bool timed_out = false;
const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast<chrono::nanoseconds>(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now();
//send the current datagram
{
std::unique_lock<std::mutex> lg(m_mutex);
if (block)
{
while ((m_queue.size() >= m_max_size) & (!m_exit) & (!timed_out))
{
if (m_finish)
return push_result(push_error_code::Finished);
if (time_point)
{
m_cv.wait_until(lg, system_tp);
if (chrono::system_clock::now() >= *time_point)
timed_out = true;
}
else
m_cv.wait(lg);
}
if (m_exit)
return push_result(push_error_code::Exited);
}
if (m_queue.size() < m_max_size)
{
pushed_one = true;
m_queue.push_back(std::move(t));
}
else if (m_finish)
return push_result(push_error_code::Finished);
}
if (pushed_one)
{
m_cv.notify_all();
return success();
}
return timed_out ? push_result(push_error_code::Timeout) : push_result(push_error_code::Full);
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
template <typename Container>
result<Container, typename blocking_queue<T>::pop_error_code> blocking_queue<T>::_pop_many_emplace(Container& dst, size_t max, bool block, chrono::system_clock::time_point* time_point) noexcept
{
const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast<chrono::nanoseconds>(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now();
bool got_some = false;
bool timed_out = false;
{
//wait for data
std::unique_lock<std::mutex> lg(m_mutex);
if (block)
{
while (m_queue.empty() & (!m_exit) & (!timed_out))
{
if (m_finish)
return pop_many_result<Container>(pop_error_code::Finished);
if (time_point)
{
m_cv.wait_until(lg, system_tp);
if (chrono::system_clock::now() >= *time_point)
timed_out = true;
}
else
m_cv.wait(lg);
}
if (m_exit)
return pop_many_result<Container>(pop_error_code::Exited);
}
if (m_queue.empty() && m_finish)
return pop_many_result<Container>(pop_error_code::Finished);
while (!m_queue.empty())
{
if (dst.size() >= max)
break;
got_some = true;
dst.push_back(std::move(m_queue.front()));
m_queue.pop_front();
}
}
if (got_some)
{
m_cv.notify_all();
return pop_many_result<Container>(std::move(dst));
}
return timed_out ? pop_many_result<Container>(pop_error_code::Timeout) : pop_many_result<Container>(pop_error_code::Empty);
}
//////////////////////////////////////////////////////////////////////////
template <typename T>
typename blocking_queue<T>::pop_result blocking_queue<T>::_pop(bool block, chrono::system_clock::time_point* time_point) noexcept
{
const auto system_tp = time_point ? std::chrono::system_clock::now() + std::chrono::nanoseconds(chrono::duration_cast<chrono::nanoseconds>(*time_point - chrono::system_clock::now()).count()) : std::chrono::system_clock::now();
optional<T> opt_dst;
bool timed_out = false;
{
//wait for data
std::unique_lock<std::mutex> lg(m_mutex);
if (block)
{
while (m_queue.empty() & (!m_exit) & (!timed_out))
{
if (m_finish)
return pop_result(pop_error_code::Finished);
if (time_point)
{
m_cv.wait_until(lg, system_tp);
if (chrono::system_clock::now() >= *time_point)
timed_out = true;
}
else
m_cv.wait(lg);
}
if (m_exit)
return pop_result(pop_error_code::Exited);
}
if (!m_queue.empty())
{
opt_dst = std::move(m_queue.front());
m_queue.pop_front();
}
else if (m_finish)
return pop_result(pop_error_code::Finished);
}
if (opt_dst.has_value())
{
m_cv.notify_all();
return std::move(opt_dst.value());
}
return timed_out ? pop_result(pop_error_code::Timeout) : pop_result(pop_error_code::Empty);
}
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/chrono.h>
namespace tl
{
using namespace eastl;
namespace chrono
{
using namespace eastl::chrono;
template <typename Clock>
class TL_API chrono final
{
public:
//! Reset the internal time stamp. If you specify an elapsed time offset it will affect directly to getElapsed.
void reset(typename Clock::duration elapsed_offset = Clock::duration::zero()) noexcept
{
m_start_time_point = Clock::now() + elapsed_offset;
}
//! Return the time passed between the last Reset call and now.
typename Clock::duration get_elapsed() const noexcept
{
return Clock::now() - m_start_time_point;
}
//! Return the time passed between the last Reset call and now and then Reset the Clock.
typename Clock::duration get_elapsed_and_reset() noexcept
{
auto now = Clock::now();
auto d = now - m_start_time_point;
m_start_time_point = now;
return d;
}
private:
typename Clock::time_point m_start_time_point = Clock::now();
};
using system_chrono = chrono<eastl::chrono::system_clock>;
using high_resolution_chrono = chrono<eastl::chrono::high_resolution_clock>;
}
}
+542
View File
@@ -0,0 +1,542 @@
#pragma once
#include <type_traits> // std::enable_if, std::is_constructible, etc
#include <mutex>
#include "tl/unordered_map.h"
#include "tl/memory_buffer.h"
#include "tl/detail/internal_assert.h"
namespace tl
{
template<typename ThreadPolicy>
class signal_system
{
public:
class scoped_connection;
class connection
{
public:
connection() noexcept = default;
connection(const connection& other) noexcept = default;
connection(connection&& other) noexcept = default;
connection& operator=(const connection& other) noexcept = default;
connection& operator=(connection&& other) noexcept = default;
void disconnect() noexcept;
void lock() noexcept;
void unlock() noexcept;
[[nodiscard]] bool is_connected() const noexcept;
protected:
friend class signal_system;
friend class scoped_connection;
inline explicit connection(uint32_t id) noexcept;
uint32_t id = 0;
};
class [[nodiscard]] scoped_connection : public connection
{
public:
scoped_connection() noexcept = default;
scoped_connection(connection c) noexcept
: connection(c) {}
~scoped_connection() noexcept;
scoped_connection(const scoped_connection& other) = delete;
scoped_connection(scoped_connection&& other) noexcept
{
this->id = other.id;
other.id = 0;
}
scoped_connection& operator=(const scoped_connection& other) = delete;
scoped_connection& operator=(scoped_connection&& other) noexcept
{
if (this->id != 0)
this->disconnect();
this->id = other.id;
other.id = 0;
return *this;
}
void detach() noexcept
{
this->id = 0;
}
};
//static inline void create(intptr_t owner, uint8_t signal_count, size_t delegate_count_hint = 0);
template<typename... Args>
static connection add(intptr_t owner, uint8_t signal_index, tl::function<void(Args...)>&& delegate) noexcept;
template<typename... Args>
static void invoke(intptr_t owner, uint8_t signal_index, Args&&... args) noexcept;
static void disconnect(const connection& c) noexcept;
static void lock(const connection& c) noexcept;
static void unlock(const connection& c) noexcept;
static void remove_all() noexcept;
static void remove_all(intptr_t owner) noexcept;
static void remove_all(intptr_t owner, uint8_t signal_index) noexcept;
static size_t get_slot_count(intptr_t owner, uint8_t signal_index) noexcept;
private:
//static inline uint32_t compute_owner_hash(void* owner);
static inline constexpr size_t k_offset_bits = 25;
static inline constexpr size_t k_offset_mask = (1 << k_offset_bits) - 1;
static inline constexpr size_t k_delegate_size_bits = 7;
static inline constexpr size_t k_delegate_size_mask = (1 << k_delegate_size_bits) - 1;
static inline constexpr size_t k_signal_index_bits = 5;
static inline constexpr size_t k_signal_index_disconnected = (1 << k_signal_index_bits) - 1; //this is used to mark disconnected slots
struct OwnerData
{
union
{
struct
{
uint32_t offset : k_offset_bits; //div 1 << k_offset_shift
uint32_t initialized : 1;
};
uint32_t all = 0;
};
};
static_assert(sizeof(OwnerData) == 4);
struct SlotHeader
{
union
{
struct
{
uint32_t delegate_size : k_delegate_size_bits; //div 1 << k_offset_shift
uint32_t prev_offset : k_offset_bits; //div 1 << k_offset_shift
uint32_t next_offset : k_offset_bits; //div 1 << k_offset_shift
uint32_t signal_index : k_signal_index_bits; //a certain value is used to signal disconnection (k_signal_index_disconnected)
uint32_t locked : 1; //if locked, don't emit
uint32_t __unused : 1;
};
uint64_t all = 0; //8 bytes, to make sure alignment is fine
};
void (*destructor)(void*) = nullptr;
};
static_assert(sizeof(SlotHeader) == 16);
struct ConnectionData
{
uint32_t offset : k_offset_bits; //div 1 << k_offset_shift
};
static typename ThreadPolicy::mutex_t m_mutex;
static tl::unordered_map<intptr_t, OwnerData> m_owners;
static tl::unordered_map<uint32_t, ConnectionData> m_connections;
static typename ThreadPolicy::connection_id_counter_t m_last_connection_id;
static tl::memory_buffer m_slots;
static typename ThreadPolicy::data_ptr_t m_slots_data_ptr;
static size_t m_garbage;
};
//////////////////////////////////////////////////////////////////////////
template<typename ThreadPolicy>
signal_system<ThreadPolicy>::connection::connection(uint32_t id) noexcept
: id(id)
{
}
//////////////////////////////////////////////////////////////////////////
namespace detail
{
inline static constexpr size_t k_offset_shift = 3;
}
template<typename ThreadPolicy>
template<typename... Args>
auto signal_system<ThreadPolicy>::add(intptr_t owner, uint8_t signal_index, tl::function<void(Args...)>&& delegate) noexcept -> connection
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
if constexpr ((sizeof(delegate) & ((1 << detail::k_offset_shift) - 1)) != 0)
TL_PLAIN_CRASH("delegate_size not property aligned");
if (signal_index >= k_signal_index_disconnected)
TL_PLAIN_CRASH("signal_index out of range");
using delegate_t = tl::function<void(Args...)>;
OwnerData& od = m_owners[owner];
const uint32_t delegate_size = static_cast<uint32_t>(sizeof(delegate));
if ((delegate_size >> detail::k_offset_shift) > k_delegate_size_mask)
TL_PLAIN_CRASH("delegate_size out of range");
TL_PLAIN_ASSERT((delegate_size & ((1 << detail::k_offset_shift) - 1)) == 0);
TL_PLAIN_ASSERT(delegate_size < 128 * (1 << detail::k_offset_shift));
const uint32_t offset = static_cast<uint32_t>(m_slots.size()) >> detail::k_offset_shift;
if (offset > k_offset_mask)
TL_PLAIN_CRASH("offset out of range");
//TODO: this is not safe!!!! while resizing, the atomic still points to the old deleted data before it's updated.
// Here I have to double buffer stuff, so that the atomic either points to old, but valid memory or to new memory. Never to old but deleted memory
// Effectively this is what should happen:
// auto new_mem = allocate(new_size);
// memcpy(new_mem, old_mem, old_size);
// m_slots_data_ptr = new_mem;
// delete old_mem;
m_slots.resize_uninitialized(m_slots.size() + (size_t)delegate_size + sizeof(SlotHeader));
m_slots_data_ptr = reinterpret_cast<uint64_t*>(m_slots.data());
uint64_t* data = m_slots_data_ptr;
auto& header = *reinterpret_cast<SlotHeader*>(data + offset);
header = {};
header.delegate_size = delegate_size >> detail::k_offset_shift;
header.signal_index = signal_index;
header.destructor = [](void* memory) { static_cast<delegate_t*>(memory)->~delegate_t(); };
if (od.initialized == 0) //store the index
{
od.initialized = 1;
od.offset = offset;
header.prev_offset = offset;
}
else //here, we already have one slot (could be marked as deleted though!)
{
SlotHeader& first_header = *reinterpret_cast<SlotHeader*>(data + od.offset);
const uint32_t last_offset = first_header.prev_offset;//first_header->prev_offset points to the last header
SlotHeader& last_header = *reinterpret_cast<SlotHeader*>(data + last_offset);
last_header.next_offset = offset; //link our header after the last one
header.prev_offset = last_offset;
first_header.prev_offset = offset; //this is used to point to the last header, always
}
//note - data is uint64_t, so any byte offset has to be divided by 8 (or shifted by k_offset_shift)
new(data + offset + (sizeof(SlotHeader) >> detail::k_offset_shift)) delegate_t(std::move(delegate));
const uint32_t connection_id = ++m_last_connection_id;
m_connections[connection_id] = { offset };
return connection(connection_id);
}
template<typename ThreadPolicy>
template<typename... Args>
void signal_system<ThreadPolicy>::invoke(intptr_t owner, uint8_t signal_index, Args&&... args) noexcept
{
//we're locked from here ---
std::unique_lock<typename ThreadPolicy::mutex_t> lg(m_mutex);
auto it = m_owners.find(owner);
if (it == m_owners.end())
return;
OwnerData od = it->second;
lg.unlock();
// --- to here
// UNLOCKED ACCESS FOLLOWING THIS
// The only things safe are local data (copies) and atomics
// Any member should be atomic and not cached across the invoke, as the user might connect/disconnect stuff and invalidate the slots
using delegate_t = tl::function<void(Args...)>;
size_t next_offset = od.offset;
const size_t last_offset = (reinterpret_cast<const SlotHeader*>(m_slots_data_ptr + od.offset))->prev_offset;
bool is_last;
do
{
const SlotHeader* header = reinterpret_cast<const SlotHeader*>(m_slots_data_ptr + next_offset);
next_offset = header->next_offset;
is_last = (next_offset == 0) | (next_offset > last_offset);
if ((header->signal_index == signal_index) & (header->locked == 0))
{
auto delegate = reinterpret_cast<delegate_t*>(const_cast<SlotHeader*>(header) + 1);
if (is_last) //last slot? we can move potentially
(*delegate)(std::forward<Args>(args)...);
else
(*delegate)(args...);
}
} while (!is_last);
}
template<typename ThreadPolicy>
typename ThreadPolicy::mutex_t signal_system<ThreadPolicy>::m_mutex;
template<typename ThreadPolicy>
tl::unordered_map<intptr_t, typename signal_system<ThreadPolicy>::OwnerData> signal_system<ThreadPolicy>::m_owners;
template<typename ThreadPolicy>
tl::unordered_map<uint32_t, typename signal_system<ThreadPolicy>::ConnectionData> signal_system<ThreadPolicy>::m_connections;
template<typename ThreadPolicy>
typename ThreadPolicy::connection_id_counter_t signal_system<ThreadPolicy>::m_last_connection_id = 0;
template<typename ThreadPolicy>
tl::memory_buffer signal_system<ThreadPolicy>::m_slots = tl::memory_buffer(1024);
template<typename ThreadPolicy>
typename ThreadPolicy::data_ptr_t signal_system<ThreadPolicy>::m_slots_data_ptr = nullptr;
template<typename ThreadPolicy>
size_t signal_system<ThreadPolicy>::m_garbage = 0;
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::connection::disconnect() noexcept
{
signal_system<ThreadPolicy>::disconnect(*this);
this->id = 0;
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::connection::lock() noexcept
{
signal_system<ThreadPolicy>::lock(*this);
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::connection::unlock() noexcept
{
signal_system<ThreadPolicy>::unlock(*this);
}
template<typename ThreadPolicy>
bool signal_system<ThreadPolicy>::connection::is_connected() const noexcept
{
return this->id != 0;
}
template<typename ThreadPolicy>
signal_system<ThreadPolicy>::scoped_connection::~scoped_connection() noexcept
{
if (this->id > 0)
this->disconnect();
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::disconnect(const connection& c) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
auto it = m_connections.find(c.id);
if (it == m_connections.end())
return;
ConnectionData cd = it->second;
m_connections.erase(it);
uint64_t* data = m_slots_data_ptr;
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + cd.offset);
if (header->signal_index == k_signal_index_disconnected)
return; //already removed
header->destructor(header + 1);
header->signal_index = k_signal_index_disconnected; //mark it so that traversal will not call it
if (header->prev_offset > cd.offset)//first slot
{
//we cannot remove the head slot!!! The OwnerData points to it
}
else if (header->next_offset == 0) //last slot?
{
//we cannot remove the tail slot!!! The OwnerData points to it (using the prev_offset)
}
else //middle slot
{
auto* prev_header = reinterpret_cast<SlotHeader*>(data + header->prev_offset);
auto* next_header = reinterpret_cast<SlotHeader*>(data + header->next_offset);
prev_header->next_offset = header->next_offset;
next_header->prev_offset = header->prev_offset;
}
m_garbage += sizeof(SlotHeader) + (static_cast<size_t>(header->delegate_size) << detail::k_offset_shift);
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::lock(const connection& c) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
auto it = m_connections.find(c.id);
if (it == m_connections.end())
return;
ConnectionData cd = it->second;
uint64_t* data = m_slots_data_ptr;
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + cd.offset);
TL_PLAIN_ASSERT(header->locked == 0);
header->locked = 1;
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::unlock(const connection& c) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
auto it = m_connections.find(c.id);
if (it == m_connections.end())
return;
ConnectionData cd = it->second;
uint64_t* data = m_slots_data_ptr;
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + cd.offset);
TL_PLAIN_ASSERT(header->locked != 0);
header->locked = 0;
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::remove_all() noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
uint64_t* data = m_slots_data_ptr;
for (auto& p : m_owners)
{
OwnerData& od = p.second;
if (od.initialized == 0)
continue;
size_t next_offset = od.offset;
do
{
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + next_offset);
next_offset = header->next_offset;
if (header->signal_index != k_signal_index_disconnected)
{
header->destructor(header + 1);
header->signal_index = k_signal_index_disconnected;
m_garbage += sizeof(SlotHeader) + (static_cast<size_t>(header->delegate_size) << detail::k_offset_shift);
}
} while (next_offset != 0);
od.all = {};
}
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::remove_all(intptr_t owner) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
auto it = m_owners.find(owner);
if (it == m_owners.end())
return;
OwnerData& od = it->second;
uint64_t* data = m_slots_data_ptr;
if (od.initialized != 0)
{
size_t next_offset = od.offset;
do
{
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + next_offset);
next_offset = header->next_offset;
if (header->signal_index != k_signal_index_disconnected)
{
header->destructor(header + 1);
header->signal_index = k_signal_index_disconnected;
m_garbage += sizeof(SlotHeader) + (static_cast<size_t>(header->delegate_size) << detail::k_offset_shift);
}
} while (next_offset != 0);
}
m_owners.erase(it);
}
template<typename ThreadPolicy>
void signal_system<ThreadPolicy>::remove_all(intptr_t owner, uint8_t signal_index) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
if (signal_index >= k_signal_index_disconnected)
{
TL_PLAIN_FAIL();
return;
}
auto it = m_owners.find(owner);
if (it == m_owners.end())
return;
OwnerData& od = it->second;
uint64_t* data = m_slots_data_ptr;
if (od.initialized == 0)
{
m_owners.erase(it);
return;
}
bool has_other_signals = false;
size_t next_offset = od.offset;
do
{
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + next_offset);
next_offset = header->next_offset;
if (header->signal_index == signal_index)
{
header->destructor(header + 1);
header->signal_index = k_signal_index_disconnected;
m_garbage += sizeof(SlotHeader) + (static_cast<size_t>(header->delegate_size) << detail::k_offset_shift);
}
else
has_other_signals = true;
} while (next_offset != 0);
if (!has_other_signals)
m_owners.erase(it);
}
template<typename ThreadPolicy>
size_t signal_system<ThreadPolicy>::get_slot_count(intptr_t owner, uint8_t signal_index) noexcept
{
std::lock_guard<typename ThreadPolicy::mutex_t> lg(m_mutex);
if (signal_index >= k_signal_index_disconnected)
{
TL_PLAIN_FAIL();
return 0;
}
auto it = m_owners.find(owner);
if (it == m_owners.end())
return 0;
OwnerData& od = it->second;
uint64_t* data = m_slots_data_ptr;
if (od.initialized == 0)
{
m_owners.erase(it);
return 0;
}
size_t count = 0;
size_t next_offset = od.offset;
do
{
SlotHeader* header = reinterpret_cast<SlotHeader*>(data + next_offset);
next_offset = header->next_offset;
if (header->signal_index == signal_index)
count++;
} while (next_offset != 0);
return count;
}
}
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <type_traits> // std::enable_if, std::is_constructible, etc
#include <functional> // std::invoke
#include "tl/functional.h"
#include "tl/vector_map.h"
#include "tl/unordered_map.h"
#include "tl/hash_and_combine.h"
#include "tl/memory_buffer.h"
#include "tl/detail/internal_assert.h"
#include "tl/atomic.h"
#include <mutex>
#include "compact_signal.h"
namespace tl
{
namespace detail
{
struct signal_mt_thread_policy
{
using mutex_t = std::mutex;
using connection_id_counter_t = tl::atomic<uint32_t>;
using data_ptr_t = tl::atomic<uint64_t*>;
};
}
using signal_system_mt = signal_system<detail::signal_mt_thread_policy>;
using connection_mt = signal_system_mt::scoped_connection;
template <typename Signature>
class signal_mt;
template<typename R, typename...Args>
class signal_mt<R(Args...)>
{
public:
signal_mt() noexcept = default;
signal_mt(const signal_mt&) noexcept = delete;
signal_mt(signal_mt&&) noexcept = delete;
~signal_mt() noexcept
{
disconnect_all_slots();
}
signal_mt& operator=(const signal_mt<R(Args...)>&) = delete;
signal_mt& operator=(signal_mt<R(Args...)>&&) = delete;
connection_mt connect(tl::function<void(Args...)> delegate) noexcept
{
return signal_system_mt::add((intptr_t)this, 0, std::move(delegate));
}
void invoke(Args... args) const noexcept
{
signal_system_mt::invoke((intptr_t)this, 0, std::forward<Args>(args)...);
}
void disconnect_all_slots() const noexcept
{
signal_system_mt::remove_all((intptr_t)this);
}
bool has_slots() const
{
return signal_system_mt::get_slot_count((intptr_t)this, 0) > 0;
}
};
}
+70
View File
@@ -0,0 +1,70 @@
#pragma once
#include <type_traits>
#include "tl/memory_buffer.h"
#include "compact_signal.h"
namespace tl
{
namespace detail
{
struct signal_st_null_mutex
{
inline void lock() noexcept {};
inline void unlock() noexcept {};
};
struct signal_st_thread_policy
{
using mutex_t = signal_st_null_mutex;
using connection_id_counter_t = uint32_t;
using data_ptr_t = uint64_t*;
};
}
using signal_system_st = signal_system<detail::signal_st_thread_policy>;
using connection_st = signal_system_st::scoped_connection;
template <typename Signature>
class signal_st;
template<typename R, typename...Args>
class signal_st<R(Args...)>
{
public:
signal_st() noexcept = default;
signal_st(const signal_st&) noexcept = delete;
signal_st(signal_st&&) noexcept = delete;
~signal_st() noexcept
{
disconnect_all_slots();
}
signal_st& operator=(const signal_st<R(Args...)>&) = delete;
signal_st& operator=(signal_st<R(Args...)>&&) = delete;
connection_st connect(tl::function<void(Args...)> delegate) noexcept
{
return signal_system_st::add((intptr_t)this, 0, std::move(delegate));
}
void invoke(Args... args) const noexcept
{
signal_system_st::invoke((intptr_t)this, 0, std::forward<Args>(args)...);
}
void disconnect_all_slots() const noexcept
{
signal_system_st::remove_all((intptr_t)this);
}
bool has_slots() const
{
return signal_system_st::get_slot_count((intptr_t)this, 0) > 0;
}
};
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/detail/internal_crash.h"
#include "tl/format.h"
#include "tl/string.h"
#define TL_CRASH(__fmt__, ...) \
do \
{ \
const eastl::string __msg__ = tl::format<eastl::string>(__fmt__, ##__VA_ARGS__); \
tl::crash::response res = tl::crash::detail::invoke_crash_handler(__FILE__, __LINE__, __msg__.c_str()); \
if (res == tl::crash::response::CRASH) \
*reinterpret_cast<volatile unsigned int*>(0) = 0xDEAD; \
} while(false)
+64
View File
@@ -0,0 +1,64 @@
#pragma once
#include "tl/platform.h"
#if defined(TL_FORCE_DEBUG) && defined(TL_RETAIL)
# error "TL_FORCE_DEBUG and TL_RETAIL are incompatible!"
#endif
#if defined(TL_DEBUG)
# error "Do not pass TL_DEBUG to the compiler. If you want to force debug, pass TL_FORCE_DEBUG"
#endif
#if !defined(NDEBUG) || defined(TL_FORCE_DEBUG)
# define TL_DEBUG
#endif
#if defined(TL_PLATFORM_ANDROID_FAMILY)
#if defined(TL_PLATFORM_ARCHITECTURE_AMD64) || defined(TL_PLATFORM_ARCHITECTURE_X86)
extern __attribute__((gnu_inline, always_inline))
__inline__ void trap_instruction(void) noexcept
{
__asm__ volatile("int $0x03");
}
#elif defined(TL_PLATFORM_ARCHITECTURE_ARM32)
extern __attribute__((gnu_inline, always_inline))
__inline__ void trap_instruction(void) noexcept
{
/* See 'arm-linux-tdep.c' in GDB source,
* 'eabi_linux_arm_le_breakpoint' */
__asm__ volatile(".inst 0xe7f001f0");
/* Has same known problem and workaround
* as Thumb mode */
}
#elif defined(TL_PLATFORM_ARCHITECTURE_ARM64)
extern __attribute__((gnu_inline, always_inline))
__inline__ void trap_instruction(void) noexcept
{
/* See 'aarch64-tdep.c' in GDB source,
* 'aarch64_default_breakpoint' */
__asm__ volatile(".inst 0xd4200000");
}
#endif
#endif
#ifdef TL_DEBUG
# if defined(TL_PLATFORM_WINDOWS_FAMILY)
# define TL_BREAK() __debugbreak() //better than assert(0) as it will break on this exact line instead of deep in the assert function.
# elif defined(TL_PLATFORM_OSX_FAMILY) || defined(TL_PLATFORM_LINUX_FAMILY)
# include <signal.h>
# define TL_BREAK() ::raise(SIGTRAP)
# elif defined(TL_PLATFORM_ANDROID_FAMILY)
# include <signal.h>
# define TL_BREAK() ::raise(SIGSTOP)
# endif
#else
# if defined(TL_PLATFORM_WINDOWS_FAMILY)
# define TL_BREAK() do {} while (false)
# else
# define TL_BREAK() do {} while (false)
# endif
#endif
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/deque.h>
namespace tl
{
using namespace eastl;
}
+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;
}
}
}
+134
View File
@@ -0,0 +1,134 @@
#pragma once
#include "detail/magic_enum.h"
namespace tl
{
// Returns type name of enum.
template <typename E>
[[nodiscard]] constexpr auto enum_type_name() noexcept
{
return magic_enum::enum_type_name<E>();
}
// Returns number of enum values.
template <typename E>
[[nodiscard]] constexpr auto enum_count() noexcept
{
return magic_enum::enum_count<E>();
}
// Returns enum value at specified index.
// No bounds checking is performed: the behavior is undefined if index >= number of enum values.
template <typename E>
[[nodiscard]] constexpr auto enum_value(std::size_t index) noexcept
{
return magic_enum::enum_value<E>(index);
}
// Returns std::array with enum values, sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_values() noexcept
{
return magic_enum::enum_values<E>();
}
// Returns name from static storage enum variable.
// This version is much lighter on the compile times and is not restricted to the enum_range limitation.
template <auto V>
[[nodiscard]] constexpr auto enum_name() noexcept
{
return magic_enum::enum_name<V>();
}
// Returns name from enum value.
// If enum value does not have name or value out of range, returns empty string.
template <typename E>
[[nodiscard]] constexpr auto enum_name(E value) noexcept
{
return magic_enum::enum_name(value);
}
// Returns std::array with names, sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_names() noexcept
{
return magic_enum::enum_names<E>();
}
// Returns std::array with pairs (value, name), sorted by enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_entries() noexcept
{
return magic_enum::enum_entries<E>();
}
// Obtains enum value from integer value.
// Returns optional with enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(std::underlying_type_t<E> value) noexcept
{
return magic_enum::enum_cast<E>(value);
}
// Obtains enum value from name.
// Returns optional with enum value.
template <typename E, typename BinaryPredicate>
[[nodiscard]] constexpr auto enum_cast(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>)
{
return magic_enum::enum_cast<E, BinaryPredicate>(value, p);
}
// Obtains enum value from name.
// Returns optional with enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_cast(std::string_view value) noexcept
{
return magic_enum::enum_cast<E>(value);
}
// Returns integer value from enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_integer(E value) noexcept
{
return magic_enum::enum_integer<E>(value);
}
// Obtains index in enum values from enum value.
// Returns optional with index.
template <typename E>
[[nodiscard]] constexpr auto enum_index(E value) noexcept
{
return magic_enum::enum_index<E>(value);
}
// Checks whether enum contains enumerator with such enum value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(E value) noexcept
{
return magic_enum::enum_contains<E>(value);
}
// Checks whether enum contains enumerator with such integer value.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::underlying_type_t<E> value) noexcept
{
return magic_enum::enum_contains<E>(value);
}
// Checks whether enum contains enumerator with such name.
template <typename E, typename BinaryPredicate>
[[nodiscard]] constexpr auto enum_contains(std::string_view value, BinaryPredicate p) noexcept(std::is_nothrow_invocable_r_v<bool, BinaryPredicate, char, char>)
{
return magic_enum::enum_names<E, BinaryPredicate>(value, p);
}
// Checks whether enum contains enumerator with such name.
template <typename E>
[[nodiscard]] constexpr auto enum_contains(std::string_view value) noexcept
{
return magic_enum::enum_contains<E>(value);
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/finally.h>
namespace tl
{
using namespace eastl;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/fixed_function.h>
namespace tl
{
using namespace eastl;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/fixed_string.h>
namespace tl
{
using namespace eastl;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/fixed_vector.h>
namespace tl
{
using namespace eastl;
}
+340
View File
@@ -0,0 +1,340 @@
#pragma once
#include "tl/detail/prologue.h"
#include <numeric>
#include <tl/string.h>
#include <cstdint>
#include <type_traits>
namespace tl
{
template <typename enumT>
class flag_set
{
public:
typedef enumT enum_type;
typedef std::underlying_type_t<enumT> store_type;
store_type combine();
store_type combine(enumT head);
template<typename... Ts>
store_type combine(enumT head, Ts... tail);
template<typename ... Types>
flag_set(Types... args);
// Value constructor.
//Only enabled if the storage_type and enum_type are different (i.e. one is an enum and the other is a primitive type)
explicit flag_set(store_type value);
template<class String>
String get_as() const;
flag_set& set();
flag_set& set(enum_type flag, bool val = true);
flag_set& reset();
flag_set& reset(enum_type flag);
flag_set& flip();
flag_set& flip(enum_type flag);
size_t count() const;
static size_t size_in_bits();
bool test(enum_type flag) const;
template <typename Enum, typename...Enums>
bool test_all(Enum first, Enums... remaining) const;
template <typename Enum, typename...Enums>
bool test_any(Enum first, Enums... remaining) const;
bool any() const;
bool none() const;
store_type value() const;
flag_set<enum_type>(const flag_set<enum_type>& other);
flag_set<enum_type>& operator=(const flag_set<enum_type>& other);
bool operator==(const flag_set<enum_type>& other) const;
bool operator!=(const flag_set<enum_type>& other) const;
flag_set<enum_type>& operator|=(const flag_set<enum_type>& other);
flag_set<enum_type>& operator&=(const flag_set<enum_type>& other);
flag_set<enum_type>& operator^=(const flag_set<enum_type>& other);
flag_set<enum_type>& operator|=(const enum_type& other);
flag_set<enum_type>& operator&=(const enum_type& other);
flag_set<enum_type>& operator^=(const enum_type& other);
flag_set<enum_type> operator~() const;
private:
store_type flags_;
};
template<typename enumT>
flag_set<enumT> operator&(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs);
template<typename enumT>
flag_set<enumT> operator|(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs);
template<typename enumT>
flag_set<enumT> operator^(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs);
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
template <typename enumT>
typename flag_set<enumT>::store_type flag_set<enumT>::combine()
{
return flag_set<enumT>::store_type(0);
}
template <typename enumT>
auto flag_set<enumT>::combine(enumT head) -> store_type
{
return flag_set<enumT>::store_type(head);
}
template <typename enumT>
template <typename... Ts>
auto flag_set<enumT>::combine(enumT head, Ts... tail) -> store_type
{
return static_cast<store_type>(head) | combine(std::forward<Ts>(tail)...);
}
template <typename enumT>
template <typename... Types>
flag_set<enumT>::flag_set(Types... args)
: flags_(combine(std::forward<Types>(args)...))
{
}
template <typename enumT>
flag_set<enumT>::flag_set(store_type value)
: flags_(value)
{
}
template <typename enumT>
template <typename String>
String flag_set<enumT>::get_as() const
{
eastl::string str(size_in_bits(), '0');
for (size_t x = 0; x < size_in_bits(); ++x)
str[size_in_bits() - x - 1] = (flags_ & (1 << x) ? '1' : '0');
return String(str);
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::set()
{
flags_ = ~store_type(0);
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::set(enum_type flag, bool val)
{
flags_ = (val ? (flags_ | static_cast<store_type>(flag)) : (flags_ & ~static_cast<store_type>(flag)));
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::reset()
{
flags_ = store_type(0);
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::reset(enumT flag)
{
flags_ &= ~static_cast<store_type>(flag);
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::flip()
{
flags_ = ~flags_;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::flip(enum_type flag)
{
flags_ ^= static_cast<store_type>(flag);
return *this;
}
template <typename enumT>
size_t flag_set<enumT>::count() const
{
// http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
store_type bits = flags_;
size_t total = 0;
for (; bits != 0; ++total)
{
bits &= bits - 1; // clear the least significant bit set
}
return total;
}
template <typename enumT>
size_t flag_set<enumT>::size_in_bits()
{
return sizeof(store_type) * 8;
}
template <typename enumT>
bool flag_set<enumT>::test(enum_type flag) const
{
return (flags_ & static_cast<store_type>(flag)) == static_cast<store_type>(flag);
}
template <typename enumT>
template <typename Enum, typename...Enums>
bool flag_set<enumT>::test_all(Enum first, Enums... remaining) const
{
store_type expected = flag_set(first, remaining...).value();
return (flags_ & expected) == expected;
}
template <typename enumT>
template <typename Enum, typename...Enums>
bool flag_set<enumT>::test_any(Enum first, Enums... remaining) const
{
return (flags_ & flag_set(first, remaining...).value()) != 0;
}
template <typename enumT>
bool flag_set<enumT>::any() const
{
return flags_ > 0;
}
template <typename enumT>
bool flag_set<enumT>::none() const
{
return flags_ == 0;
}
template <typename enumT>
typename flag_set<enumT>::store_type flag_set<enumT>::value() const
{
return flags_;
}
template <typename enumT>
flag_set<enumT>::flag_set(const flag_set<enumT>& other)
: flags_(other.flags_)
{ }
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator=(const flag_set<enumT>& other)
{
flags_ = other.flags_;
return *this;
}
template <typename enumT>
bool flag_set<enumT>::operator==(const flag_set<enumT>& other) const
{
return flags_ == other.flags_;
}
template <typename enumT>
bool flag_set<enumT>::operator!=(const flag_set<enumT>& other) const
{
return !operator==(other);
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator|=(const flag_set<enumT>& other)
{
flags_ |= other.flags_;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator&=(const flag_set<enumT>& other)
{
flags_ &= other.flags_;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator^=(const flag_set<enumT>& other)
{
flags_ ^= other.flags_;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator|=(const enumT& other)
{
flags_ |= other;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator&=(const enumT& other)
{
flags_ &= other;
return *this;
}
template <typename enumT>
flag_set<enumT>& flag_set<enumT>::operator^=(const enumT& other)
{
flags_ ^= other;
return *this;
}
template <typename enumT>
flag_set<enumT> flag_set<enumT>::operator~() const
{
flag_set<enumT> ret;
ret.flags_ = ~flags_;
return ret;
}
template<typename enumT>
flag_set<enumT> operator&(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs)
{
//MBR: on VisualStudio 2015 uint8 & uint8 -> uint32, thus difering from enum store type
return flag_set<enumT>(static_cast<typename flag_set<enumT>::store_type>(lhs.value() & rhs.value()));
}
template<typename enumT>
flag_set<enumT> operator|(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs)
{
return flag_set<enumT>(lhs.value() | rhs.value());
}
template<typename enumT>
flag_set<enumT> operator^(const flag_set<enumT>& lhs, const flag_set<enumT>& rhs)
{
return flag_set<enumT>(lhs.value() ^ rhs.value());
}
}
+356
View File
@@ -0,0 +1,356 @@
#pragma once
#include "tl/detail/prologue.h"
#include <numeric>
#include <EASTL/string.h>
#include <cstdint>
#include <compare>
#include <type_traits>
namespace tl
{
namespace detail
{
template<typename T, bool v>
struct enum_storage;
template<typename T>
struct enum_storage<T, true>
{
using type = std::underlying_type_t<T>;
};
template<typename T>
struct enum_storage<T, false>
{
using type = T;
};
}
template <typename enumT>
class flag_set2
{
public:
using enum_type = enumT;
using store_type = typename detail::enum_storage<enumT, std::is_enum_v<enumT>>::type;
store_type combine() noexcept;
store_type combine(enumT head) noexcept;
template<typename... Ts>
store_type combine(enumT head, Ts... tail) noexcept;
static flag_set2<enumT> from_raw(store_type) noexcept;
template<typename ... Types>
flag_set2(Types... args) noexcept;
template<class String>
String get_as() const noexcept;
flag_set2& set() noexcept;
flag_set2& set(enum_type flag, bool val = true) noexcept;
flag_set2& reset() noexcept;
flag_set2& reset(enum_type flag) noexcept;
flag_set2& flip() noexcept;
flag_set2& flip(enum_type flag) noexcept;
size_t count() const noexcept;
static size_t size_in_bits() noexcept;
bool test(enum_type flag) const noexcept;
template <typename Enum, typename...Enums>
bool test_all(Enum first, Enums... remaining) const noexcept;
template <typename Enum, typename...Enums>
bool test_any(Enum first, Enums... remaining) const noexcept;
bool any() const noexcept;
bool none() const noexcept;
store_type value() const noexcept;
flag_set2<enum_type>(const flag_set2<enum_type>& other) noexcept;
flag_set2<enum_type>& operator=(const flag_set2<enum_type>& other) noexcept;
bool operator==(const flag_set2<enum_type>& other) const noexcept;
bool operator!=(const flag_set2<enum_type>& other) const noexcept;
auto operator<=>(const flag_set2<enum_type>& other) const noexcept = default;
flag_set2<enum_type>& operator|=(const flag_set2<enum_type>& other) noexcept;
flag_set2<enum_type>& operator&=(const flag_set2<enum_type>& other) noexcept;
flag_set2<enum_type>& operator^=(const flag_set2<enum_type>& other) noexcept;
flag_set2<enum_type>& operator|=(const enum_type& other) noexcept;
flag_set2<enum_type>& operator&=(const enum_type& other) noexcept;
flag_set2<enum_type>& operator^=(const enum_type& other) noexcept;
flag_set2<enum_type> operator~() const noexcept;
private:
store_type m_flags;
};
template<typename enumT>
flag_set2<enumT> operator&(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept;
template<typename enumT>
flag_set2<enumT> operator|(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept;
template<typename enumT>
flag_set2<enumT> operator^(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
template <typename enumT>
flag_set2<enumT> flag_set2<enumT>::from_raw(store_type raw) noexcept
{
flag_set2<enumT> flags;
flags.m_flags = raw;
return flags;
}
template <typename enumT>
typename flag_set2<enumT>::store_type flag_set2<enumT>::combine() noexcept
{
return flag_set2<enumT>::store_type(0);
}
template <typename enumT>
auto flag_set2<enumT>::combine(enumT head) noexcept -> store_type
{
return (1 << flag_set2<enumT>::store_type(head));
}
template <typename enumT>
template <typename... Ts>
auto flag_set2<enumT>::combine(enumT head, Ts... tail) noexcept -> store_type
{
return (1 << static_cast<store_type>(head)) | combine(std::forward<Ts>(tail)...);
}
template <typename enumT>
template <typename... Types>
flag_set2<enumT>::flag_set2(Types... args) noexcept
: m_flags(combine(std::forward<Types>(args)...))
{
}
template <typename enumT>
template <typename String>
String flag_set2<enumT>::get_as() const noexcept
{
eastl::string str(size_in_bits(), '0');
for (size_t x = 0; x < size_in_bits(); ++x)
str[size_in_bits() - x - 1] = (m_flags & (1 << x) ? '1' : '0');
return String(str);
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::set() noexcept
{
m_flags = ~store_type(0);
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::set(enum_type flag, bool val) noexcept
{
m_flags = (val ? (m_flags | (1 << static_cast<store_type>(flag))) : (m_flags & ~(1 << static_cast<store_type>(flag))));
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::reset() noexcept
{
m_flags = store_type(0);
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::reset(enumT flag) noexcept
{
m_flags &= ~(1 << static_cast<store_type>(flag));
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::flip() noexcept
{
m_flags = ~m_flags;
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::flip(enum_type flag) noexcept
{
m_flags ^= (1 << static_cast<store_type>(flag));
return *this;
}
template <typename enumT>
size_t flag_set2<enumT>::count() const noexcept
{
// http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan
store_type bits = m_flags;
size_t total = 0;
for (; bits != 0; ++total)
{
bits &= bits - 1; // clear the least significant bit set
}
return total;
}
template <typename enumT>
size_t flag_set2<enumT>::size_in_bits() noexcept
{
return sizeof(store_type) * 8;
}
template <typename enumT>
bool flag_set2<enumT>::test(enum_type flag) const noexcept
{
return (m_flags & (1u << static_cast<store_type>(flag))) == (1u << static_cast<store_type>(flag));
}
template <typename enumT>
template <typename Enum, typename...Enums>
bool flag_set2<enumT>::test_all(Enum first, Enums... remaining) const noexcept
{
store_type expected = flag_set2(first, remaining...).value();
return (m_flags & expected) == expected;
}
template <typename enumT>
template <typename Enum, typename...Enums>
bool flag_set2<enumT>::test_any(Enum first, Enums... remaining) const noexcept
{
return (m_flags & flag_set2(first, remaining...).value()) != 0;
}
template <typename enumT>
bool flag_set2<enumT>::any() const noexcept
{
return m_flags > 0;
}
template <typename enumT>
bool flag_set2<enumT>::none() const noexcept
{
return m_flags == 0;
}
template <typename enumT>
typename flag_set2<enumT>::store_type flag_set2<enumT>::value() const noexcept
{
return m_flags;
}
template <typename enumT>
flag_set2<enumT>::flag_set2(const flag_set2<enumT>& other) noexcept
: m_flags(other.m_flags)
{ }
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator=(const flag_set2<enumT>& other) noexcept
{
m_flags = other.m_flags;
return *this;
}
template <typename enumT>
bool flag_set2<enumT>::operator==(const flag_set2<enumT>& other) const noexcept
{
return m_flags == other.m_flags;
}
template <typename enumT>
bool flag_set2<enumT>::operator!=(const flag_set2<enumT>& other) const noexcept
{
return !operator==(other);
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator|=(const flag_set2<enumT>& other) noexcept
{
m_flags |= other.m_flags;
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator&=(const flag_set2<enumT>& other) noexcept
{
m_flags &= other.m_flags;
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator^=(const flag_set2<enumT>& other) noexcept
{
m_flags ^= other.m_flags;
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator|=(const enumT& other) noexcept
{
m_flags |= (1 << static_cast<store_type>(other));
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator&=(const enumT& other) noexcept
{
m_flags &= (1 << static_cast<store_type>(other));
return *this;
}
template <typename enumT>
flag_set2<enumT>& flag_set2<enumT>::operator^=(const enumT& other) noexcept
{
m_flags ^= (1 << static_cast<store_type>(other));
return *this;
}
template <typename enumT>
flag_set2<enumT> flag_set2<enumT>::operator~() const noexcept
{
flag_set2<enumT> ret;
ret.m_flags = ~m_flags;
return ret;
}
template<typename enumT>
flag_set2<enumT> operator&(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept
{
return flag_set2<enumT>::from_raw((typename flag_set2<enumT>::store_type)(lhs.value() & rhs.value()));
}
template<typename enumT>
flag_set2<enumT> operator|(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept
{
return flag_set2<enumT>::from_raw((typename flag_set2<enumT>::store_type)(lhs.value() | rhs.value()));
}
template<typename enumT>
flag_set2<enumT> operator^(const flag_set2<enumT>& lhs, const flag_set2<enumT>& rhs) noexcept
{
return flag_set2<enumT>::from_raw((typename flag_set2<enumT>::store_type)(lhs.value() ^ rhs.value()));
}
}
+61
View File
@@ -0,0 +1,61 @@
#pragma once
#include <format>
#include "tl/detail/prologue.h"
#include <tl/fixed_vector.h>
#include "tl/string.h"
#include "string.h"
namespace tl
{
template <typename DST>
constexpr DST format() noexcept
{
return DST();
}
template <typename DST, typename... Args>
constexpr DST format(std::format_string<Args...> format_str, Args&&... args) noexcept
{
DST buffer;
std::format_to(std::back_inserter(buffer), format_str, std::forward<Args>(args)...);
return buffer;
}
template <typename DST, typename... Args>
constexpr DST& format_append(DST& dst, std::format_string<Args...> format_str, Args&&... args) noexcept
{
std::format_to(std::back_inserter(dst), format_str, std::forward<Args>(args)...);
return dst;
}
//template <typename DST, typename S>
//constexpr DST format(const S& format_str) noexcept
//{
// return DST(format_str);
//}
template <typename... Args>
tl::string format(std::format_string<Args...> format_str, Args&&... args) noexcept
{
tl::fixed_vector<char, 1024> buffer;
std::format_to(std::back_inserter(buffer), format_str, std::forward<Args>(args)...);
return tl::string(buffer.data(), buffer.size());
}
template <typename... Args>
tl::string& format_append(tl::string& dst, std::format_string<Args...> format_str, Args&&... args) noexcept
{
tl::fixed_vector<char, 1024> buffer(dst.size());
if (!dst.empty())
memcpy(buffer.data(), dst.data(), dst.size());
std::format_to(std::back_inserter(buffer), format_str, std::forward<Args>(args)...);
dst = tl::string(buffer.data(), buffer.size());
return dst;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/functional.h>
namespace tl
{
using namespace eastl;
}
+60
View File
@@ -0,0 +1,60 @@
#pragma once
#include "tl/detail/prologue.h"
#include "EASTL/functional.h"
namespace tl
{
using namespace eastl;
template <class T>
constexpr inline void hash_and_combine(uint32_t& seed, const T& v) noexcept
{
//the result of the hasher is size_t so - if used in arithmetic - it might produce different results in 32 and 64 bit platforms
//therefore we cast it here to 32 bit.
tl::hash<T> hasher;
seed ^= static_cast<uint32_t>(hasher(v)) + static_cast<uint32_t>(0x9e3779b9) + (seed << 6) + (seed >> 2);
}
template <class T>
constexpr inline void hash_and_combine(uint64_t& seed, const T& v) noexcept
{
//the result of the hasher is size_t so - if used in arithmetic - it might produce different results in 32 and 64 bit platforms
//therefore we cast it here to 32 bit.
tl::hash<T> hasher;
seed ^= static_cast<uint32_t>(hasher(v)) + 0xA6E9377DAF75BDFEULL + (seed << 6) + (seed >> 2);
}
#if defined(TL_PLATFORM_OSX_FAMILY)
template <class T>
constexpr inline void hash_and_combine(size_t& seed, const T& v) noexcept
{
tl::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
#endif
constexpr inline void hash_combine(uint32_t& seed, const uint32_t v) noexcept
{
//the result of the hasher is size_t so - if used in arithmetic - it might produce different results in 32 and 64 bit platforms
//therefore we cast it here to 32 bit.
seed ^= v + static_cast<uint32_t>(0x9e3779b9u) + (seed << 6) + (seed >> 2);
}
constexpr inline void hash_combine(uint64_t& seed, const uint64_t v) noexcept
{
//the result of the hasher is size_t so - if used in arithmetic - it might produce different results in 32 and 64 bit platforms
//therefore we cast it here to 32 bit.
seed ^= static_cast<uint32_t>(v) + 0xA6E9377DAF75BDFEULL + (seed << 6) + (seed >> 2);
}
#if defined(TL_PLATFORM_OSX_FAMILY)
constexpr inline void hash_combine(size_t& seed, const size_t v) noexcept
{
seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
#endif
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/hash_map.h>
namespace tl
{
using namespace eastl;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+54
View File
@@ -0,0 +1,54 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/hash_map.h"
#include <compare>
namespace tl
{
template<typename T, T invalid_value, typename Tag>
struct identifier
{
using underlying_type = T;
constexpr identifier() noexcept = default;
explicit constexpr identifier(T value) noexcept : m_value(std::move(value)) {}
constexpr T value() const noexcept { return m_value; }
constexpr bool is_valid() const noexcept { return m_value != invalid_value; }
//inline static identifier<T, invalid_value, Tag> invalid() noexcept { return identifier<T, invalid_value, Tag>(); }
auto operator<=>(const identifier<T, invalid_value, Tag>& other) const noexcept { return m_value <=> other.value(); }
auto operator<=>(const T& other) const noexcept { return m_value <=> other; }
bool operator==(const identifier<T, invalid_value, Tag>& other) const noexcept { return m_value == other.value(); }
bool operator==(const T& other) const noexcept { return m_value == other; }
private:
T m_value = invalid_value;
};
}
#define TL_DECLARE_INTEGRAL_ID(NAME, TYPE, INVALID_VALUE) \
struct NAME##_tag {};\
using NAME = tl::identifier<TYPE, INVALID_VALUE, NAME##_tag>;
template <typename T, T invalid_value, typename Tag>
struct std::formatter<tl::identifier<T, invalid_value, Tag>>
{
constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); }
auto format(const tl::identifier<T, invalid_value, Tag>& id, std::format_context& ctx) const
{
return format_to(ctx.out(), "{}", id.value());
}
};
template<typename T, T invalid_value, typename Tag>
struct eastl::hash<tl::identifier<T, invalid_value, Tag>>
{
size_t operator()(tl::identifier<T, invalid_value, Tag> const& id) const
{
return hash<T>()(id.value());
}
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/list.h>
namespace tl
{
using namespace eastl;
}
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/map.h>
namespace tl
{
using namespace eastl;
}
+53
View File
@@ -0,0 +1,53 @@
#pragma once
#include "tl/detail/prologue.h"
#include "detail/internal_assert.h"
#include <type_traits>
#include <cassert>
#include <cstddef>
#include <cstdlib>
#if defined(TL_PLATFORM_WINDOWS_FAMILY)
#include <malloc.h>
#endif
namespace tl
{
inline bool is_aligned(void* ptr, size_t alignment)
{
size_t space = 1;
return tl::align(alignment, space, ptr, space);
}
inline void aligned_free16(void* p) noexcept
{
#if defined(TL_PLATFORM_WINDOWS_FAMILY)
::_aligned_free(p);
#else
::free(p);
#endif
}
inline void* aligned_alloc16(size_t size) noexcept
{
#if defined(TL_PLATFORM_WINDOWS_FAMILY)
return ::_aligned_malloc(size, 16);
#else
return malloc(size);
#endif
}
inline void* aligned_realloc16(void* p, size_t new_size) noexcept
{
#if defined(TL_PLATFORM_WINDOWS_FAMILY)
return ::_aligned_realloc(p, new_size, 16);
#else
return realloc(p, new_size);
#endif
}
}
+223
View File
@@ -0,0 +1,223 @@
#pragma once
#include "tl/detail/prologue.h"
#include <cstdint>
#include <algorithm>
#include "tl/api.h"
#include "tl/hash_map.h"
#include "tl/plain_assert.h"
#include "tl/murmur_hash.h"
#include <unordered_map>
namespace tl
{
class TL_API memory_buffer
{
public:
using iterator = uint8_t *;
using const_iterator = const uint8_t *;
using value_type = uint8_t;
memory_buffer();
~memory_buffer();
explicit memory_buffer(size_t size, uint8_t value = 0);
memory_buffer(const uint8_t* start, const uint8_t* end);
memory_buffer(const uint8_t* start, size_t size);
memory_buffer(const memory_buffer& other);
memory_buffer& operator=(const memory_buffer& other);
memory_buffer(memory_buffer&& other) noexcept;
memory_buffer& operator=(memory_buffer&& other) noexcept;
bool operator==(const memory_buffer& other) const;
bool operator!=(const memory_buffer& other) const;
inline void push_back(uint8_t c);
void append(const memory_buffer& other);
void append(const uint8_t* start, const uint8_t* end);
void append(const uint8_t* start, size_t size);
void assign(const uint8_t* start, const uint8_t* end);
void erase(iterator begin, iterator end);
void resize(size_t size, uint8_t value = 0);
void resize_uninitialized(size_t size);
void reserve(size_t capacity);
void shrink_to_fit();
inline void swap(memory_buffer& other) noexcept;
void clear();
inline const uint8_t* data() const;
inline uint8_t* data();
inline const uint8_t& operator[](size_t i) const;
inline uint8_t& operator[](size_t i);
inline uint8_t front() const;
inline uint8_t back() const;
inline uint8_t& front();
inline uint8_t& back();
inline bool empty() const;
inline size_t size() const;
inline size_t capacity() const;
inline iterator begin();
inline iterator end();
inline const_iterator begin() const;
inline const_iterator end() const;
private:
//inline uint32_t& _capacity_unsafe();
//inline const uint32_t& _capacity_unsafe() const;
static size_t get_grow_capacity(size_t needed, size_t capacity);
//We're storing the sizes in uint32_t and in the allocated buffer itself to save some memory.
//This means the memory_buffer is limited to 4GB!
//Trying to go over 4GB will result in a crash
struct Header// this is only for sizeof purposes
{
uint32_t m_capacity;
uint32_t m_size;
};
uint8_t* m_buf;
};
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#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))
namespace tl
{
inline void memory_buffer::swap(memory_buffer& other) noexcept
{
std::swap(m_buf, other.m_buf);
}
inline void memory_buffer::push_back(uint8_t c)
{
const size_t size = _size();
const size_t capacity = _capacity();
if (size >= capacity)
reserve(get_grow_capacity(size + 1u, capacity));
m_buf[sizeof(Header) + size] = c;
_size() = uint32_t(size + 1);
}
inline const uint8_t* memory_buffer::data() const
{
return size() > 0 ? _data() : nullptr;
}
inline uint8_t* memory_buffer::data()
{
return size() > 0 ? _data() : nullptr;
}
inline size_t memory_buffer::size() const
{
return _size();
}
inline size_t memory_buffer::capacity() const
{
return _capacity();
}
inline const uint8_t& memory_buffer::operator[](size_t i) const
{
TL_PLAIN_ASSERT(i < size());
return _data()[i];
}
inline uint8_t& memory_buffer::operator[](size_t i)
{
TL_PLAIN_ASSERT(i < size());
return _data()[i];
}
inline uint8_t memory_buffer::front() const
{
TL_PLAIN_ASSERT(size() > 0);
return *_data();
}
inline uint8_t memory_buffer::back() const
{
TL_PLAIN_ASSERT(size() > 0);
return _data()[_size() - 1];
}
inline uint8_t& memory_buffer::front()
{
TL_PLAIN_ASSERT(size() > 0);
return *_data();
}
inline uint8_t& memory_buffer::back()
{
TL_PLAIN_ASSERT(size() > 0);
return _data()[_size() - 1];
}
inline bool memory_buffer::empty() const
{
return size() == 0;
}
inline memory_buffer::iterator memory_buffer::begin()
{
return _data();
}
inline memory_buffer::iterator memory_buffer::end()
{
return _data() + _size();
}
inline memory_buffer::const_iterator memory_buffer::begin() const
{
return _data();
}
inline memory_buffer::const_iterator memory_buffer::end() const
{
return _data() + _size();
}
}
inline void swap(tl::memory_buffer& a, tl::memory_buffer& b) noexcept
{
a.swap(b);
}
template <> struct std::hash<tl::memory_buffer>
{
size_t operator()(tl::memory_buffer const& s) const noexcept
{
if (s.empty())
return 0;
return tl::murmur32(s.data(), s.size(), 0u);
}
};
template <> struct eastl::hash<tl::memory_buffer>
{
size_t operator()(tl::memory_buffer const& s) const
{
if (s.empty())
return 0;
return tl::murmur32(s.data(), s.size(), 0u);
}
};
#undef _size
#undef _capacity
#undef _data
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "tl/detail/prologue.h"
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "tl/detail/prologue.h"
#include <cstdint>
#include <cstddef>
namespace tl
{
//case insensitive murmur hash. Used for case insensitive strings
//uint32_t murmur32_ci(const void* buffer, size_t size, uint32_t seed);
//uint32_t murmur32_ci(const char* c_str, uint32_t seed);
TL_API uint32_t murmur32(const void* buffer, size_t size, uint32_t seed);
TL_API uint64_t murmur64(const void* buffer, size_t size, uint32_t seed);
TL_API uint64_t murmur64_seed64(const void* buffer, size_t size, uint64_t seed);
TL_API uint32_t murmur32(const char* c_str, uint32_t seed);
TL_API uint64_t murmur64(const char* c_str, uint32_t seed);
TL_API uint32_t murmur_unaligned32(const char* c_str, uint32_t seed);
TL_API uint64_t murmur_unaligned64(const char* c_str, uint32_t seed);
TL_API uint32_t murmur_unaligned32(const void* i_buffer, size_t i_size, uint32_t i_seed);
TL_API uint64_t murmur_unaligned64(const void* i_buffer, size_t i_size, uint64_t i_seed);
//templated interface for metaprogramming
//buffer + size hashing
template<typename T> T murmur(const void* buffer, size_t size, uint32_t seed); //intentionally left without implementation to fail compilation for unsupported types
template<> inline uint8_t murmur(const void* buffer, size_t size, uint32_t seed) { return murmur32(buffer, size, seed) & 0xff; }
template<> inline uint16_t murmur(const void* buffer, size_t size, uint32_t seed) { return murmur32(buffer, size, seed) & 0xffff; }
template<> inline uint32_t murmur(const void* buffer, size_t size, uint32_t seed) { return murmur32(buffer, size, seed); }
template<> inline uint64_t murmur(const void* buffer, size_t size, uint32_t seed) { return murmur64(buffer, size, seed); }
//c string hashing
template<typename T> T murmur(const char* c_str, uint32_t seed); //intentionally left without implementation to fail compilation for unsupported types
template<> inline uint8_t murmur(const char* c_str, uint32_t seed) { return murmur32(c_str, seed) & 0xF; }
template<> inline uint16_t murmur(const char* c_str, uint32_t seed) { return murmur32(c_str, seed) & 0xFF; }
template<> inline uint32_t murmur(const char* c_str, uint32_t seed) { return murmur32(c_str, seed); }
template<> inline uint64_t murmur(const char* c_str, uint32_t seed) { return murmur64(c_str, seed); }
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/plain_crash.h"
#include "tl/type_traits.h"
namespace tl
{
namespace detail
{
template <class T, class U>
struct is_same_signedness : integral_constant<bool, is_signed<T>::value == is_signed<U>::value>
{
};
}
template <class T, class U>
T narrow(U u) noexcept
{
T t = static_cast<T>(u);
if (static_cast<U>(t) != u)
TL_PLAIN_CRASH("Narrowing error");
if (!detail::is_same_signedness<T, U>::value && ((t < T{}) != (u < U{})))
TL_PLAIN_CRASH("Narrowing error");
return t;
}
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/numeric.h>
namespace tl
{
using namespace eastl;
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/numeric_limits.h>
namespace tl
{
using namespace eastl;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "tl/detail/prologue.h"
#include <EASTL/optional.h>
#include <format>
namespace tl
{
using namespace eastl;
}
template<typename T>
struct std::formatter<tl::optional<T>>
{
constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); }
auto format(const tl::optional<T>& s, std::format_context& ctx) const
{
if (s.has_value())
return format_to(ctx.out(), "{}", s.value());
return format_to(ctx.out(), "<null>");
}
};
+278
View File
@@ -0,0 +1,278 @@
#pragma once
#include "tl/detail/prologue.h"
#include <tl/vector.h>
#include <tl/fixed_vector.h>
#include "tl/string.h"
#include "tl/detail/path_system_utils.h"
#include "tl/toolchain.h"
//////////////////////////////////////////////////////////////////////////
#define TL_DECLARE_STRING_LITERAL(struct_name, cstr_value)\
struct struct_name\
{\
static const char* value() noexcept\
{\
return cstr_value;\
}\
};
#define TL_DECLARE_STRING_VECTOR(struct_name, ...)\
struct struct_name\
{\
static const tl::vector<const char*>& vector() noexcept\
{\
static tl::vector<const char*> s_vector {__VA_ARGS__};\
return s_vector;\
}\
};
#define TL_DECLARE_TLSTRING_VECTOR(struct_name, ...)\
struct struct_name\
{\
static const tl::vector<tl::string>& vector() noexcept\
{\
static tl::vector<tl::string> s_vector {__VA_ARGS__};\
return s_vector;\
}\
};
//////////////////////////////////////////////////////////////////////////
namespace tl
{
namespace path_system
{
TL_DECLARE_STRING_LITERAL(back_t, "..");
TL_DECLARE_STRING_LITERAL(current_t, ".");
}
}
//////////////////////////////////////////////////////////////////////////
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
struct path_systems
{
template<typename T>
static T format_absolute(int type_id, const tl::vector<string>& members) noexcept;
static void parse_absolute(tl::vector<string>& o_elements, int type_id, const char* path, size_t size) noexcept;
static void parse_relative(tl::vector<string>& o_elements, 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_id, const tl::vector<string>& members) noexcept;
static const tl::vector<tl::vector<const char*> >& get_all_parse_separators() noexcept;
template <typename TypeToCheck>
static bool is(int type_id) noexcept;
};
//////////////////////////////////////////////////////////////////////////
template<typename custom_implementation, typename parse_separators, typename format_separator>
struct custom_path_system : public custom_implementation, public detail::path_system::base_path_system<parse_separators, format_separator>
{
};
//////////////////////////////////////////////////////////////////////////
template<typename root_tag, typename parse_separators, typename format_separator>
struct simple_path_system : public detail::path_system::base_path_system<parse_separators, format_separator>
{
template<typename T>
static T format_absolute(const tl::vector<string>& members) noexcept;
static void parse_absolute(tl::vector<string>& o_elements, const char* path, size_t size) noexcept;
static bool validate_abs_path(const tl::vector<string>& members) noexcept;
static bool match(const char* path, size_t size) noexcept;
};
//////////////////////////////////////////////////////////////////////////
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace tl
{
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
template<typename T>
T path_systems<Systems...>::format_absolute(int type_id, const tl::vector<string>& members) noexcept
{
TL_PLAIN_ASSERT(type_id >= 0, "Error: trying to format an unknown path system type.");
return detail::path_system::path_system_getter<0, Systems...>::template format_absolute<T>(type_id, members);
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
void path_systems<Systems...>::parse_absolute(tl::vector<string>& o_elements, int type_id, const char* path, size_t size) noexcept
{
TL_PLAIN_ASSERT(type_id >= 0, "Error: trying to parse an unknown path system type.");
detail::path_system::path_system_getter<0, Systems...>::parse_absolute(o_elements, type_id, path, size);
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
int path_systems<Systems...>::deduce_system_type(const char* path, size_t size) noexcept
{
return detail::path_system::path_system_getter<0, Systems...>::deduce_system_type(path, size);
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
bool path_systems<Systems...>::validate_abs_path(int type_id, const tl::vector<string>& members) noexcept
{
TL_PLAIN_ASSERT(type_id >= 0, "Error: trying to validate an unknown path system type.");
return detail::path_system::path_system_getter<0, Systems...>::validate_abs_path(type_id, members);
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
const tl::vector<tl::vector<const char*> >& path_systems<Systems...>::get_all_parse_separators() noexcept
{
static tl::vector<tl::vector<const char*>> s_all_separators_cache = detail::path_system::path_systems_getter<Systems...>::get_all_parse_separators();
return s_all_separators_cache;
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
void path_systems<Systems...>::parse_relative(tl::vector<string>& o_elements, const char* path, size_t size) noexcept
{
const tl::vector<tl::vector<const char*> >& all_separators = get_all_parse_separators();
const size_t path_system_count = all_separators.size();
eastl::fixed_vector<string, 512> elements;
int systemIndex = -1;
// parse the path
const char* start = path;
const char* const end = start + size;
while (start < end)
{
bool matchedSeparator = false;
const char* sepStart = start;
const char* sepEnd = nullptr;
// system hasn't been deduced yet
if (systemIndex == -1)
{
for (size_t s = 0; s < path_system_count; ++s)
{
const tl::vector<const char*>& separators = all_separators[s];
const char* const* separators_begin = separators.data();
const char* const* separators_end = separators_begin + separators.size();
matchedSeparator = detail::path_system::find_next_separator(separators_begin, separators_end, sepStart, end, sepEnd);
if (matchedSeparator)
{
systemIndex = (int)s;
break;
}
}
}
// path system already deduced
else
{
const tl::vector<const char*>& separators = all_separators[systemIndex];
const char* const* separators_begin = separators.data();
const char* const* separators_end = separators_begin + separators.size();
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.reserve(o_elements.size() + elements.size());
for (string& element : elements)
o_elements.emplace_back(std::move(element));
}
//////////////////////////////////////////////////////////////////////////
template <typename... Systems>
template <typename TypeToCheck>
bool path_systems<Systems...>::is(int type_id) noexcept
{
return detail::path_system::path_system_type_checker<0, TypeToCheck, Systems...>::is(type_id);
}
//////////////////////////////////////////////////////////////////////////
template<typename root_tag, typename parse_separators, typename format_separator>
template<typename T>
T simple_path_system<root_tag, parse_separators, format_separator>::format_absolute(const tl::vector<string>& members) noexcept
{
if constexpr (is_same_v<T, string>)
{
eastl::string buffer;
buffer.reserve(8192);
detail::path_system::simple_format_abs_path(buffer, members, root_tag::value(), format_separator::value());
return string(buffer.data(), buffer.size());
}
else
{
T str;
detail::path_system::simple_format_abs_path(str, members, root_tag::value(), format_separator::value());
return str;
}
}
//////////////////////////////////////////////////////////////////////////
template<typename root_tag, typename parse_separators, typename format_separator>
void simple_path_system<root_tag, parse_separators, format_separator>::parse_absolute(tl::vector<string>& o_elements, const char* path, size_t size) noexcept
{
detail::path_system::simple_parse_path(o_elements, path, size, root_tag::value(), parse_separators::vector());
}
//////////////////////////////////////////////////////////////////////////
template<typename root_tag, typename parse_separators, typename format_separator>
bool simple_path_system<root_tag, parse_separators, format_separator>::match(const char* path, size_t size) noexcept
{
return detail::path_system::simple_match_prefix(path, size, root_tag::value());
}
//////////////////////////////////////////////////////////////////////////
template<typename root_tag, typename parse_separators, typename format_separator>
bool simple_path_system<root_tag, parse_separators, format_separator>::validate_abs_path(const tl::vector<string>& /*members*/) noexcept
{
return true;
}
//////////////////////////////////////////////////////////////////////////
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/detail/internal_assert.h"
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include "tl/detail/prologue.h"
#include "tl/detail/internal_crash.h"
#define TL_PLAIN_CRASH(msg) \
do \
{ \
tl::crash::response response = tl::crash::detail::invoke_crash_handler(__FILE__, __LINE__, msg); \
if (response == tl::crash::response::CRASH) \
*reinterpret_cast<volatile unsigned int*>(0) = 0xDEAD; \
} while(false)
+120
View File
@@ -0,0 +1,120 @@
#pragma once
/*
//possible platform versions
//note the fixed point XX.XX format, so that we can compare versions
#define TL_PLATFORM_VERSION_8_0 800
#define TL_PLATFORM_VERSION_8_1 810
#define TL_PLATFORM_VERSION_10_0 1000
//actual version
#define TL_PLATFORM_VERSION TL_PLATFORM_VERSION_8_0
*/
//////////////////////////////////////////////////////////////////////////
//PLATFORM
//////////////////////////////////////////////////////////////////////////
# if defined (__arm64__) || defined (__aarch64__) || defined(_M_ARM64)
# define TL_PLATFORM_64
# define TL_PLATFORM_ARCHITECTURE_ARM64
# elif defined(__arm__) || defined(_M_ARM)
# define TL_PLATFORM_32
# define TL_PLATFORM_ARCHITECTURE_ARM32
# elif defined(__i386__) || defined(_M_IX86)
# define TL_PLATFORM_32
# define TL_PLATFORM_ARCHITECTURE_X86
# elif defined(__x86_64__) || defined(_M_AMD64)
# define TL_PLATFORM_64
# define TL_PLATFORM_ARCHITECTURE_AMD64
# else
# pragma error "Unrecognized architecture "
# endif
//////////////////////////////////////////////////////////////////////////
//ANDROID
//////////////////////////////////////////////////////////////////////////
#if defined (__ANDROID__)
# define TL_PLATFORM_POSIX_THREADS_API
# define TL_PLATFORM_ANDROID_FAMILY
# define TL_PLATFORM_ANDROID_APP
# define TL_PLATFORM_POSIX_API
#elif defined(_WIN32) || defined(_WIN64) || defined(__MINGW64__)
# define TL_PLATFORM_WINDOWS_FAMILY
# define TL_PLATFORM_WIN_API
# define TL_PLATFORM_WINDOWS_THREADS_API
# if defined(_MSC_VER)
# pragma warning(disable:4100)
# pragma warning(disable:4251)
# endif
# if defined(WINAPI_FAMILY)
# include <winapifamily.h>
# if (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP)
//////////////////////////////////////////////////////////////////////////
// WINDOWS STORE
//////////////////////////////////////////////////////////////////////////
# define TL_PLATFORM_WINDOWS_STORE_APP
# elif (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
//////////////////////////////////////////////////////////////////////////
// WINDOWS PHONE
//////////////////////////////////////////////////////////////////////////s
# define TL_PLATFORM_WINDOWS_PHONE_APP
# else
//////////////////////////////////////////////////////////////////////////
// WINDOWS
//////////////////////////////////////////////////////////////////////////
# define TL_PLATFORM_WINDOWS_DESKTOP_APP
# endif
# else
# define TL_PLATFORM_WINDOWS_DESKTOP_APP
# endif
//no posix here unfortunately
//////////////////////////////////////////////////////////////////////////
//IOS
//////////////////////////////////////////////////////////////////////////
#elif defined(__APPLE__) || defined(MACOSX)
# include "TargetConditionals.h"
# define TL_PLATFORM_POSIX_THREADS_API
# define TL_PLATFORM_OSX_FAMILY
# define TL_PLATFORM_POSIX_API
# if (TARGET_IPHONE_SIMULATOR)
# define TL_PLATFORM_IOS_FAMILY
# define TL_PLATFORM_IOS_SIMULATOR_APP
# elif (TARGET_OS_IPHONE)
# define TL_PLATFORM_IOS_FAMILY
# define TL_PLATFORM_IOS_APP
# if(TARGET_OS_MACCATALYST)
# define TL_PLATFORM_CATALYST
# endif
# elif (TARGET_OS_MAC)
# define TL_PLATFORM_MAC_APP
# else
# error "Unknown Platform"
# endif
//////////////////////////////////////////////////////////////////////////
//LINUX
//////////////////////////////////////////////////////////////////////////
#elif defined (__linux)
# define TL_PLATFORM_LINUX_FAMILY
# define TL_PLATFORM_LINUX_APP
# define TL_PLATFORM_POSIX_API
# define TL_PLATFORM_POSIX_THREADS_API
#else
# error Unrecognized platform.
#endif

Some files were not shown because too many files have changed in this diff Show More