#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)malloc.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: malloc.c,v 1.13 2026/04/28 13:02:10 riastradh Exp $");
#endif
#endif
#include <sys/types.h>
#if defined(DEBUG) || defined(RCHECK)
#include <sys/uio.h>
#endif
#include <errno.h>
#include <limits.h>
#include <stddef.h>
#include <stdint.h>
#if defined(RCHECK) || defined(MSTATS)
#include <stdio.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "reentrant.h"
union overhead {
union overhead *ov_next;
struct {
u_char ovu_magic;
u_char ovu_index;
#ifdef RCHECK
u_short ovu_rmagic;
u_long ovu_size;
#endif
} ovu;
#define ov_magic ovu.ovu_magic
#define ov_index ovu.ovu_index
#define ov_rmagic ovu.ovu_rmagic
#define ov_size ovu.ovu_size
};
#define MAGIC 0xef
#ifdef RCHECK
#define RMAGIC 0x5555
#endif
#ifdef RCHECK
#define RSLOP sizeof (u_short)
#else
#define RSLOP 0
#endif
#define NBUCKETS 30
static union overhead *nextf[NBUCKETS];
static long pagesz;
static int pagebucket;
#ifdef MSTATS
static u_int nmalloc[NBUCKETS];
#endif
#ifdef _REENT
static mutex_t malloc_mutex = MUTEX_INITIALIZER;
#endif
static void morecore(int);
static int findbucket(union overhead *, int);
#ifdef MSTATS
void mstats(const char *);
#endif
#if defined(DEBUG) || defined(RCHECK)
#define ASSERT(p) if (!(p)) botch(__STRING(p))
static void botch(const char *);
static void
botch(const char *s)
{
struct iovec iov[3];
iov[0].iov_base = __UNCONST("\nassertion botched: ");
iov[0].iov_len = 20;
iov[1].iov_base = __UNCONST(s);
iov[1].iov_len = strlen(s);
iov[2].iov_base = __UNCONST("\n");
iov[2].iov_len = 1;
(void)writev(STDERR_FILENO, iov, 3);
abort();
}
#else
#define ASSERT(p) ((void)sizeof((long)(p)))
#endif
void *
malloc(size_t nbytes)
{
union overhead *op;
int bucket;
long n;
unsigned amt;
mutex_lock(&malloc_mutex);
if (pagesz == 0) {
pagesz = n = getpagesize();
ASSERT(pagesz > 0);
op = (union overhead *)(void *)sbrk(0);
n = n - sizeof (*op) - ((long)op & (n - 1));
if (n < 0)
n += pagesz;
if (n) {
if (sbrk((int)n) == (void *)-1) {
goto out;
}
}
bucket = 0;
amt = 8;
while (pagesz > amt) {
amt <<= 1;
bucket++;
}
pagebucket = bucket;
}
if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
#ifndef RCHECK
amt = 8;
bucket = 0;
#else
amt = 16;
bucket = 1;
#endif
n = -((long)sizeof (*op) + RSLOP);
} else {
amt = (unsigned)pagesz;
bucket = pagebucket;
}
while (nbytes > amt + n) {
amt <<= 1;
if (amt == 0)
goto out;
bucket++;
}
if ((op = nextf[bucket]) == NULL) {
morecore(bucket);
if ((op = nextf[bucket]) == NULL) {
goto out;
}
}
nextf[bucket] = op->ov_next;
op->ov_magic = MAGIC;
op->ov_index = bucket;
#ifdef MSTATS
nmalloc[bucket]++;
#endif
mutex_unlock(&malloc_mutex);
#ifdef RCHECK
op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
op->ov_rmagic = RMAGIC;
*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
#endif
return ((void *)(op + 1));
out:
mutex_unlock(&malloc_mutex);
return (NULL);
}
static void
morecore(int bucket)
{
union overhead *op;
long sz;
long amt;
long nblks;
sz = 1 << (bucket + 3);
#ifdef DEBUG
ASSERT(sz > 0);
#else
if (sz <= 0)
return;
#endif
if (sz < pagesz) {
amt = pagesz;
nblks = amt / sz;
} else {
amt = sz + pagesz;
nblks = 1;
}
op = (union overhead *)(void *)sbrk((int)amt);
if ((long)op == -1)
return;
nextf[bucket] = op;
while (--nblks > 0) {
op->ov_next =
(union overhead *)(void *)((caddr_t)(void *)op+(size_t)sz);
op = op->ov_next;
}
}
void
free(void *cp)
{
long size;
union overhead *op;
if (cp == NULL)
return;
op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead));
#ifdef DEBUG
ASSERT(op->ov_magic == MAGIC);
#else
if (op->ov_magic != MAGIC)
return;
#endif
#ifdef RCHECK
ASSERT(op->ov_rmagic == RMAGIC);
ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
#endif
size = op->ov_index;
ASSERT(size < NBUCKETS);
mutex_lock(&malloc_mutex);
op->ov_next = nextf[(unsigned int)size];
nextf[(unsigned int)size] = op;
#ifdef MSTATS
nmalloc[(size_t)size]--;
#endif
mutex_unlock(&malloc_mutex);
}
int __realloc_srchlen = 4;
void *
realloc(void *cp, size_t nbytes)
{
u_long onb;
long i;
union overhead *op;
char *res;
int was_alloced = 0;
if (cp == NULL)
return (malloc(nbytes));
if (nbytes == 0) {
free (cp);
return (NULL);
}
op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead));
mutex_lock(&malloc_mutex);
if (op->ov_magic == MAGIC) {
was_alloced++;
i = op->ov_index;
} else {
if ((i = findbucket(op, 1)) < 0 &&
(i = findbucket(op, __realloc_srchlen)) < 0)
i = NBUCKETS;
}
onb = (u_long)1 << (u_long)(i + 3);
if (onb < pagesz)
onb -= sizeof (*op) + RSLOP;
else
onb += pagesz - sizeof (*op) - RSLOP;
if (was_alloced) {
if (i) {
i = (long)1 << (long)(i + 2);
if (i < pagesz)
i -= sizeof (*op) + RSLOP;
else
i += pagesz - sizeof (*op) - RSLOP;
}
if (nbytes <= onb && nbytes > i) {
#ifdef RCHECK
op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
*(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
#endif
mutex_unlock(&malloc_mutex);
return (cp);
}
#ifndef _REENT
else
free(cp);
#endif
}
mutex_unlock(&malloc_mutex);
if ((res = malloc(nbytes)) == NULL) {
#ifdef _REENT
free(cp);
#endif
return (NULL);
}
#ifndef _REENT
if (cp != res)
(void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb));
#else
(void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb));
free(cp);
#endif
return (res);
}
static int
findbucket(union overhead *freep, int srchlen)
{
union overhead *p;
int i, j;
for (i = 0; i < NBUCKETS; i++) {
j = 0;
for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
if (p == freep)
return (i);
j++;
}
}
return (-1);
}
#ifdef MSTATS
void
mstats(const char *s)
{
int i, j;
union overhead *p;
int totfree = 0,
totused = 0;
fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
for (i = 0; i < NBUCKETS; i++) {
for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
;
fprintf(stderr, " %d", j);
totfree += j * (1 << (i + 3));
}
fprintf(stderr, "\nused:\t");
for (i = 0; i < NBUCKETS; i++) {
fprintf(stderr, " %d", nmalloc[i]);
totused += nmalloc[i] * (1 << (i + 3));
}
fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
totused, totfree);
}
#endif
static long __constfunc
cachedpagesize(void)
{
long n;
if (__predict_false((n = pagesz) == 0)) {
mutex_lock(&malloc_mutex);
if ((n = pagesz) == 0)
n = pagesz = getpagesize();
mutex_unlock(&malloc_mutex);
}
return n;
}
void *
aligned_alloc(size_t alignment, size_t size)
{
char *p;
if (alignment == 0 ||
(alignment & (alignment - 1)) != 0 ||
alignment > cachedpagesize()) {
errno = EINVAL;
return NULL;
}
p = malloc(size < alignment ? alignment : size);
if (__predict_true(p != NULL))
ASSERT((uintptr_t)p % alignment == 0);
return p;
}
void *
calloc(size_t nmemb, size_t size)
{
void *p;
size_t n;
if (__builtin_mul_overflow(nmemb, size, &n)) {
errno = ENOMEM;
return NULL;
}
p = malloc(n);
if (__predict_false(p == NULL))
return NULL;
memset(p, 0, n);
return p;
}
int
posix_memalign(void **memptr, size_t alignment, size_t size)
{
char *p;
if (alignment < sizeof(void *) ||
(alignment & (alignment - 1)) != 0 ||
alignment > cachedpagesize())
return EINVAL;
p = malloc(size < alignment ? alignment : size);
if (__predict_false(p == NULL))
return ENOMEM;
ASSERT((uintptr_t)p % alignment == 0);
*memptr = p;
return 0;
}
#include "../libc/include/extern.h"
void
_malloc_prefork(void)
{
mutex_lock(&malloc_mutex);
}
void
_malloc_postfork(void)
{
mutex_unlock(&malloc_mutex);
}
void
_malloc_postfork_child(void)
{
mutex_init(&malloc_mutex, NULL);
}