[libc-commits] [libc] 6ec465a - [libc] add printf oct conversion

Michael Jones via libc-commits libc-commits at lists.llvm.org
Mon Jun 27 15:36:54 PDT 2022


Author: Michael Jones
Date: 2022-06-27T15:36:50-07:00
New Revision: 6ec465ab8ff25f897aa61addb2daf424c7de5b6f

URL: https://github.com/llvm/llvm-project/commit/6ec465ab8ff25f897aa61addb2daf424c7de5b6f
DIFF: https://github.com/llvm/llvm-project/commit/6ec465ab8ff25f897aa61addb2daf424c7de5b6f.diff

LOG: [libc] add printf oct conversion

The oct converter handles the %o conversion.

Reviewed By: lntue

Differential Revision: https://reviews.llvm.org/D127985

Added: 
    libc/src/stdio/printf_core/oct_converter.h

Modified: 
    libc/src/stdio/printf_core/CMakeLists.txt
    libc/src/stdio/printf_core/converter.cpp
    libc/src/stdio/printf_core/converter_atlas.h
    libc/test/src/stdio/printf_core/converter_test.cpp
    libc/test/src/stdio/sprintf_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index 1f1db368cb176..345ecc7e2eab8 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -63,6 +63,7 @@ add_object_library(
     int_converter.h
     hex_converter.h
     ptr_converter.h
+    oct_converter.h
   DEPENDS
     .writer
     .core_structs

diff  --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
index 6092951953867..f809408ad0777 100644
--- a/libc/src/stdio/printf_core/converter.cpp
+++ b/libc/src/stdio/printf_core/converter.cpp
@@ -40,7 +40,7 @@ int convert(Writer *writer, const FormatSection &to_conv) {
   case 'u':
     return convert_int(writer, to_conv);
   case 'o':
-    // return convert_oct(writer, to_conv);
+    return convert_oct(writer, to_conv);
   case 'x':
   case 'X':
     return convert_hex(writer, to_conv);

diff  --git a/libc/src/stdio/printf_core/converter_atlas.h b/libc/src/stdio/printf_core/converter_atlas.h
index fe26060a7594d..2c39255cdefe7 100644
--- a/libc/src/stdio/printf_core/converter_atlas.h
+++ b/libc/src/stdio/printf_core/converter_atlas.h
@@ -23,6 +23,7 @@
 #include "src/stdio/printf_core/int_converter.h"
 
 // defines convert_oct
+#include "src/stdio/printf_core/oct_converter.h"
 // defines convert_hex
 #include "src/stdio/printf_core/hex_converter.h"
 

diff  --git a/libc/src/stdio/printf_core/oct_converter.h b/libc/src/stdio/printf_core/oct_converter.h
new file mode 100644
index 0000000000000..873464911ced1
--- /dev/null
+++ b/libc/src/stdio/printf_core/oct_converter.h
@@ -0,0 +1,111 @@
+//===-- Octal Converter for printf ------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H
+#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H
+
+#include "src/stdio/printf_core/converter_utils.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/writer.h"
+
+#include <inttypes.h>
+#include <stddef.h>
+
+namespace __llvm_libc {
+namespace printf_core {
+
+int inline convert_oct(Writer *writer, const FormatSection &to_conv) {
+  // This is the number of digits it takes to represent a octal value of a
+  // certain number of bits. Each oct digit represents 3 bits, so the value is
+  // ceil(number of bits / 3).
+  constexpr size_t BUFF_LEN = ((sizeof(uintmax_t) * 8) + 2) / 3;
+  uintmax_t num = to_conv.conv_val_raw;
+  char buffer[BUFF_LEN];
+
+  num = apply_length_modifier(num, to_conv.length_modifier);
+
+  // Since the buffer is size to sized to be able fit the entire number, buf_cur
+  // can never reach 0. So, we do not need bounds checking on buf_cur.
+  size_t buff_cur = BUFF_LEN;
+  for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 8)
+    buffer[buff_cur - 1] = (num % 8) + '0';
+
+  size_t num_digits = BUFF_LEN - buff_cur;
+
+  // These are signed to prevent underflow due to negative values. Negative
+  // values are treated the same as 0.
+  int zeroes;
+  int spaces;
+
+  // Negative precision indicates that it was not specified.
+  if (to_conv.precision < 0) {
+    if ((to_conv.flags &
+         (FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
+        FormatFlags::LEADING_ZEROES) {
+      // If this conv has flag 0 but not - and no specified precision, it's
+      // padded with 0's instead of spaces identically to if precision =
+      // min_width. For example: ("%04o", 15) -> "0017"
+      zeroes = to_conv.min_width - num_digits;
+      spaces = 0;
+    } else if (num_digits < 1) {
+      // If no precision is specified, precision defaults to 1. This means that
+      // if the integer passed to the conversion is 0, a 0 will be printed.
+      // Example: ("%3o", 0) -> "  0"
+      zeroes = 1;
+      spaces = to_conv.min_width - zeroes;
+    } else {
+      // If there are enough digits to pass over the precision, just write the
+      // number, padded by spaces.
+      zeroes = 0;
+      spaces = to_conv.min_width - num_digits;
+    }
+  } else {
+    // If precision was specified, possibly write zeroes, and possibly write
+    // spaces. Example: ("%5.4o", 010000) -> "10000"
+    // If the check for if zeroes is negative was not there, spaces would be
+    // incorrectly evaluated as 1.
+    zeroes = to_conv.precision - num_digits; // a negative value means 0
+    if (zeroes < 0)
+      zeroes = 0;
+    spaces = to_conv.min_width - zeroes - num_digits;
+  }
+
+  // The alternate form prefix is "0", so it's handled by increasing the number
+  // of zeroes if necessary.
+  if (((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
+       FormatFlags::ALTERNATE_FORM) &&
+      zeroes < 1) {
+    zeroes = 1;
+    --spaces;
+  }
+
+  if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
+      FormatFlags::LEFT_JUSTIFIED) {
+    // If left justified the pattern is zeroes digits spaces
+    if (zeroes > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
+    if (num_digits > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
+    if (spaces > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
+  } else {
+    // Else the pattern is spaces zeroes digits
+    if (spaces > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
+    if (zeroes > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
+    if (num_digits > 0)
+      RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
+  }
+  return 0;
+}
+
+} // namespace printf_core
+} // namespace __llvm_libc
+
+#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H

diff  --git a/libc/test/src/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp
index d7c292394cb0f..62a7356ec787c 100644
--- a/libc/test/src/stdio/printf_core/converter_test.cpp
+++ b/libc/test/src/stdio/printf_core/converter_test.cpp
@@ -196,9 +196,6 @@ TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
   ASSERT_EQ(writer.get_chars_written(), 5);
 }
 
-// This needs to be switched to the new testing layout, but that's still in the
-// int patch so I need to land that first. This is what I get for not keeping my
-// patches small and focused.
 TEST(LlvmLibcPrintfConverterTest, HexConversion) {
   char str[20];
   __llvm_libc::printf_core::StringWriter str_writer(str);
@@ -206,18 +203,17 @@ TEST(LlvmLibcPrintfConverterTest, HexConversion) {
       reinterpret_cast<void *>(&str_writer),
       __llvm_libc::printf_core::write_to_string);
 
-  __llvm_libc::printf_core::FormatSection left_justified_conv;
-  left_justified_conv.has_conv = true;
-  left_justified_conv.raw_string = "%#018x";
-  left_justified_conv.raw_len = 6;
-  left_justified_conv.conv_name = 'x';
-  left_justified_conv.flags =
-      static_cast<__llvm_libc::printf_core::FormatFlags>(
-          __llvm_libc::printf_core::FormatFlags::ALTERNATE_FORM |
-          __llvm_libc::printf_core::FormatFlags::LEADING_ZEROES);
-  left_justified_conv.min_width = 18;
-  left_justified_conv.conv_val_raw = 0x123456ab;
-  __llvm_libc::printf_core::convert(&writer, left_justified_conv);
+  __llvm_libc::printf_core::FormatSection section;
+  section.has_conv = true;
+  section.raw_string = "%#018x";
+  section.raw_len = 6;
+  section.conv_name = 'x';
+  section.flags = static_cast<__llvm_libc::printf_core::FormatFlags>(
+      __llvm_libc::printf_core::FormatFlags::ALTERNATE_FORM |
+      __llvm_libc::printf_core::FormatFlags::LEADING_ZEROES);
+  section.min_width = 18;
+  section.conv_val_raw = 0x123456ab;
+  __llvm_libc::printf_core::convert(&writer, section);
 
   str_writer.terminate();
   ASSERT_STREQ(str, "0x00000000123456ab");
@@ -243,3 +239,23 @@ TEST(LlvmLibcPrintfConverterTest, PointerConversion) {
   ASSERT_STREQ(str, "0x123456ab");
   ASSERT_EQ(writer.get_chars_written(), 10);
 }
+
+TEST(LlvmLibcPrintfConverterTest, OctConversion) {
+  char str[20];
+  __llvm_libc::printf_core::StringWriter str_writer(str);
+  __llvm_libc::printf_core::Writer writer(
+      reinterpret_cast<void *>(&str_writer),
+      __llvm_libc::printf_core::write_to_string);
+
+  __llvm_libc::printf_core::FormatSection section;
+  section.has_conv = true;
+  section.raw_string = "%o";
+  section.raw_len = 2;
+  section.conv_name = 'o';
+  section.conv_val_raw = 01234;
+  __llvm_libc::printf_core::convert(&writer, section);
+
+  str_writer.terminate();
+  ASSERT_STREQ(str, "1234");
+  ASSERT_EQ(writer.get_chars_written(), 4);
+}

diff  --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp
index 4411216a1544e..4c8e24e95329b 100644
--- a/libc/test/src/stdio/sprintf_test.cpp
+++ b/libc/test/src/stdio/sprintf_test.cpp
@@ -362,6 +362,127 @@ TEST(LlvmLibcSPrintfTest, PointerConv) {
   EXPECT_GT(written, 0);
 }
 
+TEST(LlvmLibcSPrintfTest, OctConv) {
+  char buff[64];
+  int written;
+
+  // Basic Tests.
+
+  written = __llvm_libc::sprintf(buff, "%o", 01234);
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "1234");
+
+  written = __llvm_libc::sprintf(buff, "%o", 04567);
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "4567");
+
+  // Length Modifier Tests.
+
+  written = __llvm_libc::sprintf(buff, "%hho", 0401);
+  EXPECT_EQ(written, 1);
+  ASSERT_STREQ(buff, "1");
+
+  written = __llvm_libc::sprintf(buff, "%llo", 01777777777777777777777ull);
+  EXPECT_EQ(written, 22);
+  ASSERT_STREQ(buff, "1777777777777777777777"); // ull max
+
+  written = __llvm_libc::sprintf(buff, "%to", ~ptr
diff _t(0));
+  if (sizeof(ptr
diff _t) == 8) {
+    EXPECT_EQ(written, 22);
+    ASSERT_STREQ(buff, "1777777777777777777777");
+  } else if (sizeof(ptr
diff _t) == 4) {
+    EXPECT_EQ(written, 11);
+    ASSERT_STREQ(buff, "37777777777");
+  }
+
+  // Min Width Tests.
+
+  written = __llvm_libc::sprintf(buff, "%4o", 0701);
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, " 701");
+
+  written = __llvm_libc::sprintf(buff, "%2o", 0107);
+  EXPECT_EQ(written, 3);
+  ASSERT_STREQ(buff, "107");
+
+  // Precision Tests.
+
+  written = __llvm_libc::sprintf(buff, "%o", 0);
+  EXPECT_EQ(written, 1);
+  ASSERT_STREQ(buff, "0");
+
+  written = __llvm_libc::sprintf(buff, "%.0o", 0);
+  EXPECT_EQ(written, 0);
+  ASSERT_STREQ(buff, "");
+
+  written = __llvm_libc::sprintf(buff, "%.5o", 0153);
+  EXPECT_EQ(written, 5);
+  ASSERT_STREQ(buff, "00153");
+
+  written = __llvm_libc::sprintf(buff, "%.2o", 0135);
+  EXPECT_EQ(written, 3);
+  ASSERT_STREQ(buff, "135");
+
+  // Flag Tests.
+
+  written = __llvm_libc::sprintf(buff, "%-5o", 0246);
+  EXPECT_EQ(written, 5);
+  ASSERT_STREQ(buff, "246  ");
+
+  written = __llvm_libc::sprintf(buff, "%#o", 0234);
+  EXPECT_EQ(written, 4);
+  ASSERT_STREQ(buff, "0234");
+
+  written = __llvm_libc::sprintf(buff, "%05o", 0470);
+  EXPECT_EQ(written, 5);
+  ASSERT_STREQ(buff, "00470");
+
+  written = __llvm_libc::sprintf(buff, "%0#6o", 0753);
+  EXPECT_EQ(written, 6);
+  ASSERT_STREQ(buff, "000753");
+
+  written = __llvm_libc::sprintf(buff, "%-#6o", 0642);
+  EXPECT_EQ(written, 6);
+  ASSERT_STREQ(buff, "0642  ");
+
+  // Combined Tests.
+
+  written = __llvm_libc::sprintf(buff, "%#-07o", 0703);
+  EXPECT_EQ(written, 7);
+  ASSERT_STREQ(buff, "0703   ");
+
+  written = __llvm_libc::sprintf(buff, "%7.5o", 0314);
+  EXPECT_EQ(written, 7);
+  ASSERT_STREQ(buff, "  00314");
+
+  written = __llvm_libc::sprintf(buff, "%#9.5o", 0234);
+  EXPECT_EQ(written, 9);
+  ASSERT_STREQ(buff, "    00234");
+
+  written = __llvm_libc::sprintf(buff, "%-7.5o", 0260);
+  EXPECT_EQ(written, 7);
+  ASSERT_STREQ(buff, "00260  ");
+
+  written = __llvm_libc::sprintf(buff, "%5.4o", 010000);
+  EXPECT_EQ(written, 5);
+  ASSERT_STREQ(buff, "10000");
+
+  // Multiple Conversion Tests.
+
+  written = __llvm_libc::sprintf(buff, "%10o %-#10o", 0456, 0123);
+  EXPECT_EQ(written, 21);
+  ASSERT_STREQ(buff, "       456 0123      ");
+
+  written = __llvm_libc::sprintf(buff, "%-5.4o%#.4o", 075, 025);
+  EXPECT_EQ(written, 9);
+  ASSERT_STREQ(buff, "0075 0025");
+
+  written = __llvm_libc::sprintf(buff, "%04hho %#.5llo %-6.3zo", 256 + 077,
+                                 01000000000000ll, size_t(2));
+  EXPECT_EQ(written, 26);
+  ASSERT_STREQ(buff, "0077 01000000000000 002   ");
+}
+
 #ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE
 TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
   char buff[64];


        


More information about the libc-commits mailing list