or concept
This commit is contained in:
parent
eec98af604
commit
f94156f564
@ -36,6 +36,9 @@ namespace ObservableCollections
|
||||
public readonly int NewStartingIndex;
|
||||
public readonly int OldStartingIndex;
|
||||
|
||||
// TODO:is this required?
|
||||
// byte distinguishedKey;
|
||||
|
||||
public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, bool isSingleItem, T newItem = default!, T oldItem = default!, ReadOnlySpan<T> newItems = default, ReadOnlySpan<T> oldItems = default, int newStartingIndex = -1, int oldStartingIndex = -1)
|
||||
{
|
||||
Action = action;
|
||||
|
217
src/ObservableCollections/ObservableRingBuffer.Views.cs
Normal file
217
src/ObservableCollections/ObservableRingBuffer.Views.cs
Normal file
@ -0,0 +1,217 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System.Collections;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed partial class ObservableRingBuffer<T>
|
||||
{
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
{
|
||||
return new View<TView>(this, transform, reverse);
|
||||
}
|
||||
|
||||
sealed class View<TView> : ISynchronizedView<T, TView>
|
||||
{
|
||||
readonly ObservableRingBuffer<T> source;
|
||||
readonly Func<T, TView> selector;
|
||||
readonly bool reverse;
|
||||
readonly RingBuffer<(T, TView)> ringBuffer;
|
||||
|
||||
ISynchronizedViewFilter<T, TView> filter;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? RoutingCollectionChanged;
|
||||
public event Action<NotifyCollectionChangedAction>? CollectionStateChanged;
|
||||
|
||||
public object SyncRoot { get; }
|
||||
|
||||
public View(ObservableRingBuffer<T> source, Func<T, TView> selector, bool reverse)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
this.reverse = reverse;
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
this.SyncRoot = new object();
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
this.ringBuffer = new RingBuffer<(T, TView)>(source.buffer.Select(x => (x, selector(x))));
|
||||
this.source.CollectionChanged += SourceCollectionChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return ringBuffer.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AttachFilter(ISynchronizedViewFilter<T, TView> filter)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = filter;
|
||||
foreach (var (value, view) in ringBuffer)
|
||||
{
|
||||
filter.InvokeOnAttach(value, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFilter(Action<T, TView>? resetAction)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
this.filter = SynchronizedViewFilter<T, TView>.Null;
|
||||
if (resetAction != null)
|
||||
{
|
||||
foreach (var (item, view) in ringBuffer)
|
||||
{
|
||||
resetAction(item, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public INotifyCollectionChangedSynchronizedView<T, TView> WithINotifyCollectionChanged()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return new NotifyCollectionChangedSynchronizedView<T, TView>(this);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<(T, TView)> GetEnumerator()
|
||||
{
|
||||
if (!reverse)
|
||||
{
|
||||
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, ringBuffer.AsEnumerable().GetEnumerator(), filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SynchronizedViewEnumerator<T, TView>(SyncRoot, ringBuffer.AsEnumerable().Reverse().GetEnumerator(), filter);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.source.CollectionChanged -= SourceCollectionChanged;
|
||||
}
|
||||
|
||||
private void SourceCollectionChanged(in NotifyCollectionChangedEventArgs<T> e)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
// can not distinguish AddFirst and AddLast when collection count is 0.
|
||||
// So, in that case, use AddLast.
|
||||
// The internal structure may be different from the parent, but the result is same.
|
||||
// RangeOperation is only exists AddLastRange because we can not distinguish FirstRange or LastRange.
|
||||
if (e.NewStartingIndex == 0 && ringBuffer.Count != 0)
|
||||
{
|
||||
// AddFirst
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var v = (e.NewItem, selector(e.NewItem));
|
||||
ringBuffer.AddFirst(v);
|
||||
filter.InvokeOnAdd(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
var v = (item, selector(item));
|
||||
ringBuffer.AddFirst(v);
|
||||
filter.InvokeOnAdd(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// AddLast
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var v = (e.NewItem, selector(e.NewItem));
|
||||
ringBuffer.AddLast(v);
|
||||
filter.InvokeOnAdd(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
var v = (item, selector(item));
|
||||
ringBuffer.AddLast(v);
|
||||
filter.InvokeOnAdd(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
if (e.IsSingleItem)
|
||||
{
|
||||
var v = ringBuffer[e.OldStartingIndex];
|
||||
ringBuffer.RemoveAt(e.OldStartingIndex);
|
||||
filter.InvokeOnRemove(v.Item1, v.Item2);
|
||||
}
|
||||
else
|
||||
{
|
||||
var len = e.OldStartingIndex + e.OldItems.Length;
|
||||
for (int i = e.OldStartingIndex; i < len; i++)
|
||||
{
|
||||
var v = ringBuffer[i];
|
||||
filter.InvokeOnRemove(v.Item1, v.Item2);
|
||||
}
|
||||
|
||||
ringBuffer.RemoveRange(e.OldStartingIndex, e.OldItems.Length);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
// ObservableList does not support replace range
|
||||
{
|
||||
var v = (e.NewItem, selector(e.NewItem));
|
||||
|
||||
var oldItem = ringBuffer[e.NewStartingIndex];
|
||||
ringBuffer[e.NewStartingIndex] = v;
|
||||
|
||||
filter.InvokeOnRemove(oldItem);
|
||||
filter.InvokeOnAdd(v);
|
||||
break;
|
||||
}
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
{
|
||||
var removeItem = ringBuffer[e.OldStartingIndex];
|
||||
ringBuffer.RemoveAt(e.OldStartingIndex);
|
||||
ringBuffer.Insert(e.NewStartingIndex, removeItem);
|
||||
|
||||
filter.InvokeOnMove(removeItem);
|
||||
}
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
if (!filter.IsNullFilter())
|
||||
{
|
||||
foreach (var item in ringBuffer)
|
||||
{
|
||||
filter.InvokeOnRemove(item);
|
||||
}
|
||||
}
|
||||
ringBuffer.Clear();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
RoutingCollectionChanged?.Invoke(e);
|
||||
CollectionStateChanged?.Invoke(e.Action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,13 @@
|
||||
namespace ObservableCollections
|
||||
{
|
||||
using System.Collections;
|
||||
|
||||
public sealed partial class ObservableRingBuffer<T>
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed partial class ObservableRingBuffer<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
|
||||
{
|
||||
readonly RingBuffer<T> buffer;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
|
||||
|
||||
// TODO:SyncRoot
|
||||
|
||||
public ObservableRingBuffer()
|
||||
@ -17,10 +20,14 @@
|
||||
this.buffer = new RingBuffer<T>(collection);
|
||||
}
|
||||
|
||||
public int Count => buffer.Count;
|
||||
public bool IsReadOnly => false;
|
||||
|
||||
public object SyncRoot { get; } = new object();
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
// TODO:notify!
|
||||
|
||||
get
|
||||
{
|
||||
return this.buffer[index];
|
||||
@ -31,12 +38,129 @@
|
||||
}
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return buffer.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFirst(T item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
buffer.AddFirst(item);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLast(T item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, buffer.Count - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// AddFirstRange???
|
||||
|
||||
public void AddLastRange(T[] items)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Insert(int index, T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Contains(T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public bool Remove(T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO:Is this?
|
||||
public sealed class ObservableFixedSizeRingBuffer<T>
|
||||
{
|
||||
RingBuffer<T> buffer = default!; // TODO:???
|
||||
|
||||
int fixedSize;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? 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<T>.Remove(removed, 0));
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(value, buffer.Count - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed class RingBuffer<T> : IList<T>
|
||||
public sealed class RingBuffer<T> : IList<T>, IReadOnlyList<T>
|
||||
{
|
||||
T[] buffer;
|
||||
int head;
|
||||
@ -100,23 +100,27 @@ namespace ObservableCollections
|
||||
count++;
|
||||
}
|
||||
|
||||
public void RemoveLast()
|
||||
public T RemoveLast()
|
||||
{
|
||||
if (count == 0) ThrowForEmpty();
|
||||
|
||||
var index = (head + count) & mask;
|
||||
var v = buffer[index];
|
||||
buffer[index] = default!;
|
||||
count--;
|
||||
return v;
|
||||
}
|
||||
|
||||
public void RemoveFirst()
|
||||
public T RemoveFirst()
|
||||
{
|
||||
if (count == 0) ThrowForEmpty();
|
||||
|
||||
var index = head & mask;
|
||||
var v = buffer[index];
|
||||
buffer[index] = default!;
|
||||
head = head + 1;
|
||||
count--;
|
||||
return v;
|
||||
}
|
||||
|
||||
void EnsureCapacity()
|
||||
|
Loading…
x
Reference in New Issue
Block a user