/* * Truncate 'list' to contain no more than 'new_size' elements. This * modifies the list in-place! Despite this, callers should use the * pointer returned by this function to refer to the newly truncated * list -- it may or may not be the same as the pointer that was * passed. * * Note that any cells removed by list_truncate() are NOT pfree'd. */ List * list_truncate(List *list, int new_size) { ListCell *cell; int n; if (new_size <= 0) return NIL; /* truncate to zero length */ /* If asked to effectively extend the list, do nothing */ if (new_size >= list_length(list)) return list; n = 1; foreach(cell, list) { if (n == new_size) { cell->next = NULL; list->tail = cell; list->length = new_size; check_list_invariants(list); return list; } n++; } /* keep the compiler quiet; never reached */ Assert(false); return list; }
/* * Delete 'cell' from 'list'; 'prev' is the previous element to 'cell' * in 'list', if any (i.e. prev == NULL iff list->head == cell) * * The cell is pfree'd, as is the List header if this was the last member. */ List * list_delete_cell(List *list, ListCell *cell, ListCell *prev) { check_list_invariants(list); Assert(prev != NULL ? lnext(prev) == cell : list_head(list) == cell); /* * If we're about to delete the last node from the list, free the whole * list instead and return NIL, which is the only valid representation of * a zero-length list. */ if (list->length == 1) { list_free(list); return NIL; } /* * Otherwise, adjust the necessary list links, deallocate the particular * node we have just removed, and return the list we were given. */ list->length--; if (prev) prev->next = cell->next; else list->head = cell->next; if (list->tail == cell) list->tail = prev; pfree(cell); return list; }
/* * Delete the first element of the list. * * This is useful to replace the Lisp-y code "list = lnext(list);" in cases * where the intent is to alter the list rather than just traverse it. * Beware that the removed cell is freed, whereas the lnext() coding leaves * the original list head intact if there's another pointer to it. */ List * list_delete_first(List *list) { check_list_invariants(list); if (list == NIL) return NIL; /* would an error be better? */ return list_delete_cell(list, list_head(list), NULL); }
ListCell * lappend_cell_oid(List *list, ListCell *prev, Oid datum) { ListCell *new_cell; Assert(IsOidList(list)); new_cell = add_new_cell(list, prev); lfirst_oid(new_cell) = datum; check_list_invariants(list); return new_cell; }
ListCell * lappend_cell_int(List *list, ListCell *prev, int datum) { ListCell *new_cell; Assert(IsIntegerList(list)); new_cell = add_new_cell(list, prev); lfirst_int(new_cell) = datum; check_list_invariants(list); return new_cell; }
/* * Prepend an OID to the list. See lcons() */ List * lcons_oid(Oid datum, List *list) { Assert(IsOidList(list)); if (list == NIL) list = new_list(T_OidList); else new_head_cell(list); lfirst_oid(list->head) = datum; check_list_invariants(list); return list; }
/* * Prepend an integer to the list. See lcons() */ List * lcons_int(int datum, List *list) { Assert(IsIntegerList(list)); if (list == NIL) list = new_list(T_IntList); else new_head_cell(list); lfirst_int(list->head) = datum; check_list_invariants(list); return list; }
/* * Prepend a new element to the list. A pointer to the modified list * is returned. Note that this function may or may not destructively * modify the list; callers should always use this function's return * value, rather than continuing to use the pointer passed as the * second argument. * * Caution: before Postgres 8.0, the original List was unmodified and * could be considered to retain its separate identity. This is no longer * the case. */ List * lcons(void *datum, List *list) { Assert(IsPointerList(list)); if (list == NIL) list = new_list(T_List); else new_head_cell(list); lfirst(list->head) = datum; check_list_invariants(list); return list; }
/* * Append an OID to the specified list. See lappend() */ List * lappend_oid(List *list, Oid datum) { Assert(IsOidList(list)); if (list == NIL) list = new_list(T_OidList); else new_tail_cell(list); lfirst_oid(list->tail) = datum; check_list_invariants(list); return list; }
/* * Append an integer to the specified list. See lappend() */ List * lappend_int(List *list, int datum) { Assert(IsIntegerList(list)); if (list == NIL) list = new_list(T_IntList); else new_tail_cell(list); lfirst_int(list->tail) = datum; check_list_invariants(list); return list; }
/* * Append a pointer to the list. A pointer to the modified list is * returned. Note that this function may or may not destructively * modify the list; callers should always use this function's return * value, rather than continuing to use the pointer passed as the * first argument. */ List * lappend(List *list, void *datum) { Assert(IsPointerList(list)); if (list == NIL) list = new_list(T_List); else new_tail_cell(list); lfirst(list->tail) = datum; check_list_invariants(list); return list; }
/* * Return a shallow copy of the specified list, without the first N elements. */ List * list_copy_tail(const List *oldlist, int nskip) { List *newlist; ListCell *newlist_prev; ListCell *oldlist_cur; if (nskip < 0) nskip = 0; /* would it be better to elog? */ if (oldlist == NIL || nskip >= oldlist->length) return NIL; newlist = new_list(oldlist->type); newlist->length = oldlist->length - nskip; /* * Skip over the unwanted elements. */ oldlist_cur = oldlist->head; while (nskip-- > 0) oldlist_cur = oldlist_cur->next; /* * Copy over the data in the first remaining cell; new_list() has already * allocated the head cell itself */ newlist->head->data = oldlist_cur->data; newlist_prev = newlist->head; oldlist_cur = oldlist_cur->next; while (oldlist_cur) { ListCell *newlist_cur; newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); newlist_cur->data = oldlist_cur->data; newlist_prev->next = newlist_cur; newlist_prev = newlist_cur; oldlist_cur = oldlist_cur->next; } newlist_prev->next = NULL; newlist->tail = newlist_prev; check_list_invariants(newlist); return newlist; }
/* * Return true iff the integer 'datum' is a member of the list. */ bool list_member_int(const List *list, int datum) { const ListCell *cell; Assert(IsIntegerList(list)); check_list_invariants(list); foreach(cell, list) { if (lfirst_int(cell) == datum) return true; } return false; }
/* * Return true iff the OID 'datum' is a member of the list. */ bool list_member_oid(const List *list, Oid datum) { const ListCell *cell; Assert(IsOidList(list)); check_list_invariants(list); foreach(cell, list) { if (lfirst_oid(cell) == datum) return true; } return false; }
/* * Return true iff 'datum' is a member of the list. Equality is * determined via equal(), so callers should ensure that they pass a * Node as 'datum'. */ bool list_member(const List *list, const void *datum) { const ListCell *cell; Assert(IsPointerList(list)); check_list_invariants(list); foreach(cell, list) { if (equal(lfirst(cell), datum)) return true; } return false; }
/* * Return true iff 'datum' is a member of the list. Equality is * determined by using simple pointer comparison. */ bool list_member_ptr(List *list, void *datum) { ListCell *cell; Assert(IsPointerList(list)); check_list_invariants(list); foreach(cell, list) { if (lfirst(cell) == datum) return true; } return false; }
/* * Append to list1 each member of list2 that isn't already in list1. * * Whether an element is already a member of the list is determined * via equal(). * * This is almost the same functionality as list_union(), but list1 is * modified in-place rather than being copied. Note also that list2's cells * are not inserted in list1, so the analogy to list_concat() isn't perfect. */ List * list_concat_unique(List *list1, List *list2) { ListCell *cell; Assert(IsPointerList(list1)); Assert(IsPointerList(list2)); foreach(cell, list2) { if (!list_member(list1, lfirst(cell))) list1 = lappend(list1, lfirst(cell)); } check_list_invariants(list1); return list1; }
/* * This variant of list_union() operates upon lists of OIDs. */ List * list_union_oid(const List *list1, const List *list2) { List *result; const ListCell *cell; Assert(IsOidList(list1)); Assert(IsOidList(list2)); result = list_copy(list1); foreach(cell, list2) { if (!list_member_oid(result, lfirst_oid(cell))) result = lappend_oid(result, lfirst_oid(cell)); } check_list_invariants(result); return result; }
/* * Concatenate list2 to the end of list1, and return list1. list1 is * destructively changed. Callers should be sure to use the return * value as the new pointer to the concatenated list: the 'list1' * input pointer may or may not be the same as the returned pointer. * * The nodes in list2 are merely appended to the end of list1 in-place * (i.e. they aren't copied; the two lists will share some of the same * storage). Therefore, invoking list_free() on list2 will also * invalidate a portion of list1. */ List * list_concat(List *list1, List *list2) { if (list1 == NIL) return list2; if (list2 == NIL) return list1; if (list1 == list2) elog(ERROR, "cannot list_concat() a list to itself"); Assert(list1->type == list2->type); list1->length += list2->length; list1->tail->next = list2->head; list1->tail = list2->tail; check_list_invariants(list1); return list1; }
/* * This variant of list_union() operates upon lists of integers. */ List * list_union_int(List *list1, List *list2) { List *result; ListCell *cell; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); result = list_copy(list1); foreach(cell, list2) { if (!list_member_int(result, lfirst_int(cell))) result = lappend_int(result, lfirst_int(cell)); } check_list_invariants(result); return result; }
/* * Locate the n'th cell (counting from 0) of the list. It is an assertion * failure if there is no such cell. */ ListCell * list_nth_cell(const List *list, int n) { ListCell *match; Assert(list != NIL); Assert(n >= 0); Assert(n < list->length); check_list_invariants(list); /* Does the caller actually mean to fetch the tail? */ if (n == list->length - 1) return list->tail; for (match = list->head; n-- > 0; match = match->next) ; return match; }
/* * This variant of list_difference() operates upon lists of OIDs. */ List * list_difference_oid(const List *list1, const List *list2) { const ListCell *cell; List *result = NIL; Assert(IsOidList(list1)); Assert(IsOidList(list2)); if (list2 == NIL) return list_copy(list1); foreach(cell, list1) { if (!list_member_oid(list2, lfirst_oid(cell))) result = lappend_oid(result, lfirst_oid(cell)); } check_list_invariants(result); return result; }
/* * This variant of list_difference() operates upon lists of integers. */ List * list_difference_int(List *list1, List *list2) { ListCell *cell; List *result = NIL; Assert(IsIntegerList(list1)); Assert(IsIntegerList(list2)); if (list2 == NIL) return list_copy(list1); foreach(cell, list1) { if (!list_member_int(list2, lfirst_int(cell))) result = lappend_int(result, lfirst_int(cell)); } check_list_invariants(result); return result; }
/* As above, but for OIDs */ List * list_delete_oid(List *list, Oid datum) { ListCell *cell; ListCell *prev; Assert(IsOidList(list)); check_list_invariants(list); prev = NULL; foreach(cell, list) { if (lfirst_oid(cell) == datum) return list_delete_cell(list, cell, prev); prev = cell; } /* Didn't find a match: return the list unmodified */ return list; }
/* * Delete the first cell in list that matches datum, if any. * Equality is determined via equal(). */ List * list_delete(List *list, void *datum) { ListCell *cell; ListCell *prev; Assert(IsPointerList(list)); check_list_invariants(list); prev = NULL; foreach(cell, list) { if (equal(lfirst(cell), datum)) return list_delete_cell(list, cell, prev); prev = cell; } /* Didn't find a match: return the list unmodified */ return list; }
/* * Free all storage in a list, and optionally the pointed-to elements */ static void list_free_private(List *list, bool deep) { ListCell *cell; check_list_invariants(list); cell = list_head(list); while (cell != NULL) { ListCell *tmp = cell; cell = lnext(cell); if (deep) pfree(lfirst(tmp)); pfree(tmp); } if (list) pfree(list); }
/* * Return a list that contains all the cells that are in both list1 and * list2. The returned list is freshly allocated via palloc(), but the * cells themselves point to the same objects as the cells of the * input lists. * * Duplicate entries in list1 will not be suppressed, so it's only a true * "intersection" if list1 is known unique beforehand. * * This variant works on lists of pointers, and determines list * membership via equal(). Note that the list1 member will be pointed * to in the result. */ List * list_intersection(const List *list1, const List *list2) { List *result; const ListCell *cell; if (list1 == NIL || list2 == NIL) return NIL; Assert(IsPointerList(list1)); Assert(IsPointerList(list2)); result = NIL; foreach(cell, list1) { if (list_member(list2, lfirst(cell))) result = lappend(result, lfirst(cell)); } check_list_invariants(result); return result; }
/* * Return a shallow copy of the specified list. */ List * list_copy(const List *oldlist) { List *newlist; ListCell *newlist_prev; ListCell *oldlist_cur; if (oldlist == NIL) return NIL; newlist = new_list(oldlist->type); newlist->length = oldlist->length; /* * Copy over the data in the first cell; new_list() has already allocated * the head cell itself */ newlist->head->data = oldlist->head->data; newlist_prev = newlist->head; oldlist_cur = oldlist->head->next; while (oldlist_cur) { ListCell *newlist_cur; newlist_cur = (ListCell *) palloc(sizeof(*newlist_cur)); newlist_cur->data = oldlist_cur->data; newlist_prev->next = newlist_cur; newlist_prev = newlist_cur; oldlist_cur = oldlist_cur->next; } newlist_prev->next = NULL; newlist->tail = newlist_prev; check_list_invariants(newlist); return newlist; }