/** @file DString.hpp * * @author Roland Conybeare, Jan 2026 **/ #pragma once #include #include #include #include #include #include #include //#include namespace xo { namespace mm { class ACollector; } namespace scm { /** @class DString * @brief String implementation with gc hooks * * String implementation for Schematika. * Size-prefixed and null-terminated. * Note however that string length != size for utf-8. * * Uses flexible array for chars, * with string contents in memory immediately * following the DString itself **/ struct DString { public: /** @defgroup dstring-types type traits **/ ///@{ /** character traits for this DString **/ using traits_type = std::char_traits; /** type of each character in this DString **/ using value_type = char; /** type for string index / size **/ using size_type = std::uint32_t; /** representation for a read/write iterator **/ using iterator = char *; /** representation for a readonly iterator **/ using const_iterator = const char *; /** xo allocator **/ using AAllocator = xo::mm::AAllocator; /** garbage collector **/ using ACollector = xo::mm::ACollector; /** ppindentinfo for APrintable **/ using ppindentinfo = xo::print::ppindentinfo; ///@} /** @defgroup dstring-ctors constructors **/ ///@{ /** default ctor **/ DString() = default; /** not simply copyable, because of flexible array. * Need allocator **/ DString(const DString &) = delete; /** create empty string with space for @p cap chars * (including null terminator). * Use memory from allocator @p mm **/ static DString * empty(obj mm, size_type cap); /** create string containing a copy of null-terminated @p cstr. * Use memory from allocator @p mm **/ static DString * from_cstr(obj mm, const char * cstr); /** create string containing a copy of @p sv. * Use memory from allocator @p mm. **/ static DString * from_view(obj mm, std::string_view sv); /** create string containing a copy @p str. * Use memory from allocator @p mm. **/ static DString * from_str(obj mm, const std::string & str); /** create string containing a copy of @p sv. * Use memory from allocator @p mm via sub_alloc. * (load-bearing for StringTable) **/ static DString * from_view_suballoc(obj mm, std::string_view sv); /** clone existing string **/ static DString * clone(obj mm, const DString * src); #ifdef NOT_YET /** **/ static DString * concat(obj mm, DString * s1, DString * s2); #endif /** create string using printf-style formatting. * Use memory from allocator @p mm with capacity @p cap. * Truncates if result exceeds capacity. * @return pointer to newly created DString **/ template static DString * printf(obj mm, size_type cap, const char * fmt, Args&&... args); ///@} /** @defgroup dstring-access access methods **/ ///@{ /** get writeable access to string representation. * Caller responsible for calling fixup() if string length modified **/ char * data() noexcept { return chars_; } /** return char at position @p pos in this string, counting from zero. * Does not check bounds. Undefined behavior if @p pos = @ref capacity_ **/ char & operator[](size_type pos) noexcept { return chars_[pos]; } const char & operator[](size_type pos) const noexcept { return chars_[pos]; } size_type capacity() const noexcept { return capacity_; } size_type size() const noexcept { return size_; } const char * chars() const noexcept { return chars_; } ///@} /** @defgroup dstring-iterators iterators **/ ///@{ iterator begin() noexcept { return &chars_[0]; } iterator end() noexcept { return &chars_[size_]; } const_iterator cbegin() const noexcept { return &chars_[0]; } const_iterator cend() const noexcept { return &chars_[size_]; } const_iterator begin() const noexcept { return cbegin(); } const_iterator end() const noexcept { return cend(); } ///@} /** @defgroup dstring-assign assignment **/ ///@{ /** put string into empty state **/ void clear() noexcept { size_ = 0; chars_[0] = '\0'; } /** replace contents with @p other, or prefix of up to @p capacity - 1 chars **/ DString & assign(const DString & other); ///@} /** @defgroup dstring-general general methods **/ ///@{ /** format string into this DString using printf-style formatting. * Truncates if result exceeds capacity. * @return number of characters written (excluding null terminator) **/ template size_type sprintf(const char * fmt, Args&&... args) { int n; if constexpr (sizeof...(Args) == 0) { n = std::snprintf(chars_, capacity_, "%s", fmt); } else { n = std::snprintf(chars_, capacity_, fmt, std::forward(args)...); } if (n < 0) { size_ = 0; chars_[0] = '\0'; } else { size_ = (n < static_cast(capacity_)) ? n : capacity_ - 1; } return size_; } /** lexicographically compare two strings. * @return <0 if lhs < rhs, 0 if equal, >0 if lhs > rhs **/ static int compare(const DString & lhs, const DString & rhs) noexcept; /** compute hash of string contents **/ std::size_t hash() const noexcept { return std::hash{}(std::string_view(chars_, size_)); } // TODO - behave like std::string, to the extent feasible // insert // insert_range // erase // push_back // append // append_range // operator+= // replace // replace_with_range // copy // find // rfind // find_first_of // find_first_not_of // find_last_of // find_last_not_of // starts_with // end_with // contains // substr /** recalculate string size if string contents modified without * through side effects **/ size_type fixup_size() noexcept; ///@} /** @defgroup dstring-conversion-operators conversion operators **/ ///@{ operator std::string_view() const noexcept { return std::string_view(chars_); } /** @brief conversion oeprator to C-style string. * * Example * @code * DString s = ...; * ::strcmp(s, "obey..."); * @endcode **/ operator const char * () const noexcept { return &(chars_[0]); } ///@} /** @defgroup dstring-printable-methods printable facet methods **/ ///@{ bool pretty(const ppindentinfo & ppii) const; ///@} /** @defgroup dstring-gcobject-methods gcobject facet methods **/ ///@{ size_type shallow_size() const noexcept; /** clone string, using memory from allocator @p mm **/ DString * shallow_move(obj gc) noexcept; size_type forward_children(obj gc) noexcept; /** fixup child pointers (trivial for DString, no children) * note: cref so we can use forward decl **/ ///@} private: /** @defgroup dstring-impl-methods implementation methods **/ ///@{ /** create instance from view @p sv, using memory from @p mm. * @p suballoc_flag chooses whether to use alloc() or suballoc(). * Load-bearing for StringTable **/ static DString * _from_view_aux(obj mm, std::string_view sv, bool suballoc_flag); ///@} private: /** @defgroup dstring-instance-variables instance variables **/ ///@{ /** extent of @ref chars_ array **/ size_type capacity_ = 0; /** null terminator at @c chars_[size_] **/ size_type size_ = 0; /** string contents **/ char chars_[]; ///@} }; /** create string using printf-style formatting. * Use memory from allocator @p mm with capacity @p cap. * Truncates if result exceeds capacity. * @return pointer to newly created DString **/ template DString * DString::printf(obj mm, size_type cap, const char * fmt, Args&&... args) { DString * result = DString::empty(mm, cap); if (result) { result->sprintf(fmt, std::forward(args)...); } return result; } inline std::ostream & operator<<(std::ostream & os, const DString * x) { if (x) { os << std::string_view(*x); } else { os << "nullptr"; } return os; } inline bool operator==(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) == 0; } inline bool operator!=(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) != 0; } inline bool operator<(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) < 0; } inline bool operator<=(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) <= 0; } inline bool operator>(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) > 0; } inline bool operator>=(const DString & lhs, const DString & rhs) { return DString::compare(lhs, rhs) >= 0; } } /*namespace scm*/ } /*namespace xo*/ namespace std { template <> struct hash { std::size_t operator()(const xo::scm::DString & x) const noexcept { return x.hash(); } }; } /*namespace std*/ /* end DString.hpp */