You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
8.9 KiB
C
388 lines
8.9 KiB
C
/*
|
|
* tclist.c -- a list for transcode / implementation
|
|
* (C) 2008-2010 - Francesco Romani <fromani -at- gmail -dot- com>
|
|
*
|
|
* This file is part of transcode, a video stream processing tool.
|
|
*
|
|
* transcode is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* transcode is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "libtc.h"
|
|
#include "tclist.h"
|
|
|
|
/*************************************************************************/
|
|
|
|
enum {
|
|
DIR_BACKWARD = -1,
|
|
DIR_FORWARD = +1
|
|
};
|
|
|
|
static int free_item(TCListItem *item, void *unused)
|
|
{
|
|
tc_free(item);
|
|
return 0;
|
|
}
|
|
|
|
static TCListItem* next_item(TCListItem *cur, int direction)
|
|
{
|
|
TCListItem *ret = NULL;
|
|
if (cur) {
|
|
if (direction == DIR_BACKWARD) {
|
|
ret = cur->prev;
|
|
} else {
|
|
ret = cur->next;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static TCListItem *start_item(TCList *L, int direction)
|
|
{
|
|
TCListItem *ret = NULL;
|
|
if (L) {
|
|
if (direction == DIR_BACKWARD) {
|
|
ret = L->tail;
|
|
} else {
|
|
ret = L->head;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void del_item(TCList *L, TCListItem *IT)
|
|
{
|
|
if (L->use_cache) {
|
|
IT->prev = NULL;
|
|
IT->data = NULL;
|
|
IT->next = L->cache;
|
|
L->cache = IT;
|
|
} else {
|
|
tc_free(IT);
|
|
}
|
|
}
|
|
|
|
static TCListItem *new_item(TCList *L)
|
|
{
|
|
TCListItem *IT = NULL;
|
|
if (L->use_cache && L->cache) {
|
|
IT = L->cache;
|
|
L->cache = L->cache->next;
|
|
} else {
|
|
IT = tc_zalloc(sizeof(TCListItem));
|
|
}
|
|
return IT;
|
|
}
|
|
|
|
static int foreach_item(TCListItem *start, int direction,
|
|
TCListVisitor vis, void *userdata)
|
|
{
|
|
int ret = 0;
|
|
TCListItem *cur = NULL, *inc = NULL;
|
|
|
|
for (cur = start; cur; cur = inc) {
|
|
inc = next_item(cur, direction);
|
|
ret = vis(cur, userdata);
|
|
if (ret != 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
struct find_data {
|
|
int dir;
|
|
int cur_idx;
|
|
int stop_idx;
|
|
TCListItem *ptr;
|
|
};
|
|
|
|
static int elem_finder(TCListItem *item, void *userdata)
|
|
{
|
|
struct find_data *FD = userdata;
|
|
int ret = 0;
|
|
|
|
FD->ptr = item;
|
|
|
|
if (FD->cur_idx != FD->stop_idx) {
|
|
FD->cur_idx += FD->dir;
|
|
} else {
|
|
ret = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static TCListItem *find_position(TCList *L, int pos)
|
|
{
|
|
TCListItem *ret = NULL;
|
|
if (L) {
|
|
/* common cases first */
|
|
if (pos == 0) {
|
|
ret = L->head;
|
|
} else if (pos == -1) {
|
|
ret = L->tail;
|
|
} else {
|
|
/* if we're here we can't avoid a full scan */
|
|
struct find_data FD = { DIR_FORWARD, 0, 0, NULL };
|
|
if (pos >= 0) {
|
|
FD.dir = DIR_FORWARD; /* enforce */
|
|
FD.cur_idx = 0;
|
|
FD.stop_idx = pos;
|
|
} else {
|
|
/*
|
|
* we're perfectly fine with negative indexes;
|
|
* we'll just starting from the end going backwards
|
|
* with -1 being the last element.
|
|
*/
|
|
FD.dir = DIR_BACKWARD;
|
|
FD.cur_idx = L->nelems - 1;
|
|
FD.stop_idx = L->nelems + pos;
|
|
}
|
|
/* we can now catch some over/under-run common cases */
|
|
if (FD.stop_idx > 0 || FD.stop_idx < L->nelems) {
|
|
/* we want something in the middle, so let's run */
|
|
FD.ptr = NULL; /* for safeness */
|
|
foreach_item(start_item(L, FD.dir), FD.dir, elem_finder, &FD);
|
|
ret = FD.ptr; /* cannot fail */
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int item_insert_before(TCList *L, int pos, void *data)
|
|
{
|
|
int ret = TC_ERROR;
|
|
TCListItem *ref = find_position(L, pos);
|
|
if (ref) {
|
|
TCListItem *ext = new_item(L);
|
|
if (ext) {
|
|
ext->data = data;
|
|
ref->prev->next = ext;
|
|
ext->prev = ref->prev;
|
|
ext->next = ref;
|
|
ref->prev = ext;
|
|
L->nelems++;
|
|
ret = TC_OK;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int item_insert_after(TCList *L, int pos, void *data)
|
|
{
|
|
int ret = TC_ERROR;
|
|
TCListItem *ref = find_position(L, pos);
|
|
if (ref) {
|
|
TCListItem *ext = new_item(L);
|
|
if (ext) {
|
|
ext->data = data;
|
|
ref->next->prev = ext;
|
|
ext->next = ref->next;
|
|
ext->prev = ref;
|
|
ref->next = ext;
|
|
L->nelems++;
|
|
ret = TC_OK;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
int tc_list_init(TCList *L, int elemcache)
|
|
{
|
|
if (L) {
|
|
L->head = NULL;
|
|
L->tail = NULL;
|
|
L->nelems = 0;
|
|
L->cache = NULL;
|
|
L->use_cache = elemcache;
|
|
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int tc_list_fini(TCList *L)
|
|
{
|
|
/* if !use_cache, this will not hurt anyone */
|
|
foreach_item(L->head, DIR_FORWARD, free_item, NULL);
|
|
foreach_item(L->cache, DIR_FORWARD, free_item, NULL);
|
|
/* now reset to clean status */
|
|
return tc_list_init(L, 0);
|
|
}
|
|
|
|
int tc_list_size(TCList *L)
|
|
{
|
|
return (L) ?L->nelems :0;
|
|
}
|
|
|
|
int tc_list_foreach(TCList *L, TCListVisitor vis, void *userdata)
|
|
{
|
|
return foreach_item(L->head, DIR_FORWARD, vis, userdata);
|
|
}
|
|
|
|
int tc_list_append(TCList *L, void *data)
|
|
{
|
|
int ret = TC_ERROR;
|
|
TCListItem *IT = new_item(L);
|
|
|
|
if (IT) {
|
|
IT->data = data;
|
|
IT->prev = L->tail;
|
|
if (!L->head) {
|
|
L->head = IT;
|
|
} else {
|
|
/* at least one element */
|
|
L->tail->next = IT;
|
|
}
|
|
L->tail = IT;
|
|
L->nelems++;
|
|
|
|
ret = TC_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int tc_list_prepend(TCList *L, void *data)
|
|
{
|
|
int ret = TC_ERROR;
|
|
TCListItem *IT = new_item(L);
|
|
|
|
if (IT) {
|
|
IT->data = data;
|
|
IT->next = L->head;
|
|
if (!L->tail) {
|
|
L->tail = IT;
|
|
} else {
|
|
/* at least one element */
|
|
L->head->prev = IT;
|
|
}
|
|
L->head = IT;
|
|
L->nelems++;
|
|
|
|
ret = TC_OK;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int tc_list_insert(TCList *L, int pos, void *data)
|
|
{
|
|
int ret = TC_ERROR;
|
|
if (L && data) {
|
|
if (pos == 0) {
|
|
ret = tc_list_prepend(L, data);
|
|
} else if (pos == -1) {
|
|
ret = tc_list_append(L, data);
|
|
} else if (pos > 0) {
|
|
ret = item_insert_before(L, pos, data);
|
|
} else {
|
|
ret = item_insert_after(L, pos, data);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void *tc_list_get(TCList *L, int pos)
|
|
{
|
|
TCListItem *IT = find_position(L, pos);
|
|
return (IT) ?IT->data :NULL;
|
|
}
|
|
|
|
void *tc_list_pop(TCList *L, int pos)
|
|
{
|
|
TCListItem *IT = find_position(L, pos);
|
|
void *data = NULL;
|
|
if (IT) {
|
|
data = IT->data;
|
|
if (L->head == IT) {
|
|
if (IT->next) {
|
|
IT->next->prev = NULL;
|
|
}
|
|
L->head = IT->next;
|
|
} else if (L->tail == IT) {
|
|
if (IT->prev) {
|
|
IT->prev->next = NULL;
|
|
}
|
|
L->tail = IT->prev;
|
|
} else {
|
|
IT->next->prev = IT->prev;
|
|
IT->prev->next = IT->next;
|
|
}
|
|
|
|
del_item(L, IT);
|
|
L->nelems--;
|
|
}
|
|
return data;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
int tc_list_insert_dup(TCList *L, int pos, void *data, size_t size)
|
|
{
|
|
int ret = TC_ERROR;
|
|
void *mem = tc_malloc(size);
|
|
if (mem) {
|
|
memcpy(mem, data, size);
|
|
ret = tc_list_insert(L, pos, mem);
|
|
if (ret == TC_ERROR) {
|
|
tc_free(mem);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int free_item_all(TCListItem *item, void *unused)
|
|
{
|
|
if (item->data != NULL) {
|
|
free(item->data);
|
|
}
|
|
free(item);
|
|
return 0;
|
|
}
|
|
|
|
void tc_list_del(TCList *L, int deepclean)
|
|
{
|
|
if (deepclean) {
|
|
foreach_item(L->head, DIR_FORWARD, free_item_all, NULL);
|
|
/* if !use_cache, this will not hurt anyone */
|
|
foreach_item(L->cache, DIR_FORWARD, free_item_all, NULL);
|
|
}
|
|
tc_free(L);
|
|
}
|
|
|
|
TCList *tc_list_new(int usecache)
|
|
{
|
|
TCList *L = tc_malloc(sizeof(TCList));
|
|
if (L) {
|
|
tc_list_init(L, usecache);
|
|
}
|
|
return L;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|