Files
test2/source/blender/blenlib/intern/listbase.cc
Campbell Barton c90e8bae0b Cleanup: spelling in comments & replace some use of single quotes
Previously spell checker ignored text in single quotes however this
meant incorrect spelling was ignored in text where it shouldn't have
been.

In cases single quotes were used for literal strings
(such as variables, code & compiler flags),
replace these with back-ticks.

In cases they were used for UI labels,
replace these with double quotes.

In cases they were used to reference symbols,
replace them with doxygens symbol link syntax (leading hash).

Apply some spelling corrections & tweaks (for check_spelling_* targets).
2025-04-26 11:17:13 +00:00

936 lines
21 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bli
*
* Manipulations on double-linked list (#ListBase structs).
*
* For single linked lists see `BLI_linklist.h`.
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "BLI_assert.h"
#include "BLI_listbase.h"
#include "BLI_strict_flags.h" /* IWYU pragma: keep. Keep last. */
void BLI_movelisttolist(ListBase *dst, ListBase *src)
{
if (src->first == nullptr) {
return;
}
if (dst->first == nullptr) {
dst->first = src->first;
dst->last = src->last;
}
else {
((Link *)dst->last)->next = static_cast<Link *>(src->first);
((Link *)src->first)->prev = static_cast<Link *>(dst->last);
dst->last = src->last;
}
src->first = src->last = nullptr;
}
void BLI_movelisttolist_reverse(ListBase *dst, ListBase *src)
{
if (src->first == nullptr) {
return;
}
if (dst->first == nullptr) {
dst->first = src->first;
dst->last = src->last;
}
else {
((Link *)src->last)->next = static_cast<Link *>(dst->first);
((Link *)dst->first)->prev = static_cast<Link *>(src->last);
dst->first = src->first;
}
src->first = src->last = nullptr;
}
void BLI_listbase_split_after(ListBase *original_listbase, ListBase *split_listbase, void *vlink)
{
BLI_assert(BLI_listbase_is_empty(split_listbase));
BLI_assert(vlink == nullptr || BLI_findindex(original_listbase, vlink) >= 0);
if (vlink == original_listbase->last) {
/* Nothing to split, and `split_listbase` is assumed already empty (see assert above). */
return;
}
if (vlink == nullptr) {
/* Move everything into `split_listbase`. */
std::swap(*original_listbase, *split_listbase);
return;
}
Link *link = static_cast<Link *>(vlink);
Link *next_link = link->next;
BLI_assert(next_link != nullptr);
Link *last_link = static_cast<Link *>(original_listbase->last);
original_listbase->last = link;
split_listbase->first = next_link;
split_listbase->last = last_link;
link->next = nullptr;
next_link->prev = nullptr;
}
void BLI_addhead(ListBase *listbase, void *vlink)
{
Link *link = static_cast<Link *>(vlink);
if (link == nullptr) {
return;
}
link->next = static_cast<Link *>(listbase->first);
link->prev = nullptr;
if (listbase->first) {
((Link *)listbase->first)->prev = link;
}
if (listbase->last == nullptr) {
listbase->last = link;
}
listbase->first = link;
}
void BLI_addtail(ListBase *listbase, void *vlink)
{
Link *link = static_cast<Link *>(vlink);
if (link == nullptr) {
return;
}
link->next = nullptr;
link->prev = static_cast<Link *>(listbase->last);
if (listbase->last) {
((Link *)listbase->last)->next = link;
}
if (listbase->first == nullptr) {
listbase->first = link;
}
listbase->last = link;
}
void BLI_remlink(ListBase *listbase, void *vlink)
{
Link *link = static_cast<Link *>(vlink);
if (link == nullptr) {
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;
}
}
bool BLI_remlink_safe(ListBase *listbase, void *vlink)
{
if (BLI_findindex(listbase, vlink) != -1) {
BLI_remlink(listbase, vlink);
return true;
}
return false;
}
void BLI_listbase_swaplinks(ListBase *listbase, void *vlinka, void *vlinkb)
{
Link *linka = static_cast<Link *>(vlinka);
Link *linkb = static_cast<Link *>(vlinkb);
if (!linka || !linkb) {
return;
}
if (linkb->next == linka) {
std::swap(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. */
std::swap(linka->prev, linkb->prev);
std::swap(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;
}
}
void BLI_listbases_swaplinks(ListBase *listbasea, ListBase *listbaseb, void *vlinka, void *vlinkb)
{
Link *linka = static_cast<Link *>(vlinka);
Link *linkb = static_cast<Link *>(vlinkb);
Link linkc = {nullptr};
if (!linka || !linkb) {
return;
}
/* The reference to `linkc` assigns nullptr, not a dangling pointer so it can be ignored. */
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdangling-pointer"
#endif
/* Temporary link to use as placeholder of the links positions */
BLI_insertlinkafter(listbasea, linka, &linkc);
#if defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 1201 /* gcc12.1+ only */
# pragma GCC diagnostic pop
#endif
/* Bring linka into linkb position */
BLI_remlink(listbasea, linka);
BLI_insertlinkafter(listbaseb, linkb, linka);
/* Bring linkb into linka position */
BLI_remlink(listbaseb, linkb);
BLI_insertlinkafter(listbasea, &linkc, linkb);
/* Remove temporary link */
BLI_remlink(listbasea, &linkc);
}
void *BLI_pophead(ListBase *listbase)
{
Link *link = static_cast<Link *>(listbase->first);
if (link) {
BLI_remlink(listbase, link);
}
return link;
}
void *BLI_poptail(ListBase *listbase)
{
Link *link = static_cast<Link *>(listbase->last);
if (link) {
BLI_remlink(listbase, link);
}
return link;
}
void BLI_freelinkN(ListBase *listbase, void *vlink)
{
Link *link = static_cast<Link *>(vlink);
if (link == nullptr) {
return;
}
BLI_remlink(listbase, link);
MEM_freeN(link);
}
/**
* Assigns all #Link.prev pointers from #Link.next
*/
static void listbase_double_from_single(Link *iter, ListBase *listbase)
{
Link *prev = nullptr;
listbase->first = iter;
do {
iter->prev = prev;
prev = iter;
} while ((iter = iter->next));
listbase->last = prev;
}
#define SORT_IMPL_LINKTYPE Link
/* regular call */
#define SORT_IMPL_FUNC listbase_sort_fn
#include "list_sort_impl.h"
#undef SORT_IMPL_FUNC
/* re-entrant call */
#define SORT_IMPL_USE_THUNK
#define SORT_IMPL_FUNC listbase_sort_fn_r
#include "list_sort_impl.h"
#undef SORT_IMPL_FUNC
#undef SORT_IMPL_USE_THUNK
#undef SORT_IMPL_LINKTYPE
void BLI_listbase_sort(ListBase *listbase, int (*cmp)(const void *, const void *))
{
if (listbase->first != listbase->last) {
Link *head = static_cast<Link *>(listbase->first);
head = listbase_sort_fn(head, cmp);
listbase_double_from_single(head, listbase);
}
}
void BLI_listbase_sort_r(ListBase *listbase,
int (*cmp)(void *, const void *, const void *),
void *thunk)
{
if (listbase->first != listbase->last) {
Link *head = static_cast<Link *>(listbase->first);
head = listbase_sort_fn_r(head, cmp, thunk);
listbase_double_from_single(head, listbase);
}
}
void BLI_insertlinkafter(ListBase *listbase, void *vprevlink, void *vnewlink)
{
Link *prevlink = static_cast<Link *>(vprevlink);
Link *newlink = static_cast<Link *>(vnewlink);
/* newlink before nextlink */
if (newlink == nullptr) {
return;
}
/* empty list */
if (listbase->first == nullptr) {
listbase->first = newlink;
listbase->last = newlink;
return;
}
/* insert at head of list */
if (prevlink == nullptr) {
newlink->prev = nullptr;
newlink->next = static_cast<Link *>(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;
}
}
void BLI_insertlinkbefore(ListBase *listbase, void *vnextlink, void *vnewlink)
{
Link *nextlink = static_cast<Link *>(vnextlink);
Link *newlink = static_cast<Link *>(vnewlink);
/* newlink before nextlink */
if (newlink == nullptr) {
return;
}
/* empty list */
if (listbase->first == nullptr) {
listbase->first = newlink;
listbase->last = newlink;
return;
}
/* insert at end of list */
if (nextlink == nullptr) {
newlink->prev = static_cast<Link *>(listbase->last);
newlink->next = nullptr;
((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;
}
}
void BLI_insertlinkreplace(ListBase *listbase, void *vreplacelink, void *vnewlink)
{
Link *l_old = static_cast<Link *>(vreplacelink);
Link *l_new = static_cast<Link *>(vnewlink);
/* update adjacent links */
if (l_old->next != nullptr) {
l_old->next->prev = l_new;
}
if (l_old->prev != nullptr) {
l_old->prev->next = l_new;
}
/* set direct links */
l_new->next = l_old->next;
l_new->prev = l_old->prev;
/* update list */
if (listbase->first == l_old) {
listbase->first = l_new;
}
if (listbase->last == l_old) {
listbase->last = l_new;
}
}
bool BLI_listbase_link_move(ListBase *listbase, void *vlink, int step)
{
Link *link = static_cast<Link *>(vlink);
Link *hook = link;
const bool is_up = step < 0;
if (step == 0) {
return false;
}
BLI_assert(BLI_findindex(listbase, link) != -1);
/* find link to insert before/after */
const int abs_step = abs(step);
for (int i = 0; i < abs_step; i++) {
hook = is_up ? hook->prev : hook->next;
if (!hook) {
return false;
}
}
/* reinsert link */
BLI_remlink(listbase, vlink);
if (is_up) {
BLI_insertlinkbefore(listbase, hook, vlink);
}
else {
BLI_insertlinkafter(listbase, hook, vlink);
}
return true;
}
bool BLI_listbase_move_index(ListBase *listbase, int from, int to)
{
if (from == to) {
return false;
}
/* Find the link to move. */
void *link = BLI_findlink(listbase, from);
if (!link) {
return false;
}
return BLI_listbase_link_move(listbase, link, to - from);
}
void BLI_freelist(ListBase *listbase)
{
Link *link, *next;
link = static_cast<Link *>(listbase->first);
while (link) {
next = link->next;
free(link);
link = next;
}
BLI_listbase_clear(listbase);
}
void BLI_freelistN(ListBase *listbase)
{
Link *link, *next;
link = static_cast<Link *>(listbase->first);
while (link) {
next = link->next;
MEM_freeN(link);
link = next;
}
BLI_listbase_clear(listbase);
}
int BLI_listbase_count_at_most(const ListBase *listbase, const int count_max)
{
Link *link;
int count = 0;
for (link = static_cast<Link *>(listbase->first); link && count != count_max; link = link->next)
{
count++;
}
return count;
}
int BLI_listbase_count(const ListBase *listbase)
{
int count = 0;
LISTBASE_FOREACH (Link *, link, listbase) {
count++;
}
return count;
}
void *BLI_findlink(const ListBase *listbase, int number)
{
Link *link = nullptr;
if (number >= 0) {
link = static_cast<Link *>(listbase->first);
while (link != nullptr && number != 0) {
number--;
link = link->next;
}
}
return link;
}
void *BLI_rfindlink(const ListBase *listbase, int number)
{
Link *link = nullptr;
if (number >= 0) {
link = static_cast<Link *>(listbase->last);
while (link != nullptr && number != 0) {
number--;
link = link->prev;
}
}
return link;
}
void *BLI_findlinkfrom(Link *start, int step)
{
Link *link = nullptr;
if (step >= 0) {
link = start;
while (link != nullptr && step != 0) {
step--;
link = link->next;
}
}
else {
link = start;
while (link != nullptr && step != 0) {
step++;
link = link->prev;
}
}
return link;
}
int BLI_findindex(const ListBase *listbase, const void *vlink)
{
Link *link = nullptr;
int number = 0;
if (vlink == nullptr) {
return -1;
}
link = static_cast<Link *>(listbase->first);
while (link) {
if (link == vlink) {
return number;
}
number++;
link = link->next;
}
return -1;
}
void *BLI_findstring(const ListBase *listbase, const char *id, const int offset)
{
const char *id_iter;
if (id == nullptr) {
return nullptr;
}
LISTBASE_FOREACH (Link *, link, listbase) {
id_iter = ((const char *)link) + offset;
if (id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_rfindstring(const ListBase *listbase, const char *id, const int offset)
{
/* Same as #BLI_findstring but find reverse. */
LISTBASE_FOREACH_BACKWARD (Link *, link, listbase) {
const char *id_iter = ((const char *)link) + offset;
if (id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_listbase_findafter_string(Link *link, const char *id, const int offset)
{
/* Same as #BLI_findstring but start searching after given link. */
for (link = link->next; link; link = link->next) {
const char *id_iter = ((const char *)link) + offset;
if (id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_findstring_ptr(const ListBase *listbase, const char *id, const int offset)
{
const char *id_iter;
LISTBASE_FOREACH (Link *, link, listbase) {
/* Exact copy of BLI_findstring(), except for this line, and the check for potential nullptr
* below. */
id_iter = *((const char **)(((const char *)link) + offset));
if (id_iter && id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_rfindstring_ptr(const ListBase *listbase, const char *id, const int offset)
{
/* Same as #BLI_findstring_ptr but find reverse. */
const char *id_iter;
LISTBASE_FOREACH_BACKWARD (Link *, link, listbase) {
/* Exact copy of BLI_rfindstring(), except for this line, and the check for potential nullptr
* below. */
id_iter = *((const char **)(((const char *)link) + offset));
if (id_iter && id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_listbase_findafter_string_ptr(Link *link, const char *id, const int offset)
{
const char *id_iter;
for (link = link->next; link; link = link->next) {
/* Exact copy of BLI_findstring(), except for this line, and the check for potential nullptr
* below. */
id_iter = *((const char **)(((const char *)link) + offset));
if (id_iter && id[0] == id_iter[0] && STREQ(id, id_iter)) {
return link;
}
}
return nullptr;
}
void *BLI_findptr(const ListBase *listbase, const void *ptr, const int offset)
{
LISTBASE_FOREACH (Link *, link, listbase) {
/* Exact copy of #BLI_findstring(), except for this line. */
const void *ptr_iter = *((const void **)(((const char *)link) + offset));
if (ptr == ptr_iter) {
return link;
}
}
return nullptr;
}
void *BLI_rfindptr(const ListBase *listbase, const void *ptr, const int offset)
{
/* Same as #BLI_findptr but find reverse. */
LISTBASE_FOREACH_BACKWARD (Link *, link, listbase) {
/* Exact copy of #BLI_rfindstring(), except for this line. */
const void *ptr_iter = *((const void **)(((const char *)link) + offset));
if (ptr == ptr_iter) {
return link;
}
}
return nullptr;
}
void *BLI_listbase_bytes_find(const ListBase *listbase,
const void *bytes,
const size_t bytes_size,
const int offset)
{
LISTBASE_FOREACH (Link *, link, listbase) {
const void *ptr_iter = (const void *)(((const char *)link) + offset);
if (memcmp(bytes, ptr_iter, bytes_size) == 0) {
return link;
}
}
return nullptr;
}
void *BLI_listbase_bytes_rfind(const ListBase *listbase,
const void *bytes,
const size_t bytes_size,
const int offset)
{
/* Same as #BLI_listbase_bytes_find but find reverse. */
LISTBASE_FOREACH_BACKWARD (Link *, link, listbase) {
const void *ptr_iter = (const void *)(((const char *)link) + offset);
if (memcmp(bytes, ptr_iter, bytes_size) == 0) {
return link;
}
}
return nullptr;
}
void *BLI_listbase_string_or_index_find(const ListBase *listbase,
const char *string,
const size_t string_offset,
const int index)
{
Link *link = nullptr;
Link *link_at_index = nullptr;
int index_iter;
for (link = static_cast<Link *>(listbase->first), index_iter = 0; link;
link = link->next, index_iter++)
{
if (string != nullptr && string[0] != '\0') {
const char *string_iter = ((const char *)link) + string_offset;
if (string[0] == string_iter[0] && STREQ(string, string_iter)) {
return link;
}
}
if (index_iter == index) {
link_at_index = link;
}
}
return link_at_index;
}
int BLI_findstringindex(const ListBase *listbase, const char *id, const int offset)
{
Link *link = nullptr;
const char *id_iter;
int i = 0;
link = static_cast<Link *>(listbase->first);
while (link) {
id_iter = ((const char *)link) + offset;
if (id[0] == id_iter[0] && STREQ(id, id_iter)) {
return i;
}
i++;
link = link->next;
}
return -1;
}
ListBase BLI_listbase_from_link(Link *some_link)
{
ListBase list = {some_link, some_link};
if (some_link == nullptr) {
return list;
}
/* Find the first element. */
while (((Link *)list.first)->prev != nullptr) {
list.first = ((Link *)list.first)->prev;
}
/* Find the last element. */
while (((Link *)list.last)->next != nullptr) {
list.last = ((Link *)list.last)->next;
}
return list;
}
void BLI_duplicatelist(ListBase *dst, const ListBase *src)
{
Link *dst_link, *src_link;
/* in this order, to ensure it works if dst == src */
src_link = static_cast<Link *>(src->first);
dst->first = dst->last = nullptr;
while (src_link) {
dst_link = static_cast<Link *>(MEM_dupallocN(src_link));
BLI_addtail(dst, dst_link);
src_link = src_link->next;
}
}
void BLI_listbase_reverse(ListBase *lb)
{
Link *curr = static_cast<Link *>(lb->first);
Link *prev = nullptr;
Link *next = nullptr;
while (curr) {
next = curr->next;
curr->next = prev;
curr->prev = next;
prev = curr;
curr = next;
}
/* swap first/last */
curr = static_cast<Link *>(lb->first);
lb->first = lb->last;
lb->last = curr;
}
void BLI_listbase_rotate_first(ListBase *lb, void *vlink)
{
/* make circular */
((Link *)lb->first)->prev = static_cast<Link *>(lb->last);
((Link *)lb->last)->next = static_cast<Link *>(lb->first);
lb->first = vlink;
lb->last = ((Link *)vlink)->prev;
((Link *)lb->first)->prev = nullptr;
((Link *)lb->last)->next = nullptr;
}
void BLI_listbase_rotate_last(ListBase *lb, void *vlink)
{
/* make circular */
((Link *)lb->first)->prev = static_cast<Link *>(lb->last);
((Link *)lb->last)->next = static_cast<Link *>(lb->first);
lb->first = ((Link *)vlink)->next;
lb->last = vlink;
((Link *)lb->first)->prev = nullptr;
((Link *)lb->last)->next = nullptr;
}
bool BLI_listbase_validate(ListBase *lb)
{
if (lb->first == nullptr && lb->last == nullptr) {
/* Empty list. */
return true;
}
if (ELEM(nullptr, lb->first, lb->last)) {
/* If one of the pointer is null, but not this other, this is a corrupted listbase. */
return false;
}
/* Walk the list in bot directions to ensure all next & prev pointers are valid and consistent.
*/
LISTBASE_FOREACH (Link *, lb_link, lb) {
if (lb_link == lb->first) {
if (lb_link->prev != nullptr) {
return false;
}
}
if (lb_link == lb->last) {
if (lb_link->next != nullptr) {
return false;
}
}
}
LISTBASE_FOREACH_BACKWARD (Link *, lb_link, lb) {
if (lb_link == lb->last) {
if (lb_link->next != nullptr) {
return false;
}
}
if (lb_link == lb->first) {
if (lb_link->prev != nullptr) {
return false;
}
}
}
return true;
}
LinkData *BLI_genericNodeN(void *data)
{
LinkData *ld;
if (data == nullptr) {
return nullptr;
}
/* create new link, and make it hold the given data */
ld = MEM_callocN<LinkData>(__func__);
ld->data = data;
return ld;
}