Ok, I've attached new versions of list.c and pg_list.h -- this is just a
*rough sketch* of the new List code -- it definitely won't compile, the
intent is just to concretely specify the new List design. Also, I've
only updated the important list functions: I stopped at nth().
(I've attached the whole files, rather than diffs, because I thought
that would be easier to read...)
Any comments would be welcome -- I'm sure I've mucked a few things up.
Also, since it doesn't compile and I haven't done any testing, there are
probably some thinkos & typos in the code.
On Tue, 2003-11-04 at 11:40, Tom Lane wrote:
> Hmm ... taking that one step further, you could probably avoid the need
> for two palloc's in the first lcons/lappend if the List header were
> combined with the first ListCell. This would make for some grottiness
> in ldelete when removing the first list entry, but that's probably
> a good tradeoff.
I haven't implemented this (yet), or the aset.c changes you suggested.
-Neil
/*-------------------------------------------------------------------------
*
* pg_list.h
* POSTGRES generic list package
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_list.h,v 1.41 2003/08/22 20:34:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PG_LIST_H
#define PG_LIST_H
#include "nodes/nodes.h"
/* ----------------------------------------------------------------
* node definitions
* ----------------------------------------------------------------
*/
/*----------------------
* Value node
*
* The same Value struct is used for five node types: T_Integer,
* T_Float, T_String, T_BitString, T_Null.
*
* Integral values are actually represented by a machine integer,
* but both floats and strings are represented as strings.
* Using T_Float as the node type simply indicates that
* the contents of the string look like a valid numeric literal.
*
* (Before Postgres 7.0, we used a double to represent T_Float,
* but that creates loss-of-precision problems when the value is
* ultimately destined to be converted to NUMERIC. Since Value nodes
* are only used in the parsing process, not for runtime data, it's
* better to use the more general representation.)
*
* Note that an integer-looking string will get lexed as T_Float if
* the value is too large to fit in a 'long'.
*
* Nulls, of course, don't need the value part at all.
*----------------------
*/
typedef struct Value
{
NodeTag type; /* tag appropriately (eg. T_String) */
union ValUnion
{
long ival; /* machine integer */
char *str; /* string */
} val;
} Value;
#define intVal(v) (((Value *)(v))->val.ival)
#define floatVal(v) atof(((Value *)(v))->val.str)
#define strVal(v) (((Value *)(v))->val.str)
typedef struct ListCell
{
union
{
void *ptr_value;
int int_value;
Oid oid_value;
} elem;
struct ListCell *next;
} ListCell;
typedef struct List
{
NodeTag type;
ListCell *head;
ListCell *tail;
int length;
} List;
#define NIL ((List *) NULL)
/* ----------------
* accessor macros
*
* The general naming convention is that the base name xyz() is for the
* pointer version, xyzi() is for integers, xyzo() is for Oids. We don't
* bother with multiple names if the same routine can handle all cases.
* ----------------
*/
#define length(l) ((l)->length)
#define value(cell) ((cell)->elem.ptr_value)
#define ivalue(cell) ((cell)->elem.int_value)
#define ovalue(cell) ((cell)->elem.oid_value)
#define lfirst(l) value((l)->head)
#define lfirsti(l) ivalue((l)->head)
#define lfirsto(l) ovalue((l)->head)
#define llast(l) value((l)->tail)
#define lsecond(l) value((l)->head->next)
#define lthird(l) value((l)->head->next->next)
#define lfourth(l) value((l)->head->next->next->next)
/*
* foreach -
* a convenience macro which loops through the list
*/
#define foreach(_cell_,_list_) \
for (_cell_ = (_list_)->head; _elt_->next != NULL; _elt_ = _elt->next)
/*
* Convenience macros for building fixed-length lists
*/
#define makeList1(x1) lcons(x1, NIL)
#define makeList2(x1,x2) lcons(x1, makeList1(x2))
#define makeList3(x1,x2,x3) lcons(x1, makeList2(x2,x3))
#define makeList4(x1,x2,x3,x4) lcons(x1, makeList3(x2,x3,x4))
#define makeListi1(x1) lconsi(x1, NIL)
#define makeListi2(x1,x2) lconsi(x1, makeListi1(x2))
#define makeListo1(x1) lconso(x1, NIL)
#define makeListo2(x1,x2) lconso(x1, makeListo1(x2))
/*
* function prototypes in nodes/list.c
*/
extern Value *makeInteger(long i);
extern Value *makeFloat(char *numericStr);
extern Value *makeString(char *str);
extern Value *makeBitString(char *str);
extern List *lcons(void *datum, List *list);
extern List *lconsi(int datum, List *list);
extern List *lconso(Oid datum, List *list);
extern List *lappend(List *list, void *datum);
extern List *lappendi(List *list, int datum);
extern List *lappendo(List *list, Oid datum);
extern List *nconc(List *list1, List *list2);
extern void *nth(int n, List *l);
extern bool member(void *datum, List *list);
extern bool ptrMember(void *datum, List *list);
extern bool intMember(int datum, List *list);
extern bool oidMember(Oid datum, List *list);
extern List *lremove(void *elem, List *list);
extern List *LispRemove(void *elem, List *list);
extern List *lremovei(int elem, List *list);
extern List *ltruncate(int n, List *list);
extern List *set_union(List *list1, List *list2);
extern List *set_uniono(List *list1, List *list2);
extern List *set_ptrUnion(List *list1, List *list2);
extern List *set_difference(List *list1, List *list2);
extern List *set_differenceo(List *list1, List *list2);
extern List *set_ptrDifference(List *list1, List *list2);
extern bool equali(List *list1, List *list2);
extern bool equalo(List *list1, List *list2);
extern void freeList(List *list);
/* in copyfuncs.c */
extern List *listCopy(List *list);
#endif /* PG_LIST_H */
/*-------------------------------------------------------------------------
*
* list.c
* POSTGRES generic list package
*
*
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /var/lib/cvs/pgsql-server/src/backend/nodes/list.c,v 1.54 2003/08/08 21:41:44 momjian Exp $
*
* NOTES
* XXX a few of the following functions are duplicated to handle
* List of pointers and List of integers separately. Some day,
* someone should unify them. - ay 11/2/94
* This file needs cleanup.
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* Andrew Yu Oct, 1994 file creation
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/parsenodes.h"
/*
* makeInteger
*/
Value *
makeInteger(long i)
{
Value *v = makeNode(Value);
v->type = T_Integer;
v->val.ival = i;
return v;
}
/*
* makeFloat
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeFloat(char *numericStr)
{
Value *v = makeNode(Value);
v->type = T_Float;
v->val.str = numericStr;
return v;
}
/*
* makeString
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeString(char *str)
{
Value *v = makeNode(Value);
v->type = T_String;
v->val.str = str;
return v;
}
/*
* makeBitString
*
* Caller is responsible for passing a palloc'd string.
*/
Value *
makeBitString(char *str)
{
Value *v = makeNode(Value);
v->type = T_BitString;
v->val.str = str;
return v;
}
static List *
new_list(NodeTag type)
{
List *retval;
retval = palloc(sizeof(*retval));
retval->type = type;
retval->head = NULL;
retval->tail = NULL;
retval->length = 0;
return retval;
}
/*
* Add a new, and as yet undefined, head element to the list, which
* must be non-NIL. The list is mutated in place. The caller should
* fill in the value of the new head element.
*/
static List *
new_head_elem(List *list)
{
ListCell *new_head;
new_head = palloc(sizeof(*new_head));
new_head->next = list->head;
list->head = new_head;
/*
* If this is the only element in the list, it is both the head
* and the tail, so we need to update the tail pointer as well
*/
if (list->tail == NULL)
list->tail = list->head;
list->length++;
}
/*
* Add a new, and as yet undefined, tail element to the list, which
* must be non-NIL. The list is mutated in place. The caller should
* fill in the value of the new tail element.
*/
static List *
new_tail_elem(List *list)
{
ListCell *new_tail;
new_tail = palloc(sizeof(*new_tail));
new_tail->next = NULL;
list->tail->next = new_tail;
list->tail = new_tail;
/*
* If this is the only element in the list, it is both the tail
* and the head, so we need to update the head pointer as well
*/
if (list->head == NULL)
list->head = list->tail;
list->length++;
}
#ifdef USE_ASSERT_CHECKING
/*
* This should probably be made into a macro...
*/
static void
check_invariant(List *list)
{
if (list == NIL)
return;
/*
* An empty list MUST be represented as NIL
*/
Assert(list->length > 0);
/*
* Since the list is non-empty, its head and tail must be defined
*/
Assert(list->head != NULL);
Assert(list->tail != NULL);
if (list->length == 1)
Assert(list->tail == list->head);
/*
* The list must have a valid tag
*/
Assert(list->type == T_List ||
list->type == T_IntList ||
list->type == T_OidList);
/*
* XXX: We could iterate through the list here and check if the
* list's length field is actually correct
*/
}
#else
#define check_invariant(l)
#endif /* USE_ASSERT_CHECKING */
List *
lcons(void *datum, List *list)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_List);
new_head_elem(list);
value(list->head) = datum;
return list;
}
List *
lconsi(int datum, List *list)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_IntList);
new_head_elem(list);
ivalue(list->head) = datum;
return list;
}
List *
lconso(Oid datum, List *list)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_OidList);
new_head_elem(list);
ovalue(list->head) = datum;
return list;
}
List *
lappend(List *list, void *datum)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_List);
new_tail_elem(list);
value(list->tail) = datum;
return list;
}
List *
lappendi(List *list, int datum)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_IntList);
new_tail_elem(list);
ivalue(list->tail) = datum;
return list;
}
List *
lappendo(List *list, Oid datum)
{
check_invariant(list);
if (list == NIL)
list = new_list(T_OidList);
new_tail_elem(list);
ovalue(list->tail) = datum;
return list;
}
List *
nconc(List *l1, List *l2)
{
check_invariant(l1);
check_invariant(l2);
/*
* XXX: memory allocation is ambiguous here: does the caller free
* the memory for l1 or l2?
*/
if (l1 == NIL)
return l2;
if (l2 == NIL)
return l1;
if (l1 == l2)
elog(ERROR, "cannot nconc a list to itself");
l1->tail->next = l2->head;
l1->tail = l2->tail;
l1->length += l2->length;
return l1;
}
void *
nth(int n, List *list)
{
ListCell *match;
check_invariant(list);
Assert(n >= 0);
Assert(n < list->length);
/*
* Does the caller actually mean to fetch the tail?
*/
if (n == list->length - 1)
return value(list->tail);
for (match = list->head; n-- > 0; match = match->next)
;
return value(match);
}
/*
* freeList
*
* Free the List nodes of a list
* The pointed-to nodes, if any, are NOT freed.
* This works for integer and Oid lists too.
*/
void
freeList(List *list)
{
while (list != NIL)
{
List *l = list;
list = lnext(list);
pfree(l);
}
}
/*
* equali
* compares two lists of integers
*/
bool
equali(List *list1, List *list2)
{
List *l;
foreach(l, list1)
{
if (list2 == NIL)
return false;
if (lfirsti(l) != lfirsti(list2))
return false;
list2 = lnext(list2);
}
if (list2 != NIL)
return false;
return true;
}
/*
* equalo
* compares two lists of Oids
*/
bool
equalo(List *list1, List *list2)
{
List *l;
foreach(l, list1)
{
if (list2 == NIL)
return false;
if (lfirsto(l) != lfirsto(list2))
return false;
list2 = lnext(list2);
}
if (list2 != NIL)
return false;
return true;
}
/*
* Generate the union of two lists,
* ie, l1 plus all members of l2 that are not already in l1.
*
* NOTE: if there are duplicates in l1 they will still be duplicate in the
* result; but duplicates in l2 are discarded.
*
* The result is a fresh List, but it points to the same member nodes
* as were in the inputs.
*/
List *
set_union(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!member(lfirst(i), retval))
retval = lappend(retval, lfirst(i));
}
return retval;
}
/* set_union for Oid lists */
List *
set_uniono(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!oidMember(lfirsto(i), retval))
retval = lappendo(retval, lfirsto(i));
}
return retval;
}
/* set_union when pointer-equality comparison is sufficient */
List *
set_ptrUnion(List *l1, List *l2)
{
List *retval = listCopy(l1);
List *i;
foreach(i, l2)
{
if (!ptrMember(lfirst(i), retval))
retval = lappend(retval, lfirst(i));
}
return retval;
}
/*
* Generate the intersection of two lists,
* ie, all members of both l1 and l2.
*
* NOTE: if there are duplicates in l1 they will still be duplicate in the
* result; but duplicates in l2 are discarded.
*
* The result is a fresh List, but it points to the same member nodes
* as were in the inputs.
*/
#ifdef NOT_USED
List *
set_intersect(List *l1, List *l2)
{
List *retval = NIL;
List *i;
foreach(i, l1)
{
if (member(lfirst(i), l2))
retval = lappend(retval, lfirst(i));
}
return retval;
}
#endif
/*
* member()
* nondestructive, returns t iff l1 is a member of the list l2
*/
bool
member(void *l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (equal((Node *) l1, (Node *) lfirst(i)))
return true;
}
return false;
}
/*
* like member(), but use when pointer-equality comparison is sufficient
*/
bool
ptrMember(void *l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirst(i))
return true;
}
return false;
}
/*
* membership test for integer lists
*/
bool
intMember(int l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirsti(i))
return true;
}
return false;
}
/*
* membership test for Oid lists
*/
bool
oidMember(Oid l1, List *l2)
{
List *i;
foreach(i, l2)
{
if (l1 == lfirsto(i))
return true;
}
return false;
}
/*
* lremove
* Removes 'elem' from the linked list (destructively changing the list!).
* (If there is more than one equal list member, the first is removed.)
*
* This version matches 'elem' using simple pointer comparison.
* See also LispRemove.
*/
List *
lremove(void *elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (elem == lfirst(l))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* LispRemove
* Removes 'elem' from the linked list (destructively changing the list!).
* (If there is more than one equal list member, the first is removed.)
*
* This version matches 'elem' using equal().
* See also lremove.
*/
List *
LispRemove(void *elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (equal(elem, lfirst(l)))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* lremovei
* lremove() for integer lists.
*/
List *
lremovei(int elem, List *list)
{
List *l;
List *prev = NIL;
List *result = list;
foreach(l, list)
{
if (elem == lfirsti(l))
break;
prev = l;
}
if (l != NIL)
{
if (prev == NIL)
result = lnext(l);
else
lnext(prev) = lnext(l);
pfree(l);
}
return result;
}
/*
* ltruncate
* Truncate a list to n elements.
* Does nothing if n >= length(list).
* NB: the list is modified in-place!
*/
List *
ltruncate(int n, List *list)
{
List *ptr;
if (n <= 0)
return NIL; /* truncate to zero length */
foreach(ptr, list)
{
if (--n == 0)
{
lnext(ptr) = NIL;
break;
}
}
return list;
}
/*
* set_difference
*
* Return l1 without the elements in l2.
*
* The result is a fresh List, but it points to the same member nodes
* as were in l1.
*/
List *
set_difference(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!member(lfirst(i), l2))
result = lappend(result, lfirst(i));
}
return result;
}
/*
* set_differenceo
*
* Same as set_difference, but for Oid lists
*/
List *
set_differenceo(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!oidMember(lfirsto(i), l2))
result = lappendo(result, lfirsto(i));
}
return result;
}
/*
* set_ptrDifference
*
* Same as set_difference, when pointer-equality comparison is sufficient
*/
List *
set_ptrDifference(List *l1, List *l2)
{
List *result = NIL;
List *i;
if (l2 == NIL)
return listCopy(l1); /* slightly faster path for empty l2 */
foreach(i, l1)
{
if (!ptrMember(lfirst(i), l2))
result = lappend(result, lfirst(i));
}
return result;
}
/*
* Reverse a list, non-destructively
*/
#ifdef NOT_USED
List *
lreverse(List *l)
{
List *result = NIL;
List *i;
foreach(i, l)
result = lcons(lfirst(i), result);
return result;
}
#endif
---------------------------(end of broadcast)---------------------------
TIP 3: if posting/reading through Usenet, please send an appropriate
subscribe-nomail command to [EMAIL PROTECTED] so that your
message can get through to the mailing list cleanly