/* $Id$ Part of SWI-Prolog Author: Jan Wielemaker E-mail: jan@swi.psy.uva.nl WWW: http://www.swi-prolog.org Copyright (C): 1985-2002, University of Amsterdam This library 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 2.1 of the License, or (at your option) any later version. This library 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /*#define O_DEBUG 1*/ #include "pl-incl.h" #ifdef O_PLMT #define LOCK_TABLE(t) if ( t->mutex ) simpleMutexLock(t->mutex) #define UNLOCK_TABLE(t) if ( t->mutex ) simpleMutexUnlock(t->mutex) #else #define LOCK_TABLE(t) (void)0 #define UNLOCK_TABLE(t) (void)0 #endif static inline Symbol rawAdvanceTableEnum(TableEnum e); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This file provides generic hash-tables. Most of the implementation is rather straightforward. Special are the *TableEnum() functions to create, advance over and destroy enumerator objects. These objects are used to enumerate the symbols of these tables, used primarily for the pl_current_* predicates. The enumerators cause two things: (1) as intptr_t enumerators are associated, the table will not be rehashed and (2) if symbols are deleted that are referenced by an enumerator, the enumerator is automatically advanced to the next free symbol. This, in general, makes the enumeration of hash-tables safe. TODO: abort should delete any pending enumerators. This should be thread-local, as thread_exit/1 should do the same. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static void allocHTableEntries(Table ht) { GET_LD int n; Symbol *p; ht->entries = allocHeap(ht->buckets * sizeof(Symbol)); for(n=0, p = &ht->entries[0]; n < ht->buckets; n++, p++) *p = NULL; } Table newHTable(int buckets) { GET_LD Table ht; ht = allocHeap(sizeof(struct table)); ht->buckets = (buckets & ~TABLE_MASK); ht->size = 0; ht->enumerators = NULL; ht->free_symbol = NULL; ht->copy_symbol = NULL; #ifdef O_PLMT if ( (buckets & TABLE_UNLOCKED) ) ht->mutex = NULL; else { ht->mutex = allocHeap(sizeof(simpleMutex)); simpleMutexInit(ht->mutex); } #endif allocHTableEntries(ht); return ht; } void destroyHTable(Table ht) { GET_LD #ifdef O_PLMT if ( ht->mutex ) { simpleMutexDelete(ht->mutex); freeHeap(ht->mutex, sizeof(*ht->mutex)); ht->mutex = NULL; } #endif clearHTable(ht); freeHeap(ht->entries, ht->buckets * sizeof(Symbol)); freeHeap(ht, sizeof(struct table)); } #if O_DEBUG || O_HASHSTAT #define HASHSTAT(c) c static int lookups; static int cmps; void exitTables(int status, void *arg) { Sdprintf("hashstat: Anonymous tables: %d lookups using %d compares\n", lookups, cmps); } #else #define HASHSTAT(c) #endif /*O_DEBUG*/ void initTables(void) { static int done = FALSE; if ( !done ) { done = TRUE; HASHSTAT(PL_on_halt(exitTables, NULL)); } } Symbol lookupHTable(Table ht, void *name) { Symbol s = ht->entries[pointerHashValue(name, ht->buckets)]; HASHSTAT(lookups++); for( ; s; s = s->next) { HASHSTAT(cmps++); if ( s->name == name ) return s; } return NULL; } #ifdef O_DEBUG void checkHTable(Table ht) { int i; int n = 0; for(i=0; ibuckets; i++) { Symbol s; for(s=ht->entries[i]; s; s=s->next) { assert(lookupHTable(ht, s->name) == s); n++; } } assert(n == ht->size); } #endif /* MT: Locked by calling addHTable() */ static void rehashHTable(Table ht) { GET_LD Symbol *oldtab; int oldbucks; int i; oldtab = ht->entries; oldbucks = ht->buckets; ht->buckets *= 2; allocHTableEntries(ht); DEBUG(1, Sdprintf("Rehashing table %p to %d entries\n", ht, ht->buckets)); for(i=0; iname, ht->buckets); n = s->next; s->next = ht->entries[v]; ht->entries[v] = s; } } freeHeap(oldtab, oldbucks * sizeof(Symbol)); DEBUG(0, checkHTable(ht)); } Symbol addHTable(Table ht, void *name, void *value) { GET_LD Symbol s; int v; LOCK_TABLE(ht); v = (int)pointerHashValue(name, ht->buckets); if ( lookupHTable(ht, name) ) { UNLOCK_TABLE(ht); return NULL; } s = allocHeap(sizeof(struct symbol)); s->name = name; s->value = value; s->next = ht->entries[v]; ht->entries[v] = s; ht->size++; DEBUG(9, Sdprintf("addHTable(0x%x, 0x%x, 0x%x) --> size = %d\n", ht, name, value, ht->size)); if ( ht->buckets * 2 < ht->size && !ht->enumerators ) rehashHTable(ht); UNLOCK_TABLE(ht); DEBUG(1, checkHTable(ht)); return s; } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Note: s must be in the table! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void deleteSymbolHTable(Table ht, Symbol s) { GET_LD int v; Symbol *h; TableEnum e; LOCK_TABLE(ht); v = (int)pointerHashValue(s->name, ht->buckets); h = &ht->entries[v]; for( e=ht->enumerators; e; e = e->next ) { if ( e->current == s ) rawAdvanceTableEnum(e); } for( ; *h; h = &(*h)->next ) { if ( *h == s ) { *h = (*h)->next; freeHeap(s, sizeof(struct symbol)); ht->size--; break; } } UNLOCK_TABLE(ht); } void clearHTable(Table ht) { GET_LD int n; TableEnum e; LOCK_TABLE(ht); for( e=ht->enumerators; e; e = e->next ) { e->current = NULL; e->key = ht->buckets; } for(n=0; n < ht->buckets; n++) { Symbol s, q; for(s = ht->entries[n]; s; s = q) { q = s->next; if ( ht->free_symbol ) (*ht->free_symbol)(s); freeHeap(s, sizeof(struct symbol)); } ht->entries[n] = NULL; } ht->size = 0; UNLOCK_TABLE(ht); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Table copyHTable(Table org) Make a copy of a hash-table. This is used to realise the copy-on-write as defined by SharedTable. The table is copied to have exactly the same dimensions as the original. If the copy_symbol function is provided, it is called to allow duplicating the symbols name or value fields. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ Table copyHTable(Table org) { GET_LD Table ht; int n; ht = allocHeap(sizeof(struct table)); LOCK_TABLE(org); *ht = *org; /* copy all attributes */ #ifdef O_PLMT ht->mutex = NULL; #endif allocHTableEntries(ht); for(n=0; n < ht->buckets; n++) { Symbol s, *q; q = &ht->entries[n]; for(s = org->entries[n]; s; s = s->next) { Symbol s2 = allocHeap(sizeof(*s2)); *q = s2; q = &s2->next; s2->name = s->name; s2->value = s->value; if ( ht->copy_symbol ) (*ht->copy_symbol)(s2); } *q = NULL; } #ifdef O_PLMT if ( org->mutex ) { ht->mutex = allocHeap(sizeof(simpleMutex)); simpleMutexInit(ht->mutex); } #endif UNLOCK_TABLE(org); return ht; } /******************************* * ENUMERATING * *******************************/ TableEnum newTableEnum(Table ht) { GET_LD TableEnum e = allocHeap(sizeof(struct table_enum)); Symbol n; LOCK_TABLE(ht); e->table = ht; e->key = 0; e->next = ht->enumerators; ht->enumerators = e; n = ht->entries[0]; while(!n && ++e->key < ht->buckets) n=ht->entries[e->key]; e->current = n; UNLOCK_TABLE(ht); return e; } void freeTableEnum(TableEnum e) { GET_LD TableEnum *ep; Table ht; if ( !e ) return; ht = e->table; LOCK_TABLE(ht); for( ep=&ht->enumerators; *ep ; ep = &(*ep)->next ) { if ( *ep == e ) { *ep = (*ep)->next; freeHeap(e, sizeof(*e)); break; } } UNLOCK_TABLE(ht); } static inline Symbol rawAdvanceTableEnum(TableEnum e) { Symbol s, n; Table ht = e->table; if ( !(s = e->current) ) return s; n = s->next; while(!n) { if ( ++e->key >= ht->buckets ) { e->current = NULL; return s; } n=ht->entries[e->key]; } e->current = n; return s; } Symbol advanceTableEnum(TableEnum e) { Symbol s; #ifdef O_PLMT Table ht = e->table; #endif LOCK_TABLE(ht); s = rawAdvanceTableEnum(e); UNLOCK_TABLE(ht); return s; }