/* vim: set expandtab ts=4 sw=4: */
/*
* You may redistribute this program and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#define Set_NOCREATE
#include "util/Set.h"
#include "util/Bits.h"
#include
#include
#include
struct Entry {
void* data;
uint32_t hashCode;
struct Allocator* alloc;
struct Set_pvt* set;
struct {
struct Entry* rbe_left;
struct Entry* rbe_right;
struct Entry* rbe_parent;
int rbe_color;
} tree;
};
#define BLOCK_SZ 8
struct Block {
int usedCount;
int number;
struct Block* next;
struct Allocator* alloc;
struct Entry entries[BLOCK_SZ];
};
struct Set_Iter
{
void* val;
struct Entry* entry;
};
struct Set_pvt
{
int size;
struct Allocator* alloc;
struct Block* block;
struct ActiveTree {
struct Entry* rbh_root;
} activeTree;
struct FreeTree {
struct Entry* rbh_root;
} freeTree;
Set_Compare_t compare;
Set_HashCode_t hashCode;
Identity
};
static int compare(const struct Entry* a, const struct Entry* b)
{
if (a->hashCode != b->hashCode) { return a->hashCode - b->hashCode; }
struct Set_pvt* set = Identity_check((struct Set_pvt*) a->set);
return set->compare(a->data, b->data);
}
RB_GENERATE_STATIC(ActiveTree, Entry, tree, compare)
static int freeCompare(const struct Entry* a, const struct Entry* b)
{
return a->hashCode - b->hashCode;
}
RB_GENERATE_STATIC(FreeTree, Entry, tree, freeCompare)
static struct Block* blockForEntry(const struct Set_pvt* set, const struct Entry* e)
{
struct Block* b = set->block;
uintptr_t ep = (uintptr_t) e;
while (b) {
if (ep >= (uintptr_t)&b->entries && ep < (uintptr_t)&b->entries[BLOCK_SZ]) { return b; }
b = b->next;
}
Assert_failure("No matching block for entry");
}
static struct Entry* allocateBlock(struct Set_pvt* set)
{
struct Allocator* alloc = Allocator_child(set->alloc);
struct Block* newBlock = Allocator_calloc(alloc, sizeof(struct Block), 1);
newBlock->alloc = alloc;
newBlock->next = set->block;
set->block = newBlock;
uint32_t num = newBlock->number = (set->block ? set->block->number : -1) + 1;
for (int i = 0; i < BLOCK_SZ; i++) {
newBlock->entries[i].set = set;
newBlock->entries[i].hashCode = num;
FreeTree_RB_INSERT(&set->freeTree, &newBlock->entries[i]);
}
return &newBlock->entries[0];
}
static struct Entry* newEntry(struct Set_pvt* set)
{
struct Entry* e = RB_MIN(FreeTree, &set->freeTree);
if (!e) { e = allocateBlock(set); }
FreeTree_RB_REMOVE(&set->freeTree, e);
struct Block* b = blockForEntry(set, e);
b->usedCount++;
set->size++;
return e;
}
static void freeBlock(struct Set_pvt* set, struct Block* b)
{
for (int i = 0; i < BLOCK_SZ; i++) {
FreeTree_RB_REMOVE(&set->freeTree, &b->entries[i]);
}
int ok = 0;
for (struct Block** bp = &set->block; *bp; bp = &(*bp)->next) {
if (*bp == b) {
*bp = b->next;
ok = 1;
break;
}
}
Assert_true(ok && "INTERNAL: block was not linked into linked list");
Allocator_free(b->alloc);
}
static void freeEntry(struct Set_pvt* set, struct Entry* e)
{
struct Block* b = blockForEntry(set, e);
ActiveTree_RB_REMOVE(&set->activeTree, e);
e->data = NULL;
e->hashCode = b->number;
if (e->alloc) {
Allocator_free(e->alloc);
e->alloc = NULL;
}
FreeTree_RB_INSERT(&set->freeTree, e);
b->usedCount--;
set->size--;
if (!b->usedCount) { freeBlock(set, b); }
}
static struct Entry* get(struct Set_pvt* set, void* val, uint32_t hashCode)
{
struct Entry e = {
.hashCode = hashCode,
.data = val,
.set = set
};
return ActiveTree_RB_FIND(&set->activeTree, &e);
}
int Set_addCopy(struct Set* _set, void* val, uint32_t size)
{
struct Set_pvt* set = Identity_check((struct Set_pvt*) _set);
uint32_t hashCode = set->hashCode(val);
struct Entry* e = get(set, val, hashCode);
if (!e) {
struct Entry* e = newEntry(set);
e->hashCode = hashCode;
ActiveTree_RB_INSERT(&set->activeTree, e);
struct Block* b = blockForEntry(set, e);
e->alloc = Allocator_child(b->alloc);
e->data = Allocator_malloc(e->alloc, size);
Bits_memcpy(e->data, val, size);
}
return set->size;
}
int Set_add(struct Set* _set, void* val)
{
struct Set_pvt* set = Identity_check((struct Set_pvt*) _set);
uint32_t hashCode = set->hashCode(val);
struct Entry* e = get(set, val, hashCode);
if (!e) {
struct Entry* e = newEntry(set);
e->data = val;
e->hashCode = hashCode;
ActiveTree_RB_INSERT(&set->activeTree, e);
}
return set->size;
}
void Set_iter(struct Set* _set, struct Set_Iter* iter)
{
struct Set_pvt* set = Identity_check((struct Set_pvt*) _set);
struct Entry* e = iter->entry = RB_MIN(ActiveTree, &set->activeTree);
if (e) {
iter->val = e->data;
iter->entry = ActiveTree_RB_NEXT(e);
} else {
iter->val = NULL;
}
}
void Set_iterNext(struct Set_Iter* iter)
{
struct Entry* e = iter->entry;
if (e) {
iter->val = e->data;
iter->entry = ActiveTree_RB_NEXT(e);
} else {
iter->val = NULL;
}
}
void* Set_remove(struct Set* _set, void* val)
{
struct Set_pvt* set = Identity_check((struct Set_pvt*) _set);
struct Entry* e = get(set, val, set->hashCode(val));
void* out = NULL;
if (e) {
out = e->data;
freeEntry(set, e);
}
return out;
}
void* Set_get(struct Set* _set, void* val)
{
struct Set_pvt* set = Identity_check((struct Set_pvt*) _set);
struct Entry* e = get(set, val, set->hashCode(val));
void* out = NULL;
if (e) { out = e->data; }
return out;
}
struct Set* Set_new(struct Allocator* alloc, Set_HashCode_t hashCode, Set_Compare_t compare)
{
struct Set_pvt* out = Allocator_calloc(alloc, sizeof(struct Set_pvt), 1);
Identity_set(out);
out->alloc = alloc;
out->compare = compare;
out->hashCode = hashCode;
return (struct Set*) out;
}