This commit is contained in:
ZY4N
2024-12-22 16:58:40 +01:00
parent 2704814de2
commit db8db8f9d7
161 changed files with 17102 additions and 0 deletions

285
include/util/arx.hpp Executable file
View 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

View 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 {};
}

View 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; \
}

View 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
View 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
View 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
View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

139
include/util/result.hpp Normal file
View 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;
}*/
}

View 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
View 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 };
}

View 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{};
};
}

View 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

View 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
View 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

View 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...);
}