/*
 *	gdataset.c
 *
 *	Keyed Data Lists, Datasets
 *
 *	* Tue May 31 22:28:49 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- GLibのKeyed Data List(https://developer.gnome.org/glib/stable/glib-Keyed-Data-Lists.html),及び,Datasets(https://developer.gnome.org/glib/stable/glib-Datasets.html)を参考にしました。
 *	  関数仕様はオリジナル版と同じですが、実装は独自で、省メモリよりも単純な実装を優先しました。
 *	- オリジナル版は単純な配列を用いて省メモリを優先して実装されていますが、当実装ではght_hash_tableを利用して単純な実装を優先しました。
 *	  オリジナル版はGObject(GLibの基底クラス)で使用するために省メモリを最優先としているようですが、当ライブラリではユーティリティとして使う予定なので省メモリはさほど重要ではないからです。
 *	* Wed Jun 01 21:34:08 JST 2016 Naoyuki Sawa
 *	- gdataset.cの一番下に、データセットの使用例を追記しました。
 *	  データリストの使用例は書いていません。
 *	  実際にデータリストを直接使用する事はほとんど無く、データセットを介して利用する事しか無いと思うので、データリストの使用例は不要だと思います。
 *	  データリストの使用例が必要な場合は、データセットの実装からデータリストを利用している部分を参照して下さい。
 *	* Sun Sep 04 21:41:17 JST 2016 Naoyuki Sawa
 *	- 2016/09/04変更:G_DATALIST_GET_POINTER(),及び,G_DATALIST_SET_POINTER()が、(int)でアドレス演算を行っていたのを、(intptr_t)で行うように修正しました。
 *	  64ビット対応のための準備ですが、32ビットでも(intptr_t)で行う方が本当は正しいからです。
 *	  とは言え、32ビットではどちらでも同じ結果になるので、既存の32ビットコードには影響有りません。
 */
#include "clip.h"
/*****************************************************************************
 *	Keyed Data Lists - lists of data elements which are accessible by a string or GQuark identifier
 *****************************************************************************/
