[llvm-branch-commits] [libcxx] release/23.x: [libc++][format] Don't leave the output buffer full after a bulk write (#226791) (PR #226868)

via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sun Sep 27 18:55:53 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: llvmbot

<details>
<summary>Changes</summary>

Backport 372dc97670f7b3ce422dc26e5f626b2711426e8d

Requested by: @<!-- -->frederick-vs-ja

---
Full diff: https://github.com/llvm/llvm-project/pull/226868.diff


2 Files Affected:

- (modified) libcxx/include/__format/buffer.h (+12) 
- (added) libcxx/test/std/utilities/format/format.functions/bug_154670.pass.cpp (+74) 


``````````diff
diff --git a/libcxx/include/__format/buffer.h b/libcxx/include/__format/buffer.h
index e7454f08f45d5..fbb67b02a6d17 100644
--- a/libcxx/include/__format/buffer.h
+++ b/libcxx/include/__format/buffer.h
@@ -250,6 +250,10 @@ class __output_buffer {
       __first += __chunk;
       __n -= __chunk;
     } while (__n);
+
+    // push_back needs a free code unit, which the last chunk may have used.
+    if (__size_ == __capacity_)
+      __prepare_write(0);
   }
 
   /// A std::transform wrapper.
@@ -276,6 +280,10 @@ class __output_buffer {
       __first += __chunk;
       __n -= __chunk;
     } while (__n);
+
+    // push_back needs a free code unit, which the last chunk may have used.
+    if (__size_ == __capacity_)
+      __prepare_write(0);
   }
 
   /// A \c fill_n wrapper.
@@ -293,6 +301,10 @@ class __output_buffer {
       __size_ += __chunk;
       __n -= __chunk;
     } while (__n);
+
+    // push_back needs a free code unit, which the last chunk may have used.
+    if (__size_ == __capacity_)
+      __prepare_write(0);
   }
 
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t __capacity() const { return __capacity_; }
diff --git a/libcxx/test/std/utilities/format/format.functions/bug_154670.pass.cpp b/libcxx/test/std/utilities/format/format.functions/bug_154670.pass.cpp
new file mode 100644
index 0000000000000..7f36d7d95b6f9
--- /dev/null
+++ b/libcxx/test/std/utilities/format/format.functions/bug_154670.pass.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// XFAIL: availability-fp_to_chars-missing
+
+// https://llvm.org/PR154670
+//
+// Formatting into a back_insert_iterator of a string, vector or deque goes
+// through a fixed-size buffer. Tests that the code unit written after an
+// argument or a fill that ends at a multiple of the buffer's size is not
+// written past the end of that buffer.
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <deque>
+#include <format>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "make_string.h"
+#include "test_macros.h"
+
+#define SV(S) MAKE_STRING_VIEW(CharT, S)
+
+template <class CharT, class Container>
+void test(std::size_t size) {
+  const std::basic_string<CharT> arg(size, CharT('a'));
+  const std::basic_string<CharT> expected = arg + CharT('!');
+
+  { // The argument is copied.
+    Container out;
+    std::format_to(std::back_inserter(out), SV("{}!"), arg);
+    assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end()));
+  }
+  if (size != 0) { // The argument's padding is filled.
+    Container out;
+    std::format_to(std::back_inserter(out), SV("{:a<{}}!"), SV(""), size);
+    assert(std::equal(out.begin(), out.end(), expected.begin(), expected.end()));
+  }
+  for (std::size_t n : {size, size + 1, size + 2}) {
+    Container out;
+    auto result = std::format_to_n(std::back_inserter(out), n, SV("{}!"), arg);
+    assert(result.size == static_cast<std::ptrdiff_t>(expected.size()));
+    std::size_t written = std::min(n, expected.size());
+    assert(std::equal(out.begin(), out.end(), expected.begin(), expected.begin() + written));
+  }
+}
+
+template <class CharT>
+void test() {
+  for (std::size_t size : {0, 1, 255, 256, 257, 511, 512, 513, 1024}) {
+    test<CharT, std::basic_string<CharT>>(size);
+    test<CharT, std::vector<CharT>>(size);
+    test<CharT, std::deque<CharT>>(size);
+  }
+}
+
+int main(int, char**) {
+  test<char>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+  test<wchar_t>();
+#endif
+
+  return 0;
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/226868


More information about the llvm-branch-commits mailing list