123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- /*
- * ptrlist.c
- *
- * (C) Copyright Linus Torvalds 2003-2005
- */
- ///
- // Pointer list manipulation
- // -------------------------
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include "ptrlist.h"
- #include "allocate.h"
- #include "compat.h"
- __DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
- __ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
- ///
- // get the size of a ptrlist
- // @head: the head of the list
- // @return: the size of the list given by @head.
- int ptr_list_size(struct ptr_list *head)
- {
- int nr = 0;
- if (head) {
- struct ptr_list *list = head;
- do {
- nr += list->nr - list->rm;
- } while ((list = list->next) != head);
- }
- return nr;
- }
- ///
- // test if a list is empty
- // @head: the head of the list
- // @return: ``true`` if the list is empty, ``false`` otherwise.
- bool ptr_list_empty(const struct ptr_list *head)
- {
- const struct ptr_list *list = head;
- if (!head)
- return true;
- do {
- if (list->nr - list->rm)
- return false;
- } while ((list = list->next) != head);
- return true;
- }
- ///
- // test is a list contains more than one element
- // @head: the head of the list
- // @return: ``true`` if the list has more than 1 element, ``false`` otherwise.
- bool ptr_list_multiple(const struct ptr_list *head)
- {
- const struct ptr_list *list = head;
- int nr = 0;
- if (!head)
- return false;
- do {
- nr += list->nr - list->rm;
- if (nr > 1)
- return true;
- } while ((list = list->next) != head);
- return false;
- }
- ///
- // get the first element of a ptrlist
- // @head: the head of the list
- // @return: the first element of the list or ``NULL`` if the list is empty
- void *first_ptr_list(struct ptr_list *head)
- {
- struct ptr_list *list = head;
- if (!head)
- return NULL;
- while (list->nr == 0) {
- list = list->next;
- if (list == head)
- return NULL;
- }
- return PTR_ENTRY_NOTAG(list, 0);
- }
- ///
- // get the last element of a ptrlist
- // @head: the head of the list
- // @return: the last element of the list or ``NULL`` if the list is empty
- void *last_ptr_list(struct ptr_list *head)
- {
- struct ptr_list *list;
- if (!head)
- return NULL;
- list = head->prev;
- while (list->nr == 0) {
- if (list == head)
- return NULL;
- list = list->prev;
- }
- return PTR_ENTRY_NOTAG(list, list->nr-1);
- }
- ///
- // get the nth element of a ptrlist
- // @head: the head of the list
- // @return: the nth element of the list or ``NULL`` if the list is too short.
- void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx)
- {
- struct ptr_list *head = list;
- if (!head)
- return NULL;
- do {
- unsigned int nr = list->nr;
- if (idx < nr)
- return list->list[idx];
- else
- idx -= nr;
- } while ((list = list->next) != head);
- return NULL;
- }
- ///
- // linearize the entries of a list
- //
- // @head: the list to be linearized
- // @arr: a ``void*`` array to fill with @head's entries
- // @max: the maximum number of entries to store into @arr
- // @return: the number of entries linearized.
- //
- // Linearize the entries of a list up to a total of @max,
- // and return the nr of entries linearized.
- //
- // The array to linearize into (@arr) should really
- // be ``void *x[]``, but we want to let people fill in any kind
- // of pointer array, so let's just call it ``void **``.
- int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
- {
- int nr = 0;
- if (head && max > 0) {
- struct ptr_list *list = head;
- do {
- int i = list->nr;
- if (i > max)
- i = max;
- memcpy(arr, list->list, i*sizeof(void *));
- arr += i;
- nr += i;
- max -= i;
- if (!max)
- break;
- } while ((list = list->next) != head);
- }
- return nr;
- }
- ///
- // pack a ptrlist
- //
- // @listp: a pointer to the list to be packed.
- //
- // When we've walked the list and deleted entries,
- // we may need to re-pack it so that we don't have
- // any empty blocks left (empty blocks upset the
- // walking code).
- void pack_ptr_list(struct ptr_list **listp)
- {
- struct ptr_list *head = *listp;
- if (head) {
- struct ptr_list *entry = head;
- do {
- struct ptr_list *next;
- restart:
- next = entry->next;
- if (!entry->nr) {
- struct ptr_list *prev;
- if (next == entry) {
- __free_ptrlist(entry);
- *listp = NULL;
- return;
- }
- prev = entry->prev;
- prev->next = next;
- next->prev = prev;
- __free_ptrlist(entry);
- if (entry == head) {
- *listp = next;
- head = next;
- entry = next;
- goto restart;
- }
- }
- entry = next;
- } while (entry != head);
- }
- }
- ///
- // split a ptrlist block
- // @head: the ptrlist block to be split
- //
- // A new block is inserted just after @head and the entries
- // at the half end of @head are moved to this new block.
- // The goal being to create space inside @head for a new entry.
- void split_ptr_list_head(struct ptr_list *head)
- {
- int old = head->nr, nr = old / 2;
- struct ptr_list *newlist = __alloc_ptrlist(0);
- struct ptr_list *next = head->next;
- old -= nr;
- head->nr = old;
- newlist->next = next;
- next->prev = newlist;
- newlist->prev = head;
- head->next = newlist;
- newlist->nr = nr;
- memcpy(newlist->list, head->list + old, nr * sizeof(void *));
- memset(head->list + old, 0xf0, nr * sizeof(void *));
- }
- ///
- // add an entry to a ptrlist
- // @listp: a pointer to the list
- // @ptr: the entry to add to the list
- // @return: the address where the new entry is stored.
- //
- // :note: code must not use this function and should use
- // :func:`add_ptr_list` instead.
- void **__add_ptr_list(struct ptr_list **listp, void *ptr)
- {
- struct ptr_list *list = *listp;
- struct ptr_list *last = NULL; /* gcc complains needlessly */
- void **ret;
- int nr;
- if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
- struct ptr_list *newlist = __alloc_ptrlist(0);
- if (!list) {
- newlist->next = newlist;
- newlist->prev = newlist;
- *listp = newlist;
- } else {
- newlist->prev = last;
- newlist->next = list;
- list->prev = newlist;
- last->next = newlist;
- }
- last = newlist;
- nr = 0;
- }
- ret = last->list + nr;
- *ret = ptr;
- nr++;
- last->nr = nr;
- return ret;
- }
- ///
- // add a tagged entry to a ptrlist
- // @listp: a pointer to the list
- // @ptr: the entry to add to the list
- // @tag: the tag to add to @ptr
- // @return: the address where the new entry is stored.
- //
- // :note: code must not use this function and should use
- // :func:`add_ptr_list_tag` instead.
- void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag)
- {
- /* The low two bits are reserved for tags */
- assert((3 & (unsigned long)ptr) == 0);
- assert((~3 & tag) == 0);
- ptr = (void *)(tag | (unsigned long)ptr);
- return __add_ptr_list(listp, ptr);
- }
- ///
- // test if some entry is already present in a ptrlist
- // @list: the head of the list
- // @entry: the entry to test
- // @return: ``true`` if the entry is already present, ``false`` otherwise.
- bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry)
- {
- const struct ptr_list *list = head;
- if (!head)
- return false;
- do {
- int nr = list->nr;
- int i;
- for (i = 0; i < nr; i++)
- if (list->list[i] == entry)
- return true;
- } while ((list = list->next) != head);
- return false;
- }
- ///
- // delete an entry from a ptrlist
- // @list: a pointer to the list
- // @entry: the item to be deleted
- // @count: the minimum number of times @entry should be deleted or 0.
- int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
- {
- void *ptr;
- FOR_EACH_PTR(*list, ptr) {
- if (ptr == entry) {
- DELETE_CURRENT_PTR(ptr);
- if (!--count)
- goto out;
- }
- } END_FOR_EACH_PTR(ptr);
- assert(count <= 0);
- out:
- pack_ptr_list(list);
- return count;
- }
- ///
- // replace an entry in a ptrlist
- // @list: a pointer to the list
- // @old_ptr: the entry to be replaced
- // @new_ptr: the new entry
- // @count: the minimum number of times @entry should be deleted or 0.
- int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr,
- void *new_ptr, int count)
- {
- void *ptr;
- FOR_EACH_PTR(*list, ptr) {
- if (ptr==old_ptr) {
- REPLACE_CURRENT_PTR(ptr, new_ptr);
- if (!--count)
- goto out;
- }
- }END_FOR_EACH_PTR(ptr);
- assert(count <= 0);
- out:
- return count;
- }
- ///
- // remove the last entry of a ptrlist
- // @head: a pointer to the list
- // @return: the last element of the list or NULL if the list is empty.
- //
- // :note: this doesn't repack the list
- void * undo_ptr_list_last(struct ptr_list **head)
- {
- struct ptr_list *last, *first = *head;
- if (!first)
- return NULL;
- last = first;
- do {
- last = last->prev;
- if (last->nr) {
- void *ptr;
- int nr = --last->nr;
- ptr = last->list[nr];
- last->list[nr] = (void *)0xf1f1f1f1;
- return ptr;
- }
- } while (last != first);
- return NULL;
- }
- ///
- // remove the last entry and repack the list
- // @head: a pointer to the list
- // @return: the last element of the list or NULL if the list is empty.
- void * delete_ptr_list_last(struct ptr_list **head)
- {
- void *ptr = NULL;
- struct ptr_list *last, *first = *head;
- if (!first)
- return NULL;
- last = first->prev;
- if (last->nr)
- ptr = last->list[--last->nr];
- if (last->nr <=0) {
- first->prev = last->prev;
- last->prev->next = first;
- if (last == first)
- *head = NULL;
- __free_ptrlist(last);
- }
- return ptr;
- }
- ///
- // concat two ptrlists
- // @a: the source list
- // @b: a pointer to the destination list.
- // The element of @a are added at the end of @b.
- void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
- {
- void *entry;
- FOR_EACH_PTR(a, entry) {
- add_ptr_list(b, entry);
- } END_FOR_EACH_PTR(entry);
- }
- ///
- // copy the elements of a list at the end of another list.
- // @listp: a pointer to the destination list.
- // @src: the head of the source list.
- void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src)
- {
- struct ptr_list *head, *tail;
- struct ptr_list *cur = src;
- int idx;
- if (!src)
- return;
- head = *listp;
- if (!head) {
- *listp = src;
- return;
- }
- tail = head->prev;
- idx = tail->nr;
- do {
- struct ptr_list *next;
- int nr = cur->nr;
- int i;
- for (i = 0; i < nr;) {
- void *ptr = cur->list[i++];
- if (!ptr)
- continue;
- if (idx >= LIST_NODE_NR) {
- struct ptr_list *prev = tail;
- tail = __alloc_ptrlist(0);
- prev->next = tail;
- tail->prev = prev;
- prev->nr = idx;
- idx = 0;
- }
- tail->list[idx++] = ptr;
- }
- next = cur->next;
- __free_ptrlist(cur);
- cur = next;
- } while (cur != src);
- tail->nr = idx;
- head->prev = tail;
- tail->next = head;
- }
- ///
- // free a ptrlist
- // @listp: a pointer to the list
- // Each blocks of the list are freed (but the entries
- // themselves are not freed).
- //
- // :note: code must not use this function and should use
- // the macro :func:`free_ptr_list` instead.
- void __free_ptr_list(struct ptr_list **listp)
- {
- struct ptr_list *tmp, *list = *listp;
- if (!list)
- return;
- list->prev->next = NULL;
- while (list) {
- tmp = list;
- list = list->next;
- __free_ptrlist(tmp);
- }
- *listp = NULL;
- }
|