/*
 *	clipcbkq.c
 *
 *	コールバックキュー
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Thu Jan 28 21:43:35 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 *	- 主に、描画処理のレイヤ管理を行うために作成したモジュールです。
 *	  個々のオブジェクトの計算処理の中で、描画関数を登録しておいて、あとでまとめて描画するような使い方です。
 *	- 一つのオブジェクトが複数のレイヤに描画する場合も、異なる優先度で描画関数を複数回登録しておけば対応出来ます。
 *	  その場合、複数回登録する描画関数は、同じであっても異なっていても構いません。
 *	  同じである場合は、コールバック関数に渡された優先度やパラメータで、どのレイヤの画像を描くかを判断して下さい。
 *	- CbkQue_RemoveIf()とCbkQue_CallIf()は、条件をかなり柔軟に指定出来るようにしました。
 *	  上手く優先度を設計すれば、背景用レイヤの登録だけを削除したり、背景と前景を分けてコールバックしたり出来ます。
 *	  また、関数ポインタとパラメータを指定すれば、特定のオブジェクトが登録したノードだけを登録解除したり出来ます。
 *	  とは言え、ほとんどの場合は必要無いと思うので、CbkQue_Remove()とCbkQue_Call()だけを使えば充分かも知れません。
 *	* Sat Jan 30 21:30:05 JST 2016 Naoyuki Sawa
 *	- 全ての関数を大幅に変更しました。
 *	  変更前のコードはコメントアウトして残していません。
 *	  変更前のコードを参照する必要が生じた場合は、前回のアーカイブを参照して下さい。
 *	- 主な変更点は、以下の通りです。
 *	  ・変更前は、当モジュールの機能は、ノードを登録して呼び出す事だけでした。
 *	    ノードに付随するパラメータのための、メモリ確保や開放(必要ならば)は、アプリケーション側で行う必要が有りました。
 *	    実際に使って見たところ、パラメータとしてポインタ一つでは大抵不足であり、常にメモリ確保を行う必要が生じました。
 *	    そこで、パラメータのためのメモリ確保や開放の機能も、当モジュールに取り込む事にしました。
 *	    ノード登録もメモリ確保も同じバッファを使って行うので、もしメモリ確保を使わなくてもバッファの無駄は生じません。
 *	  ・ノードを登録解除する機能を単純化して、CbkQue_Clear()だけにしました。
 *	    変更前はCbkQue_RemoveIf()に登録解除条件を指定出来ましたが、その機能を使う事はほとんど無い事と思ったからです。
 *	  ・バッファ容量が不足した時の動作を変更しました。
 *	    変更前はエラーコードを返していましたが、変更後はエラー停止,又は,例外を投入するようにしました。
 *	    そうした理由は、アプリケーションがCbkQue_Add()/CbkQue_AddMem()を呼び出す箇所がかなり多くなると思ったからです。
 *	    アプリケーションが、全てのエラーコードを確認するのは、見落としの危険が大きいと思ったからです。
 *	    それよりも、エラー停止してバッファ容量を調整する事を促すか、又は、まとめて例外ハンドラで処理する方が安全です。
 *	- コールバックキューの応用例として、コールバックキューを用いた優先度付き描画を作成しました。
 *	  詳細は、clipcqrd.h,clipcqrd.c,及び,test.pce/app.cを参照して下さい。
 *	* Sun Jan 31 21:43:46 JST 2016 Naoyuki Sawa
 *	- 優先度が同じ場合、登録順序の通りにコールバックするようにしました。
 *	  これまでは優先度が同じ場合コールバック順が不定でしたが、登録順序の通りにコールバックする方が、使い易いと思ったからです。
 *	  この変更の影響で、優先度に指定出来る値の範囲が、(変更前:INT_MIN～INT_MAX)⇒(変更後:SHRT_MIN～SHRT_MAX) に狭まりました。
 *	* Sun Sep 04 21:41:17 JST 2016 Naoyuki Sawa
 *	- 64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
 *	  setjmp()/longjmp()のパラメータは(int)であり、(void*)や(intptr_t)を使えないからです。
 *	  尚、32ビット版も64ビット版と共通に変更しても良いのですが、万一既存のコードに影響が生じる事を防ぐために、32ビット版はこれまで通りとしました。
 *	- (int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
 *	  詳細は、CbkQueNode_Compar(),CbkQue_InvokePri()内のコメントを参照して下さい。
 *	* Wed Oct 19 21:15:15 JST 2016 Naoyuki Sawa
 *	- 動的コールバックキューの機能を追加しました。
 *	  動的コールバックキューとして動作するか、(これまでと同じ)静的コールバックキューとして動作するかは、作成時に使用する関数によって選択出来ます。
 *	  StaticCbkQue_New()を呼び出して作成するか、StaticCbkQue_Init()を直接呼び出して初期化すれば、静的コールバックキューになります。
 *	  DynamicCbkQue_New()を呼び出して作成すれば、動的コールバックキューになります。
 *	  上記のように、作成時に使用する関数名を変更したので、既存のアプリケーションも修正が必要となります。
 *	- 今回、動的コールバックキューの機能を追加した理由は、必要に応じてコールバックキューのサイズを拡張したいケースが生じたからです。
 *	  元々の静的コールバックキューは、組み込み環境等で動的なメモリ確保を伴わずに優先度付きのキューを管理できるようにするために作成したものでした。
 *	  そのため、アプリケーション側で静的に確保したメモリを、StaticCbkQue_Init()(変更前の関数名はCbkQue_Init())で初期化して使用する事を想定していました。
 *	  WindowsやP/ECE等、動的メモリ確保が利用し易い環境では、アプリケーション側でメモリを静的に確保しなくても良いように、StaticCbkQue_New()(変更前の関数名はCbkQue_New())も用意しましたが、
 *	  初期化時にコールバックキューのサイズが固定されてしまう点は同じであり、やや使い辛いケースが有りました。
 *	  コールバックキューのサイズが不足にならないよう、想定される最大サイズでコールバックキューを作成しておく必要が有り、予測が難しいと無駄に大きなサイズで初期化する必要が生じるからです。
 *	- そこで今回、ノードやメモリの使用量に応じて自動的にサイズを拡張する、動的コールバックキューの機能を追加する事にしました。
 *	  組み込み環境では使用し辛いですが、WindowsやP/ECE等では使い易いと思います。
 *	  ただし、組み込み環境用に、(これまでと同じ)静的コールバックキューの機能も残しておく必要が有りました。
 *	  そのための方法として、既存の静的コールバックキューとは別のモジュールで、新たに動的コールバックキューを追加する事も検討したのですが、それは出来ませんでした。
 *	  既に、DxlibHelper.cppモジュールやclipcqrd.c等が、既存の静的コールバックキューの関数名に依存してしまっているからです。
 *	  そこで、作成,及び,初期化以外のコールバックキューの関数名はそのままで、作成,及び,初期化時に両方のコールバックキューのタイプを選択する事にしました。
 *	- アプリケーションの変更方法は、おおよそ以下の通りです。
 *	  組み込み環境等で、静的コールバックキューを静的に初期化していた場合は、静的に定義していた構造体の名前をST_CbkQue⇒ST_StaticCbkQueに変更するだけで済むと思います。
 *	  WindowsやP/ECEで、CbkQue_New()でコールバックキューを作成していた場合は、StaticCbkQue_New(),又は,DynamicCbkQue_New()に変更して下さい。
 *	- 今回の変更は、変更箇所が多かったので、変更前のコードはコメントアウトして残していません。
 *	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
 *	* Wed Oct 26 21:45:51 JST 2016 Naoyuki Sawa
 *	- DynamicCbkQue_Clear()の中で、MemPool_Clear()の呼び出しを忘れていたバグを修正しました。
 *	  修正前は、メモリプールから常に新しいメモリチャンクを確保し続けて、メモリ使用量が際限無く増えてしまっていました。
 *	  単純なバグですが、Windows環境ではメモリが多いため数時間放置しないとメモリ不足にならず、気付きづらいバグでした。
 */