//{{2016/09/04変更:G_DATALIST_GET_POINTER(),及び,G_DATALIST_SET_POINTER()が、(int)でアドレス演算を行っていたのを、(intptr_t)で行うように修正しました。
//#define G_DATALIST_GET_POINTER(dl)	((ght_hash_table_t*)(*(int*)(dl) & ~G_DATALIST_FLAGS_MASK))	//データリストのポインタは、[31:2]=ポインタ,[1:0]=フラグ という構造である。当マクロは、[31:2]=ポインタ の部分を取得する。
//#define G_DATALIST_SET_POINTER(dl,p)	*(int*)(dl) = (*(int*)(dl) & G_DATALIST_FLAGS_MASK) | (int)(p)	//データリストのポインタは、[31:2]=ポインタ,[1:0]=フラグ という構造である。当マクロは、[31:2]=ポインタ の部分を変更する。
//↓2016/09/04変更:G_DATALIST_GET_POINTER(),及び,G_DATALIST_SET_POINTER()が、(int)でアドレス演算を行っていたのを、(intptr_t)で行うように修正しました。
#define G_DATALIST_GET_POINTER(dl)	((ght_hash_table_t*)(*(intptr_t*)(dl) & ~G_DATALIST_FLAGS_MASK))		//データリストのポインタは、[31:2]=ポインタ,[1:0]=フラグ という構造である。当マクロは、[31:2]=ポインタ の部分を取得する。
#define G_DATALIST_SET_POINTER(dl,p)	*(intptr_t*)(dl) = (*(intptr_t*)(dl) & G_DATALIST_FLAGS_MASK) | (intptr_t)(p)	//データリストのポインタは、[31:2]=ポインタ,[1:0]=フラグ という構造である。当マクロは、[31:2]=ポインタ の部分を変更する。
//}}2016/09/04変更:G_DATALIST_GET_POINTER(),及び,G_DATALIST_SET_POINTER()が、(int)でアドレス演算を行っていたのを、(intptr_t)で行うように修正しました。
/*--------------------------------------------------------------------------*/
typedef struct {
	void*		data;				//データ		NULL不可
	void		(*destroy)(void* data);		//デストラクタ		NULL可
} GDataElt;
/*--------------------------------------------------------------------------*/
static void g_datalist_id_set_internal(GData** datalist, /*GQuark*/int key_id, void* data, void (*destroy_func)(void* data)) {
	if(!key_id ||		//キーに、クォーク0(=文字列NULL)を指定してはいけない。
	   !data) { DIE(); }	//データに、NULLポインタを指定してはいけない。
	{
		ght_hash_table_t* p_ht;
		if(!(p_ht = G_DATALIST_GET_POINTER(datalist))) {			//ハッシュテーブルが未作成ならば…
			ght_set_rehash((p_ht = ght_create(0)), 1);			//ハッシュテーブルを作成する。
			G_DATALIST_SET_POINTER(datalist, p_ht);				//ハッシュテーブルのポインタを格納する。
		}
		{
			GDataElt* elt = malloc(sizeof(GDataElt));			//要素のメモリを確保する。
			if(!elt) { DIE(); }						//メモリ不足
			elt->data    = data;						//要素に、データのポインタを格納する。(NULL不可)
			elt->destroy = destroy_func;					//要素に、デストラクタのポインタを格納する。(NULL可)
			if(ght_insert(p_ht, elt, sizeof key_id, &key_id)) { DIE(); }	//ハッシュテーブルに、要素を追加する。同じキーを持つ要素が既に存在していたら、当モジュールのバグです。
		}
	}
}
/*--------------------------------------------------------------------------*/
static void* g_datalist_id_get_internal(GData** datalist, /*GQuark*/int key_id) {
	if(!key_id) { DIE(); }	//キーに、クォーク0(=文字列NULL)を指定してはいけない。
	{
		ght_hash_table_t* p_ht;
		void* ret_data = NULL;							//指定されたキーに一致する要素が無かった場合のために、戻り値をNULLとしておく。
		if((p_ht = G_DATALIST_GET_POINTER(datalist))) {				//ハッシュテーブルが未作成でなければ…
			GDataElt* elt = ght_get(p_ht, sizeof key_id, &key_id);		//指定されたキーに一致する要素が有れば、取得する。
			if(elt) {							//指定されたキーに一致する要素が有れば…
				ret_data = elt->data;					//取得したデータを戻り値とする。
				if(!ret_data) { DIE(); }				//ここで停止したら、当モジュールのバグです。
			}
		}
		return ret_data;							//取得したデータ,又は,NULLを返す。
	}
}
/*--------------------------------------------------------------------------*/
static void* g_datalist_id_remove_internal(GData** datalist, /*GQuark*/int key_id, void (**old_destroy/*NULL可*/)(void* data)) {	//(old_destroy!=NULL)ならば非通知と見なす。
	if(!key_id) { DIE(); }	//キーに、クォーク0(=文字列NULL)を指定してはいけない。
	{
		ght_hash_table_t* p_ht;
		void* old_data = NULL;							//指定されたキーに一致する要素が無かった場合のために、戻り値をNULLとしておく。
		if(old_destroy) { *old_destroy = NULL; }				//デストラクタを格納する変数が指定されていたら、まずNULLを格納しておく。
		if((p_ht = G_DATALIST_GET_POINTER(datalist))) {				//ハッシュテーブルが未作成でなければ…
			GDataElt* elt = ght_remove(p_ht, sizeof key_id, &key_id);	//指定されたキーに一致する要素が有れば、取り外す。
			if(elt) {							//指定されたキーに一致する要素が有れば…
				//〜〜〜オリジナル版ではここでハッシュテーブルが空ならばデータリストのメモリ開放を行っていたが、当実装では不要と考えて行わない事にした。当実装ではデータリストのメモリ開放はg_datalist_clear()でのみ行う。〜〜〜
				old_data = elt->data;					//デストラクタを呼び出さなかった場合のために、古いデータを戻り値としておく。
				if(!old_data) { DIE(); }				//ここで停止したら、当モジュールのバグです。
				if(old_destroy) {					//デストラクタを格納する変数が指定されていたら(=非通知ならば)…
					*old_destroy = elt->destroy;			//デストラクタを格納する。(NULL可)
				} else if(elt->destroy) {				//非通知でなく,かつ,デストラクタが有れば…
					(*elt->destroy)(elt->data);			//デストラクタを呼び出す。
					old_data = NULL;				//デストラクタを呼び出した場合は、戻り値をNULLとする。
				}
				free(elt);						//要素のメモリを開放する。
			}
		}
		return old_data;							//古いデータ,又は,NULLを返す。
	}
}
/*--------------------------------------------------------------------------*/
//Resets the datalist to NULL.
//It does not free any memory or call any destroy functions.
//Parameters
//		datalist		A pointer to a pointer to a datalist.
void g_datalist_init(GData** datalist) {
	datalist = NULL;
}
/*--------------------------------------------------------------------------*/
//Sets the data corresponding to the given GQuark id, and the function to be called when the element is removed from the datalist.
//Any previous data with the same key is removed, and its destroy function is called.
//Parameters
//		datalist		A datalist.
//		key_id			The GQuark to identify the data element.
//		data			The data element or NULL to remove any previous element corresponding to key_id.	[allow-none]
//		destroy_func		The function to call when the data element is removed.
//					This function will be called with the data element and can be used to free any memory allocated for it.
//					If data is NULL, then destroy_func must also be NULL.
void g_datalist_id_set_data_full(GData** datalist, /*GQuark*/int key_id, void* data, void (*destroy_func)(void* data)) {
	g_datalist_id_remove_internal(datalist, key_id, NULL/*old_destroy*/);
	if(data) { g_datalist_id_set_internal(datalist, key_id, data, destroy_func); }
}
/*--------------------------------------------------------------------------*/
//Retrieves the data element corresponding to key_id.
//Parameters
//		datalist		A datalist.
//		key_id			The GQuark identifying a data element.
//Returns
//		The data element, or NULL if it is not found.
void* g_datalist_id_get_data(GData** datalist, /*GQuark*/int key_id) {
	return g_datalist_id_get_internal(datalist, key_id);
}
/*--------------------------------------------------------------------------*/
//Removes an element, without calling its destroy notification function.
//Parameters
//		datalist		A datalist.
//		key_id			The GQuark identifying a data element.
//Returns
//		The data previously stored at key_id, or NULL if none.
void* g_datalist_id_remove_no_notify(GData** datalist, /*GQuark*/int key_id) {
	void (*old_destroy)(void* data);
	return g_datalist_id_remove_internal(datalist, key_id, &old_destroy/*非通知とするためのダミー*/);
}
/*--------------------------------------------------------------------------*/
//This is a variant of g_datalist_id_get_data() which returns a 'duplicate' of the value.
//dup_func defines the meaning of 'duplicate' in this context, it could e.g. take a reference on a ref-counted object.
//If the key_id is not set in the datalist then dup_func will be called with a NULL argument.
//Note that dup_func is called while the datalist is locked, so it is not allowed to read or modify the datalist.
//This function can be useful to avoid races when multiple threads are using the same datalist and the same key.
//Parameters
//		datalist		Location of a datalist.
//		key_id			The GQuark identifying a data element.
//		dup_func		Function to duplicate the old value.	[allow-none]
//		user_data		Passed as user_data to dup_func.	[allow-none]
//Returns
//		The result of calling dup_func on the value associated with key_id in datalist, or NULL if not set.
//		If dup_func is NULL, the value is returned unmodified.
void* g_datalist_id_dup_data(GData** datalist, /*GQuark*/int key_id, void* (*dup_func)(void* data, void* user_data), void* user_data) {
	void* data = g_datalist_id_get_data(datalist, key_id);
	if(dup_func) { data = (*dup_func)(data/*NULL可*/, user_data); }
	return data;
}
/*--------------------------------------------------------------------------*/
//Compares the member that is associated with key_id in datalist to oldval, and if they are the same, replace oldval with newval.
//This is like a typical atomic compare-and-exchange operation, for a member of datalist.
//If the previous value was replaced then ownership of the old value (oldval) is passed to the caller, including the registred destroy notify for it (passed out in old_destroy).
//Its up to the caller to free this as he wishes, which may or may not include using old_destroy as sometimes replacement should not destroy the object in the normal way.
//Parameters
//		datalist		Location of a datalist.
//		key_id			The GQuark identifying a data element.
//		oldval			The old value to compare against.	[allow-none]
//		newval			The new value to replace it with.	[allow-none]
//		destroy			Destroy notify for the new value.	[allow-none]
//		old_destroy		Destroy notify for the existing value.	[allow-none]
//Returns
//		TRUE if the existing value for key_id was replaced by newval, FALSE otherwise.
int g_datalist_id_replace_data(GData** datalist, /*GQuark*/int key_id, void* oldval, void* newval, void (*destroy)(void* data), void (**old_destroy)(void* data)) {
	DIE();	//※TODO:[allow-none]である引数をNULLにした時の動作仕様が良く判らないので実装を保留した。
}
/*--------------------------------------------------------------------------*/
//Calls the given function for each data element of the datalist.
//The function is called with each data element's GQuark id and data, together with the given user_data parameter.
//Note that this function is NOT thread-safe.
//So unless datalist can be protected from any modifications during invocation of this function, it should not be called.
//Parameters
//		datalist		A datalist.
//		func			The function to call for each data element.
//		user_data		User data to pass to the function.
void g_datalist_foreach(GData** datalist, void (*func)(/*GQuark*/int key_id, void* data, void* user_data), void* user_data) {
	ght_hash_table_t* p_ht;
	if((p_ht = G_DATALIST_GET_POINTER(datalist))) {	//ハッシュテーブルが未作成でなければ…
		ght_iterator_t iterator;
		GDataElt* elt;
		const int* p_key_id;
		for(elt = ght_first(p_ht, &iterator, (const void**)&p_key_id);
		    elt;
		    elt = ght_next( p_ht, &iterator, (const void**)&p_key_id)) {
			(*func)(*p_key_id, elt->data, user_data);
		}
	}
}
/*--------------------------------------------------------------------------*/
//Frees all the data elements of the datalist.
//The data elements' destroy functions are called if they have been set.
//Parameters
//		datalist		A datalist.
static void g_datalist_clear_cb(/*GQuark*/int key_id, void* data, void* user_data) {
	g_datalist_id_remove_internal(user_data, key_id, NULL/*old_destroy*/);
}
void g_datalist_clear(GData** datalist) {
	ght_hash_table_t* p_ht;
	g_datalist_foreach(datalist, g_datalist_clear_cb, datalist);	//ハッシュテーブル内の、全ての要素を削除する。
	if((p_ht = G_DATALIST_GET_POINTER(datalist))) {			//ハッシュテーブルが未作成でなければ…
		if(ght_size(p_ht)) { DIE(); }				//ここで停止したら、当モジュールのバグです。
		ght_finalize(p_ht);					//ハッシュテーブルを削除する。
		G_DATALIST_SET_POINTER(datalist, NULL);			//ハッシュテーブルのポインタをクリアする。
	}
}
/*--------------------------------------------------------------------------*/
//Turns on flag values for a data list.
//This function is used to keep a small number of boolean flags in an object with a data list without using any additional space.
//It is not generally useful except in circumstances where space is very tight.
//(It is used in the base GObject type, for example.)
//Parameters
//		datalist		Pointer to the location that holds a list.
//		flags			The flags to turn on.
//					The values of the flags are restricted by G_DATALIST_FLAGS_MASK (currently 3; giving two possible boolean flags).
//					A value for flags that doesn't fit within the mask is an error.
void g_datalist_set_flags(GData** datalist, int flags) {
	if(flags & ~G_DATALIST_FLAGS_MASK) { DIE(); }
	*(int*)datalist |= flags;
}
/*--------------------------------------------------------------------------*/
//Turns off flag values for a data list.	See g_datalist_set_flags()
//Parameters
//		datalist		Pointer to the location that holds a list.
//		flags			The flags to turn off.
//					The values of the flags are restricted by G_DATALIST_FLAGS_MASK (currently 3: giving two possible boolean flags).
//					A value for flags that doesn't fit within the mask is an error.
void g_datalist_unset_flags(GData** datalist, int flags) {
	if(flags & ~G_DATALIST_FLAGS_MASK) { DIE(); }
	*(int*)datalist &= ~flags;
}
/*--------------------------------------------------------------------------*/
//Gets flags values packed in together with the datalist.	See g_datalist_set_flags().
//Parameters
//		datalist		Pointer to the location that holds a list.
//Returns
//		The flags of the datalist.
int g_datalist_get_flags(GData** datalist) {
	return *(int*)datalist & G_DATALIST_FLAGS_MASK;
}
/*****************************************************************************
 *	Datasets - associate groups of data elements with particular memory locations
 *****************************************************************************/
