/*****
 NAME
	cidset.m - source code for CIdSet class
 VERSION
	$Id$
 CHANGELOG
	$Log$
 */

#include <coconut/cidset.h>
#include <coconut/cmemalloc.h>
#include <coconut/ptext.h>

#define	MAX_ITEM_NUM		64

typedef	struct {
	u_int		empty_item ;
	id		item_list[MAX_ITEM_NUM] ;
} set_page_t ;

static set_page_t * newPage(void){
	set_page_t *	page ;

	page = (set_page_t *) [CMemAlloc allocate: sizeof(set_page_t)] ;
	g_assert(page != NULL) ;
	page->empty_item = 0 ;
	return page ;
}

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

@implementation CIdSet

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

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

- (void) preDealloc
{
	GList *		node ;
	GList *		prev ;
	set_page_t *	page ;
	u_int		i, last ;

	for(node = last_node ; node ; node = prev){
		prev = node->prev ;
		page = PAGE_PTR(node->data) ;
		last = page->empty_item ;
		for(i=0 ; i<last ; i++)
			[(page->item_list)[i] release] ;
		[CMemAlloc free: page] ;
		node->data = NULL ;
		g_list_free(node) ;
	}
}

- appendNewPage
{
	/* this function does not returns last node */
	g_list_append(last_node, newPage()) ;
	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() ;
	return nil ;
}

- foreach: (SEL) msg with: p1 with: p2
{
	id		result ;
	GList *		node ;
	set_page_t *	page ;
	int		i, last ;

	for(node = root_node ; node ; node = node->next){
		page = PAGE_PTR(node->data) ;
		last = page->empty_item ;
		for(i=0 ; i<last ; i++){
			result = [(page->item_list)[i] perform: msg with: p1 
			  with: p2] ;
			if(result != nil)
				return result ;
		}
	}
	return nil ;
}

- foreach: (id) obj message: (SEL) msg with: p1 
{
	id		result ;
	GList *		node ;
	set_page_t *	page ;
	int		i, last ;

	for(node = root_node ; node ; node = node->next){
		page = PAGE_PTR(node->data) ;
		last = page->empty_item ;
		for(i=0 ; i<last ; i++){
			result = [obj perform: msg with: (page->item_list)[i]
			  with: p1] ;
			if(result != nil)
				return result ;
		}
	}
	return nil ;
}

- append: (id) obj
{
	set_page_t *	page ;
	u_int		item ;

	page = PAGE_PTR(last_node->data) ; item = page->empty_item ;
	if(item < MAX_ITEM_NUM){
		[obj retain] ;
		(page->item_list)[item] = obj ;
		(page->empty_item)++ ;
		return nil ;
	} else {
		[self appendNewPage] ;
		return [self append: obj] ;
	}
}

- print: (id <PIndentStream>) stream
{
	GList *		node ;
	set_page_t *	page ;
	int		i, last ;

	for(node = root_node ; node ; node = node->next){
		page = PAGE_PTR(node->data) ;
		last = page->empty_item ;
		for(i=0 ; i<last ; i++){
			[(page->item_list)[i] print: stream] ;
			[stream putChar: '\n'] ;
		}
	}
	return nil ;
}

@end

