Created
January 12, 2021 14:27
-
-
Save mjs3339/06196c2752e33e5bfdf8a292169c64e1 to your computer and use it in GitHub Desktop.
Sequential Ordering Object Indexer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Diagnostics; | |
using System.Linq; | |
[DebuggerDisplay("Count = {" + nameof(Count) + "}")] | |
[Serializable] | |
public class ObjectIndexer4 : IEnumerable<object> | |
{ | |
private readonly FNV1a32 hasher; | |
private int[] _hashBuckets; | |
public List<int> BucketDepthList; | |
public int Position; | |
internal Slot[] Slots; | |
public ObjectIndexer4(int size = 0) | |
{ | |
if (size == 0) | |
size = 11; | |
_hashBuckets = new int[size]; | |
Slots = new Slot[size]; | |
Count = 0; | |
Position = -1; | |
hasher = new FNV1a32(); | |
BucketDepthList = new List<int>(size); | |
} | |
public int Count | |
{ | |
get; | |
private set; | |
} | |
public Dictionary<int, int> BucketDepthAnalysis | |
{ | |
get | |
{ | |
var items = new Dictionary<int, int>(); | |
foreach (var number in BucketDepthList) | |
if (items.ContainsKey(number)) | |
items[number]++; | |
else | |
items.Add(number, 1); | |
return items; | |
} | |
} | |
public float LoadRatio => GetLoadRatio(); | |
IEnumerator<object> IEnumerable<object>.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return GetEnumerator(); | |
} | |
public float GetLoadRatio() | |
{ | |
var x = Count; | |
var y = Slots.Length; | |
var r = x / (float) y; | |
return r; | |
} | |
public object[] ToArray() | |
{ | |
var newArray = new object[Count]; | |
var copied = 0; | |
for (var i = 0; i < Count && copied < Count; i++) | |
if (Slots[i].Value != null) | |
newArray[copied++] = Slots[i].Value; | |
return newArray; | |
} | |
private static bool IsPrimitive(object obj) | |
{ | |
switch (Type.GetTypeCode(obj.GetType())) | |
{ | |
case TypeCode.Boolean: | |
case TypeCode.Char: | |
case TypeCode.SByte: | |
case TypeCode.Byte: | |
case TypeCode.Int16: | |
case TypeCode.UInt16: | |
case TypeCode.Int32: | |
case TypeCode.UInt32: | |
case TypeCode.Single: | |
case TypeCode.String: | |
case TypeCode.Decimal: | |
case TypeCode.DateTime: | |
case TypeCode.Int64: | |
case TypeCode.UInt64: | |
case TypeCode.Double: | |
return true; | |
default: | |
return false; | |
} | |
} | |
private new bool Equals(object x, object y) | |
{ | |
if (x == null || y == null) | |
return false; | |
var xp = IsPrimitive(x); | |
var yp = IsPrimitive(y); | |
if (xp != yp) | |
return false; | |
if (xp && yp) | |
return x.Equals(y); | |
var xb = x.GetBytes(); | |
var yb = y.GetBytes(); | |
if (xb.Length != yb.Length) | |
return false; | |
return xb.Compare(yb); | |
} | |
public void Clear() | |
{ | |
var size = Slots.Length; | |
_hashBuckets = new int[size]; | |
Slots = new Slot[size]; | |
Count = 0; | |
Position = -1; | |
} | |
public bool Add(object item) | |
{ | |
return Insert(item, true); | |
} | |
public int AddRange(IEnumerable<object> items) | |
{ | |
return items.Sum(i => !Add(i) ? 0 : 1); | |
} | |
public bool Contains(object item) | |
{ | |
return Insert(item, false) == false; | |
} | |
private int GetHashCodeInt(object obj) | |
{ | |
return hasher.ComputeHash(obj.GetBytes()).ToInt() & int.MaxValue; | |
} | |
internal bool Insert(object item, bool add) | |
{ | |
var hashCode = GetHashCodeInt(item); | |
var BucketDepth = 1; | |
for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = Slots[position].Next) | |
{ | |
if (Equals(Slots[position].Value, item)) | |
{ | |
Position = position; | |
BucketDepthList.Add(BucketDepth); | |
return false; | |
} | |
BucketDepth++; | |
} | |
if (add) | |
{ | |
if (Count >= Slots.Length) | |
Resize(_hashBuckets.Length + _hashBuckets.Length / 10 * 9); | |
var hashPos = hashCode % _hashBuckets.Length; | |
Slots[Count].Next = _hashBuckets[hashPos] - 1; | |
Slots[Count].Value = item; | |
_hashBuckets[hashPos] = Count + 1; | |
Position = Count; | |
++Count; | |
} | |
return true; | |
} | |
public int GetIndex(object obj) | |
{ | |
var pos = FindEntry(obj, GetHashCodeInt(obj)); | |
if (pos == -1) | |
{ | |
Add(obj); | |
pos = FindEntry(obj, GetHashCodeInt(obj)); | |
} | |
return _hashBuckets[pos] - 1; | |
} | |
public int FindIndex(object obj) | |
{ | |
var pos = FindEntry(obj, GetHashCodeInt(obj)); | |
return pos == -1 ? -1 : _hashBuckets[pos] - 1; | |
} | |
private void Resize(int Size) | |
{ | |
if (Count == 0) | |
return; | |
var newSize = Size == 0 ? _hashBuckets.Length : Size; | |
var newSlots = new Slot[newSize]; | |
var newHashBuckets = new int[newSize]; | |
BucketDepthList.Capacity = newSize; | |
if (Slots != null) | |
Array.Copy(Slots, 0, newSlots, 0, Count); | |
; | |
for (var i = 0; i < newSize; ++i) | |
if (newSlots[i].Value != null) | |
{ | |
var pos = GetHashCodeInt(newSlots[i].Value) % newSize; | |
newSlots[i].Next = newHashBuckets[pos] - 1; | |
newHashBuckets[pos] = i + 1; | |
} | |
Slots = newSlots; | |
_hashBuckets = newHashBuckets; | |
} | |
public void TrimExcess() | |
{ | |
var newHashBuckets = new int[Count]; | |
var newSlots = new Slot[Count]; | |
Array.Copy(Slots, newSlots, Count); | |
for (var i = 0; i < Count; i++) | |
{ | |
var pos = GetHashCodeInt(newSlots[i].Value) % Count; | |
newSlots[i].Next = newHashBuckets[pos] - 1; | |
newHashBuckets[pos] = i + 1; | |
} | |
_hashBuckets = newHashBuckets; | |
Slots = newSlots; | |
} | |
public bool Remove(object item) | |
{ | |
if (Count != 0) | |
{ | |
var hashCode = GetHashCodeInt(item); | |
var iPos = hashCode % _hashBuckets.Length; | |
var tPos = -1L; | |
for (var sPos = _hashBuckets[iPos] - 1; sPos >= 0; sPos = Slots[sPos].Next) | |
{ | |
if (Equals(Slots[sPos].Value, item)) | |
{ | |
if (tPos < 0) | |
_hashBuckets[iPos] = Slots[sPos].Next + 1; | |
else | |
Slots[tPos].Next = Slots[sPos].Next; | |
Slots[sPos].Value = default; | |
Slots[sPos].Next = -1; | |
--Count; | |
return true; | |
} | |
tPos = sPos; | |
} | |
} | |
return false; | |
} | |
private int FindEntry(object item, int hashCode) | |
{ | |
for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = Slots[position].Next) | |
if (Equals(Slots[position].Value, item)) | |
{ | |
Position = position; | |
return position; | |
} | |
return -1; | |
} | |
public int FindEntry(object item) | |
{ | |
return FindEntry(item, GetHashCodeInt(item)); | |
} | |
public void ExceptWith(IEnumerable<object> other) | |
{ | |
if (other == null) | |
throw new Exception("The other set must not be null."); | |
if (Count == 0) | |
return; | |
if (Equals(other, this)) | |
Clear(); | |
else | |
foreach (var obj in other) | |
Remove(obj); | |
} | |
public void UnionWith(IEnumerable<object> other) | |
{ | |
if (other == null) | |
throw new Exception("The other set must not be null."); | |
foreach (var obj in other) | |
Add(obj); | |
} | |
public bool Overlaps(IEnumerable<object> other) | |
{ | |
if (other == null) | |
throw new Exception("The other set must not be null."); | |
return Count != 0 && other.Any(Contains); | |
} | |
public bool ContainsAllElements(IEnumerable<object> other) | |
{ | |
return other.All(Contains); | |
} | |
public int RemoveWhere(Predicate<object> pred) | |
{ | |
if (pred == null) | |
throw new Exception("The Predicate cannot be null."); | |
var matches = 0; | |
for (var i = 0; i < Count; ++i) | |
if (Slots[i].Value != null) | |
{ | |
var obj = Slots[i].Value; | |
if (pred(obj) && Remove(obj)) | |
++matches; | |
} | |
return matches; | |
} | |
public IEnumerator<object> GetEnumerator() | |
{ | |
return GetEnum(); | |
} | |
public IEnumerator<object> GetEnum() | |
{ | |
for (var i = 0; i < Count; i++) | |
if (Slots[i].Value != null) | |
yield return Slots[i].Value; | |
} | |
internal struct Slot | |
{ | |
public int Next; | |
public object Value; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment