/* list.c
*/
/* This software is copyrighted as detailed in the LICENSE file. */
#include "EXTERN.h"
#include "common.h"
#include "util.h"
#include "INTERN.h"
#include "list.h"
#include "list.ih"
void
list_init()
{
;
}
/* Create the header for a dynamic list of items.
*/
LIST*
new_list(low, high, item_size, items_per_node, flags, init_node)
long low;
long high;
int item_size;
int items_per_node;
int flags;
void (*init_node) _((LIST*,LISTNODE*));
{
LIST* list = (LIST*)safemalloc(sizeof (LIST));
list->first = list->recent = NULL;
list->init_node = init_node? init_node : def_init_node;
list->low = low;
list->high = high;
list->item_size = item_size;
list->items_per_node = items_per_node;
list->flags = flags;
return list;
}
/* The default way to initialize a node */
static void
def_init_node(list, node)
LIST* list;
LISTNODE* node;
{
if (list->flags & LF_ZERO_MEM)
bzero(node->data, list->items_per_node * list->item_size);
}
/* Take the number of a list element and return its pointer. This
** will allocate new list items as needed, keeping the list->high
** value up to date.
*/
char*
listnum2listitem(list, num)
LIST* list;
long num;
{
LISTNODE* node = list->recent;
LISTNODE* prevnode = NULL;
if (node && num < node->low)
node = list->first;
for (;;) {
if (!node || num < node->low) {
node = (LISTNODE*)safemalloc(list->items_per_node*list->item_size
+ sizeof (LISTNODE) - 1);
if (list->flags & LF_SPARSE)
node->low = ((num - list->low) / list->items_per_node)
* list->items_per_node + list->low;
else
node->low = num;
node->high = node->low + list->items_per_node - 1;
node->data_high = node->data
+ (list->items_per_node - 1) * list->item_size;
if (node->high > list->high)
list->high = node->high;
if (prevnode) {
node->next = prevnode->next;
prevnode->next = node;
}
else {
node->next = list->first;
list->first = node;
}
/*node->mid = $$;*/
list->init_node(list, node);
break;
}
if (num <= node->high)
break;
prevnode = node;
node = node->next;
}
list->recent = node;
return node->data + (num - node->low) * list->item_size;
}
/* Take the pointer of a list element and return its number. The item
** must already exist or this will infinite loop.
*/
long
listitem2listnum(list, ptr)
LIST* list;
char* ptr;
{
LISTNODE* node;
char* cp;
int i;
int item_size = list->item_size;
for (node = list->recent; ; node = node->next) {
if (!node)
node = list->first;
i = node->high - node->low + 1;
for (cp = node->data; i--; cp += item_size) {
if (ptr == cp) {
list->recent = node;
return (ptr - node->data) / list->item_size + node->low;
}
}
}
return -1;
}
/* Execute the indicated callback function on every item in the list.
*/
bool
walk_list(list, callback, arg)
LIST* list;
bool (*callback) _((char*,int));
int arg;
{
LISTNODE* node;
char* cp;
int i;
int item_size = list->item_size;
for (node = list->first; node; node = node->next) {
i = node->high - node->low + 1;
for (cp = node->data; i--; cp += item_size)
if (callback(cp, arg))
return 1;
}
return 0;
}
/* Since the list can be sparsely allocated, find the nearest number
** that is already allocated, rounding in the indicated direction from
** the initial list number.
*/
long
existing_listnum(list, num, direction)
LIST* list;
long num;
int direction;
{
register LISTNODE* node = list->recent;
LISTNODE* prevnode = NULL;
if (node && num < node->low)
node = list->first;
while (node) {
if (num <= node->high) {
if (direction > 0) {
if (num < node->low)
num = node->low;
}
else if (direction == 0) {
if (num < node->low)
num = 0;
}
else if (num < node->low) {
if (!prevnode)
break;
list->recent = prevnode;
return prevnode->high;
}
list->recent = node;
return num;
}
prevnode = node;
node = node->next;
}
if (!direction)
return 0;
if (direction > 0)
return list->high + 1;
return list->low - 1;
}
/* Increment the item pointer to the next allocated item.
** Returns NULL if ptr is the last one.
*/
char*
next_listitem(list, ptr)
LIST* list;
char* ptr;
{
register LISTNODE* node = list->recent;
if (ptr == node->data_high) {
node = node->next;
if (!node)
return NULL;
list->recent = node;
return node->data;
}
#if 0
if (node->high > list->high) {
if ((ptr - node->data) / list->item_size + node->low >= list->high)
return NULL;
}
#endif
return ptr += list->item_size;
}
/* Decrement the item pointer to the prev allocated item.
** Returns NULL if ptr is the first one.
*/
char*
prev_listitem(list, ptr)
LIST* list;
char* ptr;
{
register LISTNODE* node = list->recent;
if (ptr == node->data) {
LISTNODE* prev = list->first;
if (prev == node)
return NULL;
while (prev->next != node)
prev = prev->next;
list->recent = prev;
return prev->data_high;
}
return ptr -= list->item_size;
}
/* Delete the list and all its allocated nodes. If you need to cleanup
** the individual nodes, call walk_list() with a cleanup function before
** calling this.
*/
void
delete_list(list)
LIST* list;
{
LISTNODE* node = list->first;
LISTNODE* prevnode = NULL;
while (node) {
prevnode = node;
node = node->next;
free((char*)prevnode);
}
free((char*)list);
}
syntax highlighted by Code2HTML, v. 0.9.1