mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
453 lines
14 KiB
C++
453 lines
14 KiB
C++
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2025 by Ronald "Eidolon" Kinard
|
|
// Copyright (C) 2025 by Kart Krew
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifndef SRB2_CORE_STRING_HPP
|
|
#define SRB2_CORE_STRING_HPP
|
|
|
|
#ifdef __cplusplus
|
|
|
|
#include <cstdint>
|
|
#include <initializer_list>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <type_traits>
|
|
|
|
#include "fmt/core.h"
|
|
|
|
#include "static_vec.hpp"
|
|
#include "vector.hpp"
|
|
|
|
namespace srb2
|
|
{
|
|
|
|
class Utf8Iter
|
|
{
|
|
public:
|
|
using difference_type = size_t;
|
|
using value_type = uint32_t;
|
|
using pointer = void;
|
|
using reference = void;
|
|
using iterator_category = std::input_iterator_tag;
|
|
|
|
private:
|
|
size_t i_;
|
|
std::string_view s_;
|
|
|
|
Utf8Iter(std::string_view s, size_t i) : i_(i), s_(s) {}
|
|
|
|
friend class String;
|
|
uint32_t do_codepoint() const;
|
|
|
|
public:
|
|
Utf8Iter() = default;
|
|
Utf8Iter(const Utf8Iter&) = default;
|
|
Utf8Iter(Utf8Iter&&) noexcept = default;
|
|
~Utf8Iter() = default;
|
|
Utf8Iter& operator=(const Utf8Iter&) = default;
|
|
Utf8Iter& operator=(Utf8Iter&&) noexcept = default;
|
|
|
|
static Utf8Iter begin(std::string_view s) { return Utf8Iter(s, 0); }
|
|
static Utf8Iter end(std::string_view s) { return Utf8Iter(s, s.size()); }
|
|
|
|
bool operator==(const Utf8Iter& r) const noexcept
|
|
{
|
|
return s_ == r.s_ && i_ == r.i_;
|
|
}
|
|
|
|
bool operator!=(const Utf8Iter& r) const noexcept { return !(*this == r); }
|
|
|
|
uint32_t operator*() const { return codepoint(); }
|
|
Utf8Iter& operator++()
|
|
{
|
|
i_ += size();
|
|
return *this;
|
|
}
|
|
Utf8Iter operator++(int)
|
|
{
|
|
Utf8Iter copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
uint32_t codepoint() const;
|
|
bool valid() const;
|
|
uint8_t size() const;
|
|
};
|
|
|
|
class Utf16Iter
|
|
{
|
|
public:
|
|
using difference_type = size_t;
|
|
using value_type = uint32_t;
|
|
using pointer = void;
|
|
using reference = void;
|
|
using iterator_category = std::input_iterator_tag;
|
|
|
|
private:
|
|
size_t i_;
|
|
std::u16string_view s_;
|
|
|
|
Utf16Iter(std::u16string_view s, size_t i) : i_(i), s_(s) {}
|
|
|
|
uint32_t do_codepoint() const;
|
|
|
|
public:
|
|
Utf16Iter() = default;
|
|
Utf16Iter(const Utf16Iter&) = default;
|
|
Utf16Iter(Utf16Iter&&) noexcept = default;
|
|
~Utf16Iter() = default;
|
|
Utf16Iter& operator=(const Utf16Iter&) = default;
|
|
Utf16Iter& operator=(Utf16Iter&&) = default;
|
|
|
|
static Utf16Iter begin(std::u16string_view s) { return Utf16Iter(s, 0); }
|
|
static Utf16Iter end(std::u16string_view s) { return Utf16Iter(s, s.size()); }
|
|
|
|
bool operator==(const Utf16Iter& r) const noexcept
|
|
{
|
|
return s_ == r.s_ && i_ == r.i_;
|
|
}
|
|
|
|
bool operator!=(const Utf16Iter& r) const noexcept { return !(*this == r); }
|
|
|
|
uint32_t operator*() const { return codepoint(); }
|
|
Utf16Iter& operator++()
|
|
{
|
|
i_ += size();
|
|
return *this;
|
|
}
|
|
Utf16Iter operator++(int)
|
|
{
|
|
Utf16Iter copy = *this;
|
|
++*this;
|
|
return copy;
|
|
}
|
|
|
|
uint32_t codepoint() const;
|
|
bool valid() const { return true; } // we allow unpaired surrogates in general
|
|
uint8_t size() const;
|
|
};
|
|
|
|
class String
|
|
{
|
|
public:
|
|
using size_type = uint32_t;
|
|
using difference_type = int64_t;
|
|
using value_type = uint8_t;
|
|
using reference = uint8_t&;
|
|
using const_reference = const uint8_t&;
|
|
using pointer = uint8_t*;
|
|
using const_pointer = const uint8_t*;
|
|
using iterator = uint8_t*;
|
|
using const_iterator = const uint8_t*;
|
|
|
|
private:
|
|
srb2::Vector<uint8_t> data_;
|
|
|
|
public:
|
|
|
|
friend struct std::hash<String>;
|
|
|
|
static constexpr const size_type npos = -1;
|
|
|
|
String() = default;
|
|
String(const String&);
|
|
String(String&&) noexcept;
|
|
~String();
|
|
String& operator=(const String&);
|
|
String& operator=(String&&) noexcept;
|
|
|
|
String(const char* s);
|
|
String(const char* s, size_t len);
|
|
String(const std::string&);
|
|
|
|
explicit String(std::string_view view);
|
|
|
|
operator std::string() const;
|
|
operator std::string_view() const;
|
|
|
|
size_type size() const noexcept;
|
|
bool empty() const noexcept { return data_.empty(); }
|
|
const char* c_str() const;
|
|
uint8_t* data() noexcept { return data_.data(); }
|
|
const uint8_t* data() const noexcept { return data_.data(); }
|
|
void reserve(size_type capacity);
|
|
|
|
uint8_t* begin() noexcept;
|
|
uint8_t* end() noexcept;
|
|
const uint8_t* cbegin() const noexcept;
|
|
const uint8_t* cend() const noexcept;
|
|
const uint8_t* begin() const noexcept { return cbegin(); }
|
|
const uint8_t* end() const noexcept { return cend(); }
|
|
|
|
uint8_t& at(size_type i);
|
|
const uint8_t& at(size_type i) const;
|
|
uint8_t& operator[](size_type i) { return data_[i]; }
|
|
const uint8_t& operator[](size_type i) const { return data_[i]; }
|
|
uint8_t& front() { return data_[0]; }
|
|
const uint8_t& front() const { return data_[0]; }
|
|
uint8_t& back() { return data_[size() - 1]; }
|
|
const uint8_t& back() const { return data_[size() - 1]; }
|
|
|
|
void clear() { data_.clear(); }
|
|
String& insert(size_type index, size_type count, uint8_t ch);
|
|
String& insert(size_type index, const char* s);
|
|
String& insert(size_type index, const char* s, size_type count);
|
|
String& insert(size_type index, std::string_view str);
|
|
String& insert(size_type index, std::string_view str, size_t s_index, size_t count = npos);
|
|
iterator insert(const_iterator pos, uint8_t ch);
|
|
iterator insert(const_iterator pos, size_type count, uint8_t ch);
|
|
|
|
template <
|
|
typename InputIt,
|
|
typename std::enable_if_t<
|
|
std::is_constructible<
|
|
uint8_t,
|
|
typename std::iterator_traits<InputIt>::reference
|
|
>::value,
|
|
int
|
|
> = 0
|
|
>
|
|
iterator insert(const_iterator pos, InputIt first, InputIt last)
|
|
{
|
|
size_type offset = pos - data();
|
|
if (!empty())
|
|
{
|
|
// remove null byte
|
|
data_.pop_back();
|
|
}
|
|
auto ret = data_.insert(pos, first, last);
|
|
if (data_.size() > 0)
|
|
{
|
|
data_.push_back(0);
|
|
}
|
|
return data() + offset;
|
|
}
|
|
|
|
iterator insert(const_iterator pos, std::initializer_list<uint8_t> list);
|
|
String& erase(size_type index = 0, size_type count = npos);
|
|
iterator erase(const_iterator position);
|
|
iterator erase(const_iterator first, const_iterator last);
|
|
void push_back(uint8_t v);
|
|
void pop_back();
|
|
String& append(size_type count, uint8_t ch);
|
|
String& append(const char* s, size_type count);
|
|
String& append(const char* s);
|
|
String& append(std::string_view str);
|
|
String& append(std::string_view str, size_type pos, size_type count = npos);
|
|
|
|
template <
|
|
typename InputIt,
|
|
typename std::enable_if_t<
|
|
std::is_constructible<
|
|
uint8_t,
|
|
typename std::iterator_traits<InputIt>::reference
|
|
>::value,
|
|
int
|
|
> = 0
|
|
>
|
|
String& append(InputIt first, InputIt last)
|
|
{
|
|
if (!empty())
|
|
{
|
|
// remove null byte
|
|
data_.pop_back();
|
|
}
|
|
for (; first != last; first++)
|
|
{
|
|
data_.push_back(*first);
|
|
}
|
|
if (data_.size() > 0)
|
|
{
|
|
data_.push_back(0);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String& append(std::initializer_list<uint8_t> ilist);
|
|
String& operator+=(std::string_view r);
|
|
String& operator+=(const char* r);
|
|
String& operator+=(uint8_t r);
|
|
String& operator+=(std::initializer_list<uint8_t> r);
|
|
String& replace(size_type pos, size_type count, std::string_view str);
|
|
String& replace(const_iterator first, const_iterator last, std::string_view str);
|
|
String& replace(size_type pos, size_type count, std::string_view str, size_t pos2, size_t count2 = -1);
|
|
String& replace(size_type pos, size_type count, const char* cstr, size_type count2);
|
|
String& replace(const_iterator first, const_iterator last, const char* cstr, size_type count2);
|
|
String& replace(size_type pos, size_type count, const char* cstr);
|
|
String& replace(const_iterator first, const_iterator last, const char* cstr);
|
|
// String& replace(size_type pos, size_type count, size_type count2, uint8_t ch);
|
|
String& replace(const_iterator first, const_iterator last, uint8_t ch);
|
|
|
|
template <
|
|
typename InputIt,
|
|
typename std::enable_if_t<
|
|
std::is_constructible<
|
|
uint8_t,
|
|
typename std::iterator_traits<InputIt>::reference
|
|
>::value,
|
|
int
|
|
> = 0
|
|
>
|
|
String& replace(const_iterator first, const_iterator last, InputIt first2, InputIt last2)
|
|
{
|
|
for (; first != last && first2 != last2; first++, first2++)
|
|
{
|
|
*const_cast<iterator>(first) = *first2;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
String& replace(const_iterator first, const_iterator last, std::initializer_list<uint8_t> ilist);
|
|
size_type copy(uint8_t* dest, size_type count, size_type pos = 0) const;
|
|
size_type copy(char* dest, size_type count, size_type pos = 0) const;
|
|
void resize(size_type count);
|
|
void resize(size_type count, uint8_t ch);
|
|
void swap(String& other) noexcept;
|
|
|
|
size_type find(const String& str, size_type pos = 0) const;
|
|
size_type find(std::string_view str, size_type pos = 0) const;
|
|
size_type find(const char* s, size_type pos, size_t count) const;
|
|
size_type find(const char* s, size_type pos = 0) const;
|
|
size_type find(uint8_t ch, size_type pos = 0) const;
|
|
size_type rfind(const String& str, size_type pos = npos) const;
|
|
size_type rfind(std::string_view str, size_type pos = npos) const;
|
|
size_type rfind(const char* s, size_type pos, size_type count) const;
|
|
size_type rfind(const char* s, size_type pos = npos) const;
|
|
size_type rfind(uint8_t ch, size_type pos = npos) const;
|
|
// size_type find_first_of(std::string_view str, size_type pos = 0) const;
|
|
// size_type find_first_of(const char* s, size_type pos, size_type count) const;
|
|
// size_type find_first_of(const char* s, size_type pos = 0) const;
|
|
// size_type find_first_of(uint8_t ch, size_type pos = 0) const;
|
|
// size_type find_first_not_of(std::string_view str, size_type pos = 0) const;
|
|
// size_type find_first_not_of(const char* s, size_type pos, size_type count) const;
|
|
// size_type find_first_not_of(const char* s, size_type pos = 0) const;
|
|
// size_type find_first_not_of(uint8_t ch, size_type pos = 0) const;
|
|
// size_type find_last_of(std::string_view str, size_type pos = npos) const;
|
|
// size_type find_last_of(const char* s, size_type pos, size_type count) const;
|
|
// size_type find_last_of(const char* s, size_type pos = npos) const;
|
|
// size_type find_last_of(uint8_t ch, size_type pos = npos) const;
|
|
// size_type find_last_not_of(std::string_view str, size_type pos = npos) const;
|
|
// size_type find_last_not_of(const char* s, size_type pos, size_type count) const;
|
|
// size_type find_last_not_of(const char* s, size_type pos = npos) const;
|
|
// size_type find_last_not_of(uint8_t ch, size_type pos = npos) const;
|
|
|
|
int compare(std::string_view str) const noexcept;
|
|
// int compare(size_type pos1, size_type count1, std::string_view str) const;
|
|
// int compare(size_type pos1, size_type count1, std::string_view str, size_type pos2, size_type count2 = npos) const;
|
|
int compare(const char* s) const;
|
|
// int compare(size_type pos1, size_type count1, const char* s) const;
|
|
// int compare(size_type pos1, size_type count1, const char* s, size_type count2) const;
|
|
String substr(size_type pos = 0, size_type count = npos) const;
|
|
|
|
// Non-STL String functions
|
|
bool valid_utf8() const noexcept;
|
|
srb2::Vector<uint16_t> to_utf16() const;
|
|
srb2::Vector<uint32_t> to_utf32() const;
|
|
size_t length_decoded() const;
|
|
|
|
Utf8Iter decode_begin() const { return Utf8Iter(*this, 0); }
|
|
Utf8Iter decode_end() const { return Utf8Iter(*this, size()); };
|
|
};
|
|
|
|
String operator+(const String& lhs, const String& rhs);
|
|
String operator+(const String& lhs, const char* rhs);
|
|
String operator+(const String& lhs, uint8_t rhs);
|
|
String operator+(const String& lhs, std::string_view view);
|
|
|
|
bool operator==(const String& lhs, const String& rhs);
|
|
bool operator==(const String& lhs, const char* rhs);
|
|
// bool operator==(const String& lhs, std::string_view rhs);
|
|
bool operator!=(const String& lhs, const String& rhs);
|
|
bool operator!=(const String& lhs, const char* rhs);
|
|
// bool operator!=(const String& lhs, std::string_view rhs);
|
|
bool operator<(const String& lhs, const String& rhs);
|
|
bool operator<(const String& lhs, const char* rhs);
|
|
// bool operator<(const String& lhs, std::string_view rhs);
|
|
bool operator<=(const String& lhs, const String& rhs);
|
|
bool operator<=(const String& lhs, const char* rhs);
|
|
// bool operator<=(const String& lhs, std::string_view rhs);
|
|
bool operator>(const String& lhs, const String& rhs);
|
|
bool operator>(const String& lhs, const char* rhs);
|
|
// bool operator>(const String& lhs, std::string_view rhs);
|
|
bool operator>=(const String& lhs, const String& rhs);
|
|
bool operator>=(const String& lhs, const char* rhs);
|
|
// bool operator>=(const String& lhs, std::string_view rhs);
|
|
|
|
Vector<uint16_t> to_utf16(std::string_view utf8);
|
|
Vector<uint32_t> to_utf32(std::string_view utf8);
|
|
|
|
srb2::StaticVec<uint8_t, 4> to_utf8(uint32_t codepoint);
|
|
|
|
template <
|
|
typename ItUTF32,
|
|
typename std::enable_if_t<
|
|
std::is_constructible<
|
|
uint32_t,
|
|
typename std::iterator_traits<ItUTF32>::reference
|
|
>::value,
|
|
int
|
|
> = 0
|
|
>
|
|
srb2::String to_utf8(ItUTF32 begin, ItUTF32 end)
|
|
{
|
|
srb2::String ret;
|
|
for (auto itr = begin; itr != end; ++itr)
|
|
{
|
|
srb2::StaticVec<uint8_t, 4> utf8 = to_utf8(*itr);
|
|
ret.append(utf8.begin(), utf8.end());
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
srb2::String to_utf8(std::u32string_view utf32view);
|
|
|
|
// fmtlib
|
|
inline auto format_as(const String& str) { return fmt::string_view(static_cast<std::string_view>(str)); }
|
|
|
|
srb2::String vformat(fmt::string_view fmt, fmt::format_args args);
|
|
|
|
template <typename... T>
|
|
inline auto format(fmt::format_string<T...> fmt, T&&... args)
|
|
{
|
|
return ::srb2::vformat(fmt, fmt::vargs<T...>{{args...}});
|
|
}
|
|
|
|
} // namespace srb2
|
|
|
|
namespace std
|
|
{
|
|
|
|
template <> struct hash<srb2::String>
|
|
{
|
|
size_t operator()(const srb2::String& v);
|
|
};
|
|
|
|
} // namespace std
|
|
|
|
#endif // __cplusplus
|
|
|
|
#ifndef __cplusplus
|
|
#include <stdint.h>
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
int Str_IsValidUTF8(const char* str);
|
|
// int Str_ToUTF16(uint16_t* dst, size_t dstlen, const char* src);
|
|
// int Str_ToUTF32(uint32_t* dst, size_t dstlen, const char* src);
|
|
uint32_t Str_NextCodepointFromUTF8(const char** itr);
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif
|
|
|
|
#endif // SRB2_CORE_STRING_HPP
|