Skip to content

Conversation

mordante
Copy link
Member

Per [ostream.formatted.reqmts]/3 padding should only be done when explicitly stated.

Fixes: #116054

Per [ostream.formatted.reqmts]/3 padding should only be done when
explicitly stated.

Fixes: llvm#116054
@mordante mordante requested a review from a team as a code owner February 22, 2025 12:36
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Feb 22, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 22, 2025

@llvm/pr-subscribers-libcxx

Author: Mark de Wever (mordante)

Changes

Per [ostream.formatted.reqmts]/3 padding should only be done when explicitly stated.

Fixes: #116054


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

4 Files Affected:

  • (modified) libcxx/include/__ostream/print.h (+2-12)
  • (modified) libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp (+7-23)
  • (modified) libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp (+7-23)
  • (modified) libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp (+7-23)
diff --git a/libcxx/include/__ostream/print.h b/libcxx/include/__ostream/print.h
index eb4233342214d..a4d7506cffc48 100644
--- a/libcxx/include/__ostream/print.h
+++ b/libcxx/include/__ostream/print.h
@@ -49,21 +49,11 @@ __vprint_nonunicode(ostream& __os, string_view __fmt, format_args __args, bool _
     if (__write_nl)
       __o += '\n';
 
-    const char* __str = __o.data();
-    size_t __len      = __o.size();
-
 #    if _LIBCPP_HAS_EXCEPTIONS
     try {
 #    endif // _LIBCPP_HAS_EXCEPTIONS
-      typedef ostreambuf_iterator<char> _Ip;
-      if (std::__pad_and_output(
-              _Ip(__os),
-              __str,
-              (__os.flags() & ios_base::adjustfield) == ios_base::left ? __str + __len : __str,
-              __str + __len,
-              __os,
-              __os.fill())
-              .failed())
+      if (auto __rdbuf = __os.rdbuf();
+          !__rdbuf || __rdbuf->sputn(__o.data(), __o.size()) != static_cast<streamsize>(__o.size()))
         __os.setstate(ios_base::badbit | ios_base::failbit);
 
 #    if _LIBCPP_HAS_EXCEPTIONS
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
index 711152ba6f32a..1f188b36db9a3 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/print.pass.cpp
@@ -133,6 +133,7 @@ static void test_write_failure() {
   assert(os.fail());
 }
 
+// Test the formatting does no padding.
 static void test_stream_formatting() {
   std::stringstream sstr;
   auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
@@ -148,37 +149,20 @@ static void test_stream_formatting() {
   test("hello", "{}", "hello");
 
   sstr.width(10);
-  test("     hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   sstr.fill('+');
 
   sstr.width(10);
-  test("+++++hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   // *** Test embedded NUL character ***
   using namespace std::literals;
   sstr.width(15);
-  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
-
-  // *** Test Unicode ***
-  // Streams count code units not code points
-  // 2-byte code points
-  sstr.width(5);
-  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
-  sstr.width(5);
-  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
-
-  // 3-byte code points
-  sstr.width(5);
-  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
-  sstr.width(5);
-  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
-
-  // 4-byte code points
-  sstr.width(5);
-  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
-  sstr.width(5);
-  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+  test("hello\0world"sv, "hello{}{}", '\0', "world");
+  assert(sstr.width() == 15);
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
index 73ad19132f17f..7aa7390216adc 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_nonunicode.pass.cpp
@@ -149,6 +149,7 @@ static void test_write_failure() {
   assert(os.fail());
 }
 
+// Test the formatting does no padding.
 static void test_stream_formatting() {
   std::stringstream sstr;
   auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
@@ -164,37 +165,20 @@ static void test_stream_formatting() {
   test("hello", "{}", "hello");
 
   sstr.width(10);
-  test("     hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   sstr.fill('+');
 
   sstr.width(10);
-  test("+++++hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   // *** Test embedded NUL character ***
   using namespace std::literals;
   sstr.width(15);
-  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
-
-  // *** Test Unicode ***
-  // Streams count code units not code points
-  // 2-byte code points
-  sstr.width(5);
-  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
-  sstr.width(5);
-  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
-
-  // 3-byte code points
-  sstr.width(5);
-  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
-  sstr.width(5);
-  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
-
-  // 4-byte code points
-  sstr.width(5);
-  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
-  sstr.width(5);
-  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+  test("hello\0world"sv, "hello{}{}", '\0', "world");
+  assert(sstr.width() == 15);
 }
 
 int main(int, char**) {
diff --git a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
index 21f6654d5f9cf..8b43a71215ade 100644
--- a/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
+++ b/libcxx/test/std/input.output/iostream.format/output.streams/ostream.formatted/ostream.formatted.print/vprint_unicode.pass.cpp
@@ -149,6 +149,7 @@ static void test_write_failure() {
   assert(os.fail());
 }
 
+// Test the formatting does no padding.
 static void test_stream_formatting() {
   std::stringstream sstr;
   auto test = [&]<class... Args>(std::string_view expected, test_format_string<char, Args...> fmt, Args&&... args) {
@@ -164,37 +165,20 @@ static void test_stream_formatting() {
   test("hello", "{}", "hello");
 
   sstr.width(10);
-  test("     hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   sstr.fill('+');
 
   sstr.width(10);
-  test("+++++hello", "{}", "hello");
+  test("hello", "{}", "hello");
+  assert(sstr.width() == 10);
 
   // *** Test embedded NUL character ***
   using namespace std::literals;
   sstr.width(15);
-  test("++++hello\0world"sv, "hello{}{}", '\0', "world");
-
-  // *** Test Unicode ***
-  // Streams count code units not code points
-  // 2-byte code points
-  sstr.width(5);
-  test("+++\u00a1", "{}", "\u00a1"); // INVERTED EXCLAMATION MARK
-  sstr.width(5);
-  test("+++\u07ff", "{}", "\u07ff"); // NKO TAMAN SIGN
-
-  // 3-byte code points
-  sstr.width(5);
-  test("++\u0800", "{}", "\u0800"); // SAMARITAN LETTER ALAF
-  sstr.width(5);
-  test("++\ufffd", "{}", "\ufffd"); // REPLACEMENT CHARACTER
-
-  // 4-byte code points
-  sstr.width(5);
-  test("+\U00010000", "{}", "\U00010000"); // LINEAR B SYLLABLE B008 A
-  sstr.width(5);
-  test("+\U0010FFFF", "{}", "\U0010FFFF"); // Undefined Character
+  test("hello\0world"sv, "hello{}{}", '\0', "world");
+  assert(sstr.width() == 15);
 }
 
 int main(int, char**) {

@mordante mordante merged commit a841cf9 into llvm:main Feb 26, 2025
79 of 83 checks passed
@mordante mordante deleted the review/print_do_not_pad_ostream branch February 26, 2025 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[libc++] vprint_[non]unicode(ostream &, string_view, Args&&...) should not pad the output
3 participants