/*
 *	obstack.c
 *
 *	Obstack - Pool of memory containing a stack of objects.
 *
 *	* Mon Oct 05 21:33:13 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	- 「Obstack」(http://gcc.gnu.org/onlinedocs/libiberty/Obstacks.html)互換モジュールです。
 *	  関数仕様は「Obstack」と同じで、実装は独自です。
 *	  オリジナル版の「Obstack」は速度を重視してやや複雑ですが、当実装は単純に実装しました。
 *	- 関数仕様や使い方の説明は、上記のドキュメントの他、以下のドキュメントも参考になります。
 *	  「Wikipedia - Obstack」(http://en.wikipedia.org/wiki/Obstack)
 *	  「IBM developerWorks Japan - メモリー管理の内側」(http://www.ibm.com/developerworks/jp/linux/library/l-memory/)
 *	* Tue Oct 06 23:01:47 JST 2015 Naoyuki Sawa
 *	- obstack_free()のobject引数指定を対応しました。
 *	  objectにNULL以外を指定すると、object以降(objectも含む)に作成したオブジェクトだけを開放します。
 *	  もっとも実際にそんな処理を行いたいケースはほとんど無く、大抵はobstack_free()のobject引数にはNULLを指定すると思います。
 *	  唯一、object引数指定が役に立つケースとしては、下記の用法ぐらいです。
 *	  │obstack_free(o,obstack_base(o)); //現在作成中のオブジェクトの状態を取り消して0バイトに戻す。
 */
#include "clip.h"
/****************************************************************************
 *	
 ****************************************************************************/
struct _obstack_chunk {
	struct _obstack_chunk*	prev;			//これまでに完成した個々のオブジェクトを含む古いチャンクへのチェイン。
	int			size;			//現在作成中のオブジェクトサイズ。
	int			room;			//現在の残り容量。
	char			data[0/*size*/];	//現在作成中のオブジェクト。
};
/*--------------------------------------------------------------------------*/
static void _obstack_newchunk(struct obstack* o, int size) {
	struct _obstack_chunk* chunk = o->chunk;
	//既存のチャンクが有れば…
	if(chunk) {
		//残り容量が不足ならば…
		if(chunk->room < size) {
			//(現在作成中のオブジェクトサイズ+追加サイズ)を単位サイズの倍数に切り上げたサイズとする。
			size = (chunk->size + size + (obstack_chunk_size(o) - 1)) & ~(obstack_chunk_size(o) - 1);
			chunk = realloc(chunk, sizeof(struct _obstack_chunk) + size);
			if(!chunk) { DIE(); }
			//残り容量を設定する。
			chunk->room = size - chunk->size;
		}
	//既存のチャンクが無ければ…
	} else {
		//(初期サイズ)を単位サイズの倍数に切り上げたサイズとする。
		size = (size + (obstack_chunk_size(o) - 1)) & ~(obstack_chunk_size(o) - 1);
		chunk = calloc(sizeof(struct _obstack_chunk) + size, 1);
		if(!chunk) { DIE(); }
		//残り容量を設定する。
		chunk->room = size;
	}
	//チャンクを設定する。
	o->chunk = chunk;
}
/****************************************************************************
 *	
 ****************************************************************************/
