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