|  | /* | 
|  | * Copyright (c) 1997-2010, 2012-2015 Wind River Systems, Inc. | 
|  | * Copyright (c) 2020 Nordic Semiconductor ASA | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <errno.h> | 
|  | #include <inttypes.h> | 
|  | #include <limits.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdbool.h> | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  | #include <string.h> | 
|  | #include <zephyr/toolchain.h> | 
|  | #include <sys/types.h> | 
|  | #include <zephyr/sys/util.h> | 
|  | #include <zephyr/sys/cbprintf.h> | 
|  |  | 
|  | /* newlib doesn't declare this function unless __POSIX_VISIBLE >= 200809.  No | 
|  | * idea how to make that happen, so lets put it right here. | 
|  | */ | 
|  | size_t strnlen(const char *s, size_t maxlen); | 
|  |  | 
|  | /* Provide typedefs used for signed and unsigned integral types | 
|  | * capable of holding all convertible integral values. | 
|  | */ | 
|  | #ifdef CONFIG_CBPRINTF_FULL_INTEGRAL | 
|  | typedef intmax_t sint_value_type; | 
|  | typedef uintmax_t uint_value_type; | 
|  | #else | 
|  | typedef int32_t sint_value_type; | 
|  | typedef uint32_t uint_value_type; | 
|  | #endif | 
|  |  | 
|  | /* The maximum buffer size required is for octal formatting: one character for | 
|  | * every 3 bits.  Neither EOS nor alternate forms are required. | 
|  | */ | 
|  | #define CONVERTED_INT_BUFLEN ((CHAR_BIT * sizeof(uint_value_type) + 2) / 3) | 
|  |  | 
|  | /* The float code may extract up to 16 digits, plus a prefix, a | 
|  | * leading 0, a dot, and an exponent in the form e+xxx for a total of | 
|  | * 24. Add a trailing NULL so the buffer length required is 25. | 
|  | */ | 
|  | #define CONVERTED_FP_BUFLEN 25U | 
|  |  | 
|  | #ifdef CONFIG_CBPRINTF_FP_SUPPORT | 
|  | #define CONVERTED_BUFLEN MAX(CONVERTED_INT_BUFLEN, CONVERTED_FP_BUFLEN) | 
|  | #else | 
|  | #define CONVERTED_BUFLEN CONVERTED_INT_BUFLEN | 
|  | #endif | 
|  |  | 
|  | /* The allowed types of length modifier. */ | 
|  | enum length_mod_enum { | 
|  | LENGTH_NONE,		/* int */ | 
|  | LENGTH_HH,		/* char */ | 
|  | LENGTH_H,		/* short */ | 
|  | LENGTH_L,		/* long */ | 
|  | LENGTH_LL,		/* long long */ | 
|  | LENGTH_J,		/* intmax */ | 
|  | LENGTH_Z,		/* size_t */ | 
|  | LENGTH_T,		/* ptrdiff_t */ | 
|  | LENGTH_UPPER_L,		/* long double */ | 
|  | }; | 
|  |  | 
|  | /* Categories of conversion specifiers. */ | 
|  | enum specifier_cat_enum { | 
|  | /* unrecognized */ | 
|  | SPECIFIER_INVALID, | 
|  | /* d, i */ | 
|  | SPECIFIER_SINT, | 
|  | /* c, o, u, x, X */ | 
|  | SPECIFIER_UINT, | 
|  | /* n, p, s */ | 
|  | SPECIFIER_PTR, | 
|  | /* a, A, e, E, f, F, g, G */ | 
|  | SPECIFIER_FP, | 
|  | }; | 
|  |  | 
|  | #define CHAR_IS_SIGNED (CHAR_MIN != 0) | 
|  | #if CHAR_IS_SIGNED | 
|  | #define CASE_SINT_CHAR case 'c': | 
|  | #define CASE_UINT_CHAR | 
|  | #else | 
|  | #define CASE_SINT_CHAR | 
|  | #define CASE_UINT_CHAR case 'c': | 
|  | #endif | 
|  |  | 
|  | /* We need two pieces of information about wchar_t: | 
|  | * * WCHAR_IS_SIGNED: whether it's signed or unsigned; | 
|  | * * WINT_TYPE: the type to use when extracting it from va_args | 
|  | * | 
|  | * The former can be determined from the value of WCHAR_MIN if it's defined. | 
|  | * It's not for minimal libc, so treat it as whatever char is. | 
|  | * | 
|  | * The latter should be wint_t, but minimal libc doesn't provide it.  We can | 
|  | * substitute wchar_t as long as that type does not undergo default integral | 
|  | * promotion as an argument.  But it does for at least one toolchain (xtensa), | 
|  | * and where it does we need to use the promoted type in va_arg() to avoid | 
|  | * build errors, otherwise we can use the base type.  We can tell that | 
|  | * integral promotion occurs if WCHAR_MAX is strictly less than INT_MAX. | 
|  | */ | 
|  | #ifndef WCHAR_MIN | 
|  | #define WCHAR_IS_SIGNED CHAR_IS_SIGNED | 
|  | #if WCHAR_IS_SIGNED | 
|  | #define WINT_TYPE int | 
|  | #else /* wchar signed */ | 
|  | #define WINT_TYPE unsigned int | 
|  | #endif /* wchar signed */ | 
|  | #else /* WCHAR_MIN defined */ | 
|  | #define WCHAR_IS_SIGNED ((WCHAR_MIN - 0) != 0) | 
|  | #if WCHAR_MAX < INT_MAX | 
|  | /* Signed or unsigned, it'll be int */ | 
|  | #define WINT_TYPE int | 
|  | #else /* wchar rank vs int */ | 
|  | #define WINT_TYPE wchar_t | 
|  | #endif /* wchar rank vs int */ | 
|  | #endif /* WCHAR_MIN defined */ | 
|  |  | 
|  | /* Case label to identify conversions for signed integral values.  The | 
|  | * corresponding argument_value tag is sint and category is | 
|  | * SPECIFIER_SINT. | 
|  | */ | 
|  | #define SINT_CONV_CASES				\ | 
|  | 'd':					\ | 
|  | CASE_SINT_CHAR				\ | 
|  | case 'i' | 
|  |  | 
|  | /* Case label to identify conversions for signed integral arguments. | 
|  | * The corresponding argument_value tag is uint and category is | 
|  | * SPECIFIER_UINT. | 
|  | */ | 
|  | #define UINT_CONV_CASES				\ | 
|  | 'o':					\ | 
|  | CASE_UINT_CHAR				\ | 
|  | case 'u':				\ | 
|  | case 'x':				\ | 
|  | case 'X' | 
|  |  | 
|  | /* Case label to identify conversions for floating point arguments. | 
|  | * The corresponding argument_value tag is either dbl or ldbl, | 
|  | * depending on length modifier, and the category is SPECIFIER_FP. | 
|  | */ | 
|  | #define FP_CONV_CASES				\ | 
|  | 'a':					\ | 
|  | case 'A':				\ | 
|  | case 'e':				\ | 
|  | case 'E':				\ | 
|  | case 'f':				\ | 
|  | case 'F':				\ | 
|  | case 'g':				\ | 
|  | case 'G' | 
|  |  | 
|  | /* Case label to identify conversions for pointer arguments.  The | 
|  | * corresponding argument_value tag is ptr and the category is | 
|  | * SPECIFIER_PTR. | 
|  | */ | 
|  | #define PTR_CONV_CASES				\ | 
|  | 'n':					\ | 
|  | case 'p':				\ | 
|  | case 's' | 
|  |  | 
|  | /* Storage for an argument value. */ | 
|  | union argument_value { | 
|  | /* For SINT conversions */ | 
|  | sint_value_type sint; | 
|  |  | 
|  | /* For UINT conversions */ | 
|  | uint_value_type uint; | 
|  |  | 
|  | /* For FP conversions without L length */ | 
|  | double dbl; | 
|  |  | 
|  | /* For FP conversions with L length */ | 
|  | long double ldbl; | 
|  |  | 
|  | /* For PTR conversions */ | 
|  | void *ptr; | 
|  | }; | 
|  |  | 
|  | /* Structure capturing all attributes of a conversion | 
|  | * specification. | 
|  | * | 
|  | * Initial values come from the specification, but are updated during | 
|  | * the conversion. | 
|  | */ | 
|  | struct conversion { | 
|  | /** Indicates flags are inconsistent */ | 
|  | bool invalid: 1; | 
|  |  | 
|  | /** Indicates flags are valid but not supported */ | 
|  | bool unsupported: 1; | 
|  |  | 
|  | /** Left-justify value in width */ | 
|  | bool flag_dash: 1; | 
|  |  | 
|  | /** Explicit sign */ | 
|  | bool flag_plus: 1; | 
|  |  | 
|  | /** Space for non-negative sign */ | 
|  | bool flag_space: 1; | 
|  |  | 
|  | /** Alternative form */ | 
|  | bool flag_hash: 1; | 
|  |  | 
|  | /** Pad with leading zeroes */ | 
|  | bool flag_zero: 1; | 
|  |  | 
|  | /** Width field present */ | 
|  | bool width_present: 1; | 
|  |  | 
|  | /** Width value from int argument | 
|  | * | 
|  | * width_value is set to the absolute value of the argument. | 
|  | * If the argument is negative flag_dash is also set. | 
|  | */ | 
|  | bool width_star: 1; | 
|  |  | 
|  | /** Precision field present */ | 
|  | bool prec_present: 1; | 
|  |  | 
|  | /** Precision from int argument | 
|  | * | 
|  | * prec_value is set to the value of a non-negative argument. | 
|  | * If the argument is negative prec_present is cleared. | 
|  | */ | 
|  | bool prec_star: 1; | 
|  |  | 
|  | /** Length modifier (value from length_mod_enum) */ | 
|  | unsigned int length_mod: 4; | 
|  |  | 
|  | /** Indicates an a or A conversion specifier. | 
|  | * | 
|  | * This affects how precision is handled. | 
|  | */ | 
|  | bool specifier_a: 1; | 
|  |  | 
|  | /** Conversion specifier category (value from specifier_cat_enum) */ | 
|  | unsigned int specifier_cat: 3; | 
|  |  | 
|  | /** If set alternate form requires 0 before octal. */ | 
|  | bool altform_0: 1; | 
|  |  | 
|  | /** If set alternate form requires 0x before hex. */ | 
|  | bool altform_0c: 1; | 
|  |  | 
|  | /** Set when pad0_value zeroes are to be to be inserted after | 
|  | * the decimal point in a floating point conversion. | 
|  | */ | 
|  | bool pad_postdp: 1; | 
|  |  | 
|  | /** Set for floating point values that have a non-zero | 
|  | * pad0_prefix or pad0_pre_exp. | 
|  | */ | 
|  | bool pad_fp: 1; | 
|  |  | 
|  | /** Conversion specifier character */ | 
|  | unsigned char specifier; | 
|  |  | 
|  | union { | 
|  | /** Width value from specification. | 
|  | * | 
|  | * Valid until conversion begins. | 
|  | */ | 
|  | int width_value; | 
|  |  | 
|  | /** Number of extra zeroes to be inserted around a | 
|  | * formatted value: | 
|  | * | 
|  | * * before a formatted integer value due to precision | 
|  | *   and flag_zero; or | 
|  | * * before a floating point mantissa decimal point | 
|  | *   due to precision; or | 
|  | * * after a floating point mantissa decimal point due | 
|  | *   to precision. | 
|  | * | 
|  | * For example for zero-padded hexadecimal integers | 
|  | * this would insert where the angle brackets are in: | 
|  | * 0x<>hhhh. | 
|  | * | 
|  | * For floating point numbers this would insert at | 
|  | * either <1> or <2> depending on #pad_postdp: | 
|  | * VVV<1>.<2>FFFFeEEE | 
|  | * | 
|  | * Valid after conversion begins. | 
|  | */ | 
|  | int pad0_value; | 
|  | }; | 
|  |  | 
|  | union { | 
|  | /** Precision from specification. | 
|  | * | 
|  | * Valid until conversion begins. | 
|  | */ | 
|  | int prec_value; | 
|  |  | 
|  | /** Number of extra zeros to be inserted after a decimal | 
|  | * point due to precision. | 
|  | * | 
|  | * Inserts at <> in: VVVV.FFFF<>eEE | 
|  | * | 
|  | * Valid after conversion begins. | 
|  | */ | 
|  | int pad0_pre_exp; | 
|  | }; | 
|  | }; | 
|  |  | 
|  | /** Get a size represented as a sequence of decimal digits. | 
|  | * | 
|  | * @param[inout] str where to read from.  Updated to point to the first | 
|  | * unconsumed character.  There must be at least one non-digit character in | 
|  | * the referenced text. | 
|  | * | 
|  | * @return the decoded integer value. | 
|  | */ | 
|  | static size_t extract_decimal(const char **str) | 
|  | { | 
|  | const char *sp = *str; | 
|  | size_t val = 0; | 
|  |  | 
|  | while (isdigit((int)(unsigned char)*sp) != 0) { | 
|  | val = 10U * val + *sp++ - '0'; | 
|  | } | 
|  | *str = sp; | 
|  | return val; | 
|  | } | 
|  |  | 
|  | /** Extract C99 conversion specification flags. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the first character after the % of a conversion | 
|  | * specifier. | 
|  | * | 
|  | * @return a pointer the first character that follows the flags. | 
|  | */ | 
|  | static inline const char *extract_flags(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | bool loop = true; | 
|  |  | 
|  | do { | 
|  | switch (*sp) { | 
|  | case '-': | 
|  | conv->flag_dash = true; | 
|  | break; | 
|  | case '+': | 
|  | conv->flag_plus = true; | 
|  | break; | 
|  | case ' ': | 
|  | conv->flag_space = true; | 
|  | break; | 
|  | case '#': | 
|  | conv->flag_hash = true; | 
|  | break; | 
|  | case '0': | 
|  | conv->flag_zero = true; | 
|  | break; | 
|  | default: | 
|  | loop = false; | 
|  | } | 
|  | if (loop) { | 
|  | ++sp; | 
|  | } | 
|  | } while (loop); | 
|  |  | 
|  | /* zero && dash => !zero */ | 
|  | if (conv->flag_zero && conv->flag_dash) { | 
|  | conv->flag_zero = false; | 
|  | } | 
|  |  | 
|  | /* space && plus => !plus, handled in emitter code */ | 
|  |  | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | /** Extract a C99 conversion specification width. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the first character after the flags element of a | 
|  | * conversion specification. | 
|  | * | 
|  | * @return a pointer the first character that follows the width. | 
|  | */ | 
|  | static inline const char *extract_width(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | conv->width_present = true; | 
|  |  | 
|  | if (*sp == '*') { | 
|  | conv->width_star = true; | 
|  | return ++sp; | 
|  | } | 
|  |  | 
|  | const char *wp = sp; | 
|  | size_t width = extract_decimal(&sp); | 
|  |  | 
|  | if (sp != wp) { | 
|  | conv->width_present = true; | 
|  | conv->width_value = width; | 
|  | conv->unsupported |= ((conv->width_value < 0) | 
|  | || (width != (size_t)conv->width_value)); | 
|  | } | 
|  |  | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | /** Extract a C99 conversion specification precision. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the first character after the width element of a | 
|  | * conversion specification. | 
|  | * | 
|  | * @return a pointer the first character that follows the precision. | 
|  | */ | 
|  | static inline const char *extract_prec(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | conv->prec_present = (*sp == '.'); | 
|  |  | 
|  | if (!conv->prec_present) { | 
|  | return sp; | 
|  | } | 
|  | ++sp; | 
|  |  | 
|  | if (*sp == '*') { | 
|  | conv->prec_star = true; | 
|  | return ++sp; | 
|  | } | 
|  |  | 
|  | size_t prec = extract_decimal(&sp); | 
|  |  | 
|  | conv->prec_value = prec; | 
|  | conv->unsupported |= ((conv->prec_value < 0) | 
|  | || (prec != (size_t)conv->prec_value)); | 
|  |  | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | /** Extract a C99 conversion specification length. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the first character after the precision element of a | 
|  | * conversion specification. | 
|  | * | 
|  | * @return a pointer the first character that follows the precision. | 
|  | */ | 
|  | static inline const char *extract_length(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | switch (*sp) { | 
|  | case 'h': | 
|  | if (*++sp == 'h') { | 
|  | conv->length_mod = LENGTH_HH; | 
|  | ++sp; | 
|  | } else { | 
|  | conv->length_mod = LENGTH_H; | 
|  | } | 
|  | break; | 
|  | case 'l': | 
|  | if (*++sp == 'l') { | 
|  | conv->length_mod = LENGTH_LL; | 
|  | ++sp; | 
|  | } else { | 
|  | conv->length_mod = LENGTH_L; | 
|  | } | 
|  | break; | 
|  | case 'j': | 
|  | conv->length_mod = LENGTH_J; | 
|  | ++sp; | 
|  | break; | 
|  | case 'z': | 
|  | conv->length_mod = LENGTH_Z; | 
|  | ++sp; | 
|  | break; | 
|  | case 't': | 
|  | conv->length_mod = LENGTH_T; | 
|  | ++sp; | 
|  | break; | 
|  | case 'L': | 
|  | conv->length_mod = LENGTH_UPPER_L; | 
|  | ++sp; | 
|  |  | 
|  | /* We recognize and consume these, but can't format | 
|  | * them. | 
|  | */ | 
|  | conv->unsupported = true; | 
|  | break; | 
|  | default: | 
|  | conv->length_mod = LENGTH_NONE; | 
|  | break; | 
|  | } | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | /* Extract a C99 conversion specifier. | 
|  | * | 
|  | * This is the character that identifies the representation of the converted | 
|  | * value. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the first character after the length element of a | 
|  | * conversion specification. | 
|  | * | 
|  | * @return a pointer the first character that follows the specifier. | 
|  | */ | 
|  | static inline const char *extract_specifier(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | bool unsupported = false; | 
|  |  | 
|  | conv->specifier = *sp; | 
|  | ++sp; | 
|  |  | 
|  | switch (conv->specifier) { | 
|  | case SINT_CONV_CASES: | 
|  | conv->specifier_cat = SPECIFIER_SINT; | 
|  | goto int_conv; | 
|  | case UINT_CONV_CASES: | 
|  | conv->specifier_cat = SPECIFIER_UINT; | 
|  | int_conv: | 
|  | /* L length specifier not acceptable */ | 
|  | if (conv->length_mod == LENGTH_UPPER_L) { | 
|  | conv->invalid = true; | 
|  | } | 
|  |  | 
|  | /* For c LENGTH_NONE and LENGTH_L would be ok, | 
|  | * but we don't support formatting wide characters. | 
|  | */ | 
|  | if (conv->specifier == 'c') { | 
|  | unsupported = (conv->length_mod != LENGTH_NONE); | 
|  | } else if (!IS_ENABLED(CONFIG_CBPRINTF_FULL_INTEGRAL)) { | 
|  | /* Disable conversion that might produce truncated | 
|  | * results with buffers sized for 32 bits. | 
|  | */ | 
|  | switch (conv->length_mod) { | 
|  | case LENGTH_L: | 
|  | unsupported = sizeof(long) > 4; | 
|  | break; | 
|  | case LENGTH_LL: | 
|  | unsupported = sizeof(long long) > 4; | 
|  | break; | 
|  | case LENGTH_J: | 
|  | unsupported = sizeof(uintmax_t) > 4; | 
|  | break; | 
|  | case LENGTH_Z: | 
|  | unsupported = sizeof(size_t) > 4; | 
|  | break; | 
|  | case LENGTH_T: | 
|  | unsupported = sizeof(ptrdiff_t) > 4; | 
|  | break; | 
|  | default: | 
|  | /* Add an empty default with break, this is a defensive | 
|  | * programming. Static analysis tool won't raise a violation | 
|  | * if default is empty, but has that comment. | 
|  | */ | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | ; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case FP_CONV_CASES: | 
|  | conv->specifier_cat = SPECIFIER_FP; | 
|  |  | 
|  | /* Don't support if disabled */ | 
|  | if (!IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) { | 
|  | unsupported = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* When FP enabled %a support is still conditional. */ | 
|  | conv->specifier_a = (conv->specifier == 'a') | 
|  | || (conv->specifier == 'A'); | 
|  | if (conv->specifier_a | 
|  | && !IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT)) { | 
|  | unsupported = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* The l specifier has no effect.  Otherwise length | 
|  | * modifiers other than L are invalid. | 
|  | */ | 
|  | if (conv->length_mod == LENGTH_L) { | 
|  | conv->length_mod = LENGTH_NONE; | 
|  | } else if ((conv->length_mod != LENGTH_NONE) | 
|  | && (conv->length_mod != LENGTH_UPPER_L)) { | 
|  | conv->invalid = true; | 
|  | } else { | 
|  | ; | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | /* PTR cases are distinct */ | 
|  | case 'n': | 
|  | conv->specifier_cat = SPECIFIER_PTR; | 
|  | /* Anything except L */ | 
|  | if (conv->length_mod == LENGTH_UPPER_L) { | 
|  | unsupported = true; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 's': | 
|  | case 'p': | 
|  | conv->specifier_cat = SPECIFIER_PTR; | 
|  |  | 
|  | /* p: only LENGTH_NONE | 
|  | * | 
|  | * s: LENGTH_NONE or LENGTH_L but wide | 
|  | * characters not supported. | 
|  | */ | 
|  | if (conv->length_mod != LENGTH_NONE) { | 
|  | unsupported = true; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | conv->invalid = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | conv->unsupported |= unsupported; | 
|  |  | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | /* Extract the complete C99 conversion specification. | 
|  | * | 
|  | * @param conv pointer to the conversion being defined. | 
|  | * | 
|  | * @param sp pointer to the % that introduces a conversion specification. | 
|  | * | 
|  | * @return pointer to the first character that follows the specification. | 
|  | */ | 
|  | static inline const char *extract_conversion(struct conversion *conv, | 
|  | const char *sp) | 
|  | { | 
|  | *conv = (struct conversion) { | 
|  | .invalid = false, | 
|  | }; | 
|  |  | 
|  | /* Skip over the opening %.  If the conversion specifier is %, | 
|  | * that's the only thing that should be there, so | 
|  | * fast-exit. | 
|  | */ | 
|  | ++sp; | 
|  | if (*sp == '%') { | 
|  | conv->specifier = *sp; | 
|  | ++sp; | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | sp = extract_flags(conv, sp); | 
|  | sp = extract_width(conv, sp); | 
|  | sp = extract_prec(conv, sp); | 
|  | sp = extract_length(conv, sp); | 
|  | sp = extract_specifier(conv, sp); | 
|  |  | 
|  | return sp; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_64BIT | 
|  |  | 
|  | static void _ldiv5(uint64_t *v) | 
|  | { | 
|  | /* The compiler can optimize this on its own on 64-bit architectures */ | 
|  | *v /= 5U; | 
|  | } | 
|  |  | 
|  | #else /* CONFIG_64BIT */ | 
|  |  | 
|  | /* | 
|  | * Tiny integer divide-by-five routine.  The full 64 bit division | 
|  | * implementations in libgcc are very large on some architectures, and | 
|  | * currently nothing in Zephyr pulls it into the link.  So it makes | 
|  | * sense to define this much smaller special case here to avoid | 
|  | * including it just for printf. | 
|  | * | 
|  | * It works by multiplying v by the reciprocal of 5 i.e.: | 
|  | * | 
|  | *	result = v * ((1 << 64) / 5) / (1 << 64) | 
|  | * | 
|  | * This produces a 128-bit result, but we drop the bottom 64 bits which | 
|  | * accounts for the division by (1 << 64). The product is kept to 64 bits | 
|  | * by summing partial multiplications and shifting right by 32 which on | 
|  | * most 32-bit architectures means only a register drop. | 
|  | * | 
|  | * Here the multiplier is: (1 << 64) / 5 = 0x3333333333333333 | 
|  | * i.e. a 62 bits value. To compensate for the reduced precision, we | 
|  | * add an initial bias of 1 to v. This conveniently allows for keeping | 
|  | * the multiplier in a single 32-bit register given its pattern. | 
|  | * Enlarging the multiplier to 64 bits would also work but carry handling | 
|  | * on the summing of partial mults would be necessary, and a final right | 
|  | * shift would be needed, requiring more instructions. | 
|  | */ | 
|  | static void _ldiv5(uint64_t *v) | 
|  | { | 
|  | uint32_t v_lo = *v; | 
|  | uint32_t v_hi = *v >> 32; | 
|  | uint32_t m = 0x33333333; | 
|  | uint64_t result; | 
|  |  | 
|  | /* | 
|  | * Force the multiplier constant into a register and make it | 
|  | * opaque to the compiler, otherwise gcc tries to be too smart | 
|  | * for its own good with a large expansion of adds and shifts. | 
|  | */ | 
|  | __asm__ ("" : "+r" (m)); | 
|  |  | 
|  | /* | 
|  | * Apply a bias of 1 to v. We can't add it to v as this would overflow | 
|  | * it when at max range. Factor it out with the multiplier upfront. | 
|  | */ | 
|  | result = ((uint64_t)m << 32) | m; | 
|  |  | 
|  | /* The actual multiplication. */ | 
|  | result += (uint64_t)v_lo * m; | 
|  | result >>= 32; | 
|  | result += (uint64_t)v_lo * m; | 
|  | result += (uint64_t)v_hi * m; | 
|  | result >>= 32; | 
|  | result += (uint64_t)v_hi * m; | 
|  |  | 
|  | *v = result; | 
|  | } | 
|  |  | 
|  | #endif /* CONFIG_64BIT */ | 
|  |  | 
|  | /* Division by 10 */ | 
|  | static void _ldiv10(uint64_t *v) | 
|  | { | 
|  | *v >>= 1; | 
|  | _ldiv5(v); | 
|  | } | 
|  |  | 
|  | /* Extract the next decimal character in the converted representation of a | 
|  | * fractional component. | 
|  | */ | 
|  | static char _get_digit(uint64_t *fr, int *digit_count) | 
|  | { | 
|  | char rval; | 
|  |  | 
|  | if (*digit_count > 0) { | 
|  | --*digit_count; | 
|  | *fr *= 10U; | 
|  | rval = ((*fr >> 60) & 0xF) + '0'; | 
|  | *fr &= (BIT64(60) - 1U); | 
|  | } else { | 
|  | rval = '0'; | 
|  | } | 
|  |  | 
|  | return rval; | 
|  | } | 
|  |  | 
|  | static inline size_t conversion_radix(char specifier) | 
|  | { | 
|  | switch (specifier) { | 
|  | default: | 
|  | case 'd': | 
|  | case 'i': | 
|  | case 'u': | 
|  | return 10; | 
|  | case 'o': | 
|  | return 8; | 
|  | case 'p': | 
|  | case 'x': | 
|  | case 'X': | 
|  | return 16; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Writes the given value into the buffer in the specified base. | 
|  | * | 
|  | * Precision is applied *ONLY* within the space allowed. | 
|  | * | 
|  | * Alternate form value is applied to o, x, and X conversions. | 
|  | * | 
|  | * The buffer is filled backwards, so the input bpe is the end of the | 
|  | * generated representation.  The returned pointer is to the first | 
|  | * character of the representation. | 
|  | */ | 
|  | static char *encode_uint(uint_value_type value, | 
|  | struct conversion *conv, | 
|  | char *bps, | 
|  | const char *bpe) | 
|  | { | 
|  | bool upcase = isupper((int)conv->specifier) != 0; | 
|  | const unsigned int radix = conversion_radix(conv->specifier); | 
|  | char *bp = bps + (bpe - bps); | 
|  |  | 
|  | do { | 
|  | unsigned int lsv = (unsigned int)(value % radix); | 
|  |  | 
|  | --bp; | 
|  | *bp = (lsv <= 9) ? ('0' + lsv) | 
|  | : upcase ? ('A' + lsv - 10) : ('a' + lsv - 10); | 
|  | value /= radix; | 
|  | } while ((value != 0) && (bps < bp)); | 
|  |  | 
|  | /* Record required alternate forms.  This can be determined | 
|  | * from the radix without re-checking specifier. | 
|  | */ | 
|  | if (conv->flag_hash) { | 
|  | if (radix == 8) { | 
|  | conv->altform_0 = true; | 
|  | } else if (radix == 16) { | 
|  | conv->altform_0c = true; | 
|  | } else { | 
|  | ; | 
|  | } | 
|  | } | 
|  |  | 
|  | return bp; | 
|  | } | 
|  |  | 
|  | /* Number of bits in the fractional part of an IEEE 754-2008 double | 
|  | * precision float. | 
|  | */ | 
|  | #define FRACTION_BITS 52 | 
|  |  | 
|  | /* Number of hex "digits" in the fractional part of an IEEE 754-2008 | 
|  | * double precision float. | 
|  | */ | 
|  | #define FRACTION_HEX DIV_ROUND_UP(FRACTION_BITS, 4) | 
|  |  | 
|  | /* Number of bits in the exponent of an IEEE 754-2008 double precision | 
|  | * float. | 
|  | */ | 
|  | #define EXPONENT_BITS 11 | 
|  |  | 
|  | /* Mask for the sign (negative) bit of an IEEE 754-2008 double precision | 
|  | * float. | 
|  | */ | 
|  | #define SIGN_MASK BIT64(63) | 
|  |  | 
|  | /* Mask for the high-bit of a uint64_t representation of a fractional | 
|  | * value. | 
|  | */ | 
|  | #define BIT_63 BIT64(63) | 
|  |  | 
|  | /* Convert the IEEE 754-2008 double to text format. | 
|  | * | 
|  | * @param value the 64-bit floating point value. | 
|  | * | 
|  | * @param conv details about how the conversion is to proceed.  Some fields | 
|  | * are adjusted based on the value being converted. | 
|  | * | 
|  | * @param precision the precision for the conversion (generally digits past | 
|  | * the decimal point). | 
|  | * | 
|  | * @param bps pointer to the first character in a buffer that will hold the | 
|  | * converted value. | 
|  | * | 
|  | * @param bpe On entry this points to the end of the buffer reserved to hold | 
|  | * the converted value.  On exit it is updated to point just past the | 
|  | * converted value. | 
|  | * | 
|  | * return a pointer to the start of the converted value.  This may not be @p | 
|  | * bps but will be consistent with the exit value of *bpe. | 
|  | */ | 
|  | static char *encode_float(double value, | 
|  | struct conversion *conv, | 
|  | int precision, | 
|  | char *sign, | 
|  | char *bps, | 
|  | const char **bpe) | 
|  | { | 
|  | union { | 
|  | uint64_t u64; | 
|  | double dbl; | 
|  | } u = { | 
|  | .dbl = value, | 
|  | }; | 
|  | bool prune_zero = false; | 
|  | char *buf = bps; | 
|  |  | 
|  | /* Prepend the sign: '-' if negative, flags control | 
|  | * non-negative behavior. | 
|  | */ | 
|  | if ((u.u64 & SIGN_MASK) != 0U) { | 
|  | *sign = '-'; | 
|  | } else if (conv->flag_plus) { | 
|  | *sign = '+'; | 
|  | } else if (conv->flag_space) { | 
|  | *sign = ' '; | 
|  | } else { | 
|  | ; | 
|  | } | 
|  |  | 
|  | /* Extract the non-negative offset exponent and fraction.  Record | 
|  | * whether the value is subnormal. | 
|  | */ | 
|  | char c = conv->specifier; | 
|  | int expo = (u.u64 >> FRACTION_BITS) & BIT_MASK(EXPONENT_BITS); | 
|  | uint64_t fract = u.u64 & BIT64_MASK(FRACTION_BITS); | 
|  | bool is_subnormal = (expo == 0) && (fract != 0); | 
|  |  | 
|  | /* Exponent of all-ones signals infinity or NaN, which are | 
|  | * text constants regardless of specifier. | 
|  | */ | 
|  | if (expo == BIT_MASK(EXPONENT_BITS)) { | 
|  | if (fract == 0) { | 
|  | if (isupper((unsigned char)c) != 0) { | 
|  | buf[0] = 'I'; | 
|  | buf[1] = 'N'; | 
|  | buf[2] = 'F'; | 
|  | buf += 3; | 
|  | } else { | 
|  | buf[0] = 'i'; | 
|  | buf[1] = 'n'; | 
|  | buf[2] = 'f'; | 
|  | buf += 3; | 
|  | } | 
|  | } else { | 
|  | if (isupper((unsigned char)c) != 0) { | 
|  | buf[0] = 'N'; | 
|  | buf[1] = 'A'; | 
|  | buf[2] = 'N'; | 
|  | buf += 3; | 
|  | } else { | 
|  | buf[0] = 'n'; | 
|  | buf[1] = 'a'; | 
|  | buf[2] = 'n'; | 
|  | buf += 3; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* No zero-padding with text values */ | 
|  | conv->flag_zero = false; | 
|  |  | 
|  | *bpe = buf; | 
|  | return bps; | 
|  | } | 
|  |  | 
|  | /* The case of an F specifier is no longer relevant. */ | 
|  | if (c == 'F') { | 
|  | c = 'f'; | 
|  | } | 
|  |  | 
|  | /* Handle converting to the hex representation. */ | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_FP_A_SUPPORT) | 
|  | && (IS_ENABLED(CONFIG_CBPRINTF_FP_ALWAYS_A) | 
|  | || conv->specifier_a)) { | 
|  | buf[0] = '0'; | 
|  | buf[1] = 'x'; | 
|  | buf += 2; | 
|  |  | 
|  | /* Remove the offset from the exponent, and store the | 
|  | * non-fractional value.  Subnormals require increasing the | 
|  | * exponent as first bit isn't the implicit bit. | 
|  | */ | 
|  | expo -= 1023; | 
|  | if (is_subnormal) { | 
|  | *buf = '0'; | 
|  | ++buf; | 
|  | ++expo; | 
|  | } else { | 
|  | *buf = '1'; | 
|  | ++buf; | 
|  | } | 
|  |  | 
|  | /* If we didn't get precision from a %a specification then we | 
|  | * treat it as from a %a specification with no precision: full | 
|  | * range, zero-pruning enabled. | 
|  | * | 
|  | * Otherwise we have to cap the precision of the generated | 
|  | * fraction, or possibly round it. | 
|  | */ | 
|  | if (!(conv->specifier_a && conv->prec_present)) { | 
|  | precision = FRACTION_HEX; | 
|  | prune_zero = true; | 
|  | } else if (precision > FRACTION_HEX) { | 
|  | conv->pad0_pre_exp = precision - FRACTION_HEX; | 
|  | conv->pad_fp = true; | 
|  | precision = FRACTION_HEX; | 
|  | } else if ((fract != 0) | 
|  | && (precision < FRACTION_HEX)) { | 
|  | size_t pos = 4 * (FRACTION_HEX - precision) - 1; | 
|  | uint64_t mask = BIT64(pos); | 
|  |  | 
|  | /* Round only if the bit that would round is | 
|  | * set. | 
|  | */ | 
|  | if ((fract & mask) != 0ULL) { | 
|  | fract += mask; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Record whether we must retain the decimal point even if we | 
|  | * can prune zeros. | 
|  | */ | 
|  | bool require_dp = ((fract != 0) || conv->flag_hash); | 
|  |  | 
|  | if (require_dp || (precision != 0)) { | 
|  | *buf = '.'; | 
|  | ++buf; | 
|  | } | 
|  |  | 
|  | /* Get the fractional value as a hexadecimal string, using x | 
|  | * for a and X for A. | 
|  | */ | 
|  | struct conversion aconv = { | 
|  | .specifier = isupper((unsigned char)c) != 0 ? 'X' : 'x', | 
|  | }; | 
|  | const char *spe = *bpe; | 
|  | char *sp = bps + (spe - bps); | 
|  |  | 
|  | if (fract != 0) { | 
|  | sp = encode_uint(fract, &aconv, buf, spe); | 
|  | } | 
|  |  | 
|  | /* Pad out to full range since this is below the decimal | 
|  | * point. | 
|  | */ | 
|  | while ((spe - sp) < FRACTION_HEX) { | 
|  | --sp; | 
|  | *sp = '0'; | 
|  | } | 
|  |  | 
|  | /* Append the leading significant "digits". */ | 
|  | while ((sp < spe) && (precision > 0)) { | 
|  | *buf = *sp; | 
|  | ++buf; | 
|  | ++sp; | 
|  | --precision; | 
|  | } | 
|  |  | 
|  | if (prune_zero) { | 
|  | while (*--buf == '0') { | 
|  | ; | 
|  | } | 
|  | if ((*buf != '.') || require_dp) { | 
|  | ++buf; | 
|  | } | 
|  | } | 
|  |  | 
|  | *buf = 'p'; | 
|  | ++buf; | 
|  | if (expo >= 0) { | 
|  | *buf = '+'; | 
|  | ++buf; | 
|  | } else { | 
|  | *buf = '-'; | 
|  | ++buf; | 
|  | expo = -expo; | 
|  | } | 
|  |  | 
|  | aconv.specifier = 'i'; | 
|  | sp = encode_uint(expo, &aconv, buf, spe); | 
|  |  | 
|  | while (sp < spe) { | 
|  | *buf = *sp; | 
|  | ++buf; | 
|  | ++sp; | 
|  | } | 
|  |  | 
|  | *bpe = buf; | 
|  | return bps; | 
|  | } | 
|  |  | 
|  | /* Remainder of code operates on a 64-bit fraction, so shift up (and | 
|  | * discard garbage from the exponent where the implicit 1 would be | 
|  | * stored). | 
|  | */ | 
|  | fract <<= EXPONENT_BITS; | 
|  | fract &= ~SIGN_MASK; | 
|  |  | 
|  | /* Non-zero values need normalization. */ | 
|  | if ((expo | fract) != 0) { | 
|  | if (is_subnormal) { | 
|  | /* Fraction is subnormal.  Normalize it and correct | 
|  | * the exponent. | 
|  | */ | 
|  | for (fract <<= 1; (fract & BIT_63) == 0; fract <<= 1) { | 
|  | expo--; | 
|  | } | 
|  | } | 
|  | /* Adjust the offset exponent to be signed rather than offset, | 
|  | * and set the implicit 1 bit in the (shifted) 53-bit | 
|  | * fraction. | 
|  | */ | 
|  | expo -= (1023 - 1);	/* +1 since .1 vs 1. */ | 
|  | fract |= BIT_63; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Let's consider: | 
|  | * | 
|  | *	value = fract * 2^expo * 10^decexp | 
|  | * | 
|  | * Initially decexp = 0. The goal is to bring exp between | 
|  | * 0 and -2 as the magnitude of a fractional decimal digit is 3 bits. | 
|  | */ | 
|  | int decexp = 0; | 
|  |  | 
|  | while (expo < -2) { | 
|  | /* | 
|  | * Make room to allow a multiplication by 5 without overflow. | 
|  | * We test only the top part for faster code. | 
|  | */ | 
|  | do { | 
|  | fract >>= 1; | 
|  | expo++; | 
|  | } while ((uint32_t)(fract >> 32) >= (UINT32_MAX / 5U)); | 
|  |  | 
|  | /* Perform fract * 5 * 2 / 10 */ | 
|  | fract *= 5U; | 
|  | expo++; | 
|  | decexp--; | 
|  | } | 
|  |  | 
|  | while (expo > 0) { | 
|  | /* | 
|  | * Perform fract / 5 / 2 * 10. | 
|  | * The +2 is there to do round the result of the division | 
|  | * by 5 not to lose too much precision in extreme cases. | 
|  | */ | 
|  | fract += 2; | 
|  | _ldiv5(&fract); | 
|  | expo--; | 
|  | decexp++; | 
|  |  | 
|  | /* Bring back our fractional number to full scale */ | 
|  | do { | 
|  | fract <<= 1; | 
|  | expo--; | 
|  | } while (!(fract & BIT_63)); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * The binary fractional point is located somewhere above bit 63. | 
|  | * Move it between bits 59 and 60 to give 4 bits of room to the | 
|  | * integer part. | 
|  | */ | 
|  | fract >>= (4 - expo); | 
|  |  | 
|  | if ((c == 'g') || (c == 'G')) { | 
|  | /* Use the specified precision and exponent to select the | 
|  | * representation and correct the precision and zero-pruning | 
|  | * in accordance with the ISO C rule. | 
|  | */ | 
|  | if ((decexp < (-4 + 1)) || (decexp > precision)) { | 
|  | c += 'e' - 'g';  /* e or E */ | 
|  | if (precision > 0) { | 
|  | precision--; | 
|  | } | 
|  | } else { | 
|  | c = 'f'; | 
|  | precision -= decexp; | 
|  | } | 
|  | if (!conv->flag_hash && (precision > 0)) { | 
|  | prune_zero = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | int decimals; | 
|  | if (c == 'f') { | 
|  | decimals = precision + decexp; | 
|  | if (decimals < 0) { | 
|  | decimals = 0; | 
|  | } | 
|  | } else { | 
|  | decimals = precision + 1; | 
|  | } | 
|  |  | 
|  | int digit_count = 16; | 
|  |  | 
|  | if (decimals > 16) { | 
|  | decimals = 16; | 
|  | } | 
|  |  | 
|  | /* Round the value to the last digit being printed. */ | 
|  | uint64_t round = BIT64(59); /* 0.5 */ | 
|  | while (decimals-- != 0) { | 
|  | _ldiv10(&round); | 
|  | } | 
|  | fract += round; | 
|  | /* Make sure rounding didn't make fract >= 1.0 */ | 
|  | if (fract >= BIT64(60)) { | 
|  | _ldiv10(&fract); | 
|  | decexp++; | 
|  | } | 
|  |  | 
|  | if (c == 'f') { | 
|  | if (decexp > 0) { | 
|  | /* Emit the digits above the decimal point. */ | 
|  | while ((decexp > 0) && (digit_count > 0)) { | 
|  | *buf = _get_digit(&fract, &digit_count); | 
|  | ++buf; | 
|  | decexp--; | 
|  | } | 
|  |  | 
|  | conv->pad0_value = decexp; | 
|  |  | 
|  | decexp = 0; | 
|  | } else { | 
|  | *buf = '0'; | 
|  | ++buf; | 
|  | } | 
|  |  | 
|  | /* Emit the decimal point only if required by the alternative | 
|  | * format, or if more digits are to follow. | 
|  | */ | 
|  | if (conv->flag_hash || (precision > 0)) { | 
|  | *buf = '.'; | 
|  | ++buf; | 
|  | } | 
|  |  | 
|  | if ((decexp < 0) && (precision > 0)) { | 
|  | conv->pad0_value = -decexp; | 
|  | if (conv->pad0_value > precision) { | 
|  | conv->pad0_value = precision; | 
|  | } | 
|  |  | 
|  | precision -= conv->pad0_value; | 
|  | conv->pad_postdp = (conv->pad0_value > 0); | 
|  | } | 
|  | } else { /* e or E */ | 
|  | /* Emit the one digit before the decimal.  If it's not zero, | 
|  | * this is significant so reduce the base-10 exponent. | 
|  | */ | 
|  | *buf = _get_digit(&fract, &digit_count); | 
|  | if (*buf++ != '0') { | 
|  | decexp--; | 
|  | } | 
|  |  | 
|  | /* Emit the decimal point only if required by the alternative | 
|  | * format, or if more digits are to follow. | 
|  | */ | 
|  | if (conv->flag_hash || (precision > 0)) { | 
|  | *buf = '.'; | 
|  | ++buf; | 
|  | } | 
|  | } | 
|  |  | 
|  | while ((precision > 0) && (digit_count > 0)) { | 
|  | *buf = _get_digit(&fract, &digit_count); | 
|  | ++buf; | 
|  | precision--; | 
|  | } | 
|  |  | 
|  | conv->pad0_pre_exp = precision; | 
|  |  | 
|  | if (prune_zero) { | 
|  | conv->pad0_pre_exp = 0; | 
|  | do { | 
|  | --buf; | 
|  | } while (*buf == '0'); | 
|  | if (*buf != '.') { | 
|  | ++buf; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Emit the explicit exponent, if format requires it. */ | 
|  | if ((c == 'e') || (c == 'E')) { | 
|  | *buf = c; | 
|  | ++buf; | 
|  | if (decexp < 0) { | 
|  | decexp = -decexp; | 
|  | *buf = '-'; | 
|  | ++buf; | 
|  | } else { | 
|  | *buf = '+'; | 
|  | ++buf; | 
|  | } | 
|  |  | 
|  | /* At most 3 digits to the decimal.  Spit them out. */ | 
|  | if (decexp >= 100) { | 
|  | *buf = (decexp / 100) + '0'; | 
|  | ++buf; | 
|  | decexp %= 100; | 
|  | } | 
|  |  | 
|  | buf[0] = (decexp / 10) + '0'; | 
|  | buf[1] = (decexp % 10) + '0'; | 
|  | buf += 2; | 
|  | } | 
|  |  | 
|  | /* Cache whether there's padding required */ | 
|  | conv->pad_fp = (conv->pad0_value > 0) | 
|  | || (conv->pad0_pre_exp > 0); | 
|  |  | 
|  | /* Set the end of the encoded sequence, and return its start.  Also | 
|  | * store EOS as a non-digit/non-decimal value so we don't have to | 
|  | * check against bpe when iterating in multiple places. | 
|  | */ | 
|  | *bpe = buf; | 
|  | *buf = 0; | 
|  | return bps; | 
|  | } | 
|  |  | 
|  | /* Store a count into the pointer provided in a %n specifier. | 
|  | * | 
|  | * @param conv the specifier that indicates the size of the value into which | 
|  | * the count will be stored. | 
|  | * | 
|  | * @param dp where the count should be stored. | 
|  | * | 
|  | * @param count the count to be stored. | 
|  | */ | 
|  | static inline void store_count(const struct conversion *conv, | 
|  | void *dp, | 
|  | int count) | 
|  | { | 
|  | switch ((enum length_mod_enum)conv->length_mod) { | 
|  | case LENGTH_NONE: | 
|  | *(int *)dp = count; | 
|  | break; | 
|  | case LENGTH_HH: | 
|  | *(signed char *)dp = (signed char)count; | 
|  | break; | 
|  | case LENGTH_H: | 
|  | *(short *)dp = (short)count; | 
|  | break; | 
|  | case LENGTH_L: | 
|  | *(long *)dp = (long)count; | 
|  | break; | 
|  | case LENGTH_LL: | 
|  | *(long long *)dp = (long long)count; | 
|  | break; | 
|  | case LENGTH_J: | 
|  | *(intmax_t *)dp = (intmax_t)count; | 
|  | break; | 
|  | case LENGTH_Z: | 
|  | *(size_t *)dp = (size_t)count; | 
|  | break; | 
|  | case LENGTH_T: | 
|  | *(ptrdiff_t *)dp = (ptrdiff_t)count; | 
|  | break; | 
|  | default: | 
|  | /* Add an empty default with break, this is a defensive programming. | 
|  | * Static analysis tool won't raise a violation if default is empty, | 
|  | * but has that comment. | 
|  | */ | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Outline function to emit all characters in [sp, ep). */ | 
|  | static int outs(cbprintf_cb __out, | 
|  | void *ctx, | 
|  | const char *sp, | 
|  | const char *ep) | 
|  | { | 
|  | size_t count = 0; | 
|  | cbprintf_cb_local out = __out; | 
|  |  | 
|  | while ((sp < ep) || ((ep == NULL) && *sp)) { | 
|  | int rc = out((int)*sp, ctx); | 
|  | ++sp; | 
|  |  | 
|  | if (rc < 0) { | 
|  | return rc; | 
|  | } | 
|  | ++count; | 
|  | } | 
|  |  | 
|  | return (int)count; | 
|  | } | 
|  |  | 
|  | int z_cbvprintf_impl(cbprintf_cb __out, void *ctx, const char *fp, | 
|  | va_list ap, uint32_t flags) | 
|  | { | 
|  | char buf[CONVERTED_BUFLEN]; | 
|  | size_t count = 0; | 
|  | sint_value_type sint; | 
|  | cbprintf_cb_local out = __out; | 
|  |  | 
|  | const bool tagged_ap = (flags & Z_CBVPRINTF_PROCESS_FLAG_TAGGED_ARGS) | 
|  | == Z_CBVPRINTF_PROCESS_FLAG_TAGGED_ARGS; | 
|  |  | 
|  | /* Output character, returning EOF if output failed, otherwise | 
|  | * updating count. | 
|  | * | 
|  | * NB: c is evaluated exactly once: side-effects are OK | 
|  | */ | 
|  | #define OUTC(c) do { \ | 
|  | int rc = (*out)((int)(c), ctx); \ | 
|  | \ | 
|  | if (rc < 0) { \ | 
|  | return rc; \ | 
|  | } \ | 
|  | ++count; \ | 
|  | } while (false) | 
|  |  | 
|  | /* Output sequence of characters, returning a negative error if output | 
|  | * failed. | 
|  | */ | 
|  |  | 
|  | #define OUTS(_sp, _ep) do { \ | 
|  | int rc = outs(out, ctx, (_sp), (_ep)); \ | 
|  | \ | 
|  | if (rc < 0) {	    \ | 
|  | return rc; \ | 
|  | } \ | 
|  | count += rc; \ | 
|  | } while (false) | 
|  |  | 
|  | while (*fp != 0) { | 
|  | if (*fp != '%') { | 
|  | OUTC(*fp); | 
|  | ++fp; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Force union into RAM with conversion state to | 
|  | * mitigate LLVM code generation bug. | 
|  | */ | 
|  | struct { | 
|  | union argument_value value; | 
|  | struct conversion conv; | 
|  | } state = { | 
|  | .value = { | 
|  | .uint = 0, | 
|  | }, | 
|  | }; | 
|  | struct conversion *const conv = &state.conv; | 
|  | union argument_value *const value = &state.value; | 
|  | const char *sp = fp; | 
|  | int width = -1; | 
|  | int precision = -1; | 
|  | const char *bps = NULL; | 
|  | const char *bpe = buf + sizeof(buf); | 
|  | char sign = 0; | 
|  |  | 
|  | fp = extract_conversion(conv, sp); | 
|  |  | 
|  | if (conv->specifier_cat != SPECIFIER_INVALID) { | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_PACKAGE_SUPPORT_TAGGED_ARGUMENTS) | 
|  | && tagged_ap) { | 
|  | /* Skip over the argument tag as it is not being | 
|  | * used here. | 
|  | */ | 
|  | (void)va_arg(ap, int); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If dynamic width is specified, process it, | 
|  | * otherwise set width if present. | 
|  | */ | 
|  | if (conv->width_star) { | 
|  | width = va_arg(ap, int); | 
|  |  | 
|  | if (width < 0) { | 
|  | conv->flag_dash = true; | 
|  | width = -width; | 
|  | } | 
|  | } else if (conv->width_present) { | 
|  | width = conv->width_value; | 
|  | } else { | 
|  | ; | 
|  | } | 
|  |  | 
|  | /* If dynamic precision is specified, process it, otherwise | 
|  | * set precision if present.  For floating point where | 
|  | * precision is not present use 6. | 
|  | */ | 
|  | if (conv->prec_star) { | 
|  | int arg = va_arg(ap, int); | 
|  |  | 
|  | if (arg < 0) { | 
|  | conv->prec_present = false; | 
|  | } else { | 
|  | precision = arg; | 
|  | } | 
|  | } else if (conv->prec_present) { | 
|  | precision = conv->prec_value; | 
|  | } else { | 
|  | ; | 
|  | } | 
|  |  | 
|  | /* Reuse width and precision memory in conv for value | 
|  | * padding counts. | 
|  | */ | 
|  | conv->pad0_value = 0; | 
|  | conv->pad0_pre_exp = 0; | 
|  |  | 
|  | /* FP conversion requires knowing the precision. */ | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT) | 
|  | && (conv->specifier_cat == SPECIFIER_FP) | 
|  | && !conv->prec_present) { | 
|  | if (conv->specifier_a) { | 
|  | precision = FRACTION_HEX; | 
|  | } else { | 
|  | precision = 6; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Get the value to be converted from the args. | 
|  | * | 
|  | * This can't be extracted to a helper function because | 
|  | * passing a pointer to va_list doesn't work on x86_64.  See | 
|  | * https://stackoverflow.com/a/8048892. | 
|  | */ | 
|  | enum specifier_cat_enum specifier_cat | 
|  | = (enum specifier_cat_enum)conv->specifier_cat; | 
|  | enum length_mod_enum length_mod | 
|  | = (enum length_mod_enum)conv->length_mod; | 
|  |  | 
|  | /* Extract the value based on the argument category and length. | 
|  | * | 
|  | * Note that the length modifier doesn't affect the value of a | 
|  | * pointer argument. | 
|  | */ | 
|  | if (specifier_cat == SPECIFIER_SINT) { | 
|  | switch (length_mod) { | 
|  | default: | 
|  | case LENGTH_NONE: | 
|  | case LENGTH_HH: | 
|  | case LENGTH_H: | 
|  | value->sint = va_arg(ap, int); | 
|  | break; | 
|  | case LENGTH_L: | 
|  | if (WCHAR_IS_SIGNED | 
|  | && (conv->specifier == 'c')) { | 
|  | value->sint = (wchar_t)va_arg(ap, | 
|  | WINT_TYPE); | 
|  | } else { | 
|  | value->sint = va_arg(ap, long); | 
|  | } | 
|  | break; | 
|  | case LENGTH_LL: | 
|  | value->sint = | 
|  | (sint_value_type)va_arg(ap, long long); | 
|  | break; | 
|  | case LENGTH_J: | 
|  | value->sint = | 
|  | (sint_value_type)va_arg(ap, intmax_t); | 
|  | break; | 
|  | case LENGTH_Z:		/* size_t */ | 
|  | case LENGTH_T:		/* ptrdiff_t */ | 
|  | /* Though ssize_t is the signed equivalent of | 
|  | * size_t for POSIX, there is no uptrdiff_t. | 
|  | * Assume that size_t and ptrdiff_t are the | 
|  | * unsigned and signed equivalents of each | 
|  | * other.  This can be checked in a platform | 
|  | * test. | 
|  | */ | 
|  | value->sint = | 
|  | (sint_value_type)va_arg(ap, ptrdiff_t); | 
|  | break; | 
|  | } | 
|  | if (length_mod == LENGTH_HH) { | 
|  | value->sint = (signed char)value->sint; | 
|  | } else if (length_mod == LENGTH_H) { | 
|  | value->sint = (short)value->sint; | 
|  | } | 
|  | } else if (specifier_cat == SPECIFIER_UINT) { | 
|  | switch (length_mod) { | 
|  | default: | 
|  | case LENGTH_NONE: | 
|  | case LENGTH_HH: | 
|  | case LENGTH_H: | 
|  | value->uint = va_arg(ap, unsigned int); | 
|  | break; | 
|  | case LENGTH_L: | 
|  | if ((!WCHAR_IS_SIGNED) | 
|  | && (conv->specifier == 'c')) { | 
|  | value->uint = (wchar_t)va_arg(ap, | 
|  | WINT_TYPE); | 
|  | } else { | 
|  | value->uint = va_arg(ap, unsigned long); | 
|  | } | 
|  | break; | 
|  | case LENGTH_LL: | 
|  | value->uint = | 
|  | (uint_value_type)va_arg(ap, | 
|  | unsigned long long); | 
|  | break; | 
|  | case LENGTH_J: | 
|  | value->uint = | 
|  | (uint_value_type)va_arg(ap, | 
|  | uintmax_t); | 
|  | break; | 
|  | case LENGTH_Z:		/* size_t */ | 
|  | case LENGTH_T:		/* ptrdiff_t */ | 
|  | value->uint = | 
|  | (uint_value_type)va_arg(ap, size_t); | 
|  | break; | 
|  | } | 
|  | if (length_mod == LENGTH_HH) { | 
|  | value->uint = (unsigned char)value->uint; | 
|  | } else if (length_mod == LENGTH_H) { | 
|  | value->uint = (unsigned short)value->uint; | 
|  | } | 
|  | } else if (specifier_cat == SPECIFIER_FP) { | 
|  | if (length_mod == LENGTH_UPPER_L) { | 
|  | value->ldbl = va_arg(ap, long double); | 
|  | } else { | 
|  | value->dbl = va_arg(ap, double); | 
|  | } | 
|  | } else if (specifier_cat == SPECIFIER_PTR) { | 
|  | value->ptr = va_arg(ap, void *); | 
|  | } | 
|  |  | 
|  | /* We've now consumed all arguments related to this | 
|  | * specification.  If the conversion is invalid, or is | 
|  | * something we don't support, then output the original | 
|  | * specification and move on. | 
|  | */ | 
|  | if (conv->invalid || conv->unsupported) { | 
|  | OUTS(sp, fp); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* Do formatting, either into the buffer or | 
|  | * referencing external data. | 
|  | */ | 
|  | switch (conv->specifier) { | 
|  | case '%': | 
|  | OUTC('%'); | 
|  | break; | 
|  | case 's': { | 
|  | bps = (const char *)value->ptr; | 
|  |  | 
|  | size_t len; | 
|  |  | 
|  | if (precision >= 0) { | 
|  | len = strnlen(bps, precision); | 
|  | } else { | 
|  | len = strlen(bps); | 
|  | } | 
|  |  | 
|  | bpe = bps + len; | 
|  | precision = -1; | 
|  |  | 
|  | break; | 
|  | } | 
|  | case 'p': | 
|  | /* Implementation-defined: null is "(nil)", non-null | 
|  | * has 0x prefix followed by significant address hex | 
|  | * digits, no leading zeros. | 
|  | */ | 
|  | if (value->ptr != NULL) { | 
|  | bps = encode_uint((uintptr_t)value->ptr, conv, | 
|  | buf, bpe); | 
|  |  | 
|  | /* Use 0x prefix */ | 
|  | conv->altform_0c = true; | 
|  | conv->specifier = 'x'; | 
|  |  | 
|  | goto prec_int_pad0; | 
|  | } | 
|  |  | 
|  | bps = "(nil)"; | 
|  | bpe = bps + 5; | 
|  |  | 
|  | break; | 
|  | case 'c': | 
|  | bps = buf; | 
|  | buf[0] = CHAR_IS_SIGNED ? value->sint : value->uint; | 
|  | bpe = buf + 1; | 
|  | break; | 
|  | case 'd': | 
|  | case 'i': | 
|  | if (conv->flag_plus) { | 
|  | sign = '+'; | 
|  | } else if (conv->flag_space) { | 
|  | sign = ' '; | 
|  | } | 
|  |  | 
|  | /* sint/uint overlay in the union, and so | 
|  | * can't appear in read and write operations | 
|  | * in the same statement. | 
|  | */ | 
|  | sint = value->sint; | 
|  | if (sint < 0) { | 
|  | sign = '-'; | 
|  | value->uint = (uint_value_type)-sint; | 
|  | } else { | 
|  | value->uint = (uint_value_type)sint; | 
|  | } | 
|  |  | 
|  | __fallthrough; | 
|  | case 'o': | 
|  | case 'u': | 
|  | case 'x': | 
|  | case 'X': | 
|  | bps = encode_uint(value->uint, conv, buf, bpe); | 
|  |  | 
|  | prec_int_pad0: | 
|  | /* Update pad0 values based on precision and converted | 
|  | * length.  Note that a non-empty sign is not in the | 
|  | * converted sequence, but it does not affect the | 
|  | * padding size. | 
|  | */ | 
|  | if (precision >= 0) { | 
|  | size_t len = bpe - bps; | 
|  |  | 
|  | /* Zero-padding flag is ignored for integer | 
|  | * conversions with precision. | 
|  | */ | 
|  | conv->flag_zero = false; | 
|  |  | 
|  | /* Set pad0_value to satisfy precision */ | 
|  | if (len < (size_t)precision) { | 
|  | conv->pad0_value = precision - (int)len; | 
|  | } | 
|  | } | 
|  |  | 
|  | break; | 
|  | case 'n': | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_N_SPECIFIER)) { | 
|  | store_count(conv, value->ptr, count); | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case FP_CONV_CASES: | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT)) { | 
|  | bps = encode_float(value->dbl, conv, precision, | 
|  | &sign, buf, &bpe); | 
|  | } | 
|  | break; | 
|  | default: | 
|  | /* Add an empty default with break, this is a defensive | 
|  | * programming. Static analysis tool won't raise a violation | 
|  | * if default is empty, but has that comment. | 
|  | */ | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* If we don't have a converted value to emit, move | 
|  | * on. | 
|  | */ | 
|  | if (bps == NULL) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* The converted value is now stored in [bps, bpe), excluding | 
|  | * any required zero padding. | 
|  | * | 
|  | * The unjustified output will be: | 
|  | * | 
|  | * * any sign character (sint-only) | 
|  | * * any altform prefix | 
|  | * * for FP: | 
|  | *   * any pre-decimal content from the converted value | 
|  | *   * any pad0_value padding (!postdp) | 
|  | *   * any decimal point in the converted value | 
|  | *   * any pad0_value padding (postdp) | 
|  | *   * any pre-exponent content from the converted value | 
|  | *   * any pad0_pre_exp padding | 
|  | *   * any exponent content from the converted value | 
|  | * * for non-FP: | 
|  | *   * any pad0_prefix | 
|  | *   * the converted value | 
|  | */ | 
|  | size_t nj_len = (bpe - bps); | 
|  | int pad_len = 0; | 
|  |  | 
|  | if (sign != 0) { | 
|  | nj_len += 1U; | 
|  | } | 
|  |  | 
|  | if (conv->altform_0c) { | 
|  | nj_len += 2U; | 
|  | } else if (conv->altform_0) { | 
|  | nj_len += 1U; | 
|  | } | 
|  |  | 
|  | nj_len += conv->pad0_value; | 
|  | if (conv->pad_fp) { | 
|  | nj_len += conv->pad0_pre_exp; | 
|  | } | 
|  |  | 
|  | /* If we have a width update width to hold the padding we need | 
|  | * for justification.  The result may be negative, which will | 
|  | * result in no padding. | 
|  | * | 
|  | * If a non-negative padding width is present and we're doing | 
|  | * right-justification, emit the padding now. | 
|  | */ | 
|  | if (width > 0) { | 
|  | width -= (int)nj_len; | 
|  |  | 
|  | if (!conv->flag_dash) { | 
|  | char pad = ' '; | 
|  |  | 
|  | /* If we're zero-padding we have to emit the | 
|  | * sign first. | 
|  | */ | 
|  | if (conv->flag_zero) { | 
|  | if (sign != 0) { | 
|  | OUTC(sign); | 
|  | sign = 0; | 
|  | } | 
|  | pad = '0'; | 
|  | } | 
|  |  | 
|  | while (width-- > 0) { | 
|  | OUTC(pad); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* If we have a sign that hasn't been emitted, now's the | 
|  | * time.... | 
|  | */ | 
|  | if (sign != 0) { | 
|  | OUTC(sign); | 
|  | } | 
|  |  | 
|  | if (IS_ENABLED(CONFIG_CBPRINTF_FP_SUPPORT) && conv->pad_fp) { | 
|  | const char *cp = bps; | 
|  |  | 
|  | if (conv->specifier_a) { | 
|  | /* Only padding is pre_exp */ | 
|  | while (*cp != 'p') { | 
|  | OUTC(*cp); | 
|  | ++cp; | 
|  | } | 
|  | } else { | 
|  | while (isdigit((unsigned char)*cp) != 0) { | 
|  | OUTC(*cp); | 
|  | ++cp; | 
|  | } | 
|  |  | 
|  | pad_len = conv->pad0_value; | 
|  | if (!conv->pad_postdp) { | 
|  | while (pad_len-- > 0) { | 
|  | OUTC('0'); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (*cp == '.') { | 
|  | OUTC(*cp); | 
|  | ++cp; | 
|  | /* Remaining padding is | 
|  | * post-dp. | 
|  | */ | 
|  | while (pad_len-- > 0) { | 
|  | OUTC('0'); | 
|  | } | 
|  | } | 
|  | while (isdigit((unsigned char)*cp) != 0) { | 
|  | OUTC(*cp); | 
|  | ++cp; | 
|  | } | 
|  | } | 
|  |  | 
|  | pad_len = conv->pad0_pre_exp; | 
|  | while (pad_len-- > 0) { | 
|  | OUTC('0'); | 
|  | } | 
|  |  | 
|  | OUTS(cp, bpe); | 
|  | } else { | 
|  | if ((conv->altform_0c | conv->altform_0) != 0) { | 
|  | OUTC('0'); | 
|  | } | 
|  |  | 
|  | if (conv->altform_0c) { | 
|  | OUTC(conv->specifier); | 
|  | } | 
|  |  | 
|  | pad_len = conv->pad0_value; | 
|  | while (pad_len-- > 0) { | 
|  | OUTC('0'); | 
|  | } | 
|  |  | 
|  | OUTS(bps, bpe); | 
|  | } | 
|  |  | 
|  | /* Finish left justification */ | 
|  | while (width > 0) { | 
|  | OUTC(' '); | 
|  | --width; | 
|  | } | 
|  | } | 
|  |  | 
|  | return count; | 
|  | #undef OUTS | 
|  | #undef OUTC | 
|  | } |