/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL LICENSE BLOCK ***** * */ /** \file blender/blenlib/intern/listbase.c * \ingroup bli * * Manipulations on ListBase structs */ #include #include #include "MEM_guardedalloc.h" #include "DNA_listBase.h" #include "BLI_listbase.h" /* implementation */ /** * moves the entire contents of \a src onto the end of \a dst. */ void BLI_movelisttolist(ListBase *dst, ListBase *src) { if (src->first == NULL) return; if (dst->first == NULL) { dst->first = src->first; dst->last = src->last; } else { ((Link *)dst->last)->next = src->first; ((Link *)src->first)->prev = dst->last; dst->last = src->last; } src->first = src->last = NULL; } /** * Prepends \a vlink (assumed to begin with a Link) onto listbase. */ void BLI_addhead(ListBase *listbase, void *vlink) { Link *link = vlink; if (link == NULL) return; link->next = listbase->first; link->prev = NULL; if (listbase->first) ((Link *)listbase->first)->prev = link; if (listbase->last == NULL) listbase->last = link; listbase->first = link; } /** * Appends \a vlink (assumed to begin with a Link) onto listbase. */ void BLI_addtail(ListBase *listbase, void *vlink) { Link *link = vlink; if (link == NULL) return; link->next = NULL; link->prev = listbase->last; if (listbase->last) ((Link *)listbase->last)->next = link; if (listbase->first == NULL) listbase->first = link; listbase->last = link; } /** * Removes \a vlink from \a listbase. Assumes it is linked into there! */ void BLI_remlink(ListBase *listbase, void *vlink) { Link *link = vlink; if (link == NULL) return; if (link->next) link->next->prev = link->prev; if (link->prev) link->prev->next = link->next; if (listbase->last == link) listbase->last = link->prev; if (listbase->first == link) listbase->first = link->next; } /** * Checks that \a vlink is linked into listbase, removing it from there if so. */ bool BLI_remlink_safe(ListBase *listbase, void *vlink) { if (BLI_findindex(listbase, vlink) != -1) { BLI_remlink(listbase, vlink); return true; } else { return false; } } /** * Swaps \a vlinka and \a vlinkb in the list. Assumes they are both already in the list! */ void BLI_listbase_swaplinks(ListBase *listbase, void *vlinka, void *vlinkb) { Link *linka = vlinka; Link *linkb = vlinkb; if (!linka || !linkb) return; if (linkb->next == linka) { SWAP(Link *, linka, linkb); } if (linka->next == linkb) { linka->next = linkb->next; linkb->prev = linka->prev; linka->prev = linkb; linkb->next = linka; } else { /* Non-contiguous items, we can safely swap. */ SWAP(Link *, linka->prev, linkb->prev); SWAP(Link *, linka->next, linkb->next); } /* Update neighbors of linka and linkb. */ if (linka->prev) linka->prev->next = linka; if (linka->next) linka->next->prev = linka; if (linkb->prev) linkb->prev->next = linkb; if (linkb->next) linkb->next->prev = linkb; if (listbase->last == linka) listbase->last = linkb; else if (listbase->last == linkb) listbase->last = linka; if (listbase->first == linka) listbase->first = linkb; else if (listbase->first == linkb) listbase->first = linka; } /** * Removes the head from \a listbase and returns it. */ void *BLI_pophead(ListBase *listbase) { Link *link; if ((link = listbase->first)) { BLI_remlink(listbase, link); } return link; } /** * Removes the tail from \a listbase and returns it. */ void *BLI_poptail(ListBase *listbase) { Link *link; if ((link = listbase->last)) { BLI_remlink(listbase, link); } return link; } /** * Removes \a vlink from listbase and disposes of it. Assumes it is linked into there! */ void BLI_freelinkN(ListBase *listbase, void *vlink) { Link *link = vlink; if (link == NULL) return; BLI_remlink(listbase, link); MEM_freeN(link); } /** * Sorts the elements of listbase into the order defined by cmp * (which should return 1 iff its first arg should come after its second arg). * This uses insertion sort, so NOT ok for large list. */ void BLI_listbase_sort(ListBase *listbase, int (*cmp)(const void *, const void *)) { Link *current = NULL; Link *previous = NULL; Link *next = NULL; if (listbase->first != listbase->last) { for (previous = listbase->first, current = previous->next; current; current = next) { next = current->next; previous = current->prev; BLI_remlink(listbase, current); while (previous && cmp(previous, current) == 1) { previous = previous->prev; } BLI_insertlinkafter(listbase, previous, current); } } } void BLI_listbase_sort_r(ListBase *listbase, void *thunk, int (*cmp)(void *, const void *, const void *)) { Link *current = NULL; Link *previous = NULL; Link *next = NULL; if (listbase->first != listbase->last) { for (previous = listbase->first, current = previous->next; current; current = next) { next = current->next; previous = current->prev; BLI_remlink(listbase, current); while (previous && cmp(thunk, previous, current) == 1) { previous = previous->prev; } BLI_insertlinkafter(listbase, previous, current); } } } /** * Inserts \a vnewlink immediately following \a vprevlink in \a listbase. * Or, if \a vprevlink is NULL, puts \a vnewlink at the front of the list. */ void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink) { Link *prevlink = vprevlink; Link *newlink = vnewlink; /* newlink before nextlink */ if (newlink == NULL) return; /* empty list */ if (listbase->first == NULL) { listbase->first = newlink; listbase->last = newlink; return; } /* insert at head of list */ if (prevlink == NULL) { newlink->prev = NULL; newlink->next = listbase->first; newlink->next->prev = newlink; listbase->first = newlink; return; } /* at end of list */ if (listbase->last == prevlink) { listbase->last = newlink; } newlink->next = prevlink->next; newlink->prev = prevlink; prevlink->next = newlink; if (newlink->next) { newlink->next->prev = newlink; } } /** * Inserts \a vnewlink immediately preceding \a vnextlink in listbase. * Or, if \a vnextlink is NULL, puts \a vnewlink at the end of the list. */ void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink) { Link *nextlink = vnextlink; Link *newlink = vnewlink; /* newlink before nextlink */ if (newlink == NULL) return; /* empty list */ if (listbase->first == NULL) { listbase->first = newlink; listbase->last = newlink; return; } /* insert at end of list */ if (nextlink == NULL) { newlink->prev = listbase->last; newlink->next = NULL; ((Link *)listbase->last)->next = newlink; listbase->last = newlink; return; } /* at beginning of list */ if (listbase->first == nextlink) { listbase->first = newlink; } newlink->next = nextlink; newlink->prev = nextlink->prev; nextlink->prev = newlink; if (newlink->prev) { newlink->prev->next = newlink; } } /** * Removes and disposes of the entire contents of listbase using direct free(3). */ void BLI_freelist(ListBase *listbase) { Link *link, *next; link = listbase->first; while (link) { next = link->next; free(link); link = next; } BLI_listbase_clear(listbase); } /** * Removes and disposes of the entire contents of \a listbase using guardedalloc. */ void BLI_freelistN(ListBase *listbase) { Link *link, *next; link = listbase->first; while (link) { next = link->next; MEM_freeN(link); link = next; } BLI_listbase_clear(listbase); } /** * Returns the number of elements in \a listbase, up until (and including count_max) * * \note Use to avoid redundant looping. */ int BLI_listbase_count_ex(const ListBase *listbase, const int count_max) { Link *link; int count = 0; for (link = listbase->first; link && count != count_max; link = link->next) { count++; } return count; } /** * Returns the number of elements in \a listbase. */ int BLI_listbase_count(const ListBase *listbase) { Link *link; int count = 0; for (link = listbase->first; link; link = link->next) { count++; } return count; } /** * Returns the nth element of \a listbase, numbering from 0. */ void *BLI_findlink(const ListBase *listbase, int number) { Link *link = NULL; if (number >= 0) { link = listbase->first; while (link != NULL && number != 0) { number--; link = link->next; } } return link; } /** * Returns the nth-last element of \a listbase, numbering from 0. */ void *BLI_rfindlink(const ListBase *listbase, int number) { Link *link = NULL; if (number >= 0) { link = listbase->last; while (link != NULL && number != 0) { number--; link = link->prev; } } return link; } /** * Returns the position of \a vlink within \a listbase, numbering from 0, or -1 if not found. */ int BLI_findindex(const ListBase *listbase, const void *vlink) { Link *link = NULL; int number = 0; if (vlink == NULL) return -1; link = listbase->first; while (link) { if (link == vlink) return number; number++; link = link->next; } return -1; } /** * Finds the first element of \a listbase which contains the null-terminated * string \a id at the specified offset, returning NULL if not found. */ void *BLI_findstring(const ListBase *listbase, const char *id, const int offset) { Link *link = NULL; const char *id_iter; for (link = listbase->first; link; link = link->next) { id_iter = ((const char *)link) + offset; if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { return link; } } return NULL; } /* same as above but find reverse */ /** * Finds the last element of \a listbase which contains the * null-terminated string \a id at the specified offset, returning NULL if not found. */ void *BLI_rfindstring(const ListBase *listbase, const char *id, const int offset) { Link *link = NULL; const char *id_iter; for (link = listbase->last; link; link = link->prev) { id_iter = ((const char *)link) + offset; if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { return link; } } return NULL; } /** * Finds the first element of \a listbase which contains a pointer to the * null-terminated string \a id at the specified offset, returning NULL if not found. */ void *BLI_findstring_ptr(const ListBase *listbase, const char *id, const int offset) { Link *link = NULL; const char *id_iter; for (link = listbase->first; link; link = link->next) { /* exact copy of BLI_findstring(), except for this line */ id_iter = *((const char **)(((const char *)link) + offset)); if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { return link; } } return NULL; } /* same as above but find reverse */ /** * Finds the last element of \a listbase which contains a pointer to the * null-terminated string \a id at the specified offset, returning NULL if not found. */ void *BLI_rfindstring_ptr(const ListBase *listbase, const char *id, const int offset) { Link *link = NULL; const char *id_iter; for (link = listbase->last; link; link = link->prev) { /* exact copy of BLI_rfindstring(), except for this line */ id_iter = *((const char **)(((const char *)link) + offset)); if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) { return link; } } return NULL; } /** * Finds the first element of listbase which contains the specified pointer value * at the specified offset, returning NULL if not found. */ void *BLI_findptr(const ListBase *listbase, const void *ptr, const int offset) { Link *link = NULL; const void *ptr_iter; for (link = listbase->first; link; link = link->next) { /* exact copy of BLI_findstring(), except for this line */ ptr_iter = *((const void **)(((const char *)link) + offset)); if (ptr == ptr_iter) { return link; } } return NULL; } /* same as above but find reverse */ /** * Finds the last element of listbase which contains the specified pointer value * at the specified offset, returning NULL if not found. */ void *BLI_rfindptr(const ListBase *listbase, const void *ptr, const int offset) { Link *link = NULL; const void *ptr_iter; for (link = listbase->last; link; link = link->prev) { /* exact copy of BLI_rfindstring(), except for this line */ ptr_iter = *((const void **)(((const char *)link) + offset)); if (ptr == ptr_iter) { return link; } } return NULL; } /** * Returns the 1-based index of the first element of listbase which contains the specified * null-terminated string at the specified offset, or -1 if not found. */ int BLI_findstringindex(const ListBase *listbase, const char *id, const int offset) { Link *link = NULL; const char *id_iter; int i = 0; link = listbase->first; while (link) { id_iter = ((const char *)link) + offset; if (id[0] == id_iter[0] && strcmp(id, id_iter) == 0) return i; i++; link = link->next; } return -1; } /** * Sets dst to a duplicate of the entire contents of src. dst may be the same as src. */ void BLI_duplicatelist(ListBase *dst, const ListBase *src) { struct Link *dst_link, *src_link; /* in this order, to ensure it works if dst == src */ src_link = src->first; dst->first = dst->last = NULL; while (src_link) { dst_link = MEM_dupallocN(src_link); BLI_addtail(dst, dst_link); src_link = src_link->next; } } void BLI_listbase_reverse(ListBase *lb) { struct Link *curr = lb->first; struct Link *prev = NULL; struct Link *next = NULL; while (curr) { next = curr->next; curr->next = prev; curr->prev = next; prev = curr; curr = next; } /* swap first/last */ curr = lb->first; lb->first = lb->last; lb->last = curr; } /** * \param vlink Link to make first. */ void BLI_listbase_rotate_first(ListBase *lb, void *vlink) { /* make circular */ ((Link *)lb->first)->prev = lb->last; ((Link *)lb->last)->next = lb->first; lb->first = vlink; lb->last = ((Link *)vlink)->prev; ((Link *)lb->first)->prev = NULL; ((Link *)lb->last)->next = NULL; } /** * \param vlink Link to make last. */ void BLI_listbase_rotate_last(ListBase *lb, void *vlink) { /* make circular */ ((Link *)lb->first)->prev = lb->last; ((Link *)lb->last)->next = lb->first; lb->first = ((Link *)vlink)->next; lb->last = vlink; ((Link *)lb->first)->prev = NULL; ((Link *)lb->last)->next = NULL; } /* create a generic list node containing link to provided data */ LinkData *BLI_genericNodeN(void *data) { LinkData *ld; if (data == NULL) return NULL; /* create new link, and make it hold the given data */ ld = MEM_callocN(sizeof(LinkData), __func__); ld->data = data; return ld; }