/* 6D programming language Copyright (C) 2011 Danny Milosavljevic This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "6D/Allocators" #include "Values/Values" #include "Values/Hashtable" static INLINE const struct HashtableEntry* Hashtable_findEntry(const struct Hashtable* self, NodeT key) { if(self->capacity == 0) return(NULL); int index = hash(key) % self->capacity; struct HashtableEntry* entry = &self->entries[index]; if(entry->state == HS_FREE) return(NULL); if(entry->state != HS_OCCUPIED || !equalP(entry->first, key)) { int c; for(c = 0; c < self->capacity; ++c) { ++index; if(index >= self->capacity) index = 0; entry = &self->entries[index]; if(entry->state == HS_FREE) return(NULL); if(equalP(entry->first, key)) break; } } bool bMatches = equalP(entry->first, key); return(bMatches ? entry : NULL); } static struct HashtableEntry* Hashtable_actualPut(struct Hashtable* self, NodeT key, NodeT value); static void Hashtable_growTo(struct Hashtable* self, int count) { if(count > self->capacity) { int i; const struct HashtableEntry* oldEntries = self->entries; int oldCapacity = self->capacity; self->capacity = (1 << ((int)log2((double) count) + 1)) + 1; if(self->bGC) self->entries = GC_MALLOC(sizeof(struct HashtableEntry)*self->capacity); else self->entries = GC_MALLOC_UNCOLLECTABLE(sizeof(struct HashtableEntry)*self->capacity); for(i = 0; i < self->capacity; ++i) self->entries[i].state = HS_FREE; for(i = 0; i < oldCapacity; ++i) { const struct HashtableEntry* entry = &oldEntries[i]; if(entry->state == HS_OCCUPIED) (void) Hashtable_actualPut(self, entry->first, entry->second); } } } static struct HashtableEntry* Hashtable_actualPut(struct Hashtable* self, NodeT key, NodeT value) { struct HashtableEntry* entry = (struct HashtableEntry*) Hashtable_findEntry(self, key); if(!entry) { Hashtable_growTo(self, self->count + 1); int index = hash(key) % self->capacity; entry = &self->entries[index]; if(entry->state == HS_OCCUPIED) { int c; for(c = 0; c < self->capacity; ++c) { ++index; if(index >= self->capacity) index = 0; entry = &self->entries[index]; if(entry->state != HS_OCCUPIED) break; } if(c == self->capacity) { /* oops. completely full. This shouldn't happen */ abort(); } } assert(entry->state != HS_OCCUPIED); ++self->count; } entry->state = HS_OCCUPIED; entry->first = key; entry->second = value; return(entry); } static void Hashtable_clear(struct Hashtable* self) { self->entries = nil; self->capacity = 0; self->count = 0; } static void Hashtable_init1(struct Hashtable* self, bool bGC) { self->bGC = bGC; Hashtable_clear(self); } static int Hashtable_getSize(const struct Hashtable* self) { return self->count; } /* begin iterating HashtableEntry* entries; int i; for(i = 0; i < capacity && entries[i].state != HS_OCCUPIED; ++i) ; return(const_iterator(&entries[i], capacity - i)); next assert(remainder > 0); for(++entry, --remainder; remainder > 0 && entry->state != HS_OCCUPIED; ++entry, --remainder) ; */ static void Hashtable_put(struct Hashtable* self, NodeT key, NodeT value) { (void) Hashtable_actualPut(self, key, value); } static bool Hashtable_emptyP(const struct Hashtable* self) { return self->count == 0; } static bool Hashtable_containsKeyP(const struct Hashtable* self, NodeT key) { return Hashtable_findEntry(self, key) != NULL; } static void Hashtable_removeByKey(struct Hashtable* self, NodeT key) { struct HashtableEntry* entry = (struct HashtableEntry*) Hashtable_findEntry(self, key); if(entry) entry->state = HS_FREE_AGAIN; // TODO shrink }