#pragma once #include #include #include #include #include #include #include #include namespace utils::string { #ifdef __clang__ __attribute__((__format__(__printf__, 1, 2))) #endif const char* va(const char* format, ...); template requires(std::is_trivially_copyable_v) // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) void copy(T (&array)[Size], const T* str) { if constexpr (Size == 0) { return; } const auto size = std::min(Size, std::char_traits::length(str)); memcpy(array, str, size * sizeof(T)); array[std::min(Size - 1, size)] = {}; } inline char char_to_lower(const char val) { return static_cast(std::tolower(static_cast(val))); } inline wchar_t char_to_lower(const wchar_t val) { return static_cast(std::towlower(val)); } inline char16_t char_to_lower(const char16_t val) { static_assert(sizeof(char16_t) <= sizeof(wchar_t)); static_assert(sizeof(char16_t) == sizeof(uint16_t)); return static_cast(char_to_lower(static_cast(static_cast(val)))); } template void to_lower_inplace(std::basic_string& str) { std::ranges::transform(str, str.begin(), [](const Elem e) { return char_to_lower(e); }); } template std::basic_string to_lower(std::basic_string str) { to_lower_inplace(str); return str; } template std::basic_string to_lower_consume(std::basic_string& str) { return to_lower(std::move(str)); } inline char to_nibble(std::byte value, const bool uppercase = false) { value = value & static_cast(0xF); if (value <= static_cast(9)) { return static_cast('0' + static_cast(value)); } return static_cast((uppercase ? 'A' : 'a') + (static_cast(value) - 0xA)); } inline std::pair to_hex(const std::byte value, const bool uppercase = false) { return {to_nibble(value >> 4, uppercase), to_nibble(value, uppercase)}; } inline std::string to_hex_string(const void* data, const size_t size, const bool uppercase = false) { std::string result{}; result.reserve(size * 2); for (size_t i = 0; i < size; ++i) { const auto value = static_cast(data)[i]; const auto [high, low] = to_hex(value, uppercase); result.push_back(high); result.push_back(low); } return result; } template requires(std::is_integral_v) std::string to_hex_number(const Integer& i, const bool uppercase = false) { std::string res{}; res.reserve(sizeof(i) * 2); const std::span data{reinterpret_cast(&i), sizeof(i)}; for (const auto value : data) { const auto [high, low] = to_hex(value, uppercase); res.insert(res.begin(), {high, low}); } while (res.size() > 1 && res.front() == '0') { res.erase(res.begin()); } return res; } template requires(std::is_integral_v) std::string to_hex_string(const Integer& i, const bool uppercase = false) { return to_hex_string(&i, sizeof(Integer), uppercase); } inline std::string to_hex_string(const std::span data, const bool uppercase = false) { return to_hex_string(data.data(), data.size(), uppercase); } inline std::byte parse_nibble(const char nibble) { const auto lower = char_to_lower(nibble); if (lower >= '0' && lower <= '9') { return static_cast(lower - '0'); } if (lower >= 'a' && lower <= 'f') { return static_cast(10 + (lower - 'a')); } return static_cast(0); } inline std::vector from_hex_string(const std::string_view str) { const auto size = str.size() / 2; std::vector data{}; data.reserve(size); for (size_t i = 0; i < size; ++i) { const auto high = parse_nibble(str[i * 2 + 0]); const auto low = parse_nibble(str[i * 2 + 1]); const auto value = (high << 4) | low; data.push_back(value); } return data; } template bool equals_ignore_case(const std::basic_string& lhs, const std::basic_string& rhs) { return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); }); } template bool equals_ignore_case(const std::basic_string_view& lhs, const std::basic_string_view& rhs) { return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); }); } }