// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*-

// Copyright (c) 2001-2007 International Computer Science Institute
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software")
// to deal in the Software without restriction, subject to the conditions
// listed in the XORP LICENSE file. These conditions include: you must
// preserve this copyright notice, and you cannot mention the copyright
// holders in advertising related to the Software without their permission.
// The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
// notice is a summary of the XORP LICENSE file; the license in that file is
// legally binding.

#ident "$XORP: xorp/libxorp/ref_ptr.cc,v 1.13 2007/02/16 22:46:22 pavlin Exp $"

#include "libxorp/xorp.h"

#include "ref_ptr.hh"


///////////////////////////////////////////////////////////////////////////////
//
// Debugging macro's
//

#define POOL_PARANOIA(x) /* x */
#define VERBOSE_POOL_PARANOIA(x) /* x */

///////////////////////////////////////////////////////////////////////////////
//
// ref_counter_pool implementation
//

ref_counter_pool ref_counter_pool::_the_instance;

ref_counter_pool&
ref_counter_pool::instance()
{
    return ref_counter_pool::_the_instance;
}

void
ref_counter_pool::grow()
{
    size_t old_size = _counters.size();
    _counters.resize(old_size + old_size / 8 + 1);

    for (size_t i = old_size; i < _counters.size(); i++) {
	_counters[i] = _free_index;
	_free_index = i;
    }
    POOL_PARANOIA(check());
}

void
ref_counter_pool::check()
{
    int32_t i = _free_index;
    size_t n = 0;
    VERBOSE_POOL_PARANOIA(cout << "L: ");
    while (_counters[i] != LAST_FREE) {
	VERBOSE_POOL_PARANOIA(cout << i << " ");
	i = _counters[i];
	n++;
	if (n == _counters.size()) {
	    dump();
	    abort();
	}
    }
    VERBOSE_POOL_PARANOIA(cout << endl);
}

bool
ref_counter_pool::on_free_list(int32_t index)
{
    int32_t i = _free_index;
    size_t n = 0;
    while (_counters[i] != LAST_FREE) {
	if (i == index) {
	    return true;
	}
	i = _counters[i];
	n++;

	if (n == _counters.size()) {
	    dump();
	    abort();
	}
    }
    return false;
}

void
ref_counter_pool::dump()
{
    for (size_t i = 0; i < _counters.size(); i++) {
	cout << i << " " << _counters[i] << endl;
    }
    cout << "Free index: " << _free_index << endl;
    cout << "Balance: " << _balance << endl;
}

ref_counter_pool::ref_counter_pool()
{
    const size_t n = 1;
    _counters.resize(n);
    _counters[n - 1] = LAST_FREE;
    _free_index = 0;

    grow();
    grow();
    VERBOSE_POOL_PARANOIA(dump());
}

int32_t
ref_counter_pool::new_counter()
{
    VERBOSE_POOL_PARANOIA(dump());
    if (_counters[_free_index] == LAST_FREE) {
	grow();
    }
    POOL_PARANOIA(assert(_counters[_free_index] != LAST_FREE));
    POOL_PARANOIA(check());
    int32_t new_counter = _free_index;
    _free_index = _counters[_free_index];
    _counters[new_counter] = 1;
    ++_balance;
    POOL_PARANOIA(check());
    return new_counter;
}

int32_t
ref_counter_pool::incr_counter(int32_t index)
{
    POOL_PARANOIA(assert(on_free_list(index) == false));
    POOL_PARANOIA(check());
    assert((size_t)index < _counters.size());
    ++_counters[index];
    ++_balance;
    POOL_PARANOIA(check());
    return _counters[index];
}

int32_t
ref_counter_pool::decr_counter(int32_t index)
{
    POOL_PARANOIA(assert((size_t)index < _counters.size()));
    int32_t c = --_counters[index];
    --_balance;
    if (c == 0) {
	POOL_PARANOIA(check());
	/* recycle */
	_counters[index] = _free_index;
	_free_index = index;
	VERBOSE_POOL_PARANOIA(dump());
	POOL_PARANOIA(check());
    }
    assert(c >= 0);
    return c;
}

int32_t
ref_counter_pool::count(int32_t index)
{
    POOL_PARANOIA(assert((size_t)index < _counters.size()));
    return _counters[index];
}

///////////////////////////////////////////////////////////////////////////////
//
// cref_counter_pool implementation
//

cref_counter_pool cref_counter_pool::_the_instance;

cref_counter_pool&
cref_counter_pool::instance()
{
    return cref_counter_pool::_the_instance;
}

void
cref_counter_pool::grow()
{
    size_t old_size = _counters.size();
    _counters.resize(2 * old_size);

    for (size_t i = old_size; i < _counters.size(); i++) {
	_counters[i].count = _free_index;
	_free_index = i;
    }
}

void
cref_counter_pool::check()
{
    int32_t i = _free_index;
    size_t n = 0;
    VERBOSE_POOL_PARANOIA(cout << "L: ");
    while (_counters[i].count != LAST_FREE) {
	VERBOSE_POOL_PARANOIA(cout << i << " ");
	i = _counters[i].count;
	n++;
	if (n == _counters.size()) {
	    dump();
	    abort();
	}
    }
    VERBOSE_POOL_PARANOIA(cout << endl);
}

void
cref_counter_pool::dump()
{
    for (size_t i = 0; i < _counters.size(); i++) {
	cout << i << " " << _counters[i].count << endl;
    }
    cout << "Free index: " << _free_index << endl;
}

cref_counter_pool::cref_counter_pool()
{
    const size_t n = 1;
    _counters.resize(n);
    _free_index = 0; // first free item
    _counters[n - 1].count = LAST_FREE;
    grow();
    grow();
    VERBOSE_POOL_PARANOIA(dump());
}

int32_t
cref_counter_pool::new_counter(void* data)
{
    VERBOSE_POOL_PARANOIA(dump());
    if (_counters[_free_index].count == LAST_FREE) {
	grow();
    }
    POOL_PARANOIA(assert(_counters[_free_index].count != LAST_FREE));
    POOL_PARANOIA(check());
    int32_t new_counter = _free_index;
    _free_index = _counters[_free_index].count;
    _counters[new_counter].count = 1;
    _counters[new_counter].data = data;
    POOL_PARANOIA(check());
    return new_counter;
}

int32_t
cref_counter_pool::incr_counter(int32_t index)
{
    POOL_PARANOIA(check());
    assert((size_t)index < _counters.size());
    _counters[index].count++;
    POOL_PARANOIA(check());
    return _counters[index].count;
}

int32_t
cref_counter_pool::decr_counter(int32_t index)
{
    POOL_PARANOIA(assert((size_t)index < _counters.size()));
    int32_t c = --_counters[index].count;
    if (c == 0) {
	POOL_PARANOIA(check());
	/* recycle */
	_counters[index].count = _free_index;
	_free_index = index;
	VERBOSE_POOL_PARANOIA(dump());
	POOL_PARANOIA(check());
    }
    assert(c >= 0);
    return c;
}

int32_t
cref_counter_pool::count(int32_t index)
{
    POOL_PARANOIA(assert((size_t)index < _counters.size()));
    return _counters[index].count;
}

void*
cref_counter_pool::data(int32_t index)
{
    POOL_PARANOIA(assert((size_t)index < _counters.size()));
    return _counters[index].data;
}


syntax highlighted by Code2HTML, v. 0.9.1