/* * ptrlist.c * * Pointer list manipulation * * (C) Copyright Linus Torvalds 2003-2005 */ #include #include #include #include "ptrlist.h" #include "allocate.h" #include "compat.h" __DECLARE_ALLOCATOR(struct ptr_list, ptrlist); __ALLOCATOR(struct ptr_list, "ptr list", ptrlist); int ptr_list_size(struct ptr_list *head) { int nr = 0; if (head) { struct ptr_list *list = head; do { nr += list->nr; } while ((list = list->next) != head); } return nr; } /* * Linearize the entries of a list up to a total of 'max', * and return the nr of entries linearized. * * The array to linearize into (second argument) 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; } /* * 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); } } 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 *)); } void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag) { struct ptr_list *list = *listp; struct ptr_list *last = NULL; /* gcc complains needlessly */ void **ret; int nr; /* The low two bits are reserved for tags */ assert((3 & (unsigned long)ptr) == 0); assert((~3 & tag) == 0); ptr = (void *)(tag | (unsigned long)ptr); 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; } 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; } 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; } /* This removes the last entry, but doesn't pack the ptr 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; } 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; } 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); } 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; }