/*	
 *	clipcoll.c
 *
 *	コレクション
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Mon Dec 26 01:25:00 JST 2005 Naoyuki Sawa
 *	- 新規作成。
 *	  コードサイズ節約のために、実装をclipmisc.cからclipcoll.cへ分離しました。
 *	* Sat Mar 14 21:13:42 JST 2009 Naoyuki Sawa
 *	- プールに、確保時コールバックと解放時コールバックを追加しました。
 */
#include "clip.h"

/*****************************************************************************
 *	ユーティリティ：コレクションのヘルパ関数
 *****************************************************************************/

void*
ptr_copy(void* a)
{
	/* ポインタを返します。 */
	return a;
}

void
ptr_delete(void* a)
{
	/* 何もしません。 */
}

int
ptr_equals(void* a, void* b)
{
	/* ポインタを比較します。 */
	return a == b;
}

void*
str_copy(void* a)
{
	/* 文字列を複製します。 */
	a = strdup(a);
	if(!a) {
		DIE();
	}
	return a;
}

void
str_delete(void* a)
{
	/* 文字列を解放します。 */
	free(a);
}

int
str_equals(void* a, void* b)
{
	/* 文字列を比較します。 */
	return strcmp(a, b) == 0;
}

void*
unk_copy(void* a)
{
	/* インターフェイスポインタの参照カウントを増やして、返します。 */
	UNKNOWN_INTERFACE** _a = a;
	return safe_addref(_a);
}

void
unk_delete(void* a)
{
	/* インターフェイスポインタの参照カウントを減らします。 */
	UNKNOWN_INTERFACE** _a = a;
	safe_release(_a);
}

int
unk_equals(void* a, void* b)
{
	/* * aとbが異なるインターフェイスポインタであっても、
	 *   同じオブジェクトのインターフェイスポインタであれば、等価と見なす仕様です。
	 * - 上述の判定を行うためには、MAPに格納されているUNKNOWN_INTERFACEをそのまま比較するのでなく、
	 *   明示的にUNKNOWN_INTERFACEを要求し、返されたUNKNOWN_INTERFACE同士を比較する必要があります。
	 *   なぜなら、オブジェクトが複数のインターフェイスを実装している場合、各インターフェイスは
	 *   それぞれがUNKNOWN_INTERFACEであるため、複数のUNKNOWN_INTERFACEが存在するからです。
	 *   明示的にUNKNOWN_INTERFACEを要求することで、唯一の代表UNKNOWN_INTERFACEを得られます。
	 *   代表UNKNOWN_INTERFACEは、オブジェクト毎に一意ですから、代表UNKNOWN_INTERFACEを比較すれば、
	 *   オブジェクトが同一かどうかを比較することができます。
	 * - なお、MAPへの格納時にunk_copy()にて代表UNKNOWN_INTERFACEを求めておく方法は不可です。
	 *   不可である理由の一つ目は、このような方法を採ると、MAPに格納されるインターフェイスポインタが
	 *   常に代表UNKNOWN_INTERFACEになってしまい、map_put()とmap_get()の値が不一致となるからです。
	 *   不可である理由の二つ目は、unk_equals()は、MAPに格納されている値とmap_contains()、
	 *   または、map_get()に指定された値との比較であり、map_contains()やmap_get()に指定される値は、
	 *   必ずしも代表UNKNOWN_INTERFACEとは限らないからです。
	 * * 単純なポインタ比較で良い場合は、unk_equals()の代りにptr_equals()を使ってください。
	 */
	//int result;
	//UNKNOWN_INTERFACE** _a = a;
	//UNKNOWN_INTERFACE** _b = b;
	//_a = safe_query_interface(_a, UNKNOWN_INTERFACE); /* refcnt+1 */
	//_b = safe_query_interface(_b, UNKNOWN_INTERFACE); /* refcnt+1 */
	//result = _a == _b;
	//safe_release(_a); /* refcnt-1 */
	//safe_release(_b); /* refcnt-1 */
	//return result;
	//↓2005/12/13 reference_equals()マクロを使うように変更しました。処理の内容は同じです。
	return reference_equals((UNKNOWN_INTERFACE**)a, (UNKNOWN_INTERFACE**)b);
}

/*****************************************************************************
 *	ユーティリティ：マップ (コレクション)
 *****************************************************************************/

/* マップ要素を検索します。
 * [in]
 *	map		マップ。
 *	key		キー。
 * [out]
 *	戻り値		見つかった場合は、マップ要素を返します。
 *			見つからなかった場合は、NULLを返します。
 */
