{"id":397,"date":"2021-04-12T16:00:46","date_gmt":"2021-04-12T16:00:46","guid":{"rendered":"https:\/\/michaeljohnsteiner.com\/?p=397"},"modified":"2021-05-13T09:09:35","modified_gmt":"2021-05-13T09:09:35","slug":"objectbase-cs","status":"publish","type":"post","link":"https:\/\/michaeljohnsteiner.com\/index.php\/2021\/04\/12\/objectbase-cs\/","title":{"rendered":"ObjectBase.cs"},"content":{"rendered":"\n<p>Base Object Array Class<\/p>\n\n\n\n<p>Updated: May-13,2021<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Runtime.Serialization;\nusing System.Security.Cryptography;\nusing System.Threading;\n\/\/\/ &lt;summary>\n\/\/\/     Name: ObjectBase.cs\n\/\/\/     Date: January-1, 2021\n\/\/\/     Note: Map indexed object array.\n\/\/\/     Attn: Should be used for ALL object classes going forward *** MJS *** 4.11.21\n\/\/\/ &lt;\/summary>\n[DebuggerDisplay(\"Count = {\" + nameof(Count) + \"}\")]\n[Serializable]\npublic class ObjectBase : MonitorActionFuncWrapper, IEnumerable&lt;object>\n{\n    [NonSerialized] private static readonly SizeHelper32      _sh = new();\n    [NonSerialized] private readonly        SerializationInfo _sInfo;\n    private volatile                        object[]          _array;\n    [NonSerialized] private                 HashAlgorithm     _hash;\n    [NonSerialized] private volatile        Set20B            _map;\n    public volatile                         int               Count;\n    public ObjectBase() : this(1024)\n    {\n    }\n    public ObjectBase(int size, int bitWidth = 64)\n    {\n        BitWidth = bitWidth;\n        SelectHashAlgorithm(bitWidth);\n        _array = new object[size];\n        _map   = new Set20B(size);\n    }\n    public ObjectBase(ICollection col, int size, int bitWidth = 64)\n    {\n        BitWidth = bitWidth;\n        SelectHashAlgorithm(bitWidth);\n        _array = new object[size];\n        _map   = new Set20B(size);\n        AddRange(col);\n    }\n    protected ObjectBase(SerializationInfo info, StreamingContext context)\n    {\n        _sInfo = info;\n    }\n    public object this[int index]\n    {\n        get\n        {\n            return Lock(this, () =>\n            {\n                var array = _array;\n                if (index >= Count)\n                    throw new Exception(\"Error: Index out of range.\");\n                return array[index];\n            });\n        }\n    }\n    public int BitWidth\n    {\n        get;\n        private set;\n    }\n    public KeyValuePair&lt;IGrouping&lt;int, int>, int>[] BucketDepthList\n    {\n        get\n        {\n            var lst = new List&lt;int>();\n            foreach (var i in _map)\n                lst.Add(_map.GetBucketDepth(i).bucketDepth);\n            var grp = lst.GroupBy(x => x)\n                .Where(g => g.Count() > 1)\n                .ToDictionary(x => x, y => y.Count())\n                .ToArray()\n                .OrderByDescending(z => z.Value)\n                .ToArray();\n            return grp;\n        }\n    }\n    public IEnumerator&lt;object> GetEnumerator()\n    {\n        return Lock(this, () =>\n        {\n            return GetEnum();\n        });\n    }\n    IEnumerator IEnumerable.GetEnumerator()\n    {\n        return Lock(this, () =>\n        {\n            return GetEnum();\n        });\n    }\n    public void GetObjectData(SerializationInfo info, StreamingContext context)\n    {\n        info.AddValue(\"BitWidth\", BitWidth);\n        info.AddValue(\"Array\",    _array, typeof(object[]));\n    }\n    public void OnDeserialization(object sender)\n    {\n        if (_sInfo == null)\n            return;\n        BitWidth = _sInfo.GetInt32(\"BitWidth\");\n        Count    = 0;\n        var array = (object[]) _sInfo.GetValue(\"Array\", typeof(object[]));\n        if (array == null)\n            throw new Exception(\"Array cannot be null.\");\n        foreach (var t in array)\n            Add(t);\n    }\n    public bool Add(object item)\n    {\n        return Lock(this, () =>\n        {\n            var (idx, exists) = GetIndex(item);\n            if (idx >= _array.Length)\n                Array.Resize(ref _array, (int) _sh.GetNewSize((ulong) _array.Length));\n            if (exists == false)\n            {\n                _array[idx] = item;\n                Interlocked.Increment(ref Count);\n            }\n            return exists == false;\n        });\n    }\n    public void AddRange(ICollection col)\n    {\n        var array = col.Cast&lt;object>().ToArray();\n        array.AsParallel().WithDegreeOfParallelism(2).ForAll(i =>\n        {\n            Add(i);\n        });\n    }\n    public void Clear()\n    {\n        Lock(this, () =>\n        {\n            _array = new object[_array.Length];\n            _map   = new Set20B(_array.Length);\n        });\n    }\n    private IEnumerator&lt;object> GetEnum()\n    {\n        var ary = _array;\n        var cnt = Count;\n        for (var i = 0; i &lt; cnt; i++)\n            yield return ary[i];\n    }\n    public object[] ToArray()\n    {\n        return Lock(this, () =>\n        {\n            var newArray = new object[Count];\n            using (var en = GetEnumerator())\n            {\n                var ptr = 0;\n                while (en.MoveNext())\n                {\n                    var value = en.Current;\n                    if (value == null)\n                        break;\n                    newArray[ptr++] = value;\n                }\n                return newArray;\n            }\n        });\n    }\n    public bool Contains(object item)\n    {\n        return Lock(this, () =>\n        {\n            return MapContains(item);\n        });\n    }\n    private bool MapContains(object obj)\n    {\n        if (obj == null)\n            throw new ArgumentNullException(nameof(obj));\n        var bytes = obj.GetBytes();\n        var hash  = _hash.ComputeHash(bytes, 0, bytes.Length);\n        return _map.Contains(hash);\n    }\n    internal (int idx, bool exists) GetIndex(object obj, bool add = true)\n    {\n        return Lock(this, () =>\n        {\n            var bytes  = obj.GetBytes();\n            var hash   = _hash.ComputeHash(bytes, 0, bytes.Length);\n            var exists = false;\n            if (!_map.Contains(hash))\n            {\n                if (add)\n                    _map.Add(hash);\n            }\n            else\n            {\n                exists = true;\n            }\n            return (_map.FindEntry(hash), exists);\n        });\n    }\n    private void SelectHashAlgorithm(int bitWidth)\n    {\n        BitWidth = bitWidth;\n        switch (bitWidth)\n        {\n            case 64:\n                _hash = new Fnv1a64Fast();\n                break;\n            case 128:\n                _hash = new FNVx();\n                break;\n            case 256:\n                _hash = new FNVx(256);\n                break;\n            case 512:\n                _hash = new FNVx(512);\n                break;\n            case 1024:\n                _hash = new FNVx(1024);\n                break;\n            default:\n                throw new ArgumentException(\"Supported bit widths are: 64, 128, 256, 512 and 1024 Bits.\");\n        }\n    }\n    public bool Remove(object item)\n    {\n        var (idx, exists) = GetIndex(item);\n        if (exists)\n        {\n            var tob = new ObjectBase(_array.Length, BitWidth);\n            for (var i = 0; i &lt; Count; i++)\n                if (i != idx)\n                    tob.Add(_array[i]);\n            Count  = tob.Count;\n            _array = tob._array;\n            _map   = tob._map;\n            return true;\n        }\n        return false;\n    }\n    public bool Remove(int index)\n    {\n        if (index &lt; _array.Length)\n        {\n            var (idx, exists) = GetIndex(_array[index]);\n            if (exists &amp;&amp; idx == index)\n            {\n                var tob = new ObjectBase(_array.Length, BitWidth);\n                for (var i = 0; i &lt; Count; i++)\n                    if (i != idx)\n                        tob.Add(_array[i]);\n                Count  = tob.Count;\n                _array = tob._array;\n                _map   = tob._map;\n                return true;\n            }\n        }\n        return false;\n    }\n    public void UnionWith(IEnumerable&lt;object> other)\n    {\n        if (other == null)\n            throw new ArgumentNullException(nameof(other));\n        foreach (var obj in other)\n            Add(obj);\n    }\n    public void ExceptWith(IEnumerable&lt;object> other)\n    {\n        if (other == null)\n            throw new ArgumentNullException(nameof(other));\n        if (Equals(other, this))\n            Clear();\n        else\n            foreach (var obj in other)\n                Remove(obj);\n    }\n    public bool Overlaps(IEnumerable&lt;object> other)\n    {\n        if (other == null)\n            throw new ArgumentNullException(nameof(other));\n        foreach (var obj in other)\n            if (Contains(obj))\n                return true;\n        return false;\n    }\n    public int RemoveWhere(Predicate&lt;object> match)\n    {\n        if (match == null)\n            throw new ArgumentNullException(nameof(match));\n        var num = 0;\n        for (var i = 0; i &lt; Count; ++i)\n        {\n            var obj = _array[i];\n            if (match(obj) &amp;&amp; Remove(obj))\n                ++num;\n        }\n        return num;\n    }\n    public bool ContainsAllElements(IEnumerable&lt;object> other)\n    {\n        foreach (var obj in other)\n            if (!Contains(obj))\n                return false;\n        return true;\n    }\n    public void TrimExcess()\n    {\n        Array.Resize(ref _array, Count);\n    }\n    internal class Set20B\n    {\n        private int    _count;\n        private int[]  _hashBuckets;\n        private Slot[] _slots;\n        public Set20B(int size)\n        {\n            _hashBuckets = new int[size];\n            _slots       = new Slot[size];\n            _count       = 0;\n        }\n        public IEnumerator&lt;byte[]> GetEnumerator()\n        {\n            for (var i = 0; i &lt; _count; i++)\n                if (_slots[i].HashCode > 0)\n                    yield return _slots[i].Value;\n        }\n        public bool Add(byte[] item)\n        {\n            var hashCode = GetHashCode(item) &amp; int.MaxValue;\n            if (FindEntry(item, hashCode) != -1)\n                return true;\n            if (_count >= _slots.Length)\n                Resize();\n            var hashPos = hashCode % _hashBuckets.Length;\n            _slots[_count].Next     = _hashBuckets[hashPos] - 1;\n            _slots[_count].Value    = item;\n            _slots[_count].HashCode = hashCode;\n            _hashBuckets[hashPos]   = _count + 1;\n            ++_count;\n            return false;\n        }\n        private void Resize()\n        {\n            var newSize        = (int) _sh.GetNewSize((ulong) _hashBuckets.Length);\n            var newSlots       = new Slot[newSize];\n            var newHashBuckets = new int[newSize];\n            var newCount       = 0;\n            var en             = GetEnumerator();\n            while (en.MoveNext())\n            {\n                var item     = en.Current;\n                var hashCode = GetHashCode(item) &amp; int.MaxValue;\n                var hashPos  = hashCode % newHashBuckets.Length;\n                newSlots[newCount].Next     = newHashBuckets[hashPos] - 1;\n                newSlots[newCount].Value    = item;\n                newSlots[newCount].HashCode = hashCode;\n                newHashBuckets[hashPos]     = newCount + 1;\n                ++newCount;\n            }\n            _slots       = newSlots;\n            _hashBuckets = newHashBuckets;\n            _count       = newCount;\n        }\n        private int FindEntry(byte[] item, int hashCode)\n        {\n            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)\n                if (_slots[position].HashCode == hashCode &amp;&amp; Equals(_slots[position].Value, item))\n                    return position;\n            return -1;\n        }\n        public int FindEntry(byte[] item)\n        {\n            var hashCode = GetHashCode(item) &amp; int.MaxValue;\n            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)\n                if (_slots[position].HashCode == hashCode &amp;&amp; Equals(_slots[position].Value, item))\n                    return position;\n            return -1;\n        }\n        public (int bucketDepth, bool exists) GetBucketDepth(byte[] item)\n        {\n            var hashCode    = GetHashCode(item) &amp; int.MaxValue;\n            var bucketDepth = 1;\n            for (var position = _hashBuckets[hashCode % _hashBuckets.Length] - 1; position >= 0; position = _slots[position].Next)\n            {\n                if (_slots[position].HashCode == hashCode &amp;&amp; Equals(_slots[position].Value, item))\n                    return (bucketDepth, true);\n                ++bucketDepth;\n            }\n            return (-1, false);\n        }\n        public bool Contains(byte[] item)\n        {\n            return FindEntry(item, GetHashCode(item) &amp; int.MaxValue) != -1;\n        }\n        private static bool Equals(byte[] x, byte[] y)\n        {\n            if (x == null || y == null)\n                return false;\n            return x.Length == y.Length &amp;&amp; x.Compare(y);\n        }\n        private static unsafe int GetHashCode(byte[] obj)\n        {\n            var cbSize = obj.Length;\n            var h1     = 0xCBF29CE484222325;\n            fixed (byte* pb = obj)\n            {\n                var nb = pb;\n                while (cbSize >= 8)\n                {\n                    h1     ^= *(ulong*) nb;\n                    h1     *= 0x100000001B3;\n                    nb     += 8;\n                    cbSize -= 8;\n                }\n            }\n            return (int) h1;\n        }\n        private struct Slot\n        {\n            public int    HashCode;\n            public int    Next;\n            public byte[] Value;\n        }\n    }\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Base Object Array Class Updated: May-13,2021<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/397"}],"collection":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/comments?post=397"}],"version-history":[{"count":5,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/397\/revisions"}],"predecessor-version":[{"id":416,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/posts\/397\/revisions\/416"}],"wp:attachment":[{"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/media?parent=397"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/categories?post=397"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/michaeljohnsteiner.com\/index.php\/wp-json\/wp\/v2\/tags?post=397"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}