Mirror
This commit is contained in:
parent
b618f509ad
commit
b5431b9f9d
@ -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<T> : IDisposable
|
||||
/// <summary>
|
||||
/// ReadOnly cloned collection.
|
||||
/// </summary>
|
||||
internal struct CloneCollection<T> : IDisposable
|
||||
{
|
||||
T[]? array;
|
||||
int length;
|
||||
@ -16,13 +16,13 @@ namespace ObservableCollections.Internal
|
||||
|
||||
public IEnumerable<T> AsEnumerable() => new EnumerableCollection(array, length);
|
||||
|
||||
public CopyedCollection(T item)
|
||||
public CloneCollection(T item)
|
||||
{
|
||||
this.array = ArrayPool<T>.Shared.Rent(1);
|
||||
this.length = 1;
|
||||
}
|
||||
|
||||
public CopyedCollection(IEnumerable<T> source)
|
||||
public CloneCollection(IEnumerable<T> source)
|
||||
{
|
||||
if (Enumerable.TryGetNonEnumeratedCount(source, out var count))
|
||||
{
|
||||
@ -58,7 +58,7 @@ namespace ObservableCollections.Internal
|
||||
}
|
||||
}
|
||||
|
||||
public CopyedCollection(ReadOnlySpan<T> source)
|
||||
public CloneCollection(ReadOnlySpan<T> source)
|
||||
{
|
||||
var array = ArrayPool<T>.Shared.Rent(source.Length);
|
||||
source.CopyTo(array);
|
214
src/ObservableCollections/ObservableFixedSizeRingBuffer.cs
Normal file
214
src/ObservableCollections/ObservableFixedSizeRingBuffer.cs
Normal file
@ -0,0 +1,214 @@
|
||||
using ObservableCollections.Internal;
|
||||
using System.Collections;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
public sealed partial class ObservableFixedSizeRingBuffer<T> : IList<T>, IReadOnlyList<T>, IObservableCollection<T>
|
||||
{
|
||||
readonly RingBuffer<T> buffer;
|
||||
readonly int capacity;
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
|
||||
|
||||
public ObservableFixedSizeRingBuffer(int capacity)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.buffer = new RingBuffer<T>(capacity);
|
||||
}
|
||||
|
||||
public ObservableFixedSizeRingBuffer(int capacity, IEnumerable<T> collection)
|
||||
{
|
||||
this.capacity = capacity;
|
||||
this.buffer = new RingBuffer<T>(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<T>.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<T>.Remove(item, capacity - 1));
|
||||
}
|
||||
|
||||
buffer.AddFirst(item);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLast(T item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (capacity == buffer.Count)
|
||||
{
|
||||
buffer.RemoveLast();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(item, capacity - 1));
|
||||
}
|
||||
|
||||
buffer.AddLast(item);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(item, buffer.Count - 1));
|
||||
}
|
||||
}
|
||||
|
||||
// AddFirstRange is not exists.
|
||||
|
||||
public void AddLastRange(IEnumerable<T> items)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = buffer.Count;
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
foreach (var item in xs.Span)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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<T>.Add(items, index));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLastRange(ReadOnlySpan<T> items)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = buffer.Count;
|
||||
foreach (var item in items)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
|
||||
}
|
||||
}
|
||||
|
||||
public int IndexOf(T item)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return buffer.IndexOf(item);
|
||||
}
|
||||
}
|
||||
|
||||
void IList<T>.Insert(int index, T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void IList<T>.RemoveAt(int index)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item)
|
||||
{
|
||||
AddLast(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
buffer.Clear();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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<T> GetEnumerator()
|
||||
{
|
||||
return new SynchronizedEnumerator<T>(SyncRoot, buffer.GetEnumerator());
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
// View
|
||||
|
||||
public ISynchronizedView<T, TView> CreateView<TView>(Func<T, TView> transform, bool reverse = false)
|
||||
{
|
||||
// TODO:
|
||||
throw new NotImplementedException();
|
||||
// return new View<TView>(this, transform, reverse);
|
||||
}
|
||||
}
|
||||
}
|
@ -78,7 +78,7 @@ namespace ObservableCollections
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = list.Count;
|
||||
using (var xs = new CopyedCollection<T>(items))
|
||||
using (var xs = new CloneCollection<T>(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<T>(items))
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
list.InsertRange(index, xs.AsEnumerable());
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
|
||||
@ -201,7 +201,7 @@ namespace ObservableCollections
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
using (var xs = new CopyedCollection<T>(items))
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
list.InsertRange(index, xs.AsEnumerable());
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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<T>(range))
|
||||
using (var xs = new CloneCollection<T>(range))
|
||||
{
|
||||
list.RemoveRange(index, count);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(xs.Span, index));
|
||||
|
@ -54,7 +54,7 @@ namespace ObservableCollections
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = queue.Count;
|
||||
using (var xs = new CopyedCollection<T>(items))
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
foreach (var item in xs.Span)
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Collections;
|
||||
using ObservableCollections.Internal;
|
||||
using System.Collections;
|
||||
|
||||
namespace ObservableCollections
|
||||
{
|
||||
@ -8,8 +9,6 @@ namespace ObservableCollections
|
||||
|
||||
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
|
||||
|
||||
// TODO:SyncRoot
|
||||
|
||||
public ObservableRingBuffer()
|
||||
{
|
||||
this.buffer = new RingBuffer<T>();
|
||||
@ -26,15 +25,21 @@ namespace ObservableCollections
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
// TODO:notify!
|
||||
|
||||
get
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
return this.buffer[index];
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
this.buffer[index] = value;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var oldValue = buffer[index];
|
||||
buffer[index] = value;
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Replace(value, oldValue, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,120 +72,111 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
// AddFirstRange???
|
||||
// AddFirstRange is not exists.
|
||||
|
||||
public void AddLastRange(IEnumerable<T> items)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = buffer.Count;
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
foreach (var item in xs.Span)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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<T>.Add(items, index));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddLastRange(ReadOnlySpan<T> items)
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
var index = buffer.Count;
|
||||
foreach (var item in items)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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<T>.Insert(int index, T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
bool ICollection<T>.Remove(T item)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
void IList<T>.RemoveAt(int index)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
void ICollection<T>.Add(T item)
|
||||
{
|
||||
AddLast(item);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
lock (SyncRoot)
|
||||
{
|
||||
buffer.Clear();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.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)
|
||||
lock (SyncRoot)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
buffer.CopyTo(array, arrayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<T> GetEnumerator()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return new SynchronizedEnumerator<T>(SyncRoot, buffer.GetEnumerator());
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ namespace ObservableCollections
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
using (var xs = new CopyedCollection<T>(items))
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
foreach (var item in xs.Span)
|
||||
{
|
||||
|
@ -171,12 +171,7 @@ namespace ObservableCollections
|
||||
}
|
||||
}
|
||||
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(GetSpan());
|
||||
}
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||
public IEnumerator<T> 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<T>.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<T>
|
||||
{
|
||||
public readonly ReadOnlySpan<T> First;
|
||||
public readonly ReadOnlySpan<T> Second;
|
||||
public readonly int Count;
|
||||
|
||||
internal RingBufferSpan(ReadOnlySpan<T> first, ReadOnlySpan<T> 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<T>
|
||||
{
|
||||
public readonly ReadOnlySpan<T> First;
|
||||
public readonly ReadOnlySpan<T> Second;
|
||||
public readonly int Count;
|
||||
|
||||
internal RingBufferSpan(ReadOnlySpan<T> first, ReadOnlySpan<T> second, int count)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user