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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/sysmacros.h>
27 #include <sys/systm.h>
28 #include <sys/param.h>
29 #include <sys/debug.h>
31 #include <sys/group.h>
32 #include <sys/cmn_err.h>
35 #define GRP_SET_SIZE_DEFAULT 2
37 static void group_grow_set(group_t
*);
38 static void group_shrink_set(group_t
*);
39 static void group_pack_set(void **, uint_t
);
42 * Initialize a group_t
45 group_create(group_t
*g
)
47 bzero(g
, sizeof (group_t
));
52 * The group must already be empty
55 group_destroy(group_t
*g
)
57 ASSERT(g
->grp_size
== 0);
59 if (g
->grp_capacity
> 0) {
60 kmem_free(g
->grp_set
, g
->grp_capacity
* sizeof (void *));
68 * Capacity is preserved.
71 group_empty(group_t
*g
)
77 for (i
= 0; i
< sz
; i
++)
82 * Add element "e" to group "g"
84 * Returns -1 if addition would result in overcapacity, and
85 * resize operations aren't allowed, and 0 otherwise
88 group_add(group_t
*g
, void *e
, int gflag
)
92 if ((gflag
& GRP_NORESIZE
) &&
93 g
->grp_size
== g
->grp_capacity
)
96 ASSERT(g
->grp_size
!= g
->grp_capacity
|| (gflag
& GRP_RESIZE
));
98 entry
= g
->grp_size
++;
99 if (g
->grp_size
> g
->grp_capacity
)
102 ASSERT(g
->grp_set
[entry
] == NULL
);
103 g
->grp_set
[entry
] = e
;
109 * Remove element "e" from group "g"
111 * Returns -1 if "e" was not present in "g" and 0 otherwise
114 group_remove(group_t
*g
, void *e
, int gflag
)
118 if (g
->grp_size
== 0)
122 * Find the element in the group's set
124 for (i
= 0; i
< g
->grp_size
; i
++)
125 if (g
->grp_set
[i
] == e
)
127 if (g
->grp_set
[i
] != e
)
130 g
->grp_set
[i
] = NULL
;
131 group_pack_set(g
->grp_set
, g
->grp_size
);
134 if ((gflag
& GRP_RESIZE
) &&
135 g
->grp_size
> GRP_SET_SIZE_DEFAULT
&& ISP2(g
->grp_size
))
142 * Expand the capacity of group "g" so that it may
143 * contain at least "n" elements
146 group_expand(group_t
*g
, uint_t n
)
148 while (g
->grp_capacity
< n
)
153 * Upsize a group's holding capacity
156 group_grow_set(group_t
*g
)
158 uint_t cap_old
, cap_new
;
159 void **set_old
, **set_new
;
161 cap_old
= g
->grp_capacity
;
162 set_old
= g
->grp_set
;
165 * The array size grows in powers of two
167 if ((cap_new
= (cap_old
<< 1)) == 0) {
169 * The set is unallocated.
170 * Allocate a default sized set.
172 cap_new
= GRP_SET_SIZE_DEFAULT
;
173 g
->grp_set
= kmem_zalloc(cap_new
* sizeof (void *), KM_SLEEP
);
174 g
->grp_capacity
= cap_new
;
177 * Allocate a newly sized array,
178 * copy the data, and free the old array.
180 set_new
= kmem_zalloc(cap_new
* sizeof (void *), KM_SLEEP
);
181 (void) kcopy(set_old
, set_new
, cap_old
* sizeof (void *));
182 g
->grp_set
= set_new
;
183 g
->grp_capacity
= cap_new
;
184 kmem_free(set_old
, cap_old
* sizeof (void *));
187 * The new array size should be a power of two
189 ASSERT(((cap_new
- 1) & cap_new
) == 0);
193 * Downsize a group's holding capacity
196 group_shrink_set(group_t
*g
)
198 uint_t cap_old
, cap_new
;
199 void **set_old
, **set_new
;
201 cap_old
= g
->grp_capacity
;
202 set_old
= g
->grp_set
;
205 * The group's existing array size must already
208 ASSERT(((cap_old
- 1) & cap_old
) == 0);
209 cap_new
= cap_old
>> 1;
212 * GRP_SET_SIZE_DEFAULT is the minumum set size.
214 if (cap_new
< GRP_SET_SIZE_DEFAULT
)
217 set_new
= kmem_zalloc(cap_new
* sizeof (void *), KM_SLEEP
);
218 (void) kcopy(set_old
, set_new
, cap_new
* sizeof (void *));
219 g
->grp_capacity
= cap_new
;
220 g
->grp_set
= set_new
;
222 ASSERT(((cap_new
- 1) & cap_new
) == 0);
223 kmem_free(set_old
, cap_old
* sizeof (void *));
228 * Element order is not preserved
231 group_pack_set(void **set
, uint_t sz
)
237 for (i
= 0; i
< sz
; i
++) {
238 if (set
[i
] == NULL
&& free
== (uint_t
)-1) {
240 * Found a new free slot.
241 * Start packing from here.
244 } else if (set
[i
] != NULL
&& free
!= (uint_t
)-1) {
246 * Found a slot to pack into
247 * an earlier free slot.
249 ASSERT(set
[free
] == NULL
);
254 * Find the next free slot
256 for (j
= free
+ 1; set
[j
] != NULL
; j
++) {
270 * Initialize a group iterator cookie
273 group_iter_init(group_iter_t
*iter
)
279 * Iterate over the elements in a group
282 group_iterate(group_t
*g
, group_iter_t
*iter
)
287 while (idx
< g
->grp_size
) {
288 data
= g
->grp_set
[idx
++];
298 * Indexed access to a group's elements
301 group_access_at(group_t
*g
, uint_t idx
)
303 if (idx
>= g
->grp_capacity
)
306 return (g
->grp_set
[idx
]);
310 * Add a new ordered group element at specified
311 * index. The group must already be of sufficient
312 * capacity to hold an element at the specified index.
314 * Returns 0 if addition was sucessful, and -1 if the
315 * addition failed because the table was too small
318 group_add_at(group_t
*g
, void *e
, uint_t idx
)
320 if (idx
>= g
->grp_capacity
)
323 if (idx
>= g
->grp_size
)
324 g
->grp_size
= idx
+ 1;
326 ASSERT(g
->grp_set
[idx
] == NULL
);
332 * Remove the element at the specified index
335 group_remove_at(group_t
*g
, uint_t idx
)
337 ASSERT(idx
< g
->grp_capacity
);
338 g
->grp_set
[idx
] = NULL
;
342 * Find an element in the group, and return its index
343 * Returns -1 if the element could not be found.
346 group_find(group_t
*g
, void *e
)
350 for (idx
= 0; idx
< g
->grp_capacity
; idx
++) {
351 if (g
->grp_set
[idx
] == e
)
358 * Return a string in a given buffer with list of integer entries in a group.
359 * The string concatenates consecutive integer ranges ax x-y.
360 * The resulting string looks like "1,2-5,8"
362 * The convert argument is used to map group elements to integer IDs.
365 group2intlist(group_t
*group
, char *buffer
, size_t len
, int (convert
)(void*))
370 boolean_t first_iteration
= B_TRUE
;
371 boolean_t first_value
= B_TRUE
;
372 int start
= 0, end
= 0;
375 * Allow for the terminating NULL-byte
379 group_iter_init(&iter
);
380 while ((v
= group_iterate(group
, &iter
)) != NULL
&& len
> 0) {
384 if (first_iteration
) {
386 first_iteration
= B_FALSE
;
387 } else if (end
+ 1 == id
) {
389 * Got consecutive ID, so extend end of range without
390 * doing anything since the range may extend further
395 first_value
= B_FALSE
;
405 * Next ID is not consecutive, so dump IDs gotten so
408 if (end
> start
+ 1) /* range */
409 nbytes
= snprintf(ptr
, len
, "%d-%d",
411 else if (end
> start
) /* different values */
412 nbytes
= snprintf(ptr
, len
, "%d,%d",
414 else /* same value */
415 nbytes
= snprintf(ptr
, len
, "%d", start
);
423 * Advance position in the string
429 * Try finding consecutive range starting from current
444 if (end
> start
+ 1) {
445 (void) snprintf(ptr
, len
, "%d-%d", start
, end
);
446 } else if (end
!= start
) {
447 (void) snprintf(ptr
, len
, "%d,%d", start
, end
);
449 (void) snprintf(ptr
, len
, "%d", start
);