Newer
Older
bashrc / .bash.d / tmp / util / src / util_queue.c
Nomura.Kei on 24 Jan 2020 9 KB UPDATE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "util_thread.h"
#include "util_queue.h"



/* =============================================================================
 *  構造体定義
 * =============================================================================
 */

/**
 * キューのエントリ構造体
 */
struct queue_entry {
	size_t size;		/**< 実データサイズ			*/
	char*  data;		/**< データバッファ			*/
};
 
 
/**
 * キューのインスタンス構造体
 */
struct queue {
	size_t              capacity;			/**< キューの容量		*/
	size_t              size;				/**< キューのサイズ		*/
	size_t              max_data_size;		/**< データの最大サイズ	*/
	struct queue_entry* entries;			/**< データのエントリ	*/
	struct queue_entry* head;				/**< キューの取出位置	*/
	struct queue_entry* tail;				/**< キューの挿入位置	*/
};



/* =============================================================================
 *  内部関数プロトタイプ宣言
 * =============================================================================
 */
static struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry);
static bool queue_is_enabled(struct queue* queue, struct queue_entry* entry);



/* =============================================================================
 *  公開関数
 * =============================================================================
 */

/**
 * キューを生成します。
 * インスタンスを生成できない場合、NULL を返します。
 *
 * [注意]
 * このキューは、同期化されません。
 * 同期化が必要な場合は、blocking_queue を使用してください。
 *
 * @param cap  キューの容量(格納可能なデータの個数)
 * @param size キューに格納する1つあたりのデータサイズ
 * @return キューのインスタンス
 */
/* [実装メモ]
 * キューのデータ管理構造
 * メモリ確保を 1 回の malloc でできるようにしている。
 * +-------------------+
 * | queue 構造体      |
 * +-------------------+ 
 * | entry[0]          |
 * +-------------------+
 *           :
 * +-------------------+
 * | entry[cap-1]      |
  +-------------------+
 * | entry[0].data     |
 * | 用バッファ        |
 * +-------------------+
 *           :
 * +-------------------+
 * | entry[cap-1].data |
 * | 用バッファ        |
 * +-------------------+
 */
struct queue* queue_new(size_t cap, size_t size)
{
	size_t queue_size   = sizeof(struct queue)
						+ ((sizeof(struct queue_entry) + size) * cap);
	struct queue* queue = (struct queue*) malloc(queue_size);
	if (queue != NULL)
	{
		queue->capacity      = cap;
		queue->size          = 0;
		queue->max_data_size = size;
		queue->entries       = (struct queue_entry*) (queue + 1);
		queue->head          = queue->entries;
		queue->tail          = queue->entries;

		
		char*  tmp_data_ptr           = (char*) (queue->entries + queue->capacity);
		struct queue_entry* tmp_entry = queue->entries;
		for (size_t i = 0; i < queue->capacity; i++)
		{
			tmp_entry->data = tmp_data_ptr;
			tmp_entry++;
			tmp_data_ptr += queue->max_data_size;
		}
	}
	return queue;
}



/**
 * 指定されたキューを破棄します。
 *
 * @param queue キューのインスタンス
 */
void queue_destroy(struct queue* queue)
{
	free(queue);
}


/**
 * キューにデータを追加します。
 *
 * @param queue キューのインスタンス
 * @param data  キューに追加するエントリ(データ)
 * @param size  キューに追加するエントリ(データ)のサイズ
 * @return true/false (追加成功/追加失敗)
 */
bool queue_push(struct queue* queue, void* data, size_t size)
{
	bool result = false;

	if (size < queue->max_data_size)
	{
		if (queue->size < queue->capacity)
		{
			memset(queue->tail->data, 0x00, sizeof(struct queue_entry));
			memcpy(queue->tail->data, data, size);
			queue->tail->size = size;
			queue->tail++;
			if (queue->tail >= (queue->entries + queue->capacity))
			{
				queue->tail = queue->entries;
			}
			queue->size++;
			result = true;
		}
	}
	return result;
}


/**
 * キューの先頭よりデータを取り出します。
 * データの取り出しに失敗した場合、0を返します。
 *
 * @param queue  キューのインスタンス
 * @param buf    取り出したデータ格納用バッファ
 * @param buflen バッファのサイズ
 * @return 取り出したデータのサイズ
 */
size_t queue_pop( struct queue* queue, void* buf, size_t buflen)
{
	size_t size = 0;
	if (queue->size > 0)
	{
		if (queue->head->size < buflen)
		{
			size = queue->head->size;
			memset(buf, 0x00, buflen);
			memcpy(buf, queue->head->data, size);

			// データクリア (なくても動作上問題ない)
			memset(queue->head->data, 0x00, queue->max_data_size);
			queue->head->size = 0;

			queue->head++;
			if (queue->head >= (queue->entries + queue->capacity))
			{
				queue->head = queue->entries;
			}
			queue->size--;
		}
	}
	return size;
}


