diff --git a/sys/include/ape/xalloc.h b/sys/include/ape/xalloc.h index b26ed4952..31b7a0ffd 100644 --- a/sys/include/ape/xalloc.h +++ b/sys/include/ape/xalloc.h @@ -16,15 +16,24 @@ extern void xalloc_die(void); extern void *xmalloc(size_t); extern void *xcalloc(size_t, size_t); extern void *xrealloc(void *, size_t); +extern void *xreallocarray(void *, size_t, size_t); +extern void *xnmalloc(size_t, size_t); +extern void *xnrealloc(void *, size_t, size_t); /* fixed resize to n*s */ extern void *xmemdup(const void *, size_t); +extern char *xcharalloc(size_t); +extern void *xzalloc(size_t); extern char *xstrdup(const char *); extern char *xstrndup(const char *, size_t); -extern void *xnrealloc(void *, size_t *, size_t); +extern void *x2nrealloc(void *, size_t *, size_t); /* growing: ~1.5x */ +extern void *x2realloc(void *, size_t *); +extern void *xpalloc(void *, ptrdiff_t *, ptrdiff_t, ptrdiff_t, ptrdiff_t); /* Convenience: typed versions */ -#define XMALLOC(t, n) ((t *)xmalloc((n) * sizeof(t))) -#define XCALLOC(t, n) ((t *)xcalloc((n), sizeof(t))) -#define XREALLOC(t, p, n) ((t *)xrealloc((p), (n) * sizeof(t))) +#define XMALLOC(t, n) ((t *)xmalloc((n) * sizeof(t))) +#define XCALLOC(t, n) ((t *)xcalloc((n), sizeof(t))) +#define XREALLOC(t, p, n) ((t *)xrealloc((p), (n) * sizeof(t))) +#define XNMALLOC(n, t) ((t *)xnmalloc((n), sizeof(t))) +#define XNREALLOC(p, n, t) ((t *)xnrealloc((p), (n), sizeof(t))) #ifdef __cplusplus } diff --git a/sys/src/ape/lib/ap/malloc/xmalloc.c b/sys/src/ape/lib/ap/malloc/xmalloc.c index 3591881de..a24484ada 100644 --- a/sys/src/ape/lib/ap/malloc/xmalloc.c +++ b/sys/src/ape/lib/ap/malloc/xmalloc.c @@ -1,6 +1,7 @@ #include #include #include +#include #include /* Called on allocation failure. Programs may override this. */ @@ -38,6 +39,31 @@ xrealloc(void *p, size_t n) return p; } +/* Reallocate P as an array of N objects of S bytes each, with error checking. */ +void * +xreallocarray(void *p, size_t n, size_t s) +{ + void *r = reallocarray(p, n, s); + if(!r && n != 0 && s != 0) + xalloc_die(); + return r; +} + +/* Allocate an array of N objects of S bytes each, with error checking. */ +void * +xnmalloc(size_t n, size_t s) +{ + return xreallocarray(NULL, n, s); +} + +/* Resize P to an array of N objects of S bytes each, with error checking. + Unlike x2nrealloc, this resizes to exactly N — it does not grow. */ +void * +xnrealloc(void *p, size_t n, size_t s) +{ + return xreallocarray(p, n, s); +} + void * xmemdup(const void *src, size_t n) { @@ -46,6 +72,18 @@ xmemdup(const void *src, size_t n) return p; } +char * +xcharalloc(size_t n) +{ + return xmalloc(n); +} + +void * +xzalloc(size_t s) +{ + return xcalloc(s, 1); +} + char * xstrdup(const char *s) { @@ -64,20 +102,70 @@ xstrndup(const char *s, size_t n) return p; } -/* - * xnrealloc - realloc *pp to hold at least *pn elements of size s, - * doubling *pn each time. Useful for growing arrays. - */ +/* Grow P (array of *PN objects of S bytes) by ~1.5x, with error checking. + If P is null, allocate an initial block sized for the default fast path. */ void * -xnrealloc(void *p, size_t *pn, size_t s) +x2nrealloc(void *p, size_t *pn, size_t s) { size_t n = *pn; - if(n == 0) - n = 1; - else if(n > (size_t)-1 / 2 / s) - xalloc_die(); - else - n *= 2; + + if(!p) { + enum { DEFAULT_MXFAST = 64 * sizeof(size_t) / 4 }; + if(!n) { + n = DEFAULT_MXFAST / s; + n += !n; + } + } else { + /* n = floor(1.5 * n) + 1, checking for overflow */ + size_t incr = (n >> 1) + 1; + if(n > (size_t)-1 - incr) + xalloc_die(); + n += incr; + } + p = xreallocarray(p, n, s); + *pn = n; + return p; +} + +void * +x2realloc(void *p, size_t *ps) +{ + return x2nrealloc(p, ps, 1); +} + +/* Grow array PA of *PN items (each S bytes) by at least N_INCR_MIN items, + not exceeding N_MAX total (-1 = unlimited). */ +void * +xpalloc(void *pa, ptrdiff_t *pn, ptrdiff_t n_incr_min, ptrdiff_t n_max, ptrdiff_t s) +{ + enum { DEFAULT_MXFAST = 64 * sizeof(size_t) / 4 }; + ptrdiff_t n0 = *pn; + ptrdiff_t n; + size_t nbytes; + + /* Grow by ~50%; cap at n_max if set. */ + n = n0 + (n0 >> 1) + 1; + if(n0 > (ptrdiff_t)((size_t)-1 / 2)) + n = (ptrdiff_t)((size_t)-1 / 2); /* overflow guard */ + if(0 <= n_max && n_max < n) + n = n_max; + + nbytes = (size_t)n * (size_t)s; + /* If tiny, round up to DEFAULT_MXFAST */ + if(nbytes < DEFAULT_MXFAST && s > 0) { + n = DEFAULT_MXFAST / s; + nbytes = (size_t)n * (size_t)s; + } + + if(!pa) + *pn = 0; + if(n - n0 < n_incr_min) { + n = n0 + n_incr_min; + if(n < 0 || (0 <= n_max && n_max < n)) + xalloc_die(); + nbytes = (size_t)n * (size_t)s; + } + pa = xrealloc(pa, nbytes); *pn = n; - return xrealloc(p, n * s); + return pa; }