Constructing a string from a streambuf iterator pair or range has
been found to be 3x faster using the streambuf member `sgetn`.
This is the minimal implementation as outlined in [PR112925], not
checking for an underlying filebuf.
libstdc++-v3/ChangeLog:
PR libstdc++/112925
* include/bits/basic_string.h: (basic_string range ctor)
Special-case ranges of streambuf iterators.
* include/bits/basic_string.tcc: (_M_construct) Special-case
streambuf iterators. These always consume the whole stream.
* testsuite/21_strings/basic_string/cons/from_range2.cc:
New test.
---
libstdc++-v3/include/bits/basic_string.h | 6 +++-
libstdc++-v3/include/bits/basic_string.tcc | 15 ++++++++++
.../basic_string/cons/from_range2.cc | 29 +++++++++++++++++++
3 files changed, 49 insertions(+), 1 deletion(-)
create mode 100644
libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
diff --git a/libstdc++-v3/include/bits/basic_string.h
b/libstdc++-v3/include/bits/basic_string.h
index fd9419220a4..513ea274ca7 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -826,7 +826,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
basic_string(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
: basic_string(__a)
{
- if constexpr (ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
+ using _SbufIter = istreambuf_iterator<_CharT, char_traits<_CharT>>;
+ if constexpr(is_same_v<ranges::iterator_t<_Rg>, _SbufIter>)
+ _M_construct(__rg.begin(), _SbufIter(), input_iterator_tag{});
+ else if constexpr (
+ ranges::forward_range<_Rg> || ranges::sized_range<_Rg>)
{
const auto __n = static_cast<size_type>(ranges::distance(__rg));
reserve(__n);
diff --git a/libstdc++-v3/include/bits/basic_string.tcc
b/libstdc++-v3/include/bits/basic_string.tcc
index de82e997888..d68a4492ef0 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -199,6 +199,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_init_local_buf();
+ // NB: size() (i.e. _M_length) is initialized only at the end.
while (__beg != __end && __len < __capacity)
{
_M_local_buf[__len++] = *__beg;
@@ -228,6 +229,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_M_data(__another.__ptr);
_M_capacity(__capacity);
}
+
+#if __cplusplus >= 201103L
+ using _SbufIter = istreambuf_iterator<_CharT, char_traits<_CharT>>;
+ if _GLIBCXX17_CONSTEXPR (is_same<_InIterator, _SbufIter>::value)
+ {
+ if (!std::__is_constant_evaluated())
+ {
+ auto __r = std::__copy_n_a(__beg, __capacity - __len,
+ data() + __len, false);
+ __len = __r - data();
+ continue;
+ }
+ }
+#endif
traits_type::assign(_M_data()[__len++],
static_cast<_CharT>(*__beg));
++__beg;
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
new file mode 100644
index 00000000000..33a654f35af
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range2.cc
@@ -0,0 +1,29 @@
+// { dg-do run { target c++23 } }
+
+#include <string>
+#include <sstream>
+#include <streambuf>
+#include <ranges>
+#include <testsuite_hooks.h>
+
+struct istreambuf_view : std::ranges::view_interface<istreambuf_view> {
+ std::istream& str;
+ istreambuf_view(std::istream& str) : str(str) {}
+ auto begin() { return std::istreambuf_iterator<char>(str); }
+ auto end() { return std::istreambuf_iterator<char>(); }
+};
+
+constexpr void
+do_test()
+{
+ std::string abcs{"abcdefghijklmnopqrstuvwxyz"};
+ std::istringstream stream{abcs + abcs};
+ istreambuf_view range(stream);
+ std::basic_string str{std::from_range, range};
+ VERIFY(str == abcs + abcs);
+}
+
+int main()
+{
+ do_test();
+}
--
2.56.0