[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:
@@ -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": {
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
},
|
||||
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
|
||||
"value": true
|
||||
},
|
||||
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"scanf": {
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
},
|
||||
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
|
||||
"value": true
|
||||
},
|
||||
"LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
|
||||
"value": false
|
||||
}
|
||||
},
|
||||
"scanf": {
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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("");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 §ion) {
|
||||
if (!section.has_conv)
|
||||
return writer->write(section.raw_string);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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), ©_to_out,
|
||||
reinterpret_cast<void *>(&out_buff));
|
||||
Writer writer(&wb);
|
||||
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
|
||||
wb_buff, sizeof(wb_buff), ©_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), ©_to_out,
|
||||
reinterpret_cast<void *>(&out_buff));
|
||||
Writer writer(&wb);
|
||||
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
|
||||
wb_buff, sizeof(wb_buff), ©_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), ©_to_out,
|
||||
reinterpret_cast<void *>(&out_buff));
|
||||
Writer writer(&wb);
|
||||
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
|
||||
wb_buff, sizeof(wb_buff), ©_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, ©_to_out, reinterpret_cast<void *>(&out_buff));
|
||||
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
|
||||
wb_buff, 0, ©_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, ©_to_out, reinterpret_cast<void *>(&out_buff));
|
||||
WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
|
||||
nullptr, 0, ©_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);
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user