static MAP_NODE*
map_find(MAP* map, void* key)
{
	LIST_ENTRY* list_head = &map->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	MAP_NODE* map_node;

	/* リストを走査して... */
	while(list_entry != list_head) {
		map_node = CONTAINING_RECORD(list_entry, MAP_NODE, list_entry);
		if(map->map_info.key_equals(map_node->key, key)) {
			/*{{高速化のためだけの処理: この処理は省略しても大丈夫です。*/
			/* 高速化のために、見つけたマップ要素をリスト先頭へ移動します。
			 * 次のようなパターンでの使用が一般的だと、予測されるからです。
			 *	if(map_contains(map, key)) {
			 *		val = map_get(map, key);
			 *		...
			 *		free(val); // 2005/12/11の仕様変更により、値の解放が必要となりました。
			 *	}
			 */
			RemoveEntryList(/*&map_node->*/list_entry);
			InsertHeadList(&map->list_head, /*&map_node->*/list_entry);
			/*}}高速化のためだけの処理: この処理は省略しても大丈夫です。*/
			return map_node; /* 見つかった */
		}
		list_entry = list_entry->Flink;
	}

	return NULL; /* 見つからなかった */
}

/*---------------------------------------------------------------------------*/

MAP*
map_create(const MAP_INFO* map_info)
{
	/* 構造体メモリを確保します。 */
	MAP* map = calloc(1, sizeof(MAP));
	if(!map) {
		DIE();
	}

	/* リストヘッダを初期化します。 */
	InitializeListHead(&map->list_head);

	/* マップ情報を格納します。 */
	map->map_info = *map_info;

	return map;
}

void
map_delete(MAP* map)
{
	/* 残っているマップ要素をすべて削除します。 */
	map_clear(map);

	/* 構造体メモリを解放します。 */
	free(map);
}

void
map_clear(MAP* map)
{
	LIST_ENTRY* list_entry;
	MAP_NODE* map_node;

	/* リストが空になるまで... */
	while(!IsListEmpty(&map->list_head)) {

		/* 先頭のマップ要素を取得し、リストから取り外します。 */
		list_entry = RemoveHeadList(&map->list_head);
		map_node = CONTAINING_RECORD(list_entry, MAP_NODE, list_entry);

		/* マップ要素を削除します。 */
		map->map_info.key_delete(map_node->key); /* キーを削除 */
		map->map_info.val_delete(map_node->val); /* 値  を削除 */
		free(map_node); /* マップ要素のメモリを解放 */
	}
}

int
map_contains(MAP* map, void* key)
{
	/* マップ要素を検索し、見つかったら1、見つからなければ0を返します。 */
	return map_find(map, key) != NULL;
}

void
map_put(MAP* map, void* key, void* val)
{
	MAP_NODE* map_node;

	/* 新しいマップ要素を作成します。 */
	map_node = calloc(1, sizeof(MAP_NODE)); /* マップ要素のメモリを確保 */
	if(!map_node) {
		DIE();
	}
	map_node->key = map->map_info.key_copy(key); /* キーを複製して格納 */
	map_node->val = map->map_info.val_copy(val); /* 値  を複製して格納 */

	/* 同じキーを持つ、既存のマップ要素があれば、削除します。 */
	if(map_contains(map, key)) {
		map_remove(map, key); /* 既存のマップ要素を削除 */
	}

	/* 新しいマップ要素を、リストに追加します。 */
	InsertHeadList(&map->list_head, &map_node->list_entry);
}

void*
map_get(MAP* map, void* key)
{
	MAP_NODE* map_node;

	/* マップ要素を検索します。 */
	map_node = map_find(map, key);
	if(!map_node) {
		DIE(); /* 見つからなかった */
	}

	///* マップ要素の、値を返します。 */
	//return map_node->val;
	//↓2005/12/11 仕様変更
	/* マップ要素が保持している値の複製を返します。 */
	return map->map_info.val_copy(map_node->val);
}

void
map_remove(MAP* map, void* key)
{
	MAP_NODE* map_node;

	/* 削除するマップ要素を検索します。 */
	map_node = map_find(map, key);
	if(!map_node) {
		DIE(); /* 見つからなかった */
	}

	/* 削除するマップ要素を、リストから取り外します。 */
	RemoveEntryList(&map_node->list_entry);

	/* マップ要素を削除します。 */
	map->map_info.key_delete(map_node->key); /* キーを削除 */
	map->map_info.val_delete(map_node->val); /* 値  を削除 */
	free(map_node); /* マップ要素のメモリを解放 */
}

