diff --git a/src/backend/lib/Makefile b/src/backend/lib/Makefile index 2e1061e..c94297a 100644 *** a/src/backend/lib/Makefile --- b/src/backend/lib/Makefile *************** *** 12,17 **** subdir = src/backend/lib top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = dllist.o stringinfo.o include $(top_srcdir)/src/backend/common.mk --- 12,17 ---- top_builddir = ../../.. include $(top_builddir)/src/Makefile.global ! OBJS = dllist.o stringinfo.o ilist.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/lib/REnew file mode 100644 index 0000000..3a3d8a8 *** /dev/null --- b/src/backend/lib/README.ilist *************** *** 0 **** --- 1,43 ---- + his is intended to be used in the following manner: + + #include "lib/ilist.h" + + typedef struct something_in_a_list + { + int value; + int somethingelse; + Dlist_head values; + } something_in_a_list; + + typedef struct value_in_a_list + { + int data; + int somethingelse; + Dlist_node list_node; + } value_in_a_list; + + something_in_a_list *somelist = get_blarg(); + + Dlist_push_head(somelist, &get_value_in_a_list()->list_node); + ... + Dlist_push_head(somelist, &get_value_in_a_list()->list_node); + ... + ... + Dlist_node *node, + *next; + + Dlist_foreach(node, somelist) + { + value_in_a_list *val = ilist_container(value_in_a_list, list_node, node); + do_something_with_val(val); + } + + Dlist_foreach_modify(node, next, somelist) + { + /* + * here we can inspect the node and maybe delete it. Any other + * manipulation is forbidden. + */ + if (node is to be deleted) + Dlist_delete(node); + } diff --git a/src/backend/lib/ilist.new file mode 100644 index 0000000..239c538 *** /dev/null --- b/src/backend/lib/ilist.c *************** *** 0 **** --- 1,117 ---- + /*------------------------------------------------------------------------- + * + * ilist.c + * support for integrated/inline doubly- and singly- linked lists + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/lib/ilist.c + * + * NOTES + * This file only contains functions that are too big to be considered + * for inlining. See ilist.h for most of the goodies. + * + *------------------------------------------------------------------------- + */ + #include "postgres.h" + + /* + * If inlines are in use, include the header normally which will get us only + * the function definitions as inlines. But if inlines aren't available, + * include the header with ILIST_USE_DEFINITION defined; this makes it pour in + * regular (not inline) function declarations and their definitions. + */ + #ifdef USE_INLINE + #include "lib/ilist.h" + #else /* USE_INLINE */ + #define ILIST_USE_DEFINITION + #include "lib/ilist.h" + #undef ILIST_USE_DEFINITION + #endif + + /* + * removes a node from a list + * + * Attention: O(n) + */ + void + Slist_delete(Slist_head *head, Slist_node *node) + { + Slist_node *last = &head->head; + Slist_node *cur; + bool found PG_USED_FOR_ASSERTS_ONLY = false; + + while ((cur = last->next) != NULL) + { + if (cur == node) + { + last->next = cur->next; + #ifdef USE_ASSERT_CHECKING + found = true; + #endif + break; + } + last = cur; + } + + Slist_check(head); + Assert(found); + } + + #ifdef ILIST_DEBUG + /* + * Verify integrity of a doubly linked list + */ + void + Dlist_check(Dlist_head *head) + { + Dlist_node *cur; + + if (!head || !(&head->head)) + elog(ERROR, "doubly linked list head is not properly initialized"); + + for (cur = head->head.next; cur != &head->head; cur = cur->next) + { + if (!(cur) || + !(cur->next) || + !(cur->prev) || + !(cur->prev->next == cur) || + !(cur->next->prev == cur)) + elog(ERROR, "doubly linked list is corrupted"); + } + + for (cur = head->head.prev; cur != &head->head; cur = cur->prev) + { + if (!(cur) || + !(cur->next) || + !(cur->prev) || + !(cur->prev->next == cur) || + !(cur->next->prev == cur)) + elog(ERROR, "doubly linked list is corrupted"); + } + } + + /* + * Verify integrity of a singly linked list + */ + void + Slist_check(Slist_head *head) + { + Slist_node *cur; + + if (!head || !(&head->head)) + elog(ERROR, "singly linked list head is not properly initialized"); + + /* + * there isn't much we can test in a singly linked list except that its + * really circular + */ + for (cur = head->head.next; cur != &head->head; cur = cur->next) + if (!cur) + elog(ERROR, "singly linked list is corrupted"); + } + + #endif /* ILIST_DEBUG */ diff --git a/src/include/lib/inew file mode 100644 index 0000000..27c5dba *** /dev/null --- b/src/include/lib/ilist.h *************** *** 0 **** --- 1,453 ---- + /*------------------------------------------------------------------------- + * + * ilist.h + * integrated/inline doubly- and singly-linked lists + * + * This implementation is as efficient as possible: the lists don't have + * any memory management overhead, because the list pointers are embedded + * within some larger structure. Also, they are always circularly linked, even + * when empty; this enables many manipulations to be done without branches, + * which is beneficial performance-wise. + * + * See src/backend/lib/README.ilist for intended usage. + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/include/lib/ilist.h + *------------------------------------------------------------------------- + */ + #ifndef ILIST_H + #define ILIST_H + + /* + * enable for extra debugging. This is rather expensive, so it's not enabled by + * default even with --enable-cassert. + */ + #undef ILIST_DEBUG + + /* + * gcc warns about unused parameters if -Wunused-parameter is specified (part + * of -Wextra). In the cases below its perfectly fine not to use those + * parameters because they are just passed to make the interface consistent and + * to allow debugging in case of ILIST_DEBUG. + * + * FIXME -- this should be part of c.h. + */ + #ifdef __GNUC__ + #define unused_attr __attribute__((unused)) + #else + #define unused_attr + #endif + + /* + * struct to embed in other structs that need to be part of a doubly linked + * list. + */ + typedef struct Dlist_node Dlist_node; + struct Dlist_node + { + Dlist_node *prev; + Dlist_node *next; + }; + + /* + * Head of a doubly linked list. + * + * Lists are internally *always* circularly linked, even when empty. + */ + typedef struct Dlist_head + { + Dlist_node head; + } Dlist_head; + + /* + * struct to embed in other structs that need to be part of a singly linked + * list. + */ + typedef struct Slist_node Slist_node; + struct Slist_node + { + Slist_node *next; + }; + + /* + * Head of a singly linked list. + * + * Lists are internally *always* circularly linked, even when empty. + */ + typedef struct Slist_head + { + Slist_node head; + } Slist_head; + + #ifdef USE_INLINE + #define INLINE_IF_POSSIBLE static inline + #define ILIST_USE_DEFINITION + #else + #define INLINE_IF_POSSIBLE + + /* Prototypes for functions we want to be inlined if possible */ + extern void Dlist_check(unused_attr Dlist_head *head); + extern void Dlist_init(Dlist_head *head); + extern void Dlist_push_head(Dlist_head *head, Dlist_node *node); + extern void Dlist_push_tail(Dlist_head *head, Dlist_node *node); + extern void Dlist_insert_after(unused_attr Dlist_head *head, + Dlist_node *after, Dlist_node *node); + extern void Dlist_insert_before(unused_attr Dlist_head *head, + Dlist_node *before, Dlist_node *node); + extern void Dlist_delete(unused_attr Dlist_head *head, Dlist_node *node); + extern Dlist_node *Dlist_pop_head(Dlist_head *head); + extern void Dlist_move_head(Dlist_head *head, Dlist_node *node); + extern bool Dlist_has_next(Dlist_head *head, Dlist_node *node); + extern bool Dlist_has_prev(Dlist_head *head, Dlist_node *node); + extern bool Dlist_is_empty(Dlist_head *head); + extern void Dlist_check(Dlist_head *head); + + extern void Slist_init(Slist_head *head); + extern void Slist_push_head(Slist_head *head, Slist_node *node); + extern Slist_node *Slist_pop_head(Slist_head *head); + extern void Slist_insert_after(unused_attr Slist_head *head, + Slist_node *after, Slist_node *node); + extern bool Slist_is_empty(Slist_head *head); + extern bool Slist_has_next(unused_attr Slist_head *head, + Slist_node *node); + #endif /* USE_INLINE */ + + /* These functions are too big to be inline, so they are in the C file always */ + extern void Slist_delete(Slist_head *head, Slist_node *node); + + #ifdef ILIST_DEBUG + extern void Dlist_check(Dlist_head *head); + extern void Slist_check(Slist_head *head); + #else + #define Dlist_check(head) + #define Slist_check(head) + #endif /* ILIST_DEBUG */ + + /* + * Return a pointer to the struct 'type' when the passed 'ptr' points to the + * element 'membername'. + */ + #define ilist_container(type, membername, ptr) \ + ((type*)((char*)(ptr) - offsetof(type, membername))) + + /* + * The following function definitions are only used if inlining is supported by + * the compiler, or when included from a file that explicitly declares + * ILIST_USE_DEFINITION. + */ + #ifdef ILIST_USE_DEFINITION + + /* + * Initialize the head of a list. Previous state will be thrown away without + * any cleanup. + */ + INLINE_IF_POSSIBLE void + Dlist_init(Dlist_head *head) + { + head->head.next = head->head.prev = &head->head; + + Dlist_check(head); + } + + /* + * inserts a node at the beginning of the list + */ + INLINE_IF_POSSIBLE void + Dlist_push_head(Dlist_head *head, Dlist_node *node) + { + node->next = head->head.next; + node->prev = &head->head; + node->next->prev = node; + head->head.next = node; + + Dlist_check(head); + } + + /* + * inserts a node at the end of the list + */ + INLINE_IF_POSSIBLE void + Dlist_push_tail(Dlist_head *head, Dlist_node *node) + { + node->next = &head->head; + node->prev = head->head.prev; + node->prev->next = node; + head->head.prev = node; + + Dlist_check(head); + } + + /* + * insert a node after another *in the same list* + */ + INLINE_IF_POSSIBLE void + Dlist_insert_after(unused_attr Dlist_head *head, Dlist_node *after, + Dlist_node *node) + { + Dlist_check(head); + + node->prev = after; + node->next = after->next; + after->next = node; + node->next->prev = node; + + Dlist_check(head); + } + + /* + * insert a node after another *in the same list* + */ + INLINE_IF_POSSIBLE void + Dlist_insert_before(unused_attr Dlist_head *head, Dlist_node *before, + Dlist_node *node) + { + Dlist_check(head); + + node->prev = before->prev; + node->next = before; + before->prev = node; + node->prev->next = node; + + Dlist_check(head); + } + + /* + * deletes a node from a list + */ + INLINE_IF_POSSIBLE void + Dlist_delete(unused_attr Dlist_head *head, Dlist_node *node) + { + Dlist_check(head); + + node->prev->next = node->next; + node->next->prev = node->prev; + + Dlist_check(head); + } + + /* + * deletes the first node from a list or returns NULL + */ + INLINE_IF_POSSIBLE Dlist_node * + Dlist_pop_head(Dlist_head *head) + { + Dlist_node *ret; + + if (&head->head == head->head.next) + return NULL; + + ret = head->head.next; + Dlist_delete(head, head->head.next); + return ret; + } + + /* + * moves an element (that has to be in the list) to the head of the list + */ + INLINE_IF_POSSIBLE void + Dlist_move_head(Dlist_head *head, Dlist_node *node) + { + /* fast path if it's already at the head */ + if (&head->head == node) + return; + + Dlist_delete(head, node); + Dlist_push_head(head, node); + + Dlist_check(head); + } + + /* + * Check whether the passed node is the last element in the list + */ + INLINE_IF_POSSIBLE bool + Dlist_has_next(Dlist_head *head, Dlist_node *node) + { + return node->next != &head->head; + } + + /* + * Check whether the passed node is the first element in the list + */ + INLINE_IF_POSSIBLE bool + Dlist_has_prev(Dlist_head *head, Dlist_node *node) + { + return node->prev != &head->head; + } + + /* + * Check whether the list is empty. + */ + INLINE_IF_POSSIBLE bool + Dlist_is_empty(Dlist_head *head) + { + return head->head.next == head->head.prev; + } + #endif /* ILIST_USE_DEFINITION */ + + /* + * Return the value of first element in the list if there is one, return NULL + * otherwise. + * + * ptr *will* be evaluated multiple times. + */ + #define Dlist_head(type, membername, ptr) \ + ( \ + (&((ptr)->head) == (ptr)->head.next) ? \ + NULL : ilist_container(type, membername, (ptr)->head.next) \ + ) + + /* + * Return the value of the first element. This will return corrupt data if + * there is no element in the list. + */ + #define Dlist_head_unchecked(type, membername, ptr) \ + ilist_container(type, membername, (ptr)->head.next) + + /* + * Return the value of the last element in the list if there is one, return + * NULL otherwise. + * + * ptr *will* be evaluated multiple times. + */ + #define Dlist_tail(type, membername, ptr) \ + ( \ + (&((ptr)->head) == (ptr)->head.prev) ? \ + NULL : ilist_container(type, membername, (ptr)->head.prev) \ + ) + + /* + * Iterate through the list storing the current element in the variable + * 'name'. 'ptr' will be evaluated repeatedly. + * + * It is *not* allowed to manipulate the list during iteration. + */ + #define Dlist_foreach(name, ptr) \ + for (name = (ptr)->head.next; name != &(ptr)->head; name = name->next) + + /* + * Iterate through the list storing the current element in the variable 'name' + * and also storing the next list element in the variable 'nxt'. + * + * It is allowed to delete the current element from the list. Every other + * manipulation can lead to corruption. + */ + #define Dlist_foreach_modify(name, nxt, ptr) \ + for (name = (ptr)->head.next, nxt = name->next; \ + name != &(ptr)->head; \ + name = nxt, nxt = name->next) + + /* + * Iterate through the list in reverse order storing the current element in the + * variable 'name'. 'ptr' will be evaluated repeatedly. + * + * It is *not* allowed to manipulate the list during iteration. + */ + #define Dlist_reverse_foreach(name, ptr) \ + for (name = (ptr)->head.prev; name != &(ptr)->head; name = name->prev) + + + #ifdef ILIST_USE_DEFINITION + + /* + * Initialize a singly linked list. + */ + INLINE_IF_POSSIBLE void + Slist_init(Slist_head *head) + { + head->head.next = NULL; + + Slist_check(head); + } + + /* + * Install a new element as head of the list, pushing the original head to the + * second position. + */ + INLINE_IF_POSSIBLE void + Slist_push_head(Slist_head *head, Slist_node *node) + { + node->next = head->head.next; + head->head.next = node; + + Slist_check(head); + } + + /* + * Returns the first element of the list, removing it. + * Fails if the list is empty + */ + INLINE_IF_POSSIBLE Slist_node * + Slist_pop_head(Slist_head *head) + { + Slist_node *node; + + node = head->head.next; + head->head.next = head->head.next->next; + + Slist_check(head); + + return node; + } + + /* + * Insert a new element after the listed one. + */ + INLINE_IF_POSSIBLE void + Slist_insert_after(unused_attr Slist_head *head, Slist_node *after, + Slist_node *node) + { + node->next = after->next; + after->next = node; + + Slist_check(head); + } + + /* + * Is the list empty? + */ + INLINE_IF_POSSIBLE bool + Slist_is_empty(Slist_head *head) + { + return head->head.next == NULL; + } + + /* + * Is there at least one more element in the list? + */ + INLINE_IF_POSSIBLE bool + Slist_has_next(unused_attr Slist_head *head, + Slist_node *node) + { + return node->next != NULL; + } + #endif /* ILIST_USE_DEFINITION */ + + #define Slist_get_head(type, membername, ptr) \ + ( \ + Slist_is_empty(ptr) ? NULL : \ + ilist_container(type, membername, (ptr).next) \ + ) + + #define Slist_get_head_unchecked(type, membername, ptr) \ + ilist_container(type, membername, (ptr)->head.next) + + #define Slist_foreach(name, ptr) \ + for (name = (ptr)->head.next; name != NULL; name = name->next) + + #define Slist_foreach_modify(name, nxt, ptr) \ + for (name = (ptr)->head.next, nxt = name ? name->next : NULL; \ + name != NULL; \ + name = nxt, nxt = name ? name->next : NULL) + + /* + * Get rid of ILIST_USE_DEFINITION; it's not interesting outside this file. + */ + #ifdef USE_INLINE + #undef ILIST_USE_DEFINITION + #endif + + #endif /* ILIST_H */ diff --git a/src/tools/pgindenindex a831a1e..0aa3e3c 100644