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 constexpr String(const char (&str)[Size]) : m_data(reinterpret_cast(&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 { static constexpr bool is_valid = true; }; static_assert(Moveable); static_assert(Copyable); static_assert(Equivalence); } // namespace br