// license:GPL-2.0+ // copyright-holders:Couriersud /* * pstring.h */ #ifndef PSTRING_H_ #define PSTRING_H_ #include #include #include // ---------------------------------------------------------------------------------------- // pstring: semi-immutable strings ... // // The only reason this class exists is the absence of support for multi-byte // strings in std:: which I would consider usuable for the use-cases I encounter. // ---------------------------------------------------------------------------------------- template class pstring_const_iterator final { public: typedef typename T::ref_value_type value_type; typedef value_type const *pointer; typedef value_type const &reference; typedef std::ptrdiff_t difference_type; typedef std::forward_iterator_tag iterator_category; typedef typename T::string_type string_type; typedef typename T::traits_type traits_type; pstring_const_iterator() noexcept : p() { } explicit constexpr pstring_const_iterator(const typename string_type::const_iterator &x) noexcept : p(x) { } pstring_const_iterator(const pstring_const_iterator &rhs) noexcept = default; pstring_const_iterator(pstring_const_iterator &&rhs) noexcept = default; pstring_const_iterator &operator=(const pstring_const_iterator &rhs) noexcept = default; pstring_const_iterator &operator=(pstring_const_iterator &&rhs) noexcept = default; pstring_const_iterator& operator++() noexcept { p += static_cast(traits_type::codelen(&(*p))); return *this; } pstring_const_iterator operator++(int) noexcept { pstring_const_iterator tmp(*this); operator++(); return tmp; } bool operator==(const pstring_const_iterator& rhs) const noexcept { return p == rhs.p; } bool operator!=(const pstring_const_iterator& rhs) const noexcept { return p != rhs.p; } reference operator*() const noexcept { return *reinterpret_cast(&(*p)); } pointer operator->() const noexcept { return reinterpret_cast(&(*p)); } private: template friend struct pstring_t; typename string_type::const_iterator p; }; template struct pstring_t { public: typedef F traits_type; typedef typename traits_type::mem_t mem_t; typedef typename traits_type::code_t code_t; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef typename traits_type::string_type string_type; class ref_value_type final { public: ref_value_type() = delete; ref_value_type(const ref_value_type &) = delete; ref_value_type(ref_value_type &&) = delete; ref_value_type &operator=(const ref_value_type &) = delete; ref_value_type &operator=(ref_value_type &&) = delete; operator code_t() const noexcept { return traits_type::code(&m); } private: const mem_t m; }; typedef const ref_value_type& const_reference ; typedef const_reference reference; enum enc_t { UTF8, UTF16 }; // simple construction/destruction pstring_t() { } ~pstring_t() { } // FIXME: Do something with encoding pstring_t(const mem_t *string, const enc_t enc) : m_str(string) { } pstring_t(const mem_t *string, const size_type len, const enc_t enc) : m_str(string, len) { } template::value>::type> pstring_t(C (&string)[N]) { static_assert(N > 0,"pstring from array of length 0"); if (string[N-1] != 0) throw std::exception(); m_str.assign(string, N - 1); } pstring_t(const pstring_t &string) : m_str(string.m_str) { } explicit pstring_t(const string_type &string, const enc_t enc) : m_str(string) { } pstring_t(pstring_t &&string) : m_str(string.m_str) { } explicit pstring_t(code_t code) { *this += code; } template ::value>::type> explicit pstring_t(const pstring_t &string) { m_str.clear(); for (auto &c : string) *this += static_cast(c); // FIXME: codepage conversion for u8 } pstring_t &operator=(const pstring_t &string) { m_str = string.m_str; return *this; } template ::value>::type> pstring_t &operator=(const pstring_t &string) { m_str.clear(); for (auto &c : string) *this += c; return *this; } // no non-const const_iterator for now typedef pstring_const_iterator iterator; typedef pstring_const_iterator const_iterator; iterator begin() { return iterator(m_str.begin()); } iterator end() { return iterator(m_str.end()); } const_iterator begin() const { return const_iterator(m_str.begin()); } const_iterator end() const { return const_iterator(m_str.end()); } const_iterator cbegin() const { return const_iterator(m_str.begin()); } const_iterator cend() const { return const_iterator(m_str.end()); } // C string conversion helpers const mem_t *c_str() const { return static_cast(m_str.c_str()); } const mem_t *data() const { return c_str(); } size_type length() const { return traits_type::len(m_str); } size_type size() const { return traits_type::len(m_str); } bool empty() const { return m_str.size() == 0; } pstring_t substr(size_type start, size_type nlen = npos) const; int compare(const pstring_t &right) const; size_type find(const pstring_t &search, size_type start = 0) const; size_type find(code_t search, size_type start = 0) const; size_type find_first_not_of(const pstring_t &no) const; size_type find_last_not_of(const pstring_t &no) const; // concatenation operators pstring_t& operator+=(const pstring_t &string) { m_str.append(string.m_str); return *this; } pstring_t& operator+=(const code_t c) { traits_type::encode(c, m_str); return *this; } friend pstring_t operator+(const pstring_t &lhs, const pstring_t &rhs) { return pstring_t(lhs) += rhs; } friend pstring_t operator+(const pstring_t &lhs, const code_t rhs) { return pstring_t(lhs) += rhs; } // comparison operators bool operator==(const pstring_t &string) const { return (compare(string) == 0); } bool operator!=(const pstring_t &string) const { return (compare(string) != 0); } bool operator<(const pstring_t &string) const { return (compare(string) < 0); } bool operator<=(const pstring_t &string) const { return (compare(string) <= 0); } bool operator>(const pstring_t &string) const { return (compare(string) > 0); } bool operator>=(const pstring_t &string) const { return (compare(string) >= 0); } const_reference at(const size_type pos) const { return *reinterpret_cast(F::nthcode(m_str.c_str(),pos)); } /* The following is not compatible to std::string */ bool equals(const pstring_t &string) const { return (compare(string) == 0); } bool startsWith(const pstring_t &arg) const { return arg.mem_t_size() > mem_t_size() ? false : m_str.compare(0, arg.mem_t_size(), arg.m_str) == 0; } bool endsWith(const pstring_t &arg) const { return arg.mem_t_size() > mem_t_size() ? false : m_str.compare(mem_t_size()-arg.mem_t_size(), arg.mem_t_size(), arg.m_str) == 0; } pstring_t replace_all(const pstring_t &search, const pstring_t &replace) const; pstring_t cat(const pstring_t &s) const { return *this + s; } pstring_t cat(code_t c) const { return *this + c; } // conversions double as_double(bool *error = nullptr) const; long as_long(bool *error = nullptr) const; /* the following are extensions to */ size_type mem_t_size() const { return m_str.size(); } pstring_t left(size_type len) const { return substr(0, len); } pstring_t right(size_type nlen) const { return nlen >= length() ? *this : substr(length() - nlen, nlen); } pstring_t ltrim(const pstring_t &ws = pstring_t(" \t\n\r")) const { return substr(find_first_not_of(ws)); } pstring_t rtrim(const pstring_t &ws = pstring_t(" \t\n\r")) const { auto f = find_last_not_of(ws); return f == npos ? pstring_t() : substr(0, f + 1); } pstring_t trim(const pstring_t &ws = pstring_t(" \t\n\r")) const { return this->ltrim(ws).rtrim(ws); } pstring_t rpad(const pstring_t &ws, const size_type cnt) const; pstring_t ucase() const; const string_type &cpp_string() const { return m_str; } static const size_type npos = static_cast(-1); protected: string_type m_str; private: }; struct pu8_traits { typedef char mem_t; typedef char code_t; typedef std::string string_type; static std::size_t len(const string_type &p) { return p.size(); } static std::size_t codelen(const mem_t *p) { return 1; } static std::size_t codelen(const code_t c) { return 1; } static code_t code(const mem_t *p) { return *p; } static void encode(const code_t c, string_type &s) { s += static_cast(c); } static const mem_t *nthcode(const mem_t *p, const std::size_t n) { return &(p[n]); } }; /* No checking, this may deliver invalid codes */ struct putf8_traits { typedef char mem_t; typedef char32_t code_t; typedef std::string string_type; static std::size_t len(const string_type &p) { std::size_t ret = 0; for (const auto &c : p) { if (!((c & 0xC0) == 0x80)) ret++; } return ret; } static std::size_t codelen(const mem_t *p) { const unsigned char *p1 = reinterpret_cast(p); if ((*p1 & 0x80) == 0x00) return 1; else if ((*p1 & 0xE0) == 0xC0) return 2; else if ((*p1 & 0xF0) == 0xE0) return 3; else if ((*p1 & 0xF8) == 0xF0) return 4; else { return 1; // not correct } } static std::size_t codelen(const code_t c) { if (c < 0x0080) return 1; else if (c < 0x800) return 2; else if (c < 0x10000) return 3; else /* U+10000 U+1FFFFF */ return 4; /* no checks */ } static code_t code(const mem_t *p) { const unsigned char *p1 = reinterpret_cast(p); if ((*p1 & 0x80) == 0x00) return *p1; else if ((*p1 & 0xE0) == 0xC0) return static_cast(((p1[0] & 0x3f) << 6) | (p1[1] & 0x3f)); else if ((*p1 & 0xF0) == 0xE0) return static_cast(((p1[0] & 0x1f) << 12) | ((p1[1] & 0x3f) << 6) | ((p1[2] & 0x3f) << 0)); else if ((*p1 & 0xF8) == 0xF0) return static_cast(((p1[0] & 0x0f) << 18) | ((p1[1] & 0x3f) << 12) | ((p1[2] & 0x3f) << 6) | ((p1[3] & 0x3f) << 0)); else return *p1; // not correct } static void encode(const code_t c, string_type &s) { if (c < 0x0080) { s += static_cast(c); } else if (c < 0x800) { s += static_cast(0xC0 | (c >> 6)); s += static_cast(0x80 | (c & 0x3f)); } else if (c < 0x10000) { s += static_cast(0xE0 | (c >> 12)); s += static_cast(0x80 | ((c>>6) & 0x3f)); s += static_cast(0x80 | (c & 0x3f)); } else /* U+10000 U+1FFFFF */ { s += static_cast(0xF0 | (c >> 18)); s += static_cast(0x80 | ((c>>12) & 0x3f)); s += static_cast(0x80 | ((c>>6) & 0x3f)); s += static_cast(0x80 | (c & 0x3f)); } } static const mem_t *nthcode(const mem_t *p, const std::size_t n) { const mem_t *p1 = p; std::size_t i = n; while (i-- > 0) p1 += codelen(p1); return p1; } }; struct putf16_traits { typedef char16_t mem_t; typedef char32_t code_t; typedef std::u16string string_type; static std::size_t len(const string_type &p) { std::size_t ret = 0; auto i = p.begin(); while (i != p.end()) { // FIXME: check that size is equal uint16_t c = static_cast(*i++); if (!((c & 0xd800) == 0xd800)) ret++; } return ret; } static std::size_t codelen(const mem_t *p) { uint16_t c = static_cast(*p); return ((c & 0xd800) == 0xd800) ? 2 : 1; } static std::size_t codelen(const code_t c) { if (c < 0x10000) return 1; else /* U+10000 U+1FFFFF */ return 2; } static code_t code(const mem_t *p) { uint32_t c = static_cast(*p++); if ((c & 0xd800) == 0xd800) { c = (c - 0xd800) << 10; c += static_cast(*p) - 0xdc00 + 0x10000; } return static_cast(c); } static void encode(code_t c, string_type &s) { uint32_t cu = static_cast(c); if (c > 0xffff) { //make a surrogate pair uint32_t t = ((cu - 0x10000) >> 10) + 0xd800; cu = (cu & 0x3ff) + 0xdc00; s += static_cast(t); s += static_cast(cu); } else { s += static_cast(cu); } } static const mem_t *nthcode(const mem_t *p, const std::size_t n) { std::size_t i = n; while (i-- > 0) p += codelen(p); return p; } }; struct pwchar_traits { typedef wchar_t mem_t; typedef char32_t code_t; typedef std::wstring string_type; static std::size_t len(const string_type &p) { if (sizeof(wchar_t) == 2) { std::size_t ret = 0; auto i = p.begin(); while (i != p.end()) { // FIXME: check that size is equal uint32_t c = static_cast(*i++); if (!((c & 0xd800) == 0xd800)) ret++; } return ret; } else return p.size(); } static std::size_t codelen(const mem_t *p) { if (sizeof(wchar_t) == 2) { uint16_t c = static_cast(*p); return ((c & 0xd800) == 0xd800) ? 2 : 1; } else return 1; } static std::size_t codelen(const code_t c) { if (sizeof(wchar_t) == 2) return ((c & 0xd800) == 0xd800) ? 2 : 1; else return 1; } static code_t code(const mem_t *p) { if (sizeof(wchar_t) == 2) { uint32_t c = static_cast(*p++); if ((c & 0xd800) == 0xd800) { c = (c - 0xd800) << 10; c += static_cast(*p) - 0xdc00 + 0x10000; } return static_cast(c); } else return static_cast(*p); } static void encode(code_t c, string_type &s) { if (sizeof(wchar_t) == 2) { uint32_t cu = static_cast(c); if (c > 0xffff) { //make a surrogate pair uint32_t t = ((cu - 0x10000) >> 10) + 0xd800; cu = (cu & 0x3ff) + 0xdc00; s += static_cast(t); s += static_cast(cu); } else s += static_cast(cu); } else s += static_cast(c); } static const mem_t *nthcode(const mem_t *p, const std::size_t n) { if (sizeof(wchar_t) == 2) { std::size_t i = n; while (i-- > 0) p += codelen(p); return p; } else return p + n; } }; extern template struct pstring_t; extern template struct pstring_t; extern template struct pstring_t; extern template struct pstring_t; typedef pstring_t pstring; typedef pstring_t pu16string; typedef pstring_t pwstring; namespace plib { template pstring to_string(const T &v) { return pstring(std::to_string(v), pstring::UTF8); } template pwstring to_wstring(const T &v) { return pwstring(std::to_wstring(v), pwstring::UTF16); } } // custom specialization of std::hash can be injected in namespace std namespace std { template struct hash> { typedef pstring_t argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const { const pstring::mem_t *string = s.c_str(); result_type result = 5381; for (pstring::mem_t c = *string; c != 0; c = *string++) result = ((result << 5) + result ) ^ (result >> (32 - 5)) ^ static_cast(c); return result; } }; } #endif /* PSTRING_H_ */