From a05d3416f0fb72d9c1ee35d02030292c71175f47 Mon Sep 17 00:00:00 2001 From: Lephe Date: Thu, 19 Sep 2019 15:58:35 +0200 Subject: [PATCH] std: support integer size formats (hh, h, l, ll) --- include/gint/std/stdio.h | 18 ++++++++++-- src/std/stdio.c | 59 ++++++++++++++++++++++++++++------------ 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/include/gint/std/stdio.h b/include/gint/std/stdio.h index f9560aa..a341d82 100644 --- a/include/gint/std/stdio.h +++ b/include/gint/std/stdio.h @@ -9,9 +9,21 @@ #include /* Formatted printing functions - These functions implement most of printf()'s features, except: - * Large parameters (ll) - * Floating-point (%e, %E, %f, %F, %g, %G, %a, %A) */ + + These functions implement most of printf()'s features, including: + * Signed and unsigned integer formats (%d, %i, %o, %u, %x, %X) + * Character, string and pointer formats (%c, %s, %p) + * Format options (0, #, -, (space), length, precision) + * Parameter size (hh, h, l, ll) + * Limiting the size of the output and still returning the whole length + + They do not support: + * Floating-point (%e, %E, %f, %F, %g, %G, %a, %A) + * Exotic integer types (intmax_t) or features (thousands separators) + + A new fixed-point format %j has been added; it behaves like %d but includes + a decimal point. The number of decimal places is specified by the precision + field. */ /* Print to string from var args */ int sprintf(char *str, char const *format, ...); diff --git a/src/std/stdio.c b/src/std/stdio.c index d733073..18e2221 100644 --- a/src/std/stdio.c +++ b/src/std/stdio.c @@ -54,12 +54,12 @@ struct kprint_options /* How much significant characters of data, meaning varies */ uint16_t precision; - /* Size specifier, may be one of: - (b) 8-bit data (%o, %x) - (w) 16-bit data (%o, %x) - (l) 32-bit data (%o, %x) or long int type (%i, %d, %u) - (q) 64-bit data (%o, %x) or long long int type (%i, %d, %u) - (h) short int type (%i, %d, %u) */ + /* Size specifier for integers (%o, %x, %i, %d, %u), may be one of: + (0) char (8-bit) + (1) short (16-bit) + (2) int (32-bit) + (3) long (32-bit) + (4) long long (64-bit) */ uint8_t size; /* (#) Alternative form: base prefixes, decimal point */ @@ -146,8 +146,9 @@ GINLINE static void kprint_outn(int byte, size_t n) /* kprint_opt(): Parse option strings */ struct kprint_options kprint_opt(char const **options_ptr) { - /* No options enabled by default */ - struct kprint_options opt = { 0 }; + /* No options enabled by default, set the size to int */ + struct kprint_options opt = { .size = 2 }; + /* This function acts as a deterministic finite automaton */ enum { basic, /* Reading option characters */ @@ -159,8 +160,8 @@ struct kprint_options kprint_opt(char const **options_ptr) for(int c; (c = *options); options++) { - int c_lower = c | 0x20; - if(!c || (c_lower >= 'a' && c_lower < 'z')) break; + int c_low = c | 0x20; + if(c_low >= 'a' && c_low <= 'z' && c != 'h' && c != 'l') break; if(c == '.') { @@ -192,8 +193,8 @@ struct kprint_options kprint_opt(char const **options_ptr) } /* Data size */ - if(c == 'l') opt.size++; if(c == 'h') opt.size--; + if(c == 'l') opt.size++; if(c >= '1' && c <= '9') state = length, options--; } @@ -336,7 +337,7 @@ static void kformat_geometry(int spec, struct kprint_options *opt, Fills up the provided digit string from least significant to most significant digit, not adding zeros except if argument is zero. Returns the number of digits. No NUL terminator is added. */ -static int digits_10(char *str, uint n) +static int digits_10(char *str, uint64_t n) { int digits = 0; @@ -350,7 +351,7 @@ static int digits_10(char *str, uint n) } /* digits_16(): Generate digits in base 16 */ -static int digits_16(char *str, int uppercase, uint32_t n) +static int digits_16(char *str, int uppercase, uint64_t n) { char *hex = uppercase ? "0123456789ABCDEF" : "0123456789abcdef"; int digits = 0; @@ -364,7 +365,7 @@ static int digits_16(char *str, int uppercase, uint32_t n) } /* digits_8(): Generate digits in base 8 */ -static int digits_8(char *str, uint32_t n) +static int digits_8(char *str, uint64_t n) { int digits = 0; @@ -376,6 +377,28 @@ static int digits_8(char *str, uint32_t n) return digits; } +//--- +// Loading helpers +//--- + +static int64_t load_i(int size, va_list *args) +{ + /* All smaller types are promoeted to int so we can't read the + explicitly. They have been converted and sign-extended we don't need + to care what their size is, the result will remain the same */ + if(size == 3) return va_arg(*args, long); + if(size == 4) return va_arg(*args, long long); + return va_arg(*args, int); +} + +static uint64_t load_u(int size, va_list *args) +{ + /* Again, no need to care about small types */ + if(size == 3) return va_arg(*args, unsigned long); + if(size == 4) return va_arg(*args, unsigned long long); + return va_arg(*args, unsigned int); +} + //--- // Individual formatters //--- @@ -430,7 +453,7 @@ static void kformat_str(KFORMAT_ARGS) {pre} Forces a minimal number of digits, creating 0s (overrides '0') */ static void kformat_int(KFORMAT_ARGS) { - int n = va_arg(*args, int); + int64_t n = load_i(opt->size, args); /* Compute the sign and the absolute value */ struct geometry g = { @@ -468,9 +491,9 @@ static void kformat_int(KFORMAT_ARGS) {pre} Forces a minimal number of digits, creating 0s (overrides '0') */ static void kformat_uint(KFORMAT_ARGS) { - uint n = va_arg(*args, uint); + uint64_t n = load_u(opt->size, args); - char digits[32]; + char digits[48]; int pure = 0, total; int specl = spec | 0x20; @@ -525,7 +548,7 @@ static void kformat_ptr(KFORMAT_ARGS) {pre} Number of digits after the decimal dot */ static void kformat_fixed(KFORMAT_ARGS) { - int n = va_arg(*args, int); + int64_t n = load_i(opt->size, args); /* Compute the sign and the absolute value */ struct geometry g = {