[libc] Template the writing mode for the writer class (#111559)

Summary:
Currently we dispatch the writing mode off of a runtime enum passed in
by the constructor. This causes very unfortunate codegen for the GPU
targets where we get worst-case codegen because of the unused function
pointer for `sprintf`. Instead, this patch moves all of this to a
template so it can be masked out. This results in no dynamic stack and
uses 60 VGPRs instead of 117. It also compiles about 5x as fast.
This commit is contained in:
Joseph Huber
2025-03-12 13:51:44 -05:00
committed by GitHub
parent 3aa96f52cf
commit 598e882ee8
45 changed files with 508 additions and 509 deletions

View File

@@ -41,6 +41,10 @@
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": false,
"doc": "Disable handling of %m to print strerror in printf and friends."
},
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
"value": true,
"doc": "Use dynamic dispatch for the output mechanism to reduce code size."
}
},
"scanf": {

View File

@@ -19,6 +19,9 @@
},
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": true
},
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
"value": false
}
},
"scanf": {

View File

@@ -19,6 +19,9 @@
},
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": true
},
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
"value": false
}
},
"scanf": {

View File

@@ -45,6 +45,7 @@ to learn about the defaults for your platform and target.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
- ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic dispatch for the output mechanism to reduce code size.
* **"pthread" options**
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
- ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).

View File

