diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -64,6 +64,7 @@ hex_converter.h ptr_converter.h oct_converter.h + write_int_converter.h DEPENDS .writer .core_structs diff --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp --- a/libc/src/stdio/printf_core/converter.cpp +++ b/libc/src/stdio/printf_core/converter.cpp @@ -57,9 +57,10 @@ case 'g': case 'G': // return convert_float_mixed(writer, to_conv); - // TODO(michaelrj): add a flag to disable writing an int here +#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case 'n': - // return convert_write_int(writer, to_conv); + return convert_write_int(writer, to_conv); +#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case 'p': return convert_pointer(writer, to_conv); default: diff --git a/libc/src/stdio/printf_core/converter_atlas.h b/libc/src/stdio/printf_core/converter_atlas.h --- a/libc/src/stdio/printf_core/converter_atlas.h +++ b/libc/src/stdio/printf_core/converter_atlas.h @@ -33,8 +33,9 @@ // defines convert_float_hex_exp // defines convert_float_mixed -// TODO(michaelrj): add a flag to disable writing an int here -// defines convert_write_int +#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT +#include "src/stdio/printf_core/write_int_converter.h" +#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT // defines convert_pointer #include "src/stdio/printf_core/ptr_converter.h" diff --git a/libc/src/stdio/printf_core/parser.cpp b/libc/src/stdio/printf_core/parser.cpp --- a/libc/src/stdio/printf_core/parser.cpp +++ b/libc/src/stdio/printf_core/parser.cpp @@ -137,7 +137,9 @@ section.conv_val_raw = bit_cast::UIntType>( GET_ARG_VAL_SIMPLEST(long double, conv_index)); break; +#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case ('n'): +#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case ('p'): case ('s'): section.conv_val_ptr = GET_ARG_VAL_SIMPLEST(void *, conv_index); @@ -351,7 +353,9 @@ else conv_size = TYPE_DESC; break; +#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case ('n'): +#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT case ('p'): case ('s'): conv_size = TYPE_DESC; diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h new file mode 100644 --- /dev/null +++ b/libc/src/stdio/printf_core/write_int_converter.h @@ -0,0 +1,65 @@ +//===-- Write integer 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_WRITE_INT_CONVERTER_H +#define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H + +#include "src/__support/CPP/Limits.h" +#include "src/stdio/printf_core/core_structs.h" +#include "src/stdio/printf_core/writer.h" + +#include +#include + +namespace __llvm_libc { +namespace printf_core { + +int inline convert_write_int(Writer *writer, const FormatSection &to_conv) { + + // This is an additional check added by LLVM-libc. The reason it returns -3 is + // because printf uses negative return values for errors, and -1 and -2 are + // already in use by the file_writer class for file errors. + if (to_conv.conv_val_ptr == nullptr) + return -3; + + int written = writer->get_chars_written(); + + switch (to_conv.length_modifier) { + case LengthModifier::none: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::l: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::ll: + case LengthModifier::L: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::h: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::hh: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::z: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::t: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + case LengthModifier::j: + *reinterpret_cast(to_conv.conv_val_ptr) = written; + break; + } + return 0; +} + +} // namespace printf_core +} // namespace __llvm_libc + +#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITE_INT_CONVERTER_H diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp --- a/libc/test/src/stdio/sprintf_test.cpp +++ b/libc/test/src/stdio/sprintf_test.cpp @@ -92,7 +92,7 @@ // Length Modifier Tests. - written = __llvm_libc::sprintf(buff, "%hhu", 257); // 0x10001 + written = __llvm_libc::sprintf(buff, "%hhu", 257); // 0x101 EXPECT_EQ(written, 1); ASSERT_STREQ(buff, "1"); @@ -483,6 +483,41 @@ ASSERT_STREQ(buff, "0077 01000000000000 002 "); } +#ifndef LLVM_LIBC_PRINTF_DISABLE_WRITE_INT +TEST(LlvmLibcSPrintfTest, WriteIntConv) { + char buff[64]; + int written; + int test_val = -1; + + test_val = -1; + written = __llvm_libc::sprintf(buff, "12345%n67890", &test_val); + EXPECT_EQ(written, 10); + EXPECT_EQ(test_val, 5); + ASSERT_STREQ(buff, "1234567890"); + + test_val = -1; + written = __llvm_libc::sprintf(buff, "%n", &test_val); + EXPECT_EQ(written, 0); + EXPECT_EQ(test_val, 0); + ASSERT_STREQ(buff, ""); + + test_val = 0x100; + written = __llvm_libc::sprintf(buff, "ABC%hhnDEF", &test_val); + EXPECT_EQ(written, 6); + EXPECT_EQ(test_val, 0x103); + ASSERT_STREQ(buff, "ABCDEF"); + + test_val = -1; + written = __llvm_libc::sprintf(buff, "%s%n", "87654321", &test_val); + EXPECT_EQ(written, 8); + EXPECT_EQ(test_val, 8); + ASSERT_STREQ(buff, "87654321"); + + written = __llvm_libc::sprintf(buff, "abc123%n", nullptr); + EXPECT_LT(written, 0); +} +#endif // LLVM_LIBC_PRINTF_DISABLE_WRITE_INT + #ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE TEST(LlvmLibcSPrintfTest, IndexModeParsing) { char buff[64];