ref: f88a55e79b5bf656e7f9578d1318a955b9a4963a
dir: /sys/src/ape/lib/ap/riscv64/malloc.c/
/*
* V7 unix malloc, adapted to the modern world
*/
/*
* C storage allocator
* circular first-fit strategy
* works with noncontiguous, but monotonically linked, arena
* each block is preceded by a ptr to the (pointer of)
* the next following block
* blocks are exact number of words long
* aligned to the data type requirements of ALIGN
* pointers to blocks must have BUSY bit 0
* bit in ptr is 1 for busy, 0 for idle
* gaps in arena are merely noted as busy blocks
* last block of arena (pointed to by alloct) is empty and
* has a pointer to first
* idle blocks are coalesced during space search
*
* a different implementation may need to redefine
* ALIGN, NALIGN, BLOCK, BUSY, INT
* where INT is integer type to which a pointer can be cast
*/
#include <u.h>
#include <libc.h>
#ifdef debug
#define ASSERT(p) if(!(p))botch("p");else
void
botch(char *s)
{
unlock(&malloclck);
print("assertion botched: %s\n", s);
abort();
}
#else
#define ASSERT(p)
#endif
#define INT uintptr
#define ALIGN vlong
#define NALIGN 1
#define WORD sizeof(union store)
#define BLOCK 1024 /* a multiple of WORD */
#define BUSY 1
#define NULL 0
#define testbusy(p) ((INT)(p)&BUSY)
#define setbusy(p) (union store *)((INT)(p)|BUSY)
#define clearbusy(p) (union store *)((INT)(p)&~BUSY)
typedef union store Store;
union store {
Store *ptr;
ALIGN dummy[NALIGN];
int calloc; /*calloc clears an array of integers*/
};
static Store allocs[2]; /*initial arena*/
static Store *allocp; /*search ptr*/
static Store *alloct; /*arena top*/
static Store *allocx; /*for benefit of realloc*/
static Lock malloclck;
void *
malloc(uintptr nbytes)
{
uintptr nw, temp;
Store *p, *q;
lock(&malloclck);
if (allocs[0].ptr == 0) { /* first time */
allocs[0].ptr = setbusy(&allocs[1]);
allocs[1].ptr = setbusy(&allocs[0]);
alloct = &allocs[1];
allocp = &allocs[0];
}
nw = (nbytes + WORD + WORD - 1) / WORD;
ASSERT(allocp >= allocs && allocp <= alloct);
ASSERT(allock());
for (p = allocp; ; ) {
for (temp = 0; ; ) {
if (!testbusy(p->ptr)) {
while (!testbusy((q = p->ptr)->ptr)) {
ASSERT(q > p && q < alloct);
p->ptr = q->ptr;
}
if (q >= p + nw && p + nw >= p)
goto found;
}
q = p;
p = clearbusy(p->ptr);
if (p > q) {
ASSERT(p <= alloct);
} else if (q != alloct || p != allocs) {
ASSERT(q == alloct && p == allocs);
unlock(&malloclck);
return NULL;
} else if (++temp > 1)
break;
}
temp = ((nw + BLOCK/WORD) / (BLOCK/WORD)) * (BLOCK/WORD);
q = (Store *)sbrk(0);
if (q + temp < q) {
unlock(&malloclck);
return NULL;
}
q = (Store *)sbrk(temp * WORD);
if ((INT)q == -1) {
unlock(&malloclck);
return NULL;
}
ASSERT(q > alloct);
alloct->ptr = q;
if (q != alloct + 1)
alloct->ptr = setbusy(alloct->ptr);
alloct = q->ptr = q + temp - 1;
alloct->ptr = setbusy(allocs);
}
found:
allocp = p + nw;
ASSERT(allocp <= alloct);
if (q > allocp) {
allocx = allocp->ptr;
allocp->ptr = p->ptr;
}
p->ptr = setbusy(allocp);
unlock(&malloclck);
return p + 1;
}
void *
mallocz(uintptr size, int clr)
{
void *p;
p = malloc(size);
if (p && clr)
memset(p, 0, size);
return p;
}
/*
* freeing strategy tuned for LIFO allocation
*/
void
free(void *ap)
{
Store *p = (Store *)ap;
if (p == 0)
return;
lock(&malloclck);
ASSERT(p > clearbusy(allocs[1].ptr) && p <= alloct);
ASSERT(allock());
allocp = --p;
ASSERT(testbusy(p->ptr));
p->ptr = clearbusy(p->ptr);
ASSERT(p->ptr > allocp && p->ptr <= alloct);
unlock(&malloclck);
}
/* realloc(p, nbytes) reallocates a block obtained from malloc()
* and freed since last call of malloc()
* to have new size nbytes, and old content
* returns new location, or 0 on failure
*/
void *
realloc(void *ap, uintptr nbytes)
{
Store *p, *q;
uintptr nw, onw;
p = ap;
if (nbytes == 0) {
free(p);
return nil;
}
if (p == nil)
return malloc(nbytes);
if (testbusy(p[-1].ptr))
free(p);
onw = p[-1].ptr - p;
q = (Store *)malloc(nbytes);
if (q == NULL || q == p)
return q;
lock(&malloclck);
memmove(q, p, nbytes);
nw = (nbytes + WORD - 1) / WORD;
if (q < p && q + nw >= p)
(q + (q + nw - p))->ptr = allocx;
unlock(&malloclck);
return q;
}
#ifdef debug
void
allock(void)
{
#ifdef longdebug
Store *p;
int x;
x = 0;
for (p = &allocs[0]; clearbusy(p->ptr) > p; p = clearbusy(p->ptr))
if (p == allocp)
x++;
ASSERT(p == alloct);
return x == 1 | p == allocp;
#else
return 1;
#endif
}
#endif
void*
calloc(uintptr n, uintptr szelem)
{
void *v;
if (v = mallocz(n * szelem, 1))
setmalloctag(v, getcallerpc(&n));
return v;
}
void
setmalloctag(void *, uintptr)
{
}
void
setrealloctag(void *, uintptr)
{
}
uintptr
getmalloctag(void *)
{
return ~0;
}
uintptr
getrealloctag(void *)
{
return ~0;
}