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.
26 * Define an Alist, a list maintained as a reallocable array, and a for() loop
27 * macro to generalize its traversal. Note that the array can be reallocated
28 * as it is being traversed, thus the offset of each element is recomputed from
29 * the start of the structure.
39 #include <sys/types.h>
40 #include <sys/machelf.h>
43 * An Alist implements array lists. The functionality is similar to
44 * that of a linked list. However, an Alist is represented by a single
45 * contigious allocation of memory. The head of the memory is a header
46 * that contains control information for the list. Following the header
47 * is an array used to hold the user data. In the type definitions that
48 * follow, we define these as an array with a single element, but when
49 * we allocate the memory, we actually allocate the amount of memory needed.
51 * There are two "flavors" of array list:
53 * Alist - Contain arbitrary data, usually structs.
54 * APlist - Contain pointers to data allocated elsewhere.
56 * This differentiation is useful, because pointer lists are heavily
57 * used, and support a slightly different set of operations that are
58 * unique to their purpose.
60 * Array lists are initially represented by a NULL pointer. The memory
61 * for the list is only allocated if an item is inserted. This is very
62 * efficient for data structures that may or may not be needed for a
63 * given linker operation --- you only pay for what you use. In addition:
65 * - Array lists grow as needed (memory is reallocated as necessary)
66 * - Data is kept contiguously (no unused holes in between elements)
67 * at the beginning of the data area. This locality has
68 * good cache behavior, as access to adjacent items are
69 * highly likely to be in the same page of memory.
70 * - Insert/Delete operations at the end of the list are very
71 * efficient. However, insert/delete operations elsewhere
72 * will cause a relatively expensive overlapped memory
73 * copy of the data following the insert/delete location.
74 * - As with any generic memory alloctor (i.e. malloc()/free()),
75 * array lists are not type safe for the data they contain.
76 * Data is managed as (void *) pointers to data of a given
77 * length, so the Alist module cannot prevent the caller from
78 * inserting/extracting the wrong type of data. The caller
79 * must guard against this.
80 * - To free an array list, simply call the standard free() function
81 * on the list pointer.
87 * Aliste is used to represent list indexes, offsets, and sizes.
89 typedef size_t Aliste
;
94 * Alist is used to hold non-pointer items --- usually structs:
95 * - There must be an even number of Aliste fields before the
96 * al_data field. This ensures that al_data will have
97 * an alignment of 8, no matter whether sizeof(Aliste)
98 * is 4 or 8. That means that al_data will have sufficient
99 * alignment for any use, just like memory allocated via
101 * - al_nitems and al_next are redundant, in that they are
103 * al_next = al_nitems * al_size
104 * We do this to make ALIST_TRAVERSE_BYOFFSET maximally
105 * efficient. This doesn't waste space, because of the
106 * requirement to have an even # of Alist fields (above).
108 * Note that Alists allow the data to be referenced by 0 based array
109 * index, or by their byte offset from the start of the Alist memory
110 * allocation. The index form is preferred for most use, as it is simpler.
111 * However, by-offset access is used by rtld link maps, and this ability
112 * is convenient in that case.
115 Aliste al_arritems
; /* # of items in al_data allocation */
116 Aliste al_nitems
; /* # items (index of next avail item) */
117 Aliste al_next
; /* offset of next available al_data[] */
118 Aliste al_size
; /* size of each al_data[] item */
119 void *al_data
[1]; /* data (can grow) */
123 * APlist is a variant of Alist that contains pointers. There are several
124 * benefits to this special type:
126 * - Pointers are used directly, instead of requiring a
127 * pointer-to-pointer double indirection.
128 * - The implementation is slightly more efficient.
129 * - Operations that make particular sense for pointers
130 * can be supported without confusing the API for the
134 Aliste apl_arritems
; /* # of items in apl_data allocation */
135 Aliste apl_nitems
; /* # items (index of next avail item) */
136 void *apl_data
[1]; /* data area: (arrcnt * size) bytes */
139 #ifdef _SYSCALL32 /* required by librtld_db */
141 Elf32_Word apl_arritems
;
142 Elf32_Word apl_nitems
;
143 Elf32_Addr apl_data
[1];
145 #endif /* _SYSCALL32 */
148 * The ALIST_OFF_DATA and APLIST_OFF_DATA macros give the byte offset
149 * from the start of an array list to the first byte of the data area
150 * used to hold user data. The same trick used by the standard offsetof()
153 #define ALIST_OFF_DATA ((size_t)(((Alist *)0)->al_data))
154 #define APLIST_OFF_DATA ((size_t)(((APlist *)0)->apl_data))
158 * The TRAVERSE macros are intended to be used within a for(), and
159 * cause the resulting loop to iterate over each item in the loop,
161 * ALIST_TRAVERSE: Traverse over the items in an Alist,
162 * using the zero based item array index to refer to
164 * ALIST_TRAVERSE_BY_OFFSET: Traverse over the items in an
165 * Alist using the byte offset from the head of the
166 * Alist pointer to refer to each item. It should be noted
167 * that the first such offset is given by ALIST_OFF_DATA,
168 * and as such, there will never be a 0 offset. Some code
169 * uses this fact to treat 0 as a reserved value with
172 * By-offset access is convenient for some parts of
173 * rtld, where a value of 0 is used to indicate an
174 * uninitialized link map control.
176 * APLIST_TRAVERSE: Traverse over the pointers in an APlist, using
177 * the zero based item array index to refer to each pointer.
183 * LIST - Pointer to Alist structure for list
184 * IDX - The current item index
185 * OFF - The current item offset
186 * DATA - Pointer to item
188 #define ALIST_TRAVERSE(LIST, IDX, DATA) \
190 ((LIST) != NULL) && ((DATA) = (void *)(LIST)->al_data); \
192 ((LIST) != NULL) && ((IDX) < (LIST)->al_nitems); \
195 (DATA) = (void *) (((LIST)->al_size * (IDX)) + (char *)(LIST)->al_data)
197 #define ALIST_TRAVERSE_BY_OFFSET(LIST, OFF, DATA) \
198 (((LIST) != NULL) && ((OFF) = ALIST_OFF_DATA) && \
199 (((DATA) = (void *)((char *)(LIST) + (OFF))))); \
201 (((LIST) != NULL) && ((OFF) < (LIST)->al_next)); \
203 (((OFF) += ((LIST)->al_size)), \
204 ((DATA) = (void *)((char *)(LIST) + (OFF))))
209 * LIST - Pointer to APlist structure for list
210 * IDX - The current item index
213 * Note that this macro is designed to ensure that PTR retains the
214 * value of the final pointer in the list after exiting the for loop,
215 * and to avoid dereferencing an out of range address. This is done by
216 * doing the dereference in the middle expression, using the comma
217 * operator to ensure that a NULL pointer won't stop the loop.
219 #define APLIST_TRAVERSE(LIST, IDX, PTR) \
222 ((LIST) != NULL) && ((IDX) < (LIST)->apl_nitems) && \
223 (((PTR) = ((LIST)->apl_data)[IDX]), 1); \
229 * Possible values returned by aplist_test()
232 ALE_ALLOCFAIL
= 0, /* memory allocation error */
233 ALE_EXISTS
= 1, /* alist entry already exists */
234 ALE_NOTFND
= 2, /* item not found and insert not required */
235 ALE_CREATE
= 3 /* alist entry created */
240 * Access to an Alist item by index or offset. This is needed because the
241 * size of an item in an Alist is not known by the C compiler, and we
242 * have to do the indexing arithmetic explicitly.
244 * For an APlist, index the apl_data field directly --- No macro is needed.
246 #define alist_item(_lp, _idx) \
247 ((void *)(ALIST_OFF_DATA + ((_idx) * (_lp)->al_size) + (char *)(_lp)))
248 #define alist_item_by_offset(_lp, _off) \
249 ((void *)((_off) + (char *)(_lp)))
252 * The number of items currently found in a list (nitems), and the total number
253 * of slots in the current data allocation (arritems). These macros handle the
254 * case where the list has not been allocated yet.
256 #define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems)
257 #define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems)
258 #define alist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_arritems)
259 #define aplist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_arritems)
262 extern void *alist_append(Alist
**, const void *, size_t, Aliste
);
263 extern void alist_delete(Alist
*, Aliste
*);
264 extern void alist_delete_by_offset(Alist
*, Aliste
*);
265 extern void *alist_insert(Alist
**, const void *, size_t,
267 extern void *alist_insert_by_offset(Alist
**, const void *, size_t,
269 extern void alist_reset(Alist
*);
272 extern void *aplist_append(APlist
**, const void *, Aliste
);
273 extern void aplist_delete(APlist
*, Aliste
*);
274 extern int aplist_delete_value(APlist
*, const void *);
275 extern void *aplist_insert(APlist
**, const void *,
277 extern void aplist_reset(APlist
*);
278 extern aplist_test_t
aplist_test(APlist
**, const void *, Aliste
);
284 #endif /* _ALIST_H */