/* String for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OCTA_STRING_H #define OCTA_STRING_H #include #include #include "octa/utility.h" #include "octa/range.h" #include "octa/vector.h" namespace octa { static constexpr size_t npos = -1; template> class StringBase { octa::Vector<_T> p_buf; void terminate() { if (p_buf.empty() || (p_buf.back() != '\0')) p_buf.push('\0'); } public: typedef size_t Size; typedef ptrdiff_t Difference; typedef _T Value; typedef _T &Reference; typedef const _T &ConstReference; typedef _T *Pointer; typedef const _T *ConstPointer; typedef PointerRange< _T> Range; typedef PointerRange ConstRange; typedef _A Allocator; StringBase(const _A &a = _A()): p_buf(1, '\0', a) {} StringBase(const StringBase &s): p_buf(s.p_buf) {} StringBase(const StringBase &s, const _A &a): p_buf(s.p_buf, a) {} StringBase(StringBase &&s): p_buf(octa::move(s.p_buf)) {} StringBase(StringBase &&s, const _A &a): p_buf(octa::move(s.p_buf), a) {} StringBase(const StringBase &s, size_t pos, size_t len = npos, const _A &a = _A()): p_buf(s.p_buf.each().slice(pos, (len == npos) ? s.p_buf.size() : (pos + len)), a) { terminate(); } /* TODO: traits for utf-16/utf-32 string lengths, for now assume char */ StringBase(const _T *v, const _A &a = _A()): p_buf(ConstRange(v, strlen(v) + 1), a) {} template StringBase(_R range, const _A &a = _A()): p_buf(range, a) { terminate(); } void clear() { p_buf.clear(); } StringBase<_T> &operator=(const StringBase &v) { p_buf.operator=(v); return *this; } StringBase<_T> &operator=(StringBase &&v) { p_buf.operator=(octa::move(v)); return *this; } StringBase<_T> &operator=(const _T *v) { p_buf = ConstRange(v, strlen(v) + 1); return *this; } void resize(size_t n, _T v = _T()) { p_buf.pop(); p_buf.resize(n, v); terminate(); } void reserve(size_t n) { p_buf.reserve(n + 1); } _T &operator[](size_t i) { return p_buf[i]; } const _T &operator[](size_t i) const { return p_buf[i]; } _T &at(size_t i) { return p_buf[i]; } const _T &at(size_t i) const { return p_buf[i]; } _T &front() { return p_buf[0]; } const _T &front() const { return p_buf[0]; }; _T &back() { return p_buf[size() - 1]; } const _T &back() const { return p_buf[size() - 1]; } _T *data() { return p_buf.data(); } const _T *data() const { return p_buf.data(); } size_t size() const { return p_buf.size() - 1; } size_t capacity() const { return p_buf.capacity() - 1; } bool empty() const { return (size() == 0); } void push(_T v) { p_buf.back() = v; p_buf.push('\0'); } StringBase<_T> &append(const StringBase &s) { p_buf.pop(); p_buf.insert_range(p_buf.size(), s.p_buf.each()); return *this; } StringBase<_T> &append(const StringBase &s, size_t idx, size_t len) { p_buf.pop(); p_buf.insert_range(p_buf.size(), octa::PointerRange<_T>(&s[idx], (len == npos) ? (s.size() - idx) : len)); terminate(); return *this; } StringBase<_T> &append(const _T *s) { p_buf.pop(); p_buf.insert_range(p_buf.size(), ConstRange(s, strlen(s) + 1)); return *this; } StringBase<_T> &append(size_t n, _T c) { p_buf.pop(); for (size_t i = 0; i < n; ++i) p_buf.push(c); p_buf.push('\0'); return *this; } template StringBase<_T> &append_range(_R range) { p_buf.pop(); p_buf.insert_range(p_buf.size(), range); terminate(); return *this; } StringBase<_T> &operator+=(const StringBase &s) { return append(s); } StringBase<_T> &operator+=(const _T *s) { return append(s); } StringBase<_T> &operator+=(_T c) { p_buf.pop(); p_buf.push(c); p_buf.push('\0'); return *this; } Range each() { return Range(p_buf.data(), size()); } ConstRange each() const { return ConstRange(p_buf.data(), size()); } void swap(StringBase &v) { p_buf.swap(v); } }; typedef StringBase String; template String concat(const _T v, const String &sep, _F func) { String ret; auto range = octa::each(v); if (range.empty()) return octa::move(ret); for (;;) { ret += func(range.front()); range.pop_front(); if (range.empty()) break; ret += sep; } return octa::move(ret); } template String concat(const _T &v, const String &sep = " ") { String ret; auto range = octa::each(v); if (range.empty()) return octa::move(ret); for (;;) { ret += range.front(); range.pop_front(); if (range.empty()) break; ret += sep; } return octa::move(ret); } template String concat(std::initializer_list<_T> v, const String &sep, _F func) { return concat(octa::each(v), sep, func); } template String concat(std::initializer_list<_T> v, const String &sep = " ") { return concat(octa::each(v), sep); } namespace detail { template struct ToStringTest { template struct Test {}; template static char test(Test<_U, &_U::to_string> *); template static int test(...); static constexpr bool value = (sizeof(test<_T>(0)) == sizeof(char)); }; } template struct ToString { typedef _T Argument; typedef String Result; template static String to_str(const _U &v, octa::EnableIf::value, bool> = true ) { return v.to_string(); } template static String to_str(const _U &v, octa::EnableIf::value && !octa::IsScalar<_U>::value, bool> = true ) { String ret("{"); ret += concat(octa::each(v), ", ", ToString>()); ret += "}"; return octa::move(ret); } template static String to_str(const _U &v, octa::EnableIf::value && octa::IsScalar<_U>::value, bool> = true ) { return ToString<_U>()(v); } String operator()(const _T &v) { return octa::move(to_str >>(v)); } }; namespace detail { template void str_printf(octa::Vector *s, const char *fmt, _T v) { char buf[256]; int n = snprintf(buf, sizeof(buf), fmt, v); s->clear(); s->reserve(n + 1); if (n >= (int)sizeof(buf)) snprintf(s->data(), n + 1, fmt, v); else if (n > 0) memcpy(s->data(), buf, n + 1); else { n = 0; *(s->data()) = '\0'; } *(((size_t *)s) + 1) = n + 1; } } template<> struct ToString { typedef bool Argument; typedef String Result; String operator()(bool b) { return b ? "true" : "false"; } }; template<> struct ToString { typedef char Argument; typedef String Result; String operator()(char c) { String ret; ret.push(c); return octa::move(ret); } }; #define OCTA_TOSTR_NUM(_T, fmt) \ template<> struct ToString<_T> { \ typedef _T Argument; \ typedef String Result; \ String operator()(_T v) { \ String ret; \ octa::detail::str_printf((octa::Vector *)&ret, fmt, v); \ return octa::move(ret); \ } \ }; OCTA_TOSTR_NUM(int, "%d") OCTA_TOSTR_NUM(int &, "%d") OCTA_TOSTR_NUM(uint, "%u") OCTA_TOSTR_NUM(long, "%ld") OCTA_TOSTR_NUM(ulong, "%lu") OCTA_TOSTR_NUM(llong, "%lld") OCTA_TOSTR_NUM(ullong, "%llu") OCTA_TOSTR_NUM(float, "%f") OCTA_TOSTR_NUM(double, "%f") OCTA_TOSTR_NUM(ldouble, "%Lf") #undef OCTA_TOSTR_NUM template struct ToString<_T *> { typedef _T *Argument; typedef String Result; String operator()(Argument v) { String ret; octa::detail::str_printf((octa::Vector *)&ret, "%p", v); return octa::move(ret); } }; template<> struct ToString { typedef const String &Argument; typedef String Result; String operator()(Argument s) { return s; } }; template struct ToString> { typedef const octa::Pair<_T, _U> &Argument; typedef String Result; String operator()(Argument v) { String ret("{"); ret += ToString>>() (v.first); ret += ", "; ret += ToString>>() (v.second); ret += "}"; return octa::move(ret); } }; template String to_string(const _T &v) { return octa::move(ToString>> ()(v)); } template String to_string(std::initializer_list<_T> init) { return octa::move(ToString>()(init)); } } /* namespace octa */ #endif