/*	
 *	clipque.c
 *
 *	ユーティリティ：任意長パケットキュー
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015 Naoyuki Sawa
 *
 *	* Tue Jan 06 21:26:41 JST 2015 Naoyuki Sawa
 *	- 新規作成。
 *	- 「ユーティリティ：任意長パケットキュー」を、clipmisc.*⇒clipque.*へ分離しました。
 *	  使用頻度が低いので、無駄にリンクして容量が増加する事を防ぐためです。
 */
#include "clip.h"

/*****************************************************************************
 *	ユーティリティ：任意長パケットキュー
 *****************************************************************************/

QUEUE*
create_queue(int capacity)
{
	QUEUE* queue;

	if(capacity < 0) {
		DIE();
	}

	/* パケットサイズ格納用のsizeof(int)を加算して、実際のストレージサイズとします。
	 * 最大で、capacityバイトのパケットを一つ、キューに格納できるようにするためです。
	 */
	capacity += sizeof(int);

	/* ヘッダとストレージのメモリを確保します。 */
	queue = malloc(sizeof(QUEUE) + capacity);
	if(!queue) {
		DIE(); /* メモリ不足 */
	}

	/* ヘッダを初期化します。 */
	queue->capacity = capacity;
	queue->size = 0;

	return queue;
}

void
delete_queue(QUEUE* queue)
{
	/* ヘッダとストレージのメモリを開放します。 */
	free(queue);
}

void
clear_queue(QUEUE* queue)
{
	queue->size = 0;
}

int
write_queue(QUEUE* queue, const void* packet/*[length]*/, int length)
{
	unsigned char* storage = (unsigned char*)(queue + 1);

	if(length < 0) {
		DIE();
	}

	/* パケットサイズとパケットデータを追加する空き容量があるか？ */
	if((int)(queue->size + (sizeof(int) + length)) > queue->capacity) {
		return -1;
	}

	/* キューの末尾にパケットサイズとパケットデータを追加します。 */
	PUT_LEWORD(storage + queue->size, length);
	memcpy(storage + queue->size + sizeof(int), packet, length);
	queue->size += (sizeof(int) + length);

	return length;
}

int
read_queue(QUEUE* queue, void* packet/*[maxlength]*/, int maxlength)
{
	unsigned char* storage = (unsigned char*)(queue + 1);
	int length;

	if(maxlength < 0) {
		DIE();
	}

	/* キューが空でないか？ */
	if(!queue->size) {
		return -1;
	}

	/* キューの先頭のパケットサイズを取得します。 */
	if(queue->size < sizeof(int)) {
		DIE(); /* キュー破損？ */
	}
	length = LEWORD(storage);
	if((length < 0) || ((int)(sizeof(int) + length) > queue->size)) {
		DIE(); /* キュー破損？ */
	}

	/* 指定されたメモリ領域に格納できるか？ */
	if(length > maxlength) {
		return -1;
	}

	/* キューの先頭のパケットを、指定されたメモリ領域にコピーします。
	 * ただし、packet=NULLならば、読み出したパケットデータを破棄します。
	 */
	if(packet) {
		memcpy(packet, storage + sizeof(int), length);
	}

	/* キューの先頭のパケットを削除し、その分、キューを詰めます。 */
	queue->size -= (sizeof(int) + length);
	memmove(storage, storage + (sizeof(int) + length), queue->size);

	return length;
}

const void*
peek_queue(QUEUE* queue, int* _length)
{
	unsigned char* storage = (unsigned char*)(queue + 1);
	int length;

	/* キューが空か？ */
	if(!queue->size) {
		return NULL;
	}

	/* キューの先頭のパケットサイズを取得します。 */
	if(queue->size < sizeof(int)) {
		DIE(); /* キュー破損？ */
	}
	length = LEWORD(storage);
	if((length < 0) || ((int)(sizeof(int) + length) > queue->size)) {
		DIE(); /* キュー破損？ */
	}

	/* パケットサイズを格納します。 */
	if(_length) {
		*_length = length;
	}

	/* パケットデータの先頭アドレスを返します。 */
	return storage + sizeof(int);
}

int
queue_space(QUEUE* queue)
{
	int space = queue->capacity - queue->size - sizeof(int);
	if(space < 0) {
		space = 0;
	}
	return space;
}