#include "clip.h"
/****************************************************************************
 *	
 ****************************************************************************/
#ifdef  USE_CBKQUE_SEH
//USE_CBKQUE_SEHシンボルが定義されたら、clipcbkq.c内のDIE()を全て、SEH_throw(CbkQueException)に置き換えます。
#undef  DIE
#ifndef PIECE
  #define DIE() SEH_throw(CbkQueException)
#else //PIECE
  //P/ECEでも上のように定義しても良いのですが、メモリ節約のため下のように定義する事にしました。結果は同じになります。
  //以下の定義はclipseh.hのSEH_throw()の定義に依存しています。もし今後SEH_throw()を変更する時は以下も変更して下さい。
  #define DIE() do { CbkQue_throw(__FILE__, __LINE__); } while(0)
  static void __attribute__((noreturn)) CbkQue_throw(const char* file, int line) {
    SEH_info.file = file;
    SEH_info.line = line;
    SEH_info.msg  = NULL;
    SEH_longjmp((int)CbkQueException); }
#endif//PIECE
//{{2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
//const char CbkQueException[1/*dummy*/];
//↓2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
#ifndef _WIN64
const char CbkQueException[1/*dummy*/];	//32ビット版
#endif//_WIN64
//}}2016/09/04変更:64ビット版では、例外コードをアドレスでなく、一意なFourCC識別子で表すようにしました。
#endif//USE_CBKQUE_SEH
/****************************************************************************
 *	ローカル関数宣言
 ****************************************************************************/
