src/lib/collections/lookup-table.js
import Grouping from './grouping';
import Iterator from '../iteration/iterator';
import EqualityComparer from './equality-comparer';
import resize from '../utils/resize';
import extend from '../utils/extend';
import mixin from '../utils/mixin';
var emptyGrouping = new Grouping(null, []);
export default function LookupTable(comparer) {
this.size = 0;
this.slots = new Array(7);
this.buckets = new Array(7);
this.comparer = EqualityComparer.from(comparer);
}
mixin(LookupTable.prototype, {
add: function (key, value) {
this.getGrouping(key, value, true);
},
get: function (key) {
return this.getGrouping(key, null, false) || emptyGrouping;
},
contains: function (key) {
return this.getGrouping(key, null, false) !== null;
},
entries: function () {
var arr = new Array(this.size),
index = 0,
slot = null;
for (var i = 0, count = this.slots.length; i < count; i++) {
slot = this.slots[i];
if (slot !== undefined) {
arr[index++] = slot.grouping;
}
}
return arr;
},
getGrouping: function (key, value, create) {
var comparer = this.comparer,
hash = comparer.hash(key) & 0x7FFFFFFF,
bucket = hash % this.buckets.length,
index = this.buckets[bucket],
grouping = null,
slot = null;
while (index !== undefined) {
slot = this.slots[index];
if (slot.hash === hash && comparer.equals(slot.grouping.key, key)) {
grouping = slot.grouping;
break;
}
index = slot.next;
}
if (create === true) {
if (grouping === null) {
if (this.size === this.slots.length) {
this.resize();
bucket = hash % this.buckets.length;
}
index = this.size;
this.size++;
grouping = new Grouping(key, [value]);
this.slots[index] = new LookupTableSlot(hash, grouping, this.buckets[bucket]);
this.buckets[bucket] = index;
}
else {
grouping.elements.push(value);
}
}
return grouping;
},
resize: function () {
var size = this.size,
newSize = resize(size),
slot = null,
bucket = 0;
this.slots.length = newSize;
this.buckets = new Array(newSize);
// rehash values & update buckets and slots
for (var index = 0; index < size; index++) {
slot = this.slots[index];
bucket = slot.hash % newSize;
slot.next = this.buckets[bucket];
this.buckets[bucket] = index;
}
},
'@@iterator': function () {
return new LookupTableIterator(this);
}
});
export function LookupTableIterator(lookup) {
var index = -1,
size = lookup.size,
slots = lookup.slots;
Iterator.call(this, function () {
if (++index < size) {
return {
value: slots[index].grouping,
done: false
};
}
return {
done: true
};
});
}
extend(LookupTableIterator, Iterator);
function LookupTableSlot(hash, grouping, next) {
this.hash = hash;
this.next = next;
this.grouping = grouping;
}