MAP*
map_duplicate(MAP* map)
{
	LIST_ENTRY* list_head = &map->list_head; /* 複製元のマップのリストヘッダ */
	LIST_ENTRY* list_entry = list_head->Flink; /* 複製元のマップのリスト走査用 */
	MAP_NODE* src_map_node; /* 複製元のマップのマップ要素 */
	MAP_NODE* dst_map_node; /* 複製先のマップのマップ要素 */

	/* 複製元のマップと同じマップ情報を持つ、空のマップを作成します。
	 * これが、複製先のマップとなります。
	 */
	map = map_create(&map->map_info); /* これ以降、mapは複製先のマップを指します */

	/* 複製元のマップの各要素の値について... */
	while(list_entry != list_head) {
		src_map_node = CONTAINING_RECORD(list_entry, MAP_NODE, list_entry);

		/* key_copy()とval_copy()ヘルパ関数を用いて複製し... */
		dst_map_node = calloc(1, sizeof(MAP_NODE)); /* マップ要素のメモリを確保 */
		if(!dst_map_node) {
			DIE();
		}
		dst_map_node->key = map->map_info.key_copy(src_map_node->key); /* キーを複製して格納 */
		dst_map_node->val = map->map_info.val_copy(src_map_node->val); /* 値  を複製して格納 */

		/* 複製先のマップへ追加します。 */
		InsertTailList(&map->list_head, &dst_map_node->list_entry);

		list_entry = list_entry->Flink;
	}

	/* 複製先のマップを返します。 */
	return map;
}

struct _LIST*
map_keys(MAP* map)
{
	LIST_ENTRY* list_head = &map->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	MAP_NODE* map_node;
	//
	LIST* list;
	LIST_INFO list_info;

	/* 空のリストを作成します。 */
	list_info.val_copy = map->map_info.key_copy;
	list_info.val_delete = map->map_info.key_delete;
	list = list_create(&list_info);

	/* マップ要素に含まれている、すべてのキーを、リストに追加します。 */
	while(list_entry != list_head) {
		map_node = CONTAINING_RECORD(list_entry, MAP_NODE, list_entry);
		list_add(list, map_node->key);
		list_entry = list_entry->Flink;
	}

	return list;
}

int
map_empty(MAP* map)
{
	return IsListEmpty(&map->list_head);
}

/*****************************************************************************
 *	ユーティリティ：リスト (コレクション)
 *****************************************************************************/

/* インデクスにて指定された位置の、リスト要素を取得します。
 * [in]
 *	list		リスト。
 *	index		インデクス。
 * [out]
 *	戻り値		リスト要素。
 * [note]
 *	* インデクスは、有効な位置(0以上、配列サイズ未満)でなければなりません。
 *	  この範囲外のインデクスが指定された場合は、アサートします。
 *	  空のリストに対してこの関数を呼び出した場合は、常にアサートします。
 */
static LIST_NODE*
list_get_node(LIST* list, int index)
{
	LIST_ENTRY* list_head = &list->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	LIST_NODE* list_node;

	/* リストを走査して... */
	while(list_entry != list_head) {
		if(!index) { /* インデクスが一致した */
			list_node = CONTAINING_RECORD(list_entry, LIST_NODE, list_entry);
			return list_node;
		}
		index--; /* カウントダウン */
		list_entry = list_entry->Flink;
	}

	DIE(); /* インデクスが不正 */
}

/*---------------------------------------------------------------------------*/

LIST*
list_create(const LIST_INFO* list_info)
{
	/* 構造体メモリを確保します。 */
	LIST* list = calloc(1, sizeof(LIST));
	if(!list) {
		DIE();
	}

	/* リストヘッダを初期化します。 */
	InitializeListHead(&list->list_head);

	/* リスト情報を格納します。 */
	list->list_info = *list_info;

	return list;
}

void
list_delete(LIST* list)
{
	/* 残っているリスト要素をすべて削除します。 */
	list_clear(list);

	/* 構造体メモリを解放します。 */
	free(list);
}

