]> git.saurik.com Git - redis.git/blame - deps/jemalloc/src/util.c
Query the archive to provide a complete KEYS list.
[redis.git] / deps / jemalloc / src / util.c
CommitLineData
4934f93d 1#define assert(e) do { \
2 if (config_debug && !(e)) { \
3 malloc_write("<jemalloc>: Failed assertion\n"); \
4 abort(); \
5 } \
6} while (0)
7
8#define not_reached() do { \
9 if (config_debug) { \
10 malloc_write("<jemalloc>: Unreachable code reached\n"); \
11 abort(); \
12 } \
13} while (0)
14
15#define not_implemented() do { \
16 if (config_debug) { \
17 malloc_write("<jemalloc>: Not implemented\n"); \
18 abort(); \
19 } \
20} while (0)
21
22#define JEMALLOC_UTIL_C_
23#include "jemalloc/internal/jemalloc_internal.h"
24
25/******************************************************************************/
26/* Function prototypes for non-inline static functions. */
27
28static void wrtmessage(void *cbopaque, const char *s);
29#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
30static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
31 size_t *slen_p);
32#define D2S_BUFSIZE (1 + U2S_BUFSIZE)
33static char *d2s(intmax_t x, char sign, char *s, size_t *slen_p);
34#define O2S_BUFSIZE (1 + U2S_BUFSIZE)
35static char *o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
36#define X2S_BUFSIZE (2 + U2S_BUFSIZE)
37static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
38 size_t *slen_p);
39
40/******************************************************************************/
41
42/* malloc_message() setup. */
43static void
44wrtmessage(void *cbopaque, const char *s)
45{
46
47#ifdef SYS_write
48 /*
49 * Use syscall(2) rather than write(2) when possible in order to avoid
50 * the possibility of memory allocation within libc. This is necessary
51 * on FreeBSD; most operating systems do not have this problem though.
52 */
53 UNUSED int result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));
54#else
55 UNUSED int result = write(STDERR_FILENO, s, strlen(s));
56#endif
57}
58
59JEMALLOC_EXPORT void (*je_malloc_message)(void *, const char *s);
60
61/*
62 * Wrapper around malloc_message() that avoids the need for
63 * je_malloc_message(...) throughout the code.
64 */
65void
66malloc_write(const char *s)
67{
68
69 if (je_malloc_message != NULL)
70 je_malloc_message(NULL, s);
71 else
72 wrtmessage(NULL, s);
73}
74
75/*
76 * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
77 * provide a wrapper.
78 */
79int
80buferror(char *buf, size_t buflen)
81{
82
83#ifdef _WIN32
84 FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
85 (LPSTR)buf, buflen, NULL);
86 return (0);
87#elif defined(_GNU_SOURCE)
88 char *b = strerror_r(errno, buf, buflen);
89 if (b != buf) {
90 strncpy(buf, b, buflen);
91 buf[buflen-1] = '\0';
92 }
93 return (0);
94#else
95 return (strerror_r(errno, buf, buflen));
96#endif
97}
98
99uintmax_t
100malloc_strtoumax(const char *nptr, char **endptr, int base)
101{
102 uintmax_t ret, digit;
103 int b;
104 bool neg;
105 const char *p, *ns;
106
107 if (base < 0 || base == 1 || base > 36) {
108 set_errno(EINVAL);
109 return (UINTMAX_MAX);
110 }
111 b = base;
112
113 /* Swallow leading whitespace and get sign, if any. */
114 neg = false;
115 p = nptr;
116 while (true) {
117 switch (*p) {
118 case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
119 p++;
120 break;
121 case '-':
122 neg = true;
123 /* Fall through. */
124 case '+':
125 p++;
126 /* Fall through. */
127 default:
128 goto label_prefix;
129 }
130 }
131
132 /* Get prefix, if any. */
133 label_prefix:
134 /*
135 * Note where the first non-whitespace/sign character is so that it is
136 * possible to tell whether any digits are consumed (e.g., " 0" vs.
137 * " -x").
138 */
139 ns = p;
140 if (*p == '0') {
141 switch (p[1]) {
142 case '0': case '1': case '2': case '3': case '4': case '5':
143 case '6': case '7':
144 if (b == 0)
145 b = 8;
146 if (b == 8)
147 p++;
148 break;
149 case 'x':
150 switch (p[2]) {
151 case '0': case '1': case '2': case '3': case '4':
152 case '5': case '6': case '7': case '8': case '9':
153 case 'A': case 'B': case 'C': case 'D': case 'E':
154 case 'F':
155 case 'a': case 'b': case 'c': case 'd': case 'e':
156 case 'f':
157 if (b == 0)
158 b = 16;
159 if (b == 16)
160 p += 2;
161 break;
162 default:
163 break;
164 }
165 break;
166 default:
167 break;
168 }
169 }
170 if (b == 0)
171 b = 10;
172
173 /* Convert. */
174 ret = 0;
175 while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
176 || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
177 || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
178 uintmax_t pret = ret;
179 ret *= b;
180 ret += digit;
181 if (ret < pret) {
182 /* Overflow. */
183 set_errno(ERANGE);
184 return (UINTMAX_MAX);
185 }
186 p++;
187 }
188 if (neg)
189 ret = -ret;
190
191 if (endptr != NULL) {
192 if (p == ns) {
193 /* No characters were converted. */
194 *endptr = (char *)nptr;
195 } else
196 *endptr = (char *)p;
197 }
198
199 return (ret);
200}
201
202static char *
203u2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)
204{
205 unsigned i;
206
207 i = U2S_BUFSIZE - 1;
208 s[i] = '\0';
209 switch (base) {
210 case 10:
211 do {
212 i--;
213 s[i] = "0123456789"[x % (uint64_t)10];
214 x /= (uint64_t)10;
215 } while (x > 0);
216 break;
217 case 16: {
218 const char *digits = (uppercase)
219 ? "0123456789ABCDEF"
220 : "0123456789abcdef";
221
222 do {
223 i--;
224 s[i] = digits[x & 0xf];
225 x >>= 4;
226 } while (x > 0);
227 break;
228 } default: {
229 const char *digits = (uppercase)
230 ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
231 : "0123456789abcdefghijklmnopqrstuvwxyz";
232
233 assert(base >= 2 && base <= 36);
234 do {
235 i--;
236 s[i] = digits[x % (uint64_t)base];
237 x /= (uint64_t)base;
238 } while (x > 0);
239 }}
240
241 *slen_p = U2S_BUFSIZE - 1 - i;
242 return (&s[i]);
243}
244
245static char *
246d2s(intmax_t x, char sign, char *s, size_t *slen_p)
247{
248 bool neg;
249
250 if ((neg = (x < 0)))
251 x = -x;
252 s = u2s(x, 10, false, s, slen_p);
253 if (neg)
254 sign = '-';
255 switch (sign) {
256 case '-':
257 if (neg == false)
258 break;
259 /* Fall through. */
260 case ' ':
261 case '+':
262 s--;
263 (*slen_p)++;
264 *s = sign;
265 break;
266 default: not_reached();
267 }
268 return (s);
269}
270
271static char *
272o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)
273{
274
275 s = u2s(x, 8, false, s, slen_p);
276 if (alt_form && *s != '0') {
277 s--;
278 (*slen_p)++;
279 *s = '0';
280 }
281 return (s);
282}
283
284static char *
285x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
286{
287
288 s = u2s(x, 16, uppercase, s, slen_p);
289 if (alt_form) {
290 s -= 2;
291 (*slen_p) += 2;
292 memcpy(s, uppercase ? "0X" : "0x", 2);
293 }
294 return (s);
295}
296
297int
298malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
299{
300 int ret;
301 size_t i;
302 const char *f;
303
304#define APPEND_C(c) do { \
305 if (i < size) \
306 str[i] = (c); \
307 i++; \
308} while (0)
309#define APPEND_S(s, slen) do { \
310 if (i < size) { \
311 size_t cpylen = (slen <= size - i) ? slen : size - i; \
312 memcpy(&str[i], s, cpylen); \
313 } \
314 i += slen; \
315} while (0)
316#define APPEND_PADDED_S(s, slen, width, left_justify) do { \
317 /* Left padding. */ \
318 size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ? \
319 (size_t)width - slen : 0); \
320 if (left_justify == false && pad_len != 0) { \
321 size_t j; \
322 for (j = 0; j < pad_len; j++) \
323 APPEND_C(' '); \
324 } \
325 /* Value. */ \
326 APPEND_S(s, slen); \
327 /* Right padding. */ \
328 if (left_justify && pad_len != 0) { \
329 size_t j; \
330 for (j = 0; j < pad_len; j++) \
331 APPEND_C(' '); \
332 } \
333} while (0)
334#define GET_ARG_NUMERIC(val, len) do { \
335 switch (len) { \
336 case '?': \
337 val = va_arg(ap, int); \
338 break; \
339 case '?' | 0x80: \
340 val = va_arg(ap, unsigned int); \
341 break; \
342 case 'l': \
343 val = va_arg(ap, long); \
344 break; \
345 case 'l' | 0x80: \
346 val = va_arg(ap, unsigned long); \
347 break; \
348 case 'q': \
349 val = va_arg(ap, long long); \
350 break; \
351 case 'q' | 0x80: \
352 val = va_arg(ap, unsigned long long); \
353 break; \
354 case 'j': \
355 val = va_arg(ap, intmax_t); \
356 break; \
357 case 't': \
358 val = va_arg(ap, ptrdiff_t); \
359 break; \
360 case 'z': \
361 val = va_arg(ap, ssize_t); \
362 break; \
363 case 'z' | 0x80: \
364 val = va_arg(ap, size_t); \
365 break; \
366 case 'p': /* Synthetic; used for %p. */ \
367 val = va_arg(ap, uintptr_t); \
368 break; \
369 default: not_reached(); \
370 } \
371} while (0)
372
373 i = 0;
374 f = format;
375 while (true) {
376 switch (*f) {
377 case '\0': goto label_out;
378 case '%': {
379 bool alt_form = false;
4934f93d 380 bool left_justify = false;
381 bool plus_space = false;
382 bool plus_plus = false;
383 int prec = -1;
384 int width = -1;
385 unsigned char len = '?';
386
387 f++;
388 if (*f == '%') {
389 /* %% */
390 APPEND_C(*f);
391 break;
392 }
393 /* Flags. */
394 while (true) {
395 switch (*f) {
396 case '#':
397 assert(alt_form == false);
398 alt_form = true;
399 break;
4934f93d 400 case '-':
401 assert(left_justify == false);
402 left_justify = true;
403 break;
404 case ' ':
405 assert(plus_space == false);
406 plus_space = true;
407 break;
408 case '+':
409 assert(plus_plus == false);
410 plus_plus = true;
411 break;
412 default: goto label_width;
413 }
414 f++;
415 }
416 /* Width. */
417 label_width:
418 switch (*f) {
419 case '*':
420 width = va_arg(ap, int);
421 f++;
422 break;
423 case '0': case '1': case '2': case '3': case '4':
424 case '5': case '6': case '7': case '8': case '9': {
425 uintmax_t uwidth;
426 set_errno(0);
427 uwidth = malloc_strtoumax(f, (char **)&f, 10);
428 assert(uwidth != UINTMAX_MAX || get_errno() !=
429 ERANGE);
430 width = (int)uwidth;
431 if (*f == '.') {
432 f++;
433 goto label_precision;
434 } else
435 goto label_length;
436 break;
437 } case '.':
438 f++;
439 goto label_precision;
440 default: goto label_length;
441 }
442 /* Precision. */
443 label_precision:
444 switch (*f) {
445 case '*':
446 prec = va_arg(ap, int);
447 f++;
448 break;
449 case '0': case '1': case '2': case '3': case '4':
450 case '5': case '6': case '7': case '8': case '9': {
451 uintmax_t uprec;
452 set_errno(0);
453 uprec = malloc_strtoumax(f, (char **)&f, 10);
454 assert(uprec != UINTMAX_MAX || get_errno() !=
455 ERANGE);
456 prec = (int)uprec;
457 break;
458 }
459 default: break;
460 }
461 /* Length. */
462 label_length:
463 switch (*f) {
464 case 'l':
465 f++;
466 if (*f == 'l') {
467 len = 'q';
468 f++;
469 } else
470 len = 'l';
471 break;
472 case 'j':
473 len = 'j';
474 f++;
475 break;
476 case 't':
477 len = 't';
478 f++;
479 break;
480 case 'z':
481 len = 'z';
482 f++;
483 break;
484 default: break;
485 }
486 /* Conversion specifier. */
487 switch (*f) {
488 char *s;
489 size_t slen;
490 case 'd': case 'i': {
491 intmax_t val JEMALLOC_CC_SILENCE_INIT(0);
492 char buf[D2S_BUFSIZE];
493
494 GET_ARG_NUMERIC(val, len);
495 s = d2s(val, (plus_plus ? '+' : (plus_space ?
496 ' ' : '-')), buf, &slen);
497 APPEND_PADDED_S(s, slen, width, left_justify);
498 f++;
499 break;
500 } case 'o': {
501 uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
502 char buf[O2S_BUFSIZE];
503
504 GET_ARG_NUMERIC(val, len | 0x80);
505 s = o2s(val, alt_form, buf, &slen);
506 APPEND_PADDED_S(s, slen, width, left_justify);
507 f++;
508 break;
509 } case 'u': {
510 uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
511 char buf[U2S_BUFSIZE];
512
513 GET_ARG_NUMERIC(val, len | 0x80);
514 s = u2s(val, 10, false, buf, &slen);
515 APPEND_PADDED_S(s, slen, width, left_justify);
516 f++;
517 break;
518 } case 'x': case 'X': {
519 uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
520 char buf[X2S_BUFSIZE];
521
522 GET_ARG_NUMERIC(val, len | 0x80);
523 s = x2s(val, alt_form, *f == 'X', buf, &slen);
524 APPEND_PADDED_S(s, slen, width, left_justify);
525 f++;
526 break;
527 } case 'c': {
528 unsigned char val;
529 char buf[2];
530
531 assert(len == '?' || len == 'l');
532 assert_not_implemented(len != 'l');
533 val = va_arg(ap, int);
534 buf[0] = val;
535 buf[1] = '\0';
536 APPEND_PADDED_S(buf, 1, width, left_justify);
537 f++;
538 break;
539 } case 's':
540 assert(len == '?' || len == 'l');
541 assert_not_implemented(len != 'l');
542 s = va_arg(ap, char *);
543 slen = (prec == -1) ? strlen(s) : prec;
544 APPEND_PADDED_S(s, slen, width, left_justify);
545 f++;
546 break;
547 case 'p': {
548 uintmax_t val;
549 char buf[X2S_BUFSIZE];
550
551 GET_ARG_NUMERIC(val, 'p');
552 s = x2s(val, true, false, buf, &slen);
553 APPEND_PADDED_S(s, slen, width, left_justify);
554 f++;
555 break;
556 }
557 default: not_implemented();
558 }
559 break;
560 } default: {
561 APPEND_C(*f);
562 f++;
563 break;
564 }}
565 }
566 label_out:
567 if (i < size)
568 str[i] = '\0';
569 else
570 str[size - 1] = '\0';
571 ret = i;
572
573#undef APPEND_C
574#undef APPEND_S
575#undef APPEND_PADDED_S
576#undef GET_ARG_NUMERIC
577 return (ret);
578}
579
580JEMALLOC_ATTR(format(printf, 3, 4))
581int
582malloc_snprintf(char *str, size_t size, const char *format, ...)
583{
584 int ret;
585 va_list ap;
586
587 va_start(ap, format);
588 ret = malloc_vsnprintf(str, size, format, ap);
589 va_end(ap);
590
591 return (ret);
592}
593
594void
595malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
596 const char *format, va_list ap)
597{
598 char buf[MALLOC_PRINTF_BUFSIZE];
599
600 if (write_cb == NULL) {
601 /*
602 * The caller did not provide an alternate write_cb callback
603 * function, so use the default one. malloc_write() is an
604 * inline function, so use malloc_message() directly here.
605 */
606 write_cb = (je_malloc_message != NULL) ? je_malloc_message :
607 wrtmessage;
608 cbopaque = NULL;
609 }
610
611 malloc_vsnprintf(buf, sizeof(buf), format, ap);
612 write_cb(cbopaque, buf);
613}
614
615/*
616 * Print to a callback function in such a way as to (hopefully) avoid memory
617 * allocation.
618 */
619JEMALLOC_ATTR(format(printf, 3, 4))
620void
621malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
622 const char *format, ...)
623{
624 va_list ap;
625
626 va_start(ap, format);
627 malloc_vcprintf(write_cb, cbopaque, format, ap);
628 va_end(ap);
629}
630
631/* Print to stderr in such a way as to avoid memory allocation. */
632JEMALLOC_ATTR(format(printf, 1, 2))
633void
634malloc_printf(const char *format, ...)
635{
636 va_list ap;
637
638 va_start(ap, format);
639 malloc_vcprintf(NULL, NULL, format, ap);
640 va_end(ap);
641}