complete ringbuffer
This commit is contained in:
parent
f3b47c85e9
commit
8de22f5ba2
@ -86,8 +86,8 @@ namespace ObservableCollections
|
||||
{
|
||||
if (capacity == buffer.Count)
|
||||
{
|
||||
var remItem = buffer.RemoveLast();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(remItem, capacity - 1));
|
||||
var remItem = buffer.RemoveFirst();
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(remItem, 0));
|
||||
}
|
||||
|
||||
buffer.AddLast(item);
|
||||
@ -124,10 +124,10 @@ namespace ObservableCollections
|
||||
{
|
||||
using (var xs = new CloneCollection<T>(items))
|
||||
{
|
||||
if (capacity >= buffer.Count + xs.Span.Length - 1)
|
||||
if (capacity <= buffer.Count + xs.Span.Length)
|
||||
{
|
||||
// calc remove count
|
||||
var remCount = Math.Min(capacity, buffer.Count + xs.Span.Length - 1 - capacity);
|
||||
var remCount = Math.Min(buffer.Count, buffer.Count + xs.Span.Length - capacity);
|
||||
using (var ys = new ResizableArray<T>(remCount))
|
||||
{
|
||||
for (int i = 0; i < remCount; i++)
|
||||
@ -159,12 +159,33 @@ namespace ObservableCollections
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (capacity <= buffer.Count + items.Length)
|
||||
{
|
||||
// calc remove count
|
||||
var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity);
|
||||
using (var ys = new ResizableArray<T>(remCount))
|
||||
{
|
||||
for (int i = 0; i < remCount; i++)
|
||||
{
|
||||
ys.Add(buffer.RemoveFirst());
|
||||
}
|
||||
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
|
||||
}
|
||||
}
|
||||
|
||||
var index = buffer.Count;
|
||||
foreach (var item in items)
|
||||
var span = items.AsSpan();
|
||||
if (span.Length > capacity)
|
||||
{
|
||||
span = span.Slice(span.Length - capacity);
|
||||
}
|
||||
|
||||
foreach (var item in span)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(items, index));
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,33 +193,33 @@ namespace ObservableCollections
|
||||
{
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (capacity >= buffer.Count + xs.Span.Length - 1)
|
||||
if (capacity <= buffer.Count + items.Length)
|
||||
{
|
||||
// calc remove count
|
||||
var remCount = Math.Min(buffer.Count, buffer.Count + items.Length - capacity);
|
||||
using (var ys = new ResizableArray<T>(remCount))
|
||||
{
|
||||
// calc remove count
|
||||
var remCount = Math.Min(capacity, buffer.Count + xs.Span.Length - 1 - capacity);
|
||||
using (var ys = new ResizableArray<T>(remCount))
|
||||
for (int i = 0; i < remCount; i++)
|
||||
{
|
||||
for (int i = 0; i < remCount; i++)
|
||||
{
|
||||
ys.Add(buffer.RemoveFirst());
|
||||
}
|
||||
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
|
||||
ys.Add(buffer.RemoveFirst());
|
||||
}
|
||||
}
|
||||
|
||||
var index = buffer.Count;
|
||||
var span = xs.Span;
|
||||
if (span.Length > capacity)
|
||||
{
|
||||
span = span.Slice(span.Length - capacity);
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Remove(ys.Span, 0));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var item in span)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
|
||||
var index = buffer.Count;
|
||||
var span = items;
|
||||
if (span.Length > capacity)
|
||||
{
|
||||
span = span.Slice(span.Length - capacity);
|
||||
}
|
||||
|
||||
foreach (var item in span)
|
||||
{
|
||||
buffer.AddLast(item);
|
||||
}
|
||||
CollectionChanged?.Invoke(NotifyCollectionChangedEventArgs<T>.Add(span, index));
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,13 +286,9 @@ namespace ObservableCollections
|
||||
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);
|
||||
return new ObservableRingBuffer<T>.View<TView>(this, transform, reverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,9 +11,10 @@ namespace ObservableCollections
|
||||
return new View<TView>(this, transform, reverse);
|
||||
}
|
||||
|
||||
sealed class View<TView> : ISynchronizedView<T, TView>
|
||||
// used with ObservableFixedSizeRingBuffer
|
||||
internal sealed class View<TView> : ISynchronizedView<T, TView>
|
||||
{
|
||||
readonly ObservableRingBuffer<T> source;
|
||||
readonly IObservableCollection<T> source;
|
||||
readonly Func<T, TView> selector;
|
||||
readonly bool reverse;
|
||||
readonly RingBuffer<(T, TView)> ringBuffer;
|
||||
@ -25,7 +26,7 @@ namespace ObservableCollections
|
||||
|
||||
public object SyncRoot { get; }
|
||||
|
||||
public View(ObservableRingBuffer<T> source, Func<T, TView> selector, bool reverse)
|
||||
public View(IObservableCollection<T> source, Func<T, TView> selector, bool reverse)
|
||||
{
|
||||
this.source = source;
|
||||
this.selector = selector;
|
||||
@ -34,7 +35,7 @@ namespace ObservableCollections
|
||||
this.SyncRoot = new object();
|
||||
lock (source.SyncRoot)
|
||||
{
|
||||
this.ringBuffer = new RingBuffer<(T, TView)>(source.buffer.Select(x => (x, selector(x))));
|
||||
this.ringBuffer = new RingBuffer<(T, TView)>(source.Select(x => (x, selector(x))));
|
||||
this.source.CollectionChanged += SourceCollectionChanged;
|
||||
}
|
||||
}
|
||||
@ -202,6 +203,17 @@ namespace ObservableCollections
|
||||
ringBuffer.Clear();
|
||||
break;
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
// range is not supported
|
||||
{
|
||||
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:
|
||||
default:
|
||||
break;
|
||||
|
@ -104,7 +104,7 @@ namespace ObservableCollections
|
||||
{
|
||||
if (count == 0) ThrowForEmpty();
|
||||
|
||||
var index = (head + count) & mask;
|
||||
var index = (head + count - 1) & mask;
|
||||
var v = buffer[index];
|
||||
buffer[index] = default!;
|
||||
count--;
|
||||
|
118
tests/ObservableCollections.Tests/ObservableRingBufferTest.cs
Normal file
118
tests/ObservableCollections.Tests/ObservableRingBufferTest.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ObservableCollections.Tests
|
||||
{
|
||||
public class ObservableRingBufferTest
|
||||
{
|
||||
[Fact]
|
||||
public void Standard()
|
||||
{
|
||||
var buf = new ObservableRingBuffer<int>();
|
||||
var view = buf.CreateView(x => new ViewContainer<int>(x));
|
||||
|
||||
buf.AddLast(10);
|
||||
buf.AddLast(50);
|
||||
buf.AddLast(30);
|
||||
buf.AddLast(20);
|
||||
buf.AddLast(40);
|
||||
|
||||
void Equal(params int[] expected)
|
||||
{
|
||||
buf.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
|
||||
buf[2] = 99;
|
||||
Equal(10, 50, 99, 20, 40);
|
||||
|
||||
buf.AddFirst(1000);
|
||||
Equal(1000, 10, 50, 99, 20, 40);
|
||||
|
||||
buf.RemoveFirst().Should().Be(1000);
|
||||
Equal(10, 50, 99, 20, 40);
|
||||
|
||||
buf.RemoveLast().Should().Be(40);
|
||||
Equal(10, 50, 99, 20);
|
||||
|
||||
buf.AddLastRange(new[] { 1, 2, 3 });
|
||||
buf.AddLastRange(new[] { 4, 5, 6 }.AsSpan());
|
||||
buf.AddLastRange(new[] { 7, 8, 9 }.AsEnumerable());
|
||||
|
||||
Equal(10, 50, 99, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
|
||||
buf.Clear();
|
||||
Equal();
|
||||
|
||||
buf.AddFirst(9999);
|
||||
Equal(9999);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void FixedSize()
|
||||
{
|
||||
var buf = new ObservableFixedSizeRingBuffer<int>(5);
|
||||
var view = buf.CreateView(x => new ViewContainer<int>(x));
|
||||
|
||||
void Equal(params int[] expected)
|
||||
{
|
||||
buf.Should().Equal(expected);
|
||||
view.Select(x => x.Value).Should().Equal(expected);
|
||||
view.Select(x => x.View).Should().Equal(expected.Select(x => new ViewContainer<int>(x)));
|
||||
}
|
||||
|
||||
buf.AddLast(10);
|
||||
buf.AddLast(50);
|
||||
buf.AddLast(30);
|
||||
buf.AddLast(20);
|
||||
buf.AddLast(40);
|
||||
|
||||
Equal(10, 50, 30, 20, 40);
|
||||
|
||||
buf.AddLast(100);
|
||||
Equal(50, 30, 20, 40, 100);
|
||||
|
||||
buf.AddFirst(99);
|
||||
Equal(99, 50, 30, 20, 40);
|
||||
|
||||
buf[0] = 10;
|
||||
buf[2] = 99;
|
||||
Equal(10, 50, 99, 20, 40);
|
||||
|
||||
buf.AddFirst(1000);
|
||||
Equal(1000, 10, 50, 99, 20);
|
||||
|
||||
buf.RemoveFirst().Should().Be(1000);
|
||||
Equal(10, 50, 99, 20);
|
||||
|
||||
buf.RemoveLast().Should().Be(20);
|
||||
Equal(10, 50, 99);
|
||||
|
||||
buf.AddLastRange(new[] { 1, 2, 3 });
|
||||
Equal(50, 99, 1, 2, 3);
|
||||
buf.AddLastRange(new[] { 4, 5, 6 }.AsSpan());
|
||||
Equal(2, 3, 4, 5, 6);
|
||||
buf.AddLastRange(new[] { 7, 8, 9 }.AsEnumerable());
|
||||
Equal(5, 6, 7, 8, 9);
|
||||
|
||||
buf.Clear();
|
||||
Equal();
|
||||
|
||||
buf.AddLastRange(new int[] { });
|
||||
Equal();
|
||||
|
||||
buf.AddLastRange(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
|
||||
Equal(8, 9, 10, 11, 12);
|
||||
|
||||
buf.AddLastRange(new[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120 }.AsSpan());
|
||||
Equal(80, 90, 100, 110, 120);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,13 +23,13 @@ namespace ObservableCollections.Tests
|
||||
list.AddLast(5); list.Should().Equal(1, 2, 3, 4, 5);
|
||||
list.AddLast(6); list.Should().Equal(1, 2, 3, 4, 5, 6);
|
||||
|
||||
list.RemoveLast();
|
||||
list.RemoveLast();
|
||||
list.RemoveLast().Should().Be(6);
|
||||
list.RemoveLast().Should().Be(5);
|
||||
list.Should().Equal(1, 2, 3, 4);
|
||||
list.Reverse().Should().Equal(4, 3, 2, 1);
|
||||
|
||||
list.RemoveFirst();
|
||||
list.RemoveFirst();
|
||||
list.RemoveFirst().Should().Be(1);
|
||||
list.RemoveFirst().Should().Be(2);
|
||||
list.Should().Equal(3, 4);
|
||||
|
||||
list.AddFirst(99);
|
||||
@ -70,12 +70,12 @@ namespace ObservableCollections.Tests
|
||||
list.Should().Equal(6, 5, 4, 3, 2, 1);
|
||||
list.Reverse().Should().Equal(1, 2, 3, 4, 5, 6);
|
||||
|
||||
list.RemoveLast();
|
||||
list.RemoveLast();
|
||||
list.RemoveLast().Should().Be(1);
|
||||
list.RemoveLast().Should().Be(2);
|
||||
list.Should().Equal(6, 5, 4, 3);
|
||||
|
||||
list.RemoveFirst();
|
||||
list.RemoveFirst();
|
||||
list.RemoveFirst().Should().Be(6);
|
||||
list.RemoveFirst().Should().Be(5);
|
||||
list.Should().Equal(4, 3);
|
||||
|
||||
list.AddFirst(99);
|
||||
|
2
tests/ObservableCollections.Tests/_GlobalUsings.cs
Normal file
2
tests/ObservableCollections.Tests/_GlobalUsings.cs
Normal file
@ -0,0 +1,2 @@
|
||||
global using Xunit;
|
||||
global using FluentAssertions;
|
Loading…
x
Reference in New Issue
Block a user