This commit is contained in:
neuecc 2021-08-31 20:20:27 +09:00
parent b618f509ad
commit b5431b9f9d
7 changed files with 324 additions and 114 deletions

View File

@ -1,13 +1,13 @@
using System; using System.Buffers;
using System.Buffers;
using System.Collections; using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace ObservableCollections.Internal namespace ObservableCollections.Internal
{ {
internal struct CopyedCollection<T> : IDisposable /// <summary>
/// ReadOnly cloned collection.
/// </summary>
internal struct CloneCollection<T> : IDisposable
{ {
T[]? array; T[]? array;
int length; int length;
@ -16,13 +16,13 @@ namespace ObservableCollections.Internal
public IEnumerable<T> AsEnumerable() => new EnumerableCollection(array, length); public IEnumerable<T> AsEnumerable() => new EnumerableCollection(array, length);
public CopyedCollection(T item) public CloneCollection(T item)
{ {
this.array = ArrayPool<T>.Shared.Rent(1); this.array = ArrayPool<T>.Shared.Rent(1);
this.length = 1; this.length = 1;
} }
public CopyedCollection(IEnumerable<T> source) public CloneCollection(IEnumerable<T> source)
{ {
if (Enumerable.TryGetNonEnumeratedCount(source, out var count)) 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); var array = ArrayPool<T>.Shared.Rent(source.Length);
source.CopyTo(array); source.CopyTo(array);

View 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);
}
}
}

View File

@ -78,7 +78,7 @@ namespace ObservableCollections
lock (SyncRoot) lock (SyncRoot)
{ {
var index = list.Count; 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. // to avoid iterate twice, require copy before insert.
list.AddRange(xs.AsEnumerable()); list.AddRange(xs.AsEnumerable());
@ -189,7 +189,7 @@ namespace ObservableCollections
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
using (var xs = new CopyedCollection<T>(items)) using (var xs = new CloneCollection<T>(items))
{ {
list.InsertRange(index, xs.AsEnumerable()); list.InsertRange(index, xs.AsEnumerable());
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index)); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
@ -201,7 +201,7 @@ namespace ObservableCollections
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
using (var xs = new CopyedCollection<T>(items)) using (var xs = new CloneCollection<T>(items))
{ {
list.InsertRange(index, xs.AsEnumerable()); list.InsertRange(index, xs.AsEnumerable());
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index)); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(xs.Span, index));
@ -245,7 +245,7 @@ namespace ObservableCollections
var range = CollectionsMarshal.AsSpan(list).Slice(index, count); var range = CollectionsMarshal.AsSpan(list).Slice(index, count);
// require copy before remove // require copy before remove
using (var xs = new CopyedCollection<T>(range)) using (var xs = new CloneCollection<T>(range))
{ {
list.RemoveRange(index, count); list.RemoveRange(index, count);
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(xs.Span, index)); CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(xs.Span, index));

View File

@ -54,7 +54,7 @@ namespace ObservableCollections
lock (SyncRoot) lock (SyncRoot)
{ {
var index = queue.Count; var index = queue.Count;
using (var xs = new CopyedCollection<T>(items)) using (var xs = new CloneCollection<T>(items))
{ {
foreach (var item in xs.Span) foreach (var item in xs.Span)
{ {

View File

@ -1,4 +1,5 @@
using System.Collections; using ObservableCollections.Internal;
using System.Collections;
namespace ObservableCollections namespace ObservableCollections
{ {
@ -8,8 +9,6 @@ namespace ObservableCollections
public event NotifyCollectionChangedEventHandler<T>? CollectionChanged; public event NotifyCollectionChangedEventHandler<T>? CollectionChanged;
// TODO:SyncRoot
public ObservableRingBuffer() public ObservableRingBuffer()
{ {
this.buffer = new RingBuffer<T>(); this.buffer = new RingBuffer<T>();
@ -26,15 +25,21 @@ namespace ObservableCollections
public T this[int index] public T this[int index]
{ {
// TODO:notify!
get get
{ {
return this.buffer[index]; lock (SyncRoot)
{
return this.buffer[index];
}
} }
set 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) public void AddLastRange(T[] items)
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
var index = buffer.Count;
foreach (var item in items) foreach (var item in items)
{ {
buffer.AddLast(item); 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) 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() public void Clear()
{ {
throw new NotImplementedException(); lock (SyncRoot)
{
buffer.Clear();
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Reset());
}
} }
public bool Contains(T item) public bool Contains(T item)
{ {
throw new NotImplementedException(); lock (SyncRoot)
{
return buffer.Contains(item);
}
} }
public void CopyTo(T[] array, int arrayIndex) public void CopyTo(T[] array, int arrayIndex)
{ {
throw new NotImplementedException(); lock (SyncRoot)
} {
buffer.CopyTo(array, arrayIndex);
public bool Remove(T item) }
{
throw new NotImplementedException();
} }
public IEnumerator<T> GetEnumerator() public IEnumerator<T> GetEnumerator()
{ {
throw new NotImplementedException(); return new SynchronizedEnumerator<T>(SyncRoot, buffer.GetEnumerator());
} }
IEnumerator IEnumerable.GetEnumerator() IEnumerator IEnumerable.GetEnumerator()
{ {
throw new NotImplementedException(); return GetEnumerator();
}
}
// 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...
}
}
} }
} }
} }

View File

@ -53,7 +53,7 @@ namespace ObservableCollections
{ {
lock (SyncRoot) lock (SyncRoot)
{ {
using (var xs = new CopyedCollection<T>(items)) using (var xs = new CloneCollection<T>(items))
{ {
foreach (var item in xs.Span) foreach (var item in xs.Span)
{ {

View File

@ -171,12 +171,7 @@ namespace ObservableCollections
} }
} }
public Enumerator GetEnumerator() public IEnumerator<T> GetEnumerator()
{
return new Enumerator(GetSpan());
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{ {
if (count == 0) yield break; if (count == 0) yield break;
@ -251,7 +246,7 @@ namespace ObservableCollections
public int IndexOf(T item) public int IndexOf(T item)
{ {
var i = 0; var i = 0;
foreach (var v in this) foreach (var v in GetSpan())
{ {
if (EqualityComparer<T>.Default.Equals(item, v)) if (EqualityComparer<T>.Default.Equals(item, v))
{ {
@ -266,7 +261,7 @@ namespace ObservableCollections
{ {
var result = new T[count]; var result = new T[count];
var i = 0; var i = 0;
foreach (var item in this) foreach (var item in GetSpan())
{ {
result[i++] = item; result[i++] = item;
} }
@ -298,6 +293,25 @@ namespace ObservableCollections
{ {
throw new InvalidOperationException("RingBuffer is empty."); 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 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;
}
}
} }