13447SN/A#define	assert(e) do {							\
23447SN/A	if (config_debug && !(e)) {					\
33447SN/A		malloc_write("<jemalloc>: Failed assertion\n");		\
43447SN/A		abort();						\
53447SN/A	}								\
63447SN/A} while (0)
73447SN/A
83447SN/A#define	not_reached() do {						\
93447SN/A	if (config_debug) {						\
103447SN/A		malloc_write("<jemalloc>: Unreachable code reached\n");	\
113447SN/A		abort();						\
123447SN/A	}								\
133447SN/A} while (0)
143447SN/A
153447SN/A#define	not_implemented() do {						\
163447SN/A	if (config_debug) {						\
173447SN/A		malloc_write("<jemalloc>: Not implemented\n");		\
183447SN/A		abort();						\
193447SN/A	}								\
203447SN/A} while (0)
213447SN/A
223447SN/A#define	JEMALLOC_UTIL_C_
233447SN/A#include "jemalloc/internal/jemalloc_internal.h"
243447SN/A
253447SN/A/******************************************************************************/
263447SN/A/* Function prototypes for non-inline static functions. */
273447SN/A
283447SN/Astatic void	wrtmessage(void *cbopaque, const char *s);
2911707Stpivovarova#define	U2S_BUFSIZE	((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
3011707Stpivovarovastatic char	*u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
3111707Stpivovarova    size_t *slen_p);
323447SN/A#define	D2S_BUFSIZE	(1 + U2S_BUFSIZE)
333447SN/Astatic char	*d2s(intmax_t x, char sign, char *s, size_t *slen_p);
3411707Stpivovarova#define	O2S_BUFSIZE	(1 + U2S_BUFSIZE)
3511707Stpivovarovastatic char	*o2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p);
363447SN/A#define	X2S_BUFSIZE	(2 + U2S_BUFSIZE)
373447SN/Astatic char	*x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
383447SN/A    size_t *slen_p);
393447SN/A
403447SN/A/******************************************************************************/
413447SN/A
423447SN/A/* malloc_message() setup. */
433447SN/Astatic void
443447SN/Awrtmessage(void *cbopaque, const char *s)
453447SN/A{
463447SN/A
473447SN/A#ifdef SYS_write
483447SN/A	/*
493447SN/A	 * Use syscall(2) rather than write(2) when possible in order to avoid
503447SN/A	 * the possibility of memory allocation within libc.  This is necessary
513447SN/A	 * on FreeBSD; most operating systems do not have this problem though.
523447SN/A	 */
533447SN/A	UNUSED int result = syscall(SYS_write, STDERR_FILENO, s, strlen(s));
543447SN/A#else
553447SN/A	UNUSED int result = write(STDERR_FILENO, s, strlen(s));
563447SN/A#endif
573447SN/A}
583447SN/A
593447SN/AJEMALLOC_EXPORT void	(*je_malloc_message)(void *, const char *s);
603447SN/A
613447SN/AJEMALLOC_ATTR(visibility("hidden"))
623447SN/Avoid
633447SN/Awrtmessage_1_0(const char *s1, const char *s2, const char *s3,
643447SN/A    const char *s4)
653447SN/A{
663447SN/A
673447SN/A	wrtmessage(NULL, s1);
683447SN/A	wrtmessage(NULL, s2);
693447SN/A	wrtmessage(NULL, s3);
703447SN/A	wrtmessage(NULL, s4);
713447SN/A}
723447SN/A
733447SN/Avoid	(*__malloc_message_1_0)(const char *s1, const char *s2, const char *s3,
743447SN/A    const char *s4) = wrtmessage_1_0;
753447SN/A__sym_compat(_malloc_message, __malloc_message_1_0, FBSD_1.0);
763447SN/A
773447SN/A/*
783447SN/A * Wrapper around malloc_message() that avoids the need for
793447SN/A * je_malloc_message(...) throughout the code.
803447SN/A */
813447SN/Avoid
823447SN/Amalloc_write(const char *s)
833447SN/A{
843447SN/A
853447SN/A	if (je_malloc_message != NULL)
863447SN/A		je_malloc_message(NULL, s);
873447SN/A	else
883447SN/A		wrtmessage(NULL, s);
893447SN/A}
903447SN/A
913447SN/A/*
923447SN/A * glibc provides a non-standard strerror_r() when _GNU_SOURCE is defined, so
933447SN/A * provide a wrapper.
943447SN/A */
953447SN/Aint
963447SN/Abuferror(char *buf, size_t buflen)
973447SN/A{
983447SN/A
993447SN/A#ifdef _WIN32
1003447SN/A	FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0,
1013447SN/A	    (LPSTR)buf, buflen, NULL);
1023447SN/A	return (0);
1033447SN/A#elif defined(_GNU_SOURCE)
1043447SN/A	char *b = strerror_r(errno, buf, buflen);
1053447SN/A	if (b != buf) {
1063447SN/A		strncpy(buf, b, buflen);
1073447SN/A		buf[buflen-1] = '\0';
1083447SN/A	}
1093447SN/A	return (0);
1103447SN/A#else
1113447SN/A	return (strerror_r(errno, buf, buflen));
1123447SN/A#endif
1133447SN/A}
1143447SN/A
1153447SN/Auintmax_t
1163447SN/Amalloc_strtoumax(const char *nptr, char **endptr, int base)
1173447SN/A{
1183447SN/A	uintmax_t ret, digit;
1193447SN/A	int b;
1203447SN/A	bool neg;
1213447SN/A	const char *p, *ns;
1223447SN/A
1233447SN/A	if (base < 0 || base == 1 || base > 36) {
1243447SN/A		set_errno(EINVAL);
1253447SN/A		return (UINTMAX_MAX);
1263447SN/A	}
1273447SN/A	b = base;
1283447SN/A
1293447SN/A	/* Swallow leading whitespace and get sign, if any. */
1303447SN/A	neg = false;
1313447SN/A	p = nptr;
1323447SN/A	while (true) {
1333447SN/A		switch (*p) {
1343447SN/A		case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
1353447SN/A			p++;
1363447SN/A			break;
1373447SN/A		case '-':
1383447SN/A			neg = true;
1393447SN/A			/* Fall through. */
1403447SN/A		case '+':
1413447SN/A			p++;
1423447SN/A			/* Fall through. */
1433447SN/A		default:
1443447SN/A			goto label_prefix;
1453447SN/A		}
1463447SN/A	}
1473447SN/A
1483447SN/A	/* Get prefix, if any. */
1493447SN/A	label_prefix:
1503447SN/A	/*
1513447SN/A	 * Note where the first non-whitespace/sign character is so that it is
1523447SN/A	 * possible to tell whether any digits are consumed (e.g., "  0" vs.
1533447SN/A	 * "  -x").
1543447SN/A	 */
1553447SN/A	ns = p;
1563447SN/A	if (*p == '0') {
1573447SN/A		switch (p[1]) {
1583447SN/A		case '0': case '1': case '2': case '3': case '4': case '5':
1593447SN/A		case '6': case '7':
1603447SN/A			if (b == 0)
1613447SN/A				b = 8;
1623447SN/A			if (b == 8)
1633447SN/A				p++;
1643447SN/A			break;
1653447SN/A		case 'x':
1663447SN/A			switch (p[2]) {
1673447SN/A			case '0': case '1': case '2': case '3': case '4':
1683447SN/A			case '5': case '6': case '7': case '8': case '9':
1693447SN/A			case 'A': case 'B': case 'C': case 'D': case 'E':
1703447SN/A			case 'F':
1713447SN/A			case 'a': case 'b': case 'c': case 'd': case 'e':
1723447SN/A			case 'f':
1733447SN/A				if (b == 0)
1743447SN/A					b = 16;
1753447SN/A				if (b == 16)
1763447SN/A					p += 2;
1773447SN/A				break;
1783447SN/A			default:
1793447SN/A				break;
1803447SN/A			}
1813447SN/A			break;
1823447SN/A		default:
1833447SN/A			break;
1843447SN/A		}
1853447SN/A	}
1863447SN/A	if (b == 0)
1873447SN/A		b = 10;
1883447SN/A
1893447SN/A	/* Convert. */
1903447SN/A	ret = 0;
1913447SN/A	while ((*p >= '0' && *p <= '9' && (digit = *p - '0') < b)
1923447SN/A	    || (*p >= 'A' && *p <= 'Z' && (digit = 10 + *p - 'A') < b)
1933447SN/A	    || (*p >= 'a' && *p <= 'z' && (digit = 10 + *p - 'a') < b)) {
1943447SN/A		uintmax_t pret = ret;
1953447SN/A		ret *= b;
1963447SN/A		ret += digit;
1973447SN/A		if (ret < pret) {
1983447SN/A			/* Overflow. */
1993447SN/A			set_errno(ERANGE);
2003447SN/A			return (UINTMAX_MAX);
2013447SN/A		}
2023447SN/A		p++;
2033447SN/A	}
2043447SN/A	if (neg)
2053447SN/A		ret = -ret;
2063447SN/A
2073447SN/A	if (endptr != NULL) {
2083447SN/A		if (p == ns) {
2093447SN/A			/* No characters were converted. */
2103447SN/A			*endptr = (char *)nptr;
2113447SN/A		} else
2123447SN/A			*endptr = (char *)p;
2133447SN/A	}
2143447SN/A
2153447SN/A	return (ret);
2163447SN/A}
2173447SN/A
2183447SN/Astatic char *
2193447SN/Au2s(uintmax_t x, unsigned base, bool uppercase, char *s, size_t *slen_p)
2203447SN/A{
2213447SN/A	unsigned i;
2223447SN/A
2233447SN/A	i = U2S_BUFSIZE - 1;
2243447SN/A	s[i] = '\0';
2253447SN/A	switch (base) {
2263447SN/A	case 10:
2273447SN/A		do {
2283447SN/A			i--;
2293447SN/A			s[i] = "0123456789"[x % (uint64_t)10];
2303447SN/A			x /= (uint64_t)10;
2313447SN/A		} while (x > 0);
2323447SN/A		break;
2333447SN/A	case 16: {
2343447SN/A		const char *digits = (uppercase)
2353447SN/A		    ? "0123456789ABCDEF"
2363447SN/A		    : "0123456789abcdef";
2373447SN/A
2383447SN/A		do {
2393447SN/A			i--;
2403447SN/A			s[i] = digits[x & 0xf];
2413447SN/A			x >>= 4;
2423447SN/A		} while (x > 0);
2433447SN/A		break;
2443447SN/A	} default: {
2453447SN/A		const char *digits = (uppercase)
2463447SN/A		    ? "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2473447SN/A		    : "0123456789abcdefghijklmnopqrstuvwxyz";
2483447SN/A
2493447SN/A		assert(base >= 2 && base <= 36);
2503447SN/A		do {
2513447SN/A			i--;
2523447SN/A			s[i] = digits[x % (uint64_t)base];
2533447SN/A			x /= (uint64_t)base;
2543447SN/A		} while (x > 0);
2553447SN/A	}}
2563447SN/A
2573447SN/A	*slen_p = U2S_BUFSIZE - 1 - i;
2583447SN/A	return (&s[i]);
2593447SN/A}
2603447SN/A
2613447SN/Astatic char *
2623447SN/Ad2s(intmax_t x, char sign, char *s, size_t *slen_p)
2633447SN/A{
2643447SN/A	bool neg;
2653447SN/A
2663447SN/A	if ((neg = (x < 0)))
2673447SN/A		x = -x;
2683447SN/A	s = u2s(x, 10, false, s, slen_p);
2693447SN/A	if (neg)
2703447SN/A		sign = '-';
2713447SN/A	switch (sign) {
2723447SN/A	case '-':
2733447SN/A		if (neg == false)
2743447SN/A			break;
2753447SN/A		/* Fall through. */
2763447SN/A	case ' ':
2773447SN/A	case '+':
2783447SN/A		s--;
2793447SN/A		(*slen_p)++;
2803447SN/A		*s = sign;
2813447SN/A		break;
2823447SN/A	default: not_reached();
2833447SN/A	}
2843447SN/A	return (s);
2853447SN/A}
2863447SN/A
2873447SN/Astatic char *
2883447SN/Ao2s(uintmax_t x, bool alt_form, char *s, size_t *slen_p)
2893447SN/A{
2903447SN/A
2913447SN/A	s = u2s(x, 8, false, s, slen_p);
2923447SN/A	if (alt_form && *s != '0') {
2933447SN/A		s--;
2943447SN/A		(*slen_p)++;
2953447SN/A		*s = '0';
2963447SN/A	}
2973447SN/A	return (s);
2983447SN/A}
2993447SN/A
3003447SN/Astatic char *
3013447SN/Ax2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p)
3023447SN/A{
3033447SN/A
3043447SN/A	s = u2s(x, 16, uppercase, s, slen_p);
3053447SN/A	if (alt_form) {
3063447SN/A		s -= 2;
3073447SN/A		(*slen_p) += 2;
3083447SN/A		memcpy(s, uppercase ? "0X" : "0x", 2);
3093447SN/A	}
3103447SN/A	return (s);
3113447SN/A}
3123447SN/A
3133447SN/Aint
3143447SN/Amalloc_vsnprintf(char *str, size_t size, const char *format, va_list ap)
3153447SN/A{
3163447SN/A	int ret;
3173447SN/A	size_t i;
3183447SN/A	const char *f;
3193447SN/A
3203447SN/A#define	APPEND_C(c) do {						\
3213447SN/A	if (i < size)							\
3223447SN/A		str[i] = (c);						\
3233447SN/A	i++;								\
3243447SN/A} while (0)
3253447SN/A#define	APPEND_S(s, slen) do {						\
3263447SN/A	if (i < size) {							\
3273447SN/A		size_t cpylen = (slen <= size - i) ? slen : size - i;	\
3283447SN/A		memcpy(&str[i], s, cpylen);				\
3293447SN/A	}								\
3303447SN/A	i += slen;							\
3313447SN/A} while (0)
3323447SN/A#define	APPEND_PADDED_S(s, slen, width, left_justify) do {		\
3333447SN/A	/* Left padding. */						\
3343447SN/A	size_t pad_len = (width == -1) ? 0 : ((slen < (size_t)width) ?	\
3353447SN/A	    (size_t)width - slen : 0);					\
3363447SN/A	if (left_justify == false && pad_len != 0) {			\
3373447SN/A		size_t j;						\
3383447SN/A		for (j = 0; j < pad_len; j++)				\
3393447SN/A			APPEND_C(' ');					\
3403447SN/A	}								\
3413447SN/A	/* Value. */							\
3423447SN/A	APPEND_S(s, slen);						\
3433447SN/A	/* Right padding. */						\
3443447SN/A	if (left_justify && pad_len != 0) {				\
3453447SN/A		size_t j;						\
3463447SN/A		for (j = 0; j < pad_len; j++)				\
3473447SN/A			APPEND_C(' ');					\
3483447SN/A	}								\
3493447SN/A} while (0)
3503447SN/A#define GET_ARG_NUMERIC(val, len) do {					\
3513447SN/A	switch (len) {							\
3523447SN/A	case '?':							\
3533447SN/A		val = va_arg(ap, int);					\
3543447SN/A		break;							\
3553447SN/A	case '?' | 0x80:						\
3563447SN/A		val = va_arg(ap, unsigned int);				\
3573447SN/A		break;							\
3583447SN/A	case 'l':							\
3593447SN/A		val = va_arg(ap, long);					\
3603447SN/A		break;							\
3613447SN/A	case 'l' | 0x80:						\
3623447SN/A		val = va_arg(ap, unsigned long);			\
3633447SN/A		break;							\
3643447SN/A	case 'q':							\
3653447SN/A		val = va_arg(ap, long long);				\
3663447SN/A		break;							\
3673447SN/A	case 'q' | 0x80:						\
3683447SN/A		val = va_arg(ap, unsigned long long);			\
3693447SN/A		break;							\
3703447SN/A	case 'j':							\
3713447SN/A		val = va_arg(ap, intmax_t);				\
3723447SN/A		break;							\
3733447SN/A	case 't':							\
3743447SN/A		val = va_arg(ap, ptrdiff_t);				\
3753447SN/A		break;							\
3763447SN/A	case 'z':							\
3773447SN/A		val = va_arg(ap, ssize_t);				\
3783447SN/A		break;							\
3793447SN/A	case 'z' | 0x80:						\
3803447SN/A		val = va_arg(ap, size_t);				\
3813447SN/A		break;							\
3823447SN/A	case 'p': /* Synthetic; used for %p. */				\
3833447SN/A		val = va_arg(ap, uintptr_t);				\
3843447SN/A		break;							\
3853447SN/A	default: not_reached();						\
3863447SN/A	}								\
3873447SN/A} while (0)
3883447SN/A
3893447SN/A	i = 0;
3903447SN/A	f = format;
3913447SN/A	while (true) {
3923447SN/A		switch (*f) {
3933447SN/A		case '\0': goto label_out;
3943447SN/A		case '%': {
3953447SN/A			bool alt_form = false;
3963447SN/A			bool left_justify = false;
3973447SN/A			bool plus_space = false;
3983447SN/A			bool plus_plus = false;
3993447SN/A			int prec = -1;
4003447SN/A			int width = -1;
4013447SN/A			unsigned char len = '?';
4023447SN/A
4033447SN/A			f++;
4043447SN/A			if (*f == '%') {
4053447SN/A				/* %% */
4063447SN/A				APPEND_C(*f);
4073447SN/A				break;
4083447SN/A			}
4093447SN/A			/* Flags. */
4103447SN/A			while (true) {
4113447SN/A				switch (*f) {
4123447SN/A				case '#':
4133447SN/A					assert(alt_form == false);
4143447SN/A					alt_form = true;
4153447SN/A					break;
4163447SN/A				case '-':
4173447SN/A					assert(left_justify == false);
4183447SN/A					left_justify = true;
4193447SN/A					break;
4203447SN/A				case ' ':
4213447SN/A					assert(plus_space == false);
4223447SN/A					plus_space = true;
4233447SN/A					break;
4243447SN/A				case '+':
4253447SN/A					assert(plus_plus == false);
4263447SN/A					plus_plus = true;
4273447SN/A					break;
4283447SN/A				default: goto label_width;
4293447SN/A				}
4303447SN/A				f++;
4313447SN/A			}
4323447SN/A			/* Width. */
4333447SN/A			label_width:
4343447SN/A			switch (*f) {
4353447SN/A			case '*':
4363447SN/A				width = va_arg(ap, int);
4373447SN/A				f++;
4383447SN/A				break;
4393447SN/A			case '0': case '1': case '2': case '3': case '4':
4403447SN/A			case '5': case '6': case '7': case '8': case '9': {
4413447SN/A				uintmax_t uwidth;
4423447SN/A				set_errno(0);
4433447SN/A				uwidth = malloc_strtoumax(f, (char **)&f, 10);
4443447SN/A				assert(uwidth != UINTMAX_MAX || get_errno() !=
4453447SN/A				    ERANGE);
4463447SN/A				width = (int)uwidth;
4473447SN/A				if (*f == '.') {
4483447SN/A					f++;
4493447SN/A					goto label_precision;
4503447SN/A				} else
4513447SN/A					goto label_length;
4523447SN/A				break;
4533447SN/A			} case '.':
4543447SN/A				f++;
4553447SN/A				goto label_precision;
4563447SN/A			default: goto label_length;
4573447SN/A			}
4583447SN/A			/* Precision. */
4593447SN/A			label_precision:
4603447SN/A			switch (*f) {
4613447SN/A			case '*':
4623447SN/A				prec = va_arg(ap, int);
4633447SN/A				f++;
4643447SN/A				break;
4653447SN/A			case '0': case '1': case '2': case '3': case '4':
4663447SN/A			case '5': case '6': case '7': case '8': case '9': {
4673447SN/A				uintmax_t uprec;
4683447SN/A				set_errno(0);
4693447SN/A				uprec = malloc_strtoumax(f, (char **)&f, 10);
4703447SN/A				assert(uprec != UINTMAX_MAX || get_errno() !=
4713447SN/A				    ERANGE);
4723447SN/A				prec = (int)uprec;
4733447SN/A				break;
4743447SN/A			}
4753447SN/A			default: break;
4763447SN/A			}
4773447SN/A			/* Length. */
4783447SN/A			label_length:
4793447SN/A			switch (*f) {
4803447SN/A			case 'l':
4813447SN/A				f++;
4823447SN/A				if (*f == 'l') {
4833447SN/A					len = 'q';
4843447SN/A					f++;
4853447SN/A				} else
4863447SN/A					len = 'l';
4873447SN/A				break;
4883447SN/A			case 'j':
4893447SN/A				len = 'j';
4903447SN/A				f++;
4913447SN/A				break;
4923447SN/A			case 't':
4933447SN/A				len = 't';
4943447SN/A				f++;
4953447SN/A				break;
4963447SN/A			case 'z':
4973447SN/A				len = 'z';
4983447SN/A				f++;
4993447SN/A				break;
5003447SN/A			default: break;
5013447SN/A			}
5023447SN/A			/* Conversion specifier. */
5033447SN/A			switch (*f) {
5043447SN/A				char *s;
5053447SN/A				size_t slen;
5063447SN/A			case 'd': case 'i': {
5073447SN/A				intmax_t val JEMALLOC_CC_SILENCE_INIT(0);
5083447SN/A				char buf[D2S_BUFSIZE];
5093447SN/A
5103447SN/A				GET_ARG_NUMERIC(val, len);
5113447SN/A				s = d2s(val, (plus_plus ? '+' : (plus_space ?
5123447SN/A				    ' ' : '-')), buf, &slen);
5133447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5143447SN/A				f++;
5153447SN/A				break;
5163447SN/A			} case 'o': {
5173447SN/A				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
5183447SN/A				char buf[O2S_BUFSIZE];
5193447SN/A
5203447SN/A				GET_ARG_NUMERIC(val, len | 0x80);
5213447SN/A				s = o2s(val, alt_form, buf, &slen);
5223447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5233447SN/A				f++;
5243447SN/A				break;
5253447SN/A			} case 'u': {
5263447SN/A				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
5273447SN/A				char buf[U2S_BUFSIZE];
5283447SN/A
5293447SN/A				GET_ARG_NUMERIC(val, len | 0x80);
5303447SN/A				s = u2s(val, 10, false, buf, &slen);
5313447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5323447SN/A				f++;
5333447SN/A				break;
5343447SN/A			} case 'x': case 'X': {
5353447SN/A				uintmax_t val JEMALLOC_CC_SILENCE_INIT(0);
5363447SN/A				char buf[X2S_BUFSIZE];
5373447SN/A
5383447SN/A				GET_ARG_NUMERIC(val, len | 0x80);
5393447SN/A				s = x2s(val, alt_form, *f == 'X', buf, &slen);
5403447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5413447SN/A				f++;
5423447SN/A				break;
5433447SN/A			} case 'c': {
5443447SN/A				unsigned char val;
5453447SN/A				char buf[2];
5463447SN/A
5473447SN/A				assert(len == '?' || len == 'l');
5483447SN/A				assert_not_implemented(len != 'l');
5493447SN/A				val = va_arg(ap, int);
5503447SN/A				buf[0] = val;
5513447SN/A				buf[1] = '\0';
5523447SN/A				APPEND_PADDED_S(buf, 1, width, left_justify);
5533447SN/A				f++;
5543447SN/A				break;
5553447SN/A			} case 's':
5563447SN/A				assert(len == '?' || len == 'l');
5573447SN/A				assert_not_implemented(len != 'l');
5583447SN/A				s = va_arg(ap, char *);
5593447SN/A				slen = (prec == -1) ? strlen(s) : prec;
5603447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5613447SN/A				f++;
5623447SN/A				break;
5633447SN/A			case 'p': {
5643447SN/A				uintmax_t val;
5653447SN/A				char buf[X2S_BUFSIZE];
5663447SN/A
5673447SN/A				GET_ARG_NUMERIC(val, 'p');
5683447SN/A				s = x2s(val, true, false, buf, &slen);
5693447SN/A				APPEND_PADDED_S(s, slen, width, left_justify);
5703447SN/A				f++;
5713447SN/A				break;
5723447SN/A			}
5733447SN/A			default: not_implemented();
5743447SN/A			}
575			break;
576		} default: {
577			APPEND_C(*f);
578			f++;
579			break;
580		}}
581	}
582	label_out:
583	if (i < size)
584		str[i] = '\0';
585	else
586		str[size - 1] = '\0';
587	ret = i;
588
589#undef APPEND_C
590#undef APPEND_S
591#undef APPEND_PADDED_S
592#undef GET_ARG_NUMERIC
593	return (ret);
594}
595
596JEMALLOC_ATTR(format(printf, 3, 4))
597int
598malloc_snprintf(char *str, size_t size, const char *format, ...)
599{
600	int ret;
601	va_list ap;
602
603	va_start(ap, format);
604	ret = malloc_vsnprintf(str, size, format, ap);
605	va_end(ap);
606
607	return (ret);
608}
609
610void
611malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
612    const char *format, va_list ap)
613{
614	char buf[MALLOC_PRINTF_BUFSIZE];
615
616	if (write_cb == NULL) {
617		/*
618		 * The caller did not provide an alternate write_cb callback
619		 * function, so use the default one.  malloc_write() is an
620		 * inline function, so use malloc_message() directly here.
621		 */
622		write_cb = (je_malloc_message != NULL) ? je_malloc_message :
623		    wrtmessage;
624		cbopaque = NULL;
625	}
626
627	malloc_vsnprintf(buf, sizeof(buf), format, ap);
628	write_cb(cbopaque, buf);
629}
630
631/*
632 * Print to a callback function in such a way as to (hopefully) avoid memory
633 * allocation.
634 */
635JEMALLOC_ATTR(format(printf, 3, 4))
636void
637malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
638    const char *format, ...)
639{
640	va_list ap;
641
642	va_start(ap, format);
643	malloc_vcprintf(write_cb, cbopaque, format, ap);
644	va_end(ap);
645}
646
647/* Print to stderr in such a way as to avoid memory allocation. */
648JEMALLOC_ATTR(format(printf, 1, 2))
649void
650malloc_printf(const char *format, ...)
651{
652	va_list ap;
653
654	va_start(ap, format);
655	malloc_vcprintf(NULL, NULL, format, ap);
656	va_end(ap);
657}
658