Skip to content

Commit be503db

Browse files
committed
vsprintf: simplify number handling
Instead of dealing with all the different special types (size_t, unsigned char, ptrdiff_t..) just deal with the size of the integer type and the sign. This avoids a lot of unnecessary case statements, and the games we play with the value of the 'SIGN' flags value Signed-off-by: Linus Torvalds <[email protected]>
1 parent 4bbf902 commit be503db

File tree

1 file changed

+42
-102
lines changed

1 file changed

+42
-102
lines changed

lib/vsprintf.c

Lines changed: 42 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -407,40 +407,34 @@ int num_to_str(char *buf, int size, unsigned long long num, unsigned int width)
407407
return len + width;
408408
}
409409

410-
#define SIGN 1 /* unsigned/signed, must be 1 */
410+
#define SIGN 1 /* unsigned/signed */
411411
#define LEFT 2 /* left justified */
412412
#define PLUS 4 /* show plus */
413413
#define SPACE 8 /* space if plus */
414414
#define ZEROPAD 16 /* pad with zero, must be 16 == '0' - ' ' */
415415
#define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
416416
#define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
417417

418-
static_assert(SIGN == 1);
419418
static_assert(ZEROPAD == ('0' - ' '));
420419
static_assert(SMALL == ('a' ^ 'A'));
421420

422421
enum format_type {
423422
FORMAT_TYPE_NONE, /* Just a string part */
423+
FORMAT_TYPE_1BYTE = 1, /* char/short/int are their own sizes */
424+
FORMAT_TYPE_2BYTE = 2,
425+
FORMAT_TYPE_8BYTE = 3,
426+
FORMAT_TYPE_4BYTE = 4,
424427
FORMAT_TYPE_WIDTH,
425428
FORMAT_TYPE_PRECISION,
426429
FORMAT_TYPE_CHAR,
427430
FORMAT_TYPE_STR,
428431
FORMAT_TYPE_PTR,
429432
FORMAT_TYPE_PERCENT_CHAR,
430433
FORMAT_TYPE_INVALID,
431-
FORMAT_TYPE_LONG_LONG,
432-
FORMAT_TYPE_ULONG,
433-
FORMAT_TYPE_LONG,
434-
FORMAT_TYPE_UBYTE,
435-
FORMAT_TYPE_BYTE,
436-
FORMAT_TYPE_USHORT,
437-
FORMAT_TYPE_SHORT,
438-
FORMAT_TYPE_UINT,
439-
FORMAT_TYPE_INT,
440-
FORMAT_TYPE_SIZE_T,
441-
FORMAT_TYPE_PTRDIFF
442434
};
443435

436+
#define FORMAT_TYPE_SIZE(type) (sizeof(type) <= 4 ? sizeof(type) : FORMAT_TYPE_8BYTE)
437+
444438
struct printf_spec {
445439
unsigned int type:8; /* format_type enum */
446440
signed int field_width:24; /* width of output field */
@@ -2707,23 +2701,19 @@ int format_decode(const char *fmt, struct printf_spec *spec)
27072701
}
27082702

27092703
if (qualifier == 'L')
2710-
spec->type = FORMAT_TYPE_LONG_LONG;
2704+
spec->type = FORMAT_TYPE_SIZE(long long);
27112705
else if (qualifier == 'l') {
2712-
BUILD_BUG_ON(FORMAT_TYPE_ULONG + SIGN != FORMAT_TYPE_LONG);
2713-
spec->type = FORMAT_TYPE_ULONG + (spec->flags & SIGN);
2706+
spec->type = FORMAT_TYPE_SIZE(long);
27142707
} else if (qualifier == 'z') {
2715-
spec->type = FORMAT_TYPE_SIZE_T;
2708+
spec->type = FORMAT_TYPE_SIZE(size_t);
27162709
} else if (qualifier == 't') {
2717-
spec->type = FORMAT_TYPE_PTRDIFF;
2710+
spec->type = FORMAT_TYPE_SIZE(ptrdiff_t);
27182711
} else if (qualifier == 'H') {
2719-
BUILD_BUG_ON(FORMAT_TYPE_UBYTE + SIGN != FORMAT_TYPE_BYTE);
2720-
spec->type = FORMAT_TYPE_UBYTE + (spec->flags & SIGN);
2712+
spec->type = FORMAT_TYPE_SIZE(char);
27212713
} else if (qualifier == 'h') {
2722-
BUILD_BUG_ON(FORMAT_TYPE_USHORT + SIGN != FORMAT_TYPE_SHORT);
2723-
spec->type = FORMAT_TYPE_USHORT + (spec->flags & SIGN);
2714+
spec->type = FORMAT_TYPE_SIZE(short);
27242715
} else {
2725-
BUILD_BUG_ON(FORMAT_TYPE_UINT + SIGN != FORMAT_TYPE_INT);
2726-
spec->type = FORMAT_TYPE_UINT + (spec->flags & SIGN);
2716+
spec->type = FORMAT_TYPE_SIZE(int);
27272717
}
27282718

27292719
return ++fmt - start;
@@ -2747,6 +2737,22 @@ set_precision(struct printf_spec *spec, int prec)
27472737
}
27482738
}
27492739

