From f8ccd0d505c7bf310efd33e3dc8ea7e04f6516fc Mon Sep 17 00:00:00 2001 From: "U-WALDRENNACH\\haraldwolff" Date: Wed, 18 Nov 2020 00:09:21 +0100 Subject: [PATCH] Initial Commit --- .gitignore | 41 ++ BTree.cs | 679 ++++++++++++++++++++++++++++++++++ BTreeValueList.cs | 159 ++++++++ BTreeValueSet.cs | 134 +++++++ Cache.cs | 96 +++++ IDict.cs | 31 ++ LinkedList.cs | 44 +++ LinkedListItem.cs | 25 ++ MappingBTree.cs | 68 ++++ ObjectIdentityCache.cs | 68 ++++ WeakKeyDictionary.cs | 233 ++++++++++++ WeakKeyReferenceDictionary.cs | 31 ++ WeakValueDictionary.cs | 118 ++++++ ln.collections.csproj | 10 + 14 files changed, 1737 insertions(+) create mode 100644 .gitignore create mode 100644 BTree.cs create mode 100644 BTreeValueList.cs create mode 100644 BTreeValueSet.cs create mode 100644 Cache.cs create mode 100644 IDict.cs create mode 100644 LinkedList.cs create mode 100644 LinkedListItem.cs create mode 100644 MappingBTree.cs create mode 100644 ObjectIdentityCache.cs create mode 100644 WeakKeyDictionary.cs create mode 100644 WeakKeyReferenceDictionary.cs create mode 100644 WeakValueDictionary.cs create mode 100644 ln.collections.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf793ed --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# Autosave files +*~ + +# build +[Oo]bj/ +[Bb]in/ +packages/ +TestResults/ + +# globs +Makefile.in +*.DS_Store +*.sln.cache +*.suo +*.cache +*.pidb +*.userprefs +*.usertasks +config.log +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.user +*.tar.gz +tarballs/ +test-results/ +Thumbs.db +.vs/ + +# Mac bundle stuff +*.dmg +*.app + +# resharper +*_Resharper.* +*.Resharper + +# dotCover +*.dotCover diff --git a/BTree.cs b/BTree.cs new file mode 100644 index 0000000..e15fdab --- /dev/null +++ b/BTree.cs @@ -0,0 +1,679 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTree : IDict + { + public Comparison Comparison { get; } + public int Count => count; + public bool Empty => count == 0; + + public int LeftDepth => headNode != null ? headNode.LeftDepth : 0; + public int RightDepth => headNode != null ? headNode.RightDepth : 0; + public int Depth => Math.Max(LeftDepth, RightDepth); + + public int LimBalance + { + get => limBalance; + set + { + if (value == 0) + throw new ArgumentException(nameof(value)); + limBalance = value; + } + } + + TreeNode headNode; + private int count; + private int limBalance; + + public BTree() + { + if (!typeof(K).GetInterfaces().Contains(typeof(IComparable))) + { + Comparison = (K x, K y) => x.GetHashCode() - y.GetHashCode(); + } + else + { + Comparison = (K x, K y) => ((IComparable)x).CompareTo(y); + } + } + public BTree(Comparison comparison) + { + Comparison = comparison; + } + + public V this[K key] + { + get + { + TreeNode node = Find(key); + if (object.ReferenceEquals(node,null)) + throw new KeyNotFoundException(); + return node.Value; + } + set + { + TreeNode node = Find(key); + if (object.ReferenceEquals(node, null)) + { + Add(key, value); + } + else + { + node.Value = value; + } + } + } + public Boolean TryGet(K key, out V value) + { + TreeNode node = Find(key); + if (object.ReferenceEquals(node, null)) + { + value = default(V); + return false; + } + + value = node.Value; + return true; + } + + public virtual void Add(K key,V value) + { + TreeNode treeNode = new TreeNode(this, key, value); + Insert(treeNode); + count++; + } + public virtual void TryAdd(K key,V value) + { + TreeNode treeNode = Find(key); + if (object.ReferenceEquals(treeNode, null)) + Add(key, value); + } + + public virtual void Remove(K key) + { + TreeNode node = Find(key); + if (object.ReferenceEquals(node, null)) + throw new KeyNotFoundException(); + + Remove(node); + count--; + } + public virtual bool TryRemove(K key) + { + TreeNode node = Find(key); + if (node != null) + Remove(node); + + return node != null; + } + + public bool ContainsKey(K key) + { + return Find(key) != null; + } + public bool ContainsValue(V value) + { + return Values.Contains(value); + } + + public K GetKey(K key) + { + if (TryGetKey(key, out K storedKey)) + return storedKey; + throw new KeyNotFoundException(); + } + public bool TryGetKey(K key,out K storedKey) + { + TreeNode treeNode = Find(key); + if (treeNode == null) + { + storedKey = default(K); + return false; + } else + { + storedKey = treeNode.Key; + return true; + } + + } + + public void Clear() + { + headNode = null; + count = 0; + } + + public K First() + { + TreeNode node = First(headNode); + if (node != null) + return node.Key; + throw new KeyNotFoundException("BTree is empty"); + } + public V FirstValue() + { + TreeNode node = First(headNode); + if (node != null) + return node.Value; + throw new KeyNotFoundException("BTree is empty"); + } + public bool TryGetFirstValue(out V value) + { + TreeNode node = First(headNode); + if (node != null) + { + value = node.Value; + return true; + } + value = default(V); + return false; + } + + public K Last() + { + TreeNode node = Last(headNode); + if (node != null) + return node.Key; + throw new KeyNotFoundException("BTree is empty"); + } + public V LastValue() + { + TreeNode node = Last(headNode); + if (node != null) + return node.Value; + throw new KeyNotFoundException("BTree is empty"); + } + public bool TryGetLastValue(out V value) + { + TreeNode node = Last(headNode); + if (node != null) + { + value = node.Value; + return true; + } + value = default(V); + return false; + } + + public K Previous(K current) + { + TreeNode node = Previous(Find(current)); + if (node != null) + return node.Key; + throw new KeyNotFoundException(); + } + public bool TryGetPrevious(K current, out K previous) + { + TreeNode node = Previous(Find(current)); + if (node != null) + { + previous = node.Key; + return true; + } + previous = default(K); + return false; + } + public bool TryGetPreviousValue(K current, out V previous) + { + TreeNode node = Previous(Find(current)); + if (node != null) + { + previous = node.Value; + return true; + } + previous = default(V); + return false; + } + + public K Next(K current) + { + TreeNode node = Next(Find(current)); + if (node != null) + return node.Key; + throw new KeyNotFoundException(); + } + public bool TryGetNext(K current, out K next) + { + TreeNode node = Next(Find(current)); + if (node != null) + { + next = node.Key; + return true; + } + next = default(K); + return false; + } + public bool TryGetNextValue(K current, out V next) + { + TreeNode node = Next(Find(current)); + if (node != null) + { + next = node.Value; + return true; + } + next = default(V); + return false; + } + + + + + private TreeNode First(TreeNode node) + { + if (object.ReferenceEquals(node, null)) + return null; + + while (node.Left != null) + node = node.Left; + return node; + } + + private TreeNode Last(TreeNode node) + { + if (object.ReferenceEquals(node, null)) + return null; + + while (node.Right != null) + node = node.Right; + return node; + } + + private TreeNode Find(K key) + { + TreeNode node = headNode; + + while (node != null) + { + int comp = Comparison(key, node.Key); + if (comp == 0) + return node; + else if (comp < 0) + node = node.Left; + else + node = node.Right; + } + return null; + } + private TreeNode FindFirstGE(K key) + { + TreeNode node = headNode; + TreeNode minNode = null; + + while (node != null) + { + if ((Comparison(node.Key, key) >= 0) && ((minNode == null) || (Comparison(node.Key, minNode.Key) < 0))) + minNode = node; + + int comp = Comparison(key, node.Key); + if (comp == 0) + return node; + else if (comp < 0) + { + if (node.Left == null) + return minNode; + node = node.Left; + } + else + { + if (node.Right == null) + return minNode; + node = node.Right; + } + } + return null; + } + + private TreeNode Previous(TreeNode node) + { + if (node.Left != null) + return Last(node.Left); + + while (node != null) + { + if (object.ReferenceEquals(node.Parent, null)) + return null; + + if (node.Parent.Right == node) + return node.Parent; + + node = node.Parent; + } + return null; + } + + private TreeNode Next(TreeNode node) + { + if (node.Right != null) + return First(node.Right); + + while (node != null) + { + if (object.ReferenceEquals(node.Parent, null)) + return null; + + if (node.Parent.Left == node) + return node.Parent; + + node = node.Parent; + } + return null; + } + + private void Insert(TreeNode newNode) + { + if (object.ReferenceEquals(headNode, null)) + { + headNode = newNode; + } + else + { + TreeNode node = headNode; + while (true) + { + int c = Comparison(newNode.Key, node.Key); + if (c == 0) + { + throw new ArgumentException("Key exists"); + } + else if (c < 0) + { + if (node.Left == null) + { + newNode.Attach(node); + Balance(node.Parent); + return; + } + else + node = node.Left; + } + else + { + if (node.Right == null) + { + newNode.Attach(node); + Balance(node.Parent); + return; + } + else + node = node.Right; + } + } + } + } + + private void Remove(TreeNode node) + { + TreeNode p = node.Parent; + + if ((node.Left != null) && (node.Right != null)) + { + TreeNode replace = Last(node.Left); + TreeNode replaceParent = replace.Parent; + + if (replaceParent == node) + { + node.Detach(); + node.Right.Attach(node.Left); + node.Left.Attach(p); + } + else + { + TreeNode left = node.Left; + TreeNode right = node.Right; + + node.Detach(); + if (left != null) + left.Detach(); + if (right != null) + right.Detach(); + + replace.Detach(); + if (replace.Left != null) + replace.Left.Attach(replaceParent); + + replace.Attach(p); + if (left != null) + left.Attach(replace); + if (right != null) + right.Attach(replace); + } + + Balance(replace); + } else + { + node.Detach(); + if (node.Left != null) + node.Left.Attach(p); + else if (node.Right != null) + node.Right.Attach(p); + + Balance(p); + } + } + + private void RotateRight(TreeNode node) + { + TreeNode l = node.Left; + TreeNode p = node.Parent; + + node.Detach(); + l.Attach(p); + if (l.Right!= null) + l.Right.Attach(node); + node.Attach(l); + } + private void RotateLeft(TreeNode node) + { + TreeNode r = node.Right; + TreeNode p = node.Parent; + + node.Detach(); + r.Attach(p); + if (r.Left != null) + r.Left.Attach(node); + node.Attach(r); + } + + private void Balance(TreeNode node) + { + while (node != null) + { + TreeNode p = node.Parent; + int myBalance = node.LeftDepth - node.RightDepth; + if (myBalance + limBalance < 0) + { + RotateLeft(node); + } + else if (myBalance - limBalance > 0) + { + RotateRight(node); + } + node = p; + } + } + + public IEnumerable KeysFrom(K first) + { + TreeNode node = Find(first); + while (node != null) + { + yield return node.Key; + node = Next(node); + } + } + + public IEnumerable KeysGreaterEqual(K first) + { + TreeNode node = FindFirstGE(first); + while (node != null) + { + yield return node.Key; + node = Next(node); + } + } + + public void Add(object key, object value) => Add((K)key, (V)value); + public void Remove(object key) => Remove((K)key); + public bool ContainsKey(object key) => ContainsKey((K)key); + public object this[object key] { get => this[(K)key]; set => this[(K)key] = (V)value; } + + public IEnumerable Keys + { + get + { + TreeNode node = First(headNode); + while (node != null) + { + yield return node.Key; + node = Next(node); + } + } + } + public IEnumerable Values + { + get + { + TreeNode node = First(headNode); + while (node != null) + { + yield return node.Value; + node = Next(node); + } + } + } + + IEnumerable IDict.Keys => Keys; + IEnumerable IDict.Values => Values; + + class TreeNode + { + public BTree Tree { get; } + + public K Key { get; private set; } + public V Value { get; set; } + + public TreeNode Parent { get; private set; } + + public int Depth => 1 + Math.Max(LeftDepth, RightDepth); + + //public int LeftDepth => Left != null ? Left.Depth : 0; + //public int RightDepth => Right != null ? Right.Depth : 0; + public int LeftDepth => leftDepth; + public int RightDepth => rightDepth; + + public TreeNode Left { get; private set; } + public TreeNode Right { get; private set; } + + private int leftDepth = 0; + private int rightDepth = 0; + + + public TreeNode(BTree tree, K key, V value) + { + Tree = tree; + Key = key; + Value = value; + } + + public void Attach(TreeNode parent) + { + if (!object.ReferenceEquals(this.Parent, null)) + Detach(); + + if (parent == null) + { + Tree.headNode = this; + Parent = null; + } + else + { + int comp = Tree.Comparison(Key, parent.Key); + if (comp == 0) + throw new ArgumentException(String.Format("Key already exists in BTree ({0})",Key)); + else if (comp < 0) + { + if (parent.Left != null) + throw new InvalidOperationException("Another Key is already attached to Parent (left)"); + parent.Left = this; + parent.leftDepth = Depth + 1; + } + else + { + if (parent.Right != null) + throw new InvalidOperationException("Another Key is already attached to Parent (right)"); + parent.Right = this; + parent.rightDepth = Depth + 1; + } + Parent = parent; + } + } + public void Detach() + { + if (this.Parent != null) + { + if (this.Parent.Left == this) + { + this.Parent.Left = null; + this.Parent.leftDepth = 0; + } + else if (this.Parent.Right == this) + { + this.Parent.Right = null; + this.Parent.rightDepth = 0; + } + + Parent = null; + } else if (Tree.headNode == this) + { + Tree.headNode = null; + } + } + + public override string ToString() + { + return String.Format("[TreeNode Key={0} Value={1}]",Key,Value); + } + + } + + } + + public class BTree : BTree + { + public BTree() + { + } + public BTree(Comparison comparison) + :base(comparison) + { + } + + public void Add(K key) + { + Add(key, null); + } + public void TryAdd(K key) + { + TryAdd(key, null); + } + + public void AddRange(IEnumerable keys) + { + foreach (K key in keys) + { + Add(key, null); + } + } + public void TryAddRange(IEnumerable keys) + { + foreach (K key in keys) + { + TryAdd(key, null); + } + } + } +} diff --git a/BTreeValueList.cs b/BTreeValueList.cs new file mode 100644 index 0000000..e012cab --- /dev/null +++ b/BTreeValueList.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTreeValueList + { + public bool Empty => bTree.Empty; + + BTree> bTree; + + public BTreeValueList() + { + bTree = new BTree>(); + } + public BTreeValueList(Comparison comparison) + { + bTree = new BTree>(comparison); + } + + public IEnumerable this[K key] + { + get + { + if (TryGet(key,out IEnumerable values)) + { + return values; + } + throw new KeyNotFoundException(); + } + } + + public void Add(K key, V value) + { + if (!TryAdd(key, value)) + throw new ArgumentException("duplicate key"); + } + public void Remove(K key, V value) + { + if (!TryRemove(key, value)) + throw new KeyNotFoundException(); + } + public void Remove(K key) + { + if (!TryRemove(key)) + throw new KeyNotFoundException(); + } + + public bool TryAdd(K key,V value) + { + if (!bTree.TryGet(key, out List values)) + { + values = new List(); + bTree.Add(key, values); + } + values.Add(value); + return true; + } + public bool TryGet(K key,out IEnumerable values) + { + if ((!bTree.TryGet(key, out List v)) || (v.Count == 0)) + { + values = v; + return false; + } + values = v; + return true; + } + public bool TryRemove(K key) + { + return bTree.TryRemove(key); + } + public bool TryRemove(K key,V value) + { + if (bTree.TryGet(key, out List values)) + { + bool success = values.Remove(value); + if (values.Count == 0) + bTree.Remove(key); + return success; + } + return false; + } + public bool ContainsKey(K key) + { + if (!bTree.TryGet(key, out List _values)) + return false; + return _values.Count > 0; + } + public bool ContainsValue(V value) + { + foreach (List _values in bTree.Values) + { + foreach (V v in _values) + if (v.Equals(value)) + return true; + } + return false; + } + + public int Count(K key) + { + if ((!bTree.TryGet(key, out List _values)) || (_values.Count == 0)) + return 0; + return _values.Count; + } + + public void Clear() + { + bTree.Clear(); + } + + public V Shift() + { + K k = bTree.First(); + if (bTree.TryGet(k,out List values)) + { + V f = values[0]; + Remove(k, f); + return f; + } + throw new Exception("Serious BUG"); + } + public V Pop() + { + K k = bTree.Last(); + if (bTree.TryGet(k, out List values)) + { + V f = values[values.Count-1]; + Remove(k, f); + return f; + } + throw new Exception("Serious BUG"); + } + + public K First => bTree.First(); + public K Last => bTree.Last(); + + + + public IEnumerable Keys => bTree.Keys; + public IEnumerable Values => bTree.Values.SelectMany(vl => vl); + + public IEnumerable> GetKeyValuePairs() + { + foreach (K key in Keys) + { + List lv = bTree[key]; + foreach (V value in lv) + yield return new KeyValuePair(key, value); + } + } + public void AddRange(IEnumerable> keyValuePairs) + { + foreach (KeyValuePair keyValuePair in keyValuePairs) + Add(keyValuePair.Key, keyValuePair.Value); + } + } +} diff --git a/BTreeValueSet.cs b/BTreeValueSet.cs new file mode 100644 index 0000000..4140f13 --- /dev/null +++ b/BTreeValueSet.cs @@ -0,0 +1,134 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class BTreeValueSet + { + public bool Empty => bTree.Empty; + + BTree> bTree; + + public BTreeValueSet() + { + bTree = new BTree>(); + } + public BTreeValueSet(Comparison comparison) + { + bTree = new BTree>(comparison); + } + + public IEnumerable this[K key] + { + get + { + if (TryGet(key,out IEnumerable values)) + { + return values; + } + throw new KeyNotFoundException(); + } + } + + public void Add(K key, V value) + { + if (!TryAdd(key, value)) + throw new ArgumentException("duplicate key"); + } + public void Remove(K key, V value) + { + if (!TryRemove(key, value)) + throw new KeyNotFoundException(); + } + public void Remove(K key) + { + if (!TryRemove(key)) + throw new KeyNotFoundException(); + } + + public bool TryAdd(K key,V value) + { + if (!bTree.TryGet(key, out HashSet values)) + { + values = new HashSet(); + bTree.Add(key, values); + } + return values.Add(value); + } + public bool TryGet(K key,out IEnumerable values) + { + if ((!bTree.TryGet(key, out HashSet v)) || (v.Count == 0)) + { + values = v; + return false; + } + values = v; + return true; + } + public bool TryRemove(K key) + { + return bTree.TryRemove(key); + } + public bool TryRemove(K key,V value) + { + if (bTree.TryGet(key, out HashSet values)) + { + bool success = values.Remove(value); + if (values.Count == 0) + bTree.Remove(key); + return success; + } + return false; + } + public bool ContainsKey(K key) + { + if (!bTree.TryGet(key, out HashSet _values)) + return false; + return _values.Count > 0; + } + public bool ContainsValue(V value) + { + foreach (HashSet _values in bTree.Values) + { + foreach (V v in _values) + if (v.Equals(value)) + return true; + } + return false; + } + + public int Count(K key) + { + if ((!bTree.TryGet(key, out HashSet _values)) || (_values.Count == 0)) + return 0; + return _values.Count; + } + + public void Clear() + { + bTree.Clear(); + } + + public K First => bTree.First(); + public K Last => bTree.Last(); + + public IEnumerable Keys => bTree.Keys; + public IEnumerable Values => bTree.Values.SelectMany(vl => vl); + + public IEnumerable> GetKeyValuePairs() + { + foreach (K key in Keys) + { + HashSet lv = bTree[key]; + foreach (V value in lv) + yield return new KeyValuePair(key, value); + } + } + public void AddRange(IEnumerable> keyValuePairs) + { + foreach (KeyValuePair keyValuePair in keyValuePairs) + Add(keyValuePair.Key, keyValuePair.Value); + } + + } +} diff --git a/Cache.cs b/Cache.cs new file mode 100644 index 0000000..685626a --- /dev/null +++ b/Cache.cs @@ -0,0 +1,96 @@ +using System; +namespace ln.collections +{ + public class Cache + { + public int Count => values.Count; + + BTree values; + BTree> itemTree; + LinkedList items = new LinkedList(); + + int maxCacheSize = 4096; + + public Cache() + { + values = new BTree(); + itemTree = new BTree>(); + } + public Cache(Comparison compare) + { + values = new BTree(compare); + itemTree = new BTree>(compare); + } + + public int MaxCacheSize + { + get => maxCacheSize; + set { + maxCacheSize = value; + while (maxCacheSize < values.Count) + { + Forget(); + } + } + } + + public void Clear() + { + values.Clear(); + itemTree.Clear(); + items.Clear(); + } + + public bool TryGet(K key,out V value) + { + return values.TryGet(key, out value); + } + + public void Forget() + { + lock (this) + { + Forget(items.FirstItem); + } + } + public void Forget(K key) + { + lock (this) + { + if (itemTree.TryGet(key,out LinkedListItem item)) + Forget(item); + } + } + + private void Forget(LinkedListItem item) + { + if (item == null) + return; + + values.Remove(item.Value); + itemTree.Remove(item.Value); + items.Remove(item); + } + + public void Ensure(K key,V value) + { + lock (this) + { + if (!itemTree.TryGet(key, out LinkedListItem item)) + { + item = new LinkedListItem(key); + values.Add(key,value); + } + Ensure(item); + } + } + + private void Ensure(LinkedListItem item) + { + if (!item.IsLinked) + itemTree.Add(item.Value, item); + items.Add(item); + } + + } +} diff --git a/IDict.cs b/IDict.cs new file mode 100644 index 0000000..91a328c --- /dev/null +++ b/IDict.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Collections; +namespace ln.collections +{ + public interface IDict : IDict + { + V this[K key] { get; set; } + + void Add(K key, V value); + void Remove(K key); + + bool ContainsKey(K key); + + new IEnumerable Keys { get; } + new IEnumerable Values { get; } + } + + public interface IDict + { + object this[object key] { get; set; } + + void Add(object key, object value); + void Remove(object key); + + bool ContainsKey(object key); + + IEnumerable Keys { get; } + IEnumerable Values { get; } + } +} diff --git a/LinkedList.cs b/LinkedList.cs new file mode 100644 index 0000000..ab026b6 --- /dev/null +++ b/LinkedList.cs @@ -0,0 +1,44 @@ +namespace ln.collections +{ + /** + * A backend Implementation of Double-Linked-List + **/ + public class LinkedList + { + public LinkedListItem FirstItem => (head.Next != head) ? head.Next : null; + public LinkedListItem LastItem => (head.Previous != head) ? head.Previous : null; + + public LinkedListItem Head => head; + + LinkedListItem head = new LinkedListItem(); + + public LinkedList() + { + } + + public void Clear() + { + head = new LinkedListItem(); + } + + public void Add(LinkedListItem item) + { + if (item.Previous != item) + Remove(item); + + item.Next = head; + item.Previous = head.Previous; + item.Previous.Next = item; + item.Next.Previous = item; + } + + public void Remove(LinkedListItem item) + { + item.Previous.Next = item.Next; + item.Next.Previous = item.Previous; + item.Next = item; + item.Previous = item; + } + + } +} diff --git a/LinkedListItem.cs b/LinkedListItem.cs new file mode 100644 index 0000000..2c9ac1b --- /dev/null +++ b/LinkedListItem.cs @@ -0,0 +1,25 @@ +using System; +namespace ln.collections +{ + public class LinkedListItem + { + public bool IsLinked => (Previous != this); + + public LinkedListItem Previous; + public LinkedListItem Next; + + public T Value; + + public LinkedListItem() + { + Previous = this; + Next = this; + } + public LinkedListItem(T value) + :this() + { + Value = value; + } + + } +} diff --git a/MappingBTree.cs b/MappingBTree.cs new file mode 100644 index 0000000..03c3714 --- /dev/null +++ b/MappingBTree.cs @@ -0,0 +1,68 @@ +// /** +// * File: MappingBTree.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Runtime.CompilerServices; +using System.Collections.Generic; +using System.Collections; + +namespace ln.collections +{ + public delegate K KeyMapping(V value); + + public class MappingBTree : IEnumerable + { + public KeyMapping KeyMapping { get; } + + private BTree tree; + + public MappingBTree(KeyMapping keyMapping) + { + KeyMapping = keyMapping; + tree = new BTree(); + } + public MappingBTree(KeyMapping keyMapping,Comparison comparison) + { + KeyMapping = keyMapping; + tree = new BTree(comparison); + } + + public V this[K key] + { + get => tree[key]; + set => tree[key] = value; + } + + public void Clear() + { + tree.Clear(); + } + + public int Count => tree.Count; + public void Add(V v) => tree.Add(KeyMapping(v), v); + public void TryAdd(V v) => tree.TryAdd(KeyMapping(v), v); + public void Remove(V v) => tree.Remove(KeyMapping(v)); + public bool TryRemove(V v) => tree.TryRemove(KeyMapping(v)); + + public bool TryGet(K key, out V v) => tree.TryGet(key, out v); + public void RemoveKey(K key) => tree.Remove(key); + + public bool Contains(V v) => tree.ContainsKey(KeyMapping(v)); + public bool ContainsKey(K k) => tree.ContainsKey(k); + + public IEnumerable Keys => tree.Keys; + public IEnumerable Values => tree.Values; + + public V Next(V value) => tree[tree.Next(KeyMapping(value))]; + public V Previous(V value) => tree[tree.Previous(KeyMapping(value))]; + + public IEnumerator GetEnumerator() => Values.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => Values.GetEnumerator(); + } +} diff --git a/ObjectIdentityCache.cs b/ObjectIdentityCache.cs new file mode 100644 index 0000000..93ece34 --- /dev/null +++ b/ObjectIdentityCache.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ln.collections +{ + public class ObjectIdentityCache + { + Dictionary objectsByIdentity = new Dictionary(); + Dictionary identitiesByObject = new Dictionary(); + + Func IdGetter; + + public ObjectIdentityCache(Func idGetter) + { + if (idGetter == null) + throw new ArgumentNullException(nameof(idGetter)); + + IdGetter = idGetter; + } + + public object this[object identity] => objectsByIdentity[identity]; + + public void Update(object o) + { + if (identitiesByObject.TryGetValue(o, out object knownObject) && (!object.Equals(o, knownObject))) + throw new ArgumentException("another instance already known with this identity", nameof(o)); + + if (!object.ReferenceEquals(null, knownObject)) + { + object knownIdentity = identitiesByObject[knownObject]; + object objectIdentity = IdGetter(knownObject); + + if (!object.Equals(knownIdentity, objectIdentity)) + { + objectsByIdentity.Remove(knownIdentity); + objectsByIdentity.Add(knownIdentity, knownObject); + identitiesByObject[knownObject] = objectIdentity; + } + } else + { + object objectIdentity = IdGetter(o); + + objectsByIdentity.Add(objectIdentity, o); + identitiesByObject.Add(o, objectIdentity); + } + } + + public void RemoveObject(object o) + { + if (identitiesByObject.TryGetValue(o, out object objectIdentity)) + { + identitiesByObject.Remove(o); + objectsByIdentity.Remove(objectIdentity); + } + } + public void RemoveIdentity(object objectIdentity) + { + if (objectsByIdentity.TryGetValue(objectIdentity, out object o)) + { + objectsByIdentity.Remove(objectIdentity); + identitiesByObject.Remove(o); + } + } + + + } +} diff --git a/WeakKeyDictionary.cs b/WeakKeyDictionary.cs new file mode 100644 index 0000000..cb2a3ef --- /dev/null +++ b/WeakKeyDictionary.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace ln.collections +{ + public class WeakKeyDictionary : IDictionary where K : class + { + BTreeValueList> store = new BTreeValueList>(); + + public WeakKeyDictionary() + { + } + + public V this[K key] + { + get + { + if (TryGetValue(key, out V value)) + return value; + throw new KeyNotFoundException(); + } + set + { + SetValue(key, value, true); + } + } + + public virtual bool KeyEquals(K key1,K key2) + { + return object.Equals(key1, key2); + } + public virtual int GetKeyHashcode(K key) => key.GetHashCode(); + + private WeakKeyValuePair FindKeyValuePair(K key) + { + int keyHashCode = GetKeyHashcode(key); + + if (store.ContainsKey(keyHashCode)) + foreach (WeakKeyValuePair weakKeyValuePair in store[keyHashCode].ToArray()) + { + K weakKey = weakKeyValuePair.Key; + if (weakKey == null) + { + store.Remove(keyHashCode, weakKeyValuePair); + } + else if (KeyEquals(key, weakKey)) + { + return weakKeyValuePair; + } + } + return null; + } + + public bool TryGetValue(K key, out V value) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair != null) + { + value = weakKeyValuePair.Value; + return true; + } + value = default(V); + return false; + } + + public K GetKeyInstance(K key) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + return weakKeyValuePair.Key; + } + + public void SetValue(K key, V value) => SetValue(key, value, false); + public bool SetValue(K key, V value, bool replace) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair == null) + { + int keyHashCode = GetKeyHashcode(key); + weakKeyValuePair = new WeakKeyValuePair(keyHashCode, key, value); + store.Add(keyHashCode, weakKeyValuePair); + return false; + } else if (replace) + { + weakKeyValuePair.Value = value; + return true; + } else + { + throw new ArgumentException("Key already present"); + } + } + + public ICollection Keys + { + get + { + List keys = new List(); + + foreach (WeakKeyValuePair weakKeyValuePair in store.Values.ToArray()) + { + if (weakKeyValuePair.IsStrong) + keys.Add(weakKeyValuePair.Key); + else + store.TryRemove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + } + + return keys; + } + } + + public ICollection Values + { + get + { + List values = new List(); + + foreach (WeakKeyValuePair weakKeyValuePair in store.Values) + { + if (weakKeyValuePair.IsStrong) + values.Add(weakKeyValuePair.Value); + else + store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + } + + return values; + } + } + + public int Count => store.Keys.Count(); + public bool IsReadOnly => false; + + public void Add(K key, V value) + { + SetValue(key, value); + } + public void Add(KeyValuePair item) => Add(item.Key, item.Value); + + public void Clear() => store.Clear(); + public bool Contains(KeyValuePair item) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(item.Key); + if ((weakKeyValuePair != null) && weakKeyValuePair.IsStrong) + { + return Object.Equals(item.Value, weakKeyValuePair.Value); + } + return false; + } + public bool ContainsKey(K key) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + return (weakKeyValuePair != null) && weakKeyValuePair.IsStrong; + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (WeakKeyValuePair weakKeyValuePair in store.Values) + { + if (weakKeyValuePair.IsStrong) + array[arrayIndex++] = new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); + } + } + + public IEnumerator> GetEnumerator() + { + foreach (WeakKeyValuePair weakKeyValuePair in store.Values) + { + if (weakKeyValuePair.IsStrong) + yield return new KeyValuePair(weakKeyValuePair.Key,weakKeyValuePair.Value); + } + } + + public bool Remove(K key) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(key); + if (weakKeyValuePair != null) + { + store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + return true; + } + return false; + } + + public bool Remove(KeyValuePair item) + { + WeakKeyValuePair weakKeyValuePair = FindKeyValuePair(item.Key); + if ((weakKeyValuePair != null) && (object.Equals(item.Value, weakKeyValuePair.Value))) + { + store.Remove(weakKeyValuePair.keyHashCode, weakKeyValuePair); + return true; + } + return false; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + class WeakKeyValuePair where TK: class + { + public readonly int keyHashCode; + + WeakReference reference; + + public TK Key => reference.TryGetTarget(out TK target) ? target : null; + public bool IsStrong => reference.TryGetTarget(out TK target); + + public V Value { get; set; } + + public WeakKeyValuePair(int keyHashCode,TK key,V value) + { + reference = new WeakReference(key); + this.keyHashCode = keyHashCode; + Value = value; + } + + public override int GetHashCode() => keyHashCode; + public override bool Equals(object obj) + { + if (ReferenceEquals(this, obj)) + return true; + + TK key = Key; + if ((key != null) && (obj is WeakKeyValuePair)) + { + WeakKeyValuePair other = obj as WeakKeyValuePair; + return Equals(key, other.Key); + } + return false; + } + } + + + } +} diff --git a/WeakKeyReferenceDictionary.cs b/WeakKeyReferenceDictionary.cs new file mode 100644 index 0000000..7cda1c8 --- /dev/null +++ b/WeakKeyReferenceDictionary.cs @@ -0,0 +1,31 @@ +// /** +// * File: WeakKeyReferenceDictionary.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Runtime.CompilerServices; +namespace ln.collections +{ + public class WeakKeyReferenceDictionary : WeakKeyDictionary where K:class + { + public WeakKeyReferenceDictionary() + { + } + + public override bool KeyEquals(K key1, K key2) + { + return Object.ReferenceEquals(key1, key2); + } + + public override int GetKeyHashcode(K key) + { + return RuntimeHelpers.GetHashCode(key); + } + + } +} diff --git a/WeakValueDictionary.cs b/WeakValueDictionary.cs new file mode 100644 index 0000000..52cdd15 --- /dev/null +++ b/WeakValueDictionary.cs @@ -0,0 +1,118 @@ +// /** +// * File: WeakValueDictionary.cs +// * Author: haraldwolff +// * +// * This file and it's content is copyrighted by the Author and / or copyright holder. +// * Any use wihtout proper permission is illegal and may lead to legal actions. +// * +// * +// **/ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +namespace ln.collections +{ + public class WeakValueDictionary : IDictionary where V:class + { + Dictionary> keyValues = new Dictionary>(); + + public WeakValueDictionary() + { + } + + public V this[K key] { + get => TryGetValue(key, out V value) ? value : throw new KeyNotFoundException(); + set => SetValue(key, value); + } + + public ICollection Keys => keyValues.Keys; + public ICollection Values + { + get + { + List values = new List(); + + foreach (K key in Keys.ToArray()) + { + WeakReference weakValue = keyValues[key]; + if (weakValue.TryGetTarget(out V value)) + { + values.Add(value); + } + else + { + keyValues.Remove(key); + } + } + + return values; + } + } + + public int Count => keyValues.Count; + public bool IsReadOnly => false; + + public void Add(KeyValuePair item) => Add(item.Key, item.Value); + public void Add(K key, V value) + { + if (keyValues.ContainsKey(key)) + throw new ArgumentException(); + + SetValue(key, value); + } + + public void Clear() => keyValues.Clear(); + public bool Contains(KeyValuePair item) + { + if (TryGetValue(item.Key,out V value)) + { + return Equals(item.Value, value); + } + return false; + } + + public bool ContainsKey(K key) => keyValues.ContainsKey(key); + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (KeyValuePair> kvp in keyValues) + { + if (kvp.Value.TryGetTarget(out V value)) + array[arrayIndex++] = new KeyValuePair(kvp.Key, value); + } + } + + public IEnumerator> GetEnumerator() + { + foreach (KeyValuePair> kvp in keyValues) + { + if (kvp.Value.TryGetTarget(out V value)) + yield return new KeyValuePair(kvp.Key, value); + } + } + + public bool Remove(K key) => keyValues.Remove(key); + public bool Remove(KeyValuePair item) => Remove(item.Key); + + public bool TryGetValue(K key, out V value) + { + if (keyValues.TryGetValue(key, out WeakReference weakValue)) + { + if (weakValue.TryGetTarget(out value)) + return true; + + keyValues.Remove(key); + } + + value = null; + return false; + } + public bool SetValue(K key,V value) + { + keyValues[key] = new WeakReference(value); + return true; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/ln.collections.csproj b/ln.collections.csproj new file mode 100644 index 0000000..5f98043 --- /dev/null +++ b/ln.collections.csproj @@ -0,0 +1,10 @@ + + + + netcoreapp2.1 + + + + + +