diff options
author | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
---|---|---|
committer | Igor Sysoev <igor@sysoev.ru> | 2017-01-17 20:00:00 +0300 |
commit | 16cbf3c076a0aca6d47adaf3f719493674cf2363 (patch) | |
tree | e6530480020f62a2bdbf249988ec3e2a751d3927 /src/nxt_sprintf.c | |
download | unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.gz unit-16cbf3c076a0aca6d47adaf3f719493674cf2363.tar.bz2 |
Initial version.
Diffstat (limited to '')
-rw-r--r-- | src/nxt_sprintf.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/src/nxt_sprintf.c b/src/nxt_sprintf.c new file mode 100644 index 00000000..837d2635 --- /dev/null +++ b/src/nxt_sprintf.c @@ -0,0 +1,716 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include <nxt_main.h> +#include <math.h> +#include <float.h> + + +/* + * Supported formats: + * + * %[0][width][x][X]O nxt_off_t + * %[0][width]T nxt_time_t + * %[0][width][u][x|X]z ssize_t/size_t + * %[0][width][u][x|X]d int/u_int + * %[0][width][u][x|X]l long + * %[0][width|m][u][x|X]i nxt_int_t/nxt_uint_t + * %[0][width][u][x|X]D int32_t/uint32_t + * %[0][width][u][x|X]L int64_t/uint64_t + * %[0][width|m][u][x|X]A nxt_atomic_int_t/nxt_atomic_uint_t + * %[0][width][.width]f double, max valid number fits to %18.15f + * + * %FD nxt_fd_t, int / HANDLE + * %d nxt_socket_t, int + * + * %PI nxt_pid_t, process id + * %PT nxt_tid_t, thread id + * %PF nxt_fid_t, fiber id + * %PH pthread_t handle returned by pthread_self() + * + * %s null-terminated string + * %*s length and string + * %FN nxt_file_name_t * + * + * %M nxt_msec_t + * %N nxt_nsec_t + * %r rlim_t + * %p void * + * %b nxt_bool_t + * %E nxt_err_t + * %V nxt_str_t * + * %Z '\0' + * %n '\n' + * %c char + * %% % + * + * Reserved: + * %t ptrdiff_t + * %S null-terminated wchar string + * %C wchar + * %[0][width][u][x|X]Q int182_t/uint128_t + */ + + +u_char * nxt_cdecl +nxt_sprintf(u_char *buf, u_char *end, const char *fmt, ...) +{ + u_char *p; + va_list args; + + va_start(args, fmt); + p = nxt_vsprintf(buf, end, fmt, args); + va_end(args); + + return p; +} + + +/* + * nxt_sprintf_t is used: + * to pass several parameters of nxt_integer() via single pointer + * and to store little used variables of nxt_vsprintf(). + */ + +typedef struct { + u_char *end; + const u_char *hex; + uint32_t width; + int32_t frac_width; + uint8_t max_width; + u_char padding; +} nxt_sprintf_t; + + +static u_char *nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64); +static u_char *nxt_number(nxt_sprintf_t *spf, u_char *buf, double n); + + +/* A right way of "f == 0.0". */ +#define \ +nxt_double_is_zero(f) \ + (fabs(f) <= FLT_EPSILON) + + +u_char * +nxt_vsprintf(u_char *buf, u_char *end, const char *fmt, va_list args) +{ + u_char *p; + int d; + double f, i; + size_t len; + int64_t i64; + uint64_t ui64, frac; + nxt_str_t *v; + nxt_err_t err; + nxt_uint_t scale, n; + nxt_msec_t ms; + nxt_nsec_t ns; + nxt_bool_t sign; + nxt_sprintf_t spf; + nxt_file_name_t *fn; + + static const u_char hexadecimal[16] = "0123456789abcdef"; + static const u_char HEXADECIMAL[16] = "0123456789ABCDEF"; + static const u_char nan[] = "[nan]"; + static const u_char infinity[] = "[infinity]"; + + spf.end = end; + + while (*fmt != '\0' && buf < end) { + + /* + * "buf < end" means that we could copy at least one character: + * a plain character, "%%", "%c", or a minus without test. + */ + + if (*fmt != '%') { + *buf++ = *fmt++; + continue; + } + + fmt++; + + /* Test some often used text formats first. */ + + switch (*fmt) { + + case 'V': + fmt++; + v = va_arg(args, nxt_str_t *); + + if (nxt_fast_path(v != NULL)) { + len = v->len; + p = v->data; + goto copy; + } + + continue; + + case 's': + p = va_arg(args, u_char *); + + if (nxt_fast_path(p != NULL)) { + while (*p != '\0' && buf < end) { + *buf++ = *p++; + } + } + + fmt++; + continue; + + case '*': + len = va_arg(args, u_int); + + fmt++; + + if (*fmt == 's') { + fmt++; + p = va_arg(args, u_char *); + + if (nxt_fast_path(p != NULL)) { + goto copy; + } + } + + continue; + + default: + break; + } + + spf.hex = NULL; + spf.width = 0; + spf.frac_width = -1; + spf.max_width = 0; + spf.padding = (*fmt == '0') ? '0' : ' '; + + sign = 1; + + i64 = 0; + ui64 = 0; + + while (*fmt >= '0' && *fmt <= '9') { + spf.width = spf.width * 10 + (*fmt++ - '0'); + } + + + for ( ;; ) { + switch (*fmt) { + + case 'u': + sign = 0; + fmt++; + continue; + + case 'm': + spf.max_width = 1; + fmt++; + continue; + + case 'X': + spf.hex = HEXADECIMAL; + sign = 0; + fmt++; + continue; + + case 'x': + spf.hex = hexadecimal; + sign = 0; + fmt++; + continue; + + case '.': + fmt++; + spf.frac_width = 0; + + while (*fmt >= '0' && *fmt <= '9') { + spf.frac_width = spf.frac_width * 10 + *fmt++ - '0'; + } + + break; + + default: + break; + } + + break; + } + + + switch (*fmt) { + + case 'E': + err = va_arg(args, nxt_err_t); + + *buf++ = '('; + spf.hex = NULL; + spf.width = 0; + buf = nxt_integer(&spf, buf, err); + + if (buf < end - 1) { + *buf++ = ':'; + *buf++ = ' '; + } + + buf = nxt_strerror(err, buf, end - buf); + + if (buf < end) { + *buf++ = ')'; + } + + fmt++; + continue; + + case 'O': + i64 = (int64_t) va_arg(args, nxt_off_t); + sign = 1; + goto number; + + case 'T': + i64 = (int64_t) va_arg(args, nxt_time_t); + sign = 1; + goto number; + + case 'M': + ms = (nxt_msec_t) va_arg(args, nxt_msec_t); + if ((nxt_msec_int_t) ms == -1 && spf.hex == NULL) { + i64 = -1; + sign = 1; + } else { + ui64 = (uint64_t) ms; + sign = 0; + } + goto number; + + case 'N': + ns = (nxt_nsec_t) va_arg(args, nxt_nsec_t); + if ((nxt_nsec_int_t) ns == -1) { + i64 = -1; + sign = 1; + } else { + ui64 = (uint64_t) ns; + sign = 0; + } + goto number; + + case 'z': + if (sign) { + i64 = (int64_t) va_arg(args, ssize_t); + } else { + ui64 = (uint64_t) va_arg(args, size_t); + } + goto number; + + case 'i': + if (sign) { + i64 = (int64_t) va_arg(args, nxt_int_t); + } else { + ui64 = (uint64_t) va_arg(args, nxt_uint_t); + } + + if (spf.max_width != 0) { + spf.width = NXT_INT_T_LEN; + } + + goto number; + + case 'd': + if (sign) { + i64 = (int64_t) va_arg(args, int); + } else { + ui64 = (uint64_t) va_arg(args, u_int); + } + goto number; + + case 'l': + if (sign) { + i64 = (int64_t) va_arg(args, long); + } else { + ui64 = (uint64_t) va_arg(args, u_long); + } + goto number; + + case 'D': + if (sign) { + i64 = (int64_t) va_arg(args, int32_t); + } else { + ui64 = (uint64_t) va_arg(args, uint32_t); + } + goto number; + + case 'L': + if (sign) { + i64 = va_arg(args, int64_t); + } else { + ui64 = va_arg(args, uint64_t); + } + goto number; + + case 'A': + if (sign) { + i64 = (int64_t) va_arg(args, nxt_atomic_int_t); + } else { + ui64 = (uint64_t) va_arg(args, nxt_atomic_uint_t); + } + + if (spf.max_width != 0) { + spf.width = NXT_ATOMIC_T_LEN; + } + + goto number; + + case 'b': + ui64 = (uint64_t) va_arg(args, nxt_bool_t); + sign = 0; + goto number; + + case 'f': + fmt++; + + f = va_arg(args, double); + + if (f < 0) { + *buf++ = '-'; + f = -f; + } + + if (nxt_slow_path(isnan(f))) { + p = (u_char *) nan; + len = sizeof(nan) - 1; + + goto copy; + + } else if (nxt_slow_path(isinf(f))) { + p = (u_char *) infinity; + len = sizeof(infinity) - 1; + + goto copy; + } + + (void) modf(f, &i); + frac = 0; + + if (spf.frac_width > 0) { + + scale = 1; + for (n = spf.frac_width; n != 0; n--) { + scale *= 10; + } + + frac = (uint64_t) ((f - i) * scale + 0.5); + + if (frac == scale) { + i += 1; + frac = 0; + } + } + + buf = nxt_number(&spf, buf, i); + + if (spf.frac_width > 0) { + + if (buf < end) { + *buf++ = '.'; + + spf.hex = NULL; + spf.padding = '0'; + spf.width = spf.frac_width; + buf = nxt_integer(&spf, buf, frac); + } + + } else if (spf.frac_width < 0) { + f = modf(f, &i); + + if (!nxt_double_is_zero(f) && buf < end) { + *buf++ = '.'; + + while (!nxt_double_is_zero(f) && buf < end) { + f *= 10; + f = modf(f, &i); + *buf++ = (u_char) i + '0'; + } + } + } + + continue; + + case 'r': + i64 = (int64_t) va_arg(args, rlim_t); + sign = 1; + break; + + case 'p': + ui64 = (uintptr_t) va_arg(args, void *); + sign = 0; + spf.hex = HEXADECIMAL; + /* + * spf.width = NXT_PTR_SIZE * 2; + * spf.padding = '0'; + */ + goto number; + + case 'c': + d = va_arg(args, int); + *buf++ = (u_char) (d & 0xff); + fmt++; + + continue; + + case 'F': + fmt++; + + switch (*fmt) { + + case 'D': + i64 = (int64_t) va_arg(args, nxt_fd_t); + sign = 1; + + goto number; + + case 'N': + fn = va_arg(args, nxt_file_name_t *); + p = fn; + + while (*p != '\0' && buf < end) { + *buf++ = *p++; + } + + fmt++; + continue; + + default: + continue; + } + + case 'P': + fmt++; + + switch (*fmt) { + + case 'I': + i64 = (int64_t) va_arg(args, nxt_pid_t); + sign = 1; + goto number; + + case 'T': + ui64 = (uint64_t) va_arg(args, nxt_tid_t); + sign = 0; + goto number; + + case 'F': + ui64 = (uint64_t) va_arg(args, nxt_fid_t); + sign = 0; + goto number; + + case 'H': + ui64 = (uint64_t) (uintptr_t) va_arg(args, pthread_t); + spf.hex = HEXADECIMAL; + sign = 0; + goto number; + + default: + continue; + } + + case 'Z': + *buf++ = '\0'; + fmt++; + continue; + + case 'n': + *buf++ = NXT_LF; + fmt++; + continue; + + case '%': + *buf++ = '%'; + fmt++; + continue; + + default: + *buf++ = *fmt++; + continue; + } + + number: + + if (sign) { + if (i64 < 0) { + *buf++ = '-'; + ui64 = (uint64_t) -i64; + + } else { + ui64 = (uint64_t) i64; + } + } + + buf = nxt_integer(&spf, buf, ui64); + + fmt++; + continue; + + copy: + + buf = nxt_cpymem(buf, p, nxt_min((size_t) (end - buf), len)); + continue; + } + + return buf; +} + + +static u_char * +nxt_integer(nxt_sprintf_t *spf, u_char *buf, uint64_t ui64) +{ + u_char *p, *end; + size_t len; + u_char temp[NXT_INT64_T_LEN]; + + p = temp + NXT_INT64_T_LEN; + + if (spf->hex == NULL) { + +#if (NXT_32BIT) + + for ( ;; ) { + u_char *start; + uint32_t ui32; + + /* + * 32-bit platforms usually lack hardware support of 64-bit + * division and remainder operations. For this reason C compiler + * adds calls to the runtime library functions which provides + * these operations. These functions usually have about hundred + * lines of code. + * + * For 32-bit numbers and some constant divisors GCC, Clang and + * other compilers can use inlined multiplications and shifts + * which are faster than division or remainder operations. + * For example, unsigned "ui32 / 10" is compiled to + * + * ((uint64_t) ui32 * 0xCCCCCCCD) >> 35 + * + * So a 64-bit number is split to parts by 10^9. The parts fit + * to 32 bits and are processed separately as 32-bit numbers. A + * number of 64-bit division/remainder operations is significantly + * decreased depending on the 64-bit number's value, it is + * 0 if the 64-bit value is less than 4294967296, + * 1 if the 64-bit value is greater than 4294967295 + * and less than 4294967296000000000, + * 2 otherwise. + */ + + if (ui64 <= 0xffffffff) { + ui32 = (uint32_t) ui64; + start = NULL; + + } else { + ui32 = (uint32_t) (ui64 % 1000000000); + start = p - 9; + } + + do { + *(--p) = (u_char) (ui32 % 10 + '0'); + ui32 /= 10; + } while (ui32 != 0); + + if (start == NULL) { + break; + } + + /* Add leading zeros of part. */ + + while (p > start) { + *(--p) = '0'; + } + + ui64 /= 1000000000; + } + +#else /* NXT_64BIT */ + + do { + *(--p) = (u_char) (ui64 % 10 + '0'); + ui64 /= 10; + } while (ui64 != 0); + +#endif + + } else { + + do { + *(--p) = spf->hex[ui64 & 0xf]; + ui64 >>= 4; + } while (ui64 != 0); + } + + /* Zero or space padding. */ + + if (spf->width != 0) { + + len = (temp + NXT_INT64_T_LEN) - p; + end = buf + (spf->width - len); + end = nxt_min(end, spf->end); + + while (buf < end) { + *buf++ = spf->padding; + } + } + + /* Number copying. */ + + len = (temp + NXT_INT64_T_LEN) - p; + end = buf + len; + end = nxt_min(end, spf->end); + + while (buf < end) { + *buf++ = *p++; + } + + return buf; +} + + +static u_char * +nxt_number(nxt_sprintf_t *spf, u_char *buf, double n) +{ + u_char *p, *end; + size_t len; + u_char temp[NXT_DOUBLE_LEN]; + + p = temp + NXT_DOUBLE_LEN; + + do { + *(--p) = (u_char) (fmod(n, 10) + '0'); + n = trunc(n / 10); + } while (!nxt_double_is_zero(n)); + + /* Zero or space padding. */ + + if (spf->width != 0) { + len = (temp + NXT_DOUBLE_LEN) - p; + end = buf + (spf->width - len); + end = nxt_min(end, spf->end); + + while (buf < end) { + *buf++ = spf->padding; + } + } + + /* Number copying. */ + + len = (temp + NXT_DOUBLE_LEN) - p; + + end = buf + len; + end = nxt_min(end, spf->end); + + while (buf < end) { + *buf++ = *p++; + } + + return buf; +} |