static void StaticCbkQue_Free(ST_StaticCbkQue* pCbkQue);
static void StaticCbkQue_Clear(ST_StaticCbkQue* pCbkQue);
static void StaticCbkQue_Add(ST_StaticCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param);
static void* StaticCbkQue_AddMem(ST_StaticCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes);
static void StaticCbkQue_InvokePri(ST_StaticCbkQue* pCbkQue, int priMin/*SHRT_MIN～SHRT_MAX*/, int priMax/*SHRT_MIN～SHRT_MAX*/);
/*--------------------------------------------------------------------------*/
static ST_CbkQue* DynamicCbkQue_Init(ST_DynamicCbkQue* pCbkQue, int chunkBytes/*0可*/);	//この関数は、モジュール内には存在しますが、アプリケーションには公開しません。アプリケーションの観点からは、動的コールバックキューを静的に初期化する事は、無意味だからです。
static void DynamicCbkQue_Free(ST_DynamicCbkQue* pCbkQue);
static void DynamicCbkQue_Clear(ST_DynamicCbkQue* pCbkQue);
static void DynamicCbkQue_Add(ST_DynamicCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param);
static void* DynamicCbkQue_AddMem(ST_DynamicCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes);
static void DynamicCbkQue_InvokePri(ST_DynamicCbkQue* pCbkQue, int priMin/*SHRT_MIN～SHRT_MAX*/, int priMax/*SHRT_MIN～SHRT_MAX*/);
/*--------------------------------------------------------------------------*/
static int CbkQueNode_Compar(const void* _x, const void* _y, void* arg);
/****************************************************************************
 *	静的コールバックキュー(動的メモリ確保を伴わない)
 ****************************************************************************/
