/*
 Oaxaca: Useful Cocoa Library
 Copyright (C) 2005, Niko Matsakis (niko@alum.mit.edu)
 
 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.
 */

#import "Foundation/Foundation.h"
#import "OAArena.h"
#import <objc/objc-runtime.h>

static inline int alignTo (int value, int alignment)
{
    return (value + alignment - 1) & ~(alignment-1);
}

@implementation OAArena

#pragma mark -
#pragma mark Internal Routines

- (void) allocateNewChunk:(int)size
{
    if (size < _chunkSize)
        size = _chunkSize;
        
    OAArenaMemoryChunk *nextchunk = (OAArenaMemoryChunk *)malloc (_headerSize+size);
    if (!nextchunk) {
        /* XXX throw exception: out of memory */
    }
    
    if (_tail) {
        _tail->next = nextchunk;
        _tail = nextchunk;
    }
    else {
        _head = _tail = nextchunk;
    }
    
    _currentPointer = ((char*) _tail)+_headerSize;
    _sizeRemaining = _tail->size;    
}

#pragma mark -
#pragma mark Initialization and Destruction

- init
{
    return [self initWithChunkSize:OCCHUNKSIZE alignment:OAArenaALIGNMENT];
}

- initWithChunkSize:(int)size alignment:(int)alignment;
{
    if ((self = [super init])) {
        _chunkSize = size;
        _alignment = alignment;
        _headerSize = alignTo (sizeof (OAArenaMemoryChunk), _alignment);
    }
    
    return self;
}

- (void) dealloc
{
    OAArenaMemoryChunk *p = _head;
    while (p) {
        OAArenaMemoryChunk *n = p->next;
        free (p);
        p = n;
    }
    [super dealloc];
}

#pragma mark -
#pragma mark External Methods

- (void*) allocate:(int) size
{
    size = alignTo (size, _alignment);
    if (_sizeRemaining < size) {
        [self allocateNewChunk:size];
    }
        
    char *val = _currentPointer;
    _currentPointer += size;
    _sizeRemaining -= size;
    return val;
}

- (void*) allocateZeroed:(int)bytes
{
    void *ptr = [self allocate:bytes];
    bzero (ptr, bytes);
    return ptr;
}

@end

@implementation NSObject (OAArenaExtensions)

+ allocWithArena:(OAArena*)arena
{
    int size = ((Class)self)->instance_size;
    id newObject = (id) [arena allocateZeroed:size];    
    newObject->isa = self;
    [newObject retain]; // ensure that the ref count never drops to zero!
    return newObject;
}

@end

