/*
    SuperCollider real time audio synthesis system
    Copyright (c) 2002 James McCartney. All rights reserved.
    http://www.audiosynth.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/


#include "AdvancingAllocPool.h"
#include "SC_AllocPool.h"
//#include <assert.h>


AdvancingAllocPool::AdvancingAllocPool() {
    mAllocPool = nullptr;
    mInitSize = 0;
    mGrowSize = 0;
    mTooBig = 0;
    mCurSize = 0;
    mChunks = nullptr;
    mFatties = nullptr;
}

void AdvancingAllocPool::Init(AllocPool* inAllocPool, size_t initSize, size_t growSize, size_t tooBigSize) {
    mAllocPool = inAllocPool;
    mInitSize = initSize;
    mGrowSize = growSize;
    mTooBig = tooBigSize;
    mChunks = nullptr;
    AddChunk(initSize);
    mFatties = nullptr;
    // assert(SanityCheck());
}

void AdvancingAllocPool::AddChunk(size_t inSize) {
    size_t chunkSize = sizeof(AdvancingAllocPoolChunkHdr) + inSize;
    AdvancingAllocPoolChunk* chunk = (AdvancingAllocPoolChunk*)mAllocPool->Alloc(chunkSize);
    FailNil(chunk);
    chunk->mNext = mChunks;
    mChunks = chunk;
    chunk->mSize = mGrowSize;
    mCurSize = 0;
}

void* AdvancingAllocPool::Alloc(size_t reqsize) {
    // assert(SanityCheck());
    // assert(mAllocPool);
    size_t size = (reqsize + 15) & ~15; // round up to 16 byte alignment
    if (size < mTooBig) {
        if (!mChunks)
            AddChunk(mInitSize);
        else if (mCurSize + size > mChunks->mSize)
            AddChunk(mGrowSize);
        char* space = mChunks->mSpace + mCurSize;
        mCurSize += size;

        // assert(SanityCheck());
        return (void*)space;
    } else {
        size_t chunkSize = sizeof(AdvancingAllocPoolChunkHdr) + size;
        AdvancingAllocPoolChunk* fatty = (AdvancingAllocPoolChunk*)mAllocPool->Alloc(chunkSize);
        FailNil(fatty);
        fatty->mNext = mFatties;
        mFatties = fatty;
        fatty->mSize = size;

        // assert(SanityCheck());
        return (void*)fatty->mSpace;
    }
}

void AdvancingAllocPool::FreeAll() {
    // assert(SanityCheck());
    AdvancingAllocPoolChunk *chunk, *next;
    for (chunk = mChunks; chunk; chunk = next) {
        next = chunk->mNext;
        mAllocPool->Free(chunk);
    }
    for (chunk = mFatties; chunk; chunk = next) {
        next = chunk->mNext;
        mAllocPool->Free(chunk);
    }
    mChunks = nullptr;
    mFatties = nullptr;
    mCurSize = 0;
    // assert(SanityCheck());
}

bool AdvancingAllocPool::SanityCheck() {
    AdvancingAllocPoolChunk *chunk, *next;
    for (chunk = mChunks; chunk; chunk = next) {
        next = chunk->mNext;
        mAllocPool->DoCheckInUseChunk(AllocPool::MemToChunk(chunk));
    }
    for (chunk = mFatties; chunk; chunk = next) {
        next = chunk->mNext;
        mAllocPool->DoCheckInUseChunk(AllocPool::MemToChunk(chunk));
    }
    return true;
}
