bedrock/src/foundation/string.cppm
2025-06-07 14:17:56 -06:00

130 lines
2.9 KiB
C++

export module bedrock.foundation:string;
import bedrock.numbers;
import bedrock.semantics;
import :byte;
import :buffer;
import :bytes_iterator;
import :iterators.enumerate;
import :iterators.reverse;
namespace br {
export class String final {
private:
BufferView m_data;
explicit String(BufferView&& view)
: m_data(view) {}
public:
friend class StringBuffer;
template <usize Size>
constexpr String(const char (&str)[Size])
: m_data(reinterpret_cast<const byte*>(&str), Size - 1) {}
[[nodiscard]]
auto byteSize() const noexcept -> usize {
return m_data.size();
}
[[nodiscard]]
auto characterLength() const noexcept -> usize {
usize nCharacters = 0;
for (auto byte : m_data) {
if ((byte & 0xc0_b) != 0x80_b) {
++nCharacters;
}
}
return nCharacters;
}
[[nodiscard]]
auto isEmpty() const noexcept -> bool {
return *this == "";
}
[[nodiscard]]
auto startsWith(const String& str) noexcept -> bool {
for (auto [byte, i] : str.bytes() | enumerate()) {
if (m_data[i] != byte) {
return false;
}
}
return true;
}
[[nodiscard]]
auto endsWith(const String& str) noexcept -> bool {
for (auto [byte, i] : str.bytes() | reverse() | enumerate()) {
if (m_data[i] != byte) {
return false;
}
}
return true;
}
// ====================================================================
// Implicit conversions
// ====================================================================
[[nodiscard]] operator BufferView() const noexcept {
return m_data;
}
[[nodiscard]]
auto bytes() const noexcept -> BytesIterator {
return m_data.bytes();
}
// ====================================================================
// Moveable
// ====================================================================
String(String&& other) noexcept
: m_data(move(other.m_data)) {}
auto operator=(String&& other) noexcept -> String& {
if (this == &other) {
m_data = move(other.m_data);
}
return *this;
}
~String() = default;
// ====================================================================
// Copyable
// ====================================================================
String(const String& other) = default;
auto operator=(const String& other) noexcept -> String& {
if (&other != this) {
m_data = other.m_data;
}
return *this;
}
// ====================================================================
// Equivalence
// ====================================================================
[[nodiscard]]
auto operator==(const String& other) const noexcept -> bool {
return m_data == other.m_data;
}
[[nodiscard]]
auto operator!=(const String& other) const noexcept -> bool {
return !(*this == other);
}
};
template <>
struct EquivalenceType<String> {
static constexpr bool is_valid = true;
};
static_assert(Moveable<String>);
static_assert(Copyable<String>);
static_assert(Equivalence<String>);
} // namespace br