/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #define GRP_SET_SIZE_DEFAULT 2 static void group_grow_set(group_t *); static void group_shrink_set(group_t *); static void group_pack_set(void **, uint_t); /* * Initialize a group_t */ void group_create(group_t *g) { bzero(g, sizeof (group_t)); } /* * Destroy a group_t * The group must already be empty */ void group_destroy(group_t *g) { ASSERT(g->grp_size == 0); if (g->grp_capacity > 0) { kmem_free(g->grp_set, g->grp_capacity * sizeof (void *)); g->grp_capacity = 0; } g->grp_set = NULL; } /* * Empty a group_t * Capacity is preserved. */ void group_empty(group_t *g) { int i; int sz = g->grp_size; g->grp_size = 0; for (i = 0; i < sz; i++) g->grp_set[i] = NULL; } /* * Add element "e" to group "g" * * Returns -1 if addition would result in overcapacity, and * resize operations aren't allowed, and 0 otherwise */ int group_add(group_t *g, void *e, int gflag) { int entry; if ((gflag & GRP_NORESIZE) && g->grp_size == g->grp_capacity) return (-1); ASSERT(g->grp_size != g->grp_capacity || (gflag & GRP_RESIZE)); entry = g->grp_size++; if (g->grp_size > g->grp_capacity) group_grow_set(g); ASSERT(g->grp_set[entry] == NULL); g->grp_set[entry] = e; return (0); } /* * Remove element "e" from group "g" * * Returns -1 if "e" was not present in "g" and 0 otherwise */ int group_remove(group_t *g, void *e, int gflag) { int i; if (g->grp_size == 0) return (-1); /* * Find the element in the group's set */ for (i = 0; i < g->grp_size; i++) if (g->grp_set[i] == e) break; if (g->grp_set[i] != e) return (-1); g->grp_set[i] = NULL; group_pack_set(g->grp_set, g->grp_size); g->grp_size--; if ((gflag & GRP_RESIZE) && g->grp_size > GRP_SET_SIZE_DEFAULT && ISP2(g->grp_size)) group_shrink_set(g); return (0); } /* * Expand the capacity of group "g" so that it may * contain at least "n" elements */ void group_expand(group_t *g, uint_t n) { while (g->grp_capacity < n) group_grow_set(g); } /* * Upsize a group's holding capacity */ static void group_grow_set(group_t *g) { uint_t cap_old, cap_new; void **set_old, **set_new; cap_old = g->grp_capacity; set_old = g->grp_set; /* * The array size grows in powers of two */ if ((cap_new = (cap_old << 1)) == 0) { /* * The set is unallocated. * Allocate a default sized set. */ cap_new = GRP_SET_SIZE_DEFAULT; g->grp_set = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); g->grp_capacity = cap_new; } else { /* * Allocate a newly sized array, * copy the data, and free the old array. */ set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); (void) kcopy(set_old, set_new, cap_old * sizeof (void *)); g->grp_set = set_new; g->grp_capacity = cap_new; kmem_free(set_old, cap_old * sizeof (void *)); } /* * The new array size should be a power of two */ ASSERT(((cap_new - 1) & cap_new) == 0); } /* * Downsize a group's holding capacity */ static void group_shrink_set(group_t *g) { uint_t cap_old, cap_new; void **set_old, **set_new; cap_old = g->grp_capacity; set_old = g->grp_set; /* * The group's existing array size must already * be a power of two */ ASSERT(((cap_old - 1) & cap_old) == 0); cap_new = cap_old >> 1; /* * GRP_SET_SIZE_DEFAULT is the minumum set size. */ if (cap_new < GRP_SET_SIZE_DEFAULT) return; set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); (void) kcopy(set_old, set_new, cap_new * sizeof (void *)); g->grp_capacity = cap_new; g->grp_set = set_new; ASSERT(((cap_new - 1) & cap_new) == 0); kmem_free(set_old, cap_old * sizeof (void *)); } /* * Pack a group's set * Element order is not preserved */ static void group_pack_set(void **set, uint_t sz) { uint_t i, j, free; free = (uint_t)-1; for (i = 0; i < sz; i++) { if (set[i] == NULL && free == (uint_t)-1) { /* * Found a new free slot. * Start packing from here. */ free = i; } else if (set[i] != NULL && free != (uint_t)-1) { /* * Found a slot to pack into * an earlier free slot. */ ASSERT(set[free] == NULL); set[free] = set[i]; set[i] = NULL; /* * Find the next free slot */ for (j = free + 1; set[j] != NULL; j++) { ASSERT(j <= i); if (j == i) break; } if (set[j] == NULL) free = j; else free = (uint_t)-1; } } } /* * Initialize a group iterator cookie */ void group_iter_init(group_iter_t *iter) { *iter = 0; } /* * Iterate over the elements in a group */ void * group_iterate(group_t *g, group_iter_t *iter) { uint_t idx = *iter; void *data = NULL; while (idx < g->grp_size) { data = g->grp_set[idx++]; if (data != NULL) break; } *iter = idx; return (data); } /* * Indexed access to a group's elements */ void * group_access_at(group_t *g, uint_t idx) { if (idx >= g->grp_capacity) return (NULL); return (g->grp_set[idx]); } /* * Add a new ordered group element at specified * index. The group must already be of sufficient * capacity to hold an element at the specified index. * * Returns 0 if addition was sucessful, and -1 if the * addition failed because the table was too small */ int group_add_at(group_t *g, void *e, uint_t idx) { if (idx >= g->grp_capacity) return (-1); if (idx >= g->grp_size) g->grp_size = idx + 1; ASSERT(g->grp_set[idx] == NULL); g->grp_set[idx] = e; return (0); } /* * Remove the element at the specified index */ void group_remove_at(group_t *g, uint_t idx) { ASSERT(idx < g->grp_capacity); g->grp_set[idx] = NULL; } /* * Find an element in the group, and return its index * Returns -1 if the element could not be found. */ uint_t group_find(group_t *g, void *e) { uint_t idx; for (idx = 0; idx < g->grp_capacity; idx++) { if (g->grp_set[idx] == e) return (idx); } return ((uint_t)-1); } /* * Return a string in a given buffer with list of integer entries in a group. * The string concatenates consecutive integer ranges ax x-y. * The resulting string looks like "1,2-5,8" * * The convert argument is used to map group elements to integer IDs. */ char * group2intlist(group_t *group, char *buffer, size_t len, int (convert)(void*)) { char *ptr = buffer; void *v; group_iter_t iter; boolean_t first_iteration = B_TRUE; boolean_t first_value = B_TRUE; int start = 0, end = 0; /* * Allow for the terminating NULL-byte */ len = len -1; group_iter_init(&iter); while ((v = group_iterate(group, &iter)) != NULL && len > 0) { int id = convert(v); int nbytes = 0; if (first_iteration) { start = end = id; first_iteration = B_FALSE; } else if (end + 1 == id) { /* * Got consecutive ID, so extend end of range without * doing anything since the range may extend further */ end = id; } else { if (first_value) { first_value = B_FALSE; } else { *ptr++ = ','; len--; } if (len == 0) break; /* * Next ID is not consecutive, so dump IDs gotten so * far. */ if (end > start + 1) /* range */ nbytes = snprintf(ptr, len, "%d-%d", start, end); else if (end > start) /* different values */ nbytes = snprintf(ptr, len, "%d,%d", start, end); else /* same value */ nbytes = snprintf(ptr, len, "%d", start); if (nbytes <= 0) { len = 0; break; } /* * Advance position in the string */ ptr += nbytes; len -= nbytes; /* * Try finding consecutive range starting from current * ID. */ start = end = id; } } if (!first_value) { *ptr++ = ','; len--; } /* * Print last ID(s) */ if (len > 0) { if (end > start + 1) { (void) snprintf(ptr, len, "%d-%d", start, end); } else if (end != start) { (void) snprintf(ptr, len, "%d,%d", start, end); } else { (void) snprintf(ptr, len, "%d", start); } } return (buffer); }