mirror of
https://github.com/momo5502/emulator.git
synced 2026-01-18 03:13:55 +00:00
186 lines
5.5 KiB
C++
186 lines
5.5 KiB
C++
#pragma once
|
|
#include <span>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cwctype>
|
|
#include <algorithm>
|
|
#include <string_view>
|
|
|
|
namespace utils::string
|
|
{
|
|
#ifdef __clang__
|
|
__attribute__((__format__(__printf__, 1, 2)))
|
|
#endif
|
|
const char*
|
|
va(const char* format, ...);
|
|
|
|
template <typename T, size_t Size>
|
|
requires(std::is_trivially_copyable_v<T>)
|
|
// 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<T>::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<char>(std::tolower(static_cast<unsigned char>(val)));
|
|
}
|
|
|
|
inline wchar_t char_to_lower(const wchar_t val)
|
|
{
|
|
return static_cast<wchar_t>(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<char16_t>(char_to_lower(static_cast<wchar_t>(static_cast<uint16_t>(val))));
|
|
}
|
|
|
|
template <class Elem, class Traits, class Alloc>
|
|
void to_lower_inplace(std::basic_string<Elem, Traits, Alloc>& str)
|
|
{
|
|
std::ranges::transform(str, str.begin(), [](const Elem e) { return char_to_lower(e); });
|
|
}
|
|
|
|
template <class Elem, class Traits, class Alloc>
|
|
std::basic_string<Elem, Traits, Alloc> to_lower(std::basic_string<Elem, Traits, Alloc> str)
|
|
{
|
|
to_lower_inplace(str);
|
|
return str;
|
|
}
|
|
|
|
template <class Elem, class Traits, class Alloc>
|
|
std::basic_string<Elem, Traits, Alloc> to_lower_consume(std::basic_string<Elem, Traits, Alloc>& str)
|
|
{
|
|
return to_lower(std::move(str));
|
|
}
|
|
|
|
inline char to_nibble(std::byte value, const bool uppercase = false)
|
|
{
|
|
value = value & static_cast<std::byte>(0xF);
|
|
|
|
if (value <= static_cast<std::byte>(9))
|
|
{
|
|
return static_cast<char>('0' + static_cast<char>(value));
|
|
}
|
|
|
|
return static_cast<char>((uppercase ? 'A' : 'a') + (static_cast<char>(value) - 0xA));
|
|
}
|
|
|
|
inline std::pair<char, char> 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<const std::byte*>(data)[i];
|
|
const auto [high, low] = to_hex(value, uppercase);
|
|
result.push_back(high);
|
|
result.push_back(low);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <typename Integer>
|
|
requires(std::is_integral_v<Integer>)
|
|
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<const std::byte*>(&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 <typename Integer>
|
|
requires(std::is_integral_v<Integer>)
|
|
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<const std::byte> 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<std::byte>(lower - '0');
|
|
}
|
|
|
|
if (lower >= 'a' && lower <= 'f')
|
|
{
|
|
return static_cast<std::byte>(10 + (lower - 'a'));
|
|
}
|
|
|
|
return static_cast<std::byte>(0);
|
|
}
|
|
|
|
inline std::vector<std::byte> from_hex_string(const std::string_view str)
|
|
{
|
|
const auto size = str.size() / 2;
|
|
|
|
std::vector<std::byte> 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 <class Elem, class Traits, class Alloc>
|
|
bool equals_ignore_case(const std::basic_string<Elem, Traits, Alloc>& lhs, const std::basic_string<Elem, Traits, Alloc>& rhs)
|
|
{
|
|
return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
|
}
|
|
|
|
template <class Elem, class Traits>
|
|
bool equals_ignore_case(const std::basic_string_view<Elem, Traits>& lhs, const std::basic_string_view<Elem, Traits>& rhs)
|
|
{
|
|
return std::ranges::equal(lhs, rhs, [](const auto c1, const auto c2) { return char_to_lower(c1) == char_to_lower(c2); });
|
|
}
|
|
}
|