#include <u.h>
#include <libc.h>
#include "common.h"
#include "debug.h"
#include "collection.h"
#include "function.h"
#include "array.h"
#include "set.h"
enum { DEBUG_LIST = false };
typedef struct List List;
struct List
{
void *item;
List *next;
};
List *list_add(List *self, void *p)
{
List *newitem = (List *)emalloc_fs(sizeof(*newitem));
newitem->item = p;
newitem->next = self;
return newitem;
}
void list_do(List *self, collectioneach each, void *arg)
{
if(self == nil || each(self->item, arg) == COLLECTION_DO_STOP)
{
return;
}
list_do(self->next, each, arg);
}
void list_unary_do(List *self, functionunary each)
{
if(self == nil)
{
return;
}
each(self->item);
list_unary_do(self->next, each);
}
void list_free(List *self)
{
if(self == nil)
{
return;
}
list_free(self->next);
self->next = nil;
free(self);
}
/**
* @param isremoved is only set if it's removed, and nothing if it's not.
*/
List *list_remove(List *self,
void *p, collectionisequal isequal, bool *isremoved)
{
NOISE(DEBUG_LIST,
"entering list_remove with list: 0x%uX p: 0x%uX", self, p);
if(self == nil)
{
return nil;
}
if(isequal(self->item, p))
{
List *result = self->next;
NOISE(DEBUG_LIST, "list_remove found item");
free(self);
if(isremoved != nil)
{
*isremoved = true;
}
NOISE(DEBUG_LIST, "list_remove found return");
return result;
}
self->next = list_remove(self->next, p, isequal, isremoved);
return self;
}
bool list_find(List *self, void **p, collectionisequal isequal)
{
assert_valid(p);
assert_valid(isequal);
NOISE(DEBUG_LIST, "entering list_find");
if(self == nil)
{
return false;
}
if(isequal(self->item, *p))
{
*p = self->item;
return true;
}
return list_find(self->next, p, isequal);
}
bool list_includes(List *self, void *p, collectionisequal isequal)
{
return list_find(self, &p, isequal);
}
enum
{
DEBUG_SET = false,
SET_DEFAULT_SIZE = 11
};
#define SET_ITEM_SLOT_RATIO_THRESHOLD 0.75
struct Set
{
collectionhash hash;
collectionisequal isequal;
int total;
Array *elements;
};
static Array *set_elements(Set *self)
{
return self->elements;
}
static uint set_array_size(Set *self)
{
return array_size(set_elements(self));
}
static uint set_index_for(Set *self, void *p)
{
return self->hash(p) % set_array_size(self);
}
static void set_init(Set *self, collectionisequal isequal, collectionhash hash)
{
self->isequal = isequal;
self->hash = hash;
self->total = 0;
self->elements = array_new_size(SET_DEFAULT_SIZE);
array_resize(set_elements(self), SET_DEFAULT_SIZE);
}
Set *set_new(collectionisequal isequal, collectionhash hash)
{
Set *result;
assert_valid(isequal);
assert_valid(hash);
result = (Set *)emalloc_fs(sizeof(*result));
set_init(result, isequal, hash);
return result;
}
static void set_free_chains(Array *chains)
{
array_unary_do(chains, (functionunary)list_free);
array_free(chains);
}
static void set_destroy(Set *self)
{
set_free_chains(set_elements(self));
}
void set_free(Set *self)
{
if(self == nil)
{
return;
}
set_destroy(self);
free(self);
}
void set_free_with(Set *self, functionunary free_each)
{
set_unary_do(self, free_each);
set_free(self);
}
uint set_size(Set *self)
{
assert_valid(self);
return self->total;
}
static uint set_items_threshold(Set *self)
{
return set_array_size(self) * SET_ITEM_SLOT_RATIO_THRESHOLD;
}
static bool set_issaturated(Set *self)
{
return set_size(self) >= set_items_threshold(self);
}
static uint set_next_capacity(Set *self)
{
return set_size(self) * 2 + 1;
}
static collection_do_ret set_rehash_each(void *p, void *arg)
{
assert_valid(p);
assert_valid(arg);
set_add((Set *)arg, p);
return COLLECTION_DO_CONTINUE;
}
static collection_do_ret set_rehash_chain_each(void *p, void *arg)
{
list_do((List *)p, set_rehash_each, arg);
return COLLECTION_DO_CONTINUE;
}
static void set_grow(Set *self)
{
uint newsize;
Array *old;
newsize = set_next_capacity(self);
old = self->elements;
self->elements = array_new_size(newsize);
array_resize(set_elements(self), newsize);
self->total = 0;
array_do(old, set_rehash_chain_each, self);
set_free_chains(old);
}
static void set_grow_if_saturated(Set *self)
{
if(set_issaturated(self))
{
set_grow(self);
}
}
void set_add(Set *self, void *p)
{
uint index;
assert_valid(self);
NOISE(DEBUG_SET, "entering set_add with self: 0x%uX p: 0x%uX", self, p);
if(set_includes(self, p))
{
NOISE(DEBUG_SET, "set_add already has it");
return;
}
set_grow_if_saturated(self);
index = set_index_for(self, p);
NOISE(DEBUG_SET, "set_add index: %d", index);
array_put(set_elements(self), index,
list_add((List *)array_at(set_elements(self), index), p));
++(self->total);
}
bool set_find(Set *self, void **p)
{
assert_valid(self);
assert_valid(p);
NOISE(DEBUG_SET, "entering set_find with self: 0x%uX p: 0x%uX", self, p);
return list_find(array_at(set_elements(self), set_index_for(self, *p)),
p, self->isequal);
}
bool set_includes(Set *self, void *p)
{
return set_find(self, &p);
}
bool set_remove(Set *self, void *p)
{
bool result = false;
uint index;
assert_valid(self);
NOISE(DEBUG_SET, "entering set_remove with self: 0x%uX p: 0x%uX", self, p);
index = set_index_for(self, p);
NOISE(DEBUG_SET, "set_remove got index: %u", index);
array_put(set_elements(self), index,
list_remove(array_at(set_elements(self), index),
p, self->isequal, &result));
if(result)
{
--(self->total);
}
return result;
}
typedef struct SetDoData
{
collectioneach each;
void *arg;
collection_do_ret ret;
} SetDoData;
collection_do_ret set_do_each(void *p, void *arg)
{
SetDoData *data = (SetDoData *)arg;
assert_valid(data);
data->ret = data->each(p, data->arg);
return data->ret;
}
collection_do_ret set_do_chain_each(void *p, void *arg)
{
SetDoData *data = (SetDoData *)arg;
assert_valid(data);
list_do((List *)p, set_do_each, arg);
return data->ret;
}
void set_do(Set *self, collectioneach each, void *arg)
{
SetDoData data = (SetDoData){each, arg, COLLECTION_DO_CONTINUE};
assert_valid(self);
assert_valid(each);
array_do(set_elements(self), set_do_chain_each, &data);
}
collection_do_ret set_unary_do_each(void *p, void *arg)
{
list_unary_do((List *)p, (functionunary)arg);
return COLLECTION_DO_CONTINUE;
}
void set_unary_do(Set *self, functionunary each)
{
assert_valid(self);
assert_valid(each);
array_do(set_elements(self), set_unary_do_each, (void *)each);
}
|