2740+
/*
2741+
* Turn a 1/2/4-byte value into a 64-bit one for printing: truncate
2742+
* as necessary and deal with signedness.
2743+
*
2744+
* The 'spec.type' is the size of the value in bytes.
2745+
*/
2746+
static unsigned long long convert_num_spec(unsigned int val, struct printf_spec spec)
2747+
{
2748+
unsigned int shift = 32 - spec.type*8;
2749+
2750+
val <<= shift;
2751+
if (!(spec.flags & SIGN))
2752+
return val >> shift;
2753+
return (int)val >> shift;
2754+
}
2755+
27502756
/**
27512757
* vsnprintf - Format a string and place it in a buffer
27522758
* @buf: The buffer to place the result into
@@ -2873,43 +2879,10 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
28732879
goto out;
28742880

28752881
default:
2876-
switch (spec.type) {
2877-
case FORMAT_TYPE_LONG_LONG:
2882+
if (spec.type == FORMAT_TYPE_8BYTE)
28782883
num = va_arg(args, long long);
2879-
break;
2880-
case FORMAT_TYPE_ULONG:
2881-
num = va_arg(args, unsigned long);
2882-
break;
2883-
case FORMAT_TYPE_LONG:
2884-
num = va_arg(args, long);
2885-
break;
2886-
case FORMAT_TYPE_SIZE_T:
2887-
if (spec.flags & SIGN)
2888-
num = va_arg(args, ssize_t);
2889-
else
2890-
num = va_arg(args, size_t);
2891-
break;
2892-
case FORMAT_TYPE_PTRDIFF:
2893-
num = va_arg(args, ptrdiff_t);
2894-
break;
2895-
case FORMAT_TYPE_UBYTE:
2896-
num = (unsigned char) va_arg(args, int);
2897-
break;
2898-
case FORMAT_TYPE_BYTE:
2899-
num = (signed char) va_arg(args, int);
2900-
break;
2901-
case FORMAT_TYPE_USHORT:
2902-
num = (unsigned short) va_arg(args, int);
2903-
break;
2904-
case FORMAT_TYPE_SHORT:
2905-
num = (short) va_arg(args, int);
2906-
break;
2907-
case FORMAT_TYPE_INT:
2908-
num = (int) va_arg(args, int);
2909-
break;
2910-
default:
2911-
num = va_arg(args, unsigned int);
2912-
}
2884+
else
2885+
num = convert_num_spec(va_arg(args, int), spec);
29132886

29142887
str = number(str, end, num, spec);
29152888
}
@@ -3183,26 +3156,13 @@ int vbin_printf(u32 *bin_buf, size_t size, const char *fmt, va_list args)
31833156

31843157
default:
31853158
switch (spec.type) {
3186-
3187-
case FORMAT_TYPE_LONG_LONG:
3159+
case FORMAT_TYPE_8BYTE:
31883160
save_arg(long long);
31893161
break;
3190-
case FORMAT_TYPE_ULONG:
3191-
case FORMAT_TYPE_LONG:
3192-
save_arg(unsigned long);
3193-
break;
3194-
case FORMAT_TYPE_SIZE_T:
3195-
save_arg(size_t);
3196-
break;
3197-
case FORMAT_TYPE_PTRDIFF:
3198-
save_arg(ptrdiff_t);
3199-
break;
3200-
case FORMAT_TYPE_UBYTE:
3201-
case FORMAT_TYPE_BYTE:
3162+
case FORMAT_TYPE_1BYTE:
32023163
save_arg(char);
32033164
break;
3204-
case FORMAT_TYPE_USHORT:
3205-
case FORMAT_TYPE_SHORT:
3165+
case FORMAT_TYPE_2BYTE:
32063166
save_arg(short);
32073167
break;
32083168
default:
@@ -3375,37 +3335,17 @@ int bstr_printf(char *buf, size_t size, const char *fmt, const u32 *bin_buf)
33753335
unsigned long long num;
33763336

33773337
switch (spec.type) {
3378-
3379-
case FORMAT_TYPE_LONG_LONG:
3338+
case FORMAT_TYPE_8BYTE:
33803339
num = get_arg(long long);
33813340
break;
3382-
case FORMAT_TYPE_ULONG:
3383-
case FORMAT_TYPE_LONG:
3384-
num = get_arg(unsigned long);
3385-
break;
3386-
case FORMAT_TYPE_SIZE_T:
3387-
num = get_arg(size_t);
3388-
break;
3389-
case FORMAT_TYPE_PTRDIFF:
3390-
num = get_arg(ptrdiff_t);
3391-
break;
3392-
case FORMAT_TYPE_UBYTE:
3393-
num = get_arg(unsigned char);
3394-
break;
3395-
case FORMAT_TYPE_BYTE:
3396-
num = get_arg(signed char);
3397-
break;
3398-
case FORMAT_TYPE_USHORT:
3399-
num = get_arg(unsigned short);
3400-
break;
3401-
case FORMAT_TYPE_SHORT:
3402-
num = get_arg(short);
3341+
case FORMAT_TYPE_2BYTE:
3342+
num = convert_num_spec(get_arg(short), spec);
34033343
break;
3404-
case FORMAT_TYPE_UINT:
3405-
num = get_arg(unsigned int);
3344+
case FORMAT_TYPE_1BYTE:
3345+
num = convert_num_spec(get_arg(char), spec);
34063346
break;
34073347
default:
3408-
num = get_arg(int);
3348+
num = convert_num_spec(get_arg(int), spec);
34093349
}
34103350

34113351
str = number(str, end, num, spec);

0 commit comments

Comments
 (0)