//□2.3.1.2 Preparing for Using Obstacks
void obstack_init(struct obstack* o) {
	o->chunk = NULL;		//┐
	_obstack_newchunk(o, 0);	//┴新しいチャンクを作成する。
}
/*--------------------------------------------------------------------------*/
//□2.3.1.3 Allocation in an Obstack
void* obstack_alloc(struct obstack* o, int size) {
	obstack_blank(o, size);
	return obstack_finish(o);
}
void* obstack_copy(struct obstack* o, void* data, int size) {
	obstack_grow(o, data, size);
	return obstack_finish(o);
}
void* obstack_copy0(struct obstack* o, void* data, int size) {
	obstack_grow0(o, data, size);
	return obstack_finish(o);
}
/*--------------------------------------------------------------------------*/
//□2.3.1.4 Freeing Objects in an Obstack
void obstack_free(struct obstack* o, void* object) {
	struct _obstack_chunk* chunk;			//┌本物のobstack_free()の関数仕様では未初期化(o->chunk=NULL)のoに対してobstack_free(o,NULL)を呼び出した時の動作は定義されていないのですが、
	while((chunk = o->chunk)) {			//┴当実装はこのように処理しているので未初期化(o->chunk=NULL)のoに対してobstack_free(o,NULL)を呼び出しても安全です。
		if(chunk->data == object) {
			chunk->room += chunk->size;	//objectが現在作成中のオブジェクトだった場合は、残り容量とオブジェクトサイズの合計が残り容量となる。objectが完成したオブジェクトだった場合は、obstack_finish()にてroom=0にしてあるので、オブジェクトサイズが残り容量となる。
			chunk->size  = 0;		//オブジェクトサイズを0に戻す。
			return;	//ここまで
		}
		o->chunk = chunk->prev;			//┬この処理順序は必須です。
		free(chunk);				//┘
	}
	if(object) { DIE(); }	//objectが指定されたのに、見つからず全て開放してしまった場合は、おそらく呼び出し側のバグです。
}
/*--------------------------------------------------------------------------*/
//□2.3.1.6 Growing Objects
void obstack_blank(struct obstack* o, int size) {
	_obstack_newchunk(o, size);
	o->chunk->size += size;
	o->chunk->room -= size;
}
void obstack_grow(struct obstack* o, void* data, int size) {
	_obstack_newchunk(o, size);
	memcpy(&o->chunk->data[o->chunk->size], data, size);
	o->chunk->size += size;
	o->chunk->room -= size;
}
void obstack_grow0(struct obstack* o, void* data, int size) {
	obstack_grow(o, data, size);
	obstack_1grow(o, '\0');
}
void obstack_1grow(struct obstack* o, int data) {
	obstack_grow(o, &data, 1);
}
void obstack_ptr_grow(struct obstack* o, void* data) {
	obstack_grow(o, &data, sizeof data);				//ワード境界以外でも格納出来るようにした。
}
void obstack_int_grow(struct obstack* o, int data) {
	obstack_grow(o, &data, sizeof data);				//ワード境界以外でも格納出来るようにした。
}
void* obstack_finish(struct obstack* o) {
	struct _obstack_chunk* chunk = o->chunk;
	chunk = realloc(chunk, sizeof(struct _obstack_chunk) + chunk->size);	//このチャンクのオブジェクトサイズは決定したので、余分なメモリを開放する。
	if(!chunk) { DIE(); }							//縮小方向のリサイズは常に成功するはずであるが、念のため確認する事にした。
	chunk->room = 0;							//obstack_free()で残り容量を再設定する場合のために、実態と一致させておく。
	o->chunk = NULL;		//新しいチャンクを作成するために、一旦o->chunkをNULLにする。
	_obstack_newchunk(o, 0);	//新しいチャンクを作成する。
	o->chunk->prev = chunk;		//新しいチャンクに、完成したオブジェクトを含む古いチャンクをチェインする。
	return chunk->data;		//完成したオブジェクトのベースアドレスを返す。
}
/*--------------------------------------------------------------------------*/
//□2.3.1.7 Extra Fast Growing Objects
int obstack_room(struct obstack* o) {
	return o->chunk->room;
}
void obstack_blank_fast(struct obstack* o, int size) {
	if(o->chunk->room < size) { DIE(); }
	o->chunk->size += size;
	o->chunk->room -= size;
}
void obstack_1grow_fast(struct obstack* o, int data) {
	if(o->chunk->room < 1) { DIE(); }
	o->chunk->data[o->chunk->size] = data;
	o->chunk->size++;
	o->chunk->room--;
}
void obstack_ptr_grow_fast(struct obstack* o, void* data) {
	if(o->chunk->room < sizeof data) { DIE(); }
	memcpy(&o->chunk->data[o->chunk->size], &data, sizeof data);	//ワード境界以外でも格納出来るようにした。
	o->chunk->size += sizeof data;
	o->chunk->room -= sizeof data;
}
void obstack_int_grow_fast(struct obstack* o, int data) {
	if(o->chunk->room < sizeof data) { DIE(); }
	memcpy(&o->chunk->data[o->chunk->size], &data, sizeof data);	//ワード境界以外でも格納出来るようにした。
	o->chunk->size += sizeof data;
	o->chunk->room -= sizeof data;
}
/*--------------------------------------------------------------------------*/
//□2.3.1.8 Status of an Obstack
void* obstack_base(struct obstack* o) {
	return o->chunk->data;
}
void* obstack_next_free(struct obstack* o) {
	return &o->chunk->data[o->chunk->size];
}
int obstack_object_size(struct obstack* o) {
	return o->chunk->size;
}
