fixes
This commit is contained in:
285
include/util/arx.hpp
Executable file
285
include/util/arx.hpp
Executable file
@@ -0,0 +1,285 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <charconv>
|
||||
#include <functional>
|
||||
#include "util/uix.hpp"
|
||||
#include "util/pack.hpp"
|
||||
#include "util/function.hpp"
|
||||
#include "util/string_literal.hpp"
|
||||
#include "util/for_each.hpp"
|
||||
|
||||
|
||||
namespace ztu {
|
||||
|
||||
static constexpr char NO_SHORT_FLAG = '\0';
|
||||
|
||||
namespace arx_internal {
|
||||
|
||||
template<typename F, typename T>
|
||||
concept parsing_function = (
|
||||
ztu::callable<F, std::optional<T>, std::string_view> and
|
||||
(not std::is_class_v<F> or std::is_empty_v<F>)
|
||||
);
|
||||
|
||||
} // namespace arx_internal
|
||||
|
||||
template<
|
||||
char ShortName,
|
||||
ztu::string_literal LongName,
|
||||
typename T = bool,
|
||||
auto Parse = std::nullptr_t{}
|
||||
> requires (
|
||||
std::same_as<std::nullptr_t, decltype(Parse)> or
|
||||
arx_internal::parsing_function<decltype(Parse), T>
|
||||
)
|
||||
struct arx_flag {
|
||||
static constexpr auto short_name = ShortName;
|
||||
static constexpr auto long_name = LongName;
|
||||
using type = T;
|
||||
static constexpr auto parse = Parse;
|
||||
};
|
||||
|
||||
namespace arx_parsers {
|
||||
|
||||
template<std::integral Type = int, int Base = 10>
|
||||
requires (Base > 0)
|
||||
[[nodiscard]] inline std::optional<Type> integer(const std::string_view& str);
|
||||
|
||||
template<std::floating_point Type = float, std::chars_format Format = std::chars_format::general>
|
||||
[[nodiscard]] inline std::optional<Type> floating_point(const std::string_view& str);
|
||||
|
||||
} // namespace arx_parsers
|
||||
|
||||
namespace arx_internal {
|
||||
|
||||
template<ztu::string_literal LongName, class... Flags>
|
||||
struct flag_by_name {
|
||||
template<class Flag>
|
||||
struct pred : std::conditional_t<
|
||||
Flag::long_name == LongName,
|
||||
std::true_type,
|
||||
std::false_type
|
||||
> {
|
||||
};
|
||||
using type = ztu::find<pred, Flags...>;
|
||||
};
|
||||
|
||||
template<ztu::string_literal LongName, class... Flags>
|
||||
using flag_by_name_t = flag_by_name<LongName, Flags...>::type;
|
||||
|
||||
template<ztu::string_literal LongName, class... Flags>
|
||||
using flag_type_by_name_t = flag_by_name_t<LongName, Flags...>::type;
|
||||
|
||||
} // namespace arx_internal
|
||||
|
||||
|
||||
template<class... Flags>
|
||||
class arx {
|
||||
private:
|
||||
static constexpr auto short_flag_prefix = std::string_view{ "-" };
|
||||
static constexpr auto long_flag_prefix = std::string_view{ "--" };
|
||||
static constexpr auto UNBOUND_ARGUMENT = ztu::isize_max;
|
||||
|
||||
public:
|
||||
inline arx(int num_args, const char* const* args);
|
||||
|
||||
template<ztu::string_literal LongName>
|
||||
[[nodiscard]] inline std::optional<arx_internal::flag_type_by_name_t<LongName, Flags...>> get() const;
|
||||
|
||||
[[nodiscard]] inline std::optional<std::string_view> get(ztu::isize position) const;
|
||||
|
||||
[[nodiscard]] inline ztu::isize num_positional() const;
|
||||
|
||||
protected:
|
||||
[[nodiscard]] inline std::optional<std::string_view> find_flag_value(ztu::isize flag_index) const;
|
||||
|
||||
template<ztu::string_literal LongName>
|
||||
[[nodiscard]] inline std::optional<arx_internal::flag_type_by_name_t<LongName, Flags...>>
|
||||
parse_value(const std::string_view& value_str) const;
|
||||
|
||||
private:
|
||||
std::vector<std::pair<ztu::isize, std::string_view>> m_arguments;
|
||||
ztu::isize m_unbound_begin;
|
||||
};
|
||||
|
||||
|
||||
namespace arx_parsers {
|
||||
|
||||
template<std::integral Type, int Base>
|
||||
requires (Base > 0)
|
||||
[[nodiscard]] inline std::optional<Type> integer(const std::string_view& str) {
|
||||
Type value{};
|
||||
const auto [ptr, ec] = std::from_chars(str.begin(), str.end(), value, Base);
|
||||
if (ec == std::errc() and ptr == str.end()) {
|
||||
return value;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<std::floating_point Type, std::chars_format Format>
|
||||
[[nodiscard]] inline std::optional<Type> floating_point(const std::string_view& str) {
|
||||
Type value{};
|
||||
const auto [ptr, ec] = std::from_chars(str.begin(), str.end(), value, Format);
|
||||
if (ec == std::errc() and ptr == str.end()) {
|
||||
return value;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace arx_parsers
|
||||
|
||||
template<class... Flags>
|
||||
arx<Flags...>::arx(int num_args, const char* const* args) {
|
||||
|
||||
m_arguments.reserve(std::max(num_args - 1, 0));
|
||||
|
||||
for (int i = 1; i < num_args; i++) {
|
||||
const auto argument = std::string_view{ args[i] };
|
||||
|
||||
const auto found_match = for_each::indexed_type<Flags...>(
|
||||
[&]<auto Index, typename Flag>() {
|
||||
if ((
|
||||
Flag::short_name != NO_SHORT_FLAG and
|
||||
argument.length() == short_flag_prefix.length() + 1 and
|
||||
argument.starts_with(short_flag_prefix) and
|
||||
argument[short_flag_prefix.length()] == Flag::short_name
|
||||
) or (
|
||||
argument.length() == long_flag_prefix.length() + Flag::long_name.length() and
|
||||
argument.starts_with(long_flag_prefix) and
|
||||
argument.substr(long_flag_prefix.length()) == Flag::long_name
|
||||
)) {
|
||||
if constexpr (std::same_as<typename Flag::type, bool>) {
|
||||
m_arguments.emplace_back(Index, argument);
|
||||
} else {
|
||||
if (i + 1 < num_args) {
|
||||
const auto value = std::string_view{ args[++i] };
|
||||
m_arguments.emplace_back(Index, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
|
||||
if (not found_match) {
|
||||
m_arguments.emplace_back(UNBOUND_ARGUMENT, argument);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(
|
||||
m_arguments.begin(), m_arguments.end(), [](const auto& a, const auto& b) -> bool {
|
||||
return a.first < b.first;
|
||||
}
|
||||
);
|
||||
|
||||
const auto first_unbound = std::find_if(
|
||||
m_arguments.begin(), m_arguments.end(), [](const auto& a) {
|
||||
return a.first == UNBOUND_ARGUMENT;
|
||||
}
|
||||
);
|
||||
|
||||
const auto num_bound = m_arguments.end() - first_unbound;
|
||||
|
||||
const auto last = std::unique(
|
||||
std::reverse_iterator(first_unbound), m_arguments.rend(), [](const auto& a, const auto& b) {
|
||||
return a.first == b.first;
|
||||
}
|
||||
).base();
|
||||
|
||||
m_arguments.erase(m_arguments.begin(), last);
|
||||
|
||||
m_unbound_begin = m_arguments.size() - num_bound;
|
||||
};
|
||||
|
||||
|
||||
template<class... Flags>
|
||||
std::optional<std::string_view> arx<Flags...>::find_flag_value(isize flag_index) const {
|
||||
const auto begin = m_arguments.begin();
|
||||
const auto end = begin + m_unbound_begin;
|
||||
const auto it = std::lower_bound(
|
||||
begin, end, flag_index, [](const auto& a, const auto& b) {
|
||||
return a.first < b;
|
||||
}
|
||||
);
|
||||
if (it != end and it->first == flag_index) {
|
||||
return { it->second };
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
|
||||
template<class... Flags>
|
||||
template<string_literal LongName>
|
||||
std::optional<arx_internal::flag_type_by_name_t<LongName, Flags...>> arx<Flags...>::parse_value(
|
||||
const std::string_view& value_str
|
||||
) const {
|
||||
|
||||
using Flag = arx_internal::flag_by_name_t<LongName, Flags...>;
|
||||
using Type = Flag::type;
|
||||
using opt_t = std::optional<Type>;
|
||||
|
||||
opt_t ret;
|
||||
|
||||
// Use custom parser if provided and if not try using a default parser
|
||||
if constexpr (
|
||||
requires(const std::string_view str) {
|
||||
{
|
||||
std::invoke(Flag::parse, str)
|
||||
} -> std::same_as<opt_t>;
|
||||
}
|
||||
) {
|
||||
return std::invoke(Flag::parse, value_str);
|
||||
} else if constexpr (std::integral<Type> && not std::same_as<Type, bool>) {
|
||||
return arx_parsers::integer(value_str);
|
||||
} else if constexpr (std::floating_point<Type>) {
|
||||
return arx_parsers::floating_point(value_str);
|
||||
} else if constexpr (std::same_as<std::string_view, Type>) {
|
||||
return value_str;
|
||||
} else if constexpr (std::same_as<std::string, Type>) {
|
||||
return std::string(value_str);
|
||||
} else {
|
||||
Type::__cannot_parse_this_type;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template<class... Flags>
|
||||
template<string_literal LongName>
|
||||
std::optional<arx_internal::flag_type_by_name_t<LongName, Flags...>> arx<Flags...>::get() const {
|
||||
using Flag = arx_internal::flag_by_name_t<LongName, Flags...>;
|
||||
static constexpr auto index = index_of<Flag, Flags...>;
|
||||
|
||||
if (index < sizeof...(Flags)) {
|
||||
const auto value_opt = find_flag_value(index);
|
||||
if constexpr (std::same_as<typename Flag::type, bool>) {
|
||||
return value_opt.has_value();
|
||||
} else if (value_opt) {
|
||||
return parse_value<LongName>(*value_opt);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<class... Flags>
|
||||
inline isize arx<Flags...>::num_positional() const {
|
||||
return m_arguments.size() - m_unbound_begin;
|
||||
}
|
||||
|
||||
template<class... Flags>
|
||||
std::optional<std::string_view> arx<Flags...>::get(isize position) const {
|
||||
if (0 <= position and position < num_positional()) {
|
||||
return { m_arguments[m_unbound_begin + position].second };
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace ztu
|
||||
|
||||
207
include/util/binary_ifstream.hpp
Normal file
207
include/util/binary_ifstream.hpp
Normal file
@@ -0,0 +1,207 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <span>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
class binary_ifstream {
|
||||
public:
|
||||
using char_type = char;
|
||||
using size_type = std::streamsize;
|
||||
|
||||
[[nodiscard]] inline std::error_code open(
|
||||
const std::filesystem::path& filename,
|
||||
bool check_file_before_open
|
||||
);
|
||||
|
||||
[[nodiscard]] inline std::error_code read(std::span<char_type> buffer);
|
||||
|
||||
template<std::endian E, std::integral T>
|
||||
[[nodiscard]] std::error_code read(T& integer);
|
||||
|
||||
template<std::endian E, std::floating_point T>
|
||||
[[nodiscard]] std::error_code read_ieee754(T& floating_point);
|
||||
|
||||
[[nodiscard]] inline std::error_code skip(size_type count);
|
||||
|
||||
template<std::integral T>
|
||||
[[nodiscard]] std::error_code skip();
|
||||
|
||||
template<std::floating_point T>
|
||||
[[nodiscard]] std::error_code skip();
|
||||
|
||||
inline void close();
|
||||
|
||||
protected:
|
||||
[[nodiscard]] inline static std::error_code check_file(
|
||||
const std::filesystem::path& filename
|
||||
);
|
||||
|
||||
[[nodiscard]] inline std::errc get_stream_error_code() const;
|
||||
|
||||
private:
|
||||
std::basic_ifstream<char_type> in{};
|
||||
};
|
||||
|
||||
inline std::errc binary_ifstream::get_stream_error_code() const {
|
||||
const auto state = in.rdstate();
|
||||
|
||||
auto code = std::errc{};
|
||||
|
||||
if (state != std::ios::goodbit)
|
||||
{
|
||||
code = std::errc::state_not_recoverable;
|
||||
|
||||
if (state & std::ios::failbit) // irrecoverable stream error
|
||||
{
|
||||
code = std::errc::state_not_recoverable;
|
||||
}
|
||||
else if (state & std::ios::badbit) // input/output operation failed
|
||||
{
|
||||
code = std::errc::io_error;
|
||||
}
|
||||
else if (state & std::ios::eofbit) // input sequence has reached end-of-file
|
||||
{
|
||||
code = std::errc::result_out_of_range;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
inline std::error_code binary_ifstream::check_file(const std::filesystem::path& filename)
|
||||
{
|
||||
auto error = std::error_code{};
|
||||
|
||||
const auto is_file = std::filesystem::exists(filename, error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (not is_file)
|
||||
{
|
||||
return std::make_error_code(std::errc::invalid_argument);
|
||||
}
|
||||
|
||||
const auto files_exists = std::filesystem::exists(filename, error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (not files_exists)
|
||||
{
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline std::error_code binary_ifstream::open(
|
||||
const std::filesystem::path& filename,
|
||||
bool check_file_before_open
|
||||
) {
|
||||
if (check_file_before_open)
|
||||
{
|
||||
if (const auto error = check_file(filename))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
in = {};
|
||||
|
||||
// Turn of exceptions, as errors are handled via the stream state.
|
||||
in.exceptions(std::ios::goodbit);
|
||||
|
||||
in.open(filename, std::ios::binary | std::ios::in);
|
||||
|
||||
if (const auto error = std::make_error_code(get_stream_error_code()))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (not in.is_open())
|
||||
{
|
||||
// Unknown error, so 'state_not_recoverable' is assumed.
|
||||
return std::make_error_code(std::errc::state_not_recoverable);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
inline void binary_ifstream::close()
|
||||
{
|
||||
in.close();
|
||||
}
|
||||
|
||||
inline std::error_code binary_ifstream::skip(size_type count)
|
||||
{
|
||||
in.ignore(count);
|
||||
return std::make_error_code(get_stream_error_code());
|
||||
}
|
||||
|
||||
template<std::floating_point T>
|
||||
std::error_code binary_ifstream::skip()
|
||||
{
|
||||
return skip(sizeof(T));
|
||||
}
|
||||
|
||||
template<std::integral T>
|
||||
std::error_code binary_ifstream::skip()
|
||||
{
|
||||
return skip(sizeof(T));
|
||||
}
|
||||
|
||||
inline std::error_code binary_ifstream::read(std::span<char_type> buffer)
|
||||
{
|
||||
in.read(buffer.data(), static_cast<size_type>(buffer.size()));
|
||||
return std::make_error_code(get_stream_error_code());
|
||||
}
|
||||
|
||||
template<std::endian E, std::integral T>
|
||||
std::error_code binary_ifstream::read(T& integer)
|
||||
{
|
||||
std::array<char_type, sizeof(T)> buffer;
|
||||
if (const auto error = read(buffer))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if constexpr (std::endian::native != E)
|
||||
{
|
||||
std::reverse(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
// Use memcpy to avoid UB.
|
||||
std::memcpy(static_cast<void*>(&integer), buffer.data(), buffer.size());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<std::endian E, std::floating_point T>
|
||||
std::error_code binary_ifstream::read_ieee754(T& floating_point)
|
||||
{
|
||||
static_assert(std::numeric_limits<T>::is_iec559);
|
||||
|
||||
std::array<char_type, sizeof(T)> buffer;
|
||||
if (const auto error = read(buffer))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if constexpr (std::endian::native != E)
|
||||
{
|
||||
std::reverse(buffer.begin(), buffer.end());
|
||||
}
|
||||
|
||||
// Use memcpy to avoid UB.
|
||||
std::memcpy(static_cast<void*>(&floating_point), buffer.data(), buffer.size());
|
||||
|
||||
return {};
|
||||
}
|
||||
57
include/util/enum_operators.hpp
Normal file
57
include/util/enum_operators.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#define DEFINE_ENUM_FLAG_OPERATORS(ENUM_TYPE) \
|
||||
[[nodiscard]] constexpr ENUM_TYPE operator|( \
|
||||
const ENUM_TYPE lhs, const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return static_cast<ENUM_TYPE>( \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(lhs) | \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(rhs) \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
[[nodiscard]] constexpr ENUM_TYPE operator&( \
|
||||
const ENUM_TYPE lhs, const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return static_cast<ENUM_TYPE>( \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(lhs) & \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(rhs) \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
[[nodiscard]] constexpr ENUM_TYPE operator^( \
|
||||
const ENUM_TYPE lhs, const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return static_cast<ENUM_TYPE>( \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(lhs) ^ \
|
||||
static_cast<std::underlying_type_t<ENUM_TYPE>>(rhs) \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
[[nodiscard]] constexpr ENUM_TYPE operator~(const ENUM_TYPE a) \
|
||||
{ \
|
||||
return static_cast<ENUM_TYPE>( \
|
||||
~static_cast<std::underlying_type_t<ENUM_TYPE>>(a) \
|
||||
); \
|
||||
} \
|
||||
\
|
||||
constexpr ENUM_TYPE& operator|=( \
|
||||
ENUM_TYPE& lhs, \
|
||||
const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return lhs = lhs | rhs; \
|
||||
} \
|
||||
\
|
||||
constexpr ENUM_TYPE& operator&=( \
|
||||
ENUM_TYPE& lhs, \
|
||||
const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return lhs = lhs & rhs; \
|
||||
} \
|
||||
\
|
||||
constexpr ENUM_TYPE& operator^=( \
|
||||
ENUM_TYPE& lhs, \
|
||||
const ENUM_TYPE rhs \
|
||||
) { \
|
||||
return lhs = lhs ^ rhs; \
|
||||
}
|
||||
28
include/util/extra_arx_parsers.hpp
Executable file
28
include/util/extra_arx_parsers.hpp
Executable file
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
#include <charconv>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace extra_arx_parsers {
|
||||
|
||||
template<int Count, typename T, glm::qualifier Q>
|
||||
requires (Count > 0)
|
||||
[[nodiscard]] inline std::optional<glm::vec<Count, T>> glm_vec(const std::string_view& str) {
|
||||
glm::vec<Count, T, Q> vec{};
|
||||
auto it = str.cbegin();
|
||||
for (int i = 0; i < Count; i++) {
|
||||
const auto [ptr, ec] = std::from_chars(it, str.cend(), vec[i], std::chars_format::general);
|
||||
if (ec != std::errc()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
it = ptr + 1; // skip space in between components
|
||||
}
|
||||
if (it < str.cend()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
}
|
||||
49
include/util/for_each.hpp
Executable file
49
include/util/for_each.hpp
Executable file
@@ -0,0 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace ztu::for_each {
|
||||
|
||||
template<typename... Types>
|
||||
inline constexpr bool type(auto &&f) {
|
||||
return (f.template operator()<Types>() || ...);
|
||||
}
|
||||
|
||||
template<auto... Values>
|
||||
inline constexpr bool value(auto &&f) {
|
||||
return (f.template operator()<Values>() || ...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline constexpr bool argument(auto &&f, Args&&... args) {
|
||||
return (f(std::forward<Args>(args)) || ...);
|
||||
}
|
||||
|
||||
template <auto Size>
|
||||
inline constexpr bool index(auto&& f) {
|
||||
return [&]<auto... Indices>(std::index_sequence<Indices...>) {
|
||||
return (f.template operator()<Indices>() || ...);
|
||||
}(std::make_index_sequence<Size>());
|
||||
}
|
||||
|
||||
template<typename... Types>
|
||||
inline constexpr bool indexed_type(auto &&f) {
|
||||
return [&]<auto... Indices>(std::index_sequence<Indices...>) {
|
||||
return (f.template operator()<Indices, Types>() || ...);
|
||||
}(std::make_index_sequence<sizeof...(Types)>());
|
||||
}
|
||||
|
||||
template<auto... Values>
|
||||
inline constexpr bool indexed_value(auto &&f) {
|
||||
return [&]<auto... Indices>(std::index_sequence<Indices...>) {
|
||||
return (f.template operator()<Indices, Values>() || ...);
|
||||
}(std::make_index_sequence<sizeof...(Values)>());
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
inline constexpr bool indexed_argument(auto &&f, Args&&... args) {
|
||||
return [&]<auto... Indices>(std::index_sequence<Indices...>) {
|
||||
return (f.template operator()<Indices>(std::forward<Args>(args)) || ...);
|
||||
}(std::make_index_sequence<sizeof...(Args)>());
|
||||
}
|
||||
}
|
||||
63
include/util/function.hpp
Executable file
63
include/util/function.hpp
Executable file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <concepts>
|
||||
|
||||
namespace ztu {
|
||||
|
||||
template<typename F, typename R, typename... Args>
|
||||
concept callable = std::same_as<std::invoke_result_t<F, Args...>, R>;
|
||||
|
||||
template<typename T>
|
||||
concept runnable = callable<T, void>;
|
||||
|
||||
template<typename T, typename R>
|
||||
concept supplier = callable<T, R>;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
concept consumer = callable<T, void, Args...>;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
concept predicate = callable<T, bool, Args...>;
|
||||
|
||||
|
||||
template<typename>
|
||||
struct function_meta;
|
||||
|
||||
template<typename R, typename... Args>
|
||||
struct function_meta<R(*)(Args...)> {
|
||||
using ret_t = R;
|
||||
using args_t = std::tuple<Args...>;
|
||||
static constexpr bool is_const = false;
|
||||
};
|
||||
|
||||
template<class C, typename R, typename... Args>
|
||||
struct function_meta<R(C::*)(Args...)> {
|
||||
using class_t = C;
|
||||
using ret_t = R;
|
||||
using args_t = std::tuple<Args...>;
|
||||
static constexpr bool is_const = false;
|
||||
};
|
||||
|
||||
template<class C, typename R, typename... Args>
|
||||
struct function_meta<R(C::*)(Args...) const> {
|
||||
using class_t = C;
|
||||
using ret_t = R;
|
||||
using args_t = std::tuple<Args...>;
|
||||
static constexpr bool is_const = true;
|
||||
};
|
||||
|
||||
namespace function {
|
||||
template<typename F>
|
||||
using class_t = typename function_meta<F>::class_t;
|
||||
|
||||
template<typename F>
|
||||
using ret_t = typename function_meta<F>::ret_t;
|
||||
|
||||
template<typename F>
|
||||
using args_t = typename function_meta<F>::args_t;
|
||||
|
||||
template<typename F>
|
||||
constexpr bool is_const_v = function_meta<F>::is_const;
|
||||
}
|
||||
}
|
||||
25
include/util/id_type.hpp
Normal file
25
include/util/id_type.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
namespace ztu
|
||||
{
|
||||
template<class Parent, typename IndexType, int uuid = 0>
|
||||
class id_type_for
|
||||
{
|
||||
friend Parent;
|
||||
|
||||
using index_type = IndexType;
|
||||
|
||||
explicit constexpr id_type_for(index_type index) : index{ index } {}
|
||||
|
||||
index_type index{};
|
||||
|
||||
public:
|
||||
constexpr id_type_for() = default;
|
||||
constexpr auto operator<=>(const id_type_for&) const = default;
|
||||
|
||||
constexpr operator bool() const
|
||||
{
|
||||
return index == index_type{};
|
||||
}
|
||||
};
|
||||
}
|
||||
327
include/util/image.hpp
Executable file
327
include/util/image.hpp
Executable file
@@ -0,0 +1,327 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC system_header
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(push, 0)
|
||||
#endif
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STB_IMAGE_STATIC
|
||||
#include "stb_image.h"
|
||||
|
||||
|
||||
#define STBI_MSC_SECURE_CRT
|
||||
#define STB_IMAGE_WRITE_STATIC
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
|
||||
|
||||
#if defined(__GNUC__) || defined(__GNUG__)
|
||||
#pragma GCC diagnostic pop
|
||||
#elif defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
namespace ztu {
|
||||
|
||||
template<typename C>
|
||||
class image {
|
||||
public:
|
||||
using value_type = C;
|
||||
using size_type = std::make_signed_t<std::size_t>;
|
||||
using difference_type = size_type;
|
||||
using reference = std::add_lvalue_reference_t<value_type>;
|
||||
using const_reference = std::add_const_t<reference>;
|
||||
using pointer = value_type*;
|
||||
using const_pointer = const value_type*;
|
||||
using iterator = pointer;
|
||||
using const_iterator = const_pointer;
|
||||
|
||||
public:
|
||||
[[nodiscard]] static std::error_code load(const char* filename, image& dst, bool flip = false);
|
||||
|
||||
[[nodiscard]] static image create(size_type width, size_type height, const value_type& color);
|
||||
|
||||
public:
|
||||
image() = default;
|
||||
|
||||
image(std::unique_ptr<value_type>&& data, size_type width, size_type height);
|
||||
|
||||
image(const image&);
|
||||
|
||||
image(image&&) noexcept;
|
||||
|
||||
[[nodiscard]] image& operator=(const image&);
|
||||
|
||||
[[nodiscard]] image& operator=(image&&) noexcept;
|
||||
|
||||
[[nodiscard]] value_type operator()(double x, double y) const;
|
||||
|
||||
[[nodiscard]] const_reference operator()(size_type x, size_type y) const;
|
||||
|
||||
[[nodiscard]] reference operator()(size_type x, size_type y);
|
||||
|
||||
[[nodiscard]] const_iterator operator[](size_type y) const;
|
||||
|
||||
[[nodiscard]] iterator operator[](size_type y);
|
||||
|
||||
[[nodiscard]] int save(const std::string& filename) const;
|
||||
|
||||
[[nodiscard]] bool contains(size_type x, size_type y) const;
|
||||
|
||||
[[nodiscard]] size_type width() const;
|
||||
|
||||
[[nodiscard]] size_type height() const;
|
||||
|
||||
[[nodiscard]] std::pair<size_type, size_type> size() const;
|
||||
|
||||
[[nodiscard]] size_type num_pixels() const;
|
||||
|
||||
[[nodiscard]] const_iterator begin() const;
|
||||
|
||||
[[nodiscard]] iterator begin();
|
||||
|
||||
[[nodiscard]] const_iterator end() const;
|
||||
|
||||
[[nodiscard]] iterator end();
|
||||
|
||||
[[nodiscard]] const_iterator cbegin() const;
|
||||
|
||||
[[nodiscard]] const_iterator cend() const;
|
||||
|
||||
[[nodiscard]] const_pointer data() const;
|
||||
|
||||
[[nodiscard]] pointer data();
|
||||
|
||||
private:
|
||||
std::unique_ptr<value_type[]> m_data{ nullptr };
|
||||
size_type m_width{ 0 }, m_height{ 0 };
|
||||
};
|
||||
|
||||
template<typename C>
|
||||
image<C>::image(std::unique_ptr<value_type>&& data, const size_type width, const size_type height) :
|
||||
m_data{ std::move(data) }, m_width{ width }, m_height{ height } {
|
||||
};
|
||||
|
||||
template<typename C>
|
||||
image<C>::image(const image& other) :
|
||||
m_data{ new C[other.m_width * other.m_height] },
|
||||
m_width{ other.m_width }, m_height{ other.m_height } {
|
||||
std::copy_n(other.m_data.get(), other.m_width * other.m_height, this->m_data.get());
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
image<C>::image(image&& other) noexcept :
|
||||
m_data{ std::move(other.m_data) },
|
||||
m_width{ other.m_width }, m_height{ other.m_height } {
|
||||
other.m_width = 0;
|
||||
other.m_height = 0;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
image<C>& image<C>::operator=(const image& other) {
|
||||
if (this != &other) {
|
||||
|
||||
const auto m_num_pixels = m_width * m_height;
|
||||
const auto o_num_pixels = other.m_width * other.m_height;
|
||||
|
||||
if (o_num_pixels > m_num_pixels) {
|
||||
this->~image();
|
||||
this->m_data = new C[o_num_pixels];
|
||||
}
|
||||
|
||||
std::copy_n(other.m_data, o_num_pixels, this->m_data);
|
||||
|
||||
this->m_width = other.m_width;
|
||||
this->m_height = other.m_height;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
image<C>& image<C>::operator=(image&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~image();
|
||||
|
||||
this->m_data = std::move(other.m_data);
|
||||
this->m_width = other.m_width;
|
||||
this->m_height = other.m_height;
|
||||
|
||||
other.m_width = 0;
|
||||
other.m_height = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<typename C>
|
||||
std::error_code image<C>::load(const char* filename, image& dst, bool flip) {
|
||||
int width, height, channels;
|
||||
|
||||
stbi_set_flip_vertically_on_load(flip);
|
||||
|
||||
auto data = reinterpret_cast<pointer>(stbi_load(filename, &width, &height, &channels, sizeof(C)));
|
||||
|
||||
if (data == nullptr) {
|
||||
return std::make_error_code(std::errc::no_such_file_or_directory);
|
||||
}
|
||||
|
||||
dst.m_data.reset(data);
|
||||
dst.m_width = static_cast<size_type>(width);
|
||||
dst.m_height = static_cast<size_type>(height);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
image<C> image<C>::create(const size_type width, const size_type height, const C& color) {
|
||||
|
||||
const auto num_pixels = width * height;
|
||||
C* data = new C[num_pixels];
|
||||
|
||||
std::fill_n(data, num_pixels, color);
|
||||
|
||||
return image(data, width, height);
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::size_type image<C>::width() const {
|
||||
return m_width;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::size_type image<C>::height() const {
|
||||
return m_height;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
std::pair<typename image<C>::size_type, typename image<C>::size_type> image<C>::size() const {
|
||||
return { m_width, m_height };
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::size_type image<C>::num_pixels() const {
|
||||
return m_width * m_height;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
int image<C>::save(const std::string& filename) const {
|
||||
|
||||
std::string ext = filename.substr(filename.rfind('.') + 1, filename.length());
|
||||
std::transform(
|
||||
ext.begin(), ext.end(), ext.begin(), [](unsigned char c) {
|
||||
return std::tolower(c);
|
||||
}
|
||||
);
|
||||
|
||||
int status = -1;
|
||||
|
||||
if (ext == "png") {
|
||||
status = stbi_write_png(filename.c_str(), m_width, m_height, sizeof(C), m_data, m_width * sizeof(C));
|
||||
} else if (ext == "bmp") {
|
||||
status = stbi_write_bmp(filename.c_str(), m_width, m_height, sizeof(C), m_data);
|
||||
} else if (ext == "tga") {
|
||||
status = stbi_write_tga(filename.c_str(), m_width, m_height, sizeof(C), m_data);
|
||||
} else if (ext == "jpg" || ext == "jpeg") {
|
||||
status = stbi_write_jpg(filename.c_str(), m_width, m_height, sizeof(C), m_data, m_width * sizeof(C));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
bool image<C>::contains(size_type x, size_type y) const {
|
||||
return 0 <= x and x < m_width and 0 <= y and y < m_height;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_reference image<C>::operator()(size_type x, size_type y) const {
|
||||
const auto clamped_x = std::clamp(x, static_cast<size_type>(0), m_width - 1);
|
||||
const auto clamped_y = std::clamp(y, static_cast<size_type>(0), m_height - 1);
|
||||
return m_data[clamped_x + clamped_y * m_width];
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::reference image<C>::operator()(size_type x, size_type y) {
|
||||
const auto clamped_x = std::clamp(x, static_cast<size_type>(0), m_width - 1);
|
||||
const auto clamped_y = std::clamp(y, static_cast<size_type>(0), m_height - 1);
|
||||
return m_data[clamped_x + clamped_y * m_width];
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::value_type image<C>::operator()(double x, double y) const {
|
||||
auto min_x = static_cast<size_type>(std::floor(x));
|
||||
auto min_y = static_cast<size_type>(std::floor(y));
|
||||
const auto px00 = (*this)(min_x, min_y), px10 = (*this)(min_x + 1, min_y);
|
||||
const auto px01 = (*this)(min_x, min_y + 1), px11 = (*this)(min_x + 1, min_y + 1);
|
||||
const auto a_x = x - static_cast<double>(min_x), a_y = y - static_cast<double>(min_y);
|
||||
return std::lerp(
|
||||
std::lerp(px00, px10, a_x),
|
||||
std::lerp(px01, px11, a_x),
|
||||
a_y
|
||||
);
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_iterator image<C>::operator[](size_type y) const {
|
||||
return &m_data[y * m_width];
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::iterator image<C>::operator[](size_type y) {
|
||||
return &m_data[y * m_width];
|
||||
}
|
||||
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_iterator image<C>::begin() const {
|
||||
return m_data.get();
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::iterator image<C>::begin() {
|
||||
return m_data.get();
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_iterator image<C>::end() const {
|
||||
return m_data.get() + m_width * m_height;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::iterator image<C>::end() {
|
||||
return m_data.get() + m_width * m_height;
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_iterator image<C>::cbegin() const {
|
||||
return const_cast<const_iterator>(begin());
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_iterator image<C>::cend() const {
|
||||
return const_cast<const_iterator>(begin());
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::const_pointer image<C>::data() const {
|
||||
return m_data.get();
|
||||
}
|
||||
|
||||
template<typename C>
|
||||
typename image<C>::pointer image<C>::data() {
|
||||
return m_data.get();
|
||||
}
|
||||
|
||||
} // namespace ztu
|
||||
87
include/util/line_parser.hpp
Normal file
87
include/util/line_parser.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <istream>
|
||||
#include "util/for_each.hpp"
|
||||
|
||||
namespace ztu
|
||||
{
|
||||
template<bool Repeating, typename F>
|
||||
struct prefixed_line_parser {
|
||||
const std::string_view prefix;
|
||||
F parse;
|
||||
};
|
||||
|
||||
template<bool Repeating>
|
||||
struct line_repeating_type {};
|
||||
|
||||
static constexpr auto is_repeating = line_repeating_type<true>{};
|
||||
static constexpr auto is_not_repeating = line_repeating_type<false>{};
|
||||
|
||||
template<bool Repeating, typename F>
|
||||
prefixed_line_parser<Repeating, F> make_line_parser(std::string_view prefix, line_repeating_type<Repeating>, F&& f)
|
||||
{
|
||||
return { prefix, std::forward<F>(f) };
|
||||
}
|
||||
|
||||
template<typename E, bool... Rs, class... Fs>
|
||||
E parse_lines(std::istream& in, const bool pedantic, prefixed_line_parser<Rs, Fs>&&... parsers)
|
||||
{
|
||||
|
||||
auto ec = E{};
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in, line)) [[likely]]
|
||||
{
|
||||
parse_current_line:
|
||||
|
||||
auto line_updated = false;
|
||||
|
||||
for_each::argument(
|
||||
[&]<bool R, typename F>(prefixed_line_parser<R, F>&& parser) -> bool
|
||||
{
|
||||
if (line.starts_with(parser.prefix))
|
||||
{
|
||||
const auto prefix_length = parser.prefix.length();
|
||||
ec = parser.parse(line.substr(prefix_length));
|
||||
|
||||
if constexpr (R)
|
||||
{
|
||||
while (std::getline(in, line))
|
||||
{
|
||||
if (line.starts_with(parser.prefix)) [[likely]]
|
||||
{
|
||||
ec = parser.parse(line.substr(prefix_length));
|
||||
}
|
||||
else [[unlikely]]
|
||||
{
|
||||
line_updated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
std::forward<prefixed_line_parser<Rs, Fs>>(parsers)...
|
||||
);
|
||||
|
||||
if (pedantic) {
|
||||
if (ec != E{}) [[unlikely]] {
|
||||
return ec;
|
||||
}
|
||||
}
|
||||
|
||||
if (line_updated)
|
||||
{
|
||||
goto parse_current_line;
|
||||
}
|
||||
}
|
||||
|
||||
return E{};
|
||||
}
|
||||
}
|
||||
1742
include/util/logger.hpp
Executable file
1742
include/util/logger.hpp
Executable file
File diff suppressed because it is too large
Load Diff
139
include/util/result.hpp
Normal file
139
include/util/result.hpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#pragma once
|
||||
|
||||
#include <variant>
|
||||
#include <system_error>
|
||||
#include <expected>
|
||||
|
||||
namespace ztu {
|
||||
|
||||
template<typename T>
|
||||
using result = std::expected<T, std::error_code>;
|
||||
|
||||
/*
|
||||
template<typename T>
|
||||
class result {
|
||||
static constexpr auto value_index = 0;
|
||||
static constexpr auto error_index = 1;
|
||||
|
||||
public:
|
||||
constexpr result(T value);
|
||||
constexpr result(const std::error_code& error);
|
||||
|
||||
template<typename... Args>
|
||||
void emplace(Args&&... args);
|
||||
|
||||
[[nodiscard]] constexpr bool ok() const;
|
||||
|
||||
[[nodiscard]] constexpr std::error_code error() const;
|
||||
|
||||
[[nodiscard]] constexpr const T& value() const;
|
||||
|
||||
[[nodiscard]] constexpr T& value();
|
||||
|
||||
[[nodiscard]] constexpr T&& value() &&;
|
||||
|
||||
[[nodiscard]] operator bool() const;
|
||||
|
||||
[[nodiscard]] const T& operator*() const;
|
||||
|
||||
[[nodiscard]] T& operator*();
|
||||
|
||||
[[nodiscard]] T&& operator*() &&;
|
||||
|
||||
[[nodiscard]] bool operator==(const T& rhs) const;
|
||||
|
||||
[[nodiscard]] bool operator==(const std::error_code& rhs) const;
|
||||
|
||||
template<typename Func>
|
||||
auto map(Func&& func) const -> result<decltype(func(value()))>
|
||||
{
|
||||
if (ok()) return func(value());
|
||||
return error();
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<T, std::error_code> m_data;
|
||||
};
|
||||
|
||||
// Member function definitions
|
||||
|
||||
template<typename T>
|
||||
constexpr result<T>::result(T value)
|
||||
: m_data{ std::in_place_index_t<value_index>{}, std::move(value) }
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
constexpr result<T>::result(const std::error_code& error)
|
||||
: m_data{ std::in_place_index_t<error_index>{}, error }
|
||||
{}
|
||||
|
||||
template<typename T>
|
||||
template<typename... Args>
|
||||
void result<T>::emplace(Args&&... args) {
|
||||
m_data.template emplace<value_index>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename Func>
|
||||
auto result<T>::map(Func&& func) const -> result<decltype(func(value()))> {
|
||||
if (ok()) return func(value());
|
||||
return error();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr bool result<T>::ok() const {
|
||||
return m_data.index() == value_index;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr std::error_code result<T>::error() const {
|
||||
auto error_ptr = std::get_if<error_index>(m_data);
|
||||
return error_ptr ? *error_ptr : std::error_code{};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr const T& result<T>::value() const {
|
||||
return std::get<value_index>(m_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T& result<T>::value() {
|
||||
return std::get<value_index>(m_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr T&& result<T>::value() && {
|
||||
return std::get<value_index>(std::move(m_data));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr result<T>::operator bool() const {
|
||||
return ok();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& result<T>::operator*() const {
|
||||
return *std::get_if<value_index>(m_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T& result<T>::operator*() {
|
||||
return *std::get_if<value_index>(m_data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T&& result<T>::operator*() && {
|
||||
return *std::get_if<value_index>(std::move(m_data));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool result<T>::operator==(const T& rhs) const {
|
||||
return ok() and value() == rhs;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool result<T>::operator==(const std::error_code& rhs) const {
|
||||
return not ok() and error() == rhs;
|
||||
}*/
|
||||
|
||||
}
|
||||
10
include/util/specialised_lambda.hpp
Normal file
10
include/util/specialised_lambda.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace ztu
|
||||
{
|
||||
template<class... Lambdas>
|
||||
struct specialised_lambda : Lambdas... { using Lambdas::operator()...; };
|
||||
|
||||
template<class... Lambdas>
|
||||
specialised_lambda(Lambdas...) -> specialised_lambda<Lambdas...>;
|
||||
}
|
||||
126
include/util/string_indexer.hpp
Executable file
126
include/util/string_indexer.hpp
Executable file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
#include "util/uix.hpp"
|
||||
#include <util/for_each.hpp>
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
class string_indexer {
|
||||
private:
|
||||
struct index_type {
|
||||
unsigned int hash;
|
||||
ztu::usize index;
|
||||
|
||||
[[nodiscard]] inline constexpr auto operator<=>(const index_type &other) const {
|
||||
return hash <=> other.hash;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr auto operator==(const index_type &other) const {
|
||||
return hash == other.hash;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline constexpr auto operator<=>(const unsigned other_hash) const {
|
||||
return hash <=> other_hash;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] inline constexpr static unsigned hash(std::span<const char> str);
|
||||
|
||||
public:
|
||||
template<typename... Ts>
|
||||
requires (sizeof...(Ts) == NumKeys)
|
||||
consteval explicit string_indexer(const Ts&... keys) noexcept;
|
||||
|
||||
[[nodiscard]] inline constexpr std::optional<ztu::usize> index_of(std::span<const char> str) const;
|
||||
|
||||
[[nodiscard]] inline constexpr std::optional<std::string_view> name_of(ztu::usize index) const;
|
||||
|
||||
[[nodiscard]] inline constexpr std::span<const std::string_view> keys() const;
|
||||
|
||||
private:
|
||||
std::array<index_type, NumKeys> m_lookup{};
|
||||
std::array<std::string_view, NumKeys> m_keys{};
|
||||
};
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
[[nodiscard]] inline constexpr unsigned string_indexer<NumKeys>::hash(std::span<const char> str) {
|
||||
|
||||
unsigned prime = 0x1000193;
|
||||
unsigned hashed = 0x811c9dc5;
|
||||
|
||||
for (const auto &c : str) {
|
||||
hashed = hashed ^ c;
|
||||
hashed *= prime;
|
||||
}
|
||||
|
||||
return hashed;
|
||||
}
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
template<typename... Ts> requires (sizeof...(Ts) == NumKeys)
|
||||
consteval string_indexer<NumKeys>::string_indexer(const Ts&... keys) noexcept {
|
||||
|
||||
ztu::for_each::indexed_argument([&]<auto Index>(const auto &key) {
|
||||
// Since std::string_view does only truncate the '\0' of strings in the 'const char*' constructor
|
||||
// and does not deem otherwise equal views of truncated and untruncated strings equal,
|
||||
// all strings need to be truncated before constructing the view.
|
||||
const auto begin = std::begin(key), end = std::end(key);
|
||||
m_keys[Index] = { begin, std::find(begin, end, '\0') };
|
||||
m_lookup[Index] = { hash(m_keys[Index] ), Index };
|
||||
return false;
|
||||
}, keys...);
|
||||
|
||||
std::sort(m_lookup.begin(), m_lookup.end());
|
||||
|
||||
auto it = m_lookup.begin();
|
||||
while ((it = std::adjacent_find(it, m_lookup.end())) != m_lookup.end()) {
|
||||
const auto match = it->hash;
|
||||
for (auto it_a = it + 1; it_a != m_lookup.end() && it_a->hash == match; it_a++) {
|
||||
const auto &key_a = m_keys[it_a->index];
|
||||
for (auto it_b = it; it_b != it_a; it_b++) {
|
||||
const auto &key_b = m_keys[it_b->index];
|
||||
if (key_a == key_b) {
|
||||
throw std::logic_error("Duplicate keys");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
[[nodiscard]] inline constexpr std::optional<ztu::usize> string_indexer<NumKeys>::index_of(std::span<const char> str) const {
|
||||
const auto sv = std::string_view(str.begin(), std::find(str.begin(), str.end(), '\0'));
|
||||
|
||||
const auto hashed = hash(sv);
|
||||
const auto it = std::lower_bound(m_lookup.begin(), m_lookup.end(), hashed);
|
||||
|
||||
if (it == m_lookup.end() or hashed != it->hash)
|
||||
return std::nullopt;
|
||||
|
||||
do [[unlikely]] {
|
||||
const auto candidate_index = it->index;
|
||||
if (m_keys[candidate_index] == sv) [[likely]] {
|
||||
return candidate_index;
|
||||
}
|
||||
} while (it < m_lookup.end() && it->hash == hashed);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
[[nodiscard]] inline constexpr std::optional<std::string_view> string_indexer<NumKeys>::name_of(ztu::usize index) const {
|
||||
if (index < NumKeys) {
|
||||
return m_keys[index];
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template<ztu::usize NumKeys>
|
||||
[[nodiscard]] inline constexpr std::span<const std::string_view> string_indexer<NumKeys>::keys() const {
|
||||
return { m_keys };
|
||||
}
|
||||
259
include/util/string_list.hpp
Normal file
259
include/util/string_list.hpp
Normal file
@@ -0,0 +1,259 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string_view>
|
||||
#include <span>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace ztu
|
||||
{
|
||||
|
||||
class string_list_iterator
|
||||
{
|
||||
public:
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
using value_type = std::string_view;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
|
||||
string_list_iterator(
|
||||
std::string_view buffer,
|
||||
std::span<const std::uint32_t> lengths,
|
||||
std::size_t index,
|
||||
std::size_t char_offset)
|
||||
: m_buffer{ buffer }, m_lengths{ lengths }, m_index{ index }, m_char_offset{ char_offset } {}
|
||||
|
||||
value_type operator*() const
|
||||
{
|
||||
return m_buffer.substr(m_char_offset, m_lengths[m_index]);
|
||||
}
|
||||
|
||||
string_list_iterator& operator++()
|
||||
{
|
||||
++m_index;
|
||||
m_char_offset += m_lengths[m_index];
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_list_iterator operator++(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
string_list_iterator& operator--()
|
||||
{
|
||||
--m_index;
|
||||
m_char_offset -= m_lengths[m_index];
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_list_iterator operator--(int)
|
||||
{
|
||||
auto tmp = *this;
|
||||
--(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
string_list_iterator& operator+=(difference_type n)
|
||||
{
|
||||
const auto negative = n < difference_type{ 0 };
|
||||
const auto positive = n > difference_type{ 0 };
|
||||
const auto step = difference_type{ positive } - difference_type{ negative };
|
||||
n = negative ? -n : n;
|
||||
|
||||
while (n--)
|
||||
{
|
||||
m_char_offset += step * m_lengths[m_index];
|
||||
m_index += step;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
string_list_iterator& operator-=(const difference_type n)
|
||||
{
|
||||
return *this += -n;
|
||||
}
|
||||
|
||||
string_list_iterator operator+(const difference_type n) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
return tmp += n;
|
||||
}
|
||||
|
||||
string_list_iterator operator-(const difference_type n) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
return tmp -= n;
|
||||
}
|
||||
|
||||
difference_type operator-(const string_list_iterator& other) const
|
||||
{
|
||||
return static_cast<difference_type>(m_index) - static_cast<difference_type>(other.m_index);
|
||||
}
|
||||
|
||||
value_type operator[](const difference_type n) const
|
||||
{
|
||||
auto tmp = *this;
|
||||
tmp += n;
|
||||
return *tmp;
|
||||
}
|
||||
|
||||
bool operator==(const string_list_iterator& other) const {
|
||||
return m_index == other.m_index && m_char_offset == other.m_char_offset;
|
||||
}
|
||||
|
||||
bool operator!=(const string_list_iterator& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
bool operator<(const string_list_iterator& other) const {
|
||||
return m_index < other.m_index;
|
||||
}
|
||||
|
||||
bool operator<=(const string_list_iterator& other) const {
|
||||
return m_index <= other.m_index;
|
||||
}
|
||||
|
||||
bool operator>(const string_list_iterator& other) const {
|
||||
return m_index > other.m_index;
|
||||
}
|
||||
|
||||
bool operator>=(const string_list_iterator& other) const {
|
||||
return m_index >= other.m_index;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string_view m_buffer;
|
||||
std::span<const std::uint32_t> m_lengths;
|
||||
std::size_t m_index;
|
||||
std::size_t m_char_offset;
|
||||
};
|
||||
|
||||
class string_list
|
||||
{
|
||||
using index_type = std::uint32_t;
|
||||
using iterator = string_list_iterator;
|
||||
|
||||
public:
|
||||
string_list() = default;
|
||||
|
||||
string_list(std::initializer_list<std::string_view> init)
|
||||
{
|
||||
for (const auto& str : init) {
|
||||
push_back(str);
|
||||
}
|
||||
}
|
||||
|
||||
void reserve(const std::size_t characters, const std::size_t lengths)
|
||||
{
|
||||
m_buffer.reserve(characters);
|
||||
m_lengths.reserve(lengths);
|
||||
}
|
||||
|
||||
std::size_t character_count() const
|
||||
{
|
||||
return m_buffer.size();
|
||||
}
|
||||
|
||||
void push_back(const std::string_view& str)
|
||||
{
|
||||
m_buffer.reserve(m_buffer.size() + str.length() + 1);
|
||||
m_buffer.insert(m_buffer.end(), str.begin(), str.end());
|
||||
m_buffer.push_back('\0');
|
||||
m_lengths.push_back(static_cast<index_type>(str.size()));
|
||||
}
|
||||
|
||||
void push_back(const string_list& list)
|
||||
{
|
||||
m_buffer.insert(m_buffer.end(), list.m_buffer.begin(), list.m_buffer.end());
|
||||
m_lengths.insert(m_lengths.end(), list.m_lengths.begin(), list.m_lengths.end());
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
m_buffer.resize(m_buffer.size() - m_lengths.back());
|
||||
m_lengths.pop_back();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string_view operator[](index_type index) const
|
||||
{
|
||||
auto offset = std::size_t{};
|
||||
for (index_type i{}; i != index; ++i)
|
||||
{
|
||||
offset += m_lengths[i];
|
||||
}
|
||||
|
||||
return { &m_buffer[offset], m_lengths[index] };
|
||||
}
|
||||
|
||||
[[nodiscard]] index_type size() const
|
||||
{
|
||||
return m_lengths.size();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const
|
||||
{
|
||||
return m_lengths.empty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_buffer.clear();
|
||||
m_lengths.clear();
|
||||
}
|
||||
|
||||
void reserve(index_type new_cap)
|
||||
{
|
||||
m_buffer.reserve(new_cap);
|
||||
m_lengths.reserve(new_cap);
|
||||
}
|
||||
|
||||
[[nodiscard]] index_type capacity() const
|
||||
{
|
||||
return m_buffer.capacity();
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator begin() const
|
||||
{
|
||||
return iterator{
|
||||
std::string_view{ m_buffer.data(), m_buffer.size() },
|
||||
m_lengths,
|
||||
0, 0
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] iterator end() const
|
||||
{
|
||||
return iterator{
|
||||
std::string_view{ m_buffer.data(), m_buffer.size() },
|
||||
m_lengths,
|
||||
m_lengths.size(),
|
||||
m_buffer.size()
|
||||
};
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<char>& buffer() const
|
||||
{
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::vector<index_type>& string_lengths() const
|
||||
{
|
||||
return m_lengths;
|
||||
}
|
||||
|
||||
void swap(string_list& other) noexcept
|
||||
{
|
||||
m_buffer.swap(other.m_buffer);
|
||||
m_lengths.swap(other.m_lengths);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> m_buffer{};
|
||||
std::vector<index_type> m_lengths{};
|
||||
};
|
||||
|
||||
}
|
||||
734
include/util/string_literal.hpp
Normal file
734
include/util/string_literal.hpp
Normal file
@@ -0,0 +1,734 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
namespace ztu {
|
||||
|
||||
#define ztu_ic inline constexpr
|
||||
#define ztu_nic [[nodiscard]] inline constexpr
|
||||
|
||||
#ifdef __cpp_exceptions
|
||||
#define ZTU_ASSERT(exp, error) \
|
||||
if (not (exp)) [[unlikely]] throw (error);
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#define ZTU_TO_STRING_IMPL(x) #x
|
||||
#define ZTU_TO_STRING(x) ZTU_TO_STRING_IMPL(x)
|
||||
#define ZTU_ASSERT(exp, error) \
|
||||
if (not (exp)) [[unlikely]] { \
|
||||
puts(__FILE__ ":" ZTU_TO_STRING(__LINE__) ": Assertion '" ZTU_TO_STRING(exp) "' failed."); \
|
||||
abort(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
using sl_ssize_t = std::make_signed_t<std::size_t>;
|
||||
|
||||
template<sl_ssize_t N>
|
||||
requires (N > 0)
|
||||
struct string_literal {
|
||||
//--------------[ typedefs ]--------------//
|
||||
|
||||
static constexpr auto max_size = N - 1;
|
||||
using this_type = string_literal<N>;
|
||||
using array_type = std::array<char,N>;
|
||||
using value_type = array_type::value_type;
|
||||
using size_type = sl_ssize_t;
|
||||
using difference_type = array_type::difference_type;
|
||||
using reference = array_type::reference;
|
||||
using const_reference = array_type::const_reference;
|
||||
using pointer = array_type::pointer;
|
||||
using const_pointer = array_type::const_pointer;
|
||||
using iterator = array_type::iterator;
|
||||
using const_iterator = array_type::const_iterator;
|
||||
using reverse_iterator = array_type::reverse_iterator;
|
||||
using const_reverse_iterator = array_type::const_reverse_iterator;
|
||||
|
||||
|
||||
//--------------[ constructors ]--------------//
|
||||
|
||||
ztu_ic string_literal();
|
||||
|
||||
ztu_ic string_literal(const char (&str)[N]);
|
||||
|
||||
template<sl_ssize_t M>
|
||||
requires (M < N)
|
||||
ztu_ic string_literal(const char (&str)[M]);
|
||||
|
||||
template<typename... Chars>
|
||||
requires (
|
||||
(1 < sizeof...(Chars) and sizeof...(Chars) < N) and
|
||||
(std::same_as<Chars, char> and ...)
|
||||
)
|
||||
ztu_ic string_literal(Chars... chars);
|
||||
|
||||
ztu_ic string_literal(char c, ssize_t count = 1);
|
||||
|
||||
ztu_ic string_literal(const char* str, std::size_t len);
|
||||
|
||||
ztu_ic string_literal(const std::string &str);
|
||||
|
||||
ztu_ic string_literal(const std::string_view &str);
|
||||
|
||||
|
||||
//--------------[ array interface ]--------------//
|
||||
|
||||
ztu_nic reference at(size_type index);
|
||||
ztu_nic const_reference at(size_type index) const;
|
||||
|
||||
ztu_nic reference operator[](size_type index);
|
||||
ztu_nic const_reference operator[](size_type index) const;
|
||||
|
||||
ztu_nic reference front();
|
||||
ztu_nic const_reference front() const;
|
||||
|
||||
ztu_nic reference back();
|
||||
ztu_nic const_reference back() const;
|
||||
|
||||
ztu_nic pointer data() noexcept;
|
||||
ztu_nic const_pointer data() const noexcept;
|
||||
|
||||
ztu_nic iterator begin() noexcept;
|
||||
ztu_nic const_iterator begin() const noexcept;
|
||||
ztu_nic const_iterator cbegin() const noexcept;
|
||||
|
||||
ztu_nic iterator end() noexcept;
|
||||
ztu_nic const_iterator end() const noexcept;
|
||||
ztu_nic const_iterator cend() const noexcept;
|
||||
|
||||
ztu_nic reverse_iterator rbegin() noexcept;
|
||||
ztu_nic const_reverse_iterator rbegin() const noexcept;
|
||||
ztu_nic const_reverse_iterator rcbegin() const noexcept;
|
||||
|
||||
ztu_nic reverse_iterator rend() noexcept;
|
||||
ztu_nic const_reverse_iterator rend() const noexcept;
|
||||
ztu_nic const_reverse_iterator rcend() const noexcept;
|
||||
|
||||
ztu_nic bool empty() const noexcept;
|
||||
ztu_nic size_type size() const noexcept;
|
||||
ztu_nic size_type unused_size() const noexcept;
|
||||
|
||||
|
||||
//--------------[ string interface ]--------------//
|
||||
|
||||
static constexpr auto max_length = max_size;
|
||||
|
||||
ztu_nic pointer c_str() noexcept;
|
||||
ztu_nic const_pointer c_str() const noexcept;
|
||||
|
||||
ztu_nic std::string_view view() const;
|
||||
|
||||
ztu_nic size_type length() const noexcept;
|
||||
|
||||
ztu_nic size_type find(char c, ssize_t pos = 0) const;
|
||||
ztu_nic size_type find(const char *str, size_type len, ssize_t pos) const;
|
||||
ztu_nic size_type find(const char *str, ssize_t pos = 0) const;
|
||||
ztu_nic size_type find(const std::string_view &str, ssize_t pos = 0) const;
|
||||
ztu_nic size_type find(const std::string &str, ssize_t pos = 0) const;
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic size_type find(const string_literal<M> &str) const;
|
||||
|
||||
ztu_ic this_type& resize(size_type new_size, char fill = ' ');
|
||||
|
||||
ztu_ic this_type& erase(const_iterator begin_it, const_iterator end_it);
|
||||
ztu_ic this_type& erase(size_type index, size_type count = 1);
|
||||
|
||||
ztu_ic this_type& insert(size_type index, char c, size_type repeat = 1);
|
||||
ztu_ic this_type& insert(size_type index, const char *str);
|
||||
ztu_ic this_type& insert(size_type index, const char *str, size_type len);
|
||||
ztu_ic this_type& insert(size_type index, const std::string_view &str);
|
||||
ztu_ic this_type& insert(size_type index, const std::string &str);
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic this_type& insert(size_type index, const string_literal<M> &str);
|
||||
|
||||
ztu_ic this_type& replace(size_type index, size_type count, char c, size_type repeat = 1);
|
||||
ztu_ic this_type& replace(size_type index, size_type count, const char *str);
|
||||
ztu_ic this_type& replace(size_type index, size_type count, const char *str, size_type len);
|
||||
ztu_ic this_type& replace(size_type index, size_type count, const std::string_view &str);
|
||||
ztu_ic this_type& replace(size_type index, size_type count, const std::string &str);
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic this_type& replace(size_type index, size_type count, const string_literal<M> &str);
|
||||
|
||||
//--------------[ operators ]--------------//
|
||||
|
||||
ztu_nic bool operator==(const std::string &str) const;
|
||||
ztu_nic bool operator==(const std::string_view &str) const;
|
||||
ztu_nic bool operator==(const char* str) const;
|
||||
template<sl_ssize_t M>
|
||||
ztu_nic bool operator==(const string_literal<M> &str) const;
|
||||
|
||||
|
||||
template<bool KnownToFit = false>
|
||||
ztu_ic void assign(const char* str, size_type len);
|
||||
|
||||
ztu_ic this_type& operator=(const std::string &str);
|
||||
ztu_ic this_type& operator=(const std::string_view &str);
|
||||
ztu_ic this_type& operator=(const char* str);
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic this_type& operator=(const string_literal<M> &str);
|
||||
|
||||
|
||||
ztu_ic void append(const char* str, size_type len);
|
||||
|
||||
ztu_ic this_type& operator+=(const std::string &str);
|
||||
ztu_ic this_type& operator+=(const std::string_view &str);
|
||||
ztu_ic this_type& operator+=(const char* str);
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic this_type& operator+=(const string_literal<M> &str);
|
||||
|
||||
|
||||
template<sl_ssize_t M>
|
||||
ztu_nic string_literal<M + N - 1> operator+(const string_literal<M> &str) const;
|
||||
|
||||
template<sl_ssize_t M>
|
||||
inline friend std::ostream& operator<<(std::ostream &out, const string_literal<M>& str);
|
||||
|
||||
array_type m_value{};
|
||||
};
|
||||
|
||||
//--------------[ predefines ]--------------//
|
||||
|
||||
namespace detail {
|
||||
template<sl_ssize_t MaxLength = std::numeric_limits<sl_ssize_t>::max()>
|
||||
ztu_nic sl_ssize_t strlen(const char* str) {
|
||||
sl_ssize_t len = 0;
|
||||
while (str[len] != '\0') {
|
||||
len++;
|
||||
ZTU_ASSERT(len < MaxLength, std::invalid_argument("given string is not null terminated"));
|
||||
}
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::size() const noexcept {
|
||||
return detail::strlen(m_value.data());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::length() const noexcept {
|
||||
return this->size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::unused_size() const noexcept {
|
||||
return max_size - this->size();
|
||||
}
|
||||
|
||||
|
||||
//--------------[ constructors ]--------------//
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal() = default;
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal(const char (&str)[N]) {
|
||||
std::copy_n(std::begin(str), N, m_value.begin());
|
||||
m_value[N - 1] = '\0';
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M> requires (M < N)
|
||||
ztu_ic string_literal<N>::string_literal(const char (&str)[M]) {
|
||||
std::copy_n(std::begin(str), M, m_value.begin());
|
||||
m_value[M - 1] = '\0';
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<typename... Chars>
|
||||
requires (
|
||||
(1 < sizeof...(Chars) and sizeof...(Chars) < N) and
|
||||
(std::same_as<Chars, char> and ...)
|
||||
)
|
||||
ztu_ic string_literal<N>::string_literal(Chars... chars)
|
||||
: m_value{
|
||||
chars... // default initialization of array elements to 0 terminates string
|
||||
} {}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal(const char c, ssize_t count)
|
||||
: m_value{}
|
||||
{
|
||||
ZTU_ASSERT(count <= max_size, std::length_error("Given count exceeds capacity."));
|
||||
std::fill_n(m_value.begin(), count, c);
|
||||
}
|
||||
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal(const char* data, std::size_t size) {
|
||||
ZTU_ASSERT(size <= max_size, std::length_error("given string exceeds capacity"));
|
||||
std::copy_n(data, size, m_value.begin());
|
||||
m_value[size] = '\0';
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal(const std::string &str)
|
||||
: string_literal(str.data(), str.size()) {}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::string_literal(const std::string_view &str)
|
||||
: string_literal(str.data(), str.size()) {}
|
||||
|
||||
//--------------[ array interface ]--------------//
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reference string_literal<N>::at(size_type index) {
|
||||
const auto m_length = this->length();
|
||||
ZTU_ASSERT(0 <= index and index < m_length, std::out_of_range("given index exceeds length"));
|
||||
return m_value[index];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reference string_literal<N>::at(size_type index) const {
|
||||
const auto m_length = this->length();
|
||||
ZTU_ASSERT(0 <= index and index < m_length, std::out_of_range("given index exceeds length"));
|
||||
return m_value[index];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reference string_literal<N>::operator[](size_type index) {
|
||||
return m_value[index];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reference string_literal<N>::operator[](size_type index) const {
|
||||
return m_value[index];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reference string_literal<N>::front() {
|
||||
return m_value.front();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reference string_literal<N>::front() const {
|
||||
return m_value.front();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reference string_literal<N>::back() {
|
||||
return m_value[this->size() - 1];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reference string_literal<N>::back() const {
|
||||
return m_value[this->size() - 1];
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::pointer string_literal<N>::data() noexcept {
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_pointer string_literal<N>::data() const noexcept {
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::iterator string_literal<N>::begin() noexcept {
|
||||
return m_value.begin();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_iterator string_literal<N>::begin() const noexcept {
|
||||
return m_value.begin();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_iterator string_literal<N>::cbegin() const noexcept {
|
||||
return m_value.cbegin();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::iterator string_literal<N>::end() noexcept {
|
||||
return this->begin() + this->size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_iterator string_literal<N>::end() const noexcept {
|
||||
return this->begin() + this->size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_iterator string_literal<N>::cend() const noexcept {
|
||||
return this->begin() + this->size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reverse_iterator string_literal<N>::rbegin() noexcept {
|
||||
return m_value.rbegin() + this->unused_size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reverse_iterator string_literal<N>::rbegin() const noexcept {
|
||||
return m_value.rbegin() + this->unused_size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reverse_iterator string_literal<N>::rcbegin() const noexcept {
|
||||
return m_value.rcbegin() + this->unused_size();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::reverse_iterator string_literal<N>::rend() noexcept {
|
||||
return m_value.rend();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reverse_iterator string_literal<N>::rend() const noexcept {
|
||||
return m_value.rend();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_reverse_iterator string_literal<N>::rcend() const noexcept {
|
||||
return m_value.crend();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic bool string_literal<N>::empty() const noexcept {
|
||||
return this->front() == '\0';
|
||||
}
|
||||
|
||||
|
||||
//--------------[ string interface ]--------------//
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::pointer string_literal<N>::c_str() noexcept {
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::const_pointer string_literal<N>::c_str() const noexcept {
|
||||
return m_value.data();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic std::string_view string_literal<N>::view() const {
|
||||
return { this->c_str() };
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::find(char c, ssize_t pos) const {
|
||||
const auto m_length = this->length();
|
||||
ZTU_ASSERT(0 <= pos and pos <= m_length, std::out_of_range("Given start pos is out of range."));
|
||||
return std::find(this->begin() + pos, this->begin() + m_length, c) - this->begin();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::find(const char *str, size_type len, ssize_t pos) const {
|
||||
const auto m_length = this->length();
|
||||
ZTU_ASSERT(0 <= pos and pos <= m_length, std::out_of_range("Given start pos is out of range."));
|
||||
return std::search(this->begin() + pos, this->begin() + m_length, str, str + len) - this->begin();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::find(const char *str, ssize_t pos) const {
|
||||
return this->find(str, detail::strlen(str), pos);
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::find(const std::string_view &str, ssize_t pos) const {
|
||||
return this->find(str.data(), str.length(), pos);
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic string_literal<N>::size_type string_literal<N>::find(const std::string &str, ssize_t pos) const {
|
||||
return this->find(str.data(), str.length(), pos);
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic string_literal<N>::size_type string_literal<N>::find(const string_literal<M> &str) const {
|
||||
return this->find(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::resize(size_type new_size, char fill) {
|
||||
ZTU_ASSERT(0 <= new_size and new_size <= max_size, std::length_error("New size exceeds capacity."));
|
||||
m_value[new_size] = '\0';
|
||||
if (const auto m_size = this->size(); new_size > m_size)
|
||||
std::fill_n(this->begin() + m_size, new_size - m_size, fill);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::erase(const_iterator begin_it, const_iterator end_it) {
|
||||
const auto m_size = this->size();
|
||||
const auto m_end = this->begin() + m_size;
|
||||
ZTU_ASSERT(this->begin() <= begin_it and begin_it <= m_end, std::out_of_range("begin iterator out of range"));
|
||||
ZTU_ASSERT(this->begin() <= end_it and end_it <= m_end, std::out_of_range("end iterator out of range"));
|
||||
auto mutable_begin = this->begin() + (begin_it - this->cbegin());
|
||||
auto mutable_end = this->begin() + (end_it - this->cbegin());
|
||||
const auto right_begin = mutable_end;
|
||||
const auto right_end = this->begin() + m_size + 1;
|
||||
std::copy(right_begin, right_end, mutable_begin);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::erase(size_type index, size_type count) {
|
||||
const auto begin_it = this->begin() + index;
|
||||
return this->erase(begin_it, begin_it + count);
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, char c, size_type count) {
|
||||
const auto m_size = this->size();
|
||||
ZTU_ASSERT(0 <= index and index <= m_size, std::out_of_range("given index is out of range"));
|
||||
ZTU_ASSERT(0 <= count and count <= (max_size - m_size), std::length_error("given sequence exceeds capacity"));
|
||||
// move right of index with terminator
|
||||
const auto right_begin = this->begin() + index;
|
||||
const auto right_end = this->begin() + m_size + 1;
|
||||
std::copy_backward(right_begin, right_end, right_end + count);
|
||||
std::fill_n(this->begin() + index, count, c);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, const char *str, size_type count) {
|
||||
const auto m_size = this->size();
|
||||
ZTU_ASSERT(0 <= index and index <= m_size, std::out_of_range("given index is out of range"));
|
||||
ZTU_ASSERT(0 <= count and count <= (max_size - m_size), std::length_error("given sequence exceeds capacity"));
|
||||
// move right of index with terminator
|
||||
const auto right_begin = this->begin() + index;
|
||||
const auto right_end = this->begin() + m_size + 1;
|
||||
std::copy_backward(right_begin, right_end, right_end + count);
|
||||
std::copy_n(str, count, this->begin() + index);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, const char *str) {
|
||||
return this->insert(index, str, detail::strlen(str));
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, const std::string_view &str) {
|
||||
return this->insert(index, str.data(), str.length());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, const std::string &str) {
|
||||
return this->insert(index, str.data(), str.length());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::insert(size_type index, const string_literal<M> &str) {
|
||||
return this->insert(index, str.data(), str.length());
|
||||
}
|
||||
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, char c, size_type repeat) {
|
||||
const auto m_size = this->size();
|
||||
ZTU_ASSERT(0 <= index and index + count <= m_size, std::out_of_range("given index is out of range"));
|
||||
ZTU_ASSERT(0 <= count and 0 <= repeat, std::out_of_range("count and repeat must be none negative"));
|
||||
const auto delta_size = repeat - count;
|
||||
ZTU_ASSERT((m_size + delta_size) <= max_size, std::length_error("given sequence exceeds capacity"));
|
||||
|
||||
const auto right_begin = this->begin() + index + count;
|
||||
const auto right_end = this->begin() + m_size + 1;
|
||||
|
||||
if (delta_size < 0) {
|
||||
std::copy(right_begin, right_end, right_begin + delta_size);
|
||||
} else if (delta_size > 0) {
|
||||
std::copy_backward(right_begin, right_end, right_end + delta_size);
|
||||
}
|
||||
std::fill_n(this->begin() + index, repeat, c);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, const char *str, size_type len) {
|
||||
const auto m_size = this->size();
|
||||
ZTU_ASSERT(0 <= index and index + count <= m_size, std::out_of_range("given index is out of range"));
|
||||
ZTU_ASSERT(0 <= count and 0 <= len, std::out_of_range("count and len must be none negative"));
|
||||
const auto delta_size = len - count;
|
||||
ZTU_ASSERT((m_size + delta_size) <= max_size, std::length_error("given sequence exceeds capacity"));
|
||||
|
||||
const auto right_begin = this->begin() + index + count;
|
||||
const auto right_end = this->begin() + m_size + 1;
|
||||
|
||||
if (delta_size < 0) {
|
||||
std::copy(right_begin, right_end, right_begin + delta_size);
|
||||
} else if (delta_size > 0) {
|
||||
std::copy_backward(right_begin, right_end, right_end + delta_size);
|
||||
}
|
||||
std::copy_n(str, len, this->begin() + index);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, const char *str) {
|
||||
return this->replace(index, count, str, detail::strlen(str));
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, const std::string_view &str) {
|
||||
return this->replace(index, count, str.data(), str.length());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, const std::string &str) {
|
||||
return this->replace(index, count, str.data(), str.length());
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::replace(size_type index, size_type count, const string_literal<M> &str) {
|
||||
return this->replace(index, count, str.data(), str.length());
|
||||
}
|
||||
|
||||
|
||||
//--------------[ operators ]--------------//
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic bool string_literal<N>::operator==(const std::string &str) const {
|
||||
const auto o_length = static_cast<sl_ssize_t>(str.length());
|
||||
for (size_type i = 0; i < o_length; i++) {
|
||||
if (m_value[i] == '\0' or m_value[i] != str[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic bool string_literal<N>::operator==(const std::string_view &str) const {
|
||||
const auto o_length = static_cast<sl_ssize_t>(str.length());
|
||||
for (size_type i = 0; i < o_length; i++) {
|
||||
if (m_value[i] == '\0' or m_value[i] != str[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_nic bool string_literal<N>::operator==(const char* str) const {
|
||||
size_type i = 0;
|
||||
do {
|
||||
if (i == max_size) [[unlikely]]
|
||||
return true;
|
||||
if (m_value[i] != str[i])
|
||||
return false;
|
||||
} while (m_value[i++] != '\0');
|
||||
return true;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_nic bool string_literal<N>::operator==(const string_literal<M> &str) const {
|
||||
return (*this) == str.c_str();
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<bool KnownToFit>
|
||||
ztu_ic void string_literal<N>::assign(const char* str, size_type len) {
|
||||
if constexpr (not KnownToFit) {
|
||||
ZTU_ASSERT(len <= max_size, std::length_error("given string exceeds capacity"));
|
||||
}
|
||||
std::copy_n(str, len, m_value.begin());
|
||||
m_value[len] = '\0';
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator=(const std::string &str) {
|
||||
assign(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator=(const std::string_view &str) {
|
||||
assign(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator=(const char* str) {
|
||||
assign(str, detail::strlen(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator=(const string_literal<M> &str) {
|
||||
assign<true>(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic void string_literal<N>::append(const char* str, size_type len) {
|
||||
const auto m_length = this->length();
|
||||
ZTU_ASSERT(len <= (max_size - m_length), std::length_error("given string exceeds available capacity"));
|
||||
std::copy_n(str, len, this->begin() + m_length);
|
||||
m_value[m_length + len] = '\0';
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator+=(const std::string &str) {
|
||||
append(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator+=(const std::string_view &str) {
|
||||
append(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator+=(const char* str) {
|
||||
append(str, detail::strlen(str));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_ic string_literal<N>::this_type& string_literal<N>::operator+=(const string_literal<M> &str) {
|
||||
append(str.data(), str.length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<sl_ssize_t N> requires (N > 0)
|
||||
template<sl_ssize_t M>
|
||||
ztu_nic string_literal<M + N - 1> string_literal<N>::operator+(const string_literal<M> &str) const {
|
||||
string_literal<N + M - 1> combined{};
|
||||
|
||||
const auto m_length = this->length();
|
||||
const auto o_length = str.length();
|
||||
|
||||
std::copy_n(this->begin(), m_length, combined.begin());
|
||||
std::copy_n(str.begin(), o_length + 1, combined.begin() + m_length); // copy termination
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
template<sl_ssize_t M>
|
||||
inline std::ostream& operator<<(std::ostream &out, const string_literal<M>& str) {
|
||||
return out << str.c_str();
|
||||
}
|
||||
|
||||
namespace string_literals {
|
||||
template<string_literal Str>
|
||||
constexpr auto operator"" _sl() {
|
||||
return Str;
|
||||
}
|
||||
} // string_literals
|
||||
|
||||
#undef ztu_ic
|
||||
#undef ztu_nic
|
||||
|
||||
} // ztu
|
||||
35
include/util/string_lookup.hpp
Normal file
35
include/util/string_lookup.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ztu
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
struct string_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
using hash_type = std::hash<std::string_view>;
|
||||
|
||||
[[nodiscard]] std::size_t operator()(const char *txt) const
|
||||
{
|
||||
return hash_type{}(txt);
|
||||
}
|
||||
[[nodiscard]] std::size_t operator()(std::string_view txt) const
|
||||
{
|
||||
return hash_type{}(txt);
|
||||
}
|
||||
[[nodiscard]] std::size_t operator()(const std::string &txt) const
|
||||
{
|
||||
return hash_type{}(txt);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
using string_lookup = std::unordered_map<std::string, T, detail::string_hash, std::equal_to<>>;
|
||||
|
||||
}
|
||||
90
include/util/uix.hpp
Executable file
90
include/util/uix.hpp
Executable file
@@ -0,0 +1,90 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <unistd.h>
|
||||
#include <limits>
|
||||
#include <bit>
|
||||
#include <concepts>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
namespace ztu {
|
||||
|
||||
namespace uix {
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
using usize = std::size_t;
|
||||
|
||||
using i8 = std::int8_t;
|
||||
using i16 = std::int16_t;
|
||||
using i32 = std::int32_t;
|
||||
using i64 = std::int64_t;
|
||||
using isize = ssize_t;
|
||||
|
||||
[[maybe_unused]] static constexpr auto u8_max = std::numeric_limits<u8>::max();
|
||||
[[maybe_unused]] static constexpr auto u16_max = std::numeric_limits<u16>::max();
|
||||
[[maybe_unused]] static constexpr auto u32_max = std::numeric_limits<u32>::max();
|
||||
[[maybe_unused]] static constexpr auto u64_max = std::numeric_limits<u64>::max();
|
||||
[[maybe_unused]] static constexpr auto usize_max = std::numeric_limits<usize>::max();
|
||||
|
||||
[[maybe_unused]] static constexpr auto i8_max = std::numeric_limits<i8>::max();
|
||||
[[maybe_unused]] static constexpr auto i16_max = std::numeric_limits<i16>::max();
|
||||
[[maybe_unused]] static constexpr auto i32_max = std::numeric_limits<i32>::max();
|
||||
[[maybe_unused]] static constexpr auto i64_max = std::numeric_limits<i64>::max();
|
||||
[[maybe_unused]] static constexpr auto isize_max = std::numeric_limits<isize>::max();
|
||||
|
||||
[[maybe_unused]] static constexpr auto u8_min = std::numeric_limits<u8>::min();
|
||||
[[maybe_unused]] static constexpr auto u16_min = std::numeric_limits<u16>::min();
|
||||
[[maybe_unused]] static constexpr auto u32_min = std::numeric_limits<u32>::min();
|
||||
[[maybe_unused]] static constexpr auto u64_min = std::numeric_limits<u64>::min();
|
||||
[[maybe_unused]] static constexpr auto usize_min = std::numeric_limits<usize>::min();
|
||||
|
||||
[[maybe_unused]] static constexpr auto i8_min = std::numeric_limits<i8>::min();
|
||||
[[maybe_unused]] static constexpr auto i16_min = std::numeric_limits<i16>::min();
|
||||
[[maybe_unused]] static constexpr auto i32_min = std::numeric_limits<i32>::min();
|
||||
[[maybe_unused]] static constexpr auto i64_min = std::numeric_limits<i64>::min();
|
||||
[[maybe_unused]] static constexpr auto isize_min = std::numeric_limits<isize>::min();
|
||||
|
||||
} // namespace ztu
|
||||
|
||||
using namespace uix;
|
||||
|
||||
namespace detail {
|
||||
template<isize Index, typename... Ts>
|
||||
struct pack_at {};
|
||||
|
||||
template<isize Index, typename T, typename... Ts> requires (Index > 0)
|
||||
struct pack_at<Index, T, Ts...> {
|
||||
using type = pack_at<Index - 1, Ts...>::type;
|
||||
};
|
||||
|
||||
template<typename T, typename... Ts>
|
||||
struct pack_at<0, T, Ts...> {
|
||||
using type = T;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template<usize MinBytes>
|
||||
using uint_t = detail::pack_at<std::bit_width(2 * MinBytes - 1) - 1, u8, u16, u32, u64>::type;
|
||||
|
||||
template<std::integral auto N> requires (N > 0)
|
||||
using uint_holding = uint_t<(std::bit_width(static_cast<u64>(N)) + 7) / 8>;
|
||||
|
||||
|
||||
template<usize MinBytes>
|
||||
using int_t = detail::pack_at<std::bit_width(2 * MinBytes - 1) - 1, i8, i16, i32, i64>::type;
|
||||
|
||||
template<std::integral auto N> requires (N != 0)
|
||||
using int_holding = int_t<
|
||||
std::bit_width(
|
||||
N < 0 ?
|
||||
std::max(static_cast<u64>(-(N + 1)), static_cast<u64>(1)) :
|
||||
static_cast<u64>(N)
|
||||
) / 8 + 1
|
||||
>;
|
||||
|
||||
} // namespace ztu
|
||||
19
include/util/unroll_bool_template.hpp
Normal file
19
include/util/unroll_bool_template.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
template<typename F, bool... Values>
|
||||
auto unroll_bool_function_template(F&& f)
|
||||
{
|
||||
return f.template operator()<Values...>();
|
||||
}
|
||||
|
||||
template<typename F, bool... Values, typename Bool, typename... Bools>
|
||||
auto unroll_bool_function_template(F&& f, const Bool b, const Bools... bs)
|
||||
{
|
||||
if (b)
|
||||
{
|
||||
return unroll_bool_function_template<F, Values..., true>(std::forward<F>(f), bs...);
|
||||
}
|
||||
return unroll_bool_function_template<F, Values..., false>(std::forward<F>(f), bs...);
|
||||
}
|
||||
Reference in New Issue
Block a user