ctl::string cleanup (#1215)

We now fully initialize a ctl::string’s memory, so that it is always set
to a well-defined value, thus making it always safe to memcpy out of it.
This incidentally makes our string::swap function legal, which it wasn’t
before. This also saves us a store in string::reserve.

Now that we have made both big_string and small_string POD, I believe it
is safe to elide the launder calls, and have done so, thus cleaning up a
lot of the blob-related code.

I also got rid of set_big_capacity and replaced it with a set_big_string
that leaves us in a well-defined state afterwards. This function also is
able to be somewhat simpler; rather than delicate bit-twiddling, it just
reaches straight into blob and rewrites it wholesale.

Overall, this shaves about 1–2ns off of most benchmarks, and adds 1ns to
only one of them - creating a string from a char *.
This commit is contained in:
Jōshin 2024-06-15 10:45:52 -07:00
parent d9b4f647d8
commit 8e37ee2598
No known key found for this signature in database
2 changed files with 19 additions and 28 deletions

View file

@ -91,16 +91,13 @@ string::reserve(size_t c2) noexcept
if (!isbig()) { if (!isbig()) {
if (!(p2 = (char*)malloc(c2))) if (!(p2 = (char*)malloc(c2)))
__builtin_trap(); __builtin_trap();
memcpy(p2, data(), size()); memcpy(p2, data(), __::string_size);
p2[size()] = 0;
} else { } else {
if (!(p2 = (char*)realloc(big()->p, c2))) if (!(p2 = (char*)realloc(big()->p, c2)))
__builtin_trap(); __builtin_trap();
} }
std::atomic_signal_fence(std::memory_order_seq_cst); std::atomic_signal_fence(std::memory_order_seq_cst);
set_big_capacity(c2); set_big_string(p2, n, c2);
big()->n = n;
big()->p = p2;
} }
void void

View file

@ -81,28 +81,23 @@ class string
string() noexcept string() noexcept
{ {
set_small_size(0); __builtin_memset(blob, 0, sizeof(size_t) * 2);
#if 0 // equivalent to set_small_size(0) but also zeroes memory
small()->buf[0] = 0; *(((size_t *)blob) + 2) = __::sso_max << (sizeof(size_t) - 1) * 8;
#endif
} }
void swap(string& s) noexcept void swap(string& s) noexcept
{ {
char tmp[__::string_size]; char tmp[__::string_size];
__builtin_memcpy(tmp, __builtin_launder(blob), sizeof(tmp)); __builtin_memcpy(tmp, blob, __::string_size);
__builtin_memcpy( __builtin_memcpy(blob, s.blob, __::string_size);
__builtin_launder(blob), __builtin_launder(s.blob), sizeof(tmp)); __builtin_memcpy(s.blob, tmp, __::string_size);
__builtin_memcpy(__builtin_launder(s.blob), tmp, sizeof(tmp));
} }
string(string&& s) noexcept string(string&& s) noexcept
{ {
__builtin_memcpy(blob, __builtin_launder(s.blob), sizeof(blob)); __builtin_memcpy(blob, s.blob, __::string_size);
s.set_small_size(0); s.set_small_size(0);
#if 0
s.small()->buf[0] = 0;
#endif
} }
void clear() noexcept void clear() noexcept
@ -288,52 +283,51 @@ class string
private: private:
inline bool isbig() const noexcept inline bool isbig() const noexcept
{ {
return *(__builtin_launder(blob) + __::sso_max) & 0x80; return *(blob + __::sso_max) & 0x80;
} }
inline void set_small_size(size_t size) noexcept inline void set_small_size(size_t size) noexcept
{ {
if (size > __::sso_max) if (size > __::sso_max)
__builtin_trap(); __builtin_trap();
*(__builtin_launder(blob) + __::sso_max) = (__::sso_max - size); *(blob + __::sso_max) = (__::sso_max - size);
} }
inline void set_big_capacity(size_t c2) noexcept inline void set_big_string(char *p, size_t n, size_t c2) noexcept
{ {
if (c2 > __::big_mask) if (c2 > __::big_mask)
__builtin_trap(); __builtin_trap();
*(__builtin_launder(blob) + __::sso_max) = 0x80; *(char **)blob = p;
big()->c &= ~__::big_mask; *(((size_t *)blob) + 1) = n;
big()->c |= c2; *(((size_t *)blob) + 2) = c2 | ~__::big_mask;
} }
inline __::small_string* small() noexcept inline __::small_string* small() noexcept
{ {
if (isbig()) if (isbig())
__builtin_trap(); __builtin_trap();
return __builtin_launder(reinterpret_cast<__::small_string*>(blob)); return reinterpret_cast<__::small_string*>(blob);
} }
inline const __::small_string* small() const noexcept inline const __::small_string* small() const noexcept
{ {
if (isbig()) if (isbig())
__builtin_trap(); __builtin_trap();
return __builtin_launder( return reinterpret_cast<const __::small_string*>(blob);
reinterpret_cast<const __::small_string*>(blob));
} }
inline __::big_string* big() noexcept inline __::big_string* big() noexcept
{ {
if (!isbig()) if (!isbig())
__builtin_trap(); __builtin_trap();
return __builtin_launder(reinterpret_cast<__::big_string*>(blob)); return reinterpret_cast<__::big_string*>(blob);
} }
inline const __::big_string* big() const noexcept inline const __::big_string* big() const noexcept
{ {
if (!isbig()) if (!isbig())
__builtin_trap(); __builtin_trap();
return __builtin_launder(reinterpret_cast<const __::big_string*>(blob)); return reinterpret_cast<const __::big_string*>(blob);
} }
friend string strcat(const string_view, const string_view); friend string strcat(const string_view, const string_view);