13434Sesaxe /*
23434Sesaxe * CDDL HEADER START
33434Sesaxe *
43434Sesaxe * The contents of this file are subject to the terms of the
53434Sesaxe * Common Development and Distribution License (the "License").
63434Sesaxe * You may not use this file except in compliance with the License.
73434Sesaxe *
83434Sesaxe * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93434Sesaxe * or http://www.opensolaris.org/os/licensing.
103434Sesaxe * See the License for the specific language governing permissions
113434Sesaxe * and limitations under the License.
123434Sesaxe *
133434Sesaxe * When distributing Covered Code, include this CDDL HEADER in each
143434Sesaxe * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153434Sesaxe * If applicable, add the following below this CDDL HEADER, with the
163434Sesaxe * fields enclosed by brackets "[]" replaced with your own identifying
173434Sesaxe * information: Portions Copyright [yyyy] [name of copyright owner]
183434Sesaxe *
193434Sesaxe * CDDL HEADER END
203434Sesaxe */
213434Sesaxe /*
228906SEric.Saxe@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
233434Sesaxe * Use is subject to license terms.
243434Sesaxe */
253434Sesaxe
263434Sesaxe #include <sys/systm.h>
273434Sesaxe #include <sys/param.h>
283434Sesaxe #include <sys/debug.h>
293434Sesaxe #include <sys/kmem.h>
303434Sesaxe #include <sys/group.h>
31*11389SAlexander.Kolbasov@Sun.COM #include <sys/cmn_err.h>
323434Sesaxe
333434Sesaxe
343434Sesaxe #define GRP_SET_SIZE_DEFAULT 2
353434Sesaxe
363434Sesaxe static void group_grow_set(group_t *);
373434Sesaxe static void group_shrink_set(group_t *);
383434Sesaxe static void group_pack_set(void **, uint_t);
393434Sesaxe
403434Sesaxe /*
413434Sesaxe * Initialize a group_t
423434Sesaxe */
433434Sesaxe void
group_create(group_t * g)443434Sesaxe group_create(group_t *g)
453434Sesaxe {
463434Sesaxe bzero(g, sizeof (group_t));
473434Sesaxe }
483434Sesaxe
493434Sesaxe /*
503434Sesaxe * Destroy a group_t
513434Sesaxe * The group must already be empty
523434Sesaxe */
533434Sesaxe void
group_destroy(group_t * g)543434Sesaxe group_destroy(group_t *g)
553434Sesaxe {
563434Sesaxe ASSERT(g->grp_size == 0);
573434Sesaxe
583434Sesaxe if (g->grp_capacity > 0) {
593434Sesaxe kmem_free(g->grp_set, g->grp_capacity * sizeof (void *));
603434Sesaxe g->grp_capacity = 0;
613434Sesaxe }
623434Sesaxe g->grp_set = NULL;
633434Sesaxe }
643434Sesaxe
653434Sesaxe /*
668906SEric.Saxe@Sun.COM * Empty a group_t
678906SEric.Saxe@Sun.COM * Capacity is preserved.
688906SEric.Saxe@Sun.COM */
698906SEric.Saxe@Sun.COM void
group_empty(group_t * g)708906SEric.Saxe@Sun.COM group_empty(group_t *g)
718906SEric.Saxe@Sun.COM {
728906SEric.Saxe@Sun.COM int i;
738906SEric.Saxe@Sun.COM int sz = g->grp_size;
748906SEric.Saxe@Sun.COM
758906SEric.Saxe@Sun.COM g->grp_size = 0;
768906SEric.Saxe@Sun.COM for (i = 0; i < sz; i++)
778906SEric.Saxe@Sun.COM g->grp_set[i] = NULL;
788906SEric.Saxe@Sun.COM }
798906SEric.Saxe@Sun.COM
808906SEric.Saxe@Sun.COM /*
813434Sesaxe * Add element "e" to group "g"
823434Sesaxe *
833434Sesaxe * Returns -1 if addition would result in overcapacity, and
843434Sesaxe * resize operations aren't allowed, and 0 otherwise
853434Sesaxe */
863434Sesaxe int
group_add(group_t * g,void * e,int gflag)873434Sesaxe group_add(group_t *g, void *e, int gflag)
883434Sesaxe {
893434Sesaxe int entry;
903434Sesaxe
913434Sesaxe if ((gflag & GRP_NORESIZE) &&
923434Sesaxe g->grp_size == g->grp_capacity)
933434Sesaxe return (-1);
943434Sesaxe
953434Sesaxe ASSERT(g->grp_size != g->grp_capacity || (gflag & GRP_RESIZE));
963434Sesaxe
973434Sesaxe entry = g->grp_size++;
983434Sesaxe if (g->grp_size > g->grp_capacity)
993434Sesaxe group_grow_set(g);
1003434Sesaxe
1013434Sesaxe ASSERT(g->grp_set[entry] == NULL);
1023434Sesaxe g->grp_set[entry] = e;
1033434Sesaxe
1043434Sesaxe return (0);
1053434Sesaxe }
1063434Sesaxe
1073434Sesaxe /*
1083434Sesaxe * Remove element "e" from group "g"
1093434Sesaxe *
1103434Sesaxe * Returns -1 if "e" was not present in "g" and 0 otherwise
1113434Sesaxe */
1123434Sesaxe int
group_remove(group_t * g,void * e,int gflag)1133434Sesaxe group_remove(group_t *g, void *e, int gflag)
1143434Sesaxe {
1153434Sesaxe int i;
1163434Sesaxe
1179438SEric.Saxe@Sun.COM if (g->grp_size == 0)
1189438SEric.Saxe@Sun.COM return (-1);
1199438SEric.Saxe@Sun.COM
1203434Sesaxe /*
1213434Sesaxe * Find the element in the group's set
1223434Sesaxe */
1233434Sesaxe for (i = 0; i < g->grp_size; i++)
1243434Sesaxe if (g->grp_set[i] == e)
1253434Sesaxe break;
1263434Sesaxe if (g->grp_set[i] != e)
1273434Sesaxe return (-1);
1283434Sesaxe
1293434Sesaxe g->grp_set[i] = NULL;
1303434Sesaxe group_pack_set(g->grp_set, g->grp_size);
1313434Sesaxe g->grp_size--;
1323434Sesaxe
1333434Sesaxe if ((gflag & GRP_RESIZE) &&
1343434Sesaxe g->grp_size > GRP_SET_SIZE_DEFAULT &&
1353434Sesaxe ((g->grp_size - 1) & g->grp_size) == 0)
1363434Sesaxe group_shrink_set(g);
1373434Sesaxe
1383434Sesaxe return (0);
1393434Sesaxe }
1403434Sesaxe
1413434Sesaxe /*
1423434Sesaxe * Expand the capacity of group "g" so that it may
1433434Sesaxe * contain at least "n" elements
1443434Sesaxe */
1453434Sesaxe void
group_expand(group_t * g,uint_t n)1463434Sesaxe group_expand(group_t *g, uint_t n)
1473434Sesaxe {
1483434Sesaxe while (g->grp_capacity < n)
1493434Sesaxe group_grow_set(g);
1503434Sesaxe }
1513434Sesaxe
1523434Sesaxe /*
1533434Sesaxe * Upsize a group's holding capacity
1543434Sesaxe */
1553434Sesaxe static void
group_grow_set(group_t * g)1563434Sesaxe group_grow_set(group_t *g)
1573434Sesaxe {
1583434Sesaxe uint_t cap_old, cap_new;
1593434Sesaxe void **set_old, **set_new;
1603434Sesaxe
1613434Sesaxe cap_old = g->grp_capacity;
1623434Sesaxe set_old = g->grp_set;
1633434Sesaxe
1643434Sesaxe /*
1653434Sesaxe * The array size grows in powers of two
1663434Sesaxe */
1673434Sesaxe if ((cap_new = (cap_old << 1)) == 0) {
1683434Sesaxe /*
1693434Sesaxe * The set is unallocated.
1703434Sesaxe * Allocate a default sized set.
1713434Sesaxe */
1723434Sesaxe cap_new = GRP_SET_SIZE_DEFAULT;
1733434Sesaxe g->grp_set = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP);
1743434Sesaxe g->grp_capacity = cap_new;
1753434Sesaxe } else {
1763434Sesaxe /*
1773434Sesaxe * Allocate a newly sized array,
1783434Sesaxe * copy the data, and free the old array.
1793434Sesaxe */
1803434Sesaxe set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP);
1813434Sesaxe (void) kcopy(set_old, set_new, cap_old * sizeof (void *));
1823434Sesaxe g->grp_set = set_new;
1833434Sesaxe g->grp_capacity = cap_new;
1843434Sesaxe kmem_free(set_old, cap_old * sizeof (void *));
1853434Sesaxe }
1863434Sesaxe /*
1873434Sesaxe * The new array size should be a power of two
1883434Sesaxe */
1893434Sesaxe ASSERT(((cap_new - 1) & cap_new) == 0);
1903434Sesaxe }
1913434Sesaxe
1923434Sesaxe /*
1933434Sesaxe * Downsize a group's holding capacity
1943434Sesaxe */
1953434Sesaxe static void
group_shrink_set(group_t * g)1963434Sesaxe group_shrink_set(group_t *g)
1973434Sesaxe {
1983434Sesaxe uint_t cap_old, cap_new;
1993434Sesaxe void **set_old, **set_new;
2003434Sesaxe
2013434Sesaxe cap_old = g->grp_capacity;
2023434Sesaxe set_old = g->grp_set;
2033434Sesaxe
2043434Sesaxe /*
2053434Sesaxe * The group's existing array size must already
2063434Sesaxe * be a power of two
2073434Sesaxe */
2083434Sesaxe ASSERT(((cap_old - 1) & cap_old) == 0);
2093434Sesaxe cap_new = cap_old >> 1;
2103434Sesaxe
2113434Sesaxe /*
2123434Sesaxe * GRP_SET_SIZE_DEFAULT is the minumum set size.
2133434Sesaxe */
2143434Sesaxe if (cap_new < GRP_SET_SIZE_DEFAULT)
2153434Sesaxe return;
2163434Sesaxe
2173434Sesaxe set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP);
2183434Sesaxe (void) kcopy(set_old, set_new, cap_new * sizeof (void *));
2193434Sesaxe g->grp_capacity = cap_new;
2203434Sesaxe g->grp_set = set_new;
2213434Sesaxe
2223434Sesaxe ASSERT(((cap_new - 1) & cap_new) == 0);
2233434Sesaxe kmem_free(set_old, cap_old * sizeof (void *));
2243434Sesaxe }
2253434Sesaxe
2263434Sesaxe /*
2273434Sesaxe * Pack a group's set
2283434Sesaxe * Element order is not preserved
2293434Sesaxe */
2303434Sesaxe static void
group_pack_set(void ** set,uint_t sz)2313434Sesaxe group_pack_set(void **set, uint_t sz)
2323434Sesaxe {
2333434Sesaxe uint_t i, j, free;
2343434Sesaxe
2353434Sesaxe free = (uint_t)-1;
2363434Sesaxe
2373434Sesaxe for (i = 0; i < sz; i++) {
2383434Sesaxe if (set[i] == NULL && free == (uint_t)-1) {
2393434Sesaxe /*
2403434Sesaxe * Found a new free slot.
2413434Sesaxe * Start packing from here.
2423434Sesaxe */
2433434Sesaxe free = i;
2443434Sesaxe } else if (set[i] != NULL && free != (uint_t)-1) {
2453434Sesaxe /*
2463434Sesaxe * Found a slot to pack into
2473434Sesaxe * an earlier free slot.
2483434Sesaxe */
2493434Sesaxe ASSERT(set[free] == NULL);
2503434Sesaxe set[free] = set[i];
2513434Sesaxe set[i] = NULL;
2523434Sesaxe
2533434Sesaxe /*
2543434Sesaxe * Find the next free slot
2553434Sesaxe */
2563434Sesaxe for (j = free + 1; set[j] != NULL; j++) {
2573434Sesaxe ASSERT(j <= i);
2583434Sesaxe if (j == i)
2593434Sesaxe break;
2603434Sesaxe }
2613434Sesaxe if (set[j] == NULL)
2623434Sesaxe free = j;
2633434Sesaxe else
2643434Sesaxe free = (uint_t)-1;
2653434Sesaxe }
2663434Sesaxe }
2673434Sesaxe }
2683434Sesaxe
2693434Sesaxe /*
2703434Sesaxe * Initialize a group iterator cookie
2713434Sesaxe */
2723434Sesaxe void
group_iter_init(group_iter_t * iter)2733434Sesaxe group_iter_init(group_iter_t *iter)
2743434Sesaxe {
2753434Sesaxe *iter = 0;
2763434Sesaxe }
2773434Sesaxe
2783434Sesaxe /*
2793434Sesaxe * Iterate over the elements in a group
2803434Sesaxe */
2813434Sesaxe void *
group_iterate(group_t * g,group_iter_t * iter)2823434Sesaxe group_iterate(group_t *g, group_iter_t *iter)
2833434Sesaxe {
2843434Sesaxe uint_t idx = *iter;
2853434Sesaxe void *data = NULL;
2863434Sesaxe
2873434Sesaxe while (idx < g->grp_size) {
2883434Sesaxe data = g->grp_set[idx++];
2893434Sesaxe if (data != NULL)
2903434Sesaxe break;
2913434Sesaxe }
2923434Sesaxe *iter = idx;
2933434Sesaxe
2943434Sesaxe return (data);
2953434Sesaxe }
2963434Sesaxe
2973434Sesaxe /*
2983434Sesaxe * Indexed access to a group's elements
2993434Sesaxe */
3003434Sesaxe void *
group_access_at(group_t * g,uint_t idx)3013434Sesaxe group_access_at(group_t *g, uint_t idx)
3023434Sesaxe {
3033434Sesaxe if (idx >= g->grp_capacity)
3043434Sesaxe return (NULL);
3053434Sesaxe
3063434Sesaxe return (g->grp_set[idx]);
3073434Sesaxe }
3083434Sesaxe
3093434Sesaxe /*
3103434Sesaxe * Add a new ordered group element at specified
3113434Sesaxe * index. The group must already be of sufficient
3123434Sesaxe * capacity to hold an element at the specified index.
3133434Sesaxe *
3143434Sesaxe * Returns 0 if addition was sucessful, and -1 if the
3153434Sesaxe * addition failed because the table was too small
3163434Sesaxe */
3173434Sesaxe int
group_add_at(group_t * g,void * e,uint_t idx)3183434Sesaxe group_add_at(group_t *g, void *e, uint_t idx)
3193434Sesaxe {
3203434Sesaxe if (idx >= g->grp_capacity)
3213434Sesaxe return (-1);
3223434Sesaxe
3233434Sesaxe if (idx >= g->grp_size)
3243434Sesaxe g->grp_size = idx + 1;
3253434Sesaxe
3263434Sesaxe ASSERT(g->grp_set[idx] == NULL);
3273434Sesaxe g->grp_set[idx] = e;
3283434Sesaxe return (0);
3293434Sesaxe }
3303434Sesaxe
3313434Sesaxe /*
3328906SEric.Saxe@Sun.COM * Remove the element at the specified index
3333434Sesaxe */
3343434Sesaxe void
group_remove_at(group_t * g,uint_t idx)3353434Sesaxe group_remove_at(group_t *g, uint_t idx)
3363434Sesaxe {
3373434Sesaxe ASSERT(idx < g->grp_capacity);
3383434Sesaxe g->grp_set[idx] = NULL;
3393434Sesaxe }
3408906SEric.Saxe@Sun.COM
3418906SEric.Saxe@Sun.COM /*
3428906SEric.Saxe@Sun.COM * Find an element in the group, and return its index
3438906SEric.Saxe@Sun.COM * Returns -1 if the element could not be found.
3448906SEric.Saxe@Sun.COM */
3458906SEric.Saxe@Sun.COM uint_t
group_find(group_t * g,void * e)3468906SEric.Saxe@Sun.COM group_find(group_t *g, void *e)
3478906SEric.Saxe@Sun.COM {
3488906SEric.Saxe@Sun.COM uint_t idx;
3498906SEric.Saxe@Sun.COM
3508906SEric.Saxe@Sun.COM for (idx = 0; idx < g->grp_capacity; idx++) {
3518906SEric.Saxe@Sun.COM if (g->grp_set[idx] == e)
3528906SEric.Saxe@Sun.COM return (idx);
3538906SEric.Saxe@Sun.COM }
3548906SEric.Saxe@Sun.COM return ((uint_t)-1);
3558906SEric.Saxe@Sun.COM }
356*11389SAlexander.Kolbasov@Sun.COM
357*11389SAlexander.Kolbasov@Sun.COM /*
358*11389SAlexander.Kolbasov@Sun.COM * Return a string in a given buffer with list of integer entries in a group.
359*11389SAlexander.Kolbasov@Sun.COM * The string concatenates consecutive integer ranges ax x-y.
360*11389SAlexander.Kolbasov@Sun.COM * The resulting string looks like "1,2-5,8"
361*11389SAlexander.Kolbasov@Sun.COM *
362*11389SAlexander.Kolbasov@Sun.COM * The convert argument is used to map group elements to integer IDs.
363*11389SAlexander.Kolbasov@Sun.COM */
364*11389SAlexander.Kolbasov@Sun.COM char *
group2intlist(group_t * group,char * buffer,size_t len,int (convert)(void *))365*11389SAlexander.Kolbasov@Sun.COM group2intlist(group_t *group, char *buffer, size_t len, int (convert)(void*))
366*11389SAlexander.Kolbasov@Sun.COM {
367*11389SAlexander.Kolbasov@Sun.COM char *ptr = buffer;
368*11389SAlexander.Kolbasov@Sun.COM void *v;
369*11389SAlexander.Kolbasov@Sun.COM group_iter_t iter;
370*11389SAlexander.Kolbasov@Sun.COM boolean_t first_iteration = B_TRUE;
371*11389SAlexander.Kolbasov@Sun.COM boolean_t first_value = B_TRUE;
372*11389SAlexander.Kolbasov@Sun.COM int start = 0, end = 0;
373*11389SAlexander.Kolbasov@Sun.COM
374*11389SAlexander.Kolbasov@Sun.COM /*
375*11389SAlexander.Kolbasov@Sun.COM * Allow for the terminating NULL-byte
376*11389SAlexander.Kolbasov@Sun.COM */
377*11389SAlexander.Kolbasov@Sun.COM len = len -1;
378*11389SAlexander.Kolbasov@Sun.COM
379*11389SAlexander.Kolbasov@Sun.COM group_iter_init(&iter);
380*11389SAlexander.Kolbasov@Sun.COM while ((v = group_iterate(group, &iter)) != NULL && len > 0) {
381*11389SAlexander.Kolbasov@Sun.COM int id = convert(v);
382*11389SAlexander.Kolbasov@Sun.COM int nbytes = 0;
383*11389SAlexander.Kolbasov@Sun.COM
384*11389SAlexander.Kolbasov@Sun.COM if (first_iteration) {
385*11389SAlexander.Kolbasov@Sun.COM start = end = id;
386*11389SAlexander.Kolbasov@Sun.COM first_iteration = B_FALSE;
387*11389SAlexander.Kolbasov@Sun.COM } else if (end + 1 == id) {
388*11389SAlexander.Kolbasov@Sun.COM /*
389*11389SAlexander.Kolbasov@Sun.COM * Got consecutive ID, so extend end of range without
390*11389SAlexander.Kolbasov@Sun.COM * doing anything since the range may extend further
391*11389SAlexander.Kolbasov@Sun.COM */
392*11389SAlexander.Kolbasov@Sun.COM end = id;
393*11389SAlexander.Kolbasov@Sun.COM } else {
394*11389SAlexander.Kolbasov@Sun.COM if (first_value) {
395*11389SAlexander.Kolbasov@Sun.COM first_value = B_FALSE;
396*11389SAlexander.Kolbasov@Sun.COM } else {
397*11389SAlexander.Kolbasov@Sun.COM *ptr++ = ',';
398*11389SAlexander.Kolbasov@Sun.COM len--;
399*11389SAlexander.Kolbasov@Sun.COM }
400*11389SAlexander.Kolbasov@Sun.COM
401*11389SAlexander.Kolbasov@Sun.COM if (len == 0)
402*11389SAlexander.Kolbasov@Sun.COM break;
403*11389SAlexander.Kolbasov@Sun.COM
404*11389SAlexander.Kolbasov@Sun.COM /*
405*11389SAlexander.Kolbasov@Sun.COM * Next ID is not consecutive, so dump IDs gotten so
406*11389SAlexander.Kolbasov@Sun.COM * far.
407*11389SAlexander.Kolbasov@Sun.COM */
408*11389SAlexander.Kolbasov@Sun.COM if (end > start + 1) /* range */
409*11389SAlexander.Kolbasov@Sun.COM nbytes = snprintf(ptr, len, "%d-%d",
410*11389SAlexander.Kolbasov@Sun.COM start, end);
411*11389SAlexander.Kolbasov@Sun.COM else if (end > start) /* different values */
412*11389SAlexander.Kolbasov@Sun.COM nbytes = snprintf(ptr, len, "%d,%d",
413*11389SAlexander.Kolbasov@Sun.COM start, end);
414*11389SAlexander.Kolbasov@Sun.COM else /* same value */
415*11389SAlexander.Kolbasov@Sun.COM nbytes = snprintf(ptr, len, "%d", start);
416*11389SAlexander.Kolbasov@Sun.COM
417*11389SAlexander.Kolbasov@Sun.COM if (nbytes <= 0) {
418*11389SAlexander.Kolbasov@Sun.COM len = 0;
419*11389SAlexander.Kolbasov@Sun.COM break;
420*11389SAlexander.Kolbasov@Sun.COM }
421*11389SAlexander.Kolbasov@Sun.COM
422*11389SAlexander.Kolbasov@Sun.COM /*
423*11389SAlexander.Kolbasov@Sun.COM * Advance position in the string
424*11389SAlexander.Kolbasov@Sun.COM */
425*11389SAlexander.Kolbasov@Sun.COM ptr += nbytes;
426*11389SAlexander.Kolbasov@Sun.COM len -= nbytes;
427*11389SAlexander.Kolbasov@Sun.COM
428*11389SAlexander.Kolbasov@Sun.COM /*
429*11389SAlexander.Kolbasov@Sun.COM * Try finding consecutive range starting from current
430*11389SAlexander.Kolbasov@Sun.COM * ID.
431*11389SAlexander.Kolbasov@Sun.COM */
432*11389SAlexander.Kolbasov@Sun.COM start = end = id;
433*11389SAlexander.Kolbasov@Sun.COM }
434*11389SAlexander.Kolbasov@Sun.COM }
435*11389SAlexander.Kolbasov@Sun.COM
436*11389SAlexander.Kolbasov@Sun.COM if (!first_value) {
437*11389SAlexander.Kolbasov@Sun.COM *ptr++ = ',';
438*11389SAlexander.Kolbasov@Sun.COM len--;
439*11389SAlexander.Kolbasov@Sun.COM }
440*11389SAlexander.Kolbasov@Sun.COM /*
441*11389SAlexander.Kolbasov@Sun.COM * Print last ID(s)
442*11389SAlexander.Kolbasov@Sun.COM */
443*11389SAlexander.Kolbasov@Sun.COM if (len > 0) {
444*11389SAlexander.Kolbasov@Sun.COM if (end > start + 1) {
445*11389SAlexander.Kolbasov@Sun.COM (void) snprintf(ptr, len, "%d-%d", start, end);
446*11389SAlexander.Kolbasov@Sun.COM } else if (end != start) {
447*11389SAlexander.Kolbasov@Sun.COM (void) snprintf(ptr, len, "%d,%d", start, end);
448*11389SAlexander.Kolbasov@Sun.COM } else {
449*11389SAlexander.Kolbasov@Sun.COM (void) snprintf(ptr, len, "%d", start);
450*11389SAlexander.Kolbasov@Sun.COM }
451*11389SAlexander.Kolbasov@Sun.COM }
452*11389SAlexander.Kolbasov@Sun.COM
453*11389SAlexander.Kolbasov@Sun.COM return (buffer);
454*11389SAlexander.Kolbasov@Sun.COM }
455