Home | History | Annotate | Download | only in common
      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 }
    466