//静的コールバックキューを、動的に作成する。
ST_CbkQue* StaticCbkQue_New(int bufBytes/*4N,0不可*/) {
	ST_StaticCbkQue* pCbkQue;
	//バッファのバイト数は、ワードサイズの倍数,かつ,0以外でなければいけない。
	if(!bufBytes || (bufBytes & 3)) { DIE(); }
	//構造体のメモリを確保する。
	pCbkQue = malloc(sizeof(ST_StaticCbkQue) + bufBytes);
	if(!pCbkQue) { DIE(); }
	//構造体を初期化する。
	return StaticCbkQue_Init(pCbkQue, bufBytes);
}
/*--------------------------------------------------------------------------*/
//静的コールバックキューを、静的に初期化する。
ST_CbkQue* StaticCbkQue_Init(ST_StaticCbkQue* pCbkQue, int bufBytes/*4N,0不可*/) {
	//バッファのバイト数は、ワードサイズの倍数,かつ,0以外でなければいけない。
	if(!bufBytes || (bufBytes & 3)) { DIE(); }
	//バッファの容量(ワード数)を格納する。
	pCbkQue->bufWords = bufBytes >> 2;
	//コールバックキューを空の状態に戻す。
	StaticCbkQue_Clear(pCbkQue);
	//静的コールバックキューを、ST_StaticCbkQueとST_DynamicCbkQueの共通部分にキャストして返す。
	return (ST_CbkQue*)pCbkQue;
}
/*--------------------------------------------------------------------------*/
//動的に作成した、静的コールバックキューを解放する。
static void StaticCbkQue_Free(ST_StaticCbkQue* pCbkQue) {
	//構造体のメモリを解放する。
	free(pCbkQue);
}
/*--------------------------------------------------------------------------*/
//静的コールバックキューを、空の状態に戻す。
static void StaticCbkQue_Clear(ST_StaticCbkQue* pCbkQue) {
	//次にノードを登録する位置を、バッファの先頭に戻す。
	pCbkQue->nodeTail = 0;
	//最後に確保したメモリの位置を、バッファの末尾に戻す。
	pCbkQue->memHead  = pCbkQue->bufWords;
}
/*--------------------------------------------------------------------------*/
//呼び出し側が指定した任意の値をパラメータとして、ノードを登録する。
static void StaticCbkQue_Add(ST_StaticCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param) {
	int* const buf = (int*)(pCbkQue + 1);
	int priOrd, newNodeTail;
	ST_CbkQueNode* pNode;
	//ノードを登録した後の、次にノードを登録する位置を求める。
	// - この下で例外を投入する可能性が有るので、まだ格納してはいけない。
	newNodeTail = pCbkQue->nodeTail + (sizeof(ST_CbkQueNode) / sizeof(int));
	//ノードを登録,及び,メモリを確保するとバッファの容量を超えるならば、エラー終了,又は,例外を投入する。
	if(newNodeTail > pCbkQue->memHead) { DIE(); }
	//次にノードを登録する位置を格納する。
	pCbkQue->nodeTail = newNodeTail;
	//優先度(符号付き)の下位16ビットに、登録順序(符号無し)の下駄を履かせる。
	// - こうした理由は、優先度が同じ場合、登録順序の通りにコールバックする方が、使い易いと思ったからです。
	//   もし優先度だけで比較すると、qsort_r()が不安定ソートなため、同じ優先度の場合に前後が不定になります。
	//   そこで、登録順序(符号無し)の下駄を履かせて、(優先度|登録順序)として見ると同じ値のノードが生じないようにしました。
	// - 登録順序は、厳密には下記①の方法で求めるのが正しいですが、実用上は下記②の方法でも問題無ありません。
	//   登録順序は昇順にさえなっていれば良く、厳密に0ベースで+1づつ並んでいる必要は無いからです。
	//   ①の方法です、順序番号は(0,1,2,...,65534,65535)となり、最大65536個のノードを登録出来ます。
	//   ②の方法では、登録順序は(3,6,9,...,65532,65535)となり、最大21845個のノードを登録出来ます。
	//   実用上は、高々数百～数千個登録出来れば充分だと思うので、計算量が少ない②の方法を使う事にしました。
	// - もし今後、(21845+1)個以上のノードを登録する必要が生じた場合は、①の方法に変更して下さい。
  #if 0	//①計算量が多いが厳密な方法
	if(((pri < SHRT_MIN) || (pri > SHRT_MAX)) ||
	   ((unsigned)((newNodeTail / (sizeof(ST_CbkQueNode) / sizeof(int))) - 1) > USHRT_MAX)) { DIE(); }
	priOrd = (pri << 16) | ((newNodeTail / (sizeof(ST_CbkQueNode) / sizeof(int))) - 1);
  #else	//②計算量が少なく実用上は問題無い方法
	if(((pri < SHRT_MIN) || (pri > SHRT_MAX)) ||
	   ((unsigned)newNodeTail > USHRT_MAX)) { DIE(); }
	priOrd = (pri << 16) | newNodeTail;
  #endif
	//今回登録したノードの内容を設定する。
	pNode = (ST_CbkQueNode*)&buf[newNodeTail - (sizeof(ST_CbkQueNode) / sizeof(int))];
	pNode->priOrd	= priOrd;
	pNode->fn	= fn;
	pNode->param	= param;
}
/*--------------------------------------------------------------------------*/
//メモリを確保し、確保したメモリのポインタをパラメータとして、ノードを登録する。
//当関数は、確保したメモリのポインタを返す。
//呼び出し側は、当関数を呼び出した直後で(,又は,遅くともCbkQue_Invoke()を呼び出す前に)、受け取ったメモリの内容を適切に設定せよ。
static void* StaticCbkQue_AddMem(ST_StaticCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes) {
	int* const buf = (int*)(pCbkQue + 1);
	int priOrd, newNodeTail, newMemHead;
	void* param;
	ST_CbkQueNode* pNode;
	//ノードを登録した後の、次にノードを登録する位置を求める。
	// - この下で例外を投入する可能性が有るので、まだ格納してはいけない。
	newNodeTail = pCbkQue->nodeTail + (sizeof(ST_CbkQueNode) / sizeof(int));
	//メモリを確保した後の、最後に確保したメモリの位置を求める。
	// - この下で例外を投入する可能性が有るので、まだ格納してはいけない。
	newMemHead = pCbkQue->memHead - ((memBytes + 3) >> 2)/*バイト単位⇒ワード単位に変換して切り上げる*/;
	//ノードを登録,及び,メモリを確保するとバッファの容量を超えるならば、エラー終了,又は,例外を投入する。
	if(newNodeTail > newMemHead) { DIE(); }
	//次にノードを登録する位置を格納する。
	pCbkQue->nodeTail = newNodeTail;
	//最後に確保したメモリの位置を格納する。
	pCbkQue->memHead = newMemHead;
	//最後に確保したメモリのポインタを取得する。
	param = &buf[newMemHead];
	//優先度(符号付き)の下位16ビットに、登録順序(符号無し)の下駄を履かせる。
	// - こうした理由は、優先度が同じ場合、登録順序の通りにコールバックする方が、使い易いと思ったからです。
	//   もし優先度だけで比較すると、qsort_r()が不安定ソートなため、同じ優先度の場合に前後が不定になります。
	//   そこで、登録順序(符号無し)の下駄を履かせて、(優先度|登録順序)として見ると同じ値のノードが生じないようにしました。
	// - 登録順序は、厳密には下記①の方法で求めるのが正しいですが、実用上は下記②の方法でも問題無ありません。
	//   登録順序は昇順にさえなっていれば良く、厳密に0ベースで+1づつ並んでいる必要は無いからです。
	//   ①の方法です、順序番号は(0,1,2,...,65534,65535)となり、最大65536個のノードを登録出来ます。
	//   ②の方法では、登録順序は(3,6,9,...,65532,65535)となり、最大21845個のノードを登録出来ます。
	//   実用上は、高々数百～数千個登録出来れば充分だと思うので、計算量が少ない②の方法を使う事にしました。
	// - もし今後、(21845+1)個以上のノードを登録する必要が生じた場合は、①の方法に変更して下さい。
  #if 0	//①計算量が多いが厳密な方法
	if(((pri < SHRT_MIN) || (pri > SHRT_MAX)) ||
	   ((unsigned)((newNodeTail / (sizeof(ST_CbkQueNode) / sizeof(int))) - 1) > USHRT_MAX)) { DIE(); }
	priOrd = (pri << 16) | ((newNodeTail / (sizeof(ST_CbkQueNode) / sizeof(int))) - 1);
  #else	//②計算量が少なく実用上は問題無い方法
	if(((pri < SHRT_MIN) || (pri > SHRT_MAX)) ||
	   ((unsigned)newNodeTail > USHRT_MAX)) { DIE(); }
	priOrd = (pri << 16) | newNodeTail;
  #endif
	//今回登録したノードの内容を設定する。
	pNode = (ST_CbkQueNode*)&buf[newNodeTail - (sizeof(ST_CbkQueNode) / sizeof(int))];
	pNode->priOrd	= priOrd;
	pNode->fn	= fn;
	pNode->param	= param;
	//最後に確保したメモリのポインタを返す。
	return param;
}
/*--------------------------------------------------------------------------*/
//登録されているノードのうち、優先度がpriMin以上,priMax以下のノードだけを呼び出す。
static void StaticCbkQue_InvokePri(ST_StaticCbkQue* pCbkQue, int priMin/*SHRT_MIN～SHRT_MAX*/, int priMax/*SHRT_MIN～SHRT_MAX*/) {
	int* const buf = (int*)(pCbkQue + 1);
	int priOrdMin, priOrdMax, nNode;
	ST_CbkQueNode* pNode;
	//優先度(符号付き)の最大値,最小値の下位16ビットに、登録順序(符号無し)の下駄を履かせる。
	if(((priMin < SHRT_MIN) || (priMin > SHRT_MAX)) ||
	   ((priMax < SHRT_MIN) || (priMax > SHRT_MAX))) { DIE(); }	//ここで停止したら呼び出し側のバグです。
	priOrdMin = (priMin << 16) |         0;
	priOrdMax = (priMax << 16) | USHRT_MAX;
	//登録されているノードの数を求める。
	nNode = pCbkQue->nodeTail / (sizeof(ST_CbkQueNode) / sizeof(int));
	//登録されているノードを、(優先度|登録順序)によってソートする。
	// - (priOrdMin,priOrdMin+1,...,priOrdMax-1,priOrdMax,priOrdMax+1,...INT_MAX,INT_MIN,...,priOrdMin-1)という順になる。
	//    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~この範囲を呼び出す。
	pNode = (ST_CbkQueNode*)buf;
//{{2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)priOrdMin);
//↓2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)priOrdMin);		//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
#else //_WIN64
	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)(intptr_t)priOrdMin);	//64ビット版は、(int)(32ビット)⇒(void*)(64ビット)に直接キャストしようとすると警告が出ます。(int)(32ビット)⇒(intptr_t)(64ビット)⇒(void*)(64ビット)の順でキャストすれば警告は出ません。
