#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/param.h>
#include <sys/debug.h>
#include <sys/kmem.h>
#include <sys/group.h>
#include <sys/cmn_err.h>
#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);
void
group_create(group_t *g)
{
bzero(g, sizeof (group_t));
}
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;
}
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;
}
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);
}
int
group_remove(group_t *g, void *e, int gflag)
{
int i;
if (g->grp_size == 0)
return (-1);
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);
}
void
group_expand(group_t *g, uint_t n)
{
while (g->grp_capacity < n)
group_grow_set(g);
}
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;
if ((cap_new = (cap_old << 1)) == 0) {
cap_new = GRP_SET_SIZE_DEFAULT;
g->grp_set = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP);
g->grp_capacity = cap_new;
} else {
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 *));
}
ASSERT(((cap_new - 1) & cap_new) == 0);
}
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;
ASSERT(((cap_old - 1) & cap_old) == 0);
cap_new = cap_old >> 1;
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 *));
}
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) {
free = i;
} else if (set[i] != NULL && free != (uint_t)-1) {
ASSERT(set[free] == NULL);
set[free] = set[i];
set[i] = NULL;
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;
}
}
}
void
group_iter_init(group_iter_t *iter)
{
*iter = 0;
}
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);
}
void *
group_access_at(group_t *g, uint_t idx)
{
if (idx >= g->grp_capacity)
return (NULL);
return (g->grp_set[idx]);
}
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);
}
void
group_remove_at(group_t *g, uint_t idx)
{
ASSERT(idx < g->grp_capacity);
g->grp_set[idx] = NULL;
}
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);
}
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;
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) {
end = id;
} else {
if (first_value) {
first_value = B_FALSE;
} else {
*ptr++ = ',';
len--;
}
if (len == 0)
break;
if (end > start + 1)
nbytes = snprintf(ptr, len, "%d-%d",
start, end);
else if (end > start)
nbytes = snprintf(ptr, len, "%d,%d",
start, end);
else
nbytes = snprintf(ptr, len, "%d", start);
if (nbytes <= 0) {
len = 0;
break;
}
ptr += nbytes;
len -= nbytes;
start = end = id;
}
}
if (!first_value) {
*ptr++ = ',';
len--;
}
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);
}