1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 #include <sgs.h>
  27 #include <string.h>
  28 #include <stdio.h>
  29 #include <sys/debug.h>
  30 
  31 /*
  32  * Alist manipulation.  An Alist is a list of elements formed into an array.
  33  * Traversal of the list is an array scan, which because of the locality of
  34  * each reference is probably more efficient than a link-list traversal.
  35  *
  36  * See alist.h for more background information about array lists.
  37  */
  38 
  39 /*
  40  * Insert a value into an array at a specified index:
  41  *
  42  *      alist_insert(): Insert an item into an Alist at the specified index
  43  *      alist_insert_by_offset(): Insert an item into an Alist at the
  44  *              specified offset relative to the list address.
  45  *      aplist_insert() Insert a pointer into an APlist at the specified index
  46  *
  47  * entry:
  48  *      Note: All the arguments for all three routines are listed here.
  49  *      The routine to which a given argument applies is given with
  50  *      each description.
  51  *
  52  *      llp [all] - Address of a pointer to an Alist/APlist. The pointer should
  53  *              be initialized to NULL before its first use.
  54  *      datap [alist_insert / aplist_insert] - Pointer to item data, or
  55  *              NULL. If non-null the data referenced is copied into the
  56  *              Alist item. Otherwise, the list item is zeroed, and
  57  *              further initialization is left to the caller.
  58  *      ptr [aplist_insert] - Pointer to be inserted.
  59  *      size [alist_insert / alist_insert_by_offset] - Size of an item
  60  *              in the array list, in bytes. As with any array, A given
  61  *              Alist can support any item size, but every item in that
  62  *              list must have the same size.
  63  *      init_arritems [all] - Initial allocation size: On the first insertion
  64  *              into the array list, room for init_arritems items is allocated.
  65  *      idx [alist_insert / aplist_insert] - Index at which to insert the
  66  *              new item. This index must lie within the existing list,
  67  *              or be the next index following.
  68  *      off [alist_insert_by_offset] - Offset at which  to insert the new
  69  *              item, based from the start of the Alist. The offset of
  70  *              the first item is ALIST_OFF_DATA.
  71  *
  72  * exit:
  73  *      The item is inserted at the specified position. This operation
  74  *      can cause memory for the list to be allocated, or reallocated,
  75  *      either of which will cause the value of the list pointer
  76  *      to change.
  77  *
  78  *      These routines can only fail if unable to allocate memory,
  79  *      in which case NULL is returned.
  80  *
  81  *      If a pointer list (aplist_insert), then the pointer
  82  *      is stored in the requested index. On success, the address
  83  *      of the pointer within the list is returned.
  84  *
  85  *      If the list contains arbitrary data (not aplist_insert): If datap
  86  *      is non-NULL, the data it references is copied into the item at
  87  *      the index. If datap is NULL, the specified item is zeroed.
  88  *      On success, a pointer to the inserted item is returned.
  89  *
  90  *      The  caller must not retain the returned pointer from this
  91  *      routine across calls to the list module. It is only safe to use
  92  *      it until the next call to this module for the given list.
  93  *
  94  */
  95 void *
  96 alist_insert(Alist **lpp, const void *datap, size_t size,
  97     Aliste init_arritems, Aliste idx)
  98 {
  99         Alist   *lp = *lpp;
 100         char    *addr;
 101 
 102         /* The size and initial array count need to be non-zero */
 103         ASSERT(init_arritems != 0);
 104         ASSERT(size != 0);
 105 
 106         if (lp == NULL) {
 107                 Aliste bsize;
 108 
 109                 /*
 110                  * First time here, allocate a new Alist.  Note that the
 111                  * Alist al_desc[] entry is defined for 1 element,
 112                  * but we actually allocate the number we need.
 113                  */
 114                 bsize = size * init_arritems;
 115                 bsize = S_ROUND(bsize, sizeof (void *));
 116                 bsize = ALIST_OFF_DATA + bsize;
 117                 if ((lp = malloc((size_t)bsize)) == NULL)
 118                         return (NULL);
 119                 lp->al_arritems = init_arritems;
 120                 lp->al_nitems = 0;
 121                 lp->al_next = ALIST_OFF_DATA;
 122                 lp->al_size = size;
 123                 *lpp = lp;
 124         } else {
 125                 /* We must get the same value for size every time */
 126                 ASSERT(size == lp->al_size);
 127 
 128                 if (lp->al_nitems >= lp->al_arritems) {
 129                         /*
 130                          * The list is full: Increase the memory allocation
 131                          * by doubling it.
 132                          */
 133                         Aliste  bsize;
 134 
 135                         bsize = lp->al_size * lp->al_arritems * 2;
 136                         bsize = S_ROUND(bsize, sizeof (void *));
 137                         bsize = ALIST_OFF_DATA + bsize;
 138                         if ((lp = realloc((void *)lp, (size_t)bsize)) == 0)
 139                                 return (NULL);
 140                         lp->al_arritems *= 2;
 141                         *lpp = lp;
 142                 }
 143         }
 144 
 145         /*
 146          * The caller is not supposed to use an index that
 147          * would introduce a "hole" in the array.
 148          */
 149         ASSERT(idx <= lp->al_nitems);
 150 
 151         addr = (idx * lp->al_size) + (char *)lp->al_data;
 152 
 153         /*
 154          * An appended item is added to the next available array element.
 155          * An insert at any other spot requires that the data items that
 156          * exist at the point of insertion be shifted down to open a slot.
 157          */
 158         if (idx < lp->al_nitems)
 159                 (void) memmove(addr + lp->al_size, addr,
 160                     (lp->al_nitems - idx) * lp->al_size);
 161 
 162         lp->al_nitems++;
 163         lp->al_next += lp->al_size;
 164         if (datap != NULL)
 165                 (void) memcpy(addr, datap, lp->al_size);
 166         else
 167                 (void) memset(addr, 0, lp->al_size);
 168         return (addr);
 169 }
 170 
 171 void *
 172 alist_insert_by_offset(Alist **lpp, const void *datap, size_t size,
 173     Aliste init_arritems, Aliste off)
 174 {
 175         Aliste idx;
 176 
 177         if (*lpp == NULL) {
 178                 ASSERT(off == ALIST_OFF_DATA);
 179                 idx = 0;
 180         } else {
 181                 idx = (off - ALIST_OFF_DATA) / (*lpp)->al_size;
 182         }
 183 
 184         return (alist_insert(lpp, datap, size, init_arritems, idx));
 185 }
 186 
 187 void *
 188 aplist_insert(APlist **lpp, const void *ptr, Aliste init_arritems, Aliste idx)
 189 {
 190         APlist  *lp = *lpp;
 191 
 192         /* The initial array count needs to be non-zero */
 193         ASSERT(init_arritems != 0);
 194 
 195         if (lp == NULL) {
 196                 Aliste bsize;
 197 
 198                 /*
 199                  * First time here, allocate a new APlist.  Note that the
 200                  * APlist apl_desc[] entry is defined for 1 element,
 201                  * but we actually allocate the number we need.
 202                  */
 203                 bsize = APLIST_OFF_DATA + (sizeof (void *) * init_arritems);
 204                 if ((lp = malloc((size_t)bsize)) == NULL)
 205                         return (NULL);
 206                 lp->apl_arritems = init_arritems;
 207                 lp->apl_nitems = 0;
 208                 *lpp = lp;
 209         } else if (lp->apl_nitems >= lp->apl_arritems) {
 210                 /*
 211                  * The list is full: Increase the memory allocation
 212                  * by doubling it.
 213                  */
 214                 Aliste  bsize;
 215 
 216                 bsize = APLIST_OFF_DATA +
 217                     (2 * sizeof (void *) * lp->apl_arritems);
 218                 if ((lp = realloc((void *)lp, (size_t)bsize)) == 0)
 219                         return (NULL);
 220                 lp->apl_arritems *= 2;
 221                 *lpp = lp;
 222         }
 223 
 224         /*
 225          * The caller is not supposed to use an index that
 226          * would introduce a "hole" in the array.
 227          */
 228         ASSERT(idx <= lp->apl_nitems);
 229 
 230         /*
 231          * An appended item is added to the next available array element.
 232          * An insert at any other spot requires that the data items that
 233          * exist at the point of insertion be shifted down to open a slot.
 234          */
 235         if (idx < lp->apl_nitems)
 236                 (void) memmove((char *)&lp->apl_data[idx + 1],
 237                     (char *)&lp->apl_data[idx],
 238                     (lp->apl_nitems - idx) * sizeof (void *));
 239 
 240         lp->apl_nitems++;
 241         lp->apl_data[idx] = (void *)ptr;
 242         return (&lp->apl_data[idx]);
 243 }
 244 
 245 /*
 246  * Append a value to a list. These are convenience wrappers on top
 247  * of the insert operation. See the description of those routine above
 248  * for details.
 249  */
 250 void *
 251 alist_append(Alist **lpp, const void *datap, size_t size,
 252     Aliste init_arritems)
 253 {
 254         Aliste ndx = ((*lpp) == NULL) ? 0 : (*lpp)->al_nitems;
 255 
 256         return (alist_insert(lpp, datap, size, init_arritems, ndx));
 257 }
 258 
 259 void *
 260 aplist_append(APlist **lpp, const void *ptr, Aliste init_arritems)
 261 {
 262         Aliste ndx = ((*lpp) == NULL) ? 0 : (*lpp)->apl_nitems;
 263 
 264         return (aplist_insert(lpp, ptr, init_arritems, ndx));
 265 }
 266 
 267 /*
 268  * Delete the item at a specified index/offset, and decrement the variable
 269  * containing the index:
 270  *
 271  *      alist_delete - Delete an item from an Alist at the specified
 272  *              index.
 273  *      alist_delete_by_offset - Delete an item from an Alist at the
 274  *              specified offset from the list pointer.
 275  *      aplist_delete - Delete a pointer from an APlist at the specified
 276  *              index.
 277  *
 278  * entry:
 279  *      alp - List to delete item from
 280  *      idxp - Address of variable containing the index of the
 281  *              item to delete.
 282  *      offp - Address of variable containing the offset of the
 283  *              item to delete.
 284  *
 285  * exit:
 286  *      The item at the position given by (*idxp) or (*offp), depending
 287  *      on the routine, is removed from the list. Then, the position
 288  *      variable (*idxp or *offp) is decremented by one item. This is done
 289  *      to facilitate use of this routine within a TRAVERSE loop.
 290  *
 291  * note:
 292  *      Deleting the last element in an array list is cheap, but
 293  *      deleting any other item causes a memory copy to occur to
 294  *      move the following items up. If you intend to traverse the
 295  *      entire list, deleting every item as you go, it will be cheaper
 296  *      to omit the delete within the traverse, and then call
 297  *      the reset function reset() afterwards.
 298  */
 299 void
 300 alist_delete(Alist *lp, Aliste *idxp)
 301 {
 302         Aliste  idx = *idxp;
 303 
 304 
 305         /* The list must be allocated and the index in range */
 306         ASSERT(lp != NULL);
 307         ASSERT(idx < lp->al_nitems);
 308 
 309         /*
 310          * If the element to be removed is not the last entry of the array,
 311          * slide the following elements over the present element.
 312          */
 313         if (idx < --lp->al_nitems) {
 314                 char *addr = (idx * lp->al_size) + (char *)lp->al_data;
 315 
 316                 (void) memmove(addr, addr + lp->al_size,
 317                     (lp->al_nitems - idx) * lp->al_size);
 318         }
 319         lp->al_next -= lp->al_size;
 320 
 321         /* Decrement the callers index variable */
 322         (*idxp)--;
 323 }
 324 
 325 void
 326 alist_delete_by_offset(Alist *lp, Aliste *offp)
 327 {
 328         Aliste idx;
 329 
 330         ASSERT(lp != NULL);
 331         idx = (*offp - ALIST_OFF_DATA) / lp->al_size;
 332 
 333         alist_delete(lp, &idx);
 334         *offp -= lp->al_size;
 335 }
 336 
 337 void
 338 aplist_delete(APlist *lp, Aliste *idxp)
 339 {
 340         Aliste  idx = *idxp;
 341 
 342 
 343         /* The list must be allocated and the index in range */
 344         ASSERT(lp != NULL);
 345         ASSERT(idx < lp->apl_nitems);
 346 
 347         /*
 348          * If the element to be removed is not the last entry of the array,
 349          * slide the following elements over the present element.
 350          */
 351         if (idx < --lp->apl_nitems)
 352                 (void) memmove(&lp->apl_data[idx], &lp->apl_data[idx + 1],
 353                     (lp->apl_nitems - idx) * sizeof (void *));
 354 
 355         /* Decrement the callers index variable */
 356         (*idxp)--;
 357 }
 358 
 359 /*
 360  * Delete the pointer with a specified value from the APlist.
 361  *
 362  * entry:
 363  *      lp - Initialized APlist to delete item from
 364  *      ptr - Pointer to be deleted.
 365  *
 366  * exit:
 367  *      The list is searched for an item containing the given pointer,
 368  *      and if a match is found, that item is delted and True (1) returned.
 369  *      If no match is found, then False (0) is returned.
 370  *
 371  * note:
 372  *      See note for delete operation, above.
 373  */
 374 int
 375 aplist_delete_value(APlist *lp, const void *ptr)
 376 {
 377         size_t  idx;
 378 
 379         /*
 380          * If the pointer is found in the list, use aplist_delete to
 381          * remove it, and we're done.
 382          */
 383         for (idx = 0; idx < lp->apl_nitems; idx++)
 384                 if (ptr == lp->apl_data[idx]) {
 385                         aplist_delete(lp, &idx);
 386                         return (1);
 387                 }
 388 
 389         /* If we get here, the item was not in the list */
 390         return (0);
 391 }
 392 
 393 /*
 394  * Search the APlist for an element with a given value, and
 395  * if not found, optionally append the element to the end of the list.
 396  *
 397  * entry:
 398  *      lpp, ptr - As per aplist_insert().
 399  *      init_arritems - As per aplist_insert() if a non-zero value.
 400  *              A value of zero is special, and is taken to indicate
 401  *              that no insert operation should be performed if
 402  *              the item is not found in the list.
 403  *
 404  * exit
 405  *      The given item is compared to every item in the given APlist.
 406  *      If it is found, ALE_EXISTS is returned.
 407  *
 408  *      If it is not found: If init_arr_items is False (0), then
 409  *      ALE_NOTFOUND is returned. If init_arr_items is True, then
 410  *      the item is appended to the list, and ALE_CREATE returned on success.
 411  *
 412  *      On failure, which can only occur due to memory allocation failure,
 413  *      ALE_ALLOCFAIL is returned.
 414  *
 415  * note:
 416  *      The test operation used by this routine is a linear
 417  *      O(N) operation, and is not efficient for more than a
 418  *      few items.
 419  */
 420 aplist_test_t
 421 aplist_test(APlist **lpp, const void *ptr, Aliste init_arritems)
 422 {
 423         APlist  *lp = *lpp;
 424         size_t  idx;
 425 
 426         /* Is the pointer already in the list? */
 427         if (lp != NULL)
 428                 for (idx = 0; idx < lp->apl_nitems; idx++)
 429                         if (ptr == lp->apl_data[idx])
 430                                 return (ALE_EXISTS);
 431 
 432         /* Is this a no-insert case? If so, report that the item is not found */
 433         if (init_arritems == 0)
 434                 return (ALE_NOTFND);
 435 
 436         /* Add it to the end of the list */
 437         if (aplist_append(lpp, ptr, init_arritems) == NULL)
 438                 return (ALE_ALLOCFAIL);
 439         return (ALE_CREATE);
 440 }
 441 
 442 /*
 443  * Reset the given list to its empty state. Any memory allocated by the
 444  * list is preserved, ready for reuse, but the list is set to its
 445  * empty state, equivalent to having called the delete operation for
 446  * every item.
 447  *
 448  * Note that no cleanup of the discarded items is done. The caller must
 449  * take care of any necessary cleanup before calling aplist_reset().
 450  */
 451 void
 452 alist_reset(Alist *lp)
 453 {
 454         if (lp != NULL) {
 455                 lp->al_nitems = 0;
 456                 lp->al_next = ALIST_OFF_DATA;
 457         }
 458 }
 459 
 460 void
 461 aplist_reset(APlist *lp)
 462 {
 463         if (lp != NULL)
 464                 lp->apl_nitems = 0;
 465 }