[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 19 02:22:38 PST 2026
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/181815
>From 37100c36bb32066c8f66c6b567afd394d75706e0 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
---
.../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 +++++++++
6 files changed, 142 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/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