//
// $Id: PointerRefTable.m,v 1.3 2005/05/18 06:46:54 will_mason Exp $
//
// vi: set ft=objc:

/*
 * ObjectiveLib - a library of containers and algorithms for Objective-C
 *
 * Copyright (c) 2004
 * Will Mason
 *
 * Portions:
 *
 * Copyright (c) 1994
 * Hewlett-Packard Company
 *
 * Copyright (c) 1996,1997
 * Silicon Graphics Computer Systems, Inc.
 *
 * Copyright (c) 1997
 * Moscow Center for SPARC Technology
 *
 * Copyright (c) 1999 
 * Boris Fomitchev
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * You may contact the author at will_mason@users.sourceforge.net.
 */

#import "PointerRefTable.h"
#import "Macros.h"
#import <stddef.h>

typedef struct _OLPointerRefNode
{
    struct _OLPointerRefNode*    next;
    void*                          object;
    uint32_t                    handle;
} OLPointerRefNode;

@interface OLRefBucketHead : NSObject
{
@private
    OLPointerRefNode* node;
}

- (id) initWithNode: (OLPointerRefNode*)nde;

- (OLPointerRefNode*)node;
- (void) setNode: (OLPointerRefNode*)node;

@end

@implementation OLRefBucketHead

- (id) initWithNode: (OLPointerRefNode*)nde
{
    [super init];
    node = nde;
    return self;
}

- (OLPointerRefNode*)node
{
    return node;
}

- (void) setNode: (OLPointerRefNode*)nde
{
    node = nde;
}

@end

@implementation OLPointerRefTable

- (id) initWithAllocator: (OLAllocator*)alloc
{
    unsigned i;
    unsigned capacity;
    OLRefBucketHead* buck;

    [super init];
    allocator = OBJ_RETAIN(alloc);
    capacity = [self nextSize: 30];
    buckets = [[allocator allocateObject: [OLVector class]]
        initWithAllocator: allocator];
    [buckets reserve: capacity];
    for (i = 0; i < capacity; i++)
    {
        buck = [[allocator allocateObject: [OLRefBucketHead class]]
            initWithNode: NULL];
        [buckets pushBack: buck];
        [allocator deallocateObject: buck];
    }
    currentHandle = 0;
    return self;
}

- (void) dealloc
{
    OLPointerRefNode* cur;
    OLPointerRefNode* next;
    unsigned size = [buckets size];
    unsigned i;

    for (i = 0; i < size; i++)
    {
        cur = [[buckets at: i] node];
        while (cur != NULL)
        {
            next = cur->next;
            [allocator deallocateBytes: cur];
            cur = next;
        }
    }
    OBJ_RELEASE(buckets);
    OBJ_RELEASE(allocator);
    [super dealloc];
}

- (uint32_t) assign: (void*)object
{
    OLPointerRefNode* first;
    OLPointerRefNode* cur;
    BOOL insertOkay = YES;
    unsigned bucket = [self bucketOfPointer: object];
    uint32_t theHandle = currentHandle;

    first = [[buckets at: bucket] node];
    for (cur = first; cur != NULL; cur = cur->next)
    {
        if (cur->object == object)
        {
            insertOkay = NO;
            break;
        }
    }
    if (insertOkay)
    {
        cur = [allocator allocateBytes: sizeof(OLPointerRefNode)];
        cur->object = object;
        cur->handle = currentHandle++;
        cur->next = first;
        [[buckets at: bucket] setNode: cur];
    }
    return theHandle;
}

- (unsigned) bucketOfPointer: (void*)object
{
    return [self bucketOfPointer: object size: [buckets size]];
}

- (unsigned) bucketOfPointer: (void*)object size: (unsigned)size
{
    return (unsigned)object % size;
}

- (unsigned) capacity
{
    return [buckets size];
}

- (uint32_t) lookUp: (void*)object
{
    OLPointerRefNode* node = [[buckets at: [self bucketOfPointer: object]] node];

    while (node != NULL && node->object != object)
        node = node->next;
    return (node == NULL) ? UINT32_MAX : node->handle;
}

- (unsigned) nextSize: (unsigned)size
{
    unsigned newSize = 1;
    unsigned oldSize = 1;
    unsigned tempSize;

    while (newSize < size)
    {
        tempSize = oldSize;
        oldSize = newSize;
        newSize += tempSize;
    }
    newSize |= 1;
    return newSize;
}

- (void) resize: (unsigned)hint
{
    OLVector* tmp;
    OLPointerRefNode* node;
    OLRefBucketHead* head;
    unsigned oldSize = [buckets size];
    unsigned newSize;
    unsigned i;
    unsigned bucket;

    if (hint > oldSize)
    {
        newSize = [self nextSize: hint];
        if (newSize > oldSize)
        {
            tmp = [[allocator allocateObject: [OLVector class]]
                initWithAllocator: allocator];
            [tmp reserve: newSize];
            for (i = 0; i < newSize; i++)
            {
                head = [[allocator allocateObject: [OLRefBucketHead class]]
                    initWithNode: NULL];
                [tmp pushBack: head];
                [allocator deallocateObject: head];
            }
            for (i = 0; i < oldSize; i++)
            {
                node = [[buckets at: i] node];
                while (node != NULL)
                {
                    bucket = [self bucketOfPointer: node->object size: newSize];
                    [[buckets at: i] setNode: node->next];
                    node->next = [[tmp at: bucket] node];
                    [[tmp at: bucket] setNode: node];
                    node = [[buckets at: i] node];
                }
            }
            [buckets swap: tmp];
            [allocator deallocateObject: tmp];
        }
    }
}

@end
