/* vim: ts=4 sw=4 sts=4 ff=unix fenc=utf-8 :
* =====================================================================
* sc_list.c
* Copyright (c) 2003 - 2011 sys0tem
* LICENSE :
* LGPL (GNU Lesser General Public License - Version 3,29 June 2007)
* http://www.gnu.org/copyleft/lesser.html
* or
* EPL (Eclipse Public License - v1.0)
* http://www.eclipse.org/legal/epl-v10.html
* =====================================================================
*/
#include <string.h>
#include <sc_os.h>
#include <sc_error.h>
#include <sc_mmgr.h>
#include <sc_list.h>
/* ---------------------------------------------------------------------
* プロトタイプ宣言
* ---------------------------------------------------------------------
*/
static void* SC_List_add (SC_List*, int index, const void* obj, size_t size);
static char* SC_List_addStr(SC_List* list, int index, const char* obj);
static void* SC_List_get (SC_List*, int index, size_t* size);
static char* SC_List_getStr(SC_List*, int index);
static void SC_List_remove(SC_List*, int index);
static void SC_List_clear (SC_List*);
static SC_List_Entry* SC_List_search(SC_List* list, int index);
static SC_List_Entry* SC_List_newEntry(const void* obj, size_t size);
SC_List* SC_List_new(void)
{
SC_List* list;
size_t size;
size = sizeof(SC_List) + (sizeof(SC_List_Entry) * 2);
list = (SC_List*) malloc(size);
if (list == NULL)
{ /* メモリ確保失敗 */
return NULL;
}
list->_head = (SC_List_Entry*) (list + 1);
list->_tail = list->_head + 1;
/* 初期設定 */
list->size = 0;
list->_head->next = list->_head->prev = list->_tail;
list->_tail->next = list->_tail->prev = list->_head;
list->_head->value = list->_tail->value = NULL;
list->add = SC_List_add;
list->addStr = SC_List_addStr;
list->get = SC_List_get;
list->getStr = SC_List_getStr;
list->remove = SC_List_remove;
list->clear = SC_List_clear;
return list;
}
/**
* リストを破棄します。
* [注意] 参照していた値も破棄します。
*
* @param list リスト
*/
void SC_List_delete(SC_List* list)
{
list->clear(list);
free(list);
}
/**
* リストの指定された index 位置にオブジェクトを挿入します。
* オブジェクトはコピーされ、リストに保持されます。
* 不正なインデックスが指定された場合、エラーコード SC_EINVAL が
* メモリの確保に失敗した場合、エラーコード SC_ENOMEM がセットされ、
* NULL を返します。
*
* @param list リスト
* @param index インデックス
* @param obj 挿入するオブジェクト
* @param size オブジェクトのサイズ
* @return 挿入したオブジェクトのコピー
*/
static
void* SC_List_add(SC_List* list, int index, const void* obj, size_t size)
{
SC_List_Entry* entry;
SC_List_Entry* insertEntry;
/* 挿入位置取得 */
insertEntry = SC_List_search(list, index);
if (insertEntry == NULL)
{ /* 挿入位置不正 */
return NULL;
}
/* エントリ作成 */
entry = SC_List_newEntry(obj, size);
if (entry == NULL)
{ /* メモリ確保失敗 */
return NULL;
}
/* リストに追加 (insertEntry より1つ前に追加) */
list->size++;
entry->next = insertEntry;
entry->prev = insertEntry->prev;
insertEntry->prev->next = entry;
insertEntry->prev = entry;
return entry->value;
}
/**
* リストの指定された index 位置に文字列を挿入します。
* 文字列はコピーされ、リストに保持されます。
* 不正なインデックスが指定された場合、エラーコード SC_EINVAL が
* メモリの確保に失敗した場合、エラーコード SC_ENOMEM がセットされ、
* NULL を返します。
*
* @param list リスト
* @param index インデックス
* @param obj 挿入する文字列
* @return 挿入した文字列のコピー
*/
static
char* SC_List_addStr(SC_List* list, int index, const char* obj)
{
return (char*) SC_List_add(list, index, obj, strlen(obj) + 1);
}
/**
* 指定されたインデックス位置にある値を取得します。
* size には、取得された値のサイズが格納されます。
* (値のサイズが不要な場合、NULL を指定することが可能です。)
* 不正なインデックスが指定された場合、エラーコード
* SC_EINVAL がセットされ、NULL が返されます。
*
* @param list リスト
* @param index インデックス
* @param size 値のサイズが格納される
* @return 値
*/
static
void* SC_List_get(SC_List* list, int index, size_t* size)
{
SC_List_Entry* entry;
entry = SC_List_search(list, index);
if ((entry == NULL) || (entry == list->_tail))
{ /* インデックス不正 */
SC_setError(SC_EINVAL);
return NULL;
}
if (size != NULL)
{ /* サイズ格納 */
*size = entry->size;
}
return entry->value;
}
/**
* 指定されたインデックス位置にある文字列を取得します。
* size には、取得された値のサイズが格納されます。
* (値のサイズが不要な場合、NULL を指定することが可能です。)
* 不正なインデックスが指定された場合、エラーコード
* SC_EINVAL がセットされ、NULL が返されます。
*
* @param list リスト
* @param index インデックス
* @param size 値のサイズが格納される
* @return 文字列
*/
static
char* SC_List_getStr(SC_List* list, int index)
{
return (char*) SC_List_get(list, index, NULL);
}
/**
* 指定されたインデックス位置にある値を削除します。
* [注意] 参照していた値も削除されます。
*
* @param list リスト
* @param index インデックス
*/
static
void SC_List_remove(SC_List* list, int index)
{
SC_List_Entry* entry;
entry = SC_List_search(list, index);
if ((entry == NULL) || (entry == list->_tail))
{ /* インデックス不正 */
return;
}
entry->prev->next = entry->next;
entry->next->prev = entry->prev;
free(entry);
list->size--;
}
/**
* リストをクリアします。
* [注意] 参照していた値も削除されます。
*
* @param list リスト
*/
static void SC_List_clear(SC_List* list)
{
SC_List_Entry* entry;
entry = list->_head->next;
while (entry != list->_tail)
{
entry = entry->next;
free(entry->prev);
}
list->size = 0;
list->_head->next = list->_head->prev = list->_tail;
list->_tail->next = list->_tail->prev = list->_head;
list->_head->value = list->_tail->value = NULL;
}
/**
* リストより指定されたインデックスのエントリを取得します。
* インデックスが -1 または、現在の size 位置の場合、
* list->tail を返します。
* インデックス位置が不正な場合、エラーコード SC_EINVAL が
* セットされ、NULL を返します。
*
* @param list リスト
* @param index インデックス
* @return エントリ
*/
static
SC_List_Entry* SC_List_search(SC_List* list, int index)
{
SC_List_Entry* tmpEntry;
int i;
int halfSize;
if (index > (int) list->size)
{ /* インデックス位置不正 */
SC_setError(SC_EINVAL);
return NULL;
}
if ((index == -1) || (index == (int) list->size))
{ /* 最後尾 */
return list->_tail;
}
halfSize = (int) list->size / 2;
if (index > halfSize)
{ /* 前方より検索 */
tmpEntry = list->_head;
for (i = 0; i <= index; i++)
{
tmpEntry = tmpEntry->next;
}
}
else
{ /* 後方より検索 */
tmpEntry = list->_tail;
for (i = list->size; i > index; i--)
{
tmpEntry = tmpEntry->prev;
}
}
return tmpEntry;
}
/**
* 新たなエントリを生成します。
* 生成に失敗した場合、エラーコード SC_ENOMEM が
* セットされ、NULL が返されます。
*
* @param obj オブジェクト
* @param size サイズ
* @return リストエントリ
*/
static
SC_List_Entry* SC_List_newEntry(const void* obj, size_t size)
{
size_t newSize = size + sizeof(SC_List_Entry);
SC_List_Entry* entry = (SC_List_Entry*) malloc (newSize);
if (entry == NULL)
{ /* メモリ確保失敗 */
SC_setError(SC_ENOMEM);
return NULL;
}
entry->value = (entry + 1);
entry->size = size;
memcpy(entry->value, obj, size);
return entry;
}