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