/* String utilities for libostd. * * This file is part of libostd. See COPYING.md for futher information. */ #ifndef OSTD_STRING_HH #define OSTD_STRING_HH #include #include #include #include #include #include #include #include #include "ostd/range.hh" #include "ostd/vector.hh" #include "ostd/algorithm.hh" namespace ostd { template>> struct basic_char_range: input_range> { using range_category = contiguous_range_tag; using value_type = T; using reference = T &; using size_type = size_t; using difference_type = ptrdiff_t; private: struct nat {}; public: basic_char_range(): p_beg(nullptr), p_end(nullptr) {}; basic_char_range(T *beg, T *end): p_beg(beg), p_end(end) {} basic_char_range(std::nullptr_t): p_beg(nullptr), p_end(nullptr) {} template basic_char_range(U &&beg, std::enable_if_t< std::is_convertible_v, nat > = nat{}): p_beg(beg) { if constexpr(std::is_array_v>) { size_t N = std::extent_v>; p_end = beg + N - (beg[N - 1] == '\0'); } else { p_end = beg + (beg ? TR::length(beg) : 0); } } template basic_char_range(std::basic_string, STR, A> const &s): p_beg(s.data()), p_end(s.data() + s.size()) {} template >> basic_char_range(basic_char_range const &v): p_beg(&v[0]), p_end(&v[v.size()]) {} basic_char_range &operator=(basic_char_range const &v) { p_beg = v.p_beg; p_end = v.p_end; return *this; } template basic_char_range &operator=(std::basic_string const &s) { p_beg = s.data(); p_end = s.data() + s.size(); return *this; } basic_char_range &operator=(T *s) { p_beg = s; p_end = s + (s ? TR::length(s) : 0); return *this; } bool empty() const { return p_beg == p_end; } void pop_front() { ++p_beg; if (p_beg > p_end) { throw std::out_of_range{"pop_front on empty range"}; } } T &front() const { return *p_beg; } void pop_back() { if (p_end == p_beg) { return; } --p_end; } T &back() const { return *(p_end - 1); } size_t size() const { return p_end - p_beg; } basic_char_range slice(size_t start, size_t end) const { return basic_char_range(p_beg + start, p_beg + end); } basic_char_range slice(size_t start) const { return slice(start, size()); } T &operator[](size_t i) const { return p_beg[i]; } void put(T v) { if (p_beg == p_end) { throw std::out_of_range{"put into an empty range"}; } *(p_beg++) = v; } T *data() { return p_beg; } T const *data() const { return p_beg; } /* non-range */ int compare(basic_char_range s) const { size_t s1 = size(), s2 = s.size(); int ret; if (!s1 || !s2) { goto diffsize; } if ((ret = TR::compare(data(), s.data(), std::min(s1, s2)))) { return ret; } diffsize: return (s1 < s2) ? -1 : ((s1 > s2) ? 1 : 0); } int case_compare(basic_char_range s) const { size_t s1 = size(), s2 = s.size(); for (size_t i = 0, ms = std::min(s1, s2); i < ms; ++i) { int d = toupper(p_beg[i]) - toupper(s[i]); if (d) { return d; } } return (s1 < s2) ? -1 : ((s1 > s2) ? 1 : 0); } /* that way we can assign, append etc to std::string */ operator std::basic_string_view>() const { return std::basic_string_view>{data(), size()}; } private: T *p_beg, *p_end; }; using char_range = basic_char_range; using string_range = basic_char_range; /* comparisons between ranges */ template inline bool operator==( basic_char_range lhs, basic_char_range rhs ) { return !lhs.compare(rhs); } template inline bool operator!=( basic_char_range lhs, basic_char_range rhs ) { return lhs.compare(rhs); } template inline bool operator<( basic_char_range lhs, basic_char_range rhs ) { return lhs.compare(rhs) < 0; } template inline bool operator>( basic_char_range lhs, basic_char_range rhs ) { return lhs.compare(rhs) > 0; } template inline bool operator<=( basic_char_range lhs, basic_char_range rhs ) { return lhs.compare(rhs) <= 0; } template inline bool operator>=( basic_char_range lhs, basic_char_range rhs ) { return lhs.compare(rhs) >= 0; } /* comparisons between mutable ranges and char arrays */ template inline bool operator==(basic_char_range lhs, T const *rhs) { return !lhs.compare(rhs); } template inline bool operator!=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs); } template inline bool operator<(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) < 0; } template inline bool operator>(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) > 0; } template inline bool operator<=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) <= 0; } template inline bool operator>=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) >= 0; } template inline bool operator==(T const *lhs, basic_char_range rhs) { return !rhs.compare(lhs); } template inline bool operator!=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs); } template inline bool operator<(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) > 0; } template inline bool operator>(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) < 0; } template inline bool operator<=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) >= 0; } template inline bool operator>=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) <= 0; } /* comparisons between immutable ranges and char arrays */ template inline bool operator==(basic_char_range lhs, T const *rhs) { return !lhs.compare(rhs); } template inline bool operator!=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs); } template inline bool operator<(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) < 0; } template inline bool operator>(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) > 0; } template inline bool operator<=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) <= 0; } template inline bool operator>=(basic_char_range lhs, T const *rhs) { return lhs.compare(rhs) >= 0; } template inline bool operator==(T const *lhs, basic_char_range rhs) { return !rhs.compare(lhs); } template inline bool operator!=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs); } template inline bool operator<(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) > 0; } template inline bool operator>(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) < 0; } template inline bool operator<=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) >= 0; } template inline bool operator>=(T const *lhs, basic_char_range rhs) { return rhs.compare(lhs) <= 0; } inline bool starts_with(string_range a, string_range b ) { if (a.size() < b.size()) { return false; } return a.slice(0, b.size()) == b; } template struct ranged_traits> { using range = basic_char_range; static range iter(std::basic_string &v) { return range{v.data(), v.data() + v.size()}; } }; template struct ranged_traits const> { using range = basic_char_range; static range iter(std::basic_string const &v) { return range{v.data(), v.data() + v.size()}; } }; template< typename T, typename TR = std::char_traits, typename A = std::allocator, typename R > inline std::basic_string make_string(R range, A const &alloc = A{}) { std::basic_string ret{alloc}; using C = range_category_t; if constexpr(std::is_convertible_v) { ret.reserve(range.size()); ret.insert(ret.end(), range.data(), range.data() + range.size()); } else { for (; !range.empty(); range.pop_front()) { ret.push_back(range.front()); } } return ret; } template< typename R, typename TR = std::char_traits>>, typename A = std::allocator>> > inline std::basic_string>, TR, A> make_string( R range, A const &alloc = A{} ) { return make_string>, TR, A>( std::move(range), alloc ); } /* string literals */ inline namespace literals { inline namespace string_literals { inline string_range operator "" _sr(char const *str, size_t len) { return string_range(str, str + len); } } } template struct temp_c_string { private: std::remove_cv_t> *p_buf; bool p_allocated; public: temp_c_string() = delete; temp_c_string(temp_c_string const &) = delete; temp_c_string(temp_c_string &&s): p_buf(s.p_buf), p_allocated(s.p_allocated) { s.p_buf = nullptr; s.p_allocated = false; } temp_c_string(R input, std::remove_cv_t> *sbuf, size_t bufsize) : p_buf(nullptr), p_allocated(false) { if (input.empty()) { return; } if (input.size() >= bufsize) { p_buf = new std::remove_cv_t>[input.size() + 1]; p_allocated = true; } else { p_buf = sbuf; } char_range bufr{p_buf, p_buf + input.size() + 1}; range_put_all(bufr, input); bufr.put('\0'); } ~temp_c_string() { if (p_allocated) { delete[] p_buf; } } temp_c_string &operator=(temp_c_string const &) = delete; temp_c_string &operator=(temp_c_string &&s) { swap(s); return *this; } operator std::remove_cv_t> const *() const { return p_buf; } std::remove_cv_t> const *get() const { return p_buf; } void swap(temp_c_string &s) { using std::swap; swap(p_buf, s.p_buf); swap(p_allocated, s.p_allocated); } }; template inline void swap(temp_c_string &a, temp_c_string &b) { a.swap(b); } template inline temp_c_string to_temp_cstr( R input, std::remove_cv_t> *buf, size_t bufsize ) { return temp_c_string(input, buf, bufsize); } } /* namespace ostd */ namespace std { template struct hash> { size_t operator()(ostd::basic_char_range const &v) const { return hash, TR>>{}(v); } }; } #endif