#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; }