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 * 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. 30 */ 31 32 #ifndef _ALIST_H 33 #define _ALIST_H 34 35 #ifdef __cplusplus 36 extern "C" { 37 #endif 38 39 #include <sys/types.h> 40 #include <sys/machelf.h> 41 42 /* 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. 50 * 51 * There are two "flavors" of array list: 52 * 53 * Alist - Contain arbitrary data, usually structs. 54 * APlist - Contain pointers to data allocated elsewhere. 55 * 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. 59 * 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: 64 * 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. 82 */ 83 84 85 86 /* 87 * Aliste is used to represent list indexes, offsets, and sizes. 88 */ 89 typedef size_t Aliste; 90 91 92 93 /* 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 100 * malloc(). 101 * - al_nitems and al_next are redundant, in that they are 102 * directly related: 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). 107 * 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. 113 */ 114 typedef struct { 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) */ 120 } Alist; 121 122 /* 123 * APlist is a variant of Alist that contains pointers. There are several 124 * benefits to this special type: 125 * - API is simpler 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 131 * regular Alists. 132 */ 133 typedef struct { 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 */ 137 } APlist; 138 139 140 /* 141 * The ALIST_OFF_DATA and APLIST_OFF_DATA macros give the byte offset 142 * from the start of an array list to the first byte of the data area 143 * used to hold user data. The same trick used by the standard offsetof() 144 * macro is used. 145 */ 146 #define ALIST_OFF_DATA ((size_t)(((Alist *)0)->al_data)) 147 #define APLIST_OFF_DATA ((size_t)(((APlist *)0)->apl_data)) 148 149 150 /* 151 * The TRAVERSE macros are intended to be used within a for(), and 152 * cause the resulting loop to iterate over each item in the loop, 153 * in order. 154 * ALIST_TRAVERSE: Traverse over the items in an Alist, 155 * using the zero based item array index to refer to 156 * each item. 157 * ALIST_TRAVERSE_BY_OFFSET: Traverse over the items in an 158 * Alist using the byte offset from the head of the 159 * Alist pointer to refer to each item. It should be noted 160 * that the first such offset is given by ALIST_OFF_DATA, 161 * and as such, there will never be a 0 offset. Some code 162 * uses this fact to treat 0 as a reserved value with 163 * special meaning. 164 * 165 * By-offset access is convenient for some parts of 166 * rtld, where a value of 0 is used to indicate an 167 * uninitialized link map control. 168 * 169 * APLIST_TRAVERSE: Traverse over the pointers in an APlist, using 170 * the zero based item array index to refer to each pointer. 171 */ 172 173 /* 174 * Within the loop: 175 * 176 * LIST - Pointer to Alist structure for list 177 * IDX - The current item index 178 * OFF - The current item offset 179 * DATA - Pointer to item 180 */ 181 #define ALIST_TRAVERSE(LIST, IDX, DATA) \ 182 (IDX) = 0, \ 183 ((LIST) != NULL) && ((DATA) = (void *)(LIST)->al_data); \ 184 \ 185 ((LIST) != NULL) && ((IDX) < (LIST)->al_nitems); \ 186 \ 187 (IDX)++, \ 188 (DATA) = (void *) (((LIST)->al_size * (IDX)) + (char *)(LIST)->al_data) 189 190 #define ALIST_TRAVERSE_BY_OFFSET(LIST, OFF, DATA) \ 191 (((LIST) != NULL) && ((OFF) = ALIST_OFF_DATA) && \ 192 (((DATA) = (void *)((char *)(LIST) + (OFF))))); \ 193 \ 194 (((LIST) != NULL) && ((OFF) < (LIST)->al_next)); \ 195 \ 196 (((OFF) += ((LIST)->al_size)), \ 197 ((DATA) = (void *)((char *)(LIST) + (OFF)))) 198 199 /* 200 * Within the loop: 201 * 202 * LIST - Pointer to APlist structure for list 203 * IDX - The current item index 204 * PTR - item value 205 * 206 * Note that this macro is designed to ensure that PTR retains the 207 * value of the final pointer in the list after exiting the for loop, 208 * and to avoid dereferencing an out of range address. This is done by 209 * doing the dereference in the middle expression, using the comma 210 * operator to ensure that a NULL pointer won't stop the loop. 211 */ 212 #define APLIST_TRAVERSE(LIST, IDX, PTR) \ 213 (IDX) = 0; \ 214 \ 215 ((LIST) != NULL) && ((IDX) < (LIST)->apl_nitems) && \ 216 (((PTR) = ((LIST)->apl_data)[IDX]), 1); \ 217 \ 218 (IDX)++ 219 220 221 /* 222 * Possible values returned by aplist_test() 223 */ 224 typedef enum { 225 ALE_ALLOCFAIL = 0, /* Memory allocation error */ 226 ALE_EXISTS = 1, /* alist entry already exists */ 227 ALE_NOTFND = 2, /* item not found and insert not required */ 228 ALE_CREATE = 3 /* alist entry created */ 229 } aplist_test_t; 230 231 232 /* 233 * Access to an Alist item by index or offset. This is needed because the 234 * size of an item in an Alist is not known by the C compiler, and we 235 * have to do the indexing arithmetic explicitly. 236 * 237 * For an APlist, index the apl_data field directly --- No macro is needed. 238 */ 239 #define alist_item(_lp, _idx) \ 240 ((void *)(ALIST_OFF_DATA + ((_idx) * (_lp)->al_size) + (char *)(_lp))) 241 #define alist_item_by_offset(_lp, _off) \ 242 ((void *)((_off) + (char *)(_lp))) 243 244 /* 245 * The number of items currently found in a list (nitems), and the total number 246 * of slots in the current data allocation (arritems). These macros handle the 247 * case where the list has not been allocated yet. 248 */ 249 #define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems) 250 #define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems) 251 #define alist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_arritems) 252 #define aplist_arritems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_arritems) 253 254 255 extern void *alist_append(Alist **, const void *, size_t, Aliste); 256 extern void alist_delete(Alist *, Aliste *); 257 extern void alist_delete_by_offset(Alist *, Aliste *); 258 extern void *alist_insert(Alist **, const void *, size_t, 259 Aliste, Aliste); 260 extern void *alist_insert_by_offset(Alist **, const void *, size_t, 261 Aliste, Aliste); 262 extern void alist_reset(Alist *); 263 264 265 extern void *aplist_append(APlist **, const void *, Aliste); 266 extern void aplist_delete(APlist *, Aliste *); 267 extern int aplist_delete_value(APlist *, const void *); 268 extern void *aplist_insert(APlist **, const void *, 269 Aliste, Aliste idx); 270 extern void aplist_reset(APlist *); 271 extern aplist_test_t aplist_test(APlist **, const void *, Aliste); 272 273 #ifdef __cplusplus 274 } 275 #endif 276 277 #endif /* _ALIST_H */ 278