/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright 2014 Cloudius Systems */ /* * C++2014 dependencies removed. Uses of std::string_view adapted to * boost::string_ref. Matt Benjamin */ #ifndef SSTRING_HH_ #define SSTRING_HH_ #include #include #include "include/buffer.h" #include "include/denc.h" template class basic_sstring; using sstring = basic_sstring; template inline string_type to_sstring(T value); template class basic_sstring { static_assert( (std::is_same::value || std::is_same::value || std::is_same::value), "basic_sstring only supports single byte char types"); union contents { struct external_type { char_type* str; Size size; int8_t pad; } external; struct internal_type { char_type str[max_size]; int8_t size; } internal; static_assert(sizeof(external_type) <= sizeof(internal_type), "max_size too small"); static_assert(max_size <= 127, "max_size too large"); } u; bool is_internal() const noexcept { return u.internal.size >= 0; } bool is_external() const noexcept { return !is_internal(); } const char_type* str() const { return is_internal() ? u.internal.str : u.external.str; } char_type* str() { return is_internal() ? u.internal.str : u.external.str; } template static inline string_type to_sstring_sprintf(T value, const char* fmt) { char tmp[sizeof(value) * 3 + 2]; auto len = std::sprintf(tmp, fmt, value); using ch_type = typename string_type::value_type; return string_type(reinterpret_cast(tmp), len); } template static inline string_type to_sstring(int value) { return to_sstring_sprintf(value, "%d"); } template static inline string_type to_sstring(unsigned value) { return to_sstring_sprintf(value, "%u"); } template static inline string_type to_sstring(long value) { return to_sstring_sprintf(value, "%ld"); } template static inline string_type to_sstring(unsigned long value) { return to_sstring_sprintf(value, "%lu"); } template static inline string_type to_sstring(long long value) { return to_sstring_sprintf(value, "%lld"); } template static inline string_type to_sstring(unsigned long long value) { return to_sstring_sprintf(value, "%llu"); } template static inline string_type to_sstring(float value) { return to_sstring_sprintf(value, "%g"); } template static inline string_type to_sstring(double value) { return to_sstring_sprintf(value, "%g"); } template static inline string_type to_sstring(long double value) { return to_sstring_sprintf(value, "%Lg"); } template static inline string_type to_sstring(const char* value) { return string_type(value); } template static inline string_type to_sstring(sstring value) { return value; } public: using value_type = char_type; using traits_type = std::char_traits; using allocator_type = std::allocator; using reference = char_type&; using const_reference = const char_type&; using pointer = char_type*; using const_pointer = const char_type*; using iterator = char_type*; using const_iterator = const char_type*; // FIXME: add reverse_iterator and friend using difference_type = ssize_t; // std::make_signed_t can be too small using size_type = Size; static constexpr size_type npos = static_cast(-1); public: struct initialized_later {}; basic_sstring() noexcept { u.internal.size = 0; u.internal.str[0] = '\0'; } basic_sstring(const basic_sstring& x) { if (x.is_internal()) { u.internal = x.u.internal; } else { u.internal.size = -1; u.external.str = reinterpret_cast(std::malloc(x.u.external.size + 1)); if (!u.external.str) { throw std::bad_alloc(); } std::copy(x.u.external.str, x.u.external.str + x.u.external.size + 1, u.external.str); u.external.size = x.u.external.size; } } basic_sstring(basic_sstring&& x) noexcept { u = x.u; x.u.internal.size = 0; x.u.internal.str[0] = '\0'; } basic_sstring(initialized_later, size_t size) { if (size_type(size) != size) { throw std::overflow_error("sstring overflow"); } if (size + 1 <= sizeof(u.internal.str)) { u.internal.str[size] = '\0'; u.internal.size = size; } else { u.internal.size = -1; u.external.str = reinterpret_cast(std::malloc(size + 1)); if (!u.external.str) { throw std::bad_alloc(); } u.external.size = size; u.external.str[size] = '\0'; } } basic_sstring(const char_type* x, size_t size) { if (size_type(size) != size) { throw std::overflow_error("sstring overflow"); } if (size + 1 <= sizeof(u.internal.str)) { std::copy(x, x + size, u.internal.str); u.internal.str[size] = '\0'; u.internal.size = size; } else { u.internal.size = -1; u.external.str = reinterpret_cast(std::malloc(size + 1)); if (!u.external.str) { throw std::bad_alloc(); } u.external.size = size; std::copy(x, x + size, u.external.str); u.external.str[size] = '\0'; } } basic_sstring(size_t size, char_type x) : basic_sstring(initialized_later(), size) { memset(begin(), x, size); } basic_sstring(const char* x) : basic_sstring(reinterpret_cast(x), std::strlen(x)) {} basic_sstring(std::basic_string& x) : basic_sstring(x.c_str(), x.size()) {} basic_sstring(std::initializer_list x) : basic_sstring(x.begin(), x.end() - x.begin()) {} basic_sstring(const char_type* b, const char_type* e) : basic_sstring(b, e - b) {} basic_sstring(const std::basic_string& s) : basic_sstring(s.data(), s.size()) {} template basic_sstring(InputIterator first, InputIterator last) : basic_sstring(initialized_later(), std::distance(first, last)) { std::copy(first, last, begin()); } ~basic_sstring() noexcept { if (is_external()) { std::free(u.external.str); } } basic_sstring& operator=(const basic_sstring& x) { basic_sstring tmp(x); swap(tmp); return *this; } basic_sstring& operator=(basic_sstring&& x) noexcept { if (this != &x) { swap(x); x.reset(); } return *this; } operator std::basic_string() const { return { str(), size() }; } size_t size() const noexcept { return is_internal() ? u.internal.size : u.external.size; } size_t length() const noexcept { return size(); } size_t find(char_type t, size_t pos = 0) const noexcept { const char_type* it = str() + pos; const char_type* end = str() + size(); while (it < end) { if (*it == t) { return it - str(); } it++; } return npos; } size_t find(const basic_sstring& s, size_t pos = 0) const noexcept { const char_type* it = str() + pos; const char_type* end = str() + size(); const char_type* c_str = s.str(); const char_type* c_str_end = s.str() + s.size(); while (it < end) { auto i = it; auto j = c_str; while ( i < end && j < c_str_end && *i == *j) { i++; j++; } if (j == c_str_end) { return it - str(); } it++; } return npos; } /** * find_last_of find the last occurrence of c in the string. * When pos is specified, the search only includes characters * at or before position pos. * */ size_t find_last_of (char_type c, size_t pos = npos) const noexcept { const char_type* str_start = str(); if (size()) { if (pos >= size()) { pos = size() - 1; } const char_type* p = str_start + pos + 1; do { p--; if (*p == c) { return (p - str_start); } } while (p != str_start); } return npos; } /** * Append a C substring. * @param s The C string to append. * @param n The number of characters to append. * @return Reference to this string. */ basic_sstring& append (const char_type* s, size_t n) { basic_sstring ret(initialized_later(), size() + n); std::copy(begin(), end(), ret.begin()); std::copy(s, s + n, ret.begin() + size()); *this = std::move(ret); return *this; } /** * Replace characters with a value of a C style substring. * */ basic_sstring& replace(size_type pos, size_type n1, const char_type* s, size_type n2) { if (pos > size()) { throw std::out_of_range("sstring::replace out of range"); } if (n1 > size() - pos) { n1 = size() - pos; } if (n1 == n2) { if (n2) { std::copy(s, s + n2, begin() + pos); } return *this; } basic_sstring ret(initialized_later(), size() + n2 - n1); char_type* p= ret.begin(); std::copy(begin(), begin() + pos, p); p += pos; if (n2) { std::copy(s, s + n2, p); } p += n2; std::copy(begin() + pos + n1, end(), p); *this = std::move(ret); return *this; } template basic_sstring& replace (const_iterator i1, const_iterator i2, InputIterator first, InputIterator last) { if (i1 < begin() || i1 > end() || i2 < begin()) { throw std::out_of_range("sstring::replace out of range"); } if (i2 > end()) { i2 = end(); } if (i2 - i1 == last - first) { //in place replacement std::copy(first, last, const_cast(i1)); return *this; } basic_sstring ret(initialized_later(), size() + (last - first) - (i2 - i1)); char_type* p = ret.begin(); p = std::copy(cbegin(), i1, p); p = std::copy(first, last, p); std::copy(i2, cend(), p); *this = std::move(ret); return *this; } iterator erase(iterator first, iterator last) { size_t pos = first - begin(); replace(pos, last - first, nullptr, 0); return begin() + pos; } /** * Inserts additional characters into the string right before * the character indicated by p. */ template void insert(const_iterator p, InputIterator beg, InputIterator end) { replace(p, p, beg, end); } /** * Returns a read/write reference to the data at the last * element of the string. * This function shall not be called on empty strings. */ reference back() noexcept { return operator[](size() - 1); } /** * Returns a read-only (constant) reference to the data at the last * element of the string. * This function shall not be called on empty strings. */ const_reference back() const noexcept { return operator[](size() - 1); } basic_sstring substr(size_t from, size_t len = npos) const { if (from > size()) { throw std::out_of_range("sstring::substr out of range"); } if (len > size() - from) { len = size() - from; } if (len == 0) { return ""; } return { str() + from , len }; } const char_type& at(size_t pos) const { if (pos >= size()) { throw std::out_of_range("sstring::at out of range"); } return *(str() + pos); } char_type& at(size_t pos) { if (pos >= size()) { throw std::out_of_range("sstring::at out of range"); } return *(str() + pos); } bool empty() const noexcept { return u.internal.size == 0; } void reset() noexcept { if (is_external()) { std::free(u.external.str); } u.internal.size = 0; u.internal.str[0] = '\0'; } int compare(const basic_sstring& x) const noexcept { auto n = traits_type::compare(begin(), x.begin(), std::min(size(), x.size())); if (n != 0) { return n; } if (size() < x.size()) { return -1; } else if (size() > x.size()) { return 1; } else { return 0; } } int compare(size_t pos, size_t sz, const basic_sstring& x) const { if (pos > size()) { throw std::out_of_range("pos larger than string size"); } sz = std::min(size() - pos, sz); auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size())); if (n != 0) { return n; } if (sz < x.size()) { return -1; } else if (sz > x.size()) { return 1; } else { return 0; } } void swap(basic_sstring& x) noexcept { contents tmp; tmp = x.u; x.u = u; u = tmp; } const char_type* c_str() const { return str(); } const char_type* begin() const { return str(); } const char_type* end() const { return str() + size(); } const char_type* cbegin() const { return str(); } const char_type* cend() const { return str() + size(); } char_type* begin() { return str(); } char_type* end() { return str() + size(); } bool operator==(const basic_sstring& x) const { return size() == x.size() && std::equal(begin(), end(), x.begin()); } bool operator!=(const basic_sstring& x) const { return !operator==(x); } bool operator<(const basic_sstring& x) const { return compare(x) < 0; } basic_sstring operator+(const basic_sstring& x) const { basic_sstring ret(initialized_later(), size() + x.size()); std::copy(begin(), end(), ret.begin()); std::copy(x.begin(), x.end(), ret.begin() + size()); return ret; } basic_sstring& operator+=(const basic_sstring& x) { return *this = *this + x; } char_type& operator[](size_type pos) { return str()[pos]; } const char_type& operator[](size_type pos) const { return str()[pos]; } operator boost::basic_string_view() const { return boost::basic_string_view(str(), size()); } template friend inline string_type to_sstring(T value); }; template constexpr Size basic_sstring::npos; template inline basic_sstring operator+(const char(&s)[N], const basic_sstring& t) { using sstring = basic_sstring; // don't copy the terminating NUL character sstring ret(typename sstring::initialized_later(), N-1 + t.size()); auto p = std::copy(std::begin(s), std::end(s)-1, ret.begin()); std::copy(t.begin(), t.end(), p); return ret; } template static inline size_t str_len(const char(&s)[N]) { return N - 1; } template static inline const char* str_begin(const char(&s)[N]) { return s; } template static inline const char* str_end(const char(&s)[N]) { return str_begin(s) + str_len(s); } template static inline const char_type* str_begin(const basic_sstring& s) { return s.begin(); } template static inline const char_type* str_end(const basic_sstring& s) { return s.end(); } template static inline size_type str_len(const basic_sstring& s) { return s.size(); } template static inline size_t str_len(const First& first, const Second& second, const Tail&... tail) { return str_len(first) + str_len(second, tail...); } template inline void swap(basic_sstring& x, basic_sstring& y) noexcept { return x.swap(y); } template inline std::basic_ostream& operator<<(std::basic_ostream& os, const basic_sstring& s) { return os.write(s.begin(), s.size()); } template inline std::basic_istream& operator>>(std::basic_istream& is, basic_sstring& s) { std::string tmp; is >> tmp; s = tmp; return is; } namespace std { template struct hash> { size_t operator()(const basic_sstring& s) const { using traits_type = std::char_traits; return std::hash>()(s); } }; } static inline char* copy_str_to(char* dst) { return dst; } template static inline char* copy_str_to(char* dst, const Head& head, const Tail&... tail) { return copy_str_to(std::copy(str_begin(head), str_end(head), dst), tail...); } template static String make_sstring(Args&&... args) { String ret(sstring::initialized_later(), str_len(args...)); copy_str_to(ret.begin(), args...); return ret; } template inline string_type to_sstring(T value) { return sstring::to_sstring(value); } // encode/decode template struct denc_traits> { private: using value_type = basic_sstring; public: static constexpr bool supported = true; static constexpr bool featured = false; static constexpr bool bounded = false; static void bound_encode(const value_type& s, size_t& p, uint64_t f=0) { p += sizeof(Size) + s.size(); } static void encode_nohead(const value_type& s, buffer::list::contiguous_appender& p) { auto len = s.size(); if (len) { p.append(reinterpret_cast(s.c_str()), len); } } static void decode_nohead(size_t len, value_type& s, buffer::ptr::iterator& p) { s.reset(); if (len) { s.append(reinterpret_cast(p.get_pos_add(len)), len); } } static void encode(const value_type& s, buffer::list::contiguous_appender& p, uint64_t f=0) { Size len = (Size)(s.size()); ::denc(len, p); if (len) { p.append(reinterpret_cast(s.c_str()), len); } } static void decode(value_type& s, buffer::ptr::iterator& p, uint64_t f=0) { Size len; ::denc(len, p); decode_nohead(len, s, p); } }; #if 0 /* XXX conflicts w/Ceph types.h */ template inline std::ostream& operator<<(std::ostream& os, const std::vector& v) { bool first = true; os << "{"; for (auto&& elem : v) { if (!first) { os << ", "; } else { first = false; } os << elem; } os << "}"; return os; } #endif #endif /* SSTRING_HH_ */