/**
 * キューの先頭よりデータを取り出します。
 * データの取り出しに失敗した場合、0を返します。
 * queue_pop と異なり、キューからデータは削除されません。
 *
 * @param queue  キューのインスタンス
 * @param buf    取り出したデータ格納用バッファ
 * @param buflen バッファのサイズ
 * @return 取り出したデータのサイズ
 */
size_t queue_peek( struct queue* queue, void* buf, size_t buflen)
{
	size_t size = 0;
	if (queue->size > 0)
	{
		if (queue->head->size < buflen)
		{
			size = queue->head->size;
			memset(buf, 0x00, buflen);
			memcpy(buf, queue->head->data, size);
		}
	}
	return size;
}


/**
 * キューのサイズ(キューに入っているデータの個数)を取得します。
 *
 * @param queue キューのインスタンス
 * @return キューのサイズ
 */
size_t queue_size(struct queue* queue)
{
	return queue->size;
}


/**
 * キューに格納されている全エントリーを引数に、指定された handler を呼び出します。
 *
 * handler の引数:
 * - data : データ
 * - size : データのサイズ
 *
 * @param queue キューのインスタンス
 * @param handler ハンドラ
 */
void queue_entries(struct queue* queue, bool (*handler)(void* data, size_t size))
{
	bool   is_continue = true;
	size_t counter     = queue->size;
	struct queue_entry* entry = queue->head;
	while (is_continue && (counter > 0))
	{
		is_continue = handler(entry->data, entry->size);
		entry       = queue_next_entry(queue, entry);
		counter--;
	}
}


/**
 * キューインスタンスの全エントリを引数に、指定された handler を呼び出します。
 * この関数は、デバッグ用の関数です。
 * handler には、実際にデータが入っていないキューのエントリも渡されます。
 *
 * handler の引数:
 * - data    : データ
 * - size    : データのサイズ
 * - index   : キューの管理上のインデックス
 * - enabled : true/false (有効なデータ/無効なデータ)
 * - arg     : ユーザーデータ
 *
 * @param queue   キューのインスタンス
 * @param handler ハンドラ
 * @param arg     ハンドラに渡すユーザーデータ
 */
void queue_entries_full(struct queue* queue,
   	bool (*handler)(void* data, size_t size, int index, bool enabled, void* arg),
	void* arg)
{
	bool is_enabled;
	bool is_continue = true;
	struct queue_entry* entry = queue->entries;
	for (int i = 0; is_continue && (i < (int) queue->capacity); i++)
	{
		is_enabled = queue_is_enabled(queue, entry);
		is_continue = handler(entry->data, entry->size, i, is_enabled, arg);
		entry++;
	}
}



/* =============================================================================
 *  内部関数
 * =============================================================================
 */


/**
 * 指定されたエントリの次のエントリーを取得します。
 * エントリー配列の末尾に到達した場合、先頭のエントリを返します。
 *
 * @param queue  キューのインスタンス
 * @param entry  エントリ
 * @return 次のエントリ
 */
static
struct queue_entry* queue_next_entry(struct queue* queue, struct queue_entry* entry)
{
	struct queue_entry* next_entry = entry;
	next_entry++;
	if (next_entry >= (queue->entries + queue->capacity))
	{
		next_entry = queue->entries;
	}
	return next_entry;
}


/**
 * 指定されたエントリが、有効か否かを返します。
 *
 * @param queue キューのインスタンス
 * @param entry エントリ
 * @return true/false (有効/無効)
 */
static
bool queue_is_enabled(struct queue* queue, struct queue_entry* entry)
{
	bool is_enabled;
	if ((queue->size != queue->capacity) && (queue->head <= queue->tail))
	{	/* キューのエントリ配列が以下のような状態の場合
		 * [- |- |HT-|- |- |- |- |- ]
		 * [- |- |Ho |o |o |T-|- |- ]  (-:空/o:データ有/H:Head/T:Tail)
		 */
		is_enabled = ((queue->head <= entry) && (entry < queue->tail));
	}
	else
	{	/* キューのエントリ配列が以下のような状態の場合
		 * [o |o |HTo|o |o |o |o |o ]
		 * [o |o |T- |- |- |Ho|o |o ]  (-:空/o:データ有/H:Head/T:Tail)
		 */
		is_enabled = ((entry < queue->tail) || (queue->head <= entry));
	}
	return is_enabled;
}