// Datastructures: GAP package providing common datastructures. // // Copyright (C) 2015-2017 The datastructures team. // For list of the team members, please refer to the COPYRIGHT file. // // This package is licensed under the GPL 2 or later, please refer // to the COPYRIGHT.md and LICENSE files for details. //
#include"hashmap.h"
enum { // offsets in the hashmap positional object
POS_HASHFUNC = 1,
POS_EQFUNC,
POS_USED, // number of used slots
POS_DELETED, // number of deleted slots
POS_KEYS,
POS_VALUES,
HASH_SET_SIZE = POS_KEYS + 1, // one more for type
HASH_MAP_SIZE = POS_VALUES + 1, // one more for type
// various constants used to tweak the hashmap
PERTURB_SHIFT = 5,
LOADFACTOR_NUMERATOR = 7,
LOADFACTOR_DENOMINATOR = 10
};
static Obj HashMapType; // Imported from the library static Obj IsHashMapRep; // Imported from the library static Obj HashSetType; // Imported from the library static Obj IsHashSetRep; // Imported from the library
// // Resize the keys and values stores to a new capacity. // // The caller is responsible for checking that the new capacity // is sufficient to allow all elements to be stored, and that // it is a power of 2. // staticvoid _DS_Hash_Resize_intern(Obj ht, UInt new_capacity)
{
GAP_ASSERT(new_capacity >= 16);
GAP_ASSERT(0 == (new_capacity & (new_capacity - 1))); // power of 2?
// copy data into new hash
UInt new_size = 0; const UInt mask = new_capacity - 1; for (UInt old_idx = 1; old_idx <= old_capacity; ++old_idx) {
Obj k = ELM_PLIST(old_keys, old_idx); if (k == 0 || k == Fail) continue;
Obj hash = CALL_1ARGS(hashfun, k); if (!IS_INTOBJ(hash))
ErrorQuit(" must return a small int (not a %s)",
(Int)TNAM_OBJ(hash), 0L);
// Insert the element from the old table into the new table. // Since we know that no key exists twice in the old table, we // can do this slightly better than by calling lookup, since we // don't have to call _equal().
SET_ELM_PLIST(keys, idx, k); if (old_vals) {
Obj v = ELM_PLIST(old_vals, old_idx);
SET_ELM_PLIST(values, idx, v);
}
new_size++;
}
// Strictly speaking, we should call CHANGED_BAG inside the loop. However, // as we copy elements from existing lists, all the objects are already // known to GASMAN. So it is safe to delay the notification as no objects // can be lost.
CHANGED_BAG(keys); if (values)
CHANGED_BAG(values);
if (old_size != new_size)
ErrorQuit("PANIC: unexpected size change (was %d, now %d)", old_size,
new_size);
// ... and store the result
SET_ELM_PLIST(ht, POS_USED, INTOBJ_INT(new_size));
SET_ELM_PLIST(ht, POS_DELETED, INTOBJ_INT(0));
SET_ELM_PLIST(ht, POS_KEYS, keys); if (values)
SET_ELM_PLIST(ht, POS_VALUES, values);
CHANGED_BAG(ht);
}
// This helper function check if the table is very full, and if so, // reallocates it. This may increase the capacity, but not necessarily: // if the table contains many deleted items, the capacity could stay // unchanged. staticvoid _DS_GrowIfNecessary(Obj ht)
{
UInt used = INT_INTOBJ(ELM_PLIST(ht, POS_USED));
UInt deleted = INT_INTOBJ(ELM_PLIST(ht, POS_DELETED));
static Obj _DS_Hash_SetOrAccValue(Obj ht, Obj key, Obj val, Obj accufunc)
{ if (key == Fail)
ErrorQuit(" must not be equal to 'fail'", 0L, 0L); if (val == Fail)
ErrorQuit(" must not be equal to 'fail'", 0L, 0L);
staticvoid DS_RequireHashMapOrSet(Obj ht)
{ if (TNUM_OBJ(ht) != T_POSOBJ ||
(DoFilter(IsHashSetRep, ht) == False &&
DoFilter(IsHashMapRep, ht) == False)) {
ErrorQuit(" must be a hashmap or hashset (not a %s)",
(Int)TNAM_OBJ(ht), 0);
}
}
staticvoid DS_RequireHashSet(Obj ht)
{ if (TNUM_OBJ(ht) != T_POSOBJ || DoFilter(IsHashSetRep, ht) == False) {
ErrorQuit(" must be a hashset (not a %s)", (Int)TNAM_OBJ(ht), 0);
}
}
staticvoid DS_RequireHashMap(Obj ht)
{ if (TNUM_OBJ(ht) != T_POSOBJ || DoFilter(IsHashMapRep, ht) == False) {
ErrorQuit(" must be a hashmap object (not a %s)",
(Int)TNAM_OBJ(ht), 0);
}
}
staticvoid DS_RequireMutable(Obj ht)
{ if (!IS_MUTABLE_OBJ(ht)) {
ErrorQuit(" must be a mutable hashmap or hashset",
0, 0);
}
}
// // high-level functions, to be called from GAP //
static Obj FuncDS_Hash_Create(Obj self, Obj hashfunc, Obj eqfunc, Obj capacity, Obj novalues)
{ if (TNUM_OBJ(hashfunc) != T_FUNCTION) {
ErrorQuit(" must be a function (not a %s)",
(Int)TNAM_OBJ(hashfunc), 0);
} if (TNUM_OBJ(eqfunc) != T_FUNCTION) {
ErrorQuit(" must be a function (not a %s)",
(Int)TNAM_OBJ(eqfunc), 0);
} if (!IS_POS_INTOBJ(capacity)) {
ErrorQuit(" must be a small positive integer (not a %s)",
(Int)TNAM_OBJ(capacity), 0);
} if (novalues != True && novalues != False) {
ErrorQuit(" must be true or false (not a %s)",
(Int)TNAM_OBJ(novalues), 0);
}
// convert capacity into integer and round up to a power of 2
UInt requestedCapacity = INT_INTOBJ(capacity);
UInt c = 16; while (c < requestedCapacity)
c <<= 1;
static Obj FuncDS_Hash_Reserve(Obj self, Obj ht, Obj new_capacity)
{
DS_RequireHashMapOrSet(ht);
DS_RequireMutable(ht); if (!IS_POS_INTOBJ(new_capacity)) {
ErrorQuit(" must be a small positive integer (not a %s)",
(Int)TNAM_OBJ(new_capacity), 0);
}
UInt c = LEN_PLIST(ELM_PLIST(ht, POS_KEYS));
UInt requestedCapacity = INT_INTOBJ(new_capacity); if (c >= requestedCapacity) return 0;
// round up to a power of 2 while (c < requestedCapacity)
c <<= 1;
// Make sure capacity is big enough to contain all its elements // while staying under the load factor
UInt used = INT_INTOBJ(ELM_PLIST(ht, POS_USED)); while (used * LOADFACTOR_DENOMINATOR > c * LOADFACTOR_NUMERATOR)
c <<= 1;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.