#include <iostream.h>
#include <wclist.h>
#include <wclistit.h>
#include <wcskip.h>
#include <wcskipit.h>
#include <stdlib.h>

#pragma warning 549 9

const int ElemsPerBlock = 50;

//
// Simple block allocation class.  Allocate blocks for ElemsPerBlock
// elements, and use part of the block for each of the next ElemsPerBlock
// allocations, incrementing the number allocated elements.  Repeat getting
// more blocks as needed.
//
// Store the blocks in an intrusive single linked list.
//
// On a element deallocation, assume we allocated the memory and just
// decrement the count of allocated elements.  When the count gets to zero,
// free all allocated blocks
//
// This implementation assumes sizeof( char ) == 1
// 

class BlockAlloc {
private:
    // the size of elements (in bytes)
    unsigned elem_size;			

    // number of elements allocated
    unsigned num_allocated;		

    // free space of this number of elements available in first block
    unsigned num_free_in_block;		

    // list of blocks used to store elements (block are chunks of memory,
    // pointed by (char *) pointers.
    WCPtrSList<char> block_list;
   
    // pointer to the first block in the list 
    char *curr_block;

public:
    inline BlockAlloc( unsigned size ) 
    		: elem_size( size ), num_allocated( 0 )
		, num_free_in_block( 0 ) {};
		

    inline ~BlockAlloc() {
	block_list.clearAndDestroy();
    };
    
    // get memory for an element using block allocation 
    void *allocator( size_t elem_size );

    // free memory for an element using block allocation and deallocation
    void deallocator( void *old_ptr, size_t elem_size );
};
		

void *BlockAlloc::allocator( size_t size ) {
    // need a new block to perform allocation
    if( num_free_in_block == 0 ) {
	// allocate memory for ElemsPerBlock elements
	curr_block = new char[ size * ElemsPerBlock ];
	if( curr_block == 0 ) {
	    // allocation failed
	    return( 0 );
	}
	// add new block to beginning of list
	if( !block_list.insert( curr_block ) ) {
	    // allocation of list element failed
	    delete( curr_block );
	    return( 0 );
	}
	num_free_in_block = ElemsPerBlock;
    }
    
    // curr block points to a block of memory with some free memory
    num_allocated++;
    num_free_in_block--;
    // return pointer to a free part of the block, starting at the end
    // of the block
    return( curr_block + num_free_in_block * size );
}
	

void BlockAlloc::deallocator( void *, size_t ) {
    // just decrement the count
    // don't free anything until all elements are deallocated
    num_allocated--;
    if( num_allocated == 0 ) {
	// all the elements allocated BlockAlloc object have now been
	// deallocated, free all the blocks
	block_list.clearAndDestroy();
	num_free_in_block = 0;
    }
}
	



const unsigned NumTestElems = 200;

// array with random elements
static unsigned test_elems[ NumTestElems ];

static void fill_test_elems() {
    for( int i = 0; i < NumTestElems; i++ ) {
	test_elems[ i ] = rand();
    }
} 



void test_isv_list();
void test_val_list();
void test_val_skip_list();


void main() {
    fill_test_elems();

    test_isv_list();
    test_val_list();
    test_val_skip_list();
}


// An intrusive list class

class isvInt : public WCSLink {
public:
    static BlockAlloc memory_manage;
    int data;

    isvInt( int datum ) : data( datum ) {};

    void *operator new( size_t size ) {
	return( memory_manage.allocator( size ) );
    };

    void operator delete( void *old, size_t size ) {
	memory_manage.deallocator( old, size );
    };
};

// define static member data
BlockAlloc isvInt::memory_manage( sizeof( isvInt ) );


void test_isv_list() {
    WCIsvSList<isvInt> list;
    
    for( int i = 0; i < NumTestElems; i++ ) {
	list.insert( new isvInt( test_elems[ i ] ) );
    }

    WCIsvSListIter<isvInt> iter( list );
    while( ++iter ) {
	cout << iter.current()->data << " ";
    }
    cout << "\n\n\n";
    list.clearAndDestroy();
}


// WCValSList<int> memory allocator/dealloctor support
static BlockAlloc val_list_manager( WCValSListItemSize( int ) );

static void *val_list_alloc( size_t size ) {
    return( val_list_manager.allocator( size ) );
}

static void val_list_dealloc( void *old, size_t size ) {
    val_list_manager.deallocator( old, size );
}


// test WCValSList<int>
void test_val_list() {
    WCValSList<int> list( &val_list_alloc, &val_list_dealloc );
    
    for( int i = 0; i < NumTestElems; i++ ) {
	list.insert( test_elems[ i ] );
    }
    
    WCValSListIter<int> iter( list );
    while( ++iter ) {
	cout << iter.current() << " ";
    }
    cout << "\n\n\n";
    list.clear();
}


// skip list allocator dealloctors: just use allocator and dealloctor
// functions on skip list elements with one and two pointers
// (this will handle 94% of the elements)
const int one_ptr_size = WCValSkipListItemSize( int, 1 );
const int two_ptr_size = WCValSkipListItemSize( int, 2 );

static BlockAlloc one_ptr_manager( one_ptr_size );
static BlockAlloc two_ptr_manager( two_ptr_size );

static void *val_skip_list_alloc( size_t size ) {
    switch( size ) {
	case one_ptr_size:
	    return( one_ptr_manager.allocator( size ) );
	case two_ptr_size:
	    return( two_ptr_manager.allocator( size ) );
	default:
	    return( new char[ size ] );
    }
}

static void val_skip_list_dealloc( void *old, size_t size ) {
    switch( size ) {
	case one_ptr_size:
	    one_ptr_manager.deallocator( old, size );
	    break;
	case two_ptr_size:
	    two_ptr_manager.deallocator( old, size );
	    break;
	default:
	    delete old;
	    break;
    }
}


// test WCValSkipList<int>
void test_val_skip_list() {
    WCValSkipList<int> skiplist( WCSKIPLIST_PROB_QUARTER
    			       , WCDEFAULT_SKIPLIST_MAX_PTRS
			       , &val_skip_list_alloc
    			       , &val_skip_list_dealloc );
    
    for( int i = 0; i < NumTestElems; i++ ) {
	skiplist.insert( test_elems[ i ] );
    }
    
    WCValSkipListIter<int> iter( skiplist );
    while( ++iter ) {
	cout << iter.current() << " ";
    }
    cout << "\n\n\n";
    skiplist.clear();
}
