export default function(size, hash, equal, keyType, keyEmpty, valueType) { if (arguments.length === 3) { keyType = valueType = Array; keyEmpty = null; } var keystore = new keyType(size = 1 << Math.max(4, Math.ceil(Math.log(size) / Math.LN2))), valstore = new valueType(size), mask = size - 1; for (var i = 0; i < size; ++i) { keystore[i] = keyEmpty; } function set(key, value) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index] = value; if (++collisions >= size) throw new Error("full hashmap"); matchKey = keystore[index = (index + 1) & mask]; } keystore[index] = key; valstore[index] = value; return value; } function maybeSet(key, value) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index]; if (++collisions >= size) throw new Error("full hashmap"); matchKey = keystore[index = (index + 1) & mask]; } keystore[index] = key; valstore[index] = value; return value; } function get(key, missingValue) { var index = hash(key) & mask, matchKey = keystore[index], collisions = 0; while (matchKey != keyEmpty) { if (equal(matchKey, key)) return valstore[index]; if (++collisions >= size) break; matchKey = keystore[index = (index + 1) & mask]; } return missingValue; } function keys() { var keys = []; for (var i = 0, n = keystore.length; i < n; ++i) { var matchKey = keystore[i]; if (matchKey != keyEmpty) keys.push(matchKey); } return keys; } return { set: set, maybeSet: maybeSet, // set if unset get: get, keys: keys }; }