Explorar o código

[cs] Added first iteration of IntHash

Caue Waneck %!s(int64=13) %!d(string=hai) anos
pai
achega
845000d2b1
Modificáronse 2 ficheiros con 370 adicións e 38 borrados
  1. 370 25
      std/cs/_std/IntHash.hx
  2. 0 13
      std/cs/_std/haxe/lang/Null.hx

+ 370 - 25
std/cs/_std/IntHash.hx

@@ -1,3 +1,7 @@
+package;
+import cs.NativeArray;
+import cs.NativeArray;
+import cs.NativeArray;
 /*
  * Copyright (c) 2005, The haXe Project Contributors
  * All rights reserved.
@@ -23,75 +27,361 @@
  * DAMAGE.
  */
 
+/**
+ * This IntHash implementation is based on khash (https://github.com/attractivechaos/klib/blob/master/khash.h)
+ * Copyright goes to Attractive Chaos <[email protected]> and his contributors
+ */
+ 
 @:core_api class IntHash<T>  
 {
-	//private var hashes:Array<Int>;
-	private var keysArr:Array<Int>;
-	private var valuesArr:Array<T>;
+	private static inline var HASH_UPPER = 0.77;
+	
+	private var flags:NativeArray<Int>;
+	private var _keys:NativeArray<Int>;
+	private var vals:NativeArray<T>;
+	
+	private var nBuckets:Int;
+	private var size:Int;
+	private var nOccupied:Int;
+	private var upperBound:Int;
+	
+	private var cachedKey:Int;
+	private var cachedIndex:Int;
 	
 	public function new() : Void 
 	{
-		//hashes = [];
-		keysArr = [];
-		valuesArr = [];
+		cachedIndex = -1;
 	}
 
 	public function set( key : Int, value : T ) : Void 
 	{
-		keysArr.push(key);
-		valuesArr.push(value);
+		var x:Int;
+		if (nOccupied >= upperBound)
+		{
+			if (nBuckets > (size << 1))
+				resize(nBuckets - 1); //clear "deleted" elements
+			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;
+			
+			//for speed up
+			if (flagIsEmpty(flags, i)) {
+				x = i;
+			} else {
+				var inc = getInc(k, mask);
+				var last = i;
+				while (!flagIsEmpty(flags, i) && (flagIsDel(flags, i) || _keys[i] != key))
+				{
+					if (flagIsDel(flags, i))
+						site = i;
+					i = (i + inc) & mask;
+					if (i == last)
+					{
+						x = site;
+						break;
+					}
+				}
+				
+				if (x == nBuckets)
+				{
+					if (flagIsEmpty(flags, i) && site != nBuckets)
+						x = site;
+					else
+						x = i;
+				}
+			}
+		}
+		
+		if (flagIsEmpty(flags, x)) 
+		{
+			_keys[x] = key;
+			vals[x] = value;
+			setIsBothFalse(flags, x);
+			size++;
+			nOccupied++;
+		} else if (flagIsDel(flags, x)) {
+			_keys[x] = key;
+			vals[x] = value;
+			size++;
+		} else {
+			assert(_keys[x] == key);
+			vals[x] = value;
+		}
 	}
-
+	
+	@:final private function lookup( key : Int ) : Int
+	{
+		if (nBuckets != 0)
+		{
+			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))
+			{
+				i = (i + inc) & mask;
+				if (i == last)
+					return -1;
+			}
+			return isEither(flags, i) ? -1 : i;
+		}
+		
+		return -1;
+	}
+	
 	public function get( key : Int ) : Null<T> 
 	{
-		var i = 0;
-		for (k in keysArr)
+		var idx = -1;
+		if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
+		{
+			return vals[idx];
+		}
+		
+		idx = lookup(key);
+		if (idx != -1)
 		{
-			if (k == key) return valuesArr[i];
-			i++;
+			cachedKey = key;
+			cachedIndex = idx;
+			
+			return vals[idx];
 		}
+		
 		return null;
 	}
