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.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);

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)
{
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));

View File

@ -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)
{

View File

@ -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();
}
}
}

View File

@ -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)
{

View File

@ -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;
}
}
}