libstdc++: Do not leak empty COW strings
authorJonathan Wakely <jwakely@redhat.com>
Thu, 2 Dec 2021 15:50:17 +0000 (15:50 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Thu, 9 Dec 2021 22:51:06 +0000 (22:51 +0000)
commitfb9875ebf10c86be21824cb836b7b3b80f3a731b
treeb19876ff4933e2237a1ed475ac362074b1f585e0
parentf8463b0e3ec2438b4cfb8c9a468d59761db2c8a0
libstdc++: Do not leak empty COW strings

When non-const references, pointers or iterators are obtained to the
contents of a COW std::basic_string, the implementation has to assume it
could result in a write to the contents. If the string was previously
shared, it does the "copy-on-write" step of creating a new copy of the
data that is not shared by another object.  It also marks the string as
"leaked", so that no future copies of it will share ownership either.

However, if the string is empty then the only character in the sequence
is the terminating null, and modifying that is undefined behaviour. This
means that non-const references/pointers/iterators to an empty string
are effectively const. Since no direct modification is possible, there
is no need to "leak" the string, it can be safely shared with other
objects. This avoids unnecessary allocations to create new copies of
empty strings that can't be modified anyway.

We already did this optimization for strings that share ownership of the
static _S_empty_rep() object, but not for strings that have non-zero
capacity, and not for fully-dynamic-strings (where the _S_empty_rep()
object is never used).

With this change we avoid two allocations in the return statement:

  std::string s;
  s.reserve(1);       // allocate
  std::string s2 = s;
  std::string s3 = s;
  return s[0] + s2[0] + s3[0]; // leak+allocate twice

libstdc++-v3/ChangeLog:

* include/bits/cow_string.h (basic_string::_M_leak_hard): Do not
reallocate an empty string.
libstdc++-v3/include/bits/cow_string.h