while (isdigit(**s))
i = i * 10 + *((*s)++) - '0'; return i;
}
/* * put_dec_full4 handles numbers in the range 0 <= r < 10000. * The multiplier 0xccd is round(2^15/10), and the approximation * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389.
*/ static void put_dec_full4(char *end, unsignedint r)
{ int i;
for (i = 0; i < 3; i++) { unsignedint q = (r * 0xccd) >> 15;
*--end = '0' + (r - q * 10);
r = q;
}
*--end = '0' + r;
}
/* put_dec is copied from lib/vsprintf.c with small modifications */
/* * Call put_dec_full4 on x % 10000, return x / 10000. * The approximation x/10000 == (x * 0x346DC5D7) >> 43 * holds for all x < 1,128,869,999. The largest value this * helper will ever be asked to convert is 1,125,520,955. * (second call in the put_dec code, assuming n is all-ones).
*/ static unsignedint put_dec_helper4(char *end, unsignedint x)
{ unsignedint q = (x * 0x346DC5D7ULL) >> 43;
put_dec_full4(end, x - q * 10000); return q;
}
/* Based on code by Douglas W. Jones found at * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour> * (with permission from the author). * Performs no 64-bit division and hence should be fast on 32-bit machines.
*/ static char *put_dec(char *end, unsignedlonglong n)
{ unsignedint d3, d2, d1, q, h; char *p = end;
/* strip off the extra 0's we printed */ while (p < end && *p == '0')
++p;
return p;
}
static char *number(char *end, unsignedlonglong num, int base, char locase)
{ /* * locase = 0 or 0x20. ORing digits or letters with 'locase' * produces same digits or (maybe lowercased) letters
*/
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */ staticconstchar digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
switch (base) { case10: if (num != 0)
end = put_dec(end, num); break; case8: for (; num != 0; num >>= 3)
*--end = '0' + (num & 07); break; case16: for (; num != 0; num >>= 4)
*--end = digits[num & 0xf] | locase; break; default:
unreachable();
}
return end;
}
#define ZEROPAD 1/* pad with zero */ #define SIGN 2/* unsigned/signed long */ #define PLUS 4/* show plus */ #define SPACE 8/* space if plus */ #define LEFT 16/* left justified */ #define SMALL 32/* Must be 32 == 0x20 */ #define SPECIAL 64/* 0x */ #define WIDE 128/* UTF-16 string */
static int get_flags(constchar **fmt)
{ int flags = 0;
for (len = 0; len < maxlen && *s16; len += clen) {
u16 c0 = *s16++;
/* First, get the length for a BMP character */
clen = 1 + (c0 >= 0x80) + (c0 >= 0x800); if (len + clen > maxlen) break; /* * If this is a high surrogate, and we're already at maxlen, we * can't include the character if it's a valid surrogate pair. * Avoid accessing one extra word just to check if it's valid * or not.
*/ if ((c0 & 0xfc00) == 0xd800) { if (len + clen == maxlen) break; if ((*s16 & 0xfc00) == 0xdc00) {
++s16;
++clen;
}
}
}
c0 = *(*s16)++; /* not a surrogate */ if ((c0 & 0xf800) != 0xd800) return c0; /* invalid: low surrogate instead of high */ if (c0 & 0x0400) return0xfffd;
c1 = **s16; /* invalid: missing low surrogate */ if ((c1 & 0xfc00) != 0xdc00) return0xfffd; /* valid surrogate pair */
++(*s16); return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
}
#define PUTC(c) \ do { \ if (pos < size) \
buf[pos] = (c); \
++pos; \
} while (0);
int vsnprintf(char *buf, size_t size, constchar *fmt, va_list ap)
{ /* The maximum space required is to print a 64-bit number in octal */ char tmp[(sizeof(unsignedlonglong) * 8 + 2) / 3]; char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; longlong num; int base; constchar *s;
size_t len, pos; char sign;
int flags; /* flags to number() */
int field_width; /* width of output field */ int precision; /* min. # of digits for integers; max
number of chars for from string */ int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */
va_list args;
/* * We want to pass our input va_list to helper functions by reference, * but there's an annoying edge case. If va_list was originally passed * to us by value, we could just pass &ap down to the helpers. This is * the case on, for example, X86_32. * However, on X86_64 (and possibly others), va_list is actually a * size-1 array containing a structure. Our function parameter ap has * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], * which is what will be expected by a function taking a va_list * * parameter. * One standard way to solve this mess is by creating a copy in a local * variable of type va_list and then passing a pointer to that local * copy instead, which is what we do here.
*/
va_copy(args, ap);
for (pos = 0; *fmt; ++fmt) { if (*fmt != '%' || *++fmt == '%') {
PUTC(*fmt); continue;
}
/* process flags */
flags = get_flags(&fmt);
/* get field width */
field_width = get_int(&fmt, &args); if (field_width < 0) {
field_width = -field_width;
flags |= LEFT;
}
if (flags & LEFT)
flags &= ~ZEROPAD;
/* get the precision */
precision = -1; if (*fmt == '.') {
++fmt;
precision = get_int(&fmt, &args); if (precision >= 0)
flags &= ~ZEROPAD;
}
/* get the conversion qualifier */
qualifier = -1; if (*fmt == 'h' || *fmt == 'l') {
qualifier = *fmt;
++fmt; if (qualifier == *fmt) {
qualifier -= 'a'-'A';
++fmt;
}
}
default: /* * Bail out if the conversion specifier is invalid. * There's probably a typo in the format string and the * remaining specifiers are unlikely to match up with * the arguments.
*/ goto fail;
} if (*fmt == 'p') {
num = (unsignedlong)va_arg(args, void *);
} else {
num = get_number(flags & SIGN, qualifier, &args);
}
sign = get_sign(&num, flags); if (sign)
--field_width;
s = number(tmp_end, num, base, flags & SMALL);
len = tmp_end - s; /* default precision is 1 */ if (precision < 0)
precision = 1; /* precision is minimum number of digits to print */ if (precision < len)
precision = len; if (flags & SPECIAL) { /* * For octal, a leading 0 is printed only if necessary, * i.e. if it's not already there because of the * precision.
*/ if (base == 8 && precision == len)
++precision; /* * For hexadecimal, the leading 0x is skipped if the * output is empty, i.e. both the number and the * precision are 0.
*/ if (base == 16 && precision > 0)
field_width -= 2; else
flags &= ~SPECIAL;
} /* * For zero padding, increase the precision to fill the field * width.
*/ if ((flags & ZEROPAD) && field_width > precision)
precision = field_width;
output: /* Calculate the padding necessary */
field_width -= precision; /* Leading padding with ' ' */ if (!(flags & LEFT)) while (field_width-- > 0)
PUTC(' '); /* sign */ if (sign)
PUTC(sign); /* 0x/0X for hexadecimal */ if (flags & SPECIAL) {
PUTC('0');
PUTC( 'X' | (flags & SMALL));
} /* Zero padding and excess precision */ while (precision-- > len)
PUTC('0'); /* Actual output */ if (flags & WIDE) { const u16 *ws = (const u16 *)s;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.