Base Object Array Class
Updated: May-13,2021
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Threading;
/// <summary>
/// Name: ObjectBase.cs
/// Date: January-1, 2021
/// Note: Map indexed object array.
/// Attn: Should be used for ALL object classes going forward *** MJS *** 4.11.21
/// </summary>
[DebuggerDisplay("Count = {" + nameof(Count) + "}")]
[Serializable]
public class ObjectBase : MonitorActionFuncWrapper, IEnumerable<object>
{
[NonSerialized] private static readonly SizeHelper32 _sh = new();
[NonSerialized] private readonly SerializationInfo _sInfo;
private volatile object[] _array;
[NonSerialized] private HashAlgorithm _hash;
[NonSerialized] private volatile Set20B _map;
public volatile int Count;
public ObjectBase() : this(1024)
{
}
public ObjectBase(int size, int bitWidth = 64)
{
BitWidth = bitWidth;
SelectHashAlgorithm(bitWidth);
_array = new object[size];
_map = new Set20B(size);
}
public ObjectBase(ICollection col, int size, int bitWidth = 64)
{
BitWidth = bitWidth;
SelectHashAlgorithm(bitWidth);
_array = new object[size];
_map = new Set20B(size);
AddRange(col);
}
protected ObjectBase(SerializationInfo info, StreamingContext context)
{
_sInfo = info;
}
public object this[int index]
{
get
{
return Lock(this, () =>
{
var array = _array;
if (index >= Count)
throw new Exception("Error: Index out of range.");
return array[index];
});
}
}
public int BitWidth
{
get;
private set;
}
public KeyValuePair<IGrouping<int, int>, int>[] BucketDepthList
{
get
{
var lst = new List<int>();
foreach (var i in _map)
lst.Add(_map.GetBucketDepth(i).bucketDepth);
var grp = lst.GroupBy(x => x)
.Where(g => g.Count() > 1)
.ToDictionary(x => x, y => y.Count())
.ToArray()
.OrderByDescending(z => z.Value)
.ToArray();
return grp;
}
}
public IEnumerator<object> GetEnumerator()
{
return Lock(this, () =>
{
return GetEnum();
});
}
IEnumerator IEnumerable.GetEnumerator()
{
return Lock(this, () =>
{
return GetEnum();
});
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("BitWidth", BitWidth);
info.AddValue("Array", _array, typeof(object[]));
}
public void OnDeserialization(object sender)
{
if (_sInfo == null)
return;
BitWidth = _sInfo.GetInt32("BitWidth");
Count = 0;
var array = (object[]) _sInfo.GetValue("Array", typeof(object[]));
if (array == null)
throw new Exception("Array cannot be null.");
foreach (var t in array)
Add(t);
}
public bool Add(object item)
{
return Lock(this, () =>
{
var (idx, exists) = GetIndex(item);
if (idx >= _array.Length)
Array.Resize(ref _array, (int) _sh.GetNewSize((ulong) _array.Length));
if (exists == false)
{
_array[idx] = item;
Interlocked.Increment(ref Count);
}
return exists == false;
});
}
public void AddRange(ICollection col)
{
var array = col.Cast<object>().ToArray();
array.AsParallel().WithDegreeOfParallelism(2).ForAll(i =>
{
Add(i);
});
}
public void Clear()
{
Lock(this, () =>
{
_array = new object[_array.Length];
_map = new Set20B(_array.Length);
});
}
private IEnumerator<object> GetEnum()
{
var ary = _array;
var cnt = Count;
for (var i = 0; i < cnt; i++)
yield return ary[i];
}
public object[] ToArray()
{
return Lock(this, () =>
{
var newArray = new object[Count];
using (var en = GetEnumerator())
{
var ptr = 0;
while (en.MoveNext())
{
var value = en.Current;
if (value == null)
break;
newArray[ptr++] = value;
}
return newArray;
}
});
}
public bool Contains(object item)
{
return Lock(this, () =>
{
return MapContains(item);
});
}
private bool MapContains(object obj)
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
var bytes = obj.GetBytes();
var hash = _hash.ComputeHash(bytes, 0, bytes.Length);
return _map.Contains(hash);
}
internal (int idx, bool exists) GetIndex(object obj, bool add = true)
{
return Lock(this, () =>
{
var bytes = obj.GetBytes();
var hash = _hash.ComputeHash(bytes, 0, bytes.Length);
var exists = false;
if (!_map.Contains(hash))
{
if (add)
_map.Add(hash);
}
else
{
exists = true;
}
return (_map.FindEntry(hash), exists);
});
}
private void SelectHashAlgorithm(int bitWidth)
{
BitWidth = bitWidth;
switch (bitWidth)
{
case 64:
_hash = new Fnv1a64Fast();
break;
case 128:
_hash = new FNVx();
break;
case 256:
_hash = new FNVx(256);
break;
case 512:
_hash = new FNVx(512);
break;
case 1024:
_hash = new FNVx(1024);
break;
default:
throw new ArgumentException("Supported bit widths are: 64, 128, 256, 512 and 1024 Bits.");
}
}
public bool Remove(object item)
{
var (idx, exists) = GetIndex(item);
if (exists)
{
var tob = new ObjectBase(_array.Length, BitWidth);
for (var i = 0; i < Count; i++)
if (i != idx)
tob.Add(_array[i]);
Count = tob.Count;
_array = tob._array;
_map = tob._map;
return true;
}
return false;
}
public bool Remove(int index)
{
if (index < _array.Length)
{
var (idx, exists) = GetIndex(_array[index]);
if (exists && idx == index)
{
var tob = new ObjectBase(_array.Length, BitWidth);
for (var i = 0; i < Count; i++)
if (i != idx)
tob.Add(_array[i]);
Count = tob.Count;
_array = tob._array;
_map = tob._map;
return true;
}
}
return false;
}
public void UnionWith(IEnumerable<object> other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
foreach (var obj in other)
Add(obj);
}
public void ExceptWith(IEnumerable<object> other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
if (Equals(other, this))
Clear();
else
foreach (var obj in other)
Remove(obj);
}
public bool Overlaps(IEnumerable<object> other)
{
if (other == null)
throw new ArgumentNullException(nameof(other));
foreach (var obj in other)
if (Contains(obj))
return true;
return false;
}
public int RemoveWhere(Predicate<object> match)
{
if (match == null)
throw new ArgumentNullException(nameof(match));
var num = 0;
for (var i = 0; i < Count; ++i)
{
var obj = _array[i];
if (match(obj) && Remove(obj))
++num;
}
return num;
}
public bool ContainsAllElements(IEnumerable<object> other)
{
foreach (var obj in other)
if (!Contains(obj))
return false;
return true;
}
public void TrimExcess()
{
Array.Resize(ref _array, Count);
}
internal class Set20B
{
private int _count;
private int[] _hashBuckets;
private Slot[] _slots;
public Set20B(int size)
{
_hashBuckets = new int[size];
_slots = new Slot[size];
_count = 0;
}
public IEnumerator<byte[]> GetEnumerator()
{
for (var i = 0; i < _count; i++)
if (_slots[i].HashCode > 0)
yield return _slots[i].Value;
}
public bool Add(byte[] item)
{
var hashCode = GetHashCode(item) & int.MaxValue;
if (FindEntry(item, hashCode) != -1)
return true;
if (_count >= _slots.Length)
Resize();
var hashPos = hashCode % _hashBuckets.Length;
_slots[_count].Next = _hashBuckets[hashPos] - 1;
_slots[_count].Value = item;
_slots[_count].HashCode = hashCode;
_hashBuckets[hashPos] = _count + 1;
++_count;
return false;
}
private void Resize()
{
var newSize = (int) _sh.GetNewSize((ulong) _hashBuckets.Length);
var newSlots = new Slot[newSize];
var newHashBuckets = new int[newSize];
var newCount = 0;
var en = GetEnumerator();
while (en.MoveNext())
{
var item = en.Current;
var hashCode = GetHashCode(item) & int.MaxValue;
var hashPos = hashCode % newHashBuckets.Length;
newSlots[newCount].Next = newHashBuckets[hashPos] - 1;
newSlots[newCount].Value = item;
newSlots[newCount].HashCode = hashCode;
newHashBuckets[hashPos] = newCount + 1;
++newCount;
}
_slots = newSlots;
_hashBuckets = newHashBuckets;
_count = newCount;
}
private int FindEntry(byte[] item, int hashCode)
{
for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
return position;
return -1;
}
public int FindEntry(byte[] item)
{
var hashCode = GetHashCode(item) & int.MaxValue;
for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
return position;
return -1;
}
public (int bucketDepth, bool exists) GetBucketDepth(byte[] item)
{
var hashCode = GetHashCode(item) & int.MaxValue;
var bucketDepth = 1;
for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)
{
if (_slots[position].HashCode == hashCode && Equals(_slots[position].Value, item))
return (bucketDepth, true);
++bucketDepth;
}
return (-1, false);
}
public bool Contains(byte[] item)
{
return FindEntry(item, GetHashCode(item) & int.MaxValue) != -1;
}
private static bool Equals(byte[] x, byte[] y)
{
if (x == null || y == null)
return false;
return x.Length == y.Length && x.Compare(y);
}
private static unsafe int GetHashCode(byte[] obj)
{
var cbSize = obj.Length;
var h1 = 0xCBF29CE484222325;
fixed (byte* pb = obj)
{
var nb = pb;
while (cbSize >= 8)
{
h1 ^= *(ulong*) nb;
h1 *= 0x100000001B3;
nb += 8;
cbSize -= 8;
}
}
return (int) h1;
}
private struct Slot
{
public int HashCode;
public int Next;
public byte[] Value;
}
}
}