#include <stdlib.h>
#include <stddef.h>
#include "rope.h"
#include "bigalloc.h"
#define MAXIMUM(a, b) (a) > (b) ? a : b
static void
alloc_decref(rdb_ALLOCATOR *abase)
{
lcb_list_t *llcur, *llnext;
rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase;
if (--alloc->refcount) {
return;
}
LCB_LIST_SAFE_FOR(llcur, llnext, (lcb_list_t *)&alloc->bufs) {
rdb_ROPESEG *seg = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode);
lcb_clist_delete(&alloc->bufs, &seg->llnode);
free(seg->root);
free(seg);
}
free(alloc);
}
static void
recheck_thresholds(rdb_BIGALLOC *alloc)
{
if (++alloc->n_requests % RDB_BIGALLOC_RECHECK_RATE) {
return;
}
alloc->total_requests += alloc->n_requests;
alloc->total_toobig += alloc->n_toobig;
alloc->total_toosmall += alloc->n_toosmall;
if (alloc->n_toobig == alloc->n_toosmall) {
} else if (alloc->n_toobig > alloc->n_toosmall) {
if (alloc->n_toobig*2 > alloc->n_toosmall) {
alloc->min_blk_alloc *= 2;
alloc->max_blk_alloc *= 2;
}
} else if (alloc->n_toosmall > alloc->n_toobig) {
if (alloc->n_toosmall*2 > alloc->n_toobig) {
alloc->min_blk_alloc /= 2;
alloc->max_blk_alloc /= 2;
}
}
alloc->n_requests = 0;
alloc->n_toobig = 0;
alloc->n_toosmall = 0;
}
static rdb_ROPESEG *
seg_alloc(rdb_ALLOCATOR *abase, unsigned size)
{
lcb_list_t *llcur;
rdb_ROPESEG *newseg = NULL;
rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase;
recheck_thresholds(alloc);
if (size > alloc->max_blk_alloc) {
alloc->n_toobig++;
alloc->total_malloc++;
newseg = calloc(1, sizeof(*newseg));
newseg->root = malloc(size);
newseg->nalloc = size;
goto GT_RETNEW;
} else if (size < alloc->min_blk_alloc) {
alloc->n_toosmall++;
}
LCB_LIST_FOR(llcur, (lcb_list_t *)&alloc->bufs) {
rdb_ROPESEG *cur = LCB_LIST_ITEM(llcur, rdb_ROPESEG, llnode);
if (cur->nalloc < size) {
continue;
}
newseg = cur;
lcb_clist_delete(&alloc->bufs, llcur);
break;
}
if (!newseg) {
unsigned newsize = alloc->min_blk_alloc;
if (LCB_CLIST_SIZE(&alloc->bufs) >= alloc->max_blk_count) {
lcb_list_t *llold = lcb_clist_pop(&alloc->bufs);
newseg = LCB_LIST_ITEM(llold, rdb_ROPESEG, llnode);
free(newseg->root);
} else {
newseg = calloc(1, sizeof(*newseg));
alloc->total_malloc++;
}
while (newsize < size) {
newsize = (unsigned) ((double)newsize * 1.5);
}
newseg->root = malloc(newsize);
newseg->nalloc = newsize;
}
GT_RETNEW:
newseg->shflags = RDB_ROPESEG_F_LIB;
newseg->allocator = abase;
newseg->allocid = RDB_ALLOCATOR_CHUNKED;
newseg->start = 0;
newseg->nused = 0;
alloc->refcount++;
return newseg;
}
static void
buf_reserve(rdb_pALLOCATOR abase, rdb_ROPEBUF *buf, unsigned size)
{
rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase;
rdb_ROPESEG *newseg, *lastseg;
lastseg = RDB_SEG_LAST(buf);
if (lastseg && RDB_SEG_SPACE(lastseg) + buf->nused >= size) {
return;
}
newseg = seg_alloc(&alloc->base, size);
lcb_list_append(&buf->segments, &newseg->llnode);
}
static rdb_ROPESEG *
seg_realloc(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg, unsigned size)
{
rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase;
if (size < alloc->n_toosmall) {
alloc->n_toosmall++;
} else if (size > alloc->n_toobig) {
alloc->n_toobig++;
}
seg->root = realloc(seg->root, size);
seg->nalloc = size;
alloc->total_malloc++;
recheck_thresholds((rdb_BIGALLOC *)abase);
return seg;
}
static void
seg_release(rdb_ALLOCATOR *abase, rdb_ROPESEG *seg)
{
rdb_BIGALLOC *alloc = (rdb_BIGALLOC *)abase;
if (LCB_CLIST_SIZE(&alloc->bufs) >= alloc->max_blk_count ||
seg->nalloc > alloc->max_blk_alloc ||
seg->nalloc < alloc->min_blk_alloc) {
free(seg->root);
free(seg);
} else {
lcb_clist_prepend(&alloc->bufs, &seg->llnode);
}
alloc_decref(abase);
}
static void
dump_wrap(rdb_pALLOCATOR alloc, FILE *fp) { rdb_bigalloc_dump((rdb_BIGALLOC*)alloc, fp); }
rdb_ALLOCATOR *
rdb_bigalloc_new(void)
{
rdb_ALLOCATOR *abase;
rdb_BIGALLOC *alloc = calloc(1, sizeof(*alloc));
lcb_clist_init(&alloc->bufs);
alloc->max_blk_alloc = RDB_BIGALLOC_ALLOCSZ_MAX;
alloc->min_blk_alloc = RDB_BIGALLOC_ALLOCSZ_MIN;
alloc->max_blk_count = RDB_BIGALLOC_BLKCNT_MAX;
alloc->refcount = 1;
abase = &alloc->base;
abase->r_reserve = buf_reserve;
abase->s_release = seg_release;
abase->s_alloc = seg_alloc;
abase->s_realloc = seg_realloc;
abase->a_release = alloc_decref;
abase->dump = dump_wrap;
return &alloc->base;
}
void
rdb_bigalloc_dump(rdb_BIGALLOC *alloc, FILE *fp)
{
static const char *indent = " ";
fprintf(fp, "BIGALLOC @%p\n", (void *)alloc);
fprintf(fp, "%sPooled Blocks: %lu\n", indent, LCB_CLIST_SIZE(&alloc->bufs));
fprintf(fp, "%sMinAlloc: %u\n", indent, alloc->min_blk_alloc);
fprintf(fp, "%sMaxAlloc: %u\n", indent, alloc->max_blk_alloc);
fprintf(fp, "%sMaxBlocks: %u\n", indent, alloc->max_blk_count);
fprintf(fp, "%sTotalMalloc: %u\n", indent, alloc->total_malloc);
fprintf(fp, "%sTotalRequests: %u\n", indent, alloc->total_requests);
fprintf(fp, "%sTotalToobig: %u\n", indent, alloc->total_toobig);
fprintf(fp, "%sTotalToosmall: %u\n", indent, alloc->total_toosmall);
}