439 lines
8.5 KiB
C
439 lines
8.5 KiB
C
/*
|
|
* Copyright (c) 2000-2001
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include "list.h"
|
|
#include "util.h"
|
|
|
|
struct list_node
|
|
{
|
|
void *data;
|
|
struct list_node *sameregion next;
|
|
};
|
|
|
|
#define scan_node(b,var) for (var = b; var; var = var->next)
|
|
|
|
struct list
|
|
{
|
|
region sameregion r;
|
|
int length;
|
|
list_node sameregion head;
|
|
};
|
|
|
|
struct list *new_list(region r)
|
|
{
|
|
struct list *result;
|
|
|
|
assert(r);
|
|
|
|
result = ralloc(r,struct list);
|
|
result->r = r;
|
|
result->length = 0;
|
|
result->head = NULL;
|
|
|
|
return result;
|
|
}
|
|
|
|
int list_size(struct list *l)
|
|
{
|
|
return l->length;
|
|
}
|
|
|
|
struct list *list_cons(void *data, struct list *l)
|
|
{
|
|
list_node newnode = ralloc(l->r, struct list_node);
|
|
newnode->next = l->head;
|
|
newnode->data = data;
|
|
|
|
l->head = newnode;
|
|
l->length++;
|
|
|
|
return l;
|
|
}
|
|
|
|
struct list *list_reverse(struct list *l)
|
|
{
|
|
|
|
if (list_empty(l))
|
|
return l;
|
|
|
|
else
|
|
{
|
|
list_node temp,reversed = NULL;
|
|
|
|
while (l->head)
|
|
{
|
|
temp = l->head->next;
|
|
|
|
l->head->next = reversed;
|
|
|
|
reversed = l->head;
|
|
|
|
l->head = temp;
|
|
}
|
|
|
|
l->head = reversed;
|
|
return l;
|
|
}
|
|
|
|
}
|
|
|
|
bool list_empty(struct list *l)
|
|
{
|
|
return (l->head == NULL);
|
|
}
|
|
|
|
static inline list_node tail(list_node n)
|
|
{
|
|
if (n == NULL)
|
|
return NULL;
|
|
else
|
|
{
|
|
list_node temp = NULL,
|
|
tail = NULL;
|
|
|
|
scan_node(n,temp)
|
|
tail = temp;
|
|
|
|
assert(tail && tail->next == NULL);
|
|
|
|
return tail;
|
|
}
|
|
}
|
|
|
|
struct list *list_append(struct list *a, struct list *b)
|
|
{
|
|
list_node tl;
|
|
|
|
assert( a && b );
|
|
assert( a != b);
|
|
assert( ptr_eq(a->r,b->r) );
|
|
|
|
tl = tail(a->head);
|
|
|
|
|
|
if (! tl)
|
|
{
|
|
a->head = b->head;
|
|
a->length = b->length;
|
|
}
|
|
|
|
else
|
|
{
|
|
tl->next = b->head;
|
|
a->length += b->length;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
struct list *list_app(struct list *l,app_fn app)
|
|
{
|
|
list_node n = NULL;
|
|
|
|
|
|
assert(l);
|
|
|
|
scan_node(l->head,n)
|
|
{
|
|
app(n->data);
|
|
}
|
|
return l;
|
|
}
|
|
|
|
void *list_find(struct list *l,eq_fn eq)
|
|
{
|
|
list_node n = NULL;
|
|
assert(l);
|
|
|
|
scan_node(l->head,n)
|
|
{
|
|
if (eq(n->data))
|
|
return n;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct list *list_tail(struct list *l)
|
|
{
|
|
l->length--;
|
|
l->head = l->head->next;
|
|
return l;
|
|
}
|
|
|
|
void *list_head(struct list *l)
|
|
{
|
|
return l->head->data;
|
|
}
|
|
|
|
struct list *list_filter(region r,struct list *l,eq_fn eq)
|
|
{
|
|
struct list *result;
|
|
list_node n = NULL;
|
|
assert(l);
|
|
|
|
result = new_list(r);
|
|
|
|
scan_node(l->head,n)
|
|
{
|
|
if (eq(n->data))
|
|
list_cons(n->data,result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
struct list *list_keep(struct list *l, eq_fn eq)
|
|
{
|
|
list_node prev, n;
|
|
assert(l);
|
|
|
|
while (l->head && !eq(l->head->data))
|
|
{
|
|
l->head = l->head->next;
|
|
}
|
|
|
|
prev = l->head;
|
|
scan_node(l->head->next,n)
|
|
{
|
|
if (!eq(n->data))
|
|
prev->next = n->next;
|
|
else prev = n;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
struct list *list_filter2(struct list *l,eq_fn eq)
|
|
{
|
|
return list_filter(l->r,l,eq);
|
|
}
|
|
|
|
struct list *list_copy(region r, struct list *l)
|
|
{
|
|
|
|
struct list *result;
|
|
list_node n = NULL;
|
|
#ifndef NDEBUG
|
|
int count = 0;
|
|
#endif
|
|
assert(l);
|
|
|
|
result = new_list(r);
|
|
|
|
scan_node(l->head,n)
|
|
{
|
|
list_cons(n->data,result);
|
|
assert(++count <= l->length);
|
|
}
|
|
|
|
return list_reverse(result);
|
|
}
|
|
/* A Linked-List Memory Sort
|
|
by Philip J. Erdelsky
|
|
pje@acm.org
|
|
http://www.alumni.caltech.edu/~pje/
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
|
|
static void *sort_linked_list(void *p, unsigned index,
|
|
int (*compare)(const void *,const void *, comparator_fn), long *pcount, comparator_fn data)
|
|
{
|
|
unsigned base;
|
|
unsigned long block_size;
|
|
|
|
struct record
|
|
{
|
|
struct record *next[1];
|
|
/* other members not directly accessed by this function */
|
|
};
|
|
|
|
struct tape
|
|
{
|
|
struct record *first, *last;
|
|
unsigned long count;
|
|
} tape[4];
|
|
|
|
/* Distribute the records alternately to tape[0] and tape[1]. */
|
|
|
|
tape[0].count = tape[1].count = 0L;
|
|
tape[0].first = NULL;
|
|
base = 0;
|
|
while (p != NULL)
|
|
{
|
|
struct record *next = ((struct record *)p)->next[index];
|
|
((struct record *)p)->next[index] = tape[base].first;
|
|
tape[base].first = ((struct record *)p);
|
|
tape[base].count++;
|
|
p = next;
|
|
base ^= 1;
|
|
}
|
|
|
|
/* If the list is empty or contains only a single record, then */
|
|
/* tape[1].count == 0L and this part is vacuous. */
|
|
|
|
for (base = 0, block_size = 1L; tape[base+1].count != 0L;
|
|
base ^= 2, block_size <<= 1)
|
|
{
|
|
int dest;
|
|
struct tape *tape0, *tape1;
|
|
tape0 = tape + base;
|
|
tape1 = tape + base + 1;
|
|
dest = base ^ 2;
|
|
tape[dest].count = tape[dest+1].count = 0;
|
|
for (; tape0->count != 0; dest ^= 1)
|
|
{
|
|
unsigned long n0, n1;
|
|
struct tape *output_tape = tape + dest;
|
|
n0 = n1 = block_size;
|
|
while (1)
|
|
{
|
|
struct record *chosen_record;
|
|
struct tape *chosen_tape;
|
|
if (n0 == 0 || tape0->count == 0)
|
|
{
|
|
if (n1 == 0 || tape1->count == 0)
|
|
break;
|
|
chosen_tape = tape1;
|
|
n1--;
|
|
}
|
|
else if (n1 == 0 || tape1->count == 0)
|
|
{
|
|
chosen_tape = tape0;
|
|
n0--;
|
|
}
|
|
else if ((*compare)(tape0->first, tape1->first, data) > 0)
|
|
{
|
|
chosen_tape = tape1;
|
|
n1--;
|
|
}
|
|
else
|
|
{
|
|
chosen_tape = tape0;
|
|
n0--;
|
|
}
|
|
chosen_tape->count--;
|
|
chosen_record = chosen_tape->first;
|
|
chosen_tape->first = chosen_record->next[index];
|
|
if (output_tape->count == 0)
|
|
output_tape->first = chosen_record;
|
|
else
|
|
output_tape->last->next[index] = chosen_record;
|
|
output_tape->last = chosen_record;
|
|
output_tape->count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tape[base].count > 1L)
|
|
tape[base].last->next[index] = NULL;
|
|
if (pcount != NULL)
|
|
*pcount = tape[base].count;
|
|
return tape[base].first;
|
|
}
|
|
|
|
|
|
|
|
static int compare(const void *node1, const void *node2, comparator_fn data)
|
|
{
|
|
comparator_fn cmp = (comparator_fn) data;
|
|
return cmp(((struct list_node *)node1)->data,
|
|
((struct list_node *)node2)->data);
|
|
}
|
|
|
|
struct list *list_sort(struct list *l, comparator_fn cmp)
|
|
{
|
|
long pcount;
|
|
l->head = sort_linked_list(l->head,1,compare,&pcount, cmp);
|
|
assert(pcount == l->length);
|
|
return l;
|
|
}
|
|
|
|
struct list *list_merge(struct list *a,struct list *b, comparator_fn cmp)
|
|
{
|
|
return list_sort( list_append(a,b),cmp);
|
|
}
|
|
|
|
void list_scan(struct list *a,struct list_scanner *scan)
|
|
{
|
|
scan->l = a;
|
|
scan->cur = a->head;
|
|
}
|
|
|
|
bool list_next(struct list_scanner *scan, void **data)
|
|
{
|
|
if (!scan->cur)
|
|
return FALSE;
|
|
else
|
|
{
|
|
if (data)
|
|
*data = scan->cur->data;
|
|
scan->cur = scan->cur->next;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
void list_clear(struct list *l)
|
|
{
|
|
l->head = NULL;
|
|
l->length = 0;
|
|
}
|
|
|
|
bool list_member(struct list *l,void *data)
|
|
{
|
|
list_node n = NULL;
|
|
scan_node(l->head,n)
|
|
{
|
|
if (n->data == data)
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
struct list *list_from_array(region r,void **data, int length)
|
|
{
|
|
struct list *result = new_list(r);
|
|
int i;
|
|
|
|
for (i = length -1; i >= 0; i--)
|
|
{
|
|
list_cons(data[i],result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|