/* String utilities for OctaSTD. * * This file is part of OctaSTD. 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) {} template basic_char_range( U beg, std::enable_if_t< std::is_convertible_v && !std::is_array_v, Nat > = Nat() ): p_beg(beg), p_end(static_cast(beg) + (beg ? TR::length(beg) : 0)) {} basic_char_range(std::nullptr_t): p_beg(nullptr), p_end(nullptr) {} template basic_char_range(U (&beg)[N], std::enable_if_t< std::is_convertible_v, Nat > = Nat()): p_beg(beg), p_end(beg + N - (beg[N - 1] == '\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; } bool pop_front() { if (p_beg == p_end) { return false; } ++p_beg; return true; } bool push_front() { --p_beg; return true; } size_t pop_front_n(size_t n) { size_t olen = p_end - p_beg; p_beg += n; if (p_beg > p_end) { p_beg = p_end; return olen; } return n; } size_t push_front_n(size_t n) { p_beg -= n; return true; } T &front() const { return *p_beg; } bool equals_front(basic_char_range const &range) const { return p_beg == range.p_beg; } ptrdiff_t distance_front(basic_char_range const &range) const { return range.p_beg - p_beg; } bool pop_back() { if (p_end == p_beg) { return false; } --p_end; return true; } bool push_back() { ++p_end; return true; } size_t pop_back_n(size_t n) { size_t olen = p_end - p_beg; p_end -= n; if (p_end < p_beg) { p_end = p_beg; return olen; } return n; } size_t push_back_n(size_t n) { p_end += n; return true; } T &back() const { return *(p_end - 1); } bool equals_back(basic_char_range const &range) const { return p_end == range.p_end; } ptrdiff_t distance_back(basic_char_range const &range) const { return range.p_end - p_end; } 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); } T &operator[](size_t i) const { return p_beg[i]; } bool put(T v) { if (empty()) { return false; } *(p_beg++) = v; return true; } 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(), ostd::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 = ostd::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); } template std::enable_if_t, size_t> copy(R &&orange, size_t n = -1) { return range_put_n(orange, data(), ostd::min(n, size())); } size_t copy(std::remove_cv_t *p, size_t n = -1) { size_t c = ostd::min(n, size()); TR::copy(p, data(), c); return c; } /* 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; }; template inline size_t range_put_n(basic_char_range &range, T const *p, size_t n) { size_t an = ostd::min(n, range.size()); TR::copy(range.data(), p, an); range.pop_front_n(an); return an; } 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) { /* finite random access or contiguous */ auto h = range.half(); ret.reserve(range.size()); ret.insert(ret.end(), h, h + range.size()); } else { /* infinite random access and below */ 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); } } } namespace detail { template< typename T, bool = std::is_convertible_v, bool = std::is_convertible_v > struct ConcatPut; template struct ConcatPut { template static bool put(R &sink, string_range v) { return v.size() && (range_put_n(sink, &v[0], v.size()) == v.size()); } }; template struct ConcatPut { template static bool put(R &sink, char v) { return sink.put(v); } }; } template bool concat(R &&sink, T const &v, string_range sep, F func) { auto range = ostd::iter(v); if (range.empty()) { return true; } for (;;) { if (!detail::ConcatPut< decltype(func(range.front())) >::put(sink, func(range.front()))) { return false; } range.pop_front(); if (range.empty()) { break; } range_put_n(sink, &sep[0], sep.size()); } return true; } template bool concat(R &&sink, T const &v, string_range sep = " ") { auto range = ostd::iter(v); if (range.empty()) { return true; } for (;;) { string_range ret = range.front(); if (!ret.size() || (range_put_n(sink, &ret[0], ret.size()) != ret.size())) { return false; } range.pop_front(); if (range.empty()) { break; } range_put_n(sink, &sep[0], sep.size()); } return true; } template bool concat(R &&sink, std::initializer_list v, string_range sep, F func) { return concat(sink, ostd::iter(v), sep, func); } template bool concat(R &&sink, std::initializer_list v, string_range sep = " ") { return concat(sink, ostd::iter(v), sep); } namespace detail { template struct tostr_range: output_range> { using value_type = char; using reference = char &; using size_type = size_t; using difference_type = ptrdiff_t; template friend size_t range_put_n(tostr_range &range, char const *p, size_t n); tostr_range() = delete; tostr_range(R &out): p_out(out), p_written(0) {} bool put(char v) { bool ret = p_out.put(v); p_written += ret; return ret; } size_t put_string(string_range r) { size_t ret = range_put_n(p_out, r.data(), r.size()); p_written += ret; return ret; } size_t get_written() const { return p_written; } private: R &p_out; size_t p_written; }; template inline size_t range_put_n(tostr_range &range, char const *p, size_t n) { size_t ret = range_put_n(range.p_out, p, n); range.p_written += ret; return ret; } template static std::true_type test_stringify( decltype(std::declval().to_string(std::declval())) * ); template static std::false_type test_stringify(...); template constexpr bool stringify_test = decltype(test_stringify(0))::value; template static std::true_type test_iterable(decltype(ostd::iter(std::declval())) *); template static std::false_type test_iterable(...); template constexpr bool iterable_test = decltype(test_iterable(0))::value; } template struct to_string; template struct to_string>> { std::string operator()(T const &v) const { std::string ret("{"); auto x = appender(); if (concat(x, ostd::iter(v), ", ", to_string< std::remove_const_t >> >())) { ret += x.get(); } ret += "}"; return ret; } }; template struct to_string>> >> { std::string operator()(T const &v) const { auto app = appender(); detail::tostr_range> sink(app); if (!v.to_string(sink)) { return std::string{}; } return std::move(app.get()); } }; template<> struct to_string { std::string operator()(bool b) { return b ? "true" : "false"; } }; template<> struct to_string { std::string operator()(char c) { std::string ret; ret += c; return ret; } }; #define OSTD_TOSTR_NUM(T) \ template<> \ struct to_string { \ std::string operator()(T v) { \ return std::to_string(v); \ } \ }; OSTD_TOSTR_NUM(sbyte) OSTD_TOSTR_NUM(short) OSTD_TOSTR_NUM(int) OSTD_TOSTR_NUM(long) OSTD_TOSTR_NUM(float) OSTD_TOSTR_NUM(double) OSTD_TOSTR_NUM(byte) OSTD_TOSTR_NUM(ushort) OSTD_TOSTR_NUM(uint) OSTD_TOSTR_NUM(ulong) OSTD_TOSTR_NUM(llong) OSTD_TOSTR_NUM(ullong) OSTD_TOSTR_NUM(ldouble) #undef OSTD_TOSTR_NUM template struct to_string { std::string operator()(T *v) { char buf[16]; sprintf(buf, "%p", v); return buf; } }; template<> struct to_string { std::string operator()(char const *s) { return s; } }; template<> struct to_string { std::string operator()(char *s) { return s; } }; template<> struct to_string { std::string operator()(std::string const &s) { return s; } }; template<> struct to_string { std::string operator()(char_range const &s) { return std::string{s}; } }; template<> struct to_string { std::string operator()(string_range const &s) { return std::string{s}; } }; template struct to_string> { std::string operator()(std::pair const &v) { std::string ret{"{"}; ret += to_string>>()(v.first); ret += ", "; ret += to_string>>()(v.second); ret += "}"; return ret; } }; namespace detail { template struct tuple_to_str { template static void append(std::string &ret, T const &tup) { ret += ", "; ret += to_string(tup)) >>>()(std::get(tup)); tuple_to_str::append(ret, tup); } }; template struct tuple_to_str { template static void append(std::string &, T const &) {} }; template struct tuple_to_str<0, N> { template static void append(std::string &ret, T const &tup) { ret += to_string(tup)) >>>()(std::get<0>(tup)); tuple_to_str<1, N>::append(ret, tup); } }; } template struct to_string> { std::string operator()(std::tuple const &v) { std::string ret("{"); detail::tuple_to_str<0, sizeof...(T)>::append(ret, v); ret += "}"; return ret; } }; 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; } p_buf[input.copy(p_buf)] = '\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