diff --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp index 74a36cbf7432f..52412aef3c5c1 100644 --- a/libc/src/stdio/printf_core/converter.cpp +++ b/libc/src/stdio/printf_core/converter.cpp @@ -58,6 +58,8 @@ int convert(Writer *writer, const FormatSection &to_conv) { case 'o': case 'x': case 'X': + case 'b': + case 'B': return convert_int(writer, to_conv); #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT case 'f': diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h index 7744d801cbc18..2efbf53d40938 100644 --- a/libc/src/stdio/printf_core/int_converter.h +++ b/libc/src/stdio/printf_core/int_converter.h @@ -33,14 +33,17 @@ using HexFmt = IntegerToString; using HexFmtUppercase = IntegerToString; using OctFmt = IntegerToString; using DecFmt = IntegerToString; +using BinFmt = IntegerToString; LIBC_INLINE constexpr size_t num_buf_size() { - constexpr auto max = [](size_t a, size_t b) -> size_t { - return (a < b) ? b : a; - }; - return max(HexFmt::buffer_size(), - max(HexFmtUppercase::buffer_size(), - max(OctFmt::buffer_size(), DecFmt::buffer_size()))); + cpp::array sizes{ + HexFmt::buffer_size(), HexFmtUppercase::buffer_size(), + OctFmt::buffer_size(), DecFmt::buffer_size(), BinFmt::buffer_size()}; + + auto result = sizes[0]; + for (size_t i = 1; i < sizes.size(); i++) + result = cpp::max(result, sizes[i]); + return result; } LIBC_INLINE cpp::optional @@ -52,6 +55,8 @@ num_to_strview(uintmax_t num, cpp::span bufref, char conv_name) { return HexFmtUppercase::format_to(bufref, num); } else if (conv_name == 'o') { return OctFmt::format_to(bufref, num); + } else if (to_lower(conv_name) == 'b') { + return BinFmt::format_to(bufref, num); } else { return DecFmt::format_to(bufref, num); } @@ -116,6 +121,11 @@ LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) { prefix_len = 2; prefix[0] = '0'; prefix[1] = a + ('x' - 'a'); + } else if ((to_lower(to_conv.conv_name) == 'b') && + ((flags & FormatFlags::ALTERNATE_FORM) != 0) && num != 0) { + prefix_len = 2; + prefix[0] = '0'; + prefix[1] = a + ('b' - 'a'); } else { prefix_len = (sign_char == 0 ? 0 : 1); prefix[0] = sign_char; diff --git a/libc/src/stdio/printf_core/parser.h b/libc/src/stdio/printf_core/parser.h index ab491655275fb..1e7d2e58c924a 100644 --- a/libc/src/stdio/printf_core/parser.h +++ b/libc/src/stdio/printf_core/parser.h @@ -159,6 +159,8 @@ template class Parser { case ('x'): case ('X'): case ('u'): + case ('b'): + case ('B'): switch (lm) { case (LengthModifier::hh): case (LengthModifier::h): @@ -484,6 +486,8 @@ template class Parser { case ('x'): case ('X'): case ('u'): + case ('b'): + case ('B'): switch (lm) { case (LengthModifier::hh): case (LengthModifier::h): diff --git a/libc/test/src/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp index 8404ef6ec7db4..9da749f3b8ad1 100644 --- a/libc/test/src/stdio/printf_core/converter_test.cpp +++ b/libc/test/src/stdio/printf_core/converter_test.cpp @@ -210,6 +210,20 @@ TEST_F(LlvmLibcPrintfConverterTest, HexConversion) { ASSERT_EQ(writer.get_chars_written(), 18); } +TEST_F(LlvmLibcPrintfConverterTest, BinaryConversion) { + LIBC_NAMESPACE::printf_core::FormatSection section; + section.has_conv = true; + section.raw_string = "%b"; + section.conv_name = 'b'; + section.conv_val_raw = 42; + LIBC_NAMESPACE::printf_core::convert(&writer, section); + + wb.buff[wb.buff_cur] = '\0'; + + ASSERT_STREQ(str, "101010"); + ASSERT_EQ(writer.get_chars_written(), 6); +} + TEST_F(LlvmLibcPrintfConverterTest, PointerConversion) { LIBC_NAMESPACE::printf_core::FormatSection section; diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp index f3614b05a0c3e..186b37e2898af 100644 --- a/libc/test/src/stdio/sprintf_test.cpp +++ b/libc/test/src/stdio/sprintf_test.cpp @@ -410,6 +410,119 @@ TEST(LlvmLibcSPrintfTest, HexConv) { ASSERT_STREQ(buff, "007F 0x1000000000 002 "); } +TEST(LlvmLibcSPrintfTest, BinConv) { + char buff[64]; + int written; + + // Basic Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%b", 42); + EXPECT_EQ(written, 6); + ASSERT_STREQ(buff, "101010"); + + written = LIBC_NAMESPACE::sprintf(buff, "%B", 12081991); + EXPECT_EQ(written, 24); + ASSERT_STREQ(buff, "101110000101101101000111"); + + // Min Width Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%10b", 0b101010); + EXPECT_EQ(written, 10); + ASSERT_STREQ(buff, " 101010"); + + written = LIBC_NAMESPACE::sprintf(buff, "%2B", 0b101010); + EXPECT_EQ(written, 6); + ASSERT_STREQ(buff, "101010"); + + // Precision Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%b", 0); + EXPECT_EQ(written, 1); + ASSERT_STREQ(buff, "0"); + + written = LIBC_NAMESPACE::sprintf(buff, "%.0b", 0); + EXPECT_EQ(written, 0); + ASSERT_STREQ(buff, ""); + + written = LIBC_NAMESPACE::sprintf(buff, "%.5b", 0b111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, "00111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%.2b", 0b111); + EXPECT_EQ(written, 3); + ASSERT_STREQ(buff, "111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%3b", 0b111); + EXPECT_EQ(written, 3); + ASSERT_STREQ(buff, "111"); + + // Flag Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%-5b", 0b111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, "111 "); + + written = LIBC_NAMESPACE::sprintf(buff, "%#b", 0b111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, "0b111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%#b", 0); + EXPECT_EQ(written, 1); + ASSERT_STREQ(buff, "0"); + + written = LIBC_NAMESPACE::sprintf(buff, "%#B", 0b111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, "0B111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%05b", 0b111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, "00111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%0#6b", 0b111); + EXPECT_EQ(written, 6); + ASSERT_STREQ(buff, "0b0111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%-#6b", 0b111); + EXPECT_EQ(written, 6); + ASSERT_STREQ(buff, "0b111 "); + + // Combined Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%#-07b", 0b111); + EXPECT_EQ(written, 7); + ASSERT_STREQ(buff, "0b111 "); + + written = LIBC_NAMESPACE::sprintf(buff, "%7.5b", 0b111); + EXPECT_EQ(written, 7); + ASSERT_STREQ(buff, " 00111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%#9.5B", 0b111); + EXPECT_EQ(written, 9); + ASSERT_STREQ(buff, " 0B00111"); + + written = LIBC_NAMESPACE::sprintf(buff, "%#.b", 0); + EXPECT_EQ(written, 0); + ASSERT_STREQ(buff, ""); + + written = LIBC_NAMESPACE::sprintf(buff, "%-7.5b", 0b111); + EXPECT_EQ(written, 7); + ASSERT_STREQ(buff, "00111 "); + + written = LIBC_NAMESPACE::sprintf(buff, "%5.4b", 0b1111); + EXPECT_EQ(written, 5); + ASSERT_STREQ(buff, " 1111"); + + // Multiple Conversion Tests. + + written = LIBC_NAMESPACE::sprintf(buff, "%10B %-#10b", 0b101, 0b110); + EXPECT_EQ(written, 21); + ASSERT_STREQ(buff, " 101 0b110 "); + + written = LIBC_NAMESPACE::sprintf(buff, "%-5.4b%#.4b", 0b101, 0b110); + EXPECT_EQ(written, 11); + ASSERT_STREQ(buff, "0101 0b0110"); +} + TEST(LlvmLibcSPrintfTest, PointerConv) { char buff[64]; int written;