#include "lint.h"
#include "mallint.h"
#include "mtlib.h"
mutex_t __malloc_lock = DEFAULTMUTEX;
mutex_t libc_malloc_lock = DEFAULTMUTEX;
static TREE *Root,
*Bottom,
*_morecore(size_t);
static char *Baddr;
static char *Lfree;
static void t_delete(TREE *);
static void t_splay(TREE *);
static void realfree(void *);
static void cleanfree(void *);
static void *_malloc_unlocked(size_t);
#define FREESIZE (1<<5)
#define FREEMASK FREESIZE-1
static void *flist[FREESIZE];
static int freeidx;
void
malloc_locks(void)
{
(void) mutex_lock(&libc_malloc_lock);
}
void
malloc_unlocks(void)
{
(void) mutex_unlock(&libc_malloc_lock);
}
static TREE *List[MINSIZE/WORDSIZE-1];
static void *
_smalloc(size_t size)
{
TREE *tp;
size_t i;
ASSERT(size % WORDSIZE == 0);
if (size == 0)
size = WORDSIZE;
i = size / WORDSIZE - 1;
if (List[i] == NULL) {
TREE *np;
int n;
#define NPS (WORDSIZE*8)
ASSERT((size + WORDSIZE) * NPS >= MINSIZE);
if ((List[i] = _malloc_unlocked((size + WORDSIZE) * NPS)) == 0)
return (0);
for (n = 0, np = List[i]; n < NPS; ++n) {
tp = np;
SIZE(tp) = size;
np = NEXT(tp);
AFTER(tp) = np;
}
AFTER(tp) = NULL;
}
tp = List[i];
List[i] = AFTER(tp);
SETBIT0(SIZE(tp));
return (DATA(tp));
}
void *
malloc(size_t size)
{
void *ret;
if (!primary_link_map) {
errno = ENOTSUP;
return (NULL);
}
assert_no_libc_locks_held();
(void) mutex_lock(&libc_malloc_lock);
ret = _malloc_unlocked(size);
(void) mutex_unlock(&libc_malloc_lock);
return (ret);
}
static void *
_malloc_unlocked(size_t size)
{
size_t n;
TREE *tp, *sp;
size_t o_bit1;
COUNT(nmalloc);
ASSERT(WORDSIZE == ALIGN);
if (size > MAX_MALLOC) {
errno = ENOMEM;
return (NULL);
}
ROUND(size);
if (Lfree) {
sp = BLOCK(Lfree);
n = SIZE(sp);
CLRBITS01(n);
if (n == size) {
freeidx = (freeidx + FREESIZE - 1) &
FREEMASK;
flist[freeidx] = Lfree = NULL;
return (DATA(sp));
} else if (size >= MINSIZE && n > size) {
freeidx = (freeidx + FREESIZE - 1) &
FREEMASK;
flist[freeidx] = Lfree = NULL;
o_bit1 = SIZE(sp) & BIT1;
SIZE(sp) = n;
goto leftover;
}
}
o_bit1 = 0;
cleanfree(NULL);
if (size < MINSIZE)
return (_smalloc(size));
sp = NULL;
n = 0;
if (Root) {
tp = Root;
for (;;) {
if (SIZE(tp) >= size) {
if (n == 0 || n >= SIZE(tp)) {
sp = tp;
n = SIZE(tp);
}
if (LEFT(tp))
tp = LEFT(tp);
else
break;
} else {
if (RIGHT(tp))
tp = RIGHT(tp);
else
break;
}
}
if (sp) {
t_delete(sp);
} else if (tp != Root) {
t_splay(tp);
Root = tp;
}
}
if (!sp) {
if (Bottom && size <= SIZE(Bottom)) {
sp = Bottom;
CLRBITS01(SIZE(sp));
} else if ((sp = _morecore(size)) == NULL)
return (NULL);
}
CLRBIT1(SIZE(NEXT(sp)));
ASSERT(ISBIT0(SIZE(NEXT(sp))));
leftover:
if ((n = (SIZE(sp) - size)) >= MINSIZE + WORDSIZE) {
n -= WORDSIZE;
SIZE(sp) = size;
tp = NEXT(sp);
SIZE(tp) = n|BIT0;
realfree(DATA(tp));
} else if (BOTTOM(sp))
Bottom = NULL;
SIZE(sp) |= BIT0 | o_bit1;
return (DATA(sp));
}
void *
realloc(void *old, size_t size)
{
TREE *tp, *np;
size_t ts;
char *new;
if (!primary_link_map) {
errno = ENOTSUP;
return (NULL);
}
assert_no_libc_locks_held();
COUNT(nrealloc);
if (size > MAX_MALLOC) {
errno = ENOMEM;
return (NULL);
}
(void) mutex_lock(&libc_malloc_lock);
if (old == NULL) {
new = _malloc_unlocked(size);
(void) mutex_unlock(&libc_malloc_lock);
return (new);
}
cleanfree(old);
ROUND(size);
tp = BLOCK(old);
ts = SIZE(tp);
if (!ISBIT0(ts)) {
(void) mutex_unlock(&libc_malloc_lock);
return (NULL);
}
CLRBITS01(SIZE(tp));
if (size == SIZE(tp)) {
SIZE(tp) = ts;
(void) mutex_unlock(&libc_malloc_lock);
return (old);
}
if (size < MINSIZE || SIZE(tp) < MINSIZE) {
if (size == 0) {
SETOLD01(SIZE(tp), ts);
_free_unlocked(old);
(void) mutex_unlock(&libc_malloc_lock);
return (NULL);
} else {
goto call_malloc;
}
}
if (size > SIZE(tp)) {
np = NEXT(tp);
if (!ISBIT0(SIZE(np))) {
ASSERT(SIZE(np) >= MINSIZE);
ASSERT(!ISBIT1(SIZE(np)));
SIZE(tp) += SIZE(np) + WORDSIZE;
if (np != Bottom)
t_delete(np);
else
Bottom = NULL;
CLRBIT1(SIZE(NEXT(np)));
}
#ifndef SEGMENTED
if (size > SIZE(tp) && BOTTOM(tp) && GETCORE(0) == Baddr) {
Bottom = tp;
if ((tp = _morecore(size)) == NULL) {
tp = Bottom;
Bottom = NULL;
}
}
#endif
}
if (size <= SIZE(tp)) {
size_t n;
chop_big:
if ((n = (SIZE(tp) - size)) >= MINSIZE + WORDSIZE) {
n -= WORDSIZE;
SIZE(tp) = size;
np = NEXT(tp);
SIZE(np) = n|BIT0;
realfree(DATA(np));
} else if (BOTTOM(tp))
Bottom = NULL;
SETOLD01(SIZE(tp), ts);
(void) mutex_unlock(&libc_malloc_lock);
return (old);
}
call_malloc:
SETOLD01(SIZE(tp), ts);
if ((new = _malloc_unlocked(size)) != NULL) {
CLRBITS01(ts);
if (ts > size)
ts = size;
MEMCOPY(new, old, ts);
_free_unlocked(old);
(void) mutex_unlock(&libc_malloc_lock);
return (new);
}
CLRBITS01(SIZE(tp));
if (SIZE(tp) < MINSIZE) {
if (size < SIZE(tp)) {
SETOLD01(SIZE(tp), ts);
(void) mutex_unlock(&libc_malloc_lock);
return (old);
} else if (size < MINSIZE) {
size = MINSIZE;
goto call_malloc;
}
} else if (size < MINSIZE) {
size = MINSIZE;
goto chop_big;
} else if (ISBIT1(ts) &&
(SIZE(np = LAST(tp)) + SIZE(tp) + WORDSIZE) >= size) {
ASSERT(!ISBIT0(SIZE(np)));
t_delete(np);
SIZE(np) += SIZE(tp) + WORDSIZE;
(void) memmove(DATA(np), old, SIZE(tp));
old = DATA(np);
tp = np;
CLRBIT1(ts);
goto chop_big;
}
SETOLD01(SIZE(tp), ts);
(void) mutex_unlock(&libc_malloc_lock);
return (NULL);
}
static void
realfree(void *old)
{
TREE *tp, *sp, *np;
size_t ts, size;
COUNT(nfree);
tp = BLOCK(old);
ts = SIZE(tp);
if (!ISBIT0(ts))
return;
CLRBITS01(SIZE(tp));
if (SIZE(tp) < MINSIZE) {
ASSERT(SIZE(tp) / WORDSIZE >= 1);
ts = SIZE(tp) / WORDSIZE - 1;
AFTER(tp) = List[ts];
List[ts] = tp;
return;
}
np = NEXT(tp);
if (!ISBIT0(SIZE(np))) {
if (np != Bottom)
t_delete(np);
SIZE(tp) += SIZE(np) + WORDSIZE;
}
if (ISBIT1(ts)) {
np = LAST(tp);
ASSERT(!ISBIT0(SIZE(np)));
ASSERT(np != Bottom);
t_delete(np);
SIZE(np) += SIZE(tp) + WORDSIZE;
tp = np;
}
PARENT(tp) = LEFT(tp) = RIGHT(tp) = LINKFOR(tp) = NULL;
*(SELFP(tp)) = tp;
if (BOTTOM(tp))
Bottom = tp;
else {
if (Root) {
size = SIZE(tp);
np = Root;
for (;;) {
if (SIZE(np) > size) {
if (LEFT(np))
np = LEFT(np);
else {
LEFT(np) = tp;
PARENT(tp) = np;
break;
}
} else if (SIZE(np) < size) {
if (RIGHT(np))
np = RIGHT(np);
else {
RIGHT(np) = tp;
PARENT(tp) = np;
break;
}
} else {
if ((sp = PARENT(np)) != NULL) {
if (np == LEFT(sp))
LEFT(sp) = tp;
else
RIGHT(sp) = tp;
PARENT(tp) = sp;
} else
Root = tp;
if ((sp = LEFT(np)) != NULL)
PARENT(sp) = tp;
LEFT(tp) = sp;
if ((sp = RIGHT(np)) != NULL)
PARENT(sp) = tp;
RIGHT(tp) = sp;
LINKFOR(tp) = np;
LINKBAK(np) = tp;
SETNOTREE(np);
break;
}
}
} else
Root = tp;
}
SETBIT1(SIZE(NEXT(tp)));
ASSERT(ISBIT0(SIZE(NEXT(tp))));
}
static TREE *
_morecore(size_t size)
{
TREE *tp;
ssize_t n, offset;
char *addr;
ssize_t nsize;
tp = Bottom;
n = (ssize_t)size + 2 * WORDSIZE;
addr = GETCORE(0);
if (addr == ERRCORE)
return (NULL);
if ((((uintptr_t)addr) % ALIGN) != 0)
offset = ALIGN - (uintptr_t)addr % ALIGN;
else
offset = 0;
#ifndef SEGMENTED
if (addr == Baddr) {
n -= WORDSIZE;
if (tp != NULL)
n -= SIZE(tp);
}
#endif
n = ((n - 1) / CORESIZE + 1) * CORESIZE;
nsize = n + offset;
if ((size_t)nsize > MAX_MALLOC - (uintptr_t)addr) {
errno = ENOMEM;
return (NULL);
}
if ((size_t)nsize <= MAX_GETCORE) {
if (GETCORE(nsize) == ERRCORE)
return (NULL);
} else {
intptr_t delta;
delta = MAX_GETCORE;
while (delta > 0) {
if (GETCORE(delta) == ERRCORE) {
if (addr != GETCORE(0))
(void) GETCORE(-MAX_GETCORE);
return (NULL);
}
nsize -= MAX_GETCORE;
delta = nsize;
}
}
if (addr == Baddr) {
ASSERT(offset == 0);
if (tp) {
addr = (char *)tp;
n += SIZE(tp) + 2 * WORDSIZE;
} else {
addr = Baddr - WORDSIZE;
n += WORDSIZE;
}
} else
addr += offset;
Baddr = addr + n;
tp = (TREE *)(uintptr_t)addr;
SIZE(tp) = n - 2 * WORDSIZE;
ASSERT((SIZE(tp) % ALIGN) == 0);
SETBIT0(SIZE(NEXT(tp)));
if (Bottom && Bottom != tp) {
SETBIT0(SIZE(Bottom));
realfree(DATA(Bottom));
}
return (tp);
}
#define LEFT1(x, y) \
if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\
if ((PARENT(y) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\
else RIGHT(PARENT(y)) = y;\
LEFT(y) = x; PARENT(x) = y
#define RIGHT1(x, y) \
if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\
if ((PARENT(y) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\
else RIGHT(PARENT(y)) = y;\
RIGHT(y) = x; PARENT(x) = y
#define BULEFT2(x, y, z) \
if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\
if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\
if ((PARENT(z) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
else RIGHT(PARENT(z)) = z;\
LEFT(z) = y; PARENT(y) = z; LEFT(y) = x; PARENT(x) = y
#define BURIGHT2(x, y, z) \
if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\
if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\
if ((PARENT(z) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
else RIGHT(PARENT(z)) = z;\
RIGHT(z) = y; PARENT(y) = z; RIGHT(y) = x; PARENT(x) = y
#define TDLEFT2(x, y, z) \
if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\
if ((PARENT(z) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
else RIGHT(PARENT(z)) = z;\
PARENT(x) = z; LEFT(z) = x;
#define TDRIGHT2(x, y, z) \
if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\
if ((PARENT(z) = PARENT(x)) != NULL)\
if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
else RIGHT(PARENT(z)) = z;\
PARENT(x) = z; RIGHT(z) = x;
static void
t_delete(TREE *op)
{
TREE *tp, *sp, *gp;
if (ISNOTREE(op)) {
tp = LINKBAK(op);
if ((sp = LINKFOR(op)) != NULL)
LINKBAK(sp) = tp;
LINKFOR(tp) = sp;
return;
}
if (PARENT(op))
t_splay(op);
if ((tp = LINKFOR(op)) != NULL) {
PARENT(tp) = NULL;
if ((sp = LEFT(op)) != NULL)
PARENT(sp) = tp;
LEFT(tp) = sp;
if ((sp = RIGHT(op)) != NULL)
PARENT(sp) = tp;
RIGHT(tp) = sp;
Root = tp;
return;
}
if ((tp = LEFT(op)) != NULL) {
PARENT(tp) = NULL;
if (RIGHT(op)) {
while ((sp = RIGHT(tp)) != NULL) {
if ((gp = RIGHT(sp)) != NULL) {
TDLEFT2(tp, sp, gp);
tp = gp;
} else {
LEFT1(tp, sp);
tp = sp;
}
}
RIGHT(tp) = RIGHT(op);
PARENT(RIGHT(tp)) = tp;
}
} else if ((tp = RIGHT(op)) != NULL)
PARENT(tp) = NULL;
Root = tp;
}
static void
t_splay(TREE *tp)
{
TREE *pp, *gp;
while ((pp = PARENT(tp)) != NULL) {
gp = PARENT(pp);
if (LEFT(pp) == tp) {
if (gp && LEFT(gp) == pp) {
BURIGHT2(gp, pp, tp);
} else {
RIGHT1(pp, tp);
}
} else {
ASSERT(RIGHT(pp) == tp);
if (gp && RIGHT(gp) == pp) {
BULEFT2(gp, pp, tp);
} else {
LEFT1(pp, tp);
}
}
}
}
void
free(void *old)
{
if (!primary_link_map) {
errno = ENOTSUP;
return;
}
assert_no_libc_locks_held();
(void) mutex_lock(&libc_malloc_lock);
_free_unlocked(old);
(void) mutex_unlock(&libc_malloc_lock);
}
void
_free_unlocked(void *old)
{
int i;
if (old == NULL)
return;
if (old == Lfree)
return;
if (!ISBIT0(SIZE(BLOCK(old))))
return;
for (i = 0; i < freeidx; i++)
if (old == flist[i])
return;
if (flist[freeidx] != NULL)
realfree(flist[freeidx]);
flist[freeidx] = Lfree = old;
freeidx = (freeidx + 1) & FREEMASK;
}
static void
cleanfree(void *ptr)
{
char **flp;
flp = (char **)&(flist[freeidx]);
for (;;) {
if (flp == (char **)&(flist[0]))
flp = (char **)&(flist[FREESIZE]);
if (*--flp == NULL)
break;
if (*flp != ptr)
realfree(*flp);
*flp = NULL;
}
freeidx = 0;
Lfree = NULL;
}