diff --git a/Cast.cs b/Cast.cs index f82810d..e8ad79b 100644 --- a/Cast.cs +++ b/Cast.cs @@ -25,14 +25,16 @@ namespace ln.types return value; object casted; - + if ( !Implicit(value, targetType, out casted) && !Implicit(value, value.GetType(), targetType, out casted) && !Explicit(value, targetType, out casted) && !Explicit(value, value.GetType(), targetType, out casted) ) - throw new InvalidCastException(); + { + casted = Convert.ChangeType(value, targetType); + } return casted; } diff --git a/WeakHashValue.cs b/WeakHashValue.cs new file mode 100644 index 0000000..97438ca --- /dev/null +++ b/WeakHashValue.cs @@ -0,0 +1,31 @@ +using System; +namespace ln.types +{ + class WeakHashValue where T:class + { + readonly int keyHashCode; + + WeakReference reference; + + public T Value => reference.TryGetTarget(out T target) ? target : null; + public bool IsStrong => reference.TryGetTarget(out T target); + + public WeakHashValue(T value) + { + reference = new WeakReference(value); + keyHashCode = value.GetHashCode(); + } + + public override int GetHashCode() => keyHashCode; + public override bool Equals(object obj) + { + T value = Value; + if ((value != null) && (obj is WeakHashValue)) + { + WeakHashValue other = obj as WeakHashValue; + return object.Equals(value,other.Value); + } + return false; + } + } +} diff --git a/btree/BTree.cs b/btree/BTree.cs index 2ca27fb..4409f7c 100644 --- a/btree/BTree.cs +++ b/btree/BTree.cs @@ -30,11 +30,15 @@ namespace ln.types.btree public BTree() { - if (!typeof(K).GetInterfaces().Contains(typeof(IComparable))) - throw new ArgumentException("BTree need to be constructed with Comparer if T is not providing IComparable"); - - Comparison = (K x, K y) => ((IComparable)x).CompareTo(y); - } + 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; @@ -62,7 +66,7 @@ namespace ln.types.btree } } } - public Boolean TryGet(K key,ref V value) + public Boolean TryGet(K key,ref V value) { TreeNode node = Find(key); if (object.ReferenceEquals(node, null)) @@ -112,7 +116,28 @@ namespace ln.types.btree return Values.Contains(value); } - public void Clear() + 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; diff --git a/btree/BTreeValueList.cs b/btree/BTreeValueList.cs index 573460f..aa84e1b 100644 --- a/btree/BTreeValueList.cs +++ b/btree/BTreeValueList.cs @@ -107,7 +107,12 @@ namespace ln.types.btree return _values.Count; } - public IEnumerable Keys => bTree.Keys; + public void Clear() + { + bTree.Clear(); + } + + public IEnumerable Keys => bTree.Keys; public IEnumerable Values => bTree.Values.SelectMany(vl => vl); } } diff --git a/collections/WeakKeyDictionary.cs b/collections/WeakKeyDictionary.cs new file mode 100644 index 0000000..3ec73d0 --- /dev/null +++ b/collections/WeakKeyDictionary.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using ln.types.btree; +using System.Linq; + +namespace ln.types.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); + } + } + + private WeakKeyValuePair FindKeyValuePair(K key) + { + int keyHashCode = key.GetHashCode(); + if (store.ContainsKey(keyHashCode)) + foreach (WeakKeyValuePair weakKeyValuePair in store[keyHashCode]) + { + K weakKey = weakKeyValuePair.Key; + if (weakKey == null) + { + store.Remove(keyHashCode, weakKeyValuePair); + } + else if (Object.Equals(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 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) + { + weakKeyValuePair = new WeakKeyValuePair(key, value); + store.Add(key.GetHashCode(), 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) + { + if (weakKeyValuePair.IsStrong) + keys.Add(weakKeyValuePair.Key); + else + store.Remove(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(TK key,V value) + { + reference = new WeakReference(key); + keyHashCode = value.GetHashCode(); + Value = value; + } + + public override int GetHashCode() => keyHashCode; + public override bool Equals(object obj) + { + TK key = Key; + if ((key != null) && (obj is WeakKeyValuePair)) + { + WeakKeyValuePair other = obj as WeakKeyValuePair; + return object.Equals(key, other.Value); + } + return false; + } + } + + + } +} diff --git a/ln.types.csproj b/ln.types.csproj index e7d1584..a88d668 100644 --- a/ln.types.csproj +++ b/ln.types.csproj @@ -119,6 +119,9 @@ + + + diff --git a/odb/ng/Document.cs b/odb/ng/Document.cs index 69884cb..112a273 100644 --- a/odb/ng/Document.cs +++ b/odb/ng/Document.cs @@ -166,7 +166,7 @@ namespace ln.types.odb.ng static Document() { - RegisterDeserializer(0x1001, (b,o,l) => new Document(b,o+16,l)); + RegisterDeserializer(0x1001, (b,o,l) => new Document(b,o,l)); } } } diff --git a/odb/ng/Query.cs b/odb/ng/Query.cs index be2c97e..cae6efc 100644 --- a/odb/ng/Query.cs +++ b/odb/ng/Query.cs @@ -41,7 +41,7 @@ namespace ln.types.odb.ng if (object.ReferenceEquals(value,null)) value = ODBNull.Instance; - return IF(propertyName, (v) => value.Equals(v)); + return IF(propertyName, (v) => value.CompareTo(v)==0); } public static Query EqualsNot(string propertyName, ODBEntity value) => EqualsNot(IndexPath.TranslatePropertyPath(typeof(T), propertyName), value); public static Query EqualsNot(String propertyName, ODBEntity value) @@ -49,13 +49,18 @@ namespace ln.types.odb.ng if (object.ReferenceEquals(value, null)) value = ODBNull.Instance; - return IF(propertyName, (v) => !value.Equals(v)); + return IF(propertyName, (v) => value.CompareTo(v)!=0); } public static Query Equals(string propertyName, ODBEntity[] values) => Equals(IndexPath.TranslatePropertyPath(typeof(T), propertyName), values); public static Query Equals(String propertyName, ODBEntity[] values) { - return IF(propertyName, (v) => values.Contains(v)); + return IF(propertyName, (v) => { + foreach (ODBEntity value in values) + if (value.CompareTo(v) == 0) + return true; + return false; + }); } public static Query Contains(string propertyName, IEnumerable values) diff --git a/odb/ng/mappings/ClassMapping.cs b/odb/ng/mappings/ClassMapping.cs index 7625a63..76e4d16 100644 --- a/odb/ng/mappings/ClassMapping.cs +++ b/odb/ng/mappings/ClassMapping.cs @@ -4,20 +4,21 @@ using System.Reflection; using System.Collections.Generic; using ln.logging; using System.Linq; +using ln.types.collections; namespace ln.types.odb.ng.mappings { public class ClassMapping : IODBMapping { + WeakKeyDictionary cache = new WeakKeyDictionary(); + + public delegate object GetID(object o); public delegate object SetID(object o, object id); public Type MappedType { get; } - //public GetID getID { get; private set; } = (o) => Guid.NewGuid(); - //public Type IDType { get; private set; } - List mappedFields = new List(); public ClassMapping(Type type) @@ -139,8 +140,11 @@ namespace ln.types.odb.ng.mappings { if (Object.ReferenceEquals(value, null)) return ODBNull.Instance; - - return MapDocument(mapper, Guid.Empty, value); + + if (!cache.TryGetValue(value, out Guid documentID)) + documentID = Guid.NewGuid(); + + return MapDocument(mapper, documentID, value); } public Type GetFieldType(Mapper mapper,string fieldName) diff --git a/odb/ng/storage/session/SessionStorage.cs b/odb/ng/storage/session/SessionStorage.cs index 22f9667..80406d0 100644 --- a/odb/ng/storage/session/SessionStorage.cs +++ b/odb/ng/storage/session/SessionStorage.cs @@ -7,7 +7,7 @@ using ln.types.threads; using ln.types.odb.ng.diff; namespace ln.types.odb.ng.storage.session { - class SessionStorage : ChainedStorage + public class SessionStorage : ChainedStorage { public SessionStorageContainer SessionContainer { get; } diff --git a/odb/values/ODBInteger.cs b/odb/values/ODBInteger.cs index 0e9e097..e96d33b 100644 --- a/odb/values/ODBInteger.cs +++ b/odb/values/ODBInteger.cs @@ -25,7 +25,7 @@ namespace ln.types.odb.values public override byte[] GetStorageBytes() => BitConverter.GetBytes((uint)Value); protected override int compare(ODBEntity other) { - long d = (long)Value - (long)(other as ODBValue).Value; + long d = Convert.ToInt64((uint)Value) - Convert.ToInt64((uint)(other as ODBValue).Value); if (d == 0) return 0; if (d < 0) diff --git a/test/ODBTests.cs b/test/ODBTests.cs index 158e2e7..a5e32dd 100644 --- a/test/ODBTests.cs +++ b/test/ODBTests.cs @@ -1,15 +1,15 @@ -using NUnit.Framework; -using System; -using ln.types.odb.ng; -using ln.types.odb.ng.storage; +using System; using System.IO; using System.Linq; -using ln.types.odb.values; +using ln.types.odb.ng; +using ln.types.odb.ng.storage; using ln.types.odb.ng.storage.fs; using ln.types.odb.ng.storage.session; +using ln.types.odb.values; +using NUnit.Framework; namespace ln.types.test { - [TestFixture()] + [TestFixture()] public class ODBTests { [SetUp()] @@ -48,6 +48,9 @@ namespace ln.types.test document[new ODBStringValue("FeldA")] = new ODBStringValue("FeldA"); document[new ODBStringValue("FeldB")] = new ODBStringValue("FeldB"); + Document nestedDocument = new Document(); + nestedDocument["EineZeichenkette"] = new ODBStringValue("Ich bin eine Zeichenkette"); + document[new ODBStringValue("FeldDokument")] = nestedDocument; storage.Save(document); @@ -110,6 +113,9 @@ namespace ln.types.test Assert.AreEqual(1, mapper.Load(Query.Equals("FirstName", "Harald")).Count()); Assert.AreEqual(1, mapper.Load(Query.Equals("FirstName", "Liesschen")).Count()); + Person harald = mapper.Load(Query.Equals("FirstName", "Harald")).FirstOrDefault(); + + Assert.AreEqual(persons[0], harald); } } @@ -161,12 +167,24 @@ namespace ln.types.test public class Person { + public readonly Guid ID; + public String FirstName = ""; public String[] MiddleNames = new string[0]; public String LastName = ""; public int Age = 18; - } + + public Person() + { + ID = Guid.NewGuid(); + } + + public override int GetHashCode() => ID.GetHashCode(); + public override bool Equals(object obj) => (obj is Person) && ID.Equals((obj as Person).ID); + + + } } } diff --git a/test/WeakKeyDictionaryTests.cs b/test/WeakKeyDictionaryTests.cs new file mode 100644 index 0000000..2badfa2 --- /dev/null +++ b/test/WeakKeyDictionaryTests.cs @@ -0,0 +1,66 @@ +using System; +using NUnit.Framework; +using ln.types.collections; +using System.Threading; +namespace ln.types.test +{ + [TestFixture] + public class WeakKeyDictionaryTests + { + class KeyClass + { + static int next = 1; + + public readonly int Value = next++; + + public override int GetHashCode() => Value; + public override bool Equals(object obj) + { + return ((obj is KeyClass) && ((obj as KeyClass).Value == Value)); + } + public override string ToString() + { + return string.Format("[KeyClass Value={0}]",Value); + } + } + + KeyClass[] keys = new KeyClass[1024]; + WeakKeyDictionary testDict = new WeakKeyDictionary(); + + public void Fill() + { + for (int n = 0; n < keys.Length; n++) + { + keys[n] = new KeyClass(); + testDict.Add(keys[n], n); + } + } + + public void Strip() + { + keys[0] = null; + keys[keys.Length - 1] = null; + } + + [TestCase()] + public void TestWeakKeyDictionary() + { + Fill(); + + Thread.Sleep(250); + + Assert.AreEqual(keys.Length, testDict.Count); + Assert.AreEqual(keys.Length, testDict.Keys.Count); + + Strip(); + + Thread.Sleep(250); + + GC.Collect(); + + Thread.Sleep(250); + + Assert.AreEqual(keys.Length-2, testDict.Keys.Count); + } + } +}