Skip to content

Commit e28ca2d

Browse files
authored
[libc] Support C23 'b' (binary) modifier in printf (#80851)
Reference: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2612.pdf. Fixes #80727.
1 parent 34f61cf commit e28ca2d

File tree

5 files changed

+149
-6
lines changed

5 files changed

+149
-6
lines changed

libc/src/stdio/printf_core/converter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ int convert(Writer *writer, const FormatSection &to_conv) {
5858
case 'o':
5959
case 'x':
6060
case 'X':
61+
case 'b':
62+
case 'B':
6163
return convert_int(writer, to_conv);
6264
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
6365
case 'f':

libc/src/stdio/printf_core/int_converter.h

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ using HexFmt = IntegerToString<uintmax_t, radix::Hex>;
3333
using HexFmtUppercase = IntegerToString<uintmax_t, radix::Hex::Uppercase>;
3434
using OctFmt = IntegerToString<uintmax_t, radix::Oct>;
3535
using DecFmt = IntegerToString<uintmax_t>;
36+
using BinFmt = IntegerToString<uintmax_t, radix::Bin>;
3637

3738
LIBC_INLINE constexpr size_t num_buf_size() {
38-
constexpr auto max = [](size_t a, size_t b) -> size_t {
39-
return (a < b) ? b : a;
40-
};
41-
return max(HexFmt::buffer_size(),
42-
max(HexFmtUppercase::buffer_size(),
43-
max(OctFmt::buffer_size(), DecFmt::buffer_size())));
39+
cpp::array<size_t, 5> sizes{
40+
HexFmt::buffer_size(), HexFmtUppercase::buffer_size(),
41+
OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()};
42+
43+
auto result = sizes[0];
44+
for (size_t i = 1; i < sizes.size(); i++)
45+
result = cpp::max(result, sizes[i]);
46+
return result;
4447
}
4548

4649
LIBC_INLINE cpp::optional<cpp::string_view>
@@ -52,6 +55,8 @@ num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
5255
return HexFmtUppercase::format_to(bufref, num);
5356
} else if (conv_name == 'o') {
5457
return OctFmt::format_to(bufref, num);
58+
} else if (to_lower(conv_name) == 'b') {
59+
return BinFmt::format_to(bufref, num);
5560
} else {
5661
return DecFmt::format_to(bufref, num);
5762
}
@@ -116,6 +121,11 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
116121
prefix_len = 2;
117122
prefix[0] = '0';
118123
prefix[1] = a + ('x' - 'a');
124+
} else if ((to_lower(to_conv.conv_name) == 'b') &&
125+
((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) {
126+
prefix_len = 2;
127+
prefix[0] = '0';
128+
prefix[1] = a + ('b' - 'a');
119129
} else {
120130
prefix_len = (sign_char == 0 ? 0 : 1);
121131
prefix[0] = sign_char;

libc/src/stdio/printf_core/parser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ template <typename ArgProvider> class Parser {
159159
case ('x'):
160160
case ('X'):
161161
case ('u'):
162+
case ('b'):
163+
case ('B'):
162164
switch (lm) {
163165
case (LengthModifier::hh):
164166
case (LengthModifier::h):
@@ -484,6 +486,8 @@ template <typename ArgProvider> class Parser {
484486
case ('x'):
485487
case ('X'):
486488
case ('u'):
489+
case ('b'):
490+
case ('B'):
487491
switch (lm) {
488492
case (LengthModifier::hh):
489493
case (LengthModifier::h):

libc/test/src/stdio/printf_core/converter_test.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,20 @@ TEST_F(LlvmLibcPrintfConverterTest, HexConversion) {
210210
ASSERT_EQ(writer.get_chars_written(), 18);
211211
}
212212

213+
TEST_F(LlvmLibcPrintfConverterTest, BinaryConversion) {
214+
LIBC_NAMESPACE::printf_core::FormatSection section;
215+
section.has_conv = true;
216+
section.raw_string = "%b";
217+
section.conv_name = 'b';
218+
section.conv_val_raw = 42;
219+
LIBC_NAMESPACE::printf_core::convert(&writer, section);
220+
221+
wb.buff[wb.buff_cur] = '\0';
222+
223+
ASSERT_STREQ(str, "101010");
224+
ASSERT_EQ(writer.get_chars_written(), 6);
225+
}
226+
213227
TEST_F(LlvmLibcPrintfConverterTest, PointerConversion) {
214228

215229
LIBC_NAMESPACE::printf_core::FormatSection section;

libc/test/src/stdio/sprintf_test.cpp

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,119 @@ TEST(LlvmLibcSPrintfTest, HexConv) {
410410
ASSERT_STREQ(buff, "007F 0x1000000000 002 ");
411411
}
412412

413+
TEST(LlvmLibcSPrintfTest, BinConv) {
414+
char buff[64];
415+
int written;
416+
417+
// Basic Tests.
418+
419+
written = LIBC_NAMESPACE::sprintf(buff, "%b", 42);
420+
EXPECT_EQ(written, 6);
421+
ASSERT_STREQ(buff, "101010");
422+
423+
written = LIBC_NAMESPACE::sprintf(buff, "%B", 12081991);
424+
EXPECT_EQ(written, 24);
425+
ASSERT_STREQ(buff, "101110000101101101000111");
426+
427+
// Min Width Tests.
428+
429+
written = LIBC_NAMESPACE::sprintf(buff, "%10b", 0b101010);
430+
EXPECT_EQ(written, 10);
431+
ASSERT_STREQ(buff, " 101010");
432+
433+
written = LIBC_NAMESPACE::sprintf(buff, "%2B", 0b101010);
434+
EXPECT_EQ(written, 6);
435+
ASSERT_STREQ(buff, "101010");
436+
437+
// Precision Tests.
438+
439+
written = LIBC_NAMESPACE::sprintf(buff, "%b", 0);
440+
EXPECT_EQ(written, 1);
441+
ASSERT_STREQ(buff, "0");
442+
443+
written = LIBC_NAMESPACE::sprintf(buff, "%.0b", 0);
444+
EXPECT_EQ(written, 0);
445+
ASSERT_STREQ(buff, "");
446+
447+
written = LIBC_NAMESPACE::sprintf(buff, "%.5b", 0b111);
448+
EXPECT_EQ(written, 5);
449+
ASSERT_STREQ(buff, "00111");
450+
451+
written = LIBC_NAMESPACE::sprintf(buff, "%.2b", 0b111);
452+
EXPECT_EQ(written, 3);
453+
ASSERT_STREQ(buff, "111");
454+
455+
written = LIBC_NAMESPACE::sprintf(buff, "%3b", 0b111);
456+
EXPECT_EQ(written, 3);
457+
ASSERT_STREQ(buff, "111");
458+
459+
// Flag Tests.
460+
461+
written = LIBC_NAMESPACE::sprintf(buff, "%-5b", 0b111);
462+
EXPECT_EQ(written, 5);
463+
ASSERT_STREQ(buff, "111 ");
464+
465+
written = LIBC_NAMESPACE::sprintf(buff, "%#b", 0b111);
466+
EXPECT_EQ(written, 5);
467+
ASSERT_STREQ(buff, "0b111");
468+
469+
written = LIBC_NAMESPACE::sprintf(buff, "%#b", 0);
470+
EXPECT_EQ(written, 1);
471+
ASSERT_STREQ(buff, "0");
472+
473+
written = LIBC_NAMESPACE::sprintf(buff, "%#B", 0b111);
474+
EXPECT_EQ(written, 5);
475+
ASSERT_STREQ(buff, "0B111");
476+
477+
written = LIBC_NAMESPACE::sprintf(buff, "%05b", 0b111);
478+
EXPECT_EQ(written, 5);
479+
ASSERT_STREQ(buff, "00111");
480+
481+
written = LIBC_NAMESPACE::sprintf(buff, "%0#6b", 0b111);
482+
EXPECT_EQ(written, 6);
483+
ASSERT_STREQ(buff, "0b0111");
484+
485+
written = LIBC_NAMESPACE::sprintf(buff, "%-#6b", 0b111);
486+
EXPECT_EQ(written, 6);
487+
ASSERT_STREQ(buff, "0b111 ");
488+
489+
// Combined Tests.
490+
491+
written = LIBC_NAMESPACE::sprintf(buff, "%#-07b", 0b111);
492+
EXPECT_EQ(written, 7);
493+
ASSERT_STREQ(buff, "0b111 ");
494+
495+
written = LIBC_NAMESPACE::sprintf(buff, "%7.5b", 0b111);
496+
EXPECT_EQ(written, 7);
497+
ASSERT_STREQ(buff, " 00111");
498+
499+
written = LIBC_NAMESPACE::sprintf(buff, "%#9.5B", 0b111);
500+
EXPECT_EQ(written, 9);
501+
ASSERT_STREQ(buff, " 0B00111");
502+
503+
written = LIBC_NAMESPACE::sprintf(buff, "%#.b", 0);
504+
EXPECT_EQ(written, 0);
505+
ASSERT_STREQ(buff, "");
506+
507+
written = LIBC_NAMESPACE::sprintf(buff, "%-7.5b", 0b111);
508+
EXPECT_EQ(written, 7);
509+
ASSERT_STREQ(buff, "00111 ");
510+
511+
written = LIBC_NAMESPACE::sprintf(buff, "%5.4b", 0b1111);
512+
EXPECT_EQ(written, 5);
513+
ASSERT_STREQ(buff, " 1111");
514+
515+
// Multiple Conversion Tests.
516+
517+
written = LIBC_NAMESPACE::sprintf(buff, "%10B %-#10b", 0b101, 0b110);
518+
EXPECT_EQ(written, 21);
519+
ASSERT_STREQ(buff, " 101 0b110 ");
520+
521+
written = LIBC_NAMESPACE::sprintf(buff, "%-5.4b%#.4b", 0b101, 0b110);
522+
EXPECT_EQ(written, 11);
523+
ASSERT_STREQ(buff, "0101 0b0110");
524+
}
525+
413526
TEST(LlvmLibcSPrintfTest, PointerConv) {
414527
char buff[64];
415528
int written;

0 commit comments

Comments
 (0)