mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-20 04:30:55 -08:00
321 lines
8.1 KiB
C
321 lines
8.1 KiB
C
/* table.h: A dictionary mapping a Word to a void*
|
|
*
|
|
* $Id$
|
|
* Copyright (c) 2001 Ravenbrook Limited. See end of file for license.
|
|
*
|
|
* .note.good-hash: As is common in hash table implementations, we
|
|
* assume that the hash function is good.
|
|
*/
|
|
|
|
#include "table.h"
|
|
#include "mpmtypes.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include "mpstd.h"
|
|
#ifdef MPS_OS_SU
|
|
#include "ossu.h"
|
|
#endif
|
|
|
|
|
|
typedef unsigned long ulong;
|
|
|
|
|
|
#define tableUNUSED ((Word)0x2AB7E040)
|
|
#define tableDELETED ((Word)0x2AB7EDE7)
|
|
#define tableACTIVE ((Word)0x2AB7EAC2)
|
|
|
|
|
|
typedef struct TableEntryStruct *TableEntry;
|
|
typedef struct TableEntryStruct {
|
|
Word status;
|
|
Word key;
|
|
void *value;
|
|
} TableEntryStruct;
|
|
|
|
|
|
typedef struct TableStruct {
|
|
size_t length;
|
|
size_t count;
|
|
size_t limit;
|
|
TableEntry array;
|
|
} TableStruct;
|
|
|
|
|
|
|
|
/* sizeFloorLog2 -- logarithm base 2 */
|
|
|
|
static size_t sizeFloorLog2(size_t size)
|
|
{
|
|
size_t l = 0;
|
|
|
|
assert(size != 0);
|
|
while(size > 1) {
|
|
++l;
|
|
size >>= 1;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
|
|
/* TableHash -- table hashing function */
|
|
|
|
static ulong TableHash(Word key)
|
|
{
|
|
/* Shift some randomness into the low bits. */
|
|
return (key >> 10) + key;
|
|
}
|
|
|
|
|
|
/* TableFind -- finds the entry for this key, or NULL
|
|
*
|
|
* .worst: In the worst case, this looks at every slot before giving up,
|
|
* but that's what you have to do in a closed hash table, to make sure
|
|
* that all the items still fit in after growing the table.
|
|
*/
|
|
|
|
static TableEntry TableFind(Table table, Word key, int skip_deleted)
|
|
{
|
|
ulong hash;
|
|
size_t i, mask = table->length - 1;
|
|
|
|
hash = TableHash(key) & mask;
|
|
i = hash;
|
|
do {
|
|
switch (table->array[i].status) {
|
|
case tableACTIVE:
|
|
if (table->array[i].key == key)
|
|
return &table->array[i];
|
|
break;
|
|
case tableDELETED:
|
|
if (!skip_deleted)
|
|
return &table->array[i];
|
|
break;
|
|
case tableUNUSED:
|
|
return &table->array[i];
|
|
break;
|
|
}
|
|
i = (i + 1) & mask;
|
|
} while(i != hash);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* TableGrow -- doubles the size of the table */
|
|
|
|
static Res TableGrow(Table table)
|
|
{
|
|
TableEntry oldArray, newArray;
|
|
size_t i, oldLength, newLength;
|
|
|
|
oldLength = table->length;
|
|
oldArray = table->array;
|
|
newLength = oldLength * 2;
|
|
newArray = malloc(sizeof(TableEntryStruct) * newLength);
|
|
if(newArray == NULL) return ResMEMORY;
|
|
|
|
for(i = 0; i < newLength; ++i) {
|
|
newArray[i].key = 0;
|
|
newArray[i].value = NULL;
|
|
newArray[i].status = tableUNUSED;
|
|
}
|
|
|
|
table->length = newLength;
|
|
table->array = newArray;
|
|
table->limit *= 2;
|
|
|
|
for(i = 0; i < oldLength; ++i) {
|
|
if (oldArray[i].status == tableACTIVE) {
|
|
TableEntry entry;
|
|
entry = TableFind(table, oldArray[i].key, 0 /* none deleted */);
|
|
assert(entry != NULL);
|
|
assert(entry->status == tableUNUSED);
|
|
entry->key = oldArray[i].key;
|
|
entry->value = oldArray[i].value;
|
|
entry->status = tableACTIVE;
|
|
}
|
|
}
|
|
free(oldArray);
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* TableCreate -- makes a new table */
|
|
|
|
extern Res TableCreate(Table *tableReturn, size_t length)
|
|
{
|
|
Table table;
|
|
size_t i;
|
|
|
|
assert(tableReturn != NULL);
|
|
|
|
table = malloc(sizeof(TableStruct));
|
|
if(table == NULL) goto failMallocTable;
|
|
if (length < 2) length = 2;
|
|
/* Table size is length rounded up to the next power of 2. */
|
|
table->length = 1 << (sizeFloorLog2(length-1) + 1);
|
|
table->count = 0;
|
|
table->limit = (size_t)(.5 * length);
|
|
table->array = malloc(sizeof(TableEntryStruct) * length);
|
|
if(table->array == NULL) goto failMallocArray;
|
|
for(i = 0; i < length; ++i) {
|
|
table->array[i].key = 0;
|
|
table->array[i].value = NULL;
|
|
table->array[i].status = tableUNUSED;
|
|
}
|
|
|
|
*tableReturn = table;
|
|
return ResOK;
|
|
|
|
failMallocArray:
|
|
free(table);
|
|
failMallocTable:
|
|
return ResMEMORY;
|
|
}
|
|
|
|
|
|
/* TableDestroy -- destroy a table */
|
|
|
|
extern void TableDestroy(Table table)
|
|
{
|
|
assert(table != NULL);
|
|
free(table->array);
|
|
free(table);
|
|
}
|
|
|
|
|
|
/* TableLookup -- look up */
|
|
|
|
extern Bool TableLookup(void **valueReturn, Table table, Word key)
|
|
{
|
|
TableEntry entry = TableFind(table, key, 1 /* skip deleted */);
|
|
|
|
if(entry == NULL || entry->status != tableACTIVE)
|
|
return FALSE;
|
|
*valueReturn = entry->value;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/* TableDefine -- add a new mapping */
|
|
|
|
extern Res TableDefine(Table table, Word key, void *value)
|
|
{
|
|
TableEntry entry;
|
|
|
|
if (table->count >= table->limit) {
|
|
Res res = TableGrow(table);
|
|
if(res != ResOK) return res;
|
|
entry = TableFind(table, key, 0 /* no deletions yet */);
|
|
assert(entry != NULL);
|
|
if (entry->status == tableACTIVE)
|
|
return ResFAIL;
|
|
} else {
|
|
entry = TableFind(table, key, 1 /* skip deleted */);
|
|
if (entry != NULL && entry->status == tableACTIVE)
|
|
return ResFAIL;
|
|
/* Search again to find the best slot, deletions included. */
|
|
entry = TableFind(table, key, 0 /* don't skip deleted */);
|
|
assert(entry != NULL);
|
|
}
|
|
|
|
entry->status = tableACTIVE;
|
|
entry->key = key;
|
|
entry->value = value;
|
|
++table->count;
|
|
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* TableRedefine -- redefine an existing mapping */
|
|
|
|
extern Res TableRedefine(Table table, Word key, void *value)
|
|
{
|
|
TableEntry entry = TableFind(table, key, 1 /* skip deletions */);
|
|
|
|
if (entry == NULL || entry->status != tableACTIVE)
|
|
return ResFAIL;
|
|
assert(entry->key == key);
|
|
entry->value = value;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* TableRemove -- remove a mapping */
|
|
|
|
extern Res TableRemove(Table table, Word key)
|
|
{
|
|
TableEntry entry = TableFind(table, key, 1);
|
|
|
|
if (entry == NULL || entry->status != tableACTIVE)
|
|
return ResFAIL;
|
|
entry->status = tableDELETED;
|
|
--table->count;
|
|
return ResOK;
|
|
}
|
|
|
|
|
|
/* TableMap -- apply a function to all the mappings */
|
|
|
|
extern void TableMap(Table table, void(*fun)(Word key, void*value))
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < table->length; i++)
|
|
if (table->array[i].status == tableACTIVE)
|
|
(*fun)(table->array[i].key, table->array[i].value);
|
|
}
|
|
|
|
|
|
/* TableCount -- count the number of mappings in the table */
|
|
|
|
extern size_t TableCount(Table table)
|
|
{
|
|
return table->count;
|
|
}
|
|
|
|
|
|
/* C. COPYRIGHT AND LICENSE
|
|
*
|
|
* Copyright (C) 2001-2002 Ravenbrook Limited <http://www.ravenbrook.com/>.
|
|
* All rights reserved. This is an open source license. Contact
|
|
* Ravenbrook for commercial licensing options.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Redistributions in any form must be accompanied by information on how
|
|
* to obtain complete source code for this software and any accompanying
|
|
* software that uses this software. The source code must either be
|
|
* included in the distribution or be available for no more than the cost
|
|
* of distribution plus a nominal fee, and must be freely redistributable
|
|
* under reasonable conditions. For an executable file, complete source
|
|
* code means the source code for all modules it contains. It does not
|
|
* include source code for modules or files that typically accompany the
|
|
* major components of the operating system on which the executable file
|
|
* runs.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
|
* PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
|
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|