Skip to content

Instantly share code, notes, and snippets.

@mjs3339
Created January 12, 2021 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjs3339/06196c2752e33e5bfdf8a292169c64e1 to your computer and use it in GitHub Desktop.
Save mjs3339/06196c2752e33e5bfdf8a292169c64e1 to your computer and use it in GitHub Desktop.
Sequential Ordering Object Indexer
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