/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include /* * Alist manipulation. An Alist is a list of elements formed into an array. * Traversal of the list is an array scan, which because of the locality of * each reference is probably more efficient than a link-list traversal. * * See alist.h for more background information about array lists. */ /* * Insert a value into an array at a specified index: * * alist_insert(): Insert an item into an Alist at the specified index * alist_insert_by_offset(): Insert an item into an Alist at the * specified offset relative to the list address. * aplist_insert() Insert a pointer into an APlist at the specified index * * entry: * Note: All the arguments for all three routines are listed here. * The routine to which a given argument applies is given with * each description. * * llp [all] - Address of a pointer to an Alist/APlist. The pointer should * be initialized to NULL before its first use. * datap [alist_insert / aplist_insert] - Pointer to item data, or * NULL. If non-null the data referenced is copied into the * Alist item. Otherwise, the list item is zeroed, and * further initialization is left to the caller. * ptr [aplist_insert] - Pointer to be inserted. * size [alist_insert / alist_insert_by_offset] - Size of an item * in the array list, in bytes. As with any array, A given * Alist can support any item size, but every item in that * list must have the same size. * init_arritems [all] - Initial allocation size: On the first insertion * into the array list, room for init_arritems items is allocated. * idx [alist_insert / aplist_insert] - Index at which to insert the * new item. This index must lie within the existing list, * or be the next index following. * off [alist_insert_by_offset] - Offset at which to insert the new * item, based from the start of the Alist. The offset of * the first item is ALIST_OFF_DATA. * * exit: * The item is inserted at the specified position. This operation * can cause memory for the list to be allocated, or reallocated, * either of which will cause the value of the list pointer * to change. * * These routines can only fail if unable to allocate memory, * in which case NULL is returned. * * If a pointer list (aplist_insert), then the pointer * is stored in the requested index. On success, the address * of the pointer within the list is returned. * * If the list contains arbitrary data (not aplist_insert): If datap * is non-NULL, the data it references is copied into the item at * the index. If datap is NULL, the specified item is zeroed. * On success, a pointer to the inserted item is returned. * * The caller must not retain the returned pointer from this * routine across calls to the list module. It is only safe to use * it until the next call to this module for the given list. * */ void * alist_insert(Alist **lpp, const void *datap, size_t size, Aliste init_arritems, Aliste idx) { Alist *lp = *lpp; char *addr; /* The size and initial array count need to be non-zero */ ASSERT(init_arritems != 0); ASSERT(size != 0); if (lp == NULL) { Aliste bsize; /* * First time here, allocate a new Alist. Note that the * Alist al_desc[] entry is defined for 1 element, * but we actually allocate the number we need. */ bsize = size * init_arritems; bsize = S_ROUND(bsize, sizeof (void *)); bsize = ALIST_OFF_DATA + bsize; if ((lp = malloc((size_t)bsize)) == NULL) return (NULL); lp->al_arritems = init_arritems; lp->al_nitems = 0; lp->al_next = ALIST_OFF_DATA; lp->al_size = size; *lpp = lp; } else { /* We must get the same value for size every time */ ASSERT(size == lp->al_size); if (lp->al_nitems >= lp->al_arritems) { /* * The list is full: Increase the memory allocation * by doubling it. */ Aliste bsize; bsize = lp->al_size * lp->al_arritems * 2; bsize = S_ROUND(bsize, sizeof (void *)); bsize = ALIST_OFF_DATA + bsize; if ((lp = realloc(lp, (size_t)bsize)) == NULL) return (NULL); lp->al_arritems *= 2; *lpp = lp; } } /* * The caller is not supposed to use an index that * would introduce a "hole" in the array. */ ASSERT(idx <= lp->al_nitems); addr = (idx * lp->al_size) + (char *)lp->al_data; /* * An appended item is added to the next available array element. * An insert at any other spot requires that the data items that * exist at the point of insertion be shifted down to open a slot. */ if (idx < lp->al_nitems) (void) memmove(addr + lp->al_size, addr, (lp->al_nitems - idx) * lp->al_size); lp->al_nitems++; lp->al_next += lp->al_size; if (datap != NULL) (void) memcpy(addr, datap, lp->al_size); else (void) memset(addr, 0, lp->al_size); return (addr); } void * alist_insert_by_offset(Alist **lpp, const void *datap, size_t size, Aliste init_arritems, Aliste off) { Aliste idx; if (*lpp == NULL) { ASSERT(off == ALIST_OFF_DATA); idx = 0; } else { idx = (off - ALIST_OFF_DATA) / (*lpp)->al_size; } return (alist_insert(lpp, datap, size, init_arritems, idx)); } void * aplist_insert(APlist **lpp, const void *ptr, Aliste init_arritems, Aliste idx) { APlist *lp = *lpp; /* The initial array count needs to be non-zero */ ASSERT(init_arritems != 0); if (lp == NULL) { Aliste bsize; /* * First time here, allocate a new APlist. Note that the * APlist apl_desc[] entry is defined for 1 element, * but we actually allocate the number we need. */ bsize = APLIST_OFF_DATA + (sizeof (void *) * init_arritems); if ((lp = malloc((size_t)bsize)) == NULL) return (NULL); lp->apl_arritems = init_arritems; lp->apl_nitems = 0; *lpp = lp; } else if (lp->apl_nitems >= lp->apl_arritems) { /* * The list is full: Increase the memory allocation * by doubling it. */ Aliste bsize; bsize = APLIST_OFF_DATA + (2 * sizeof (void *) * lp->apl_arritems); if ((lp = realloc(lp, (size_t)bsize)) == NULL) return (NULL); lp->apl_arritems *= 2; *lpp = lp; } /* * The caller is not supposed to use an index that * would introduce a "hole" in the array. */ ASSERT(idx <= lp->apl_nitems); /* * An appended item is added to the next available array element. * An insert at any other spot requires that the data items that * exist at the point of insertion be shifted down to open a slot. */ if (idx < lp->apl_nitems) (void) memmove((char *)&lp->apl_data[idx + 1], (char *)&lp->apl_data[idx], (lp->apl_nitems - idx) * sizeof (void *)); lp->apl_nitems++; lp->apl_data[idx] = (void *)ptr; return (&lp->apl_data[idx]); } /* * Append a value to a list. These are convenience wrappers on top * of the insert operation. See the description of those routine above * for details. */ void * alist_append(Alist **lpp, const void *datap, size_t size, Aliste init_arritems) { Aliste ndx = ((*lpp) == NULL) ? 0 : (*lpp)->al_nitems; return (alist_insert(lpp, datap, size, init_arritems, ndx)); } void * aplist_append(APlist **lpp, const void *ptr, Aliste init_arritems) { Aliste ndx = ((*lpp) == NULL) ? 0 : (*lpp)->apl_nitems; return (aplist_insert(lpp, ptr, init_arritems, ndx)); } /* * Delete the item at a specified index/offset, and decrement the variable * containing the index: * * alist_delete - Delete an item from an Alist at the specified * index. * alist_delete_by_offset - Delete an item from an Alist at the * specified offset from the list pointer. * aplist_delete - Delete a pointer from an APlist at the specified * index. * * entry: * alp - List to delete item from * idxp - Address of variable containing the index of the * item to delete. * offp - Address of variable containing the offset of the * item to delete. * * exit: * The item at the position given by (*idxp) or (*offp), depending * on the routine, is removed from the list. Then, the position * variable (*idxp or *offp) is decremented by one item. This is done * to facilitate use of this routine within a TRAVERSE loop. * * note: * Deleting the last element in an array list is cheap, but * deleting any other item causes a memory copy to occur to * move the following items up. If you intend to traverse the * entire list, deleting every item as you go, it will be cheaper * to omit the delete within the traverse, and then call * the reset function reset() afterwards. */ void alist_delete(Alist *lp, Aliste *idxp) { Aliste idx = *idxp; /* The list must be allocated and the index in range */ ASSERT(lp != NULL); ASSERT(idx < lp->al_nitems); /* * If the element to be removed is not the last entry of the array, * slide the following elements over the present element. */ if (idx < --lp->al_nitems) { char *addr = (idx * lp->al_size) + (char *)lp->al_data; (void) memmove(addr, addr + lp->al_size, (lp->al_nitems - idx) * lp->al_size); } lp->al_next -= lp->al_size; /* Decrement the callers index variable */ (*idxp)--; } void alist_delete_by_offset(Alist *lp, Aliste *offp) { Aliste idx; ASSERT(lp != NULL); idx = (*offp - ALIST_OFF_DATA) / lp->al_size; alist_delete(lp, &idx); *offp -= lp->al_size; } void aplist_delete(APlist *lp, Aliste *idxp) { Aliste idx = *idxp; /* The list must be allocated and the index in range */ ASSERT(lp != NULL); ASSERT(idx < lp->apl_nitems); /* * If the element to be removed is not the last entry of the array, * slide the following elements over the present element. */ if (idx < --lp->apl_nitems) (void) memmove(&lp->apl_data[idx], &lp->apl_data[idx + 1], (lp->apl_nitems - idx) * sizeof (void *)); /* Decrement the callers index variable */ (*idxp)--; } /* * Delete the pointer with a specified value from the APlist. * * entry: * lp - Initialized APlist to delete item from * ptr - Pointer to be deleted. * * exit: * The list is searched for an item containing the given pointer, * and if a match is found, that item is delted and True (1) returned. * If no match is found, then False (0) is returned. * * note: * See note for delete operation, above. */ int aplist_delete_value(APlist *lp, const void *ptr) { size_t idx; /* * If the pointer is found in the list, use aplist_delete to * remove it, and we're done. */ for (idx = 0; idx < lp->apl_nitems; idx++) if (ptr == lp->apl_data[idx]) { aplist_delete(lp, &idx); return (1); } /* If we get here, the item was not in the list */ return (0); } /* * Search the APlist for an element with a given value, and * if not found, optionally append the element to the end of the list. * * entry: * lpp, ptr - As per aplist_insert(). * init_arritems - As per aplist_insert() if a non-zero value. * A value of zero is special, and is taken to indicate * that no insert operation should be performed if * the item is not found in the list. * * exit * The given item is compared to every item in the given APlist. * If it is found, ALE_EXISTS is returned. * * If it is not found: If init_arr_items is False (0), then * ALE_NOTFOUND is returned. If init_arr_items is True, then * the item is appended to the list, and ALE_CREATE returned on success. * * On failure, which can only occur due to memory allocation failure, * ALE_ALLOCFAIL is returned. * * note: * The test operation used by this routine is a linear * O(N) operation, and is not efficient for more than a * few items. */ aplist_test_t aplist_test(APlist **lpp, const void *ptr, Aliste init_arritems) { APlist *lp = *lpp; size_t idx; /* Is the pointer already in the list? */ if (lp != NULL) for (idx = 0; idx < lp->apl_nitems; idx++) if (ptr == lp->apl_data[idx]) return (ALE_EXISTS); /* Is this a no-insert case? If so, report that the item is not found */ if (init_arritems == 0) return (ALE_NOTFND); /* Add it to the end of the list */ if (aplist_append(lpp, ptr, init_arritems) == NULL) return (ALE_ALLOCFAIL); return (ALE_CREATE); } /* * Reset the given list to its empty state. Any memory allocated by the * list is preserved, ready for reuse, but the list is set to its * empty state, equivalent to having called the delete operation for * every item. * * Note that no cleanup of the discarded items is done. The caller must * take care of any necessary cleanup before calling aplist_reset(). */ void alist_reset(Alist *lp) { if (lp != NULL) { lp->al_nitems = 0; lp->al_next = ALIST_OFF_DATA; } } void aplist_reset(APlist *lp) { if (lp != NULL) lp->apl_nitems = 0; }