2025-07-25 22:49:56 +03:00

227 lines
7.2 KiB
C++

/**
* @file stb.hh
* @author Cristei Gabriel-Marian (cristei.g772@gmail.com)
* @brief Compile-time String To Bytes (STB)
* @version 1.0
* @date 2023-03-23
*
* Last update: 03/23/2023 (mm/dd/yyyy): [Breaking update]
* Modernize, undo some cancer, change some naming, file structure,
* implement tests directly in file.
*
*/
#ifndef STB_DEFINED
#define STB_DEFINED
#include <cstdint>
#include <array>
namespace stb {
namespace detail {
// detail methods assume null terminator.
template<std::size_t N>
constexpr auto find_first_of_start(std::array<char, N> const& data, std::size_t start, char ch) noexcept {
std::size_t idx = start;
while (data[idx] != ch && idx < N)
++idx;
return idx;
}
template<std::size_t N>
constexpr auto find_first_not_of_start(std::array<char, N> const& data, std::size_t start, char ch) noexcept {
if (start < N && data[start] != ch)
return start;
std::size_t idx = start;
while (data[idx] == ch && idx < N)
++idx;
return idx;
}
template<std::size_t N>
constexpr auto find_last_of(std::array<char, N> const& data, char ch) noexcept {
std::size_t idx = data.size() - 2;
while (data[idx] != ch && idx >= 0)
--idx;
return idx;
}
template<std::size_t N>
constexpr auto find_last_not_of(std::array<char, N> const& data, char ch) noexcept {
std::size_t idx = data.size() - 2;
while (data[idx] == ch && idx >= 0)
--idx;
return idx;
}
constexpr auto char_to_hex(char ch) noexcept {
if (ch >= '0' && ch <= '9')
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
return ch - 'a' + 10;
}
template<typename T, T F = 16>
constexpr T concat_hex(T lhs, T rhs) noexcept {
return F * lhs + rhs;
}
} // namespace detail
template<auto V>
struct consteval_value {
constexpr static decltype(V) value = V;
};
template<std::size_t N>
struct fixed_string: public std::array<char, N + 1> {
using std::array<char, N + 1>::array;
constexpr fixed_string(const char* str) noexcept
: std::array<char, N + 1>() {
for (auto i = 0; i != N; ++i)
(*this)[i] = str[i];
}
};
template<std::size_t N>
fixed_string(const char (&)[N]) noexcept -> fixed_string<N - 1>;
template<char delimiter, char mask, typename element_type, element_type masked>
struct basic_hex_string_array_conversion {
template<fixed_string str>
struct build {
private:
struct parse {
struct result {
std::size_t delimiter_count;
std::size_t start;
std::size_t next;
std::size_t end;
};
constexpr static auto get() noexcept {
std::size_t count = 1;
constexpr std::size_t start = detail::find_first_not_of_start(str, 0, delimiter);
constexpr std::size_t next = detail::find_first_of_start(str, start, delimiter);
constexpr std::size_t end = detail::find_last_not_of(str, delimiter);
bool previous_delimiter = false;
for (auto i = next; i < end; ++i) {
if (str[i] == delimiter) {
if (!previous_delimiter)
++count;
previous_delimiter = true;
} else
previous_delimiter = false;
}
return result {
count,
start,
next,
end};
}
};
constexpr static auto make() noexcept {
constexpr auto data = parse::get();
constexpr auto count = data.delimiter_count;
constexpr auto start = data.start;
constexpr auto next = data.next;
constexpr auto end = data.end;
std::array<element_type, count> result = {};
std::array<std::size_t, count> skips = {};
std::size_t skipped = 0;
std::size_t traversed = start;
bool previous_skip = false;
for (auto i = start; i < end; ++i) {
if (str[i] == delimiter) {
if (!previous_skip)
skips[skipped++] = traversed;
previous_skip = true;
} else
previous_skip = false;
++traversed;
}
bool one_char = str[start + 1] == delimiter;
result[0] = static_cast<element_type>(str[start] == mask ? masked : (one_char ? detail::char_to_hex(str[start]) : detail::concat_hex(detail::char_to_hex(str[start]), detail::char_to_hex(str[start + 1]))));
std::size_t conversions = 1;
for (auto i = next; i < end; ++i) {
for (auto entry : skips) {
if (entry == i && entry < end) {
std::size_t idx = detail::find_first_not_of_start(str, i + 1, delimiter);
one_char = str[idx + 1] == delimiter;
result[conversions++] = static_cast<element_type>(str[idx] == mask ? masked : (one_char ? detail::char_to_hex(str[idx]) : detail::concat_hex(detail::char_to_hex(str[idx]), detail::char_to_hex(str[idx + 1]))));
}
}
}
return result;
}
public:
constexpr static auto value = consteval_value<make()>::value;
};
};
using hex_string_array_conversion = basic_hex_string_array_conversion<' ', '?', int, -1>;
using simple_conversion = hex_string_array_conversion;
} // namespace stb
#ifndef STB_OMIT_TESTS
struct _ignore_me_stb_compliance_tests {
using conv_type = stb::simple_conversion;
constexpr static auto value_1 = conv_type::build<"AA BB CC DD EE FF">::value;
static_assert(value_1[0] == 0xAA);
static_assert(value_1[1] == 0xBB);
static_assert(value_1[2] == 0xCC);
static_assert(value_1[3] == 0xDD);
static_assert(value_1[4] == 0xEE);
static_assert(value_1[5] == 0xFF);
static_assert(value_1.size() == 6);
constexpr static auto value_2 = conv_type::build<" C 0f C a B ef ">::value;
static_assert(value_2[0] == 0x0C);
static_assert(value_2[1] == 0x0F);
static_assert(value_2[2] == 0x0C);
static_assert(value_2[3] == 0x0A);
static_assert(value_2[4] == 0x0B);
static_assert(value_2[5] == 0xEF);
static_assert(value_2.size() == 6);
constexpr static auto value_3 = conv_type::build<"AA bb CC dd ">::value;
static_assert(value_3[0] == 0xAA);
static_assert(value_3[1] == 0xBB);
static_assert(value_3[2] == 0xCC);
static_assert(value_3[3] == 0xDD);
static_assert(value_3.size() == 4);
constexpr static auto value_4 = conv_type::build<" aa bb ee ff">::value;
static_assert(value_4[0] == 0xAA);
static_assert(value_4[1] == 0xBB);
static_assert(value_4[2] == 0xEE);
static_assert(value_4[3] == 0xFF);
static_assert(value_4.size() == 4);
};
#endif
#endif