428 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*  $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)
 | |
| { 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)
 | |
| { 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)
 | |
| {
 | |
| #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()
 | |
| { 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; i<ht->buckets; 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)
 | |
| { 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; i<oldbucks; i++)
 | |
|   { Symbol s, n;
 | |
| 
 | |
|     for(s=oldtab[i]; s; s = n)
 | |
|     { int v = (int)pointerHashValue(s->name, 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)
 | |
| { 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)
 | |
| { 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)
 | |
| { 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)
 | |
| { 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)
 | |
| { 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)
 | |
| { 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;
 | |
| }
 |