| #pragma once |
| #include <fmt/core.h> |
| |
| #include <cstddef> |
| #include <limits> |
| #include <string> |
| #include <type_traits> |
| #ifndef NDEBUG |
| #include <stdexcept> |
| #endif |
| |
| namespace stdplus |
| { |
| namespace detail |
| { |
| |
| template <typename CharT> |
| constexpr std::ptrdiff_t zstring_find_term(const CharT* str, std::size_t min, |
| std::size_t max) noexcept |
| { |
| for (size_t i = 0; i < min; ++i) |
| { |
| if (str[i] == '\0') |
| { |
| return -1; |
| } |
| } |
| for (size_t i = min; i < max; ++i) |
| { |
| if (str[i] == '\0') |
| { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| #ifndef NDEBUG |
| template <typename CharT> |
| constexpr std::size_t zstring_validate(const CharT* str, std::size_t min, |
| std::size_t max) |
| { |
| auto ret = zstring_find_term(str, min, max); |
| if (ret < 0) |
| { |
| throw std::invalid_argument("stdplus::zstring"); |
| } |
| return ret; |
| } |
| #endif |
| |
| template <typename T, typename CharT, typename Traits> |
| struct same_string : std::false_type |
| {}; |
| |
| template <typename CharT, typename Traits, typename Allocator> |
| struct same_string<std::basic_string<CharT, Traits, Allocator>, CharT, Traits> : |
| std::true_type |
| {}; |
| |
| } // namespace detail |
| |
| /** |
| * @brief Provides a class for expressing an unbounded length string that has |
| * a nul terminating character. This is different from zstring_view in that it |
| * never computes the length, which is slightly cheaper if the string will just |
| * be downcast to a `const char *`. |
| */ |
| template <typename CharT, |
| typename Traits = std::char_traits<std::remove_const_t<CharT>>> |
| class basic_zstring |
| { |
| private: |
| using decay_t = std::remove_const_t<CharT>; |
| |
| public: |
| using traits_type = Traits; |
| using value_type = CharT; |
| using pointer = value_type*; |
| using const_pointer = const value_type*; |
| using reference = value_type&; |
| using const_reference = const value_type&; |
| using size_type = std::size_t; |
| |
| template <typename T, size_type N> |
| constexpr basic_zstring(T (&str)[N]) |
| #ifdef NDEBUG |
| noexcept |
| #endif |
| : |
| data_(str) |
| { |
| #ifndef NDEBUG |
| detail::zstring_validate(str, 0, N); |
| #endif |
| } |
| template <typename T, std::enable_if_t<std::is_pointer_v<T>, bool> = true> |
| constexpr basic_zstring(T str) noexcept : data_(str) |
| {} |
| template <typename T, |
| std::enable_if_t<detail::same_string<std::remove_cvref_t<T>, |
| decay_t, Traits>::value, |
| bool> = true> |
| constexpr basic_zstring(T&& str) |
| #ifdef NDEBUG |
| noexcept |
| #endif |
| : |
| data_(str.data()) |
| { |
| #ifndef NDEBUG |
| detail::zstring_validate(str.data(), str.size(), str.size() + 1); |
| #endif |
| } |
| |
| constexpr operator basic_zstring<const CharT, Traits>() const noexcept |
| { |
| return basic_zstring<const CharT, Traits>(data_); |
| } |
| |
| constexpr reference operator[](size_type pos) const noexcept |
| { |
| return data_[pos]; |
| } |
| constexpr reference front() const noexcept |
| { |
| return data_[0]; |
| } |
| constexpr pointer data() const noexcept |
| { |
| return data_; |
| } |
| constexpr const_pointer c_str() const noexcept |
| { |
| return data_; |
| } |
| [[nodiscard]] constexpr bool empty() const noexcept |
| { |
| return data_ == nullptr || data_[0] == '\0'; |
| } |
| |
| constexpr basic_zstring suffix(size_type size) const noexcept |
| { |
| return data_ + size; |
| } |
| constexpr basic_zstring<const CharT, Traits> |
| csuffix(size_type size) const noexcept |
| { |
| return data_ + size; |
| } |
| |
| constexpr int compare(const_pointer other, |
| size_type other_size) const noexcept |
| { |
| for (size_t i = 0; i < other_size; ++i) |
| { |
| if (data_[i] == '\0') |
| { |
| return -1; |
| } |
| if (!Traits::eq(other[i], data_[i])) |
| { |
| return Traits::lt(other[i], data_[i]) ? 1 : -1; |
| } |
| } |
| if (data_[other_size] == '\0') |
| { |
| return 0; |
| } |
| return 1; |
| } |
| constexpr int compare(const_pointer other) const noexcept |
| { |
| auto data = data_; |
| while (true) |
| { |
| if (data[0] == '\0') |
| { |
| if (other[0] == '\0') |
| { |
| return 0; |
| } |
| return -1; |
| } |
| if (other[0] == '\0') |
| { |
| return 1; |
| } |
| if (!Traits::eq(other[0], data[0])) |
| { |
| return Traits::lt(other[0], data[0]) ? 1 : -1; |
| } |
| data++; |
| other++; |
| } |
| } |
| |
| template <typename CharT1> |
| constexpr Traits::comparison_category |
| operator<=>(basic_zstring<CharT1, Traits> rhs) const noexcept |
| { |
| return compare(rhs.data()) <=> 0; |
| } |
| template <typename T, std::enable_if_t<std::is_pointer_v<T>, bool> = true> |
| constexpr Traits::comparison_category operator<=>(T rhs) const noexcept |
| { |
| return compare(rhs) <=> 0; |
| } |
| template <typename Allocator> |
| constexpr Traits::comparison_category operator<=>( |
| const std::basic_string<decay_t, Traits, Allocator>& rhs) const noexcept |
| { |
| return compare(rhs.data(), rhs.size()) <=> 0; |
| } |
| constexpr Traits::comparison_category |
| operator<=>(std::basic_string_view<decay_t, Traits> rhs) const noexcept |
| { |
| return compare(rhs.data(), rhs.size()) <=> 0; |
| } |
| |
| constexpr bool operator==(const auto& rhs) const noexcept |
| { |
| return (*this <=> rhs) == 0; |
| } |
| |
| private: |
| pointer data_; |
| }; |
| |
| template <typename CharT, typename Traits> |
| std::basic_ostream<std::remove_const_t<CharT>, Traits>& |
| operator<<(std::basic_ostream<std::remove_const_t<CharT>, Traits>& os, |
| basic_zstring<CharT, Traits> v) |
| { |
| return os << v.c_str(); |
| } |
| |
| #define zstring_all(char_t, pfx) \ |
| using pfx##zstring = basic_zstring<char_t>; \ |
| using const_##pfx##zstring = basic_zstring<const char_t>; |
| zstring_all(char, ); |
| zstring_all(char8_t, u8); |
| zstring_all(char16_t, u16); |
| zstring_all(char32_t, u32); |
| zstring_all(wchar_t, w); |
| #undef zstring_all |
| |
| } // namespace stdplus |
| |
| namespace fmt |
| { |
| template <typename CharT, typename Traits> |
| struct formatter<stdplus::basic_zstring<CharT, Traits>, |
| std::remove_const_t<CharT>> : |
| formatter<const CharT*, std::remove_const_t<CharT>> |
| { |
| template <typename FormatContext> |
| inline auto format(stdplus::basic_zstring<CharT, Traits> str, |
| FormatContext& ctx) const |
| { |
| return formatter<const CharT*, std::remove_const_t<CharT>>::format( |
| str.c_str(), ctx); |
| } |
| }; |
| } // namespace fmt |
| |
| namespace std |
| { |
| template <typename T, typename CharT> |
| struct formatter; |
| |
| template <typename CharT, typename Traits> |
| struct formatter<stdplus::basic_zstring<CharT, Traits>, |
| std::remove_const_t<CharT>> : |
| formatter<const CharT*, std::remove_const_t<CharT>> |
| { |
| template <typename FormatContext> |
| inline auto format(stdplus::basic_zstring<CharT, Traits> str, |
| FormatContext& ctx) const |
| { |
| return formatter<const CharT*, std::remove_const_t<CharT>>::format( |
| str.c_str(), ctx); |
| } |
| }; |
| } // namespace std |