// // Copyright (c) 2025 Gennaro Prota (gennaro dot prota at gmail dot com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/static_string // #ifndef BOOST_STATIC_STRING_STATIC_CSTRING_HPP #define BOOST_STATIC_STRING_STATIC_CSTRING_HPP #include #include #include #include #include #include #include #include #include namespace boost { namespace static_strings { namespace detail { // Primary template: No remaining-capacity trick; uses traits::length() for length. template class static_cstring_base { public: using traits_type = Traits; using value_type = CharT; using size_type = std::size_t; value_type data_[N + 1]{}; constexpr size_type get_size() const noexcept { return traits_type::length(data_); } constexpr void set_size(size_type sz) noexcept { data_[sz] = value_type{}; } // Defaulted comparisons for structural type support. constexpr bool operator==(const static_cstring_base&) const noexcept = default; constexpr auto operator<=>(const static_cstring_base&) const noexcept = default; }; // Specialization for N <= UCHAR_MAX: Uses remaining-capacity trick. template class static_cstring_base { public: using traits_type = Traits; using value_type = CharT; using size_type = std::size_t; value_type data_[N + 1]{}; constexpr size_type get_size() const noexcept { return N - static_cast(data_[N]); } constexpr void set_size(size_type sz) noexcept { data_[sz] = value_type{}; data_[N] = static_cast(N - sz); } // Defaulted comparisons for structural type support. constexpr bool operator==(const static_cstring_base&) const noexcept = default; constexpr auto operator<=>(const static_cstring_base&) const noexcept = default; }; } // namespace detail template> class basic_static_cstring : public detail::static_cstring_base { public: using base = detail::static_cstring_base; using base::data_; using base::get_size; using base::set_size; // Member types using traits_type = Traits; using value_type = CharT; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const value_type*; using iterator = pointer; using const_iterator = const_pointer; static constexpr size_type npos = static_cast(-1); static constexpr size_type static_capacity = N; // Constructors. constexpr basic_static_cstring() noexcept { set_size(0); } constexpr basic_static_cstring(const CharT* s) { assign(s); } constexpr basic_static_cstring(const CharT* s, size_type count) { assign(s, count); } constexpr basic_static_cstring(size_type count, CharT ch) { assign(count, ch); } template constexpr basic_static_cstring(const CharT (&arr)[M]) { static_assert(M <= N + 1, "String literal too long for static_cstring"); assign(arr, M - 1); } constexpr size_type size() const noexcept { return get_size(); } constexpr size_type length() const noexcept { return size(); } constexpr bool empty() const noexcept { return data_[0] == value_type{}; } static constexpr size_type max_size() noexcept { return N; } static constexpr size_type capacity() noexcept { return N; } // Element access. constexpr reference operator[](size_type pos) noexcept { return data_[pos]; } constexpr const_reference operator[](size_type pos) const noexcept { return data_[pos]; } constexpr reference at(size_type pos) { if (pos >= size()) { throw std::out_of_range("static_cstring::at"); } return data_[pos]; } constexpr const_reference at(size_type pos) const { if (pos >= size()) { throw std::out_of_range("static_cstring::at"); } return data_[pos]; } constexpr reference front() noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); return data_[0]; } constexpr const_reference front() const noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); return data_[0]; } constexpr reference back() noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); return data_[size() - 1]; } constexpr const_reference back() const noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); return data_[size() - 1]; } constexpr pointer data() noexcept { return data_; } constexpr const_pointer data() const noexcept { return data_; } constexpr const_pointer c_str() const noexcept { return data_; } // Iterators. constexpr iterator begin() noexcept { return data_; } constexpr const_iterator begin() const noexcept { return data_; } constexpr const_iterator cbegin() const noexcept { return data_; } constexpr iterator end() noexcept { return data_ + size(); } constexpr const_iterator end() const noexcept { return data_ + size(); } constexpr const_iterator cend() const noexcept { return data_ + size(); } // Modifiers. constexpr void clear() noexcept { set_size(0); } constexpr basic_static_cstring& assign(const CharT* s) { return assign(s, traits_type::length(s)); } constexpr basic_static_cstring& assign(const CharT* s, size_type count) { if (count > N) { throw std::length_error("static_cstring::assign"); } traits_type::copy(data_, s, count); set_size(count); return *this; } constexpr basic_static_cstring& assign(size_type count, CharT ch) { if (count > N) { throw std::length_error("static_cstring::assign"); } traits_type::assign(data_, count, ch); set_size(count); return *this; } constexpr basic_static_cstring& operator=(const CharT* s) { return assign(s); } constexpr void push_back(CharT ch) { const size_type sz = size(); if (sz >= N) { throw std::length_error("static_cstring::push_back"); } data_[sz] = ch; set_size(sz + 1); } constexpr void pop_back() noexcept { BOOST_STATIC_STRING_ASSERT(!empty()); set_size(size() - 1); } constexpr basic_static_cstring& append(const CharT* s) { return append(s, traits_type::length(s)); } constexpr basic_static_cstring& append(const CharT* s, size_type count) { const size_type sz = size(); if (sz + count > N) { throw std::length_error("static_cstring::append"); } traits_type::copy(data_ + sz, s, count); set_size(sz + count); return *this; } constexpr basic_static_cstring& append(size_type count, CharT ch) { const size_type sz = size(); if (sz + count > N) { throw std::length_error("static_cstring::append"); } traits_type::assign(data_ + sz, count, ch); set_size(sz + count); return *this; } constexpr basic_static_cstring& operator+=(const CharT* s) { return append(s); } constexpr basic_static_cstring& operator+=(CharT ch) { push_back(ch); return *this; } // Comparisons. constexpr int compare(const basic_static_cstring& other) const noexcept { const size_type lhs_sz = size(); const size_type rhs_sz = other.size(); const int result = traits_type::compare(data_, other.data_, (std::min)(lhs_sz, rhs_sz)); return result != 0 ? result : lhs_sz < rhs_sz ? -1 : lhs_sz > rhs_sz ? 1 : 0; } constexpr int compare(const CharT* s) const noexcept { return compare(basic_static_cstring(s)); } // Conversions. constexpr operator std::basic_string_view() const noexcept { return {data_, size()}; } std::basic_string str() const { return {data_, size()}; } // Swap. constexpr void swap(basic_static_cstring& other) noexcept { basic_static_cstring tmp = *this; *this = other; other = tmp; } // Defaulted comparisons for structural type (C++20). constexpr bool operator==(const basic_static_cstring&) const noexcept = default; constexpr auto operator<=>(const basic_static_cstring&) const noexcept = default; }; #if defined(BOOST_STATIC_STRING_USE_DEDUCT) // Deduction guide. template basic_static_cstring(const CharT(&)[N]) -> basic_static_cstring; #endif // Comparison with const CharT*. template constexpr bool operator==(const basic_static_cstring& lhs, const CharT* rhs) noexcept { return lhs.compare(rhs) == 0; } template constexpr bool operator==(const CharT* lhs, const basic_static_cstring& rhs) noexcept { return rhs.compare(lhs) == 0; } // Stream output. template std::basic_ostream& operator<<(std::basic_ostream& os, const basic_static_cstring& str) { return os << str.c_str(); } // Alias templates. template using static_cstring = basic_static_cstring; template using static_wcstring = basic_static_cstring; } } #endif