#endif//_WIN64
//}}2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	//(優先度|登録順序)の昇順に…
	while(--nNode >= 0) {
		//このノードの(優先度|登録順序)が、指定された(優先度|登録順序)の範囲を外れたら、抜ける。
		if((pNode->priOrd < priOrdMin) || (pNode->priOrd > priOrdMax)) { break; }
		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~こちらの条件も必要である事に注意せよ。(priMax>SHRT_MIN),かつ,(priMax=SHRT_MAX)が指定された場合、ソート後の並びが(pNode->priOrd>priOrdMax)を満たす前に、pNode->priOrdがINT_MAX⇒INT_MINに折り返す可能性が有る。
		//このノードの関数を呼び出す。
		(*pNode->fn)((ST_CbkQue*)pCbkQue, (pNode->priOrd >> 16), pNode->param);
		//                                ~~~~~~~~~~~~~~~~~~~~~(優先度|登録順序)⇒優先度に戻して関数へ渡す。
		//次のノードへ進める。
		pNode++;
	}
	//ここで自動的にCbkQue_Clear()を *呼び出さない* 事にしました。
	//毎回コールバックキューを登録するか,又は,一度登録したコールバックキューを何度も使い回すかは、アプリケーション次第だと思ったからです。
	//毎回コールバックキューを登録する場合は、アプリケーションが明示的にCbkQue_Invoke()の直後でCbkQue_Clear()を呼び出して下さい。
}
/****************************************************************************
 *	動的コールバックキュー(動的メモリ確保を伴う)
 ****************************************************************************/
