Skip to content

Commit

Permalink
sstring: add support for non-nul-terminated sstrings
Browse files Browse the repository at this point in the history
When an sstring is used as a string replacement we need a nul terminator
so that c_str() returns a nul-terminated string. However, when we use
basic_sstring as a byte container (e.g. Scylla's "bytes" type) the nul
terminator is not needed, and only occupies extra space and generates
unnecessary instructions.  This is especially visible with the buddy
allocator and power-of-two sized strings.

Fix by providing a new template parameter, NulTerminate, defaulting to
true. When false, we do not allocate and use extra space for Nul
termination.
Message-Id: <20180401151321.14588-1-avi@scylladb.com>
  • Loading branch information
avikivity authored and duarten committed Apr 2, 2018
1 parent 8c572d1 commit 7328d17
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 39 deletions.
4 changes: 2 additions & 2 deletions core/iostream-impl.hh
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ future<> output_stream<CharType>::write(const char_type* buf) {
}

template<typename CharType>
template<typename StringChar, typename SizeType, SizeType MaxSize>
template<typename StringChar, typename SizeType, SizeType MaxSize, bool NulTerminate>
inline
future<> output_stream<CharType>::write(const basic_sstring<StringChar, SizeType, MaxSize>& s) {
future<> output_stream<CharType>::write(const basic_sstring<StringChar, SizeType, MaxSize, NulTerminate>& s) {
return write(reinterpret_cast<const CharType *>(s.c_str()), s.size());
}

Expand Down
4 changes: 2 additions & 2 deletions core/iostream.hh
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,8 @@ public:
future<> write(const char_type* buf, size_t n);
future<> write(const char_type* buf);

template <typename StringChar, typename SizeType, SizeType MaxSize>
future<> write(const basic_sstring<StringChar, SizeType, MaxSize>& s);
template <typename StringChar, typename SizeType, SizeType MaxSize, bool NulTerminate>
future<> write(const basic_sstring<StringChar, SizeType, MaxSize, NulTerminate>& s);
future<> write(const std::basic_string<char_type>& s);

future<> write(net::packet p);
Expand Down
85 changes: 50 additions & 35 deletions core/sstring.hh
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@

namespace seastar {

template <typename char_type, typename Size, Size max_size>
template <typename char_type, typename Size, Size max_size, bool NulTerminate = true>
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>
template <typename char_type, typename Size, Size max_size, bool NulTerminate>
class basic_sstring {
static_assert(
(std::is_same<char_type, char>::value
Expand Down Expand Up @@ -161,23 +161,26 @@ public:
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);
static constexpr unsigned padding() { return unsigned(NulTerminate); }
public:
struct initialized_later {};

basic_sstring() noexcept {
u.internal.size = 0;
u.internal.str[0] = '\0';
if (NulTerminate) {
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));
u.external.str = reinterpret_cast<char_type*>(std::malloc(x.u.external.size + padding()));
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);
std::copy(x.u.external.str, x.u.external.str + x.u.external.size + padding(), u.external.str);
u.external.size = x.u.external.size;
}
}
Expand All @@ -190,36 +193,44 @@ public:
if (size_type(size) != size) {
throw std::overflow_error("sstring overflow");
}
if (size + 1 <= sizeof(u.internal.str)) {
u.internal.str[size] = '\0';
if (size + padding() <= sizeof(u.internal.str)) {
if (NulTerminate) {
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));
u.external.str = reinterpret_cast<char_type*>(std::malloc(size + padding()));
if (!u.external.str) {
throw std::bad_alloc();
}
u.external.size = size;
u.external.str[size] = '\0';
if (NulTerminate) {
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)) {
if (size + padding() <= sizeof(u.internal.str)) {
std::copy(x, x + size, u.internal.str);
u.internal.str[size] = '\0';
if (NulTerminate) {
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));
u.external.str = reinterpret_cast<char_type*>(std::malloc(size + padding()));
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';
if (NulTerminate) {
u.external.str[size] = '\0';
}
}
}

Expand Down Expand Up @@ -350,7 +361,7 @@ public:
} else if (n < size()) {
if (is_internal()) {
u.internal.size = n;
} else if (n + 1 <= sizeof(u.internal.str)) {
} else if (n + padding() <= sizeof(u.internal.str)) {
*this = basic_sstring(u.external.str, n);
} else {
u.external.size = n;
Expand Down Expand Up @@ -485,7 +496,9 @@ public:
std::free(u.external.str);
}
u.internal.size = 0;
u.internal.str[0] = '\0';
if (NulTerminate) {
u.internal.str[0] = '\0';
}
}
temporary_buffer<char_type> release() && {
if (is_external()) {
Expand All @@ -498,7 +511,9 @@ public:
auto buf = temporary_buffer<char_type>(u.internal.size);
std::copy(u.internal.str, u.internal.str + u.internal.size, buf.get_write());
u.internal.size = 0;
u.internal.str[0] = '\0';
if (NulTerminate) {
u.internal.str[0] = '\0';
}
return buf;
}
}
Expand Down Expand Up @@ -588,14 +603,14 @@ public:
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, Size max_size, bool NulTerminate>
constexpr Size basic_sstring<char_type, Size, max_size, NulTerminate>::npos;

template <typename char_type, typename size_type, size_type Max, size_type N>
template <typename char_type, typename size_type, size_type Max, size_type N, bool NulTerminate>
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>;
basic_sstring<char_type, size_type, Max, NulTerminate>
operator+(const char(&s)[N], const basic_sstring<char_type, size_type, Max, NulTerminate>& t) {
using sstring = basic_sstring<char_type, size_type, Max, NulTerminate>;
// 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());
Expand All @@ -615,17 +630,17 @@ 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>
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
static inline
const char_type* str_begin(const basic_sstring<char_type, size_type, max_size>& s) { return s.begin(); }
const char_type* str_begin(const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) { return s.begin(); }

template <typename char_type, typename size_type, size_type max_size>
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
static inline
const char_type* str_end(const basic_sstring<char_type, size_type, max_size>& s) { return s.end(); }
const char_type* str_end(const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) { return s.end(); }

template <typename char_type, typename size_type, size_type max_size>
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
static inline
size_type str_len(const basic_sstring<char_type, size_type, max_size>& s) { return s.size(); }
size_type str_len(const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) { return s.size(); }

template <typename First, typename Second, typename... Tail>
static inline
Expand All @@ -641,19 +656,19 @@ void swap(basic_sstring<char_type, size_type, max_size>& x,
return x.swap(y);
}

template <typename char_type, typename size_type, size_type max_size, typename char_traits>
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, 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) {
const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
return os.write(s.begin(), s.size());
}

template <typename char_type, typename size_type, size_type max_size, typename char_traits>
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, 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) {
basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
std::string tmp;
is >> tmp;
s = tmp;
Expand All @@ -664,9 +679,9 @@ operator>>(std::basic_istream<char_type, char_traits>& is,

namespace std {

template <typename char_type, typename size_type, size_type max_size>
struct hash<seastar::basic_sstring<char_type, size_type, max_size>> {
size_t operator()(const seastar::basic_sstring<char_type, size_type, max_size>& s) const {
template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
struct hash<seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>> {
size_t operator()(const seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>& s) const {
return std::hash<std::experimental::basic_string_view<char_type>>()(s);
}
};
Expand Down

0 comments on commit 7328d17

Please sign in to comment.