[libcxx-commits] [libcxx] [libc++] Optimize using std::copy with an ostreambuf_iterator (PR #181815)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Feb 26 03:00:46 PST 2026


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/181815

>From 843ebb5dff82341d1f75f5b09395389eef034605 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 17 Feb 2026 13:39:29 +0100
Subject: [PATCH] [libc++] Optimize using std::copy with an ostreambuf_iterator

---
 libcxx/docs/ReleaseNotes/23.rst               |  2 +
 .../include/__iterator/ostreambuf_iterator.h  | 23 ++++++++
 libcxx/include/__locale_dir/pad_and_output.h  | 16 +-----
 libcxx/test/benchmarks/streams/copy.bench.cpp | 26 +++++++++
 .../specialized_algorithms.compile.pass.cpp   | 12 +++++
 .../alg.copy/ostreambuf.copy.pass.cpp         | 53 +++++++++++++++++++
 libcxx/test/support/stream_types.h            | 26 +++++++++
 7 files changed, 144 insertions(+), 14 deletions(-)
 create mode 100644 libcxx/test/benchmarks/streams/copy.bench.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ostreambuf.copy.pass.cpp

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index b8a8a088abec4..9519f5b6c2938 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -46,6 +46,8 @@ Improvements and New Features
 - The ``std::ranges::fold_left_with_iter`` algorithm has been optimized for
   segmented iterators, resulting in a performance improvement of up to 1.38x
   for ``std::deque<int>`` iterators.
+- ``std::copy(CharT*, CharT*, ostreambuf_iterator<CharT>)`` has been optimized, resulting in performance improvements
+  of up to 25x.
 
 Deprecations and Removals
 -------------------------
diff --git a/libcxx/include/__iterator/ostreambuf_iterator.h b/libcxx/include/__iterator/ostreambuf_iterator.h
index 4a3b2fa024490..588844c4ec117 100644
--- a/libcxx/include/__iterator/ostreambuf_iterator.h
+++ b/libcxx/include/__iterator/ostreambuf_iterator.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___ITERATOR_OSTREAMBUF_ITERATOR_H
 #define _LIBCPP___ITERATOR_OSTREAMBUF_ITERATOR_H
 
+#include <__algorithm/specialized_algorithms.h>
 #include <__config>
 #include <__cstddef/ptrdiff_t.h>
 #include <__fwd/ios.h>
@@ -17,6 +18,8 @@
 #include <__fwd/streambuf.h>
 #include <__iterator/iterator.h>
 #include <__iterator/iterator_traits.h>
+#include <__type_traits/is_same.h>
+#include <__utility/pair.h>
 #include <iosfwd> // for forward declaration of ostreambuf_iterator
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -64,6 +67,26 @@ class ostreambuf_iterator
   friend _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_Ch, _Tr> __pad_and_output(
       ostreambuf_iterator<_Ch, _Tr> __s, const _Ch* __ob, const _Ch* __op, const _Ch* __oe, ios_base& __iob, _Ch __fl);
 #endif // _LIBCPP_HAS_LOCALIZATION
+
+  template <class, class...>
+  friend struct __specialized_algorithm;
+};
+
+template <class _InCharT, class _CharT, class _Traits>
+struct __specialized_algorithm<_Algorithm::__copy,
+                               __iterator_pair<_InCharT*, _InCharT*>,
+                               __single_iterator<ostreambuf_iterator<_CharT, _Traits> > > {
+  static const bool __has_algorithm = is_same<const _CharT, const _InCharT>::value;
+
+  _LIBCPP_HIDE_FROM_ABI static pair<_InCharT*, ostreambuf_iterator<_CharT, _Traits> >
+  operator()(_InCharT* __first, _InCharT* __last, ostreambuf_iterator<_CharT, _Traits> __result) {
+    auto __size = __last - __first;
+    if (__result.__sbuf_ && __size > 0) {
+      if (__result.__sbuf_->sputn(__first, __last - __first) != __size)
+        __result.__sbuf_ = nullptr;
+    }
+    return pair<_InCharT*, ostreambuf_iterator<_CharT, _Traits> >(__last, __result);
+  }
 };
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__locale_dir/pad_and_output.h b/libcxx/include/__locale_dir/pad_and_output.h
index bdd4d2856dad6..4b845792a3087 100644
--- a/libcxx/include/__locale_dir/pad_and_output.h
+++ b/libcxx/include/__locale_dir/pad_and_output.h
@@ -55,13 +55,7 @@ _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_CharT, _Traits> __pad_and_output(
     __ns -= __sz;
   else
     __ns = 0;
-  streamsize __np = __op - __ob;
-  if (__np > 0) {
-    if (__s.__sbuf_->sputn(__ob, __np) != __np) {
-      __s.__sbuf_ = nullptr;
-      return __s;
-    }
-  }
+  __s = std::copy(__ob, __op, __s);
   if (__ns > 0) {
     basic_string<_CharT, _Traits> __sp(__ns, __fl);
     if (__s.__sbuf_->sputn(__sp.data(), __ns) != __ns) {
@@ -69,13 +63,7 @@ _LIBCPP_HIDE_FROM_ABI ostreambuf_iterator<_CharT, _Traits> __pad_and_output(
       return __s;
     }
   }
-  __np = __oe - __op;
-  if (__np > 0) {
-    if (__s.__sbuf_->sputn(__op, __np) != __np) {
-      __s.__sbuf_ = nullptr;
-      return __s;
-    }
-  }
+  __s = std::copy(__op, __oe, __s);
   __iob.width(0);
   return __s;
 }
diff --git a/libcxx/test/benchmarks/streams/copy.bench.cpp b/libcxx/test/benchmarks/streams/copy.bench.cpp
new file mode 100644
index 0000000000000..2f01e81e2b23f
--- /dev/null
+++ b/libcxx/test/benchmarks/streams/copy.bench.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <iterator>
+#include <fstream>
+#include <vector>
+
+#include <benchmark/benchmark.h>
+
+static void bm_copy(benchmark::State& state) {
+  std::vector<char> buffer;
+  buffer.resize(16384);
+
+  std::ofstream stream("/dev/null");
+
+  for (auto _ : state)
+    std::copy(buffer.begin(), buffer.end(), std::ostreambuf_iterator<char>(stream.rdbuf()));
+}
+BENCHMARK(bm_copy)->Name("std::copy(CharT*, CharT*, ostreambuf_iterator)");
+
+BENCHMARK_MAIN();
diff --git a/libcxx/test/libcxx/algorithms/specialized_algorithms.compile.pass.cpp b/libcxx/test/libcxx/algorithms/specialized_algorithms.compile.pass.cpp
index 63084252a9ce1..22e44f897d57f 100644
--- a/libcxx/test/libcxx/algorithms/specialized_algorithms.compile.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/specialized_algorithms.compile.pass.cpp
@@ -13,6 +13,7 @@
 // ADDITIONAL_COMPILE_FLAGS: -Wno-c++14-extensions -Wno-c++17-extensions
 
 #include <algorithm>
+#include <iterator>
 #include <map>
 #include <set>
 #include <vector>
@@ -81,3 +82,14 @@ static_assert(has_alg<Alg::__for_each, iter_pair<const_iter, const_iter>>);
 static_assert(has_alg<Alg::__for_each, single_range<std::multimap<int, int>>>);
 #endif
 } // namespace multimap