//動的コールバックキューを、動的に作成する。
ST_CbkQue* DynamicCbkQue_New(int chunkBytes/*0可*/) {
	ST_DynamicCbkQue* pCbkQue;
	//構造体のメモリを確保する。
	pCbkQue = malloc(sizeof(ST_DynamicCbkQue));
	if(!pCbkQue) { DIE(); }
	//構造体を初期化する。
	return DynamicCbkQue_Init(pCbkQue, chunkBytes);
}
/*--------------------------------------------------------------------------*/
//動的コールバックキューを、静的に初期化する。
static ST_CbkQue* DynamicCbkQue_Init(ST_DynamicCbkQue* pCbkQue, int chunkBytes/*0可*/) {	//この関数は、モジュール内には存在しますが、アプリケーションには公開しません。アプリケーションの観点からは、動的コールバックキューを静的に初期化する事は、無意味だからです。
	//ダミーフィールドをクリアする。
	pCbkQue->dummy = 0;	//ST_StaticCbkQueとST_DynamicCbkQueを区別するためにクリア必須です。
	//ノード配列を作成する。
	pCbkQue->nodeArray = g_array_new(0, 0, sizeof(ST_CbkQueNode));
	//メモリプールを作成する。
	pCbkQue->memPool = MemPool_New(chunkBytes/*0可*/);
	//コールバックキューを空の状態に戻す。
	DynamicCbkQue_Clear(pCbkQue);
	//動的コールバックキューを、ST_StaticCbkQueとST_DynamicCbkQueの共通部分にキャストして返す。
	return (ST_CbkQue*)pCbkQue;
}
/*--------------------------------------------------------------------------*/
//動的に作成した、動的コールバックキューを解放する。
static void DynamicCbkQue_Free(ST_DynamicCbkQue* pCbkQue) {
	//ノード配列を解放する。
	g_array_unref(pCbkQue->nodeArray);
	//メモリプールを解放する。
	MemPool_Free(pCbkQue->memPool);
	//構造体のメモリを解放する。
	free(pCbkQue);
}
/*--------------------------------------------------------------------------*/
//動的コールバックキューを、空の状態に戻す。
static void DynamicCbkQue_Clear(ST_DynamicCbkQue* pCbkQue) {
	//次にノードを登録する位置を、ノード配列の先頭に戻す。
	pCbkQue->nodeTail = 0;
//{{2016/10/26追加:DynamicCbkQue_Clear()の中で、MemPool_Clear()の呼び出しを忘れていたバグを修正しました。
	//メモリプールを空に戻す。
	MemPool_Clear(pCbkQue->memPool);
//}}2016/10/26追加:DynamicCbkQue_Clear()の中で、MemPool_Clear()の呼び出しを忘れていたバグを修正しました。
}
/*--------------------------------------------------------------------------*/
//呼び出し側が指定した任意の値をパラメータとして、ノードを登録する。
static void DynamicCbkQue_Add(ST_DynamicCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param) {
	int priOrd;
	ST_CbkQueNode* pNode;
	//ノード配列が一杯ならば、ノード配列のサイズを増やす。
	if(pCbkQue->nodeArray->len == pCbkQue->nodeTail) {
		g_array_set_size(pCbkQue->nodeArray, pCbkQue->nodeTail + 1);	//実際にはg_array_set_size()側で内部的に余裕を持って増やされるので、呼び出し側では1づつ増やしても効率低下の問題は生じません。
	}
	//ノードを登録する位置を求める。
	pNode = &g_array_index(pCbkQue->nodeArray, ST_CbkQueNode, pCbkQue->nodeTail);	//g_array_index()はマクロなので、g_array_index()の引数の中で副作用が有る式を書くのは危険です。(実際には大丈夫ですが)
	//優先度(符号付き)の下位16ビットに、登録順序(符号無し)の下駄を履かせる。
	if(((pri < SHRT_MIN) || (pri > SHRT_MAX)) ||
	   ((unsigned)pCbkQue->nodeTail > USHRT_MAX)) { DIE(); }
	priOrd = (pri << 16) | pCbkQue->nodeTail;
	//次にノードを登録する位置を進める。
	pCbkQue->nodeTail++;
	//今回登録したノードの内容を設定する。
	pNode->priOrd	= priOrd;
	pNode->fn	= fn;
	pNode->param	= param;
}
/*--------------------------------------------------------------------------*/
//メモリを確保し、確保したメモリのポインタをパラメータとして、ノードを登録する。
//当関数は、確保したメモリのポインタを返す。
//呼び出し側は、当関数を呼び出した直後で(,又は,遅くともCbkQue_Invoke()を呼び出す前に)、受け取ったメモリの内容を適切に設定せよ。
static void* DynamicCbkQue_AddMem(ST_DynamicCbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes) {
	void* param;
	//メモリを確保する。
	param = MemPool_Alloc(pCbkQue->memPool, memBytes);
	//確保したメモリをパラメータとして、ノードを登録する。
	DynamicCbkQue_Add(pCbkQue, pri, fn, param);
	//確保したメモリのポインタを返す。
	return param;
}
/*--------------------------------------------------------------------------*/
//登録されているノードのうち、優先度がpriMin以上,priMax以下のノードだけを呼び出す。
static void DynamicCbkQue_InvokePri(ST_DynamicCbkQue* pCbkQue, int priMin/*SHRT_MIN～SHRT_MAX*/, int priMax/*SHRT_MIN～SHRT_MAX*/) {
	int priOrdMin, priOrdMax, nNode;
	ST_CbkQueNode* pNode;
	//優先度(符号付き)の最大値,最小値の下位16ビットに、登録順序(符号無し)の下駄を履かせる。
	if(((priMin < SHRT_MIN) || (priMin > SHRT_MAX)) ||
	   ((priMax < SHRT_MIN) || (priMax > SHRT_MAX))) { DIE(); }	//ここで停止したら呼び出し側のバグです。
	priOrdMin = (priMin << 16) |         0;
	priOrdMax = (priMax << 16) | USHRT_MAX;
	//登録されているノードの数を求める。
	nNode = pCbkQue->nodeTail;
	//登録されているノードを、(優先度|登録順序)によってソートする。
	// - (priOrdMin,priOrdMin+1,...,priOrdMax-1,priOrdMax,priOrdMax+1,...INT_MAX,INT_MIN,...,priOrdMin-1)という順になる。
	//    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~この範囲を呼び出す。
	pNode = pCbkQue->nodeArray->data;
//{{2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)priOrdMin);
//↓2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)priOrdMin);		//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
#else //_WIN64
	qsort_r(pNode, nNode, sizeof(ST_CbkQueNode), CbkQueNode_Compar, (void*)(intptr_t)priOrdMin);	//64ビット版は、(int)(32ビット)⇒(void*)(64ビット)に直接キャストしようとすると警告が出ます。(int)(32ビット)⇒(intptr_t)(64ビット)⇒(void*)(64ビット)の順でキャストすれば警告は出ません。
#endif//_WIN64
//}}2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	//(優先度|登録順序)の昇順に…
	while(--nNode >= 0) {
		//このノードの(優先度|登録順序)が、指定された(優先度|登録順序)の範囲を外れたら、抜ける。
		if((pNode->priOrd < priOrdMin) || (pNode->priOrd > priOrdMax)) { break; }
		// ~~~~~~~~~~~~~~~~~~~~~~~~~~~こちらの条件も必要である事に注意せよ。(priMax>SHRT_MIN),かつ,(priMax=SHRT_MAX)が指定された場合、ソート後の並びが(pNode->priOrd>priOrdMax)を満たす前に、pNode->priOrdがINT_MAX⇒INT_MINに折り返す可能性が有る。
		//このノードの関数を呼び出す。
		(*pNode->fn)((ST_CbkQue*)pCbkQue, (pNode->priOrd >> 16), pNode->param);
		//                                ~~~~~~~~~~~~~~~~~~~~~(優先度|登録順序)⇒優先度に戻して関数へ渡す。
		//次のノードへ進める。
		pNode++;
	}
	//ここで自動的にCbkQue_Clear()を *呼び出さない* 事にしました。
	//毎回コールバックキューを登録するか,又は,一度登録したコールバックキューを何度も使い回すかは、アプリケーション次第だと思ったからです。
	//毎回コールバックキューを登録する場合は、アプリケーションが明示的にCbkQue_Invoke()の直後でCbkQue_Clear()を呼び出して下さい。
}
/****************************************************************************
 *	共通処理
 ****************************************************************************/