static ght_hash_table_t* g_dataset_location_ht;
/*--------------------------------------------------------------------------*/
//Sets the data element associated with the given GQuark id, and also the function to call when the data element is destroyed.
//Any previous data with the same key is removed, and its destroy function is called.
//Parameters
//		dataset_location	The location identifying the dataset.	[not nullable]
//		key_id			The GQuark id to identify the data element.
//		data			The data element.
//		destroy_func		The function to call when the data element is removed.
//					This function will be called with the data element and can be used to free any memory allocated for it.
void g_dataset_id_set_data_full(const void* dataset_location, /*GQuark*/int key_id, void* data, void (*destroy_func)(void* data)) {
	GData** datalist;
	if(!g_dataset_location_ht) {											//ハッシュテーブルが未作成ならば…
		ght_set_rehash((g_dataset_location_ht = ght_create(0)), 1);						//ハッシュテーブルを作成する。
	}
	datalist = ght_get(g_dataset_location_ht, sizeof dataset_location, &dataset_location);				//このロケーションに対応する、データリストを取得する。
	if(!datalist) {													//このロケーションに対応する、データリストが無ければ…
		datalist = malloc(sizeof(GData*));									//データリストのメモリを確保する。
		if(!datalist) { DIE(); }										//メモリ不足
		g_datalist_init(datalist);										//データリストを初期化する。
		if(ght_insert(g_dataset_location_ht, datalist, sizeof dataset_location, &dataset_location)) { DIE(); }	//このロケーションに対応する、データリストを登録する。
	}
	g_datalist_id_set_data_full(datalist, key_id, data, destroy_func);						//データリストに、データを設定(data!=NULL),又は,削除(data==NULL)する。
}
/*--------------------------------------------------------------------------*/
//Gets the data element corresponding to a GQuark.
//Parameters
//		dataset_location	The location identifying the dataset.	[not nullable]
//		key_id			The GQuark id to identify the data element.
//Returns
//		The data element corresponding to the GQuark, or NULL if it is not found.
void* g_dataset_id_get_data(const void* dataset_location, /*GQuark*/int key_id) {
	void* data = NULL;												//ハッシュテーブル,又は,データリストが無かった場合のために、戻り値をNULLとしておく。
	if(g_dataset_location_ht) {											//ハッシュテーブルが未作成でなければ…
		GData** datalist = ght_get(g_dataset_location_ht, sizeof dataset_location, &dataset_location);		//このロケーションに対応する、データリストを取得する。
		if(datalist) {												//このロケーションに対応する、データリストが有れば…
			data = g_datalist_id_get_data(datalist, key_id);						//データリストから、データ取得を試みる。
		}
	}
	return data;
}
/*--------------------------------------------------------------------------*/
//Removes an element, without calling its destroy notification function.
//Parameters
//		dataset_location	The location identifying the dataset.	[not nullable]
//		key_id			The GQuark ID identifying the data element.
//Returns
//		The data previously stored at key_id, or NULL if none.
void* g_dataset_id_remove_no_notify(const void* dataset_location, /*GQuark*/int key_id) {
	void* data = NULL;												//ハッシュテーブル,又は,データリストが無かった場合のために、戻り値をNULLとしておく。
	if(g_dataset_location_ht) {											//ハッシュテーブルが未作成でなければ…
		GData** datalist = ght_get(g_dataset_location_ht, sizeof dataset_location, &dataset_location);		//このロケーションに対応する、データリストを取得する。
		if(datalist) {												//このロケーションに対応する、データリストが有れば…
			data = g_datalist_id_remove_no_notify(datalist, key_id);					//データリストから、データ削除を試みる。
		}
	}
	return data;
}
/*--------------------------------------------------------------------------*/
//Calls the given function for each data element which is associated with the given location.
//Note that this function is NOT thread-safe.
//So unless datalist can be protected from any modifications during invocation of this function, it should not be called.
//Parameters
//		dataset_location	The location identifying the dataset.	[not nullable]
//		func			The function to call for each data element.
//		user_data		User data to pass to the function.
void g_dataset_foreach(const void* dataset_location, void (*func)(/*GQuark*/int key_id, void* data, void* user_data), void* user_data) {
	if(g_dataset_location_ht) {											//ハッシュテーブルが未作成でなければ…
		GData** datalist = ght_get(g_dataset_location_ht, sizeof dataset_location, &dataset_location);		//このロケーションに対応する、データリストを取得する。
		if(datalist) {												//このロケーションに対応する、データリストが有れば…
			g_datalist_foreach(datalist, func, user_data);							//データリストの、データを列挙する。
		}
	}
}
/*--------------------------------------------------------------------------*/
//Destroys the dataset, freeing all memory allocated, and calling any destroy functions set for data elements.
//Parameters
//		dataset_location	The location identifying the dataset.	[not nullable]
void g_dataset_destroy(const void* dataset_location) {
	if(g_dataset_location_ht) {											//ハッシュテーブルが未作成でなければ…
		GData** datalist = ght_remove(g_dataset_location_ht, sizeof dataset_location, &dataset_location);	//このロケーションに対応する、データリストが有れば、取り外す。
		if(datalist) {												//このロケーションに対応する、データリストが有れば…
			g_datalist_clear(datalist);									//データリストを削除する。
			free(datalist);											//データリストのメモリを開放する。
		}
	}
}
/*****************************************************************************
 *	データセットの使用例
 *****************************************************************************/
#if 0
//□ファイルポインタに、パス要素の文字列を関連付ける例1。文字列をクォークに変換してキーとする、普通の使い方の例です。
static void fnDestroy(void* data) { free(data); }
static void test1_callee(FILE* fp);	//前方参照
void test1_caller(const char* path) {
	char drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME], ext[MAX_EXT];
	FILE* fp = fopen(path, "w");
	splitpath(path, drive, dir, fname, ext);
	//ポインタにデータを関連付ける。
	g_dataset_set_data_full(fp, "drive", strdup(drive), fnDestroy);	//┬当モジュールに限った事ではないが、削除関数としてfreeを直接指定してはいけない。詳細は、garray.cの'Wed Apr 20 10:51:26 JST 2016'のコメントを参照して下さい。
	g_dataset_set_data_full(fp, "dir",   strdup(dir),   fnDestroy);	//│
	g_dataset_set_data_full(fp, "fname", strdup(fname), fnDestroy);	//│
	g_dataset_set_data_full(fp, "ext",   strdup(ext),   fnDestroy);	//┘
	test1_callee(fp);
	fclose(fp);		//関連付けたポインタを削除する時に…
	g_dataset_destroy(fp);	//関連付けたデータも削除する。これを忘れると、メモリ消費量が増えていく問題や、後で別のオブジェクトがたまたま同じポインタで生成された時に以前に関連付けた古いデータを取得してしまう問題が発生するので、忘れないように注意せよ。
}
static void test1_callee(FILE* fp) {
	//ポインタに関連付けたデータを取得する。
	fprintf(fp, "drive = '%s'\n", g_dataset_get_data(fp, "drive"));
	fprintf(fp, "dir   = '%s'\n", g_dataset_get_data(fp, "dir"  ));
	fprintf(fp, "fname = '%s'\n", g_dataset_get_data(fp, "fname"));
	fprintf(fp, "ext   = '%s'\n", g_dataset_get_data(fp, "ext"  ));
}
/*--------------------------------------------------------------------------*/
//□ファイルポインタに、パス要素の文字列を関連付ける例2。数値を直接キーとする、特殊な使い方の例です。
// - g_dataset_id_〜や、g_datalist_id_〜に指定するkey_idは、クォークを指定するのが普通ですが、それ以外の使い方として、0以外の任意の数値を指定しても構いません。
//   gdataset.cモジュール内では、key_idがクォークである事に依存している処理は無く、ユニークな数値であれば何でも良いからです。
//   データリストAPIではdatalist単位で，データセットAPIではロケーション単位でユニークであれば大丈夫です。
//   たとえば、あるロケーションに対してはクォークをキーとして用い、別のロケーションに対しては数値をキーとして用いても構いません。
//   この特性を利用すると、文字列を一切使用せずにgdataset.cモジュールの機能を利用する事も出来て、速度やメモリ効率が少し良くなります。
//   (もっとも、速度やメモリ効率が重要な場面では、gdataset.cモジュールの機能を利用しないとは思いますが…)
// - 一つのdatalistや、一つのロケーションに対して、クォークのキーと数値のキーを併用する事は出来ないので注意して下さい。
//   quark_from_string()が返すクォークの値について範囲等の前提を設けることは出来ず、数値キーと被る可能性が有るからです。
static void test2_callee(FILE* fp);	//前方参照
void test2_caller(const char* path) {
	char drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME], ext[MAX_EXT];
	FILE* fp = fopen(path, "w");
	splitpath(path, drive, dir, fname, ext);
	//ポインタにデータを関連付ける。
	g_dataset_id_set_data_full(fp, 1, strdup(drive), fnDestroy);
	g_dataset_id_set_data_full(fp, 2, strdup(dir),   fnDestroy);
	g_dataset_id_set_data_full(fp, 3, strdup(fname), fnDestroy);
	g_dataset_id_set_data_full(fp, 4, strdup(ext),   fnDestroy);
	test2_callee(fp);
	fclose(fp);
	g_dataset_destroy(fp);
}
static void test2_callee(FILE* fp) {
	//ポインタに関連付けたデータを取得する。
	fprintf(fp, "drive = '%s'\n", g_dataset_id_get_data(fp, 1));
	fprintf(fp, "dir   = '%s'\n", g_dataset_id_get_data(fp, 2));
	fprintf(fp, "fname = '%s'\n", g_dataset_id_get_data(fp, 3));
	fprintf(fp, "ext   = '%s'\n", g_dataset_id_get_data(fp, 4));
}
#endif