+
+namespace ostreambuf_iterator {
+template <class CharT>
+using obi = std::ostreambuf_iterator<CharT>;
+static_assert(has_alg<Alg::__copy, iter_pair<char*, char*>, single_iter<obi<char>>>);
+static_assert(has_alg<Alg::__copy, iter_pair<const char*, const char*>, single_iter<obi<char>>>);
+static_assert(has_alg<Alg::__copy, iter_pair<wchar_t*, wchar_t*>, single_iter<obi<wchar_t>>>);
+static_assert(has_alg<Alg::__copy, iter_pair<const wchar_t*, const wchar_t*>, single_iter<obi<wchar_t>>>);
+static_assert(!has_alg<Alg::__copy, iter_pair<char*, char*>, single_iter<obi<wchar_t>>>);
+
+} // namespace ostreambuf_iterator
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ostreambuf.copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ostreambuf.copy.pass.cpp
new file mode 100644
index 0000000000000..f21e81bac06ee
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ostreambuf.copy.pass.cpp
@@ -0,0 +1,53 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template<class CharT, class Traits>
+//   constexpr OutIter
+//   copy(CharT* first, CharT* last, ostreambuf_iterator<CharT, Traits> result);
+
+// UNSUPPORTED: no-localization
+
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <sstream>
+#include <type_traits>
+
+#include "stream_types.h"
+#include "test_macros.h"
+
+template <class CCharT>
+void test() {
+  using CharT = typename std::remove_cv<CCharT>::type;
+  {
+    std::basic_ostringstream<CharT> oss;
+    CCharT buff[] = {'B', 'a', 'n', 'a', 'n', 'e'};
+    std::copy(std::begin(buff), std::end(buff), std::ostreambuf_iterator<CharT>(oss));
+    assert(oss.str() == std::basic_string_view<CharT>(buff, 6));
+  }
+  {
+    failing_streambuf<CharT> fsb(4);
+    std::basic_ostream<CharT> oss(&fsb);
+    CCharT buff[] = {'B', 'a', 'n', 'a', 'n', 'e'};
+    auto res      = std::copy(std::begin(buff), std::end(buff), std::ostreambuf_iterator<CharT>(oss));
+    assert(res.failed());
+    assert(fsb.str() == std::basic_string_view<CharT>(buff, 4));
+  }
+}
+
+int main(int, char**) {
+  test<char>();
+  test<const char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+  test<const wchar_t>();
+#endif
+  return 0;
+}
diff --git a/libcxx/test/support/stream_types.h b/libcxx/test/support/stream_types.h
index 7817fc33e914c..3826bd79a58a3 100644
--- a/libcxx/test/support/stream_types.h
+++ b/libcxx/test/support/stream_types.h
@@ -41,4 +41,30 @@ class non_buffering_streambuf : public std::basic_streambuf<CharT> {
   size_t index_;
 };
 
+template <class CharT>
+class failing_streambuf : public std::basic_streambuf<CharT> {
+  using char_type   = CharT;
+  using traits_type = std::char_traits<CharT>;
+  using int_type    = typename traits_type::int_type;
+
+public:
+  failing_streambuf(size_t fail_at) : fail_at_(fail_at) {}
+
+  std::basic_string<char_type> str() const { return underlying_data_; }
+
+protected:
+  int_type overflow(int_type c) override {
+    if (underlying_data_.size() == fail_at_)
+      return traits_type::eof();
+    if (traits_type::eq_int_type(c, traits_type::eof()))
+      return traits_type::not_eof(c);
+    underlying_data_.push_back(traits_type::to_char_type(c));
+    return c;
+  }
+
+private:
+  std::basic_string<char_type> underlying_data_;
+  size_t fail_at_;
+};
+
 #endif // TEST_SUPPORT_STREAM_TYPES_H



More information about the libcxx-commits mailing list