/*****
 NAME
	cset.m - source code for CSet class
 VERSION
	$Id$
 CHANGELOG
	$Log$
 */

#include <coconut/cset.h>
#include <coconut/cmemalloc.h>
#include <coconut/clist.h>
#include <coconut/csystem.h>
#include <coconut/ptext.h>

#define	MAX_ITEM_NUM	64

typedef struct {
	u_int	empty_item ;
	/* the array of the data. the number is defined by MAX_ITEM_NUM */
} set_page_t ;

static const size_t	header_size = ALIGNED_SIZE(sizeof(set_page_t)) ;

#define	GET_BODY(P)	((byte *) (((byte *) P) + header_size) )

#define	BODY_SIZE	(elm_size * MAX_ITEM_NUM)
#define	TOTAL_SIZE	(header_size + BODY_SIZE)

static set_page_t * newPage(size_t elm_size){
	set_page_t *	page ;

	page = (set_page_t *) [CMemAlloc allocate: TOTAL_SIZE] ;
	page->empty_item = 0 ;
	return page ;
}

#define	PAGE_PTR(P)	((set_page_t *) P)

@implementation CSet

- initSet: (size_t) elmsize 
{
	root_node = last_node = g_list_alloc() ;
	g_assert(root_node != NULL) ;
	root_node->data = newPage(elm_size = elmsize) ;
	return [super init] ;
}

- (void) dealloc
{
	[self preDealloc] ;
	[super dealloc] ;
}

- (void) preDealloc
{
	GList *		node ;
	GList *		prev ;
	set_page_t *	page ;

	for(node = last_node ; node ; node = prev){
		prev = node->prev ;
		page = PAGE_PTR(node->data) ;
		[CMemAlloc free: page] ;
		node->data = NULL ;
		g_list_free(node) ;
	}
}

- appendNewPage
{
	/* this function does not returns last node */
	g_list_append(last_node, newPage(elm_size)) ;
	last_node = g_list_last(last_node) ;
	return nil ;
}

- (u_int) count
{
	u_int		num ; 
	GList *		node ;

	/* the count of last page*/
	num = PAGE_PTR(last_node->data)->empty_item ;
	for(node = last_node->prev ; node ; node = node->prev){
		num += MAX_ITEM_NUM ;
	}
	return num ;
}

- clear
{
	[self preDealloc] ;
	root_node = last_node = g_list_alloc() ;
	g_assert(root_node != NULL) ;
	root_node->data = newPage(elm_size) ;
	return nil ;
}

- (const void *) foreach: (foreach_func_t) foreach with: (void *) p1 
    with: (void *) p2
{
	const void *	result ;
	GList *		node ;
	set_page_t *	page ;
	byte *		ptr ;
	byte *		last ;

	for(node = root_node ; node ; node = node->next){
		page = PAGE_PTR(node->data) ;
		ptr = GET_BODY(page) ;
		last = ptr + elm_size * page->empty_item ;
		for( ; ptr<last ; ptr+=elm_size){
			result = (*foreach)(ptr, p1, p2) ;
			if(result)
				return result ;
		}
	}
	return NULL ;
}

- append: (const void *) data
{
	set_page_t *	page ;
	u_int		item ;
	byte *		ptr ;

	page = PAGE_PTR(last_node->data) ; item = page->empty_item ;
	if(item >= MAX_ITEM_NUM){
		[self appendNewPage] ;
		page = PAGE_PTR(last_node->data) ; item = page->empty_item ;
		g_assert(item == 0) ;
	}
	ptr = GET_BODY(page) + elm_size * item ;
	memmove(ptr, data, elm_size) ;
	(page->empty_item)++ ;
	return nil ;
}

@end

