From 2a85ffdac8d019b817356e374983db7acd5a5aae Mon Sep 17 00:00:00 2001 From: neuecc Date: Mon, 16 Aug 2021 10:19:41 +0900 Subject: [PATCH] WIP dictionary --- .../IObservableCollection.cs | 29 +- .../Internal/GroupedView.cs | 386 ++++++++++++++++++ .../Internal/SortedView.cs | 10 +- .../Internal/SortedViewViewComparer.cs | 6 +- .../ObservableDictionary.Views.cs | 16 +- .../ObservableDictionary.cs | 2 +- .../ObservableHashSet.Views.cs | 9 - .../ObservableHashSet.cs | 2 +- .../ObservableLinkedList.Views.cs | 12 +- .../ObservableLinkedList.cs | 18 +- .../ObservableList.Views.cs | 12 - src/ObservableCollections/ObservableList.cs | 2 +- .../ObservableQueue.Views.cs | 10 - src/ObservableCollections/ObservableQueue.cs | 2 +- .../ObservableStack.Views.cs | 10 - src/ObservableCollections/ObservableStack.cs | 2 +- 16 files changed, 436 insertions(+), 92 deletions(-) create mode 100644 src/ObservableCollections/Internal/GroupedView.cs diff --git a/src/ObservableCollections/IObservableCollection.cs b/src/ObservableCollections/IObservableCollection.cs index d706aa0..1511cc0 100644 --- a/src/ObservableCollections/IObservableCollection.cs +++ b/src/ObservableCollections/IObservableCollection.cs @@ -1,4 +1,5 @@ -using System; +using ObservableCollections.Internal; +using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -7,15 +8,11 @@ namespace ObservableCollections { public delegate void NotifyCollectionChangedEventHandler(in NotifyCollectionChangedEventArgs e); - public interface IObservableCollection + public interface IObservableCollection : IReadOnlyCollection { event NotifyCollectionChangedEventHandler? CollectionChanged; - + object SyncRoot { get; } ISynchronizedView CreateView(Func transform, bool reverse = false); - ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer comparer) - where TKey : notnull; - ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer viewComparer) - where TKey : notnull; } public interface IFreezedCollection @@ -26,7 +23,6 @@ namespace ObservableCollections public interface ISynchronizedView : IReadOnlyCollection<(T Value, TView View)>, IDisposable { - // TODO:Remove SyncRoot object SyncRoot { get; } event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; @@ -34,7 +30,6 @@ namespace ObservableCollections void AttachFilter(ISynchronizedViewFilter filter); void ResetFilter(Action? resetAction); - INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged(); } @@ -44,12 +39,28 @@ namespace ObservableCollections void Sort(IComparer viewComparer); } + public interface IGroupedSynchoronizedView : ILookup, ISynchronizedView + { + } + public interface INotifyCollectionChangedSynchronizedView : ISynchronizedView, INotifyCollectionChanged, INotifyPropertyChanged { } public static class ObservableCollectionsExtensions { + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) + where TKey : notnull + { + return new SortedView(source, identitySelector, transform, comparer); + } + + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, IComparer viewComparer) + where TKey : notnull + { + return new SortedViewViewComparer(source, identitySelector, transform, viewComparer); + } + public static ISynchronizedView CreateSortedView(this IObservableCollection source, Func identitySelector, Func transform, Func compareSelector, bool ascending = true) where TKey : notnull { diff --git a/src/ObservableCollections/Internal/GroupedView.cs b/src/ObservableCollections/Internal/GroupedView.cs new file mode 100644 index 0000000..7023615 --- /dev/null +++ b/src/ObservableCollections/Internal/GroupedView.cs @@ -0,0 +1,386 @@ +using System.Collections; +using System.Collections.Specialized; + +namespace ObservableCollections.Internal +{ + // mutable lookup. + internal class Lookup : ILookup + where TKey : notnull + { + Grouping?[] groupingBuckets; + IEqualityComparer keyComparer; + int count; + Grouping? lastGroup; + + // TODO: + public int Count => throw new NotImplementedException(); + + public int ItemsCount { get; private set; } + + public IEnumerable this[TKey key] => throw new NotImplementedException(); + + public Lookup(IEqualityComparer keyComparer) + { + this.groupingBuckets = new Grouping?[7]; // initial size + this.count = 0; + this.lastGroup = null; + this.keyComparer = keyComparer; + } + + // TODO: + public bool Contains(TKey key) + { + throw new NotImplementedException(); + } + + public void Add(TKey key, TValue value) + { + var keyHash = keyComparer.GetHashCode(key); + var g = groupingBuckets[keyHash % groupingBuckets.Length]; + var last = g; + while (g != null) + { + if (keyComparer.Equals(key, g.key)) + { + break; // hit. + } + + last = g; + g = g.hashNext; + } + + if (g == null) + { + g = new Grouping(key, keyHash); + if (last != null) + { + last.hashNext = g; + } + else + { + if (groupingBuckets.Length == count) + { + Resize(); + } + + groupingBuckets[keyHash % groupingBuckets.Length] = g; + } + count++; // new group added + } + + g.Add(value); + + if (lastGroup == null) + { + lastGroup = g; + lastGroup.nextGroup = g; // last's next is first. + } + else + { + g.nextGroup = lastGroup.nextGroup; + lastGroup.nextGroup = g; + lastGroup = g; + } + + ItemsCount++; + } + + public void RemoveKeyAll(TKey key) + { + throw new NotImplementedException(); + } + + public void RemoveValue(TKey key, TValue value) + { + } + + void Resize() + { + var newSize = checked((count * 2) + 1); + var newGrouping = new Grouping[newSize]; + + var g = lastGroup!; // when resize called, always lastGroup is not null. + do + { + g = g.nextGroup!; // nextGroup is always not null, initial last.next is first. + var index = g.hash % newSize; + g.hashNext = newGrouping[index]; + newGrouping[index] = g; + } + while (g != lastGroup); + + groupingBuckets = newGrouping; + } + + public IEnumerator> GetEnumerator() + { + if (lastGroup == null) yield break; + + var g = lastGroup.nextGroup; // last's next is first. + do + { + if (g == null) yield break; + yield return g; + g = g.nextGroup; + } + while (g != lastGroup); // reaches end. + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class Grouping : IGrouping + { + internal readonly TKey key; + internal readonly int hash; + internal readonly List elements; + + internal Grouping? hashNext; // same buckets linknode + internal Grouping? nextGroup; // guarantee added order + + public TKey Key => key; + + public Grouping(TKey key, int hash) + { + this.key = key; + this.hash = hash; + this.elements = new List(1); // initial size is single. + } + + public void Add(TValue value) + { + elements.Add(value); + } + + public void Remove(TValue value) + { + elements.Remove(value); + } + + public IEnumerator GetEnumerator() + { + return elements.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class GroupedView : IGroupedSynchoronizedView + where TKey : notnull + { + readonly Lookup lookup; + readonly IObservableCollection source; + readonly Func keySelector; + readonly Func viewSelector; + + ISynchronizedViewFilter filter; + + public GroupedView(IObservableCollection source, Func keySelector, Func viewSelector, IEqualityComparer keyComparer) + { + this.source = source; + this.filter = SynchronizedViewFilter.Null; + this.keySelector = keySelector; + this.viewSelector = viewSelector; + + lock (source.SyncRoot) + { + lookup = new Lookup(keyComparer); + + foreach (var value in source) + { + var key = keySelector(value); + var view = viewSelector(value); + lookup.Add(key, (value, view)); + } + + source.CollectionChanged += SourceCollectionChanged; + } + } + + public IEnumerable<(T, TView)> this[TKey key] + { + get + { + lock (SyncRoot) + { + var v = lookup[key]; + foreach (var item in v) + { + yield return item; + } + } + } + } + + public object SyncRoot { get; } = new object(); + + public int Count + { + get + { + lock (SyncRoot) + { + return lookup.Count; + } + } + } + + int IReadOnlyCollection<(T Value, TView View)>.Count + { + get + { + lock (SyncRoot) + { + return lookup.ItemsCount; + } + } + } + + public event NotifyCollectionChangedEventHandler? RoutingCollectionChanged; + public event Action? CollectionStateChanged; + + public void AttachFilter(ISynchronizedViewFilter filter) + { + lock (SyncRoot) + { + this.filter = filter; + foreach (var item in lookup) + { + foreach (var (value, view) in item) + { + filter.InvokeOnAttach(value, view); + } + } + } + } + + public void ResetFilter(Action? resetAction) + { + lock (SyncRoot) + { + this.filter = SynchronizedViewFilter.Null; + if (resetAction != null) + { + foreach (var item in lookup) + { + foreach (var (value, view) in item) + { + resetAction(value, view); + } + } + } + } + } + + public bool Contains(TKey key) + { + lock (SyncRoot) + { + return lookup.Contains(key); + } + } + + public void Dispose() + { + source.CollectionChanged -= SourceCollectionChanged; + } + + public INotifyCollectionChangedSynchronizedView WithINotifyCollectionChanged() + { + lock (SyncRoot) + { + return new NotifyCollectionChangedSynchronizedView(this); + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator<(T Value, TView View)> IEnumerable<(T Value, TView View)>.GetEnumerator() + { + throw new NotImplementedException(); + } + + public IEnumerator> GetEnumerator() + { + throw new NotImplementedException(); + } + + private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs e) + { + lock (SyncRoot) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + if (e.IsSingleItem) + { + var value = e.NewItem; + var key = keySelector(value); + var view = viewSelector(value); + lookup.Add(key, (value, view)); + filter.InvokeOnAdd(value, view); + } + else + { + foreach (var value in e.NewItems) + { + var key = keySelector(value); + var view = viewSelector(value); + lookup.Add(key, (value, view)); + filter.InvokeOnAdd(value, view); + } + } + break; + case NotifyCollectionChangedAction.Remove: + if (e.IsSingleItem) + { + var value = e.OldItem; + var key = keySelector(value); + + lookup + + //var removeItems = lookup[key]; + //foreach (var v in removeItems) + //{ + // filter.InvokeOnRemove(v); + //} + + //lookup.Remove(key); + //filter.InvokeOnRemove( + //lookup + //dict.Remove((value, id), out var v); + //filter.InvokeOnRemove(v.Value, v.View); + } + else + { + //foreach (var value in e.OldItems) + //{ + // var id = identitySelector(value); + // dict.Remove((value, id), out var v); + // filter.InvokeOnRemove(v.Value, v.View); + //} + } + + break; + case NotifyCollectionChangedAction.Replace: + break; + case NotifyCollectionChangedAction.Move: + break; + case NotifyCollectionChangedAction.Reset: + break; + default: + break; + } + } + } + } +} diff --git a/src/ObservableCollections/Internal/SortedView.cs b/src/ObservableCollections/Internal/SortedView.cs index b400da9..a164ae7 100644 --- a/src/ObservableCollections/Internal/SortedView.cs +++ b/src/ObservableCollections/Internal/SortedView.cs @@ -4,7 +4,7 @@ using System.Collections.Specialized; namespace ObservableCollections.Internal { internal class SortedView : ISynchronizedView - where TKey : notnull + where TKey : notnull { readonly IObservableCollection source; readonly Func transform; @@ -18,16 +18,16 @@ namespace ObservableCollections.Internal public object SyncRoot { get; } = new object(); - public SortedView(IObservableCollection source, object syncRoot, IEnumerable sourceEnumerable, Func identitySelector, Func transform, IComparer comparer) + public SortedView(IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) { this.source = source; this.identitySelector = identitySelector; this.transform = transform; this.filter = SynchronizedViewFilter.Null; - lock (syncRoot) + lock (source.SyncRoot) { var dict = new SortedDictionary<(T, TKey), (T, TView)>(new Comparer(comparer)); - foreach (var v in sourceEnumerable) + foreach (var v in source) { dict.Add((v, identitySelector(v)), (v, transform(v))); } @@ -145,6 +145,8 @@ namespace ObservableCollections.Internal } break; case NotifyCollectionChangedAction.Replace: + // TODO:Range support + // ReplaceRange is not supported in all ObservableCollections collections // Replace is remove old item and insert new item. { diff --git a/src/ObservableCollections/Internal/SortedViewViewComparer.cs b/src/ObservableCollections/Internal/SortedViewViewComparer.cs index 9064bc8..b479017 100644 --- a/src/ObservableCollections/Internal/SortedViewViewComparer.cs +++ b/src/ObservableCollections/Internal/SortedViewViewComparer.cs @@ -19,17 +19,17 @@ namespace ObservableCollections.Internal public object SyncRoot { get; } = new object(); - public SortedViewViewComparer(IObservableCollection source, object syncRoot, IEnumerable sourceEnumerable, Func identitySelector, Func transform, IComparer comparer) + public SortedViewViewComparer(IObservableCollection source, Func identitySelector, Func transform, IComparer comparer) { this.source = source; this.identitySelector = identitySelector; this.transform = transform; this.filter = SynchronizedViewFilter.Null; - lock (syncRoot) + lock (source.SyncRoot) { var dict = new SortedDictionary<(TView, TKey), (T, TView)>(new Comparer(comparer)); this.viewMap = new Dictionary(); - foreach (var value in sourceEnumerable) + foreach (var value in source) { var view = transform(value); var id = identitySelector(value); diff --git a/src/ObservableCollections/ObservableDictionary.Views.cs b/src/ObservableCollections/ObservableDictionary.Views.cs index 24939c8..256fe08 100644 --- a/src/ObservableCollections/ObservableDictionary.Views.cs +++ b/src/ObservableCollections/ObservableDictionary.Views.cs @@ -12,25 +12,15 @@ namespace ObservableCollections return new View(this, transform); } + // using key implicitly public ISynchronizedView, TView> CreateSortedView(Func, TView> transform, IComparer> comparer) { - return new SortedView, TKey, TView>(this, this.SyncRoot, dictionary, x => x.Key, transform, comparer); + return this.CreateSortedView(x => x.Key, transform, comparer); } public ISynchronizedView, TView> CreateSortedView(Func, TView> transform, IComparer viewComparer) { - return new SortedViewViewComparer, TKey, TView>(this, this.SyncRoot, dictionary, x => x.Key, transform, viewComparer); - } - - // identity selector is ignored - ISynchronizedView, TView> IObservableCollection>.CreateSortedView(Func, TKey1> identitySelector, Func, TView> transform, IComparer> comparer) - { - return new SortedView, TKey, TView>(this, this.SyncRoot, dictionary, x => x.Key, transform, comparer); - } - - ISynchronizedView, TView> IObservableCollection>.CreateSortedView(Func, TKey1> identitySelector, Func, TView> transform, IComparer viewComparer) - { - return new SortedViewViewComparer, TKey, TView>(this, this.SyncRoot, dictionary, x => x.Key, transform, viewComparer); + return this.CreateSortedView(x => x.Key, transform, viewComparer); } class View : ISynchronizedView, TView> diff --git a/src/ObservableCollections/ObservableDictionary.cs b/src/ObservableCollections/ObservableDictionary.cs index 7a4ddbd..891988e 100644 --- a/src/ObservableCollections/ObservableDictionary.cs +++ b/src/ObservableCollections/ObservableDictionary.cs @@ -11,7 +11,7 @@ namespace ObservableCollections where TKey : notnull { readonly Dictionary dictionary; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public ObservableDictionary() { diff --git a/src/ObservableCollections/ObservableHashSet.Views.cs b/src/ObservableCollections/ObservableHashSet.Views.cs index 04838f2..6d3c566 100644 --- a/src/ObservableCollections/ObservableHashSet.Views.cs +++ b/src/ObservableCollections/ObservableHashSet.Views.cs @@ -5,15 +5,6 @@ namespace ObservableCollections public sealed partial class ObservableHashSet : IReadOnlyCollection, IObservableCollection { // TODO: - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer comparer) where TKey : notnull - { - throw new NotImplementedException(); - } - - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer viewComparer) where TKey : notnull - { - throw new NotImplementedException(); - } public ISynchronizedView CreateView(Func transform, bool reverse = false) { diff --git a/src/ObservableCollections/ObservableHashSet.cs b/src/ObservableCollections/ObservableHashSet.cs index fced310..ce35f4c 100644 --- a/src/ObservableCollections/ObservableHashSet.cs +++ b/src/ObservableCollections/ObservableHashSet.cs @@ -8,7 +8,7 @@ namespace ObservableCollections public sealed partial class ObservableHashSet : IReadOnlySet, IReadOnlyCollection, IObservableCollection { readonly HashSet set; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public ObservableHashSet() { diff --git a/src/ObservableCollections/ObservableLinkedList.Views.cs b/src/ObservableCollections/ObservableLinkedList.Views.cs index 51a3b03..92439cf 100644 --- a/src/ObservableCollections/ObservableLinkedList.Views.cs +++ b/src/ObservableCollections/ObservableLinkedList.Views.cs @@ -4,23 +4,13 @@ using System.Collections.Specialized; namespace ObservableCollections { - public sealed partial class ObservableLinkedList : IReadOnlyCollection, IObservableCollection> + public sealed partial class ObservableLinkedList { public ISynchronizedView, TView> CreateView(Func, TView> transform, bool reverse = false) { throw new NotImplementedException(); } - public ISynchronizedView, TView> CreateSortedView(Func, TKey> identitySelector, Func, TView> transform, IComparer> comparer) where TKey : notnull - { - throw new NotImplementedException(); - } - - public ISynchronizedView, TView> CreateSortedView(Func, TKey> identitySelector, Func, TView> transform, IComparer viewComparer) where TKey : notnull - { - throw new NotImplementedException(); - } - sealed class View : ISynchronizedView, TView> { readonly ObservableLinkedList source; diff --git a/src/ObservableCollections/ObservableLinkedList.cs b/src/ObservableCollections/ObservableLinkedList.cs index 60e1eff..a7b93af 100644 --- a/src/ObservableCollections/ObservableLinkedList.cs +++ b/src/ObservableCollections/ObservableLinkedList.cs @@ -8,7 +8,7 @@ namespace ObservableCollections public sealed partial class ObservableLinkedList : IReadOnlyCollection, IObservableCollection> { readonly LinkedList list; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public event NotifyCollectionChangedEventHandler>? CollectionChanged; @@ -126,11 +126,7 @@ namespace ObservableCollections } } - - - // TODO: GetEnumerator - - public IEnumerator GetEnumerator() + public IEnumerator> GetEnumerator() { throw new NotImplementedException(); } @@ -139,5 +135,15 @@ namespace ObservableCollections { throw new NotImplementedException(); } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + + + // TODO: GetEnumerator + } } diff --git a/src/ObservableCollections/ObservableList.Views.cs b/src/ObservableCollections/ObservableList.Views.cs index 6f26559..24d1124 100644 --- a/src/ObservableCollections/ObservableList.Views.cs +++ b/src/ObservableCollections/ObservableList.Views.cs @@ -11,18 +11,6 @@ namespace ObservableCollections return new View(this, transform, reverse); } - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer comparer) - where TKey : notnull - { - return new SortedView(this, SyncRoot, list, identitySelector, transform, comparer); - } - - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer viewComparer) - where TKey : notnull - { - return new SortedViewViewComparer(this, SyncRoot, list, identitySelector, transform, viewComparer); - } - class View : ISynchronizedView { readonly ObservableList source; diff --git a/src/ObservableCollections/ObservableList.cs b/src/ObservableCollections/ObservableList.cs index d761067..550ccde 100644 --- a/src/ObservableCollections/ObservableList.cs +++ b/src/ObservableCollections/ObservableList.cs @@ -10,7 +10,7 @@ namespace ObservableCollections public sealed partial class ObservableList : IList, IReadOnlyList, IObservableCollection { readonly List list; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public ObservableList() { diff --git a/src/ObservableCollections/ObservableQueue.Views.cs b/src/ObservableCollections/ObservableQueue.Views.cs index 1d3efdb..dc2ef59 100644 --- a/src/ObservableCollections/ObservableQueue.Views.cs +++ b/src/ObservableCollections/ObservableQueue.Views.cs @@ -11,16 +11,6 @@ namespace ObservableCollections return new View(this, transform, reverse); } - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer comparer) where TKey : notnull - { - return new SortedView(this, SyncRoot, queue, identitySelector, transform, comparer); - } - - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer viewComparer) where TKey : notnull - { - return new SortedViewViewComparer(this, SyncRoot, queue, identitySelector, transform, viewComparer); - } - class View : ISynchronizedView { readonly ObservableQueue source; diff --git a/src/ObservableCollections/ObservableQueue.cs b/src/ObservableCollections/ObservableQueue.cs index 978cf3f..a7c1234 100644 --- a/src/ObservableCollections/ObservableQueue.cs +++ b/src/ObservableCollections/ObservableQueue.cs @@ -8,7 +8,7 @@ namespace ObservableCollections public sealed partial class ObservableQueue : IReadOnlyCollection, IObservableCollection { readonly Queue queue; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public ObservableQueue() { diff --git a/src/ObservableCollections/ObservableStack.Views.cs b/src/ObservableCollections/ObservableStack.Views.cs index 3e043d5..3a3d4ef 100644 --- a/src/ObservableCollections/ObservableStack.Views.cs +++ b/src/ObservableCollections/ObservableStack.Views.cs @@ -11,16 +11,6 @@ namespace ObservableCollections return new View(this, transform, reverse); } - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer comparer) where TKey : notnull - { - return new SortedView(this, SyncRoot, stack, identitySelector, transform, comparer); - } - - public ISynchronizedView CreateSortedView(Func identitySelector, Func transform, IComparer viewComparer) where TKey : notnull - { - return new SortedViewViewComparer(this, SyncRoot, stack, identitySelector, transform, viewComparer); - } - class View : ISynchronizedView { readonly ObservableStack source; diff --git a/src/ObservableCollections/ObservableStack.cs b/src/ObservableCollections/ObservableStack.cs index 3803f0c..6eec575 100644 --- a/src/ObservableCollections/ObservableStack.cs +++ b/src/ObservableCollections/ObservableStack.cs @@ -8,7 +8,7 @@ namespace ObservableCollections public sealed partial class ObservableStack : IReadOnlyCollection, IObservableCollection { readonly Stack stack; - public readonly object SyncRoot = new object(); + public object SyncRoot { get; } = new object(); public ObservableStack() {