/*	
 *	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追加: 参照カウント式ガーベージコレクションの機能\を内包*/
