list.c   [plain text]


/*
 * 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;
}