1 /*
   2  * ptrlist.c
   3  *
   4  * Pointer list manipulation
   5  *
   6  * (C) Copyright Linus Torvalds 2003-2005
   7  */
   8 #include <stdlib.h>
   9 #include <string.h>
  10 #include <assert.h>
  11 
  12 #include "ptrlist.h"
  13 #include "allocate.h"
  14 #include "compat.h"
  15 
  16 __DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
  17 __ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
  18 __ALLOCATOR(struct ptr_list, "rl ptr list", rl_ptrlist);
  19 
  20 int ptr_list_size(struct ptr_list *head)
  21 {
  22         int nr = 0;
  23 
  24         if (head) {
  25                 struct ptr_list *list = head;
  26                 do {
  27                         nr += list->nr - list->rm;
  28                 } while ((list = list->next) != head);
  29         }
  30         return nr;
  31 }
  32 
  33 /*
  34  * Linearize the entries of a list up to a total of 'max',
  35  * and return the nr of entries linearized.
  36  *
  37  * The array to linearize into (second argument) should really
  38  * be "void *x[]", but we want to let people fill in any kind
  39  * of pointer array, so let's just call it "void **".
  40  */
  41 int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
  42 {
  43         int nr = 0;
  44         if (head && max > 0) {
  45                 struct ptr_list *list = head;
  46 
  47                 do {
  48                         int i = list->nr;
  49                         if (i > max) 
  50                                 i = max;
  51                         memcpy(arr, list->list, i*sizeof(void *));
  52                         arr += i;
  53                         nr += i;
  54                         max -= i;
  55                         if (!max)
  56                                 break;
  57                 } while ((list = list->next) != head);
  58         }
  59         return nr;
  60 }
  61 
  62 /*
  63  * When we've walked the list and deleted entries,
  64  * we may need to re-pack it so that we don't have
  65  * any empty blocks left (empty blocks upset the
  66  * walking code
  67  */
  68 void pack_ptr_list(struct ptr_list **listp)
  69 {
  70         struct ptr_list *head = *listp;
  71 
  72         if (head) {
  73                 struct ptr_list *entry = head;
  74                 do {
  75                         struct ptr_list *next;
  76 restart:
  77                         next = entry->next;
  78                         if (!entry->nr) {
  79                                 struct ptr_list *prev;
  80                                 if (next == entry) {
  81                                         __free_ptrlist(entry);
  82                                         *listp = NULL;
  83                                         return;
  84                                 }
  85                                 prev = entry->prev;
  86                                 prev->next = next;
  87                                 next->prev = prev;
  88                                 __free_ptrlist(entry);
  89                                 if (entry == head) {
  90                                         *listp = next;
  91                                         head = next;
  92                                         entry = next;
  93                                         goto restart;
  94                                 }
  95                         }
  96                         entry = next;
  97                 } while (entry != head);
  98         }
  99 }               
 100 
 101 void split_ptr_list_head(struct ptr_list *head)
 102 {
 103         int old = head->nr, nr = old / 2;
 104         struct ptr_list *newlist = __alloc_ptrlist(0);
 105         struct ptr_list *next = head->next;
 106 
 107         old -= nr;
 108         head->nr = old;
 109         newlist->next = next;
 110         next->prev = newlist;
 111         newlist->prev = head;
 112         head->next = newlist;
 113         newlist->nr = nr;
 114         memcpy(newlist->list, head->list + old, nr * sizeof(void *));
 115         memset(head->list + old, 0xf0, nr * sizeof(void *));
 116 }
 117 
 118 int rl_ptrlist_hack;
 119 void **__add_ptr_list(struct ptr_list **listp, void *ptr, unsigned long tag)
 120 {
 121         struct ptr_list *list = *listp;
 122         struct ptr_list *last = NULL; /* gcc complains needlessly */
 123         void **ret;
 124         int nr;
 125 
 126         /* The low two bits are reserved for tags */
 127         assert((3 & (unsigned long)ptr) == 0);
 128         assert((~3 & tag) == 0);
 129         ptr = (void *)(tag | (unsigned long)ptr);
 130 
 131         if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
 132                 struct ptr_list *newlist;
 133 
 134                 if (rl_ptrlist_hack)
 135                         newlist = __alloc_rl_ptrlist(0);
 136                 else
 137                         newlist = __alloc_ptrlist(0);
 138                 if (!list) {
 139                         newlist->next = newlist;
 140                         newlist->prev = newlist;
 141                         *listp = newlist;
 142                 } else {
 143                         newlist->prev = last;
 144                         newlist->next = list;
 145                         list->prev = newlist;
 146                         last->next = newlist;
 147                 }
 148                 last = newlist;
 149                 nr = 0;
 150         }
 151         ret = last->list + nr;
 152         *ret = ptr;
 153         nr++;
 154         last->nr = nr;
 155         return ret;
 156 }
 157 
 158 int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
 159 {
 160         void *ptr;
 161 
 162         FOR_EACH_PTR(*list, ptr) {
 163                 if (ptr == entry) {
 164                         DELETE_CURRENT_PTR(ptr);
 165                         if (!--count)
 166                                 goto out;
 167                 }
 168         } END_FOR_EACH_PTR(ptr);
 169         assert(count <= 0);
 170 out:
 171         pack_ptr_list(list);
 172         return count;
 173 }
 174 
 175 int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr, void *new_ptr, int count)
 176 {
 177         void *ptr;
 178 
 179         FOR_EACH_PTR(*list, ptr) {
 180                 if (ptr==old_ptr) {
 181                         REPLACE_CURRENT_PTR(ptr, new_ptr);
 182                         if (!--count)
 183                                 goto out;
 184                 }
 185         }END_FOR_EACH_PTR(ptr);
 186         assert(count <= 0);
 187 out:
 188         return count;
 189 }
 190 
 191 /* This removes the last entry, but doesn't pack the ptr list */
 192 void * undo_ptr_list_last(struct ptr_list **head)
 193 {
 194         struct ptr_list *last, *first = *head;
 195 
 196         if (!first)
 197                 return NULL;
 198         last = first;
 199         do {
 200                 last = last->prev;
 201                 if (last->nr) {
 202                         void *ptr;
 203                         int nr = --last->nr;
 204                         ptr = last->list[nr];
 205                         last->list[nr] = (void *)0xf1f1f1f1;
 206                         return ptr;
 207                 }
 208         } while (last != first);
 209         return NULL;
 210 }
 211 
 212 void * delete_ptr_list_last(struct ptr_list **head)
 213 {
 214         void *ptr = NULL;
 215         struct ptr_list *last, *first = *head;
 216 
 217         if (!first)
 218                 return NULL;
 219         last = first->prev;
 220         if (last->nr)
 221                 ptr = last->list[--last->nr];
 222         if (last->nr <=0) {
 223                 first->prev = last->prev;
 224                 last->prev->next = first;
 225                 if (last == first)
 226                         *head = NULL;
 227                 __free_ptrlist(last);
 228         }
 229         return ptr;
 230 }
 231 
 232 void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
 233 {
 234         void *entry;
 235         FOR_EACH_PTR(a, entry) {
 236                 add_ptr_list(b, entry);
 237         } END_FOR_EACH_PTR(entry);
 238 }
 239 
 240 void __free_ptr_list(struct ptr_list **listp)
 241 {
 242         struct ptr_list *tmp, *list = *listp;
 243 
 244         if (!list)
 245                 return;
 246 
 247         list->prev->next = NULL;
 248         while (list) {
 249                 tmp = list;
 250                 list = list->next;
 251                 __free_ptrlist(tmp);
 252         }
 253 
 254         *listp = NULL;
 255 }