Skip to content

Commit 6ec465a

Browse files
[libc] add printf oct conversion
The oct converter handles the %o conversion. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D127985
1 parent 85eaecb commit 6ec465a

File tree

6 files changed

+266
-16
lines changed

6 files changed

+266
-16
lines changed

libc/src/stdio/printf_core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ add_object_library(
6363
int_converter.h
6464
hex_converter.h
6565
ptr_converter.h
66+
oct_converter.h
6667
DEPENDS
6768
.writer
6869
.core_structs

libc/src/stdio/printf_core/converter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ int convert(Writer *writer, const FormatSection &to_conv) {
4040
case 'u':
4141
return convert_int(writer, to_conv);
4242
case 'o':
43-
// return convert_oct(writer, to_conv);
43+
return convert_oct(writer, to_conv);
4444
case 'x':
4545
case 'X':
4646
return convert_hex(writer, to_conv);

libc/src/stdio/printf_core/converter_atlas.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "src/stdio/printf_core/int_converter.h"
2424

2525
// defines convert_oct
26+
#include "src/stdio/printf_core/oct_converter.h"
2627
// defines convert_hex
2728
#include "src/stdio/printf_core/hex_converter.h"
2829

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//===-- Octal Converter for printf ------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H
10+
#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H
11+
12+
#include "src/stdio/printf_core/converter_utils.h"
13+
#include "src/stdio/printf_core/core_structs.h"
14+
#include "src/stdio/printf_core/writer.h"
15+
16+
#include <inttypes.h>
17+
#include <stddef.h>
18+
19+
namespace __llvm_libc {
20+
namespace printf_core {
21+
22+
int inline convert_oct(Writer *writer, const FormatSection &to_conv) {
23+
// This is the number of digits it takes to represent a octal value of a
24+
// certain number of bits. Each oct digit represents 3 bits, so the value is
25+
// ceil(number of bits / 3).
26+
constexpr size_t BUFF_LEN = ((sizeof(uintmax_t) * 8) + 2) / 3;
27+
uintmax_t num = to_conv.conv_val_raw;
28+
char buffer[BUFF_LEN];
29+
30+
num = apply_length_modifier(num, to_conv.length_modifier);
31+
32+
// Since the buffer is size to sized to be able fit the entire number, buf_cur
33+
// can never reach 0. So, we do not need bounds checking on buf_cur.
34+
size_t buff_cur = BUFF_LEN;
35+
for (; num > 0 /* && buff_cur > 0 */; --buff_cur, num /= 8)
36+
buffer[buff_cur - 1] = (num % 8) + '0';
37+
38+
size_t num_digits = BUFF_LEN - buff_cur;
39+
40+
// These are signed to prevent underflow due to negative values. Negative
41+
// values are treated the same as 0.
42+
int zeroes;
43+
int spaces;
44+
45+
// Negative precision indicates that it was not specified.
46+
if (to_conv.precision < 0) {
47+
if ((to_conv.flags &
48+
(FormatFlags::LEADING_ZEROES | FormatFlags::LEFT_JUSTIFIED)) ==
49+
FormatFlags::LEADING_ZEROES) {
50+
// If this conv has flag 0 but not - and no specified precision, it's
51+
// padded with 0's instead of spaces identically to if precision =
52+
// min_width. For example: ("%04o", 15) -> "0017"
53+
zeroes = to_conv.min_width - num_digits;
54+
spaces = 0;
55+
} else if (num_digits < 1) {
56+
// If no precision is specified, precision defaults to 1. This means that
57+
// if the integer passed to the conversion is 0, a 0 will be printed.
58+
// Example: ("%3o", 0) -> " 0"
59+
zeroes = 1;
60+
spaces = to_conv.min_width - zeroes;
61+
} else {
62+
// If there are enough digits to pass over the precision, just write the
63+
// number, padded by spaces.
64+
zeroes = 0;
65+
spaces = to_conv.min_width - num_digits;
66+
}
67+
} else {
68+
// If precision was specified, possibly write zeroes, and possibly write
69+
// spaces. Example: ("%5.4o", 010000) -> "10000"
70+
// If the check for if zeroes is negative was not there, spaces would be
71+
// incorrectly evaluated as 1.
72+
zeroes = to_conv.precision - num_digits; // a negative value means 0
73+
if (zeroes < 0)
74+
zeroes = 0;
75+
spaces = to_conv.min_width - zeroes - num_digits;
76+
}
77+
78+
// The alternate form prefix is "0", so it's handled by increasing the number
79+
// of zeroes if necessary.
80+
if (((to_conv.flags & FormatFlags::ALTERNATE_FORM) ==
81+
FormatFlags::ALTERNATE_FORM) &&
82+
zeroes < 1) {
83+
zeroes = 1;
84+
--spaces;
85+
}
86+
87+
if ((to_conv.flags & FormatFlags::LEFT_JUSTIFIED) ==
88+
FormatFlags::LEFT_JUSTIFIED) {
89+
// If left justified the pattern is zeroes digits spaces
90+
if (zeroes > 0)
91+
RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
92+
if (num_digits > 0)
93+
RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
94+
if (spaces > 0)
95+
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
96+
} else {
97+
// Else the pattern is spaces zeroes digits
98+
if (spaces > 0)
99+
RET_IF_RESULT_NEGATIVE(writer->write_chars(' ', spaces));
100+
if (zeroes > 0)
101+
RET_IF_RESULT_NEGATIVE(writer->write_chars('0', zeroes));
102+
if (num_digits > 0)
103+
RET_IF_RESULT_NEGATIVE(writer->write(buffer + buff_cur, num_digits));
104+
}
105+
return 0;
106+
}
107+
108+
} // namespace printf_core
109+
} // namespace __llvm_libc
110+
111+
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_OCT_CONVERTER_H

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -196,28 +196,24 @@ TEST_F(LlvmLibcPrintfConverterTest, IntConversionSimple) {
196196
ASSERT_EQ(writer.get_chars_written(), 5);
197197
}
198198

199-
// This needs to be switched to the new testing layout, but that's still in the
200-
// int patch so I need to land that first. This is what I get for not keeping my
201-
// patches small and focused.
202199
TEST(LlvmLibcPrintfConverterTest, HexConversion) {
203200
char str[20];
204201
__llvm_libc::printf_core::StringWriter str_writer(str);
205202
__llvm_libc::printf_core::Writer writer(
206203
reinterpret_cast<void *>(&str_writer),
207204
__llvm_libc::printf_core::write_to_string);
208205

209-
__llvm_libc::printf_core::FormatSection left_justified_conv;
210-
left_justified_conv.has_conv = true;
211-
left_justified_conv.raw_string = "%#018x";
212-
left_justified_conv.raw_len = 6;
213-
left_justified_conv.conv_name = 'x';
214-
left_justified_conv.flags =
215-
static_cast<__llvm_libc::printf_core::FormatFlags>(
216-
__llvm_libc::printf_core::FormatFlags::ALTERNATE_FORM |
217-
__llvm_libc::printf_core::FormatFlags::LEADING_ZEROES);
218-
left_justified_conv.min_width = 18;
219-
left_justified_conv.conv_val_raw = 0x123456ab;
220-
__llvm_libc::printf_core::convert(&writer, left_justified_conv);
206+
__llvm_libc::printf_core::FormatSection section;
207+
section.has_conv = true;
208+
section.raw_string = "%#018x";
209+
section.raw_len = 6;
210+
section.conv_name = 'x';
211+
section.flags = static_cast<__llvm_libc::printf_core::FormatFlags>(
212+
__llvm_libc::printf_core::FormatFlags::ALTERNATE_FORM |
213+
__llvm_libc::printf_core::FormatFlags::LEADING_ZEROES);
214+
section.min_width = 18;
215+
section.conv_val_raw = 0x123456ab;
216+
__llvm_libc::printf_core::convert(&writer, section);
221217

222218
str_writer.terminate();
223219
ASSERT_STREQ(str, "0x00000000123456ab");
@@ -243,3 +239,23 @@ TEST(LlvmLibcPrintfConverterTest, PointerConversion) {
243239
ASSERT_STREQ(str, "0x123456ab");
244240
ASSERT_EQ(writer.get_chars_written(), 10);
245241
}
242+
243+
TEST(LlvmLibcPrintfConverterTest, OctConversion) {
244+
char str[20];
245+
__llvm_libc::printf_core::StringWriter str_writer(str);
246+
__llvm_libc::printf_core::Writer writer(
247+
reinterpret_cast<void *>(&str_writer),
248+
__llvm_libc::printf_core::write_to_string);
249+
250+
__llvm_libc::printf_core::FormatSection section;
251+
section.has_conv = true;
252+
section.raw_string = "%o";
253+
section.raw_len = 2;
254+
section.conv_name = 'o';
255+
section.conv_val_raw = 01234;
256+
__llvm_libc::printf_core::convert(&writer, section);
257+
258+
str_writer.terminate();
259+
ASSERT_STREQ(str, "1234");
260+
ASSERT_EQ(writer.get_chars_written(), 4);
261+
}

libc/test/src/stdio/sprintf_test.cpp

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,127 @@ TEST(LlvmLibcSPrintfTest, PointerConv) {
362362
EXPECT_GT(written, 0);
363363
}
364364

365+
TEST(LlvmLibcSPrintfTest, OctConv) {
366+
char buff[64];
367+
int written;
368+
369+
// Basic Tests.
370+
371+
written = __llvm_libc::sprintf(buff, "%o", 01234);
372+
EXPECT_EQ(written, 4);
373+
ASSERT_STREQ(buff, "1234");
374+
375+
written = __llvm_libc::sprintf(buff, "%o", 04567);
376+
EXPECT_EQ(written, 4);
377+
ASSERT_STREQ(buff, "4567");
378+
379+
// Length Modifier Tests.
380+
381+
written = __llvm_libc::sprintf(buff, "%hho", 0401);
382+
EXPECT_EQ(written, 1);
383+
ASSERT_STREQ(buff, "1");
384+
385+
written = __llvm_libc::sprintf(buff, "%llo", 01777777777777777777777ull);
386+
EXPECT_EQ(written, 22);
387+
ASSERT_STREQ(buff, "1777777777777777777777"); // ull max
388+
389+
written = __llvm_libc::sprintf(buff, "%to", ~ptrdiff_t(0));
390+
if (sizeof(ptrdiff_t) == 8) {
391+
EXPECT_EQ(written, 22);
392+
ASSERT_STREQ(buff, "1777777777777777777777");
393+
} else if (sizeof(ptrdiff_t) == 4) {
394+
EXPECT_EQ(written, 11);
395+
ASSERT_STREQ(buff, "37777777777");
396+
}
397+
398+
// Min Width Tests.
399+
400+
written = __llvm_libc::sprintf(buff, "%4o", 0701);
401+
EXPECT_EQ(written, 4);
402+
ASSERT_STREQ(buff, " 701");
403+
404+
written = __llvm_libc::sprintf(buff, "%2o", 0107);
405+
EXPECT_EQ(written, 3);
406+
ASSERT_STREQ(buff, "107");
407+
408+
// Precision Tests.
409+
410+
written = __llvm_libc::sprintf(buff, "%o", 0);
411+
EXPECT_EQ(written, 1);
412+
ASSERT_STREQ(buff, "0");
413+
414+
written = __llvm_libc::sprintf(buff, "%.0o", 0);
415+
EXPECT_EQ(written, 0);
416+
ASSERT_STREQ(buff, "");
417+
418+
written = __llvm_libc::sprintf(buff, "%.5o", 0153);
419+
EXPECT_EQ(written, 5);
420+
ASSERT_STREQ(buff, "00153");
421+
422+
written = __llvm_libc::sprintf(buff, "%.2o", 0135);
423+
EXPECT_EQ(written, 3);
424+
ASSERT_STREQ(buff, "135");
425+
426+
// Flag Tests.
427+
428+
written = __llvm_libc::sprintf(buff, "%-5o", 0246);
429+
EXPECT_EQ(written, 5);
430+
ASSERT_STREQ(buff, "246 ");
431+
432+
written = __llvm_libc::sprintf(buff, "%#o", 0234);
433+
EXPECT_EQ(written, 4);
434+
ASSERT_STREQ(buff, "0234");
435+
436+
written = __llvm_libc::sprintf(buff, "%05o", 0470);
437+
EXPECT_EQ(written, 5);
438+
ASSERT_STREQ(buff, "00470");
439+
440+
written = __llvm_libc::sprintf(buff, "%0#6o", 0753);
441+
EXPECT_EQ(written, 6);
442+
ASSERT_STREQ(buff, "000753");
443+
444+
written = __llvm_libc::sprintf(buff, "%-#6o", 0642);
445+
EXPECT_EQ(written, 6);
446+
ASSERT_STREQ(buff, "0642 ");
447+
448+
// Combined Tests.
449+
450+
written = __llvm_libc::sprintf(buff, "%#-07o", 0703);
451+
EXPECT_EQ(written, 7);
452+
ASSERT_STREQ(buff, "0703 ");
453+
454+
written = __llvm_libc::sprintf(buff, "%7.5o", 0314);
455+
EXPECT_EQ(written, 7);
456+
ASSERT_STREQ(buff, " 00314");
457+
458+
written = __llvm_libc::sprintf(buff, "%#9.5o", 0234);
459+
EXPECT_EQ(written, 9);
460+
ASSERT_STREQ(buff, " 00234");
461+
462+
written = __llvm_libc::sprintf(buff, "%-7.5o", 0260);
463+
EXPECT_EQ(written, 7);
464+
ASSERT_STREQ(buff, "00260 ");
465+
466+
written = __llvm_libc::sprintf(buff, "%5.4o", 010000);
467+
EXPECT_EQ(written, 5);
468+
ASSERT_STREQ(buff, "10000");
469+
470+
// Multiple Conversion Tests.
471+
472+
written = __llvm_libc::sprintf(buff, "%10o %-#10o", 0456, 0123);
473+
EXPECT_EQ(written, 21);
474+
ASSERT_STREQ(buff, " 456 0123 ");
475+
476+
written = __llvm_libc::sprintf(buff, "%-5.4o%#.4o", 075, 025);
477+
EXPECT_EQ(written, 9);
478+
ASSERT_STREQ(buff, "0075 0025");
479+
480+
written = __llvm_libc::sprintf(buff, "%04hho %#.5llo %-6.3zo", 256 + 077,
481+
01000000000000ll, size_t(2));
482+
EXPECT_EQ(written, 26);
483+
ASSERT_STREQ(buff, "0077 01000000000000 002 ");
484+
}
485+
365486
#ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE
366487
TEST(LlvmLibcSPrintfTest, IndexModeParsing) {
367488
char buff[64];

0 commit comments

Comments
 (0)