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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
29 #include <sys/debug.h>
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.
36 * See alist.h for more background information about array lists.
40 * Insert a value into an array at a specified index:
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
48 * Note: All the arguments for all three routines are listed here.
49 * The routine to which a given argument applies is given with
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.
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
78 * These routines can only fail if unable to allocate memory,
79 * in which case NULL is returned.
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.
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.
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.
96 alist_insert(Alist
**lpp
, const void *datap
, size_t size
,
97 Aliste init_arritems
, Aliste idx
)
102 /* The size and initial array count need to be non-zero */
103 ASSERT(init_arritems
!= 0);
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.
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
)
119 lp
->al_arritems
= init_arritems
;
121 lp
->al_next
= ALIST_OFF_DATA
;
125 /* We must get the same value for size every time */
126 ASSERT(size
== lp
->al_size
);
128 if (lp
->al_nitems
>= lp
->al_arritems
) {
130 * The list is full: Increase the memory allocation
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)
140 lp
->al_arritems
*= 2;
146 * The caller is not supposed to use an index that
147 * would introduce a "hole" in the array.
149 ASSERT(idx
<= lp
->al_nitems
);
151 addr
= (idx
* lp
->al_size
) + (char *)lp
->al_data
;
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.
158 if (idx
< lp
->al_nitems
)
159 (void) memmove(addr
+ lp
->al_size
, addr
,
160 (lp
->al_nitems
- idx
) * lp
->al_size
);
163 lp
->al_next
+= lp
->al_size
;
165 (void) memcpy(addr
, datap
, lp
->al_size
);
167 (void) memset(addr
, 0, lp
->al_size
);
172 alist_insert_by_offset(Alist
**lpp
, const void *datap
, size_t size
,
173 Aliste init_arritems
, Aliste off
)
178 ASSERT(off
== ALIST_OFF_DATA
);
181 idx
= (off
- ALIST_OFF_DATA
) / (*lpp
)->al_size
;
184 return (alist_insert(lpp
, datap
, size
, init_arritems
, idx
));
188 aplist_insert(APlist
**lpp
, const void *ptr
, Aliste init_arritems
, Aliste idx
)
192 /* The initial array count needs to be non-zero */
193 ASSERT(init_arritems
!= 0);
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.
203 bsize
= APLIST_OFF_DATA
+ (sizeof (void *) * init_arritems
);
204 if ((lp
= malloc((size_t)bsize
)) == NULL
)
206 lp
->apl_arritems
= init_arritems
;
209 } else if (lp
->apl_nitems
>= lp
->apl_arritems
) {
211 * The list is full: Increase the memory allocation
216 bsize
= APLIST_OFF_DATA
+
217 (2 * sizeof (void *) * lp
->apl_arritems
);
218 if ((lp
= realloc((void *)lp
, (size_t)bsize
)) == 0)
220 lp
->apl_arritems
*= 2;
225 * The caller is not supposed to use an index that
226 * would introduce a "hole" in the array.
228 ASSERT(idx
<= lp
->apl_nitems
);
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.
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 *));
241 lp
->apl_data
[idx
] = (void *)ptr
;
242 return (&lp
->apl_data
[idx
]);
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
251 alist_append(Alist
**lpp
, const void *datap
, size_t size
,
252 Aliste init_arritems
)
254 Aliste ndx
= ((*lpp
) == NULL
) ? 0 : (*lpp
)->al_nitems
;
256 return (alist_insert(lpp
, datap
, size
, init_arritems
, ndx
));
260 aplist_append(APlist
**lpp
, const void *ptr
, Aliste init_arritems
)
262 Aliste ndx
= ((*lpp
) == NULL
) ? 0 : (*lpp
)->apl_nitems
;
264 return (aplist_insert(lpp
, ptr
, init_arritems
, ndx
));
268 * Delete the item at a specified index/offset, and decrement the variable
269 * containing the index:
271 * alist_delete - Delete an item from an Alist at the specified
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
279 * alp - List to delete item from
280 * idxp - Address of variable containing the index of the
282 * offp - Address of variable containing the offset of the
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.
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.
300 alist_delete(Alist
*lp
, Aliste
*idxp
)
305 /* The list must be allocated and the index in range */
307 ASSERT(idx
< lp
->al_nitems
);
310 * If the element to be removed is not the last entry of the array,
311 * slide the following elements over the present element.
313 if (idx
< --lp
->al_nitems
) {
314 char *addr
= (idx
* lp
->al_size
) + (char *)lp
->al_data
;
316 (void) memmove(addr
, addr
+ lp
->al_size
,
317 (lp
->al_nitems
- idx
) * lp
->al_size
);
319 lp
->al_next
-= lp
->al_size
;
321 /* Decrement the callers index variable */
326 alist_delete_by_offset(Alist
*lp
, Aliste
*offp
)
331 idx
= (*offp
- ALIST_OFF_DATA
) / lp
->al_size
;
333 alist_delete(lp
, &idx
);
334 *offp
-= lp
->al_size
;
338 aplist_delete(APlist
*lp
, Aliste
*idxp
)
343 /* The list must be allocated and the index in range */
345 ASSERT(idx
< lp
->apl_nitems
);
348 * If the element to be removed is not the last entry of the array,
349 * slide the following elements over the present element.
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 *));
355 /* Decrement the callers index variable */
360 * Delete the pointer with a specified value from the APlist.
363 * lp - Initialized APlist to delete item from
364 * ptr - Pointer to be deleted.
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.
372 * See note for delete operation, above.
375 aplist_delete_value(APlist
*lp
, const void *ptr
)
380 * If the pointer is found in the list, use aplist_delete to
381 * remove it, and we're done.
383 for (idx
= 0; idx
< lp
->apl_nitems
; idx
++)
384 if (ptr
== lp
->apl_data
[idx
]) {
385 aplist_delete(lp
, &idx
);
389 /* If we get here, the item was not in the list */
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.
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.
405 * The given item is compared to every item in the given APlist.
406 * If it is found, ALE_EXISTS is returned.
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.
412 * On failure, which can only occur due to memory allocation failure,
413 * ALE_ALLOCFAIL is returned.
416 * The test operation used by this routine is a linear
417 * O(N) operation, and is not efficient for more than a
421 aplist_test(APlist
**lpp
, const void *ptr
, Aliste init_arritems
)
426 /* Is the pointer already in the list? */
428 for (idx
= 0; idx
< lp
->apl_nitems
; idx
++)
429 if (ptr
== lp
->apl_data
[idx
])
432 /* Is this a no-insert case? If so, report that the item is not found */
433 if (init_arritems
== 0)
436 /* Add it to the end of the list */
437 if (aplist_append(lpp
, ptr
, init_arritems
) == NULL
)
438 return (ALE_ALLOCFAIL
);
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
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().
452 alist_reset(Alist
*lp
)
456 lp
->al_next
= ALIST_OFF_DATA
;
461 aplist_reset(APlist
*lp
)