|
@@ -24,45 +24,45 @@ import java.NativeArray;
|
|
|
* DAMAGE.
|
|
|
*/
|
|
|
|
|
|
-@:core_api class Hash<T>
|
|
|
+@:coreApi class Hash<T>
|
|
|
{
|
|
|
@:extern private static inline var HASH_UPPER = 0.77;
|
|
|
@:extern private static inline var FLAG_EMPTY = 0;
|
|
|
@:extern private static inline var FLAG_DEL = 1;
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* This is the most important structure here and the reason why it's so fast.
|
|
|
* It's an array of all the hashes contained in the table. These hashes cannot be 0 nor 1,
|
|
|
* which stand for "empty" and "deleted" states.
|
|
|
- *
|
|
|
+ *
|
|
|
* The lookup algorithm will keep looking until a 0 or the key wanted is found;
|
|
|
* The insertion algorithm will do the same but will also break when FLAG_DEL is found;
|
|
|
*/
|
|
|
private var hashes:NativeArray<HashType>;
|
|
|
private var _keys:NativeArray<String>;
|
|
|
private var vals:NativeArray<T>;
|
|
|
-
|
|
|
+
|
|
|
private var nBuckets:Int;
|
|
|
private var size:Int;
|
|
|
private var nOccupied:Int;
|
|
|
private var upperBound:Int;
|
|
|
-
|
|
|
+
|
|
|
private var cachedKey:String;
|
|
|
private var cachedIndex:Int;
|
|
|
-
|
|
|
+
|
|
|
#if DEBUG_HASHTBL
|
|
|
private var totalProbes:Int;
|
|
|
private var probeTimes:Int;
|
|
|
private var sameHash:Int;
|
|
|
private var maxProbe:Int;
|
|
|
#end
|
|
|
-
|
|
|
- public function new() : Void
|
|
|
+
|
|
|
+ public function new() : Void
|
|
|
{
|
|
|
cachedIndex = -1;
|
|
|
}
|
|
|
|
|
|
- public function set( key : String, value : T ) : Void
|
|
|
+ public function set( key : String, value : T ) : Void
|
|
|
{
|
|
|
var x:Int, k:Int;
|
|
|
if (nOccupied >= upperBound)
|
|
@@ -72,14 +72,14 @@ import java.NativeArray;
|
|
|
else
|
|
|
resize(nBuckets + 2);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
var hashes = hashes, keys = _keys, hashes = hashes;
|
|
|
{
|
|
|
var mask = (nBuckets == 0) ? 0 : nBuckets - 1;
|
|
|
var site = x = nBuckets;
|
|
|
k = hash(key);
|
|
|
var i = k & mask, nProbes = 0;
|
|
|
-
|
|
|
+
|
|
|
//for speed up
|
|
|
if (isEither(hashes[i])) {
|
|
|
x = i;
|
|
@@ -97,16 +97,16 @@ import java.NativeArray;
|
|
|
}
|
|
|
x = i;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
#if DEBUG_HASHTBL
|
|
|
if (nProbes > maxProbe)
|
|
|
maxProbe = nProbes;
|
|
|
totalProbes++;
|
|
|
#end
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
var flag = hashes[x];
|
|
|
- if (isEmpty(flag))
|
|
|
+ if (isEmpty(flag))
|
|
|
{
|
|
|
keys[x] = key;
|
|
|
vals[x] = value;
|
|
@@ -122,17 +122,17 @@ import java.NativeArray;
|
|
|
assert(_keys[x] == key);
|
|
|
vals[x] = value;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
cachedIndex = x;
|
|
|
cachedKey = key;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@:final private function lookup( key : String ) : Int
|
|
|
{
|
|
|
if (nBuckets != 0)
|
|
|
{
|
|
|
var hashes = hashes, keys = _keys;
|
|
|
-
|
|
|
+
|
|
|
var mask = nBuckets - 1, hash = hash(key), k = hash, nProbes = 0;
|
|
|
var i = k & mask;
|
|
|
var last = i, flag;
|
|
@@ -146,7 +146,7 @@ import java.NativeArray;
|
|
|
throw "assert";
|
|
|
#end
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
#if DEBUG_HASHTBL
|
|
|
if (nProbes > maxProbe)
|
|
|
maxProbe = nProbes;
|
|
@@ -154,10 +154,10 @@ import java.NativeArray;
|
|
|
#end
|
|
|
return isEither(flag) ? -1 : i;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return -1;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@:final @:private function resize(newNBuckets:Int) : Void
|
|
|
{
|
|
|
//This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets.
|
|
@@ -178,7 +178,7 @@ import java.NativeArray;
|
|
|
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);
|
|
@@ -186,16 +186,16 @@ import java.NativeArray;
|
|
|
} //otherwise shrink
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- if (j != 0)
|
|
|
+
|
|
|
+ if (j != 0)
|
|
|
{ //rehashing is required
|
|
|
//resetting cache
|
|
|
cachedKey = null;
|
|
|
cachedIndex = -1;
|
|
|
-
|
|
|
+
|
|
|
j = -1;
|
|
|
var nBuckets = nBuckets, _keys = _keys, vals = vals, hashes = hashes;
|
|
|
-
|
|
|
+
|
|
|
var newMask = newNBuckets - 1;
|
|
|
while (++j < nBuckets)
|
|
|
{
|
|
@@ -204,19 +204,19 @@ import java.NativeArray;
|
|
|
{
|
|
|
var key = _keys[j];
|
|
|
var val = vals[j];
|
|
|
-
|
|
|
+
|
|
|
hashes[j] = FLAG_DEL;
|
|
|
while (true) /* kick-out process; sort of like in Cuckoo hashing */
|
|
|
{
|
|
|
var nProbes = 0;
|
|
|
//var inc = getInc(k, newMask);
|
|
|
var i = k & newMask;
|
|
|
-
|
|
|
+
|
|
|
while (!isEmpty(newHash[i]))
|
|
|
i = (i + ++nProbes) & newMask;
|
|
|
-
|
|
|
+
|
|
|
newHash[i] = k;
|
|
|
-
|
|
|
+
|
|
|
if (i < nBuckets && !isEither(k = hashes[i])) /* kick out the existing element */
|
|
|
{
|
|
|
{
|
|
@@ -229,7 +229,7 @@ import java.NativeArray;
|
|
|
vals[i] = val;
|
|
|
val = tmp;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
hashes[i] = FLAG_DEL; /* mark it as deleted in the old hash table */
|
|
|
} else { /* write the element and jump out of the loop */
|
|
|
_keys[i] = key;
|
|
@@ -239,7 +239,7 @@ import java.NativeArray;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (nBuckets > newNBuckets) /* shrink the hash table */
|
|
|
{
|
|
|
{
|
|
@@ -253,7 +253,7 @@ import java.NativeArray;
|
|
|
this.vals = v;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
this.hashes = newHash;
|
|
|
this.nBuckets = newNBuckets;
|
|
|
this.nOccupied = size;
|
|
@@ -261,86 +261,86 @@ import java.NativeArray;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public function get( key : String ) : Null<T>
|
|
|
+ public function get( key : String ) : Null<T>
|
|
|
{
|
|
|
var idx = -1;
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return vals[idx];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
-
|
|
|
+
|
|
|
return vals[idx];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return null;
|
|
|
}
|
|
|
-
|
|
|
- private function getDefault( key : String, def : T ) : T
|
|
|
+
|
|
|
+ private function getDefault( key : String, def : T ) : T
|
|
|
{
|
|
|
var idx = -1;
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return vals[idx];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
-
|
|
|
+
|
|
|
return vals[idx];
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return def;
|
|
|
}
|
|
|
|
|
|
- public function exists( key : String ) : Bool
|
|
|
+ public function exists( key : String ) : Bool
|
|
|
{
|
|
|
var idx = -1;
|
|
|
if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
|
|
|
{
|
|
|
return true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
idx = lookup(key);
|
|
|
if (idx != -1)
|
|
|
{
|
|
|
cachedKey = key;
|
|
|
cachedIndex = idx;
|
|
|
-
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- public function remove( key : String ) : Bool
|
|
|
+ public function remove( key : String ) : Bool
|
|
|
{
|
|
|
var idx = -1;
|
|
|
if (! (cachedKey == key && ( (idx = cachedIndex) != -1 )))
|
|
|
{
|
|
|
idx = lookup(key);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (idx == -1)
|
|
|
{
|
|
|
return false;
|
|
|
} else {
|
|
|
if (cachedKey == key)
|
|
|
cachedIndex = -1;
|
|
|
-
|
|
|
+
|
|
|
hashes[idx] = FLAG_EMPTY;
|
|
|
_keys[idx] = null;
|
|
|
vals[idx] = null;
|
|
|
--size;
|
|
|
-
|
|
|
+
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -349,7 +349,7 @@ 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<String>
|
|
|
+ public function keys() : Iterator<String>
|
|
|
{
|
|
|
var i = 0;
|
|
|
var len = nBuckets;
|
|
@@ -369,7 +369,7 @@ import java.NativeArray;
|
|
|
var ret = _keys[i];
|
|
|
cachedIndex = i;
|
|
|
cachedKey = ret;
|
|
|
-
|
|
|
+
|
|
|
i = i + 1;
|
|
|
return ret;
|
|
|
}
|
|
@@ -380,7 +380,7 @@ import java.NativeArray;
|
|
|
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 function iterator() : Iterator<T>
|
|
|
{
|
|
|
var i = 0;
|
|
|
var len = nBuckets;
|
|
@@ -422,7 +422,7 @@ import java.NativeArray;
|
|
|
s.add("}");
|
|
|
return s.toString();
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function roundUp(x:Int):Int
|
|
|
{
|
|
|
--x;
|
|
@@ -433,21 +433,21 @@ import java.NativeArray;
|
|
|
x |= (x) >>> 16;
|
|
|
return ++x;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function getInc(k:Int, mask:Int):Int //return 1 for linear probing
|
|
|
return (((k) >> 3 ^ (k) << 3) | 1) & (mask)
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function isEither(v:HashType):Bool
|
|
|
return (v & 0xFFFFFFFE) == 0
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function isEmpty(v:HashType):Bool
|
|
|
return v == FLAG_EMPTY
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function isDel(v:HashType):Bool
|
|
|
return v == FLAG_DEL
|
|
|
-
|
|
|
+
|
|
|
//guarantee: Whatever this function is, it will never return 0 nor 1
|
|
|
- @:extern private static inline function hash(s:String):HashType
|
|
|
+ @:extern private static inline function hash(s:String):HashType
|
|
|
{
|
|
|
var k:Int = untyped s.hashCode();
|
|
|
//k *= 357913941;
|
|
@@ -455,14 +455,14 @@ import java.NativeArray;
|
|
|
//k += ~357913941;
|
|
|
//k ^= k >> 31;
|
|
|
//k ^= k << 31;
|
|
|
-
|
|
|
+
|
|
|
k = (k+0x7ed55d16) + (k<<12);
|
|
|
k = (k^0xc761c23c) ^ (k>>19);
|
|
|
k = (k+0x165667b1) + (k<<5);
|
|
|
k = (k+0xd3a2646c) ^ (k<<9);
|
|
|
k = (k+0xfd7046c5) + (k<<3);
|
|
|
k = (k^0xb55a4f09) ^ (k>>16);
|
|
|
-
|
|
|
+
|
|
|
var ret = k;
|
|
|
if (isEither(ret))
|
|
|
{
|
|
@@ -471,13 +471,13 @@ import java.NativeArray;
|
|
|
else
|
|
|
ret = 0xFFFFFFFF;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function arrayCopy(sourceArray:Dynamic, sourceIndex:Int, destinationArray:Dynamic, destinationIndex:Int, length:Int):Void
|
|
|
java.lang.System.arraycopy(sourceArray, sourceIndex, destinationArray, destinationIndex, length)
|
|
|
-
|
|
|
+
|
|
|
@:extern private static inline function assert(x:Bool):Void
|
|
|
{
|
|
|
#if DEBUG_HASHTBL
|