//動的に作成したコールバックキューを解放する。
void CbkQue_Free(ST_CbkQue* pCbkQue) {
	if(pCbkQue->isStatic) {
		StaticCbkQue_Free((ST_StaticCbkQue*)pCbkQue);
	} else {
		DynamicCbkQue_Free((ST_DynamicCbkQue*)pCbkQue);
	}
}
/*--------------------------------------------------------------------------*/
//コールバックキューを空の状態に戻す。
void CbkQue_Clear(ST_CbkQue* pCbkQue) {
	if(pCbkQue->isStatic) {
		StaticCbkQue_Clear((ST_StaticCbkQue*)pCbkQue);
	} else {
		DynamicCbkQue_Clear((ST_DynamicCbkQue*)pCbkQue);
	}
}
/*--------------------------------------------------------------------------*/
//呼び出し側が指定した任意の値をパラメータとして、ノードを登録する。
void CbkQue_Add(ST_CbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), void* param) {
	if(pCbkQue->isStatic) {
		StaticCbkQue_Add((ST_StaticCbkQue*)pCbkQue, pri, fn, param);
	} else {
		DynamicCbkQue_Add((ST_DynamicCbkQue*)pCbkQue, pri, fn, param);
	}
}
/*--------------------------------------------------------------------------*/
//メモリを確保し、確保したメモリのポインタをパラメータとして、ノードを登録する。
//当関数は、確保したメモリのポインタを返す。
//呼び出し側は、当関数を呼び出した直後で(,又は,遅くともCbkQue_Invoke()を呼び出す前に)、受け取ったメモリの内容を適切に設定せよ。
void* CbkQue_AddMem(ST_CbkQue* pCbkQue, int pri/*SHRT_MIN～SHRT_MAX*/, void (*fn)(ST_CbkQue* pCbkQue, int pri, void* param), int memBytes) {
	if(pCbkQue->isStatic) {
		return StaticCbkQue_AddMem((ST_StaticCbkQue*)pCbkQue, pri, fn, memBytes);
	} else {
		return DynamicCbkQue_AddMem((ST_DynamicCbkQue*)pCbkQue, pri, fn, memBytes);
	}
}
/*--------------------------------------------------------------------------*/
//登録されているノードを全て呼び出す。
void CbkQue_Invoke(ST_CbkQue* pCbkQue) {
	CbkQue_InvokePri(pCbkQue, SHRT_MIN, SHRT_MAX);
}
/*--------------------------------------------------------------------------*/
//登録されているノードのうち、優先度がpriMin以上,priMax以下のノードだけを呼び出す。
void CbkQue_InvokePri(ST_CbkQue* pCbkQue, int priMin/*SHRT_MIN～SHRT_MAX*/, int priMax/*SHRT_MIN～SHRT_MAX*/) {
	if(pCbkQue->isStatic) {
		StaticCbkQue_InvokePri((ST_StaticCbkQue*)pCbkQue, priMin, priMax);
	} else {
		DynamicCbkQue_InvokePri((ST_DynamicCbkQue*)pCbkQue, priMin, priMax);
	}
}
/*--------------------------------------------------------------------------*/
static int CbkQueNode_Compar(const void* _x, const void* _y, void* arg) {
//{{2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
//	int priOrdMin = (int)arg;
//↓2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
#ifndef _WIN64
	int priOrdMin = (int)arg;	//32ビット版は、これまでと変更有りません。32ビット版も64ビット版と同じ書き方にしても動作は変わらないのですが、簡潔さを優先してこれまでのままとする事にしました。
#else //_WIN64
	int priOrdMin = (intptr_t)arg;	//64ビット版は、(void*)(64ビット)⇒(int)(32ビット)にキャストしようとすると警告が出ます。元の値が32ビットの整数パラメータなので(int)にキャストしても安全ではあるのですが、警告を回避するために(void*)(64ビット)⇒(intptr_t)(64ビット)⇒暗黙の(int)(32ビット)へのキャスト、という手順を取る事にしました。
#endif//_WIN64
//}}2016/09/04変更:(int)値を(void*)にキャストして整数パラメータとして使用する箇所で、64ビット版ではコンパイラの警告が出ていた問題を回避しました。
	unsigned x = ((ST_CbkQueNode*)_x)->priOrd - priOrdMin;	//(優先度|登録順序)をpriOrdMinベース,符号無しに変換する。
	unsigned y = ((ST_CbkQueNode*)_y)->priOrd - priOrdMin;	//(優先度|登録順序)をpriOrdMinベース,符号無しに変換する。
	if(x < y) { return -1; }	//符号無し比較
	if(x > y) { return  1; }	//符号無し比較
	DIE();	//(優先度|登録順序)が同じノードは生じないはずです。もしここで停止したら当モジュールのバグです。
}
/****************************************************************************
 *	使用例
 ****************************************************************************/
//※TODO:使用例を書け。
