std: support integer size formats (hh, h, l, ll)

This commit is contained in:
Lephe 2019-09-19 15:58:35 +02:00
parent fc7aab6eba
commit a05d3416f0
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
2 changed files with 56 additions and 21 deletions

View file

@ -9,9 +9,21 @@
#include <stdarg.h> #include <stdarg.h>
/* Formatted printing functions /* Formatted printing functions
These functions implement most of printf()'s features, except:
* Large parameters (ll) These functions implement most of printf()'s features, including:
* Floating-point (%e, %E, %f, %F, %g, %G, %a, %A) */ * 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 */ /* Print to string from var args */
int sprintf(char *str, char const *format, ...); int sprintf(char *str, char const *format, ...);

View file

@ -54,12 +54,12 @@ struct kprint_options
/* How much significant characters of data, meaning varies */ /* How much significant characters of data, meaning varies */
uint16_t precision; uint16_t precision;
/* Size specifier, may be one of: /* Size specifier for integers (%o, %x, %i, %d, %u), may be one of:
(b) 8-bit data (%o, %x) (0) char (8-bit)
(w) 16-bit data (%o, %x) (1) short (16-bit)
(l) 32-bit data (%o, %x) or long int type (%i, %d, %u) (2) int (32-bit)
(q) 64-bit data (%o, %x) or long long int type (%i, %d, %u) (3) long (32-bit)
(h) short int type (%i, %d, %u) */ (4) long long (64-bit) */
uint8_t size; uint8_t size;
/* (#) Alternative form: base prefixes, decimal point */ /* (#) 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 */ /* kprint_opt(): Parse option strings */
struct kprint_options kprint_opt(char const **options_ptr) struct kprint_options kprint_opt(char const **options_ptr)
{ {
/* No options enabled by default */ /* No options enabled by default, set the size to int */
struct kprint_options opt = { 0 }; struct kprint_options opt = { .size = 2 };
/* This function acts as a deterministic finite automaton */ /* This function acts as a deterministic finite automaton */
enum { enum {
basic, /* Reading option characters */ basic, /* Reading option characters */
@ -159,8 +160,8 @@ struct kprint_options kprint_opt(char const **options_ptr)
for(int c; (c = *options); options++) for(int c; (c = *options); options++)
{ {
int c_lower = c | 0x20; int c_low = c | 0x20;
if(!c || (c_lower >= 'a' && c_lower < 'z')) break; if(c_low >= 'a' && c_low <= 'z' && c != 'h' && c != 'l') break;
if(c == '.') if(c == '.')
{ {
@ -192,8 +193,8 @@ struct kprint_options kprint_opt(char const **options_ptr)
} }
/* Data size */ /* Data size */
if(c == 'l') opt.size++;
if(c == 'h') opt.size--; if(c == 'h') opt.size--;
if(c == 'l') opt.size++;
if(c >= '1' && c <= '9') state = length, options--; 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 Fills up the provided digit string from least significant to most
significant digit, not adding zeros except if argument is zero. Returns the significant digit, not adding zeros except if argument is zero. Returns the
number of digits. No NUL terminator is added. */ 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; int digits = 0;
@ -350,7 +351,7 @@ static int digits_10(char *str, uint n)
} }
/* digits_16(): Generate digits in base 16 */ /* 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"; char *hex = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
int digits = 0; 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 */ /* 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; int digits = 0;
@ -376,6 +377,28 @@ static int digits_8(char *str, uint32_t n)
return digits; 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 // Individual formatters
//--- //---
@ -430,7 +453,7 @@ static void kformat_str(KFORMAT_ARGS)
{pre} Forces a minimal number of digits, creating 0s (overrides '0') */ {pre} Forces a minimal number of digits, creating 0s (overrides '0') */
static void kformat_int(KFORMAT_ARGS) 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 */ /* Compute the sign and the absolute value */
struct geometry g = { struct geometry g = {
@ -468,9 +491,9 @@ static void kformat_int(KFORMAT_ARGS)
{pre} Forces a minimal number of digits, creating 0s (overrides '0') */ {pre} Forces a minimal number of digits, creating 0s (overrides '0') */
static void kformat_uint(KFORMAT_ARGS) 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 pure = 0, total;
int specl = spec | 0x20; int specl = spec | 0x20;
@ -525,7 +548,7 @@ static void kformat_ptr(KFORMAT_ARGS)
{pre} Number of digits after the decimal dot */ {pre} Number of digits after the decimal dot */
static void kformat_fixed(KFORMAT_ARGS) 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 */ /* Compute the sign and the absolute value */
struct geometry g = { struct geometry g = {