diff options
Diffstat (limited to 'src/common/sstring.hh')
-rw-r--r-- | src/common/sstring.hh | 717 |
1 files changed, 717 insertions, 0 deletions
diff --git a/src/common/sstring.hh b/src/common/sstring.hh new file mode 100644 index 000000000..b0fcd9b5c --- /dev/null +++ b/src/common/sstring.hh @@ -0,0 +1,717 @@ +/* + * 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 + * std::string_view. Matt Benjamin <mbenjamin@redhat.com> + */ + +#ifndef SSTRING_HH_ +#define SSTRING_HH_ + +#include <string_view> +#include <type_traits> + +#include "include/buffer.h" +#include "include/denc.h" + +template <typename char_type, typename Size, Size max_size> +class basic_sstring; + +using sstring = basic_sstring<char, uint32_t, 15>; + +template <typename string_type = sstring, typename T> +inline string_type to_sstring(T value); + +template <typename char_type, typename Size, Size max_size> +class basic_sstring { + static_assert( + (std::is_same<char_type, char>::value + || std::is_same<char_type, signed char>::value + || std::is_same<char_type, unsigned char>::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 <typename string_type, typename T> + 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<ch_type*>(tmp), len); + } + + template <typename string_type> + static inline string_type to_sstring(int value) { + return to_sstring_sprintf<string_type>(value, "%d"); + } + + template <typename string_type> + static inline string_type to_sstring(unsigned value) { + return to_sstring_sprintf<string_type>(value, "%u"); + } + + template <typename string_type> + static inline string_type to_sstring(long value) { + return to_sstring_sprintf<string_type>(value, "%ld"); + } + + template <typename string_type> + static inline string_type to_sstring(unsigned long value) { + return to_sstring_sprintf<string_type>(value, "%lu"); + } + + template <typename string_type> + static inline string_type to_sstring(long long value) { + return to_sstring_sprintf<string_type>(value, "%lld"); + } + + template <typename string_type> + static inline string_type to_sstring(unsigned long long value) { + return to_sstring_sprintf<string_type>(value, "%llu"); + } + + template <typename string_type> + static inline string_type to_sstring(float value) { + return to_sstring_sprintf<string_type>(value, "%g"); + } + + template <typename string_type> + static inline string_type to_sstring(double value) { + return to_sstring_sprintf<string_type>(value, "%g"); + } + + template <typename string_type> + static inline string_type to_sstring(long double value) { + return to_sstring_sprintf<string_type>(value, "%Lg"); + } + + template <typename string_type> + static inline string_type to_sstring(const char* value) { + return string_type(value); + } + + template <typename string_type> + static inline string_type to_sstring(sstring value) { + return value; + } + +public: + using value_type = char_type; + using traits_type = std::char_traits<char_type>; + using allocator_type = std::allocator<char_type>; + 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<Size> can be too small + using size_type = Size; + static constexpr size_type npos = static_cast<size_type>(-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<char_type*>(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<char_type*>(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<char_type*>(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<const char_type*>(x), std::strlen(x)) {} + basic_sstring(std::basic_string<char_type>& x) : basic_sstring(x.c_str(), x.size()) {} + basic_sstring(std::initializer_list<char_type> 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<char_type>& s) + : basic_sstring(s.data(), s.size()) {} + template <typename InputIterator> + 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<char_type>() 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 <class InputIterator> + 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<char_type*>(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 <class InputIterator> + 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 std::basic_string_view<char_type, traits_type>() const { + return std::basic_string_view<char_type, traits_type>(str(), size()); + } + template <typename string_type, typename T> + friend inline string_type to_sstring(T value); +}; +template <typename char_type, typename Size, Size max_size> +constexpr Size basic_sstring<char_type, Size, max_size>::npos; + +template <typename char_type, typename size_type, size_type Max, size_type N> +inline +basic_sstring<char_type, size_type, Max> +operator+(const char(&s)[N], const basic_sstring<char_type, size_type, Max>& t) { + using sstring = basic_sstring<char_type, size_type, Max>; + // 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 <size_t N> +static inline +size_t str_len(const char(&s)[N]) { return N - 1; } + +template <size_t N> +static inline +const char* str_begin(const char(&s)[N]) { return s; } + +template <size_t N> +static inline +const char* str_end(const char(&s)[N]) { return str_begin(s) + str_len(s); } + +template <typename char_type, typename size_type, size_type max_size> +static inline +const char_type* str_begin(const basic_sstring<char_type, size_type, max_size>& s) { return s.begin(); } + +template <typename char_type, typename size_type, size_type max_size> +static inline +const char_type* str_end(const basic_sstring<char_type, size_type, max_size>& s) { return s.end(); } + +template <typename char_type, typename size_type, size_type max_size> +static inline +size_type str_len(const basic_sstring<char_type, size_type, max_size>& s) { return s.size(); } + +template <typename First, typename Second, typename... Tail> +static inline +size_t str_len(const First& first, const Second& second, const Tail&... tail) { + return str_len(first) + str_len(second, tail...); +} + +template <typename char_type, typename size_type, size_type max_size> +inline +void swap(basic_sstring<char_type, size_type, max_size>& x, + basic_sstring<char_type, size_type, max_size>& y) noexcept +{ + return x.swap(y); +} + +template <typename char_type, typename size_type, size_type max_size, typename char_traits> +inline +std::basic_ostream<char_type, char_traits>& +operator<<(std::basic_ostream<char_type, char_traits>& os, + const basic_sstring<char_type, size_type, max_size>& s) { + return os.write(s.begin(), s.size()); +} + +template <typename char_type, typename size_type, size_type max_size, typename char_traits> +inline +std::basic_istream<char_type, char_traits>& +operator>>(std::basic_istream<char_type, char_traits>& is, + basic_sstring<char_type, size_type, max_size>& s) { + std::string tmp; + is >> tmp; + s = tmp; + return is; +} + +namespace std { + +template <typename char_type, typename size_type, size_type max_size> +struct hash<basic_sstring<char_type, size_type, max_size>> { + size_t operator()(const basic_sstring<char_type, size_type, max_size>& s) const { + using traits_type = std::char_traits<char_type>; + return std::hash<std::basic_string_view<char_type,traits_type>>()(s); + } +}; + +} + +static inline +char* copy_str_to(char* dst) { + return dst; +} + +template <typename Head, typename... Tail> +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 <typename String = sstring, typename... Args> +static String make_sstring(Args&&... args) +{ + String ret(sstring::initialized_later(), str_len(args...)); + copy_str_to(ret.begin(), args...); + return ret; +} + +template <typename string_type, typename T> +inline string_type to_sstring(T value) { + return sstring::to_sstring<string_type>(value); +} + + +// encode/decode +template <typename Char, typename Size, Size Max> +struct denc_traits<basic_sstring<Char, Size, Max>> { +private: + using value_type = basic_sstring<Char, Size, Max>; +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<const char*>(s.c_str()), len); + } + } + + static void decode_nohead(size_t len, value_type& s, + buffer::ptr::const_iterator& p) + { + s.reset(); + if (len) { + s.append(reinterpret_cast<const Char*>(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<const char*>(s.c_str()), len); + } + } + + static void decode(value_type& s, + buffer::ptr::const_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 <typename T> +inline +std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) { + bool first = true; + os << "{"; + for (auto&& elem : v) { + if (!first) { + os << ", "; + } else { + first = false; + } + os << elem; + } + os << "}"; + return os; +} +#endif + +#endif /* SSTRING_HH_ */ |