1 /*
   2  * ptrlist.c
   3  *
   4  * (C) Copyright Linus Torvalds 2003-2005
   5  */
   6 
   7 ///
   8 // Pointer list manipulation
   9 // -------------------------
  10 
  11 #include <stdlib.h>
  12 #include <string.h>
  13 #include <assert.h>
  14 
  15 #include "ptrlist.h"
  16 #include "allocate.h"
  17 #include "compat.h"
  18 
  19 __DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
  20 __ALLOCATOR(struct ptr_list, "ptr list", ptrlist);
  21 __ALLOCATOR(struct ptr_list, "rl ptr list", rl_ptrlist);
  22 
  23 ///
  24 // get the size of a ptrlist
  25 // @head: the head of the list
  26 // @return: the size of the list given by @head.
  27 int ptr_list_size(struct ptr_list *head)
  28 {
  29         int nr = 0;
  30 
  31         if (head) {
  32                 struct ptr_list *list = head;
  33                 do {
  34                         nr += list->nr - list->rm;
  35                 } while ((list = list->next) != head);
  36         }
  37         return nr;
  38 }
  39 
  40 ///
  41 // test if a list is empty
  42 // @head: the head of the list
  43 // @return: ``true`` if the list is empty, ``false`` otherwise.
  44 bool ptr_list_empty(const struct ptr_list *head)
  45 {
  46         const struct ptr_list *list = head;
  47 
  48         if (!head)
  49                 return true;
  50 
  51         do {
  52                 if (list->nr - list->rm)
  53                         return false;
  54         } while ((list = list->next) != head);
  55 
  56         return true;
  57 }
  58 
  59 ///
  60 // test is a list contains more than one element
  61 // @head: the head of the list
  62 // @return: ``true`` if the list has more than 1 element, ``false`` otherwise.
  63 bool ptr_list_multiple(const struct ptr_list *head)
  64 {
  65         const struct ptr_list *list = head;
  66         int nr = 0;
  67 
  68         if (!head)
  69                 return false;
  70 
  71         do {
  72                 nr += list->nr - list->rm;
  73                 if (nr > 1)
  74                         return true;
  75         } while ((list = list->next) != head);
  76 
  77         return false;
  78 }
  79 
  80 ///
  81 // get the first element of a ptrlist
  82 // @head: the head of the list
  83 // @return: the first element of the list or ``NULL`` if the list is empty
  84 void *first_ptr_list(struct ptr_list *head)
  85 {
  86         struct ptr_list *list = head;
  87 
  88         if (!head)
  89                 return NULL;
  90 
  91         while (list->nr == 0) {
  92                 list = list->next;
  93                 if (list == head)
  94                         return NULL;
  95         }
  96         return PTR_ENTRY_NOTAG(list, 0);
  97 }
  98 
  99 ///
 100 // get the last element of a ptrlist
 101 // @head: the head of the list
 102 // @return: the last element of the list or ``NULL`` if the list is empty
 103 void *last_ptr_list(struct ptr_list *head)
 104 {
 105         struct ptr_list *list;
 106 
 107         if (!head)
 108                 return NULL;
 109         list = head->prev;
 110         while (list->nr == 0) {
 111                 if (list == head)
 112                         return NULL;
 113                 list = list->prev;
 114         }
 115         return PTR_ENTRY_NOTAG(list, list->nr-1);
 116 }
 117 
 118 ///
 119 // get the nth element of a ptrlist
 120 // @head: the head of the list
 121 // @return: the nth element of the list or ``NULL`` if the list is too short.
 122 void *ptr_list_nth_entry(struct ptr_list *list, unsigned int idx)
 123 {
 124         struct ptr_list *head = list;
 125 
 126         if (!head)
 127                 return NULL;
 128 
 129         do {
 130                 unsigned int nr = list->nr;
 131 
 132                 if (idx < nr)
 133                         return list->list[idx];
 134                 else
 135                         idx -= nr;
 136         } while ((list = list->next) != head);
 137         return NULL;
 138 }
 139 
 140 ///
 141 // linearize the entries of a list
 142 //
 143 // @head: the list to be linearized
 144 // @arr: a ``void*`` array to fill with @head's entries
 145 // @max: the maximum number of entries to store into @arr
 146 // @return: the number of entries linearized.
 147 //
 148 // Linearize the entries of a list up to a total of @max,
 149 // and return the nr of entries linearized.
 150 //
 151 // The array to linearize into (@arr) should really
 152 // be ``void *x[]``, but we want to let people fill in any kind
 153 // of pointer array, so let's just call it ``void **``.
 154 int linearize_ptr_list(struct ptr_list *head, void **arr, int max)
 155 {
 156         int nr = 0;
 157         if (head && max > 0) {
 158                 struct ptr_list *list = head;
 159 
 160                 do {
 161                         int i = list->nr;
 162                         if (i > max) 
 163                                 i = max;
 164                         memcpy(arr, list->list, i*sizeof(void *));
 165                         arr += i;
 166                         nr += i;
 167                         max -= i;
 168                         if (!max)
 169                                 break;
 170                 } while ((list = list->next) != head);
 171         }
 172         return nr;
 173 }
 174 
 175 ///
 176 // pack a ptrlist
 177 //
 178 // @listp: a pointer to the list to be packed.
 179 //
 180 // When we've walked the list and deleted entries,
 181 // we may need to re-pack it so that we don't have
 182 // any empty blocks left (empty blocks upset the
 183 // walking code).
 184 void pack_ptr_list(struct ptr_list **listp)
 185 {
 186         struct ptr_list *head = *listp;
 187 
 188         if (head) {
 189                 struct ptr_list *entry = head;
 190                 do {
 191                         struct ptr_list *next;
 192 restart:
 193                         next = entry->next;
 194                         if (!entry->nr) {
 195                                 struct ptr_list *prev;
 196                                 if (next == entry) {
 197                                         __free_ptrlist(entry);
 198                                         *listp = NULL;
 199                                         return;
 200                                 }
 201                                 prev = entry->prev;
 202                                 prev->next = next;
 203                                 next->prev = prev;
 204                                 __free_ptrlist(entry);
 205                                 if (entry == head) {
 206                                         *listp = next;
 207                                         head = next;
 208                                         entry = next;
 209                                         goto restart;
 210                                 }
 211                         }
 212                         entry = next;
 213                 } while (entry != head);
 214         }
 215 }               
 216 
 217 ///
 218 // split a ptrlist block
 219 // @head: the ptrlist block to be splitted
 220 //
 221 // A new block is inserted just after @head and the entries
 222 // at the half end of @head are moved to this new block.
 223 // The goal being to create space inside @head for a new entry.
 224 void split_ptr_list_head(struct ptr_list *head)
 225 {
 226         int old = head->nr, nr = old / 2;
 227         struct ptr_list *newlist = __alloc_ptrlist(0);
 228         struct ptr_list *next = head->next;
 229 
 230         old -= nr;
 231         head->nr = old;
 232         newlist->next = next;
 233         next->prev = newlist;
 234         newlist->prev = head;
 235         head->next = newlist;
 236         newlist->nr = nr;
 237         memcpy(newlist->list, head->list + old, nr * sizeof(void *));
 238         memset(head->list + old, 0xf0, nr * sizeof(void *));
 239 }
 240 
 241 int rl_ptrlist_hack;
 242 ///
 243 // add an entry to a ptrlist
 244 // @listp: a pointer to the list
 245 // @ptr: the entry to add to the list
 246 // @return: the address where the new entry is stored.
 247 //
 248 // :note: code must not use this function and should use
 249 //      :func:`add_ptr_list` instead.
 250 void **__add_ptr_list(struct ptr_list **listp, void *ptr)
 251 {
 252         struct ptr_list *list = *listp;
 253         struct ptr_list *last = NULL; /* gcc complains needlessly */
 254         void **ret;
 255         int nr;
 256 
 257         if (!list || (nr = (last = list->prev)->nr) >= LIST_NODE_NR) {
 258                 struct ptr_list *newlist;
 259 
 260                 if (rl_ptrlist_hack)
 261                         newlist = __alloc_rl_ptrlist(0);
 262                 else
 263                         newlist = __alloc_ptrlist(0);
 264                 if (!list) {
 265                         newlist->next = newlist;
 266                         newlist->prev = newlist;
 267                         *listp = newlist;
 268                 } else {
 269                         newlist->prev = last;
 270                         newlist->next = list;
 271                         list->prev = newlist;
 272                         last->next = newlist;
 273                 }
 274                 last = newlist;
 275                 nr = 0;
 276         }
 277         ret = last->list + nr;
 278         *ret = ptr;
 279         nr++;
 280         last->nr = nr;
 281         return ret;
 282 }
 283 
 284 ///
 285 // add a tagged entry to a ptrlist
 286 // @listp: a pointer to the list
 287 // @ptr: the entry to add to the list
 288 // @tag: the tag to add to @ptr
 289 // @return: the address where the new entry is stored.
 290 //
 291 // :note: code must not use this function and should use
 292 //      :func:`add_ptr_list_tag` instead.
 293 void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag)
 294 {
 295         /* The low two bits are reserved for tags */
 296         assert((3 & (unsigned long)ptr) == 0);
 297         assert((~3 & tag) == 0);
 298 
 299         ptr = (void *)(tag | (unsigned long)ptr);
 300 
 301         return __add_ptr_list(listp, ptr);
 302 }
 303 
 304 ///
 305 // test if some entry is already present in a ptrlist
 306 // @list: the head of the list
 307 // @entry: the entry to test
 308 // @return: ``true`` if the entry is already present, ``false`` otherwise.
 309 bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry)
 310 {
 311         const struct ptr_list *list = head;
 312 
 313         if (!head)
 314                 return false;
 315         do {
 316                 int nr = list->nr;
 317                 int i;
 318                 for (i = 0; i < nr; i++)
 319                         if (list->list[i] == entry)
 320                                 return true;
 321         } while ((list = list->next) != head);
 322         return false;
 323 }
 324 
 325 ///
 326 // delete an entry from a ptrlist
 327 // @list: a pointer to the list
 328 // @entry: the item to be deleted
 329 // @count: the minimum number of times @entry should be deleted or 0.
 330 int delete_ptr_list_entry(struct ptr_list **list, void *entry, int count)
 331 {
 332         void *ptr;
 333 
 334         FOR_EACH_PTR(*list, ptr) {
 335                 if (ptr == entry) {
 336                         DELETE_CURRENT_PTR(ptr);
 337                         if (!--count)
 338                                 goto out;
 339                 }
 340         } END_FOR_EACH_PTR(ptr);
 341         assert(count <= 0);
 342 out:
 343         pack_ptr_list(list);
 344         return count;
 345 }
 346 
 347 ///
 348 // replace an entry in a ptrlist
 349 // @list: a pointer to the list
 350 // @old_ptr: the entry to be replaced
 351 // @new_ptr: the new entry
 352 // @count: the minimum number of times @entry should be deleted or 0.
 353 int replace_ptr_list_entry(struct ptr_list **list, void *old_ptr,
 354         void *new_ptr, int count)
 355 {
 356         void *ptr;
 357 
 358         FOR_EACH_PTR(*list, ptr) {
 359                 if (ptr==old_ptr) {
 360                         REPLACE_CURRENT_PTR(ptr, new_ptr);
 361                         if (!--count)
 362                                 goto out;
 363                 }
 364         }END_FOR_EACH_PTR(ptr);
 365         assert(count <= 0);
 366 out:
 367         return count;
 368 }
 369 
 370 ///
 371 // remove the last entry of a ptrlist
 372 // @head: a pointer to the list
 373 // @return: the last elemant of the list or NULL if the list is empty.
 374 //
 375 // :note: this doesn't repack the list
 376 void * undo_ptr_list_last(struct ptr_list **head)
 377 {
 378         struct ptr_list *last, *first = *head;
 379 
 380         if (!first)
 381                 return NULL;
 382         last = first;
 383         do {
 384                 last = last->prev;
 385                 if (last->nr) {
 386                         void *ptr;
 387                         int nr = --last->nr;
 388                         ptr = last->list[nr];
 389                         last->list[nr] = (void *)0xf1f1f1f1;
 390                         return ptr;
 391                 }
 392         } while (last != first);
 393         return NULL;
 394 }
 395 
 396 ///
 397 // remove the last entry and repack the list
 398 // @head: a pointer to the list
 399 // @return: the last elemant of the list or NULL if the list is empty.
 400 void * delete_ptr_list_last(struct ptr_list **head)
 401 {
 402         void *ptr = NULL;
 403         struct ptr_list *last, *first = *head;
 404 
 405         if (!first)
 406                 return NULL;
 407         last = first->prev;
 408         if (last->nr)
 409                 ptr = last->list[--last->nr];
 410         if (last->nr <=0) {
 411                 first->prev = last->prev;
 412                 last->prev->next = first;
 413                 if (last == first)
 414                         *head = NULL;
 415                 __free_ptrlist(last);
 416         }
 417         return ptr;
 418 }
 419 
 420 ///
 421 // concat two ptrlists
 422 // @a: the source list
 423 // @b: a pointer to the destination list.
 424 // The element of @a are added at the end of @b.
 425 void concat_ptr_list(struct ptr_list *a, struct ptr_list **b)
 426 {
 427         void *entry;
 428         FOR_EACH_PTR(a, entry) {
 429                 add_ptr_list(b, entry);
 430         } END_FOR_EACH_PTR(entry);
 431 }
 432 
 433 ///
 434 // copy the elements of a list at the end of another list.
 435 // @listp: a pointer to the destination list.
 436 // @src: the head of the source list.
 437 void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src)
 438 {
 439         struct ptr_list *head, *tail;
 440         struct ptr_list *cur = src;
 441         int idx;
 442 
 443         if (!src)
 444                 return;
 445         head = *listp;
 446         if (!head) {
 447                 *listp = src;
 448                 return;
 449         }
 450 
 451         tail = head->prev;
 452         idx = tail->nr;
 453         do {
 454                 struct ptr_list *next;
 455                 int nr = cur->nr;
 456                 int i;
 457                 for (i = 0; i < nr;) {
 458                         void *ptr = cur->list[i++];
 459                         if (!ptr)
 460                                 continue;
 461                         if (idx >= LIST_NODE_NR) {
 462                                 struct ptr_list *prev = tail;
 463                                 tail = __alloc_ptrlist(0);
 464                                 prev->next = tail;
 465                                 tail->prev = prev;
 466                                 prev->nr = idx;
 467                                 idx = 0;
 468                         }
 469                         tail->list[idx++] = ptr;
 470                 }
 471 
 472                 next = cur->next;
 473                 __free_ptrlist(cur);
 474                 cur = next;
 475         } while (cur != src);
 476 
 477         tail->nr = idx;
 478         head->prev = tail;
 479         tail->next = head;
 480 }
 481 
 482 ///
 483 // free a ptrlist
 484 // @listp: a pointer to the list
 485 // Each blocks of the list are freed (but the entries
 486 // themselves are not freed).
 487 //
 488 // :note: code must not use this function and should use
 489 //      the macro :func:`free_ptr_list` instead.
 490 void __free_ptr_list(struct ptr_list **listp)
 491 {
 492         struct ptr_list *tmp, *list = *listp;
 493 
 494         if (!list)
 495                 return;
 496 
 497         list->prev->next = NULL;
 498         while (list) {
 499                 tmp = list;
 500                 list = list->next;
 501                 __free_ptrlist(tmp);
 502         }
 503 
 504         *listp = NULL;
 505 }