|
@@ -44,74 +44,83 @@ import java.NativeArray;
|
|
|
private var nOccupied:Int;
|
|
|
private var upperBound:Int;
|
|
|
|
|
|
+#if !no_map_cache
|
|
|
private var cachedKey:Int;
|
|
|
private var cachedIndex:Int;
|
|
|
+#end
|
|
|
|
|
|
public function new() : Void
|
|
|
{
|
|
|
+#if !no_map_cache
|
|
|
cachedIndex = -1;
|
|
|
+#end
|
|
|
}
|
|
|
|
|
|
public function set( key : Int, value : T ) : Void
|
|
|
{
|
|
|
- var x:Int;
|
|
|
+ var targetIndex:Int;
|
|
|
if (nOccupied >= upperBound)
|
|
|
{
|
|
|
if (nBuckets > (size << 1))
|
|
|
+ {
|
|
|
resize(nBuckets - 1); //clear "deleted" elements
|
|
|
- else
|
|
|
+ } else {
|
|
|
resize(nBuckets + 1);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
var flags = flags, _keys = _keys;
|
|
|
{
|
|
|
- var mask = nBuckets - 1;
|
|
|
- var site = x = nBuckets;
|
|
|
- var k = hash(key);
|
|
|
- var i = k & mask;
|
|
|
-
|
|
|
- var delKey = -1;
|
|
|
- //for speed up
|
|
|
- if (flagIsEmpty(flags, i)) {
|
|
|
- x = i;
|
|
|
+ var mask = nBuckets - 1,
|
|
|
+ hashedKey = hash(key),
|
|
|
+ curIndex = hashedKey & mask;
|
|
|
+
|
|
|
+ var delKey = -1,
|
|
|
+ curFlag = 0;
|
|
|
+ // to speed things up, don't loop if the first bucket is already free
|
|
|
+ if (isEmpty(getFlag(flags, curIndex))) {
|
|
|
+ targetIndex = curIndex;
|
|
|
} else {
|
|
|
- var inc = getInc(k, mask);
|
|
|
- var last = i;
|
|
|
- while (! (flagIsEmpty(flags, i) || _keys[i] == key) )
|
|
|
+ var inc = getInc(hashedKey, mask),
|
|
|
+ last = curIndex;
|
|
|
+ while (! (_keys[curIndex] == key || isEmpty(curFlag = getFlag(flags, curIndex))) )
|
|
|
{
|
|
|
- if (flagIsDel(flags,i) && delKey == -1)
|
|
|
- delKey = i;
|
|
|
- i = (i + inc) & mask;
|
|
|
-#if DEBUG_HASHTBL
|
|
|
- if (i == last)
|
|
|
+ if (delKey == -1 && isDel(curFlag))
|
|
|
{
|
|
|
- throw "assert";
|
|
|
+ delKey = curIndex;
|
|
|
}
|
|
|
+ curIndex = (curIndex + inc) & mask;
|
|
|
+#if debug
|
|
|
+ assert(curIndex != last);
|
|
|
#end
|
|
|
}
|
|
|
|
|
|
- if (flagIsEmpty(flags, i) && delKey != -1)
|
|
|
- x = delKey;
|
|
|
- else
|
|
|
- x = i;
|
|
|
+ if (delKey != -1 && isEmpty(getFlag(flags, curIndex))) {
|
|
|
+ targetIndex = delKey;
|
|
|
+ } else {
|
|
|
+ targetIndex = curIndex;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (flagIsEmpty(flags, x))
|
|
|
+ var flag = getFlag(flags, targetIndex);
|
|
|
+ if (isEmpty(flag))
|
|
|
{
|
|
|
- _keys[x] = key;
|
|
|
- vals[x] = value;
|
|
|
- setIsBothFalse(flags, x);
|
|
|
+ _keys[targetIndex] = key;
|
|
|
+ vals[targetIndex] = value;
|
|
|
+ setIsBothFalse(flags, targetIndex);
|
|
|
size++;
|
|
|
nOccupied++;
|
|
|
- } else if (flagIsDel(flags, x)) {
|
|
|
- _keys[x] = key;
|
|
|
- vals[x] = value;
|
|
|
- setIsBothFalse(flags, x);
|
|
|
+ } else if (isDel(flag)) {
|
|
|
+ _keys[targetIndex] = key;
|
|
|
+ vals[targetIndex] = value;
|
|
|
+ setIsBothFalse(flags, targetIndex);
|
|
|
size++;
|
|
|
} else {
|
|
|
- assert(_keys[x] == key);
|
|
|
- vals[x] = value;
|
|
|
+#if debug
|
|
|
+ assert(_keys[targetIndex] == key);
|
|
|
+#end
|
|
|
+ vals[targetIndex] = value;
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -121,17 +130,28 @@ import java.NativeArray;
|
|
|
{
|
|
|
var flags = flags, _keys = _keys;
|
|
|
|
|
|
- var mask = nBuckets - 1, k = hash(key);
|
|
|
- var i = k & mask;
|
|
|
- var inc = getInc(k, mask); /* inc == 1 for linear probing */
|
|
|
- var last = i;
|
|
|
- while (!flagIsEmpty(flags, i) && (flagIsDel(flags, i) || _keys[i] != key))
|
|
|
+ var mask = nBuckets - 1,
|
|
|
+ k = hash(key),
|
|
|
+ index = k & mask,
|
|
|
+ curFlag = -1,
|
|
|
+ inc = getInc(k, mask), /* inc == 1 for linear probing */
|
|
|
+ last = index;
|
|
|
+ do
|
|
|
{
|
|
|
- i = (i + inc) & mask;
|
|
|
- if (i == last)
|
|
|
- return -1;
|
|
|
- }
|
|
|
- return isEither(flags, i) ? -1 : i;
|
|
|
+ if (_keys[index] == key) {
|
|
|
+ if (isEmpty(curFlag = getFlag(flags, index)))
|
|
|
+ {
|
|
|
+ index = (index + inc) & mask;
|
|
|
+ continue;
|
|
|
+ } else if (isDel(curFlag)) {
|
|
|
+ return -1;
|
|
|
+ } else {
|
|
|
+ return index;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ index = (index + inc) & mask;
|
|
|
+ }
|
|
|
+ } while (index != last);
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
@@ -140,17 +160,20 @@ import java.NativeArray;
|
|
|
public function get( key : Int ) : Null<T>
|
|
|
{
|
|
|
var idx = -1;
|
|
|
+#if !no_map_cache
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return vals[idx];
|
|
|
}
|
|
|
+#end
|
|
|
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
+#if !no_map_cache
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
-
|
|
|
+#end
|
|
|
return vals[idx];
|
|
|
}
|
|
|
|
|
@@ -160,17 +183,20 @@ import java.NativeArray;
|
|
|
private function getDefault( key : Int, def : T ) : T
|
|
|
{
|
|
|
var idx = -1;
|
|
|
+#if !no_map_cache
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return vals[idx];
|
|
|
}
|
|
|
+#end
|
|
|
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
+#if !no_map_cache
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
-
|
|
|
+#end
|
|
|
return vals[idx];
|
|
|
}
|
|
|
|
|
@@ -180,16 +206,20 @@ import java.NativeArray;
|
|
|
public function exists( key : Int ) : Bool
|
|
|
{
|
|
|
var idx = -1;
|
|
|
+#if !no_map_cache
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
+#end
|
|
|
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
+#if !no_map_cache
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
+#end
|
|
|
|
|
|
return true;
|
|
|
}
|
|
@@ -200,7 +230,9 @@ import java.NativeArray;
|
|
|
public function remove( key : Int ) : Bool
|
|
|
{
|
|
|
var idx = -1;
|
|
|
+#if !no_map_cache
|
|
|
if (! (cachedKey == key && ( (idx = cachedIndex) != -1 )))
|
|
|
+#end
|
|
|
{
|
|
|
idx = lookup(key);
|
|
|
}
|
|
@@ -209,16 +241,23 @@ import java.NativeArray;
|
|
|
{
|
|
|
return false;
|
|
|
} else {
|
|
|
+#if !no_map_cache
|
|
|
if (cachedKey == key)
|
|
|
+ {
|
|
|
cachedIndex = -1;
|
|
|
-
|
|
|
- if (!isEither(flags, idx))
|
|
|
+ }
|
|
|
+#end
|
|
|
+ if (!isEither(getFlag(flags, idx)))
|
|
|
{
|
|
|
setIsDelTrue(flags, idx);
|
|
|
--size;
|
|
|
|
|
|
vals[idx] = null;
|
|
|
- _keys[idx] = 0;
|
|
|
+ // we do NOT reset the keys here, as unlike StringMap, we check for keys equality
|
|
|
+ // and stop if we find a key that is equal to the one we're looking for
|
|
|
+ // setting this to 0 will allow the hash to contain duplicate `0` keys
|
|
|
+ // (see #6457)
|
|
|
+ // _keys[idx] = 0;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
@@ -240,17 +279,23 @@ import java.NativeArray;
|
|
|
var nfSize = flagsSize(newNBuckets);
|
|
|
newFlags = new NativeArray( nfSize );
|
|
|
for (i in 0...nfSize)
|
|
|
- newFlags[i] = 0xaaaaaaaa;
|
|
|
+ {
|
|
|
+ newFlags[i] = 0xaaaaaaaa; // isEmpty = true; isDel = false
|
|
|
+ }
|
|
|
if (nBuckets < newNBuckets) //expand
|
|
|
{
|
|
|
var k = new NativeArray(newNBuckets);
|
|
|
if (_keys != null)
|
|
|
+ {
|
|
|
arrayCopy(_keys, 0, k, 0, nBuckets);
|
|
|
+ }
|
|
|
_keys = k;
|
|
|
|
|
|
var v = new NativeArray(newNBuckets);
|
|
|
if (vals != null)
|
|
|
+ {
|
|
|
arrayCopy(vals, 0, v, 0, nBuckets);
|
|
|
+ }
|
|
|
vals = v;
|
|
|
} //otherwise shrink
|
|
|
}
|
|
@@ -258,9 +303,11 @@ import java.NativeArray;
|
|
|
|
|
|
if (j != 0)
|
|
|
{ //rehashing is required
|
|
|
+#if !no_map_cache
|
|
|
//resetting cache
|
|
|
cachedKey = 0;
|
|
|
cachedIndex = -1;
|
|
|
+#end
|
|
|
|
|
|
j = -1;
|
|
|
var nBuckets = nBuckets, _keys = _keys, vals = vals, flags = flags;
|
|
@@ -268,12 +315,13 @@ import java.NativeArray;
|
|
|
var newMask = newNBuckets - 1;
|
|
|
while (++j < nBuckets)
|
|
|
{
|
|
|
- if (!isEither(flags, j))
|
|
|
+ if (!isEither(getFlag(flags, j)))
|
|
|
{
|
|
|
var key = _keys[j];
|
|
|
var val = vals[j];
|
|
|
|
|
|
- _keys[j] = 0;
|
|
|
+ // do not set keys as 0 - see comment about #6457
|
|
|
+ // _keys[j] = 0;
|
|
|
vals[j] = cast null;
|
|
|
setIsDelTrue(flags, j);
|
|
|
while (true) /* kick-out process; sort of like in Cuckoo hashing */
|
|
@@ -281,11 +329,13 @@ import java.NativeArray;
|
|
|
var k = hash(key);
|
|
|
var inc = getInc(k, newMask);
|
|
|
var i = k & newMask;
|
|
|
- while (!flagIsEmpty(newFlags, i))
|
|
|
+ while (!isEmpty(getFlag(newFlags, i)))
|
|
|
+ {
|
|
|
i = (i + inc) & newMask;
|
|
|
+ }
|
|
|
setIsEmptyFalse(newFlags, i);
|
|
|
|
|
|
- if (i < nBuckets && !isEither(flags, i)) /* kick out the existing element */
|
|
|
+ if (i < nBuckets && !isEither(getFlag(flags, i))) /* kick out the existing element */
|
|
|
{
|
|
|
{
|
|
|
var tmp = _keys[i];
|
|
@@ -333,59 +383,18 @@ import java.NativeArray;
|
|
|
Returns an iterator of all keys in the hashtable.
|
|
|
Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
|
|
|
**/
|
|
|
- public function keys() : Iterator<Int>
|
|
|
+ public inline function keys() : Iterator<Int>
|
|
|
{
|
|
|
- var i = 0;
|
|
|
- var len = nBuckets;
|
|
|
- return {
|
|
|
- hasNext: function() {
|
|
|
- for (j in i...len)
|
|
|
- {
|
|
|
- if (!isEither(flags, j))
|
|
|
- {
|
|
|
- i = j;
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- },
|
|
|
- next: function() {
|
|
|
- var ret = _keys[i];
|
|
|
- cachedIndex = i;
|
|
|
- cachedKey = ret;
|
|
|
-
|
|
|
- i = i + 1;
|
|
|
- return ret;
|
|
|
- }
|
|
|
- };
|
|
|
+ return new IntMapKeyIterator(this);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
Returns an iterator of all values in the hashtable.
|
|
|
Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
|
|
|
**/
|
|
|
- public function iterator() : Iterator<T>
|
|
|
+ public inline function iterator() : Iterator<T>
|
|
|
{
|
|
|
- var i = 0;
|
|
|
- var len = nBuckets;
|
|
|
- return {
|
|
|
- hasNext: function() {
|
|
|
- for (j in i...len)
|
|
|
- {
|
|
|
- if (!isEither(flags, j))
|
|
|
- {
|
|
|
- i = j;
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- },
|
|
|
- next: function() {
|
|
|
- var ret = vals[i];
|
|
|
- i = i + 1;
|
|
|
- return ret;
|
|
|
- }
|
|
|
- };
|
|
|
+ return new IntMapValueIterator(this);
|
|
|
}
|
|
|
|
|
|
public function copy() : IntMap<T> {
|
|
@@ -431,26 +440,50 @@ import java.NativeArray;
|
|
|
private static inline function hash(i:Int):Int
|
|
|
return i;
|
|
|
|
|
|
- private static inline function flagIsEmpty(flag:NativeArray<Int>, i:Int):Bool
|
|
|
- return ( (flag[i >> 4] >>> ((i & 0xf) << 1)) & 2 ) != 0;
|
|
|
+ // flags represents a bit array with 2 significant bits for each index
|
|
|
+ // one bit for deleted (1), one for empty (2)
|
|
|
+ // so what this function does is:
|
|
|
+ // * gets the integer with (flags / 16)
|
|
|
+ // * shifts those bits to the right ((flags % 16) * 2) places
|
|
|
+ // * masks it with 0b11
|
|
|
+ private static inline function getFlag(flags:NativeArray<Int>, i:Int):Int
|
|
|
+ {
|
|
|
+ return ( (flags[i >> 4] >>> ((i & 0xf) << 1)) & 3 );
|
|
|
+ }
|
|
|
|
|
|
- private static inline function flagIsDel(flag:NativeArray<Int>, i:Int):Bool
|
|
|
- return ((flag[i >> 4] >>> ((i & 0xf) << 1)) & 1) != 0;
|
|
|
+ private static inline function isDel(flag:Int):Bool
|
|
|
+ {
|
|
|
+ return (flag & 1) != 0;
|
|
|
+ }
|
|
|
|
|
|
- private static inline function isEither(flag:NativeArray<Int>, i:Int):Bool
|
|
|
- return ((flag[i >> 4] >>> ((i & 0xf) << 1)) & 3) != 0;
|
|
|
+ private static inline function isEmpty(flag:Int):Bool
|
|
|
+ {
|
|
|
+ return (flag & 2) != 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static inline function isEither(flag:Int):Bool {
|
|
|
+ return flag != 0;
|
|
|
+ }
|
|
|
|
|
|
- private static inline function setIsDelFalse(flag:NativeArray<Int>, i:Int):Void
|
|
|
- flag[i >> 4] &= ~(1 << ((i & 0xf) << 1));
|
|
|
+ private static inline function setIsDelFalse(flags:NativeArray<Int>, i:Int):Void
|
|
|
+ {
|
|
|
+ flags[i >> 4] &= ~(1 << ((i & 0xf) << 1));
|
|
|
+ }
|
|
|
|
|
|
- private static inline function setIsEmptyFalse(flag:NativeArray<Int>, i:Int):Void
|
|
|
- flag[i >> 4] &= ~(2 << ((i & 0xf) << 1));
|
|
|
+ private static inline function setIsEmptyFalse(flags:NativeArray<Int>, i:Int):Void
|
|
|
+ {
|
|
|
+ flags[i >> 4] &= ~(2 << ((i & 0xf) << 1));
|
|
|
+ }
|
|
|
|
|
|
- private static inline function setIsBothFalse(flag:NativeArray<Int>, i:Int):Void
|
|
|
- flag[i >> 4] &= ~(3 << ((i & 0xf) << 1));
|
|
|
+ private static inline function setIsBothFalse(flags:NativeArray<Int>, i:Int):Void
|
|
|
+ {
|
|
|
+ flags[i >> 4] &= ~(3 << ((i & 0xf) << 1));
|
|
|
+ }
|
|
|
|
|
|
- private static inline function setIsDelTrue(flag:NativeArray<Int>, i:Int):Void
|
|
|
- flag[i >> 4] |= 1 << ((i & 0xf) << 1);
|
|
|
+ private static inline function setIsDelTrue(flags:NativeArray<Int>, i:Int):Void
|
|
|
+ {
|
|
|
+ flags[i >> 4] |= 1 << ((i & 0xf) << 1);
|
|
|
+ }
|
|
|
|
|
|
private static inline function roundUp(x:Int):Int
|
|
|
{
|
|
@@ -466,3 +499,76 @@ import java.NativeArray;
|
|
|
private static inline function flagsSize(m:Int):Int
|
|
|
return ((m) < 16? 1 : (m) >> 4);
|
|
|
}
|
|
|
+
|
|
|
+@:access(haxe.ds.IntMap)
|
|
|
+@:final
|
|
|
+private class IntMapKeyIterator<T>
|
|
|
+{
|
|
|
+ var m:IntMap<T>;
|
|
|
+ var i:Int;
|
|
|
+ var len:Int;
|
|
|
+
|
|
|
+ public function new(m:IntMap<T>)
|
|
|
+ {
|
|
|
+ this.i = 0;
|
|
|
+ this.m = m;
|
|
|
+ this.len = m.nBuckets;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hasNext():Bool
|
|
|
+ {
|
|
|
+ for (j in i...len)
|
|
|
+ {
|
|
|
+ if (!IntMap.isEither(IntMap.getFlag(m.flags, j)))
|
|
|
+ {
|
|
|
+ i = j;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function next():Int
|
|
|
+ {
|
|
|
+ var ret = m._keys[i];
|
|
|
+#if !no_map_cache
|
|
|
+ m.cachedIndex = i;
|
|
|
+ m.cachedKey = ret;
|
|
|
+#end
|
|
|
+ i++;
|
|
|
+ return ret;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+@:access(haxe.ds.IntMap)
|
|
|
+@:final
|
|
|
+private class IntMapValueIterator<T>
|
|
|
+{
|
|
|
+ var m:IntMap<T>;
|
|
|
+ var i:Int;
|
|
|
+ var len:Int;
|
|
|
+
|
|
|
+ public function new(m:IntMap<T>)
|
|
|
+ {
|
|
|
+ this.i = 0;
|
|
|
+ this.m = m;
|
|
|
+ this.len = m.nBuckets;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function hasNext():Bool {
|
|
|
+ for (j in i...len)
|
|
|
+ {
|
|
|
+ if (!IntMap.isEither(IntMap.getFlag(m.flags, j)))
|
|
|
+ {
|
|
|
+ i = j;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ public inline function next():T
|
|
|
+ {
|
|
|
+ return m.vals[i++];
|
|
|
+ }
|
|
|
+}
|