+	
+	private function getDefault( key : Int, 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 : Int ) : Bool 
 	{
-		for (k in keysArr)
+		var idx = -1;
+		if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
+		{
+			return true;
+		}
+		
+		idx = lookup(key);
+		if (idx != -1)
 		{
-			if (k == key) return true;
+			cachedKey = key;
+			cachedIndex = idx;
+			
+			return true;
 		}
+		
 		return false;
 	}
 
 	public function remove( key : Int ) : Bool 
 	{
-		var i = 0;
-		for (k in keysArr)
+		var idx = -1;
+		if (! (cachedKey == key && ( (idx = cachedIndex) != -1 )))
+		{
+			idx = lookup(key);
+		}
+		
+		if (idx == -1)
 		{
-			if (k == key)
+			return false;
+		} else {
+			if (cachedKey == key)
+				cachedIndex = -1;
+			
+			if (!isEither(flags, idx))
 			{
-				keysArr.splice(i, 1);
-				valuesArr.splice(i, 1);
-				return true;
+				setIsDelTrue(flags, idx);
+				--size;
 			}
-			i++;
+			
+			return true;
+		}
+	}
+	
+	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.
+		var newFlags = null;
+		var j = 1;
+		{
+			newNBuckets = roundUp(newNBuckets);
+			if (newNBuckets < 4) newNBuckets = 4;
+			if (size >= (newNBuckets * HASH_UPPER + 0.5)) /* requested size is too small */
+			{
+				j = 0;
+			} else { /* hash table size to be changed (shrink or expand); rehash */
+				var nfSize = flagsSize(newNBuckets);
+				newFlags = new NativeArray( nfSize );
+				for (i in 0...nfSize)
+					newFlags[i] = 0xaaaaaaaa;
+				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
+			}
+		}
+		
+		if (j != 0) 
+		{ //rehashing is required
+			//resetting cache
+			cachedKey = 0;
+			cachedIndex = -1;
+			
+			j = -1;
+			var nBuckets = nBuckets, _keys = _keys, vals = vals, flags = flags;
+			
+			var newMask = newNBuckets - 1;
+			while (++j < nBuckets)
+			{
+				if (!isEither(flags, j))
+				{
+					var key = _keys[j];
+					var val = vals[j];
+					
+					setIsDelTrue(flags, j);
+					while (true) /* kick-out process; sort of like in Cuckoo hashing */
+					{
+						var k = hash(key);
+						var inc = getInc(k, newMask);
+						var i = k & newMask;
+						while (!flagIsEmpty(newFlags, i))
+							i = (i + inc) & newMask;
+						setIsEmptyFalse(newFlags, i);
+						
+						if (i < nBuckets && !isEither(flags, i)) /* kick out the existing element */
+						{
+							{
+								var tmp = _keys[i];
+								_keys[i] = key;
+								key = tmp;
+							}
+							{
+								var tmp = vals[i];
+								vals[i] = val;
+								val = tmp;
+							}
+							
+							setIsDelTrue(flags, i); /* mark it as deleted in the old hash table */
+						} else { /* write the element and jump out of the loop */
+							_keys[i] = key;
+							vals[i] = val;
+							break;
+						}
+					}
+				}
+			}
+			
+			if (nBuckets > newNBuckets) /* shrink the hash table */
+			{
+				{
+					var k = new NativeArray(newNBuckets);
+					arrayCopy(_keys, 0, k, 0, newNBuckets);
+					this._keys = k;
+				}
+				{
+					var v = new NativeArray(newNBuckets);
+					arrayCopy(vals, 0, v, 0, newNBuckets);
+					this.vals = v;
+				}
+			}
+			
+			this.flags = newFlags;
+			this.nBuckets = newNBuckets;
+			this.nOccupied = size;
+			this.upperBound = Std.int(newNBuckets * HASH_UPPER + .5);
 		}
-		return false;
 	}
 
 	/**
 		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> 
 	{
-		return keysArr.iterator();
+		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;
+			}
+		};
 	}
 
 	/**
 		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> 
 	{
-		return valuesArr.iterator();
+		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;
+			}
+		};
 	}
 
 	/**
@@ -112,4 +402,59 @@
 		s.add("}");
 		return s.toString();
 	}
+	
+	private static inline function assert(x:Bool):Void
+	{
+		#if debug
+		if (!x) throw "assert failed";
+		#end
+	}
+	
+	private static inline function defaultK():Int return 0
+	
+	private static inline function arrayCopy(sourceArray:system.Array, sourceIndex:Int, destinationArray:system.Array, destinationIndex:Int, length:Int):Void
+		system.Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length)
+	
+	private static inline function getInc(k:Int, mask:Int):Int
+		return (((k) >> 3 ^ (k) << 3) | 1) & (mask)
+	
+	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
+	
+	private static inline function flagIsDel(flag:NativeArray<Int>, i:Int):Bool
+		return ((flag[i >> 4] >>> ((i & 0xf) << 1)) & 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 setIsDelFalse(flag:NativeArray<Int>, i:Int):Void
+		flag[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 setIsBothFalse(flag:NativeArray<Int>, i:Int):Void
+		flag[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 roundUp(x:Int):Int
+	{
+		--x;
+		x |= (x) >>> 1;
+		x |= (x) >>> 2;
+		x |= (x) >>> 4;
+		x |= (x) >>> 8;
+		x |= (x) >>> 16;
+		return ++x;
+	}
+	
+	private static inline function flagsSize(m:Int):Int
+		return ((m) < 16? 1 : (m) >> 4)
 }
+
+//@:struct private class 

+ 0 - 13
std/cs/_std/haxe/lang/Null.hx

@@ -23,19 +23,6 @@ package haxe.lang;
 	@:readonly public var hasValue:Bool;
 	
 	@:functionBody('
-			if (! (v is System.ValueType))
-			{
-				if (System.Object.ReferenceEquals(v, default(T)))
-				{
-					hasValue = false;
-				}
-			}
-			
-			if (!System.Object.ReferenceEquals(v, null) && !v.Equals(default(T)))
-			{
-				hasValue = true;
-			}
-			
 			this.@value = v;
 			this.hasValue = hasValue;
 	')