From b5431b9f9d4e55143471f6e38f21f6bb1f2492eb Mon Sep 17 00:00:00 2001 From: neuecc Date: Tue, 31 Aug 2021 20:20:27 +0900 Subject: [PATCH] Mirror --- ...CopyedCollection.cs => CloneCollection.cs} | 16 +- .../ObservableFixedSizeRingBuffer.cs | 214 ++++++++++++++++++ src/ObservableCollections/ObservableList.cs | 8 +- src/ObservableCollections/ObservableQueue.cs | 2 +- .../ObservableRingBuffer.cs | 152 ++++++------- src/ObservableCollections/ObservableStack.cs | 2 +- src/ObservableCollections/RingBuffer.cs | 44 ++-- 7 files changed, 324 insertions(+), 114 deletions(-) rename src/ObservableCollections/Internal/{CopyedCollection.cs => CloneCollection.cs} (92%) create mode 100644 src/ObservableCollections/ObservableFixedSizeRingBuffer.cs diff --git a/src/ObservableCollections/Internal/CopyedCollection.cs b/src/ObservableCollections/Internal/CloneCollection.cs similarity index 92% rename from src/ObservableCollections/Internal/CopyedCollection.cs rename to src/ObservableCollections/Internal/CloneCollection.cs index ff4f2c0..7bfadc9 100644 --- a/src/ObservableCollections/Internal/CopyedCollection.cs +++ b/src/ObservableCollections/Internal/CloneCollection.cs @@ -1,13 +1,13 @@ -using System; -using System.Buffers; +using System.Buffers; using System.Collections; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; namespace ObservableCollections.Internal { - internal struct CopyedCollection : IDisposable + /// + /// ReadOnly cloned collection. + /// + internal struct CloneCollection : IDisposable { T[]? array; int length; @@ -16,13 +16,13 @@ namespace ObservableCollections.Internal public IEnumerable AsEnumerable() => new EnumerableCollection(array, length); - public CopyedCollection(T item) + public CloneCollection(T item) { this.array = ArrayPool.Shared.Rent(1); this.length = 1; } - public CopyedCollection(IEnumerable source) + public CloneCollection(IEnumerable source) { if (Enumerable.TryGetNonEnumeratedCount(source, out var count)) { @@ -58,7 +58,7 @@ namespace ObservableCollections.Internal } } - public CopyedCollection(ReadOnlySpan source) + public CloneCollection(ReadOnlySpan source) { var array = ArrayPool.Shared.Rent(source.Length); source.CopyTo(array); diff --git a/src/ObservableCollections/ObservableFixedSizeRingBuffer.cs b/src/ObservableCollections/ObservableFixedSizeRingBuffer.cs new file mode 100644 index 0000000..caf08ce --- /dev/null +++ b/src/ObservableCollections/ObservableFixedSizeRingBuffer.cs @@ -0,0 +1,214 @@ +using ObservableCollections.Internal; +using System.Collections; + +namespace ObservableCollections +{ + public sealed partial class ObservableFixedSizeRingBuffer : IList, IReadOnlyList, IObservableCollection + { + readonly RingBuffer buffer; + readonly int capacity; + + public event NotifyCollectionChangedEventHandler? CollectionChanged; + + public ObservableFixedSizeRingBuffer(int capacity) + { + this.capacity = capacity; + this.buffer = new RingBuffer(capacity); + } + + public ObservableFixedSizeRingBuffer(int capacity, IEnumerable collection) + { + this.capacity = capacity; + this.buffer = new RingBuffer(capacity); + foreach (var item in collection) + { + if (capacity == buffer.Count) + { + buffer.RemoveFirst(); + } + buffer.AddLast(item); + } + } + + public bool IsReadOnly => false; + + public object SyncRoot { get; } = new object(); + + public T this[int index] + { + get + { + lock (SyncRoot) + { + return this.buffer[index]; + } + } + set + { + lock (SyncRoot) + { + var oldValue = buffer[index]; + buffer[index] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Replace(value, oldValue, index)); + } + } + } + + public int Count + { + get + { + lock (SyncRoot) + { + return buffer.Count; + } + } + } + + public void AddFirst(T item) + { + lock (SyncRoot) + { + if (capacity == buffer.Count) + { + buffer.RemoveLast(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, capacity - 1)); + } + + buffer.AddFirst(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, 0)); + } + } + + public void AddLast(T item) + { + lock (SyncRoot) + { + if (capacity == buffer.Count) + { + buffer.RemoveLast(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(item, capacity - 1)); + } + + buffer.AddLast(item); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(item, buffer.Count - 1)); + } + } + + // AddFirstRange is not exists. + + public void AddLastRange(IEnumerable items) + { + lock (SyncRoot) + { + var index = buffer.Count; + using (var xs = new CloneCollection(items)) + { + foreach (var item in xs.Span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } + + public void AddLastRange(T[] items) + { + lock (SyncRoot) + { + var index = buffer.Count; + foreach (var item in items) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void AddLastRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + var index = buffer.Count; + foreach (var item in items) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public int IndexOf(T item) + { + lock (SyncRoot) + { + return buffer.IndexOf(item); + } + } + + void IList.Insert(int index, T item) + { + throw new NotSupportedException(); + } + + bool ICollection.Remove(T item) + { + throw new NotSupportedException(); + } + + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + void ICollection.Add(T item) + { + AddLast(item); + } + + public void Clear() + { + lock (SyncRoot) + { + buffer.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } + } + + public bool Contains(T item) + { + lock (SyncRoot) + { + return buffer.Contains(item); + } + } + + public void CopyTo(T[] array, int arrayIndex) + { + lock (SyncRoot) + { + buffer.CopyTo(array, arrayIndex); + } + } + + public IEnumerator GetEnumerator() + { + return new SynchronizedEnumerator(SyncRoot, buffer.GetEnumerator()); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + // View + + public ISynchronizedView CreateView(Func transform, bool reverse = false) + { + // TODO: + throw new NotImplementedException(); + // return new View(this, transform, reverse); + } + } +} diff --git a/src/ObservableCollections/ObservableList.cs b/src/ObservableCollections/ObservableList.cs index 550ccde..92ac0f8 100644 --- a/src/ObservableCollections/ObservableList.cs +++ b/src/ObservableCollections/ObservableList.cs @@ -78,7 +78,7 @@ namespace ObservableCollections lock (SyncRoot) { var index = list.Count; - using (var xs = new CopyedCollection(items)) + using (var xs = new CloneCollection(items)) { // to avoid iterate twice, require copy before insert. list.AddRange(xs.AsEnumerable()); @@ -189,7 +189,7 @@ namespace ObservableCollections { lock (SyncRoot) { - using (var xs = new CopyedCollection(items)) + using (var xs = new CloneCollection(items)) { list.InsertRange(index, xs.AsEnumerable()); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); @@ -201,7 +201,7 @@ namespace ObservableCollections { lock (SyncRoot) { - using (var xs = new CopyedCollection(items)) + using (var xs = new CloneCollection(items)) { list.InsertRange(index, xs.AsEnumerable()); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); @@ -245,7 +245,7 @@ namespace ObservableCollections var range = CollectionsMarshal.AsSpan(list).Slice(index, count); // require copy before remove - using (var xs = new CopyedCollection(range)) + using (var xs = new CloneCollection(range)) { list.RemoveRange(index, count); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(xs.Span, index)); diff --git a/src/ObservableCollections/ObservableQueue.cs b/src/ObservableCollections/ObservableQueue.cs index e43f90b..6696a6a 100644 --- a/src/ObservableCollections/ObservableQueue.cs +++ b/src/ObservableCollections/ObservableQueue.cs @@ -54,7 +54,7 @@ namespace ObservableCollections lock (SyncRoot) { var index = queue.Count; - using (var xs = new CopyedCollection(items)) + using (var xs = new CloneCollection(items)) { foreach (var item in xs.Span) { diff --git a/src/ObservableCollections/ObservableRingBuffer.cs b/src/ObservableCollections/ObservableRingBuffer.cs index 78997af..2d3c19c 100644 --- a/src/ObservableCollections/ObservableRingBuffer.cs +++ b/src/ObservableCollections/ObservableRingBuffer.cs @@ -1,4 +1,5 @@ -using System.Collections; +using ObservableCollections.Internal; +using System.Collections; namespace ObservableCollections { @@ -8,8 +9,6 @@ namespace ObservableCollections public event NotifyCollectionChangedEventHandler? CollectionChanged; - // TODO:SyncRoot - public ObservableRingBuffer() { this.buffer = new RingBuffer(); @@ -26,15 +25,21 @@ namespace ObservableCollections public T this[int index] { - // TODO:notify! - get { - return this.buffer[index]; + lock (SyncRoot) + { + return this.buffer[index]; + } } set { - this.buffer[index] = value; + lock (SyncRoot) + { + var oldValue = buffer[index]; + buffer[index] = value; + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Replace(value, oldValue, index)); + } } } @@ -67,120 +72,111 @@ namespace ObservableCollections } } - // AddFirstRange??? + // AddFirstRange is not exists. + + public void AddLastRange(IEnumerable items) + { + lock (SyncRoot) + { + var index = buffer.Count; + using (var xs = new CloneCollection(items)) + { + foreach (var item in xs.Span) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(xs.Span, index)); + } + } + } public void AddLastRange(T[] items) { lock (SyncRoot) { + var index = buffer.Count; foreach (var item in items) { buffer.AddLast(item); } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); + } + } + + public void AddLastRange(ReadOnlySpan items) + { + lock (SyncRoot) + { + var index = buffer.Count; + foreach (var item in items) + { + buffer.AddLast(item); + } + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(items, index)); } } public int IndexOf(T item) { - throw new NotImplementedException(); + lock (SyncRoot) + { + return buffer.IndexOf(item); + } } - public void Insert(int index, T item) + void IList.Insert(int index, T item) { - throw new NotImplementedException(); + throw new NotSupportedException(); } - public void RemoveAt(int index) + bool ICollection.Remove(T item) { - throw new NotImplementedException(); + throw new NotSupportedException(); } - public void Add(T item) + void IList.RemoveAt(int index) { - throw new NotImplementedException(); + throw new NotSupportedException(); + } + + void ICollection.Add(T item) + { + AddLast(item); } public void Clear() { - throw new NotImplementedException(); + lock (SyncRoot) + { + buffer.Clear(); + CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Reset()); + } } public bool Contains(T item) { - throw new NotImplementedException(); + lock (SyncRoot) + { + return buffer.Contains(item); + } } public void CopyTo(T[] array, int arrayIndex) { - throw new NotImplementedException(); - } - - public bool Remove(T item) - { - throw new NotImplementedException(); + lock (SyncRoot) + { + buffer.CopyTo(array, arrayIndex); + } } public IEnumerator GetEnumerator() { - throw new NotImplementedException(); + return new SynchronizedEnumerator(SyncRoot, buffer.GetEnumerator()); } IEnumerator IEnumerable.GetEnumerator() { - throw new NotImplementedException(); - } - } - - - // TODO:Is this? - public sealed class ObservableFixedSizeRingBuffer - { - RingBuffer buffer = default!; // TODO:??? - - int fixedSize; - - public event NotifyCollectionChangedEventHandler? CollectionChanged; - - // TODO:SyncRoot - public bool IsReadOnly => false; - - public object SyncRoot { get; } = new object(); - - public void AddLast(T value) - { - lock (SyncRoot) - { - if (buffer.Count == fixedSize) - { - // Remove One. - var removed = buffer.RemoveFirst(); - buffer.AddLast(value); - - CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Remove(removed, 0)); - CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs.Add(value, buffer.Count - 1)); - } - } - } - - public void AddLastRange(T[] values) - { - lock (SyncRoot) - { - if (buffer.Count + values.Length -1 == fixedSize) - { - for (int i = 0; i < values.Length; i++) - { - buffer.RemoveFirst(); // removes... - } - for (int i = 0; i < values.Length; i++) - { - buffer.AddLast(values[i]); - } - - // Remove... - } - - } + return GetEnumerator(); } } } diff --git a/src/ObservableCollections/ObservableStack.cs b/src/ObservableCollections/ObservableStack.cs index 354105e..2de5999 100644 --- a/src/ObservableCollections/ObservableStack.cs +++ b/src/ObservableCollections/ObservableStack.cs @@ -53,7 +53,7 @@ namespace ObservableCollections { lock (SyncRoot) { - using (var xs = new CopyedCollection(items)) + using (var xs = new CloneCollection(items)) { foreach (var item in xs.Span) { diff --git a/src/ObservableCollections/RingBuffer.cs b/src/ObservableCollections/RingBuffer.cs index e56b737..61fd4ce 100644 --- a/src/ObservableCollections/RingBuffer.cs +++ b/src/ObservableCollections/RingBuffer.cs @@ -171,12 +171,7 @@ namespace ObservableCollections } } - public Enumerator GetEnumerator() - { - return new Enumerator(GetSpan()); - } - - IEnumerator IEnumerable.GetEnumerator() + public IEnumerator GetEnumerator() { if (count == 0) yield break; @@ -251,7 +246,7 @@ namespace ObservableCollections public int IndexOf(T item) { var i = 0; - foreach (var v in this) + foreach (var v in GetSpan()) { if (EqualityComparer.Default.Equals(item, v)) { @@ -266,7 +261,7 @@ namespace ObservableCollections { var result = new T[count]; var i = 0; - foreach (var item in this) + foreach (var item in GetSpan()) { result[i++] = item; } @@ -298,6 +293,25 @@ namespace ObservableCollections { throw new InvalidOperationException("RingBuffer is empty."); } + } + + public ref struct RingBufferSpan + { + public readonly ReadOnlySpan First; + public readonly ReadOnlySpan Second; + public readonly int Count; + + internal RingBufferSpan(ReadOnlySpan first, ReadOnlySpan second, int count) + { + First = first; + Second = second; + Count = count; + } + + public Enumerator GetEnumerator() + { + return new Enumerator(this); + } public ref struct Enumerator { @@ -349,18 +363,4 @@ namespace ObservableCollections } } } - - public ref struct RingBufferSpan - { - public readonly ReadOnlySpan First; - public readonly ReadOnlySpan Second; - public readonly int Count; - - internal RingBufferSpan(ReadOnlySpan first, ReadOnlySpan second, int count) - { - First = first; - Second = second; - Count = count; - } - } } \ No newline at end of file