blob: 7829914a78aeb695a21cb2311f0008b9d07f41ef [file] [log] [blame]
//===-- Format string parser for printf -------------------------*- C++ -*-===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "src/__support/CPP/type_traits.h"
#include "src/__support/arg_list.h"
#include "src/stdio/printf_core/core_structs.h"
#include "src/stdio/printf_core/printf_config.h"
#include <stddef.h>
namespace __llvm_libc {
namespace printf_core {
class Parser {
const char *__restrict str;
size_t cur_pos = 0;
internal::ArgList args_cur;
// args_start stores the start of the va_args, which is allows getting the
// value of arguments that have already been passed. args_index is tracked so
// that we know which argument args_cur is on.
internal::ArgList args_start;
size_t args_index = 1;
enum PrimaryType : uint8_t { Integer = 0, Float = 1, Pointer = 2 };
// TypeDesc stores the information about a type that is relevant to printf in
// a relatively compact manner.
struct TypeDesc {
uint8_t size;
PrimaryType primary_type;
constexpr bool operator==(const TypeDesc &other) const {
return (size == other.size) && (primary_type == other.primary_type);
// Defined in printf_config.h
static constexpr size_t DESC_ARR_LEN = LLVM_LIBC_PRINTF_INDEX_ARR_LEN;
// desc_arr stores the sizes of the variables in the ArgList. This is used in
// index mode to reduce repeated string parsing. The sizes are stored as
// TypeDesc objects, which store the size as well as minimal type information.
// This is necessary because some systems separate the floating point and
// integer values in va_args.
TypeDesc desc_arr[DESC_ARR_LEN] = {{0, Integer}};
// TODO: Look into object stores for optimization.
Parser(const char *__restrict new_str, internal::ArgList &args)
: str(new_str), args_cur(args), args_start(args) {}
Parser(const char *__restrict new_str, internal::ArgList &args)
: str(new_str), args_cur(args) {}
// 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
// conversion, or a format section with a conversion that has all of its
// variables stored in the format section.
FormatSection get_next_section();
// 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 <class T> T inline get_next_arg_value() {
return args_cur.next_var<T>();
// parse_index parses the index of a value inside a format string. It
// assumes that str[*local_pos] points to character after a '%' or '*', and
// returns 0 if there is no closing $, or if it finds no number. If it finds a
// number, it will move local_pos past the end of the $, else it will not move
// local_pos.
size_t parse_index(size_t *local_pos);
template <typename T> static constexpr TypeDesc get_type_desc() {
if constexpr (cpp::is_same_v<T, void>) {
return TypeDesc{0, PrimaryType::Integer};
} else {
constexpr bool isPointer = cpp::is_same_v<T, void *>;
constexpr bool isFloat =
cpp::is_same_v<T, double> || cpp::is_same_v<T, long double>;
return TypeDesc{sizeof(T), isPointer ? PrimaryType::Pointer
: isFloat ? PrimaryType::Float
: PrimaryType::Integer};
void inline set_type_desc(size_t index, TypeDesc value) {
if (index != 0 && index <= DESC_ARR_LEN)
desc_arr[index - 1] = value;
// get_arg_value gets the value from the arg list at index (starting at 1).
// This may require parsing the format string. An index of 0 is interpreted as
// the next value.
template <class T> T inline get_arg_value(size_t index) {
if (!(index == 0 || index == args_index))
set_type_desc(index, get_type_desc<T>());
return get_next_arg_value<T>();
// the ArgList can only return the next item in the list. This function is
// used in index mode when the item that needs to be read is not the next one.
// It moves cur_args to the index requested so the the appropriate value may
// be read. This may involve parsing the format string, and is in the worst
// case an O(n^2) operation.
void args_to_index(size_t index);
// get_type_desc assumes that this format string uses index mode. It iterates
// through the format string until it finds a format specifier that defines
// the type of index, and returns a TypeDesc describing that type. It does not
// modify cur_pos.
TypeDesc get_type_desc(size_t index);
} // namespace printf_core
} // namespace __llvm_libc