#include "pool.hpp"


HOOK(PRE_IO, HOOK_PRIO_MID) {
    int slot = 0;
    for (size_t s = SLAB_POOL_MIN; slot < SLAB_POOL_NUM; slot++, s*=2) {
        log(debug, "slab pool %d/%d: %zu bytes", slot+1, SLAB_POOL_NUM, s);
    }
}


SlabPool slab_pool;


SlabPool::SlabPool() {
    memset(slots, 0, sizeof(slots));
}


SlabPool::~SlabPool() {
    for (int i = 0; i<SLAB_POOL_NUM; i++) {
        while (slots[i]) {
            slab_node_t* node = slots[i];
            slots[i] = node->next;
            free(node);
        }
    }
}


void* SlabPool::pop(size_t len) {
    assert(len);
    stats_inc(spool_allocs);
    #if MAX_POOL_SPARE == 0
        #ifndef DEBUG
            MESSAGE(slab pool disabled)
        #endif
        return malloc(len);
    #endif

    len += sizeof(slab_node_t);

    int slot = 0;
    for (size_t s = SLAB_POOL_MIN; slot < SLAB_POOL_NUM; slot++, s*=2) {
        if (s >= len) break;
    }

    slab_node_t* rv;
    if (slot < SLAB_POOL_NUM && slots[slot]) {
        rv = slots[slot];
        slots[slot] = rv->next;
        stats_inc(spool_hits);
    } else {
        rv = (slab_node_t*)malloc(len);
        rv->slot = slot;
    }

    return (void*)((char*)rv + sizeof(slab_node_t));
}


char* SlabPool::dup(const char* s, size_t l) {
    assert(l);
    char* rv = (char*)pop(l+1);
    memcpy(rv, s, l);
    rv[l] = '\0';
    return rv;
}


void SlabPool::push(void* p) {
    #if MAX_POOL_SPARE == 0
        free(p);
        return;
    #endif
    if (!p) return;
    slab_node_t* node = (slab_node_t*)((char*)p - sizeof(slab_node_t));
    if (unlikely(node->slot >= SLAB_POOL_MIN)) {
        free(node);
    } else {
        // TODO: check if allowed wrt. MAX_POOL_SPARE
        node->next = slots[node->slot];
        slots[node->slot] = node;
    }
}


TEST(refcount) {
    RefCount<int>* ref = new RefCount<int>(42);

    ref->inc();
    int* foo = &ref->val;
    ref->dec();
    int bar = ref->val;
    TEST_ASSERT(*foo == bar && *foo == 42); // pretty pointless
    ref->dec();

    return true; // this test relies on valgrind rather on correctness checks
}