#include <sys/types.h>
#ifndef debug
#define NDEBUG
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "assert.h"
#include "malloc.h"
#include "mallint.h"
#include <thread.h>
#include <pthread.h>
#include <synch.h>
#include <unistd.h>
#include <limits.h>
static mutex_t mlock = DEFAULTMUTEX;
static ssize_t freespace(struct holdblk *);
static void *malloc_unlocked(size_t, int);
static void *realloc_unlocked(void *, size_t);
static void free_unlocked(void *);
static void *morecore(size_t);
static struct header arena[4] = {
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}
};
#define freeptr (arena + 2)
static struct header *arenaend;
static struct header *lastblk;
static struct holdblk **holdhead;
static int numlblks = NUMLBLKS;
static int minhead = MINHEAD;
static int change = 0;
static int fastct = FASTCT;
static unsigned int maxfast = MAXFAST;
static int grain = ALIGNSZ;
#ifdef debug
static int case1count = 0;
static void
checkq(void)
{
register struct header *p;
p = &freeptr[0];
while (p != &freeptr[1]) {
p = p->nextfree;
assert(p->prevfree->nextfree == p);
}
while (p != &freeptr[0]) {
p = p->prevfree;
assert(p->nextfree->prevfree == p);
}
}
#endif
void *
malloc(size_t nbytes)
{
void *ret;
(void) mutex_lock(&mlock);
ret = malloc_unlocked(nbytes, 0);
(void) mutex_unlock(&mlock);
return (ret);
}
void *
memalign(size_t alignment, size_t size)
{
void *alloc_buf;
struct header *hd;
size_t alloc_size;
uintptr_t fr;
static int realloc;
if (size == 0 || alignment == 0 ||
(alignment & (alignment - 1)) != 0) {
return (NULL);
}
if (alignment <= ALIGNSZ)
return (malloc(size));
alloc_size = size + alignment;
if (alloc_size < size) {
return (NULL);
}
(void) mutex_lock(&mlock);
alloc_buf = malloc_unlocked(alloc_size, 1);
(void) mutex_unlock(&mlock);
if (alloc_buf == NULL)
return (NULL);
fr = (uintptr_t)alloc_buf;
fr = (fr + alignment - 1) / alignment * alignment;
if (fr == (uintptr_t)alloc_buf)
return (alloc_buf);
if ((fr - (uintptr_t)alloc_buf) <= HEADSZ) {
realloc++;
free(alloc_buf);
alloc_size = size + alignment*2;
if (alloc_size < size) {
return (NULL);
}
(void) mutex_lock(&mlock);
alloc_buf = malloc_unlocked(alloc_size, 1);
(void) mutex_unlock(&mlock);
if (alloc_buf == NULL)
return (NULL);
fr = (uintptr_t)alloc_buf;
fr = (fr + alignment - 1) / alignment * alignment;
if (fr == (uintptr_t)alloc_buf)
return (alloc_buf);
if ((fr - (uintptr_t)alloc_buf) <= HEADSZ) {
fr = fr + alignment;
}
}
hd = (struct header *)((char *)fr - minhead);
(void) mutex_lock(&mlock);
hd->nextblk = ((struct header *)((char *)alloc_buf - minhead))->nextblk;
((struct header *)((char *)alloc_buf - minhead))->nextblk = SETBUSY(hd);
(void) mutex_unlock(&mlock);
free(alloc_buf);
CHECKQ
return ((void *)fr);
}
void *
valloc(size_t size)
{
static unsigned pagesize;
if (size == 0)
return (NULL);
if (!pagesize)
pagesize = sysconf(_SC_PAGESIZE);
return (memalign(pagesize, size));
}
static void *
malloc_unlocked(size_t nbytes, int nosmall)
{
struct header *blk;
size_t nb;
if (freeptr[0].nextfree == GROUND) {
arena[1].nextblk = (struct header *)BUSY;
arena[0].nextblk = (struct header *)BUSY;
lastblk = arenaend = &(arena[1]);
freeptr[0].nextfree = &(freeptr[1]);
freeptr[1].nextblk = &(arena[0]);
freeptr[1].prevfree = &(freeptr[0]);
}
if (nbytes == 0)
return (NULL);
if (nbytes <= maxfast && !nosmall) {
struct holdblk *holdblk;
struct lblk *lblk;
struct holdblk *newhold;
if (!change) {
int i;
change = 1;
maxfast = 0;
holdhead = (struct holdblk **)
malloc_unlocked(sizeof (struct holdblk *) *
(fastct + 1), 0);
if (holdhead == NULL)
return (malloc_unlocked(nbytes, 0));
for (i = 1; i <= fastct; i++) {
holdhead[i] = HGROUND;
}
maxfast = fastct * grain;
}
nb = (nbytes + grain - 1) / grain * grain;
holdblk = holdhead[nb / grain];
nb = nb + MINHEAD;
if ((holdblk != HGROUND) && (holdblk->lfreeq != LGROUND)) {
lblk = holdblk->lfreeq;
if (lblk < holdblk->unused) {
if ((holdblk->lfreeq =
CLRSMAL(lblk->header.nextfree)) ==
LGROUND) {
holdhead[(nb-MINHEAD) / grain] =
holdblk->nexthblk;
}
} else if (((char *)holdblk->unused + nb) <
((char *)holdblk + HOLDSZ(nb))) {
holdblk->unused = (struct lblk *)
((char *)holdblk->unused+nb);
holdblk->lfreeq = holdblk->unused;
} else {
holdblk->unused = (struct lblk *)
((char *)holdblk->unused+nb);
holdblk->lfreeq = LGROUND;
holdhead[(nb-MINHEAD)/grain] =
holdblk->nexthblk;
}
lblk->header.holder = (struct holdblk *)SETALL(holdblk);
} else {
newhold = (struct holdblk *)
malloc_unlocked(HOLDSZ(nb), 0);
if ((char *)newhold == NULL) {
return (NULL);
}
if (holdblk != HGROUND) {
newhold->nexthblk = holdblk;
newhold->prevhblk = holdblk->prevhblk;
holdblk->prevhblk = newhold;
newhold->prevhblk->nexthblk = newhold;
} else {
newhold->nexthblk = newhold->prevhblk = newhold;
}
holdhead[(nb-MINHEAD)/grain] = newhold;
lblk = (struct lblk *)(newhold->space);
newhold->lfreeq = newhold->unused =
(struct lblk *)((char *)newhold->space+nb);
lblk->header.holder = (struct holdblk *)SETALL(newhold);
newhold->blksz = nb-MINHEAD;
}
#ifdef debug
assert(((struct holdblk *)CLRALL(lblk->header.holder))->blksz >=
nbytes);
#endif
return ((char *)lblk + MINHEAD);
} else {
struct header *newblk;
nb = nbytes + minhead;
nb = (nb + ALIGNSZ - 1) / ALIGNSZ * ALIGNSZ;
nb = (nb > MINBLKSZ) ? nb : MINBLKSZ;
if ((freeptr[1].nextblk-&(freeptr[1])) < nb) {
return (NULL);
} else {
struct header *next;
struct header *nextnext;
blk = freeptr;
do {
blk = blk->nextfree;
next = blk->nextblk;
if (!TESTBUSY(nextnext = next->nextblk)) {
do {
DELFREEQ(next);
next = nextnext;
nextnext = next->nextblk;
} while (!TESTBUSY(nextnext));
if (next >= arenaend)
lastblk = blk;
blk->nextblk = next;
}
} while (((char *)(next) - (char *)blk) < nb);
}
if (blk == &(freeptr[1])) {
struct header *newend;
ssize_t nget;
if ((newblk = (struct header *)sbrk(0)) !=
(struct header *)((char *)arenaend + HEADSZ)) {
#ifdef debug
if (case1count++ > 0)
(void) write(2, "Case 1 hit more that once."
" brk or sbrk?\n", 41);
#endif
nget = nb + HEADSZ;
nget = (nget + BLOCKSZ - 1)/BLOCKSZ * BLOCKSZ;
assert((uintptr_t)newblk % ALIGNSZ == 0);
if (morecore(nget) == (void *)-1)
return (NULL);
newend = (struct header *)((char *)newblk + nget
- HEADSZ);
assert((uintptr_t)newblk % ALIGNSZ == 0);
newend->nextblk = SETBUSY(&(arena[1]));
newblk->nextblk = newend;
arenaend->nextblk = SETBUSY(newblk);
arenaend = newend;
lastblk = newblk;
blk = newblk;
} else if (TESTBUSY(lastblk->nextblk)) {
nget = (nb + BLOCKSZ - 1) / BLOCKSZ * BLOCKSZ;
if (morecore(nget) == (void *)-1)
return (NULL);
assert(((uintptr_t)newblk%ALIGNSZ) == 0);
newend =
(struct header *)((char *)arenaend+nget);
newend->nextblk = SETBUSY(&(arena[1]));
arenaend->nextblk = newend;
lastblk = blk = arenaend;
arenaend = newend;
} else {
nget = nb -
((char *)arenaend - (char *)lastblk);
nget = (nget + (BLOCKSZ - 1)) /
BLOCKSZ * BLOCKSZ;
assert(((uintptr_t)newblk % ALIGNSZ) == 0);
if (morecore(nget) == (void *)-1)
return (NULL);
newend = (struct header *)
((char *)arenaend + nget);
arenaend = lastblk->nextblk = newend;
newend->nextblk = SETBUSY(&(arena[1]));
blk = lastblk;
DELFREEQ(blk);
}
} else {
struct header *nblk;
DELFREEQ(blk);
nblk = blk->nextfree;
if (nblk != &(freeptr[1])) {
MOVEHEAD(nblk);
}
}
if (((char *)blk->nextblk - (char *)blk) - nb >= MINBLKSZ) {
newblk = (struct header *)((char *)blk + nb);
newblk->nextblk = blk->nextblk;
blk->nextblk = SETBUSY(newblk);
ADDFREEQ(newblk);
if (blk == lastblk)
lastblk = newblk;
} else {
blk->nextblk = SETBUSY(blk->nextblk);
}
}
CHECKQ
assert((char *)CLRALL(blk->nextblk) -
((char *)blk + minhead) >= nbytes);
assert((char *)CLRALL(blk->nextblk) -
((char *)blk + minhead) < nbytes + MINBLKSZ);
return ((char *)blk + minhead);
}
void
free(void *ptr)
{
(void) mutex_lock(&mlock);
free_unlocked(ptr);
(void) mutex_unlock(&mlock);
}
void
free_unlocked(void *ptr)
{
struct holdblk *holdblk;
struct holdblk *oldhead;
if (ptr == NULL)
return;
if (TESTSMAL(((struct header *)((char *)ptr - MINHEAD))->nextblk)) {
struct lblk *lblk;
ssize_t offset;
lblk = (struct lblk *)CLRBUSY((char *)ptr - MINHEAD);
assert((struct header *)lblk < arenaend);
assert((struct header *)lblk > arena);
holdblk = lblk->header.holder;
if (!TESTBUSY(holdblk))
return;
holdblk = (struct holdblk *)CLRALL(holdblk);
lblk->header.nextfree = SETSMAL(holdblk->lfreeq);
holdblk->lfreeq = lblk;
offset = holdblk->blksz / grain;
oldhead = holdhead[offset];
if (oldhead != holdblk) {
holdhead[offset] = holdblk;
holdblk->nexthblk->prevhblk = holdblk->prevhblk;
holdblk->prevhblk->nexthblk = holdblk->nexthblk;
holdblk->nexthblk = oldhead;
holdblk->prevhblk = oldhead->prevhblk;
oldhead->prevhblk = holdblk;
holdblk->prevhblk->nexthblk = holdblk;
}
} else {
struct header *blk;
struct header *next;
struct header *nextnext;
blk = (struct header *)((char *)ptr - minhead);
next = blk->nextblk;
if (!TESTBUSY(next))
return;
blk->nextblk = next = CLRBUSY(next);
ADDFREEQ(blk);
if (!TESTBUSY(nextnext = next->nextblk)) {
do {
DELFREEQ(next);
next = nextnext;
} while (!TESTBUSY(nextnext = next->nextblk));
if (next == arenaend) lastblk = blk;
blk->nextblk = next;
}
}
CHECKQ
}
void *
realloc(void *ptr, size_t size)
{
void *retval;
(void) mutex_lock(&mlock);
retval = realloc_unlocked(ptr, size);
(void) mutex_unlock(&mlock);
return (retval);
}
static void *
realloc_unlocked(void *ptr, size_t size)
{
struct header *blk;
size_t trusize;
char *newptr;
size_t cpysize;
struct header *next;
if (ptr == NULL)
return (malloc_unlocked(size, 0));
if (size == 0) {
free_unlocked(ptr);
return (NULL);
}
if (TESTSMAL(((struct lblk *)((char *)ptr - MINHEAD))->
header.holder)) {
newptr = malloc_unlocked(size, 0);
if (newptr == NULL)
return (NULL);
if ((char *)ptr != newptr) {
struct lblk *lblk;
lblk = (struct lblk *)((char *)ptr - MINHEAD);
cpysize = ((struct holdblk *)
CLRALL(lblk->header.holder))->blksz;
cpysize = (size > cpysize) ? cpysize : size;
(void) memcpy(newptr, ptr, cpysize);
free_unlocked(ptr);
}
} else {
blk = (struct header *)((char *)ptr - minhead);
next = blk->nextblk;
if (!TESTBUSY(next)) {
DELFREEQ(blk);
blk->nextblk = SETBUSY(next);
}
next = CLRBUSY(next);
if (!TESTBUSY(next->nextblk)) {
do {
DELFREEQ(next);
next = next->nextblk;
} while (!TESTBUSY(next->nextblk));
blk->nextblk = SETBUSY(next);
if (next >= arenaend) lastblk = blk;
}
trusize = size+minhead;
trusize = (trusize + ALIGNSZ - 1)/ALIGNSZ*ALIGNSZ;
trusize = (trusize >= MINBLKSZ) ? trusize : MINBLKSZ;
cpysize = (char *)next - (char *)blk;
if (cpysize >= trusize) {
struct header *newblk;
if (cpysize - trusize >= MINBLKSZ) {
newblk = (struct header *)((char *)blk +
trusize);
newblk->nextblk = next;
blk->nextblk = SETBUSY(newblk);
ADDFREEQ(newblk);
if (blk == lastblk)
lastblk = newblk;
}
newptr = ptr;
} else {
cpysize = (size > cpysize) ? cpysize : size;
newptr = malloc_unlocked(size, 0);
if (newptr == NULL)
return (NULL);
(void) memcpy(newptr, ptr, cpysize);
free_unlocked(ptr);
}
}
return (newptr);
}
void *
calloc(size_t num, size_t size)
{
char *mp;
size_t total;
if (num == 0 || size == 0) {
total = 0;
} else {
total = num * size;
if ((total / num) != size) {
errno = ENOMEM;
return (NULL);
}
}
mp = malloc(total);
if (mp == NULL)
return (NULL);
(void) memset(mp, 0, total);
return (mp);
}
int
mallopt(int cmd, int value)
{
(void) mutex_lock(&mlock);
if (change) {
(void) mutex_unlock(&mlock);
return (1);
}
switch (cmd) {
case M_MXFAST:
if (value < 0) {
(void) mutex_unlock(&mlock);
return (1);
}
fastct = (value + grain - 1) / grain;
maxfast = grain*fastct;
break;
case M_NLBLKS:
if (value <= 1) {
(void) mutex_unlock(&mlock);
return (1);
}
numlblks = value;
break;
case M_GRAIN:
if (value <= 0) {
(void) mutex_unlock(&mlock);
return (1);
}
grain = (value + ALIGNSZ - 1)/ALIGNSZ*ALIGNSZ;
fastct = (maxfast + grain - 1) / grain;
maxfast = grain * fastct;
break;
case M_KEEP:
if (change && holdhead != NULL) {
(void) mutex_unlock(&mlock);
return (1);
}
minhead = HEADSZ;
break;
default:
(void) mutex_unlock(&mlock);
return (1);
}
(void) mutex_unlock(&mlock);
return (0);
}
struct mallinfo
mallinfo(void)
{
struct header *blk, *next;
struct holdblk *hblk;
struct mallinfo inf;
int i;
ssize_t size;
ssize_t fsp;
(void) mutex_lock(&mlock);
(void) memset(&inf, 0, sizeof (struct mallinfo));
if (freeptr[0].nextfree == GROUND) {
(void) mutex_unlock(&mlock);
return (inf);
}
blk = CLRBUSY(arena[1].nextblk);
inf.arena = (char *)arenaend - (char *)blk;
next = CLRBUSY(blk->nextblk);
while (next != &(arena[1])) {
inf.ordblks++;
size = (char *)next - (char *)blk;
if (TESTBUSY(blk->nextblk)) {
inf.uordblks += size;
inf.keepcost += HEADSZ-MINHEAD;
} else {
inf.fordblks += size;
}
blk = next;
next = CLRBUSY(blk->nextblk);
}
if (change && holdhead != NULL) {
for (i = fastct; i > 0; i--) {
hblk = holdhead[i];
if (hblk != HGROUND) {
size = hblk->blksz +
sizeof (struct lblk) - sizeof (int);
do {
inf.hblks++;
fsp = freespace(hblk);
inf.fsmblks += fsp;
inf.usmblks += numlblks*size - fsp;
inf.smblks += numlblks;
hblk = hblk->nexthblk;
} while (hblk != holdhead[i]);
}
}
}
inf.hblkhd = (inf.smblks / numlblks) * sizeof (struct holdblk);
inf.ordblks -= inf.hblks;
inf.uordblks -= inf.hblkhd + inf.usmblks + inf.fsmblks;
inf.keepcost -= inf.hblks*(HEADSZ - MINHEAD);
(void) mutex_unlock(&mlock);
return (inf);
}
static ssize_t
freespace(struct holdblk *holdblk)
{
struct lblk *lblk;
ssize_t space = 0;
ssize_t size;
struct lblk *unused;
lblk = CLRSMAL(holdblk->lfreeq);
size = holdblk->blksz + sizeof (struct lblk) - sizeof (int);
unused = CLRSMAL(holdblk->unused);
while ((lblk != LGROUND) && (lblk != unused)) {
space += size;
lblk = CLRSMAL(lblk->header.nextfree);
}
space += ((char *)holdblk + HOLDSZ(size)) - (char *)unused;
return (space);
}
static void *
morecore(size_t bytes)
{
void * ret;
if (bytes > LONG_MAX) {
intptr_t wad;
if (bytes == ULONG_MAX)
return ((void *)-1);
ret = sbrk(0);
wad = LONG_MAX;
while (wad > 0) {
if (sbrk(wad) == (void *)-1) {
if (ret != sbrk(0))
(void) sbrk(-LONG_MAX);
return ((void *)-1);
}
bytes -= LONG_MAX;
wad = bytes;
}
} else
ret = sbrk(bytes);
return (ret);
}
#ifdef debug
int
check_arena(void)
{
struct header *blk, *prev, *next;
(void) mutex_lock(&mlock);
if (freeptr[0].nextfree == GROUND) {
(void) mutex_unlock(&mlock);
return (-1);
}
blk = arena + 1;
blk = (struct header *)CLRALL(blk->nextblk);
next = (struct header *)CLRALL(blk->nextblk);
while (next != arena + 1) {
assert(blk >= arena + 1);
assert(blk <= lastblk);
assert(next >= blk + 1);
assert(((uintptr_t)((struct header *)blk->nextblk) &
(4 | SMAL)) == 0);
if (TESTBUSY(blk->nextblk) == 0) {
assert(blk->nextfree >= freeptr);
assert(blk->prevfree >= freeptr);
assert(blk->nextfree <= lastblk);
assert(blk->prevfree <= lastblk);
assert(((uintptr_t)((struct header *)blk->nextfree) &
7) == 0);
assert(((uintptr_t)((struct header *)blk->prevfree) &
7) == 0 || blk->prevfree == freeptr);
}
blk = next;
next = CLRBUSY(blk->nextblk);
}
(void) mutex_unlock(&mlock);
return (0);
}
#define RSTALLOC 1
#endif
#ifdef RSTALLOC
void
rstalloc(void)
{
(void) mutex_lock(&mlock);
minhead = MINHEAD;
grain = ALIGNSZ;
numlblks = NUMLBLKS;
fastct = FASTCT;
maxfast = MAXFAST;
change = 0;
if (freeptr[0].nextfree == GROUND) {
(void) mutex_unlock(&mlock);
return;
}
brk(CLRBUSY(arena[1].nextblk));
freeptr[0].nextfree = GROUND;
#ifdef debug
case1count = 0;
#endif
(void) mutex_unlock(&mlock);
}
#endif
void
cfree(void *p, size_t num, size_t size)
{
free(p);
}
static void
malloc_prepare()
{
(void) mutex_lock(&mlock);
}
static void
malloc_release()
{
(void) mutex_unlock(&mlock);
}
#pragma init(malloc_init)
static void
malloc_init(void)
{
(void) pthread_atfork(malloc_prepare, malloc_release, malloc_release);
}