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

Reply via email to