void
list_clear(LIST* list)
{
	LIST_ENTRY* list_entry;
	LIST_NODE* list_node;

	/* リストが空になるまで... */
	while(!IsListEmpty(&list->list_head)) {

		/* 先頭のリスト要素を取得し、リストから取り外します。 */
		list_entry = RemoveHeadList(&list->list_head);
		list_node = CONTAINING_RECORD(list_entry, LIST_NODE, list_entry);

		/* リスト要素を削除します。 */
		list->list_info.val_delete(list_node->val); /* 値を削除 */
		free(list_node); /* リスト要素のメモリを解放 */
	}
}

int
list_size(LIST* list)
{
	LIST_ENTRY* list_head = &list->list_head;
	LIST_ENTRY* list_entry = list_head->Flink;
	int size = 0;

	/* リスト末尾に到達するまで... */
	while(list_entry != list_head) {
		size++; /* カウントアップ */
		list_entry = list_entry->Flink;
	}

	return size;
}

void
list_set(LIST* list, int index, void* val)
{
	/* インデクスにて指定された位置の、既存の値を削除します。 */
	list_remove(list, index);

	/* インデクスにて指定された位置に、新しい値を追加します。 */
	list_insert(list, index, val);
}

void*
list_get(LIST* list, int index)
{
	LIST_NODE* list_node;

	/* リスト要素を取得します。 */
	list_node = list_get_node(list, index);

	/* リスト要素が保持している値の複製を返します。 */
	return list->list_info.val_copy(list_node->val);
}

void
list_add(LIST* list, void* val)
{
	/* リストの末尾に、値を追加します。 */
	list_insert(list, list_size(list), val);
}

void
list_insert(LIST* list, int index, void* val)
{
	int size;
	LIST_NODE* list_node;
	LIST_ENTRY* list_entry;
	LIST_ENTRY* next_list_entry;
	LIST_ENTRY* prev_list_entry;

	/* 新しいリスト要素を追加する位置の、前後のリスト要素を取得します。 */
	size = list_size(list);
	if(index == size) { /* 末尾に追加する場合 */
		next_list_entry = &list->list_head;
	} else { /* 途中に追加する場合 */
		list_node = list_get_node(list, index); /* インデクスが不正ならば、ここでアサートします */
		next_list_entry = &list_node->list_entry;
	}
	prev_list_entry = next_list_entry->Blink;

	/* 新しいリスト要素を作成します。 */
	list_node = calloc(1, sizeof(LIST_NODE)); /* リスト要素のメモリを確保 */
	if(!list_node) {
		DIE();
	}
	list_node->val = list->list_info.val_copy(val); /* 値を複製して格納 */

	/* 新しいリスト要素を、リストに追加します。 */
	list_entry = &list_node->list_entry;
	list_entry->Flink = next_list_entry;
	list_entry->Blink = prev_list_entry;
	prev_list_entry->Flink = next_list_entry->Blink = list_entry;
}

void
list_remove(LIST* list, int index)
{
	LIST_NODE* list_node;

	/* 削除するリスト要素を取得します。 */
	list_node = list_get_node(list, index);

	/* 削除するリスト要素を、リストから取り外します。 */
	RemoveEntryList(&list_node->list_entry);

	/* リスト要素を削除します。 */
	list->list_info.val_delete(list_node->val); /* 値を削除 */
	free(list_node); /* リスト要素のメモリを解放 */
}

LIST*
list_duplicate(LIST* list)
{
	LIST_ENTRY* list_head = &list->list_head; /* 複製元のリストのリストヘッダ */
	LIST_ENTRY* list_entry = list_head->Flink; /* 複製元のリストのリスト走査用 */
	LIST_NODE* src_list_node; /* 複製元のリストのリスト要素 */
	LIST_NODE* dst_list_node; /* 複製先のリストのリスト要素 */

	/* 複製元のリストと同じリスト情報を持つ、空のリストを作成します。
	 * これが、複製先のリストとなります。
	 */
	list = list_create(&list->list_info); /* これ以降、listは複製先のリストを指します */

	/* 複製元のリストの各要素の値について... */
	while(list_entry != list_head) {
		src_list_node = CONTAINING_RECORD(list_entry, LIST_NODE, list_entry);

		/* val_copy()ヘルパ関数を用いて複製し... */
		dst_list_node = calloc(1, sizeof(LIST_NODE)); /* リスト要素のメモリを確保 */
		if(!dst_list_node) {
			DIE();
		}
		dst_list_node->val = list->list_info.val_copy(src_list_node->val); /* 値を複製して格納 */

		/* 複製先のリストへ追加します。 */
		InsertTailList(&list->list_head, &dst_list_node->list_entry);

		list_entry = list_entry->Flink;
	}

	/* 複製先のリストを返します。 */
	return list;
}

int
list_empty(LIST* list)
{
	return IsListEmpty(&list->list_head);
}

/*****************************************************************************
 *	ユーティリティ：プール (コレクション)
 *****************************************************************************/

#if 0 /*{{2006/12/06:pool_free()から返却先プールの引数を不要とする変更により削除*/
//void*
//pool_alloc(POOL* pool)
//{
//	POOL_NODE* node;
//
//	/* プールが空でなければ... */
//	if(pool->first) {
//		/* プールから、先頭のメモリブロックを取得します。 */
//		node = pool->first;
//		pool->first = node->next;
//
//	/* プールが空ならば... */
//	} else {
//		/* ヒープから、新しいメモリブロックを確保します。 */
//		node = malloc(pool->size);
//		if(!node) {
//			DIE();
//		}
//	}
//
//	/* 利便性のために、ゼロクリアして返すことにします。 */
//	memset(node, 0, pool->size);
//
//	/* 割り当て回数を増やします。 */
//	if(pool->cnt < 0) {
//		DIE(); /* pool_alloc()とpool_free()の呼び出しが対称でない */
//	}
//	pool->cnt++;
//
//	return node;
//}
//
//void
//pool_free(POOL* pool, void* ptr)
//{
//	POOL_NODE* node = ptr;
//
//	/* 返されたメモリブロックを、プールの先頭にリンクします。 */
//	node->next = pool->first;
//	pool->first = node;
//
//	/* 割り当て回数を減らします。 */
//	pool->cnt--;
//	if(pool->cnt < 0) {
//		DIE(); /* pool_alloc()とpool_free()の呼び出しが対称でない */
//	}
//
//	/* 割り当てたメモリブロックが全てプールに返されたら... */
//	if(!pool->cnt) {
//		/* メモリブロックを実際に開放し、ヒープに返します。 */
//		while(pool->first) {
//			node = pool->first;
//			pool->first = node->next;
//			free(node);
//		}
//	}
//}
#endif /*}}2006/12/06:pool_free()から返却先プールの引数を不要とする変更により削除*/

//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//#define POOL_INIT (('P')|('O'<<8)|('O'<<16)|('L'<<24))
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
#define POOL_MARK (('P')|('O'<<8)|('O'<<16)|('L'<<24))
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更

void*
pool_alloc(POOL* pool)
{
	POOL_NODE* node;
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	void* data;
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更

	/* プールが初期化されていなければ、初期化します。 */
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//	if(pool->init != POOL_INIT) {
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	if(pool->mark != POOL_MARK) {
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		if(pool->size <= 0) {
			DIE(); /* POOL.sizeフィールドはユーザーが設定してください */
		}
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//		pool->init = POOL_INIT; /* 初期化済マーク */
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		pool->mark = POOL_MARK; /* 初期化済マーク */
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		InitializeListHead(&pool->alloc_list);
		InitializeListHead(&pool->free_list);
	}

	/* 解放リストが空ならば... */
	if(IsListEmpty(&pool->free_list)) {
		/* ヒープからメモリブロックを確保します。 */
		node = malloc(sizeof(POOL_NODE) + pool->size);
		if(!node) {
			DIE(); /* メモリ不足 */
		}
		/* 割り当て元プールを記憶します。 */
		node->pool = pool;

	/* 解放リストが空でなければ... */
	} else {
		/* プールからメモリブロックを取得します。 */
		node = (POOL_NODE*)RemoveHeadList(&pool->free_list);
		if(node->pool != pool) {
			DIE(); /* メモリ破損 */
		}
	}

	/* GCの対象にならないよう、メモリブロックの先頭をチェインします。 */
	InsertHeadList(&pool->alloc_list, &node->list_entry);

	/*{{2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/
	/* メモリブロックを確保した直後の、参照カウントの初期値は1です。 */
	node->ref_cnt = 1;
	/*}}2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/

//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//	/* データ領域をゼロクリアし、データ領域の先頭アドレスを返します。 */
//	return memset(node + 1, 0, pool->size);
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	/* データ領域をゼロクリアします。 */
	data = memset(node + 1, 0, pool->size);
	/* 確保時コールバックが設定されていたら、確保時コールバックを呼び出します。
	 * [note]
	 * - 確保時コールバックの中で、さらにpool_free()やpool_lock()を呼び出すことは可能です。
	 * - 確保時コールバックの中でpool_lock()を呼び出した場合、pool_alloc()の呼び出し元から見ると、
	 *   参照カウントの初期値が2(以上)のメモリブロックが返されることになります。
	 * - 確保時コールバックの中でpool_free()を呼び出した場合、すぐにメモリブロックが解放されます。
	 *   pool_alloc()の呼び出し元から見ると、無効なメモリブロックを取得することになります。
	 *   pool_alloc()の呼び出し元は、取得したメモリブロックの有効、無効を判断できないので、
	 *   バグの原因になり易いため、確保時コールバックの中でpool_free()を呼び出さない方が安全です。
	 */
	if(pool->init) {
		(*pool->init)(data);
		/* この時点で既にメモリブロックが解放されている可能性もあります。上記コメント参照。 */
	}
	/* データ領域の先頭アドレスを返します。 */
	return data;
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
}

void
pool_free(void* data/*NULLOK*/)
{
	POOL_NODE* node;
	POOL* pool;

	/* 「pool_free(NULL);」ならば、何も行いません。 */
	if(!data) {
		return;
	}

	/* メモリブロックの管理領域が初期化済プールを指していることを確認します。 */
	node = (POOL_NODE*)data - 1;
	pool = node->pool;
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//	if(pool->init != POOL_INIT) {
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	if(pool->mark != POOL_MARK) {
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		DIE(); /* pool_alloc()で確保したメモリ以外はpool_free()できません */
	}

	/*{{2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/
	/* 参照カウントを減らし、まだ参照が残っていたら何もせずに帰ります。 */
	node->ref_cnt--;
	if(node->ref_cnt < 0) {
		DIE(); /* {pool_alloc()+pool_lock()}と{pool_free()}の回数が非対称 */
	}
	if(node->ref_cnt) {
		return;
	}
	/*}}2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/

//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	/* 解放時コールバックが設定されていたら、解放時コールバックを呼び出します。
	 * [note]
	 * - 解放時コールバックの中で、さらにpool_free()やpool_lock()を呼び出すことはできません。
	 *   もし行うと、参照カウント不正となり、pool_free()やpool_lock()の中でエラー終了します。
	 */
	if(pool->free) {
		(*pool->free)(data);
	}
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更

	/* 割り当てリストから取り外し、解放リストにチェインします。(逆順は不可!!) */
	RemoveEntryList(&node->list_entry);
	InsertTailList(&pool->free_list, &node->list_entry);

	/* 全てのメモリブロックを解放したタイミングで、実際にヒープに返却します。 */
	if(IsListEmpty(&pool->alloc_list)) {
		while(!IsListEmpty(&pool->free_list)) {
			node = (POOL_NODE*)RemoveHeadList(&pool->free_list);
			if(node->pool != pool) {
				DIE(); /* メモリ破損 */
			}
			free(node);
		}
	}
}

/*{{2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/
void
pool_lock(void* data/*NULLOK*/)
{
	POOL_NODE* node;
	POOL* pool;

	/* 「pool_lock(NULL);」ならば、何も行いません。 */
	if(!data) {
		return;
	}

	/* メモリブロックの管理領域が初期化済プールを指していることを確認します。 */
	node = (POOL_NODE*)data - 1;
	pool = node->pool;
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//	if(pool->init != POOL_INIT) {
//↓2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
	if(pool->mark != POOL_MARK) {
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		DIE(); /* pool_alloc()で確保したメモリ以外はpool_lock()できません */
	}

	/* 参照カウントを増やします。 */
	node->ref_cnt++;
//{{2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
//	if(node->ref_cnt <= 0) {
//↓2009/03/14:より確実なエラーチェックのために変更
//↓pool_lock()を呼び出す前の参照カウントは1以上のはずなので、1増やすと2以上になるはずです。
//↓もし、参照カウントが0の状態でpool_lock()が呼び出され、以下のDIE()でエラー終了した場合、
//↓pool_free()を経由し、解放時コールバックの中でpool_lock()が呼び出された可能性があります。
//↓解放時コールバックの中で、さらにpool_free()やpool_lock()を呼び出すことはできません。
	if(node->ref_cnt < 2) {
//}}2009/03/14:確保時コールバックと解放時コールバックを追加するために変更
		DIE(); /* pool_lock()の回数がオーバーフロー?(まず有り得ないが...) */
	}
}
/*}}2007/01/02追加: 参照カウント式ガーベージコレクションの機能を内包*/