@@ -25,6 +25,9 @@ endif()
if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
endif()
if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH)
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH")
endif()
if(printf_config_copts)
list(PREPEND printf_config_copts "COMPILE_OPTIONS")
endif()
@@ -62,10 +65,8 @@ add_header_library(
libc.src.__support.common
)
add_object_library(
add_header_library(
writer
SRCS
writer.cpp
HDRS
writer.h
DEPENDS
@@ -76,10 +77,8 @@ add_object_library(
libc.src.string.memory_utils.inline_memset
)
add_object_library(
add_header_library(
converter
SRCS
converter.cpp
HDRS
converter.h
converter_atlas.h
@@ -113,10 +112,8 @@ add_object_library(
libc.src.__support.StringUtil.error_to_string
)
add_object_library(
add_header_library(
printf_main
SRCS
printf_main.cpp
HDRS
printf_main.h
DEPENDS

View File

@@ -17,7 +17,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_char(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_char(Writer<write_mode> *writer,
const FormatSection &to_conv) {
char c = static_cast<char>(to_conv.conv_val_raw);
constexpr int STRING_LEN = 1;

View File

@@ -1,105 +0,0 @@
//===-- Format specifier converter implmentation 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
//
//===----------------------------------------------------------------------===//
#include "src/stdio/printf_core/converter.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/printf_config.h"
#include "src/stdio/printf_core/strerror_converter.h"
#include "src/stdio/printf_core/writer.h"
// This option allows for replacing all of the conversion functions with custom
// replacements. This allows conversions to be replaced at compile time.
#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
#include "src/stdio/printf_core/converter_atlas.h"
#else
#include LIBC_COPT_PRINTF_CONV_ATLAS
#endif
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
int convert(Writer *writer, const FormatSection &to_conv) {
if (!to_conv.has_conv)
return writer->write(to_conv.raw_string);
#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) && \
defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
if (to_conv.length_modifier == LengthModifier::L) {
switch (to_conv.conv_name) {
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
return convert_float_hex_exp(writer, to_conv);
default:
break;
}
}
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
switch (to_conv.conv_name) {
case '%':
return writer->write("%");
case 'c':
return convert_char(writer, to_conv);
case 's':
return convert_string(writer, to_conv);
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
case 'b':
case 'B':
return convert_int(writer, to_conv);
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
case 'f':
case 'F':
return convert_float_decimal(writer, to_conv);
case 'e':
case 'E':
return convert_float_dec_exp(writer, to_conv);
case 'a':
case 'A':
return convert_float_hex_exp(writer, to_conv);
case 'g':
case 'G':
return convert_float_dec_auto(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
case 'r':
case 'R':
case 'k':
case 'K':
return convert_fixed(writer, to_conv);
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
case 'm':
return convert_strerror(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case 'n':
return convert_write_int(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case 'p':
return convert_pointer(writer, to_conv);
default:
return writer->write(to_conv.raw_string);
}
return -1;
}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -11,8 +11,18 @@
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/printf_config.h"
#include "src/stdio/printf_core/strerror_converter.h"
#include "src/stdio/printf_core/writer.h"
// This option allows for replacing all of the conversion functions with custom
// replacements. This allows conversions to be replaced at compile time.
#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
#include "src/stdio/printf_core/converter_atlas.h"
#else
#include LIBC_COPT_PRINTF_CONV_ATLAS
#endif
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
@@ -21,7 +31,80 @@ namespace printf_core {
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
int convert(Writer *writer, const FormatSection &to_conv);
template <WriteMode write_mode>
int convert(Writer<write_mode> *writer, const FormatSection &to_conv) {
if (!to_conv.has_conv)
return writer->write(to_conv.raw_string);
#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) && \
defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
if (to_conv.length_modifier == LengthModifier::L) {
switch (to_conv.conv_name) {
case 'f':
case 'F':
case 'e':
case 'E':
case 'g':
case 'G':
return convert_float_hex_exp(writer, to_conv);
default:
break;
}
}
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
switch (to_conv.conv_name) {
case '%':
return writer->write("%");
case 'c':
return convert_char(writer, to_conv);
case 's':
return convert_string(writer, to_conv);
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
case 'b':
case 'B':
return convert_int(writer, to_conv);
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
case 'f':
case 'F':
return convert_float_decimal(writer, to_conv);
case 'e':
case 'E':
return convert_float_dec_exp(writer, to_conv);
case 'a':
case 'A':
return convert_float_hex_exp(writer, to_conv);
case 'g':
case 'G':
return convert_float_dec_auto(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
case 'r':
case 'R':
case 'k':
case 'K':
return convert_fixed(writer, to_conv);
#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
case 'm':
return convert_strerror(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case 'n':
return convert_write_int(writer, to_conv);
#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
case 'p':
return convert_pointer(writer, to_conv);
default:
return writer->write(to_conv.raw_string);
}
return -1;
}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -63,7 +63,9 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
} \
} while (false)
LIBC_INLINE int convert_fixed(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_fixed(Writer<write_mode> *writer,
const FormatSection &to_conv) {
// Long accum should be the largest type, so we can store all the smaller
// numbers in things sized for it.
using LARep = fixed_point::FXRep<unsigned long accum>;

View File

@@ -93,7 +93,7 @@ zero_after_digits(int32_t base_2_exp, int32_t digits_after_point, T mantissa,
return has_trailing_zeros;
}
class PaddingWriter {
template <WriteMode write_mode> class PaddingWriter {
bool left_justified = false;
bool leading_zeroes = false;
char sign_char = 0;
@@ -107,7 +107,8 @@ public:
sign_char(init_sign_char),
min_width(to_conv.min_width > 0 ? to_conv.min_width : 0) {}
LIBC_INLINE int write_left_padding(Writer *writer, size_t total_digits) {
LIBC_INLINE int write_left_padding(Writer<write_mode> *writer,
size_t total_digits) {
// The pattern is (spaces) (sign) (zeroes), but only one of spaces and
// zeroes can be written, and only if the padding amount is positive.
int padding_amount =
@@ -130,7 +131,8 @@ public:
return 0;
}
LIBC_INLINE int write_right_padding(Writer *writer, size_t total_digits) {
LIBC_INLINE int write_right_padding(Writer<write_mode> *writer,
size_t total_digits) {
// If and only if the conversion is left justified, there may be trailing
// spaces.
int padding_amount =
@@ -155,7 +157,7 @@ public:
This FloatWriter class does the buffering and counting, and writes to the
output when necessary.
*/
class FloatWriter {
template <WriteMode write_mode> class FloatWriter {
char block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
size_t buffered_digits = 0; // The number of digits held in the buffer.
bool has_written = false; // True once any digits have been output.
@@ -164,8 +166,9 @@ class FloatWriter {
size_t digits_before_decimal = 0; // The # of digits to write before the '.'
size_t total_digits_written = 0; // The # of digits that have been output.
bool has_decimal_point; // True if the number has a decimal point.
Writer *writer; // Writes to the final output.
PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits.
Writer<write_mode> *writer; // Writes to the final output.
PaddingWriter<write_mode>
padding_writer; // Handles prefixes/padding, uses total_digits.
LIBC_INLINE int flush_buffer(bool round_up_max_blocks = false) {
const char MAX_BLOCK_DIGIT = (round_up_max_blocks ? '0' : '9');
@@ -245,8 +248,9 @@ class FloatWriter {
static_assert(fputil::FPBits<long double>::EXP_LEN < (sizeof(int) * 8));
public:
LIBC_INLINE FloatWriter(Writer *init_writer, bool init_has_decimal_point,
const PaddingWriter &init_padding_writer)
LIBC_INLINE FloatWriter(Writer<write_mode> *init_writer,
bool init_has_decimal_point,
const PaddingWriter<write_mode> &init_padding_writer)
: has_decimal_point(init_has_decimal_point), writer(init_writer),
padding_writer(init_padding_writer) {}
@@ -466,12 +470,24 @@ public:
}
};
// Class-template auto deduction helpers, add more if needed.
FloatWriter(Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>, bool,
const PaddingWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
-> FloatWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
FloatWriter(Writer<WriteMode::RESIZE_AND_FILL_BUFF>, bool,
const PaddingWriter<WriteMode::RESIZE_AND_FILL_BUFF>)
-> FloatWriter<WriteMode::RESIZE_AND_FILL_BUFF>;
FloatWriter(Writer<WriteMode::FLUSH_TO_STREAM>, bool,
const PaddingWriter<WriteMode::FLUSH_TO_STREAM>)
-> FloatWriter<WriteMode::FLUSH_TO_STREAM>;
// This implementation is based on the Ryu Printf algorithm by Ulf Adams:
// Ulf Adams. 2019. Ryū revisited: printf floating point conversion.
// Proc. ACM Program. Lang. 3, OOPSLA, Article 169 (October 2019), 23 pages.
// https://doi.org/10.1145/3360595
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
template <typename T, WriteMode write_mode,
cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -498,7 +514,7 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
// ignored.
bool nonzero = false;
PaddingWriter padding_writer(to_conv, sign_char);
PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
FloatWriter float_writer(writer, has_decimal_point, padding_writer);
FloatToString<T> float_converter(float_bits.get_val());
@@ -579,8 +595,9 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
return WRITE_OK;
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
template <typename T, WriteMode write_mode,
cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -603,7 +620,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
bool has_decimal_point =
(precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
PaddingWriter padding_writer(to_conv, sign_char);
PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
FloatWriter float_writer(writer, has_decimal_point, padding_writer);
FloatToString<T> float_converter(float_bits.get_val());
@@ -740,8 +757,9 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
return WRITE_OK;
}
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
template <typename T, WriteMode write_mode,
cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -1107,7 +1125,9 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
}
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
LIBC_INLINE int convert_float_decimal(Writer *writer,
template <WriteMode write_mode>
LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1128,7 +1148,8 @@ LIBC_INLINE int convert_float_decimal(Writer *writer,
return convert_inf_nan(writer, to_conv);
}
LIBC_INLINE int convert_float_dec_exp(Writer *writer,
template <WriteMode write_mode>
LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1149,7 +1170,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
return convert_inf_nan(writer, to_conv);
}
LIBC_INLINE int convert_float_dec_auto(Writer *writer,
template <WriteMode write_mode>
LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;

View File

@@ -25,7 +25,8 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_float_hex_exp(Writer *writer,
template <WriteMode write_mode>
LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
using LDBits = fputil::FPBits<long double>;
using StorageType = LDBits::StorageType;

View File

@@ -24,7 +24,9 @@ namespace printf_core {
using StorageType = fputil::FPBits<long double>::StorageType;
LIBC_INLINE int convert_inf_nan(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
const FormatSection &to_conv) {
// All of the letters will be defined relative to variable a, which will be
// the appropriate case based on the case of the conversion.
bool is_negative;

View File

@@ -61,7 +61,9 @@ num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
} // namespace details
LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_int(Writer<write_mode> *writer,
const FormatSection &to_conv) {
static constexpr size_t BITS_IN_BYTE = 8;
static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;

View File

@@ -1,43 +0,0 @@
//===-- Starting point 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
//
//===----------------------------------------------------------------------===//
#include "src/stdio/printf_core/printf_main.h"
#include "src/__support/arg_list.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/converter.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/parser.h"
#include "src/stdio/printf_core/writer.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
int printf_main(Writer *writer, const char *__restrict str,
internal::ArgList &args) {
Parser<internal::ArgList> parser(str, args);
int result = 0;
for (FormatSection cur_section = parser.get_next_section();
!cur_section.raw_string.empty();
cur_section = parser.get_next_section()) {
if (cur_section.has_conv)
result = convert(writer, cur_section);
else
result = writer->write(cur_section.raw_string);
if (result < 0)
return result;
}
return writer->get_chars_written();
}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -11,6 +11,9 @@
#include "src/__support/arg_list.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/converter.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/parser.h"
#include "src/stdio/printf_core/writer.h"
#include <stddef.h>
@@ -18,8 +21,25 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
int printf_main(Writer *writer, const char *__restrict str,
internal::ArgList &args);
template <WriteMode write_mode>
int printf_main(Writer<write_mode> *writer, const char *__restrict str,
internal::ArgList &args) {
Parser<internal::ArgList> parser(str, args);
int result = 0;
for (FormatSection cur_section = parser.get_next_section();
!cur_section.raw_string.empty();
cur_section = parser.get_next_section()) {
if (cur_section.has_conv)
result = convert(writer, cur_section);
else
result = writer->write(cur_section.raw_string);
if (result < 0)
return result;
}
return writer->get_chars_written();
}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -18,7 +18,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_pointer(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_pointer(Writer<write_mode> *writer,
const FormatSection &to_conv) {
FormatSection new_conv = to_conv;
if (to_conv.conv_val_ptr == nullptr) {

View File

@@ -19,7 +19,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_strerror(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_strerror(Writer<write_mode> *writer,
const FormatSection &to_conv) {
FormatSection new_conv = to_conv;
const int error_num = static_cast<int>(to_conv.conv_val_raw);

View File

@@ -20,7 +20,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_string(Writer *writer, const FormatSection &to_conv) {
template <WriteMode write_mode>
LIBC_INLINE int convert_string(Writer<write_mode> *writer,
const FormatSection &to_conv) {
size_t string_len = 0;
const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);

View File

@@ -19,8 +19,9 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
printf_core::WriteBuffer *wb =
reinterpret_cast<printf_core::WriteBuffer *>(target);
WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *wb =
reinterpret_cast<
WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *>(target);
size_t new_size = new_str.size() + wb->buff_cur;
const bool isBuffOnStack = (wb->buff == wb->init_buff);
char *new_buff = static_cast<char *>(
@@ -45,9 +46,9 @@ constexpr size_t DEFAULT_BUFFER_SIZE = 200;
LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
internal::ArgList args) {
char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
printf_core::WriteBuffer wb(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
resize_overflow_hook);
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
printf_core::Writer writer(wb);
auto ret_val = printf_core::printf_main(&writer, format, args);
if (ret_val < 0) {

View File

@@ -72,9 +72,9 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
internal::ArgList &args) {
constexpr size_t BUFF_SIZE = 1024;
char buffer[BUFF_SIZE];
printf_core::WriteBuffer wb(buffer, BUFF_SIZE, &file_write_hook,
reinterpret_cast<void *>(stream));
Writer writer(&wb);
printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb(
buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
Writer writer(wb);
internal::flockfile(stream);
int retval = printf_main(&writer, format, args);
int flushval = wb.overflow_write("");

View File

@@ -19,7 +19,8 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int convert_write_int(Writer *writer,
template <WriteMode write_mode>
LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
const FormatSection &to_conv) {
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS

View File

@@ -1,46 +0,0 @@
//===-- Writer definition 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
//
//===----------------------------------------------------------------------===//
#include "writer.h"
#include "src/__support/CPP/string_view.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/string/memory_utils/inline_memset.h"
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
int Writer::pad(char new_char, size_t length) {
// First, fill as much of the buffer as possible with the padding char.
size_t written = 0;
const size_t buff_space = wb->buff_len - wb->buff_cur;
// ASSERT: length > buff_space
if (buff_space > 0) {
inline_memset(wb->buff + wb->buff_cur, new_char, buff_space);
wb->buff_cur += buff_space;
written = buff_space;
}
// Next, overflow write the rest of length using the mini_buff.
constexpr size_t MINI_BUFF_SIZE = 64;
char mini_buff[MINI_BUFF_SIZE];
inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
while (written + MINI_BUFF_SIZE < length) {
int result = wb->overflow_write(mb_string_view);
if (result != WRITE_OK)
return result;
written += MINI_BUFF_SIZE;
}
cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
return wb->overflow_write(mb_substr);
}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -21,12 +21,24 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
struct WriteBuffer {
enum class WriteMode {
FILL_BUFF_AND_DROP_OVERFLOW,
FLUSH_TO_STREAM,
RESIZE_AND_FILL_BUFF,
};
enum class WriteMode {
FILL_BUFF_AND_DROP_OVERFLOW,
FLUSH_TO_STREAM,
RESIZE_AND_FILL_BUFF,
RUNTIME_DISPATCH,
};
// Helper to omit the template argument if we are using runtime dispatch and
// avoid multiple copies of the converter functions.
template <WriteMode write_mode> struct Mode {
#ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH
static constexpr WriteMode value = WriteMode::RUNTIME_DISPATCH;
#else
static constexpr WriteMode value = write_mode;
#endif
};
template <WriteMode write_mode> struct WriteBuffer {
using StreamWriter = int (*)(cpp::string_view, void *);
char *buff;
const char *init_buff; // for checking when resize.
@@ -35,23 +47,26 @@ struct WriteBuffer {
// The stream writer will be called when the buffer is full. It will be passed
// string_views to write to the stream.
StreamWriter stream_writer;
const StreamWriter stream_writer;
void *output_target;
WriteMode write_mode;
LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook,
// The current writing mode in case the user wants runtime dispatch of the
// stream writer with function pointers.
[[maybe_unused]] WriteMode write_mode_;
LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook,
void *target)
: buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
output_target(target), write_mode(WriteMode::FLUSH_TO_STREAM) {}
: buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
output_target(target), write_mode_(WriteMode::FLUSH_TO_STREAM) {}
LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len)
: buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(nullptr),
LIBC_INLINE WriteBuffer(char *buff, size_t buff_len)
: buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr),
output_target(nullptr),
write_mode(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
write_mode_(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook)
: buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
output_target(this), write_mode(WriteMode::RESIZE_AND_FILL_BUFF) {}
LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook)
: buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
output_target(this), write_mode_(WriteMode::RESIZE_AND_FILL_BUFF) {}
LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
if (buff_cur > 0) {
@@ -92,40 +107,68 @@ struct WriteBuffer {
// this with an empty string will flush the buffer if relevant.
LIBC_INLINE int overflow_write(cpp::string_view new_str) {
switch (write_mode) {
case WriteMode::FILL_BUFF_AND_DROP_OVERFLOW:
if constexpr (write_mode == WriteMode::RUNTIME_DISPATCH) {
if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
return fill_remaining_to_buff(new_str);
else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
return flush_to_stream(new_str);
else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
return resize_and_write(new_str);
} else if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {
return fill_remaining_to_buff(new_str);
case WriteMode::FLUSH_TO_STREAM:
} else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM) {
return flush_to_stream(new_str);
case WriteMode::RESIZE_AND_FILL_BUFF:
} else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF) {
return resize_and_write(new_str);
}
__builtin_unreachable();
}
};
class Writer final {
WriteBuffer *wb;
template <WriteMode write_mode> class Writer final {
WriteBuffer<write_mode> &wb;
int chars_written = 0;
// This is a separate, non-inlined function so that the inlined part of the
// write function is shorter.
int pad(char new_char, size_t length);
LIBC_INLINE int pad(char new_char, size_t length) {
// First, fill as much of the buffer as possible with the padding char.
size_t written = 0;
const size_t buff_space = wb.buff_len - wb.buff_cur;
// ASSERT: length > buff_space
if (buff_space > 0) {
inline_memset(wb.buff + wb.buff_cur, new_char, buff_space);
wb.buff_cur += buff_space;
written = buff_space;
}
// Next, overflow write the rest of length using the mini_buff.
constexpr size_t MINI_BUFF_SIZE = 64;
char mini_buff[MINI_BUFF_SIZE];
inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
while (written + MINI_BUFF_SIZE < length) {
int result = wb.overflow_write(mb_string_view);
if (result != WRITE_OK)
return result;
written += MINI_BUFF_SIZE;
}
cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
return wb.overflow_write(mb_substr);
}
public:
LIBC_INLINE Writer(WriteBuffer *WB) : wb(WB) {}
LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}
// Takes a string, copies it into the buffer if there is space, else passes it
// to the overflow mechanism to be handled separately.
LIBC_INLINE int write(cpp::string_view new_string) {
chars_written += static_cast<int>(new_string.size());
if (LIBC_LIKELY(wb->buff_cur + new_string.size() <= wb->buff_len)) {
inline_memcpy(wb->buff + wb->buff_cur, new_string.data(),
if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {
inline_memcpy(wb.buff + wb.buff_cur, new_string.data(),
new_string.size());
wb->buff_cur += new_string.size();
wb.buff_cur += new_string.size();
return WRITE_OK;
}
return wb->overflow_write(new_string);
return wb.overflow_write(new_string);
}
// Takes a char and a length, memsets the next length characters of the buffer
@@ -134,10 +177,10 @@ public:
LIBC_INLINE int write(char new_char, size_t length) {
chars_written += static_cast<int>(length);
if (LIBC_LIKELY(wb->buff_cur + length <= wb->buff_len)) {
inline_memset(wb->buff + wb->buff_cur,
static_cast<unsigned char>(new_char), length);
wb->buff_cur += length;
if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {
inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char),
length);
wb.buff_cur += length;
return WRITE_OK;
}
return pad(new_char, length);
@@ -147,18 +190,26 @@ public:
// to the overflow mechanism to be handled separately.
LIBC_INLINE int write(char new_char) {
chars_written += 1;
if (LIBC_LIKELY(wb->buff_cur + 1 <= wb->buff_len)) {
wb->buff[wb->buff_cur] = new_char;
wb->buff_cur += 1;
if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) {
wb.buff[wb.buff_cur] = new_char;
wb.buff_cur += 1;
return WRITE_OK;
}
cpp::string_view char_string_view(&new_char, 1);
return wb->overflow_write(char_string_view);
return wb.overflow_write(char_string_view);
}
LIBC_INLINE int get_chars_written() { return chars_written; }
};
// Class-template auto deduction helpers.
Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
-> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>)
-> Writer<WriteMode::RESIZE_AND_FILL_BUFF>;
Writer(WriteBuffer<WriteMode::FLUSH_TO_STREAM>)
-> Writer<WriteMode::FLUSH_TO_STREAM>;
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -27,8 +27,10 @@ LLVM_LIBC_FUNCTION(int, snprintf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.

View File

@@ -28,8 +28,10 @@ LLVM_LIBC_FUNCTION(int, sprintf,
// destruction automatically.
va_end(vlist);
printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<
printf_core::Mode<printf_core::WriteMode::RESIZE_AND_FILL_BUFF>::value>
wb(buffer, cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
wb.buff[wb.buff_cur] = '\0';

View File

@@ -24,8 +24,10 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
internal::ArgList args(vlist); // This holder class allows for easier copying
// and pointer semantics, as well as handling
// destruction automatically.
printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.

View File

@@ -25,8 +25,10 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
// and pointer semantics, as well as handling
// destruction automatically.
printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(buffer, cpp::numeric_limits<size_t>::max());
printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
wb.buff[wb.buff_cur] = '\0';

View File

@@ -104,8 +104,8 @@ printf_core::FormatSection parse_format_string(const char *__restrict format,
return section;
}
template <typename T>
int strfromfloat_convert(printf_core::Writer *writer,
template <typename T, printf_core::WriteMode write_mode>
int strfromfloat_convert(printf_core::Writer<write_mode> *writer,
const printf_core::FormatSection &section) {
if (!section.has_conv)
return writer->write(section.raw_string);

View File

@@ -19,8 +19,10 @@ LLVM_LIBC_FUNCTION(int, strfromd,
printf_core::FormatSection section =
internal::parse_format_string(format, fp);
printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)

View File

@@ -19,8 +19,10 @@ LLVM_LIBC_FUNCTION(int, strfromf,
printf_core::FormatSection section =
internal::parse_format_string(format, fp);
printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)

View File

@@ -24,8 +24,10 @@ LLVM_LIBC_FUNCTION(int, strfroml,
// the length modifier has to be set to LenghtModifier::L
section.length_modifier = printf_core::LengthModifier::L;
printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(s, (n > 0 ? n - 1 : 0));
printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)

View File

@@ -19,8 +19,10 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(size_t, strftime,
(char *__restrict buffer, size_t buffsz,
const char *__restrict format, const tm *timeptr)) {
printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
int ret = strftime_core::strftime_main(&writer, format, timeptr);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
wb.buff[wb.buff_cur] = '\0';

View File

@@ -18,10 +18,8 @@ add_header_library(
libc.src.__support.str_to_integer
)
add_object_library(
add_header_library(
converter
SRCS
converter.cpp
HDRS
converter.h
num_converter.h
@@ -36,10 +34,8 @@ add_object_library(
libc.src.__support.integer_to_string
)
add_object_library(
add_header_library(
strftime_main
SRCS
strftime_main.cpp
HDRS
strftime_main.h
DEPENDS

View File

@@ -42,7 +42,8 @@ get_specific_int_format(const tm *timeptr, const FormatSection &base_to_conv,
return result;
}
LIBC_INLINE int convert_date_us(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_date_us(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is %m/%d/%y (month/day/year)
@@ -66,7 +67,8 @@ LIBC_INLINE int convert_date_us(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_date_iso(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_date_iso(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%Y-%m-%d" (year-month-day)
@@ -90,7 +92,8 @@ LIBC_INLINE int convert_date_iso(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_time_am_pm(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_time_am_pm(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%I:%M:%S %p" (hour:minute:second AM/PM)
@@ -119,7 +122,8 @@ LIBC_INLINE int convert_time_am_pm(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_time_minute(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_time_minute(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%H:%M" (hour:minute)
@@ -139,7 +143,8 @@ LIBC_INLINE int convert_time_minute(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_time_second(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_time_second(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%H:%M:%S" (hour:minute:second)
@@ -163,7 +168,8 @@ LIBC_INLINE int convert_time_second(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_full_date_time(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_full_date_time(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
const time_utils::TMReader time_reader(timeptr);
@@ -204,7 +210,8 @@ LIBC_INLINE int convert_full_date_time(printf_core::Writer *writer,
return WRITE_OK;
}
LIBC_INLINE int convert_composite(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_composite(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
switch (to_conv.conv_name) {

View File

@@ -1,96 +0,0 @@
//===-- Format specifier converter implmentation for strftime -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See htto_conv.times://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "hdr/types/struct_tm.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/writer.h"
#include "src/time/strftime_core/core_structs.h"
#include "composite_converter.h"
#include "num_converter.h"
#include "str_converter.h"
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
int convert(printf_core::Writer *writer, const FormatSection &to_conv,
const tm *timeptr) {
// TODO: Implement the locale support.
// Currently locale flags are ignored, as described by the posix standard for
// the default locale.
if (!to_conv.has_conv)
return writer->write(to_conv.raw_string);
switch (to_conv.conv_name) {
// The cases are grouped by type, then alphabetized with lowercase before
// uppercase.
// raw conversions
case '%':
return writer->write("%");
case 'n':
return writer->write("\n");
case 't':
return writer->write("\t");
// numeric conversions
case 'C': // Century [00-99]
case 'd': // Day of the month [01-31]
case 'e': // Day of the month [1-31]
case 'g': // last 2 digits of ISO year [00-99]
case 'G': // ISO year
case 'H': // 24-hour format [00-23]
case 'I': // 12-hour format [01-12]
case 'j': // Day of the year [001-366]
case 'm': // Month of the year [01-12]
case 'M': // Minute of the hour [00-59]
case 's': // Seconds since the epoch
case 'S': // Second of the minute [00-60]
case 'u': // ISO day of the week ([1-7] starting Monday)
case 'U': // Week of the year ([00-53] week 1 starts on first *Sunday*)
case 'V': // ISO week number ([01-53], 01 is first week majority in this year)
case 'w': // Day of week ([0-6] starting Sunday)
case 'W': // Week of the year ([00-53] week 1 starts on first *Monday*)
case 'y': // Year of the Century [00-99]
case 'Y': // Full year
return convert_int(writer, to_conv, timeptr);
// string conversions
case 'a': // Abbreviated weekday name
case 'A': // Full weekday name
case 'b': // Abbreviated month name
case 'B': // Full month name
case 'h': // same as %b
case 'p': // AM/PM designation
return convert_str(writer, to_conv, timeptr);
// composite conversions
case 'c': // locale specified date and time
case 'D': // %m/%d/%y (month/day/year)
case 'F': // %Y-%m-%d (year-month-day)
case 'r': // %I:%M:%S %p (hour:minute:second AM/PM)
case 'R': // %H:%M (hour:minute)
case 'T': // %H:%M:%S (hour:minute:second)
case 'x': // locale specified date
case 'X': // locale specified time
return convert_composite(writer, to_conv, timeptr);
// timezone conversions
case 'z': // Timezone offset (+/-hhmm) (num conv)
case 'Z': // Timezone name (string conv)
// the standard says if no time zone is determinable, write no characters.
// Leave this here until time zones are implemented.
return 0;
default:
return writer->write(to_conv.raw_string);
}
return 0;
}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -10,17 +10,94 @@
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
#include "hdr/types/struct_tm.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/writer.h"
#include "src/time/strftime_core/core_structs.h"
#include "composite_converter.h"
#include "num_converter.h"
#include "str_converter.h"
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
int convert(printf_core::Writer *writer, const FormatSection &to_conv,
const tm *timeptr);
template <printf_core::WriteMode write_mode>
int convert(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv, const tm *timeptr) {
// TODO: Implement the locale support.
// Currently locale flags are ignored, as described by the posix standard for
// the default locale.
if (!to_conv.has_conv)
return writer->write(to_conv.raw_string);
switch (to_conv.conv_name) {
// The cases are grouped by type, then alphabetized with lowercase before
// uppercase.
// raw conversions
case '%':
return writer->write("%");
case 'n':
return writer->write("\n");
case 't':
return writer->write("\t");
// numeric conversions
case 'C': // Century [00-99]
case 'd': // Day of the month [01-31]
case 'e': // Day of the month [1-31]
case 'g': // last 2 digits of ISO year [00-99]
case 'G': // ISO year
case 'H': // 24-hour format [00-23]
case 'I': // 12-hour format [01-12]
case 'j': // Day of the year [001-366]
case 'm': // Month of the year [01-12]
case 'M': // Minute of the hour [00-59]
case 's': // Seconds since the epoch
case 'S': // Second of the minute [00-60]
case 'u': // ISO day of the week ([1-7] starting Monday)
case 'U': // Week of the year ([00-53] week 1 starts on first *Sunday*)
case 'V': // ISO week number ([01-53], 01 is first week majority in this year)
case 'w': // Day of week ([0-6] starting Sunday)
case 'W': // Week of the year ([00-53] week 1 starts on first *Monday*)
case 'y': // Year of the Century [00-99]
case 'Y': // Full year
return convert_int(writer, to_conv, timeptr);
// string conversions
case 'a': // Abbreviated weekday name
case 'A': // Full weekday name
case 'b': // Abbreviated month name
case 'B': // Full month name
case 'h': // same as %b
case 'p': // AM/PM designation
return convert_str(writer, to_conv, timeptr);
// composite conversions
case 'c': // locale specified date and time
case 'D': // %m/%d/%y (month/day/year)
case 'F': // %Y-%m-%d (year-month-day)
case 'r': // %I:%M:%S %p (hour:minute:second AM/PM)
case 'R': // %H:%M (hour:minute)
case 'T': // %H:%M:%S (hour:minute:second)
case 'x': // locale specified date
case 'X': // locale specified time
return convert_composite(writer, to_conv, timeptr);
// timezone conversions
case 'z': // Timezone offset (+/-hhmm) (num conv)
case 'Z': // Timezone name (string conv)
// the standard says if no time zone is determinable, write no characters.
// Leave this here until time zones are implemented.
return 0;
default:
return writer->write(to_conv.raw_string);
}
return 0;
}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -30,7 +30,8 @@ struct IntFormatSection {
char padding_char = '0';
};
LIBC_INLINE int write_padded_int(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int write_padded_int(printf_core::Writer<write_mode> *writer,
const IntFormatSection &num_info) {
DecFmt d(num_info.num);
@@ -187,7 +188,8 @@ LIBC_INLINE IntFormatSection get_int_format(const FormatSection &to_conv,
return result;
}
LIBC_INLINE int convert_int(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_int(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv, const tm *timeptr) {
return write_padded_int(writer, get_int_format(to_conv, timeptr));

View File

@@ -27,7 +27,8 @@ unwrap_opt(cpp::optional<cpp::string_view> str_opt) {
return str_opt.has_value() ? *str_opt : OUT_OF_BOUNDS_STR;
}
LIBC_INLINE int convert_str(printf_core::Writer *writer,
template <printf_core::WriteMode write_mode>
LIBC_INLINE int convert_str(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv, const tm *timeptr) {
cpp::string_view str;
cpp::optional<cpp::string_view> str_opt;

View File

@@ -1,40 +0,0 @@
//===-- Starting point for strftime ---------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "src/time/strftime_core/strftime_main.h"
#include "hdr/types/struct_tm.h"
#include "src/stdio/printf_core/writer.h"
#include "src/time/strftime_core/converter.h"
#include "src/time/strftime_core/core_structs.h"
#include "src/time/strftime_core/parser.h"
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
int strftime_main(printf_core::Writer *writer, const char *__restrict str,
const tm *timeptr) {
Parser parser(str);
int result = 0;
for (FormatSection cur_section = parser.get_next_section();
!cur_section.raw_string.empty();
cur_section = parser.get_next_section()) {
if (cur_section.has_conv)
result = convert(writer, cur_section, timeptr);
else
result = writer->write(cur_section.raw_string);
if (result < 0)
return result;
}
return writer->get_chars_written();
}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -12,12 +12,32 @@
#include "hdr/types/struct_tm.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/writer.h"
#include "src/time/strftime_core/converter.h"
#include "src/time/strftime_core/core_structs.h"
#include "src/time/strftime_core/parser.h"
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
int strftime_main(printf_core::Writer *writer, const char *__restrict str,
const tm *timeptr);
template <printf_core::WriteMode write_mode>
int strftime_main(printf_core::Writer<write_mode> *writer,
const char *__restrict str, const tm *timeptr) {
Parser parser(str);
int result = 0;
for (strftime_core::FormatSection cur_section = parser.get_next_section();
!cur_section.raw_string.empty();
cur_section = parser.get_next_section()) {
if (cur_section.has_conv)
result = convert(writer, cur_section, timeptr);
else
result = writer->write(cur_section.raw_string);
if (result < 0)
return result;
}
return writer->get_chars_written();
}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL

View File

@@ -22,8 +22,10 @@ LLVM_LIBC_FUNCTION(size_t, strftime_l,
(char *__restrict buffer, size_t buffsz,
const char *__restrict format, const tm *timeptr,
locale_t)) {
printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(&wb);
printf_core::WriteBuffer<printf_core::Mode<
printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
printf_core::Writer writer(wb);
int ret = strftime_core::strftime_main(&writer, format, timeptr);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
wb.buff[wb.buff_cur] = '\0';

View File

@@ -18,10 +18,14 @@ protected:
// void TearDown() override {}
char str[60];
LIBC_NAMESPACE::printf_core::WriteBuffer wb =
LIBC_NAMESPACE::printf_core::WriteBuffer(str, sizeof(str) - 1);
LIBC_NAMESPACE::printf_core::Writer writer =
LIBC_NAMESPACE::printf_core::Writer(&wb);
LIBC_NAMESPACE::printf_core::WriteBuffer<
LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
wb = LIBC_NAMESPACE::printf_core::WriteBuffer<
LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>(
str, sizeof(str) - 1);
LIBC_NAMESPACE::printf_core::Writer<
LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
writer = LIBC_NAMESPACE::printf_core::Writer(wb);
};
TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {

View File

@@ -15,19 +15,20 @@
using LIBC_NAMESPACE::cpp::string_view;
using LIBC_NAMESPACE::printf_core::WriteBuffer;
using LIBC_NAMESPACE::printf_core::WriteMode;
using LIBC_NAMESPACE::printf_core::Writer;
TEST(LlvmLibcPrintfWriterTest, Constructor) {
char str[10];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
(void)writer;
}
TEST(LlvmLibcPrintfWriterTest, Write) {
char str[4] = {'D', 'E', 'F', 'G'};
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write({"abc", 3});
EXPECT_EQ(str[3], 'G');
@@ -42,8 +43,8 @@ TEST(LlvmLibcPrintfWriterTest, Write) {
TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
char str[10];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write({"abc", 3});
writer.write({"DEF", 3});
writer.write({"1234", 3});
@@ -56,8 +57,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
TEST(LlvmLibcPrintfWriterTest, WriteChars) {
char str[4] = {'D', 'E', 'F', 'G'};
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write('a', 3);
EXPECT_EQ(str[3], 'G');
@@ -69,8 +70,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) {
TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
char str[10];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write('a', 3);
writer.write('D', 3);
writer.write('1', 3);
@@ -83,8 +84,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
char str[100];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write('Z', 99);
wb.buff[wb.buff_cur] = '\0';
@@ -105,8 +106,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
char str[13];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -120,8 +121,8 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
char str[11];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write({"abcDEF123456", 12});
wb.buff[wb.buff_cur] = '\0';
@@ -132,8 +133,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
char str[11];
WriteBuffer wb(str, sizeof(str) - 1);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(wb);
writer.write('1', 15);
wb.buff[wb.buff_cur] = '\0';
@@ -144,9 +145,9 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
char str[11];
WriteBuffer wb(str, sizeof(str) - 1);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
Writer writer(&wb);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -162,9 +163,9 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
char str[1];
// This is because the max length should be at most 1 less than the size of
// the buffer it's writing to.
WriteBuffer wb(str, 0);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, 0);
Writer writer(&wb);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -177,9 +178,9 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
}
TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
WriteBuffer wb(nullptr, 0);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
Writer writer(&wb);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -213,9 +214,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(&wb);
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(wb);
writer.write({"abcDEF123456", 12});
// Flush the buffer
@@ -232,9 +234,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(&wb);
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(wb);
writer.write('1', 15);
// Flush the buffer
@@ -251,9 +254,10 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
WriteBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(&wb);
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
wb_buff, sizeof(wb_buff), &copy_to_out,
reinterpret_cast<void *>(&out_buff));
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -273,9 +277,10 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[1];
WriteBuffer wb(wb_buff, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
wb_buff, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
Writer writer(&wb);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -294,9 +299,10 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
WriteBuffer wb(nullptr, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
nullptr, 0, &copy_to_out, reinterpret_cast<void *>(&out_buff));
Writer writer(&wb);
Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);

View File

@@ -1,8 +1,4 @@
add_library(llvmlibc_rpc_server STATIC
${LIBC_SOURCE_DIR}/src/stdio/printf_core/writer.cpp
${LIBC_SOURCE_DIR}/src/stdio/printf_core/converter.cpp
rpc_server.cpp
)
add_library(llvmlibc_rpc_server STATIC rpc_server.cpp)
# Include the RPC implemenation from libc.
target_include_directories(llvmlibc_rpc_server PRIVATE ${LIBC_SOURCE_DIR})

View File

@@ -96,8 +96,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
if (!format[lane])
continue;
WriteBuffer wb(nullptr, 0);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
Writer writer(wb);
internal::DummyArgList<packed> printf_args;
Parser<internal::DummyArgList<packed> &> parser(
@@ -123,8 +123,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
if (!format[lane])
continue;
WriteBuffer wb(nullptr, 0);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
Writer writer(wb);
internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
Parser<internal::StructArgList<packed>> parser(
@@ -180,8 +180,9 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
continue;
char *buffer = temp_storage.alloc(buffer_size[lane]);
WriteBuffer wb(buffer, buffer_size[lane]);
Writer writer(&wb);
WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(buffer,
buffer_size[lane]);
Writer writer(wb);
internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
Parser<internal::StructArgList<packed>> parser(