
use mem
use core

@shared unused_blocks: [ptr x 128]
shared pool_lock : core:Mutex[void] (core:Mutex[void].new())

struct Block {
    next: ?ptr
    size: uint
    used: uint
    used_percent: uint
    isize: uint
}

fn loop_all_blocks(func: fnRef(Block)()) {
    let i : uint = 0
    let last = poolc
    while i < last {
        let pool = @ptrv(pools, Pool, i++)
        let block : ?Block = pool.first.@cast(?Block)
        while isset(block) {
            let next = block.next.@cast(?Block)
            func(block)
            block = next
        }
    }
}

fn add_unused_block(block: Block) {
    block.next = null
    let index = get_pool_index_for_size(block.isize)
    let list = @ptrv(unused_blocks, ?Bump, index)
    if !isset(list) {
        list = Bump.new(32)
        @ptrv(unused_blocks, ?Bump, index) = list
    }
    list.add_ptr(block)
}

fn get_unused_block(isize: uint) ptr !none {
    let index = get_pool_index_for_size(isize)
    let list = @ptrv(unused_blocks, ?Bump, index)
    if !isset(list) : throw none
    if list.index == 0 : throw none
    pool_lock.lock()
    if list.index == 0 {
        pool_lock.unlock()
        throw none
    }
    list.index -= size_of(ptr)
    let block = @ptrv(list.data, Block, list.index / size_of(ptr))
    pool_lock.unlock()
    return block.@cast(ptr)
}

fn free_empty_unused_blocks() {
    pool_lock.lock()
    let i : uint = 0
    while i < 128 {
        let list = @ptrv(unused_blocks, ?Bump, i++)
        if !isset(list) : continue
        let start = list.data
        let end = list.data + list.index
        list.reset()
        while start < end {
            let block = @ptrv(start, Block)
            if block.used == 0 {
                mem:free(block)
            } else {
                list.add_ptr(block)
            }
            start += size_of(ptr)
        }
    }
    pool_lock.unlock()
}
