diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt --- a/libc/src/stdio/CMakeLists.txt +++ b/libc/src/stdio/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(printf_files) + add_entrypoint_object( fopen SRCS diff --git a/libc/src/stdio/printf_files/CMakeLists.txt b/libc/src/stdio/printf_files/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/src/stdio/printf_files/CMakeLists.txt @@ -0,0 +1,20 @@ + +add_header_library( + core_structs + HDRS + core_structs.h +) + +add_object_library( + parser + SRCS + parser.cpp + HDRS + parser.h + DEPENDS + .core_structs + libc.src.__support.ctype_utils + libc.src.__support.str_to_integer + libc.src.__support.CPP.bit + +) diff --git a/libc/src/stdio/printf_files/core_structs.h b/libc/src/stdio/printf_files/core_structs.h --- a/libc/src/stdio/printf_files/core_structs.h +++ b/libc/src/stdio/printf_files/core_structs.h @@ -16,32 +16,19 @@ namespace printf_core { enum class LengthModifier { hh, h, l, ll, j, z, t, L, none }; -enum VariableType : uint8_t { - // Types - Void = 0x00, - Char = 0x01, - // WChar = 0x02, - // WInt = 0x03, - Short = 0x04, - Int = 0x05, - Long = 0x06, - LLong = 0x07, - Intmax = 0x08, - Size = 0x09, - Ptrdiff = 0x0a, - Double = 0x0b, - LDouble = 0x0c, - - // Modifiers - - Signed = 0x40, - Pointer = 0x80, - - // Masks - - Type_Mask = 0x3f, - Modifier_Mask = 0xc, +enum FormatFlags : uint8_t { + // Flags + LeftJustified = 0x01, // - + ForceSign = 0x02, // + + SpacePrefix = 0x04, // space + AlternateForm = 0x08, // # + LeadingZeroes = 0x10, // 0 + // group_decimals = 0x20, // ' + // locale_digits = 0x40, // I + + // Default: + Clear = 0, }; struct FormatSection { @@ -51,11 +38,7 @@ size_t raw_len; // Format Specifier Values - bool left_justified; - bool force_sign; - bool space_prefix; - bool alt_form; - bool leading_zeroes; + FormatFlags flags; LengthModifier length_modifier; int min_width; int precision; diff --git a/libc/src/stdio/printf_files/parser.h b/libc/src/stdio/printf_files/parser.h --- a/libc/src/stdio/printf_files/parser.h +++ b/libc/src/stdio/printf_files/parser.h @@ -9,33 +9,39 @@ #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_FILES_PARSER_H #define LLVM_LIBC_SRC_STDIO_PRINTF_FILES_PARSER_H +#include "src/__support/arg_list.h" #include "src/stdio/printf_files/core_structs.h" -#include #include namespace __llvm_libc { namespace printf_core { -// TODO: Make this a compile option. -constexpr size_t TYPE_ARR_SIZE = 32; - class Parser { const char *__restrict str; size_t cur_pos = 0; - va_list *vlist_start; - va_list *vlist_cur; - size_t vlist_index; + internal::ArgList args_start; + internal::ArgList args_cur; + size_t args_index = 1; + + // TODO: Make this a compile option. + static constexpr size_t SIZE_ARR_LEN = 32; + static constexpr uint8_t FLOAT_MASK = 0x80; + static constexpr uint8_t SIZE_MASK = 0x7f; // TODO: Make this an optional piece. - VariableType type_arr[TYPE_ARR_SIZE]; + uint8_t size_arr[SIZE_ARR_LEN]; // TODO: Look into object stores for optimization. public: - Parser(const char *__restrict str, va_list *vlist); + Parser(const char *__restrict new_str, internal::ArgList &args) + : str(new_str), args_start(args), args_cur(args) { + for (size_t i = 0; i < SIZE_ARR_LEN; ++i) + size_arr[i] = 0; + } // get_next_section will parse the format string until it has a fully // specified format section. This can either be a raw format section with no @@ -44,10 +50,23 @@ FormatSection get_next_section(); private: - // get_arg_value gets the value from the vlist at index (starting at 1). This - // may require parsing the format string. An index of 0 is interpreted as the - // next value. - template T get_arg_value(size_t index); + // parse_flags parses the flags inside a format string. It assumes that + // str[*local_pos] is inside a format specifier, and parses any flags it + // finds. It returns a FormatFlags object containing the set of found flags + // arithmetically or'd together. local_pos will be moved past any flags found. + FormatFlags parse_flags(size_t *local_pos); + + // parse_length_modifier parses the length modifier inside a format string. It + // assumes that str[*local_pos] is inside a format specifier. It returns a + // LengthModifier with the length modifier it found. It will advance local_pos + // after the format specifier if one is found. + LengthModifier parse_length_modifier(size_t *local_pos); + + // get_next_arg_value gets the next value from the arg list as type T. + template T inline get_next_arg_value() { + ++args_index; + return args_cur.next_var(); + } }; } // namespace printf_core diff --git a/libc/src/stdio/printf_files/parser.cpp b/libc/src/stdio/printf_files/parser.cpp new file mode 100644 --- /dev/null +++ b/libc/src/stdio/printf_files/parser.cpp @@ -0,0 +1,220 @@ +//===-- Format string parser implementation 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 "parser.h" + +#include "src/__support/arg_list.h" + +#include "src/__support/CPP/Bit.h" +#include "src/__support/ctype_utils.h" +#include "src/__support/str_to_integer.h" + +namespace __llvm_libc { +namespace printf_core { + +#define LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE 1 // This will be a compile flag. + +FormatSection Parser::get_next_section() { + FormatSection section; + section.raw_string = str + cur_pos; + size_t starting_pos = cur_pos; + if (str[cur_pos] == '%') { + // format section + section.has_conv = true; + + ++cur_pos; + +#ifndef LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE + size_t conv_index = parse_index(&cur_pos); +#define GET_ARG_VAL_SIMPLEST(arg_type, index) get_arg_value(index) +#else +#define GET_ARG_VAL_SIMPLEST(arg_type, _) get_next_arg_value() +#endif // LLVM_LIBC_PRINTF_DISABLE_INDEX_MODE + + section.flags = parse_flags(&cur_pos); + + // handle width + section.min_width = 0; + if (str[cur_pos] == '*') { + ++cur_pos; + + section.min_width = GET_ARG_VAL_SIMPLEST(int, parse_index(&cur_pos)); + } else if (internal::isdigit(str[cur_pos])) { + char *int_end; + section.min_width = + internal::strtointeger(str + cur_pos, &int_end, 10); + cur_pos = int_end - str; + } + if (section.min_width < 0) { + section.min_width = -section.min_width; + section.flags = + static_cast(section.flags | FormatFlags::LeftJustified); + } + + // handle precision + section.precision = -1; // negative precisions are ignored. + if (str[cur_pos] == '.') { + ++cur_pos; + section.precision = 0; // if there's a . but no specified precision, the + // precision is implicitly 0. + if (str[cur_pos] == '*') { + ++cur_pos; + + section.precision = GET_ARG_VAL_SIMPLEST(int, parse_index(&cur_pos)); + + } else if (internal::isdigit(str[cur_pos])) { + char *int_end; + section.precision = + internal::strtointeger(str + cur_pos, &int_end, 10); + cur_pos = int_end - str; + } + } + + LengthModifier lm = parse_length_modifier(&cur_pos); + + section.length_modifier = lm; + section.conv_name = str[cur_pos]; + switch (str[cur_pos]) { + case ('%'): + break; + case ('c'): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(int, conv_index); + break; + case ('d'): + case ('i'): + case ('o'): + case ('x'): + case ('X'): + case ('u'): + switch (lm) { + case (LengthModifier::hh): + case (LengthModifier::h): + case (LengthModifier::none): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(int, conv_index); + break; + case (LengthModifier::l): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(long, conv_index); + break; + case (LengthModifier::ll): + case (LengthModifier::L): // This isn't in the standard, but is in other + // libc implementations. + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(long long, conv_index); + break; + case (LengthModifier::j): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(intmax_t, conv_index); + break; + case (LengthModifier::z): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(size_t, conv_index); + break; + case (LengthModifier::t): + section.conv_val_raw = GET_ARG_VAL_SIMPLEST(ptrdiff_t, conv_index); + break; + } + break; + case ('f'): + case ('F'): + case ('e'): + case ('E'): + case ('a'): + case ('A'): + case ('g'): + case ('G'): + if (lm != LengthModifier::L) + section.conv_val_raw = + bit_cast(GET_ARG_VAL_SIMPLEST(double, conv_index)); + else + section.conv_val_raw = bit_cast<__uint128_t>( + GET_ARG_VAL_SIMPLEST(long double, conv_index)); + break; + case ('n'): + case ('p'): + case ('s'): + section.conv_val_ptr = GET_ARG_VAL_SIMPLEST(void *, conv_index); + break; + default: + // if the conversion is undefined, change this to a raw section. + section.has_conv = false; + break; + } + ++cur_pos; + } else { + // raw section + section.has_conv = false; + while (str[cur_pos] != '%' && str[cur_pos] != '\0') + ++cur_pos; + } + section.raw_len = cur_pos - starting_pos; + return section; +} + +FormatFlags Parser::parse_flags(size_t *local_pos) { + bool found_flag = true; + FormatFlags flags = FormatFlags::Clear; + while (found_flag) { + switch (str[*local_pos]) { + case '-': + flags = static_cast(flags | FormatFlags::LeftJustified); + break; + case '+': + flags = static_cast(flags | FormatFlags::ForceSign); + break; + case ' ': + flags = static_cast(flags | FormatFlags::SpacePrefix); + break; + case '#': + flags = static_cast(flags | FormatFlags::AlternateForm); + break; + case '0': + flags = static_cast(flags | FormatFlags::LeadingZeroes); + break; + default: + found_flag = false; + } + if (found_flag) + ++*local_pos; + } + return flags; +} + +LengthModifier Parser::parse_length_modifier(size_t *local_pos) { + switch (str[*local_pos]) { + case ('l'): + if (str[*local_pos + 1] == 'l') { + *local_pos += 2; + return LengthModifier::ll; + } else { + ++*local_pos; + return LengthModifier::l; + } + case ('h'): + if (str[cur_pos + 1] == 'h') { + *local_pos += 2; + return LengthModifier::hh; + } else { + ++*local_pos; + return LengthModifier::h; + } + case ('L'): + ++*local_pos; + return LengthModifier::L; + case ('j'): + ++*local_pos; + return LengthModifier::j; + case ('z'): + ++*local_pos; + return LengthModifier::z; + case ('t'): + ++*local_pos; + return LengthModifier::t; + default: + return LengthModifier::none; + } +} + +} // namespace printf_core +} // namespace __llvm_libc diff --git a/libc/src/stdio/printf_files/printf_main.h b/libc/src/stdio/printf_files/printf_main.h --- a/libc/src/stdio/printf_files/printf_main.h +++ b/libc/src/stdio/printf_files/printf_main.h @@ -9,19 +9,20 @@ #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_FILES_PRINTF_MAIN_H #define LLVM_LIBC_SRC_STDIO_PRINTF_FILES_PRINTF_MAIN_H +#include "src/__support/arg_list.h" #include "src/stdio/printf_files/converter.h" #include "src/stdio/printf_files/core_structs.h" #include "src/stdio/printf_files/parser.h" #include "src/stdio/printf_files/writer.h" -#include #include namespace __llvm_libc { namespace printf_core { -int printf_main(Writer *writer, const char *__restrict str, va_list vlist) { - Parser parser(str, &vlist); +int printf_main(Writer *writer, const char *__restrict str, + internal::ArgList args) { + Parser parser(str, args); Converter converter(writer); for (FormatSection cur_section = parser.get_next_section(); diff --git a/libc/test/src/stdio/CMakeLists.txt b/libc/test/src/stdio/CMakeLists.txt --- a/libc/test/src/stdio/CMakeLists.txt +++ b/libc/test/src/stdio/CMakeLists.txt @@ -14,4 +14,6 @@ libc.src.stdio.fwrite ) +add_subdirectory(printf_files) + add_subdirectory(testdata) diff --git a/libc/test/src/stdio/printf_files/CMakeLists.txt b/libc/test/src/stdio/printf_files/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/printf_files/CMakeLists.txt @@ -0,0 +1,10 @@ +add_libc_unittest( + parser_test + SUITE + libc_stdio_unittests + SRCS + parser_test.cpp + DEPENDS + libc.src.stdio.printf_files.parser + libc.src.__support.arg_list +) diff --git a/libc/test/src/stdio/printf_files/parser_test.cpp b/libc/test/src/stdio/printf_files/parser_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/src/stdio/printf_files/parser_test.cpp @@ -0,0 +1,235 @@ +//===-- Unittests for the printf Parser -----------------------------------===// +// +// 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/__support/arg_list.h" +#include "src/stdio/printf_files/parser.h" +#include + +#include "utils/UnitTest/Test.h" + +void init(const char *__restrict str, ...) { + va_list vlist; + va_start(vlist, str); + __llvm_libc::internal::ArgList v(vlist); + va_end(vlist); + + __llvm_libc::printf_core::Parser parser(str, v); +} + +void evaluate(__llvm_libc::printf_core::FormatSection *format_arr, + const char *__restrict str, ...) { + va_list vlist; + va_start(vlist, str); + __llvm_libc::internal::ArgList v(vlist); + va_end(vlist); + + __llvm_libc::printf_core::Parser parser(str, v); + + for (auto cur_section = parser.get_next_section(); cur_section.raw_len > 0; + cur_section = parser.get_next_section()) { + *format_arr = cur_section; + ++format_arr; + } +} + +TEST(LlvmLibcPrintfParserTests, Constructor) { init("test", 1, 2); } + +TEST(LlvmLibcPrintfParserTests, EvalRaw) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "test"; + evaluate(format_arr, str); + EXPECT_FALSE(format_arr[0].has_conv); + + // This is a pointer comparison since raw_string in a format section is only a + // pointer into the input string, and is not null terminated, it has a length. + EXPECT_EQ(str, format_arr[0].raw_string); + + EXPECT_EQ(size_t(4), format_arr[0].raw_len); +} + +TEST(LlvmLibcPrintfParserTests, EvalSimple) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "test %% test"; + evaluate(format_arr, str); + + EXPECT_FALSE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(5), format_arr[0].raw_len); + + EXPECT_TRUE(format_arr[1].has_conv); + EXPECT_EQ(str + 5, format_arr[1].raw_string); + EXPECT_EQ(size_t(2), format_arr[1].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[1].flags)); + EXPECT_EQ(0, format_arr[1].min_width); + EXPECT_EQ(-1, format_arr[1].precision); + EXPECT_EQ('%', format_arr[1].conv_name); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[1].length_modifier); + EXPECT_FALSE(format_arr[2].has_conv); + EXPECT_EQ(str + 7, format_arr[2].raw_string); + EXPECT_EQ(size_t(5), format_arr[2].raw_len); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArg) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%d"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(2), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(-1, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithFlags) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%+-0 #d"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(7), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::ForceSign | + __llvm_libc::printf_core::FormatFlags::LeftJustified | + __llvm_libc::printf_core::FormatFlags::LeadingZeroes | + __llvm_libc::printf_core::FormatFlags::SpacePrefix | + __llvm_libc::printf_core::FormatFlags::AlternateForm), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(-1, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithWidth) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%12d"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(4), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(12, format_arr[0].min_width); + EXPECT_EQ(-1, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithPrecision) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%.34d"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(5), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(34, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithTrivialPrecision) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%.d"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(3), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(0, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::none == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithShortLengthModifier) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%hd"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(3), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(-1, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::h == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithLongLengthModifier) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "%lld"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(4), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::Clear), + static_cast(format_arr[0].flags)); + EXPECT_EQ(0, format_arr[0].min_width); + EXPECT_EQ(-1, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::ll == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +} + +TEST(LlvmLibcPrintfParserTests, EvalOneArgWithAllOptions) { + __llvm_libc::printf_core::FormatSection format_arr[10]; + const char *str = "% -056.78jd"; + int arg1 = 12345; + evaluate(format_arr, str, arg1); + + EXPECT_TRUE(format_arr[0].has_conv); + EXPECT_EQ(str, format_arr[0].raw_string); + EXPECT_EQ(size_t(11), format_arr[0].raw_len); + EXPECT_EQ(uint8_t(__llvm_libc::printf_core::FormatFlags::LeftJustified | + __llvm_libc::printf_core::FormatFlags::LeadingZeroes | + __llvm_libc::printf_core::FormatFlags::SpacePrefix), + static_cast(format_arr[0].flags)); + EXPECT_EQ(56, format_arr[0].min_width); + EXPECT_EQ(78, format_arr[0].precision); + EXPECT_TRUE(__llvm_libc::printf_core::LengthModifier::j == + format_arr[0].length_modifier); + EXPECT_EQ('d', format_arr[0].conv_name); + EXPECT_EQ(static_cast<__uint128_t>(arg1), format_arr[0].conv_val_raw); +}