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