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 (!(p2 = (char*)malloc(c2)))
__builtin_trap();
memcpy(p2, data(), size());
p2[size()] = 0;
memcpy(p2, data(), __::string_size);
} else {
if (!(p2 = (char*)realloc(big()->p, c2)))
__builtin_trap();
}
std::atomic_signal_fence(std::memory_order_seq_cst);
set_big_capacity(c2);
big()->n = n;
big()->p = p2;
set_big_string(p2, n, c2);
}
void

View file

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