diff --git a/mstum.utils.tests/CircularBufferTests.cs b/mstum.utils.tests/CircularBufferTests.cs index 8a74f3f..e969898 100644 --- a/mstum.utils.tests/CircularBufferTests.cs +++ b/mstum.utils.tests/CircularBufferTests.cs @@ -6,361 +6,444 @@ namespace mstum.utils.tests { - [TestClass] public class CircularBufferTests { - [TestMethod] - public void Add_MoreThanCapacity_ProperlyCircles() + [TestClass] + public class Remove { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - - Assert.AreEqual(2, buffer.Skip(0).First()); - Assert.AreEqual(3, buffer.Skip(1).First()); - Assert.AreEqual(4, buffer.Skip(2).First()); - } + [TestMethod] + public void Remove_ReferenceType_RemovesItem() + { + var buffer = new CircularBuffer(3); + var o1 = new TestRefType(Guid.NewGuid()); + var o2 = new TestRefType(Guid.NewGuid()); + var o3 = new TestRefType(Guid.NewGuid()); - [TestMethod] - public void Add_WithinCapacity_ProperlyAdds() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); + buffer.Add(o1); + buffer.Add(o2); + buffer.Add(o3); - Assert.AreEqual(1, buffer.Skip(0).First()); - Assert.AreEqual(2, buffer.Skip(1).First()); - } + buffer.Remove(o2); - [TestMethod] - public void Add_MoreThanCapacityTwice_ProperlyCircles() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - buffer.Add(5); - buffer.Add(6); - buffer.Add(7); - buffer.Add(8); - - Assert.AreEqual(6, buffer.Skip(0).First()); - Assert.AreEqual(7, buffer.Skip(1).First()); - Assert.AreEqual(8, buffer.Skip(2).First()); - } + Assert.AreEqual(2, buffer.Count); + Assert.IsTrue(buffer.Contains(o1)); + Assert.IsFalse(buffer.Contains(o2)); + Assert.IsTrue(buffer.Contains(o3)); + } - [TestMethod] - public void Add_ToArray_ProperlyWorks() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - buffer.Add(5); - buffer.Add(6); - buffer.Add(7); - buffer.Add(8); - - var result = buffer.ToArray(); - - Assert.AreEqual(3, result.Length); - Assert.AreEqual(6, result.Skip(0).First()); - Assert.AreEqual(7, result.Skip(1).First()); - Assert.AreEqual(8, result.Skip(2).First()); - } + [TestMethod] + public void Remove_ValueType_RemovesItem() + { + var buffer = new CircularBuffer(3); - [TestMethod] - public void Sum_ProperlySums() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - buffer.Add(5); - buffer.Add(6); - buffer.Add(7); - buffer.Add(8); - - var result = buffer.Sum(); - Assert.AreEqual(21 /*6+7+8*/, result); - } + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); - [TestMethod] - public void ContainsInt_ValueExists_ReturnsTrue() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - buffer.Add(5); - buffer.Add(6); - buffer.Add(7); - buffer.Add(8); - - Assert.IsTrue(buffer.Contains(6)); - Assert.IsTrue(buffer.Contains(7)); - Assert.IsTrue(buffer.Contains(8)); - } + buffer.Remove(2); - [TestMethod] - public void ContainsInt_ValueDoesNotExist_ReturnsFalse() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - buffer.Add(5); - buffer.Add(6); - buffer.Add(7); - buffer.Add(8); - - var result = buffer.Contains(3); - - Assert.IsFalse(result); + Assert.AreEqual(2, buffer.Count); + Assert.IsTrue(buffer.Contains(1)); + Assert.IsFalse(buffer.Contains(2)); + Assert.IsTrue(buffer.Contains(3)); + } + + [TestMethod] + public void Remove_NonExisting_RemovesItem() + { + var buffer = new CircularBuffer(3); + var o1 = new TestRefType(Guid.NewGuid()); + var o2 = new TestRefType(Guid.NewGuid()); + var o3 = new TestRefType(Guid.NewGuid()); + var o4 = new TestRefType(Guid.NewGuid()); + + buffer.Add(o1); + buffer.Add(o2); + buffer.Add(o3); + + buffer.Remove(o4); + + Assert.AreEqual(3, buffer.Count); + Assert.IsTrue(buffer.Contains(o1)); + Assert.IsTrue(buffer.Contains(o2)); + Assert.IsTrue(buffer.Contains(o3)); + Assert.IsFalse(buffer.Contains(o4)); + } } - [TestMethod] - public void ContainsDefaultInt_NotYetFull_ReturnsFalse() + [TestClass] + public class Add { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - - var result = buffer.Contains(default(int)); + [TestMethod] + public void Add_MoreThanCapacity_ProperlyCircles() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + + Assert.AreEqual(2, buffer.Skip(0).First()); + Assert.AreEqual(3, buffer.Skip(1).First()); + Assert.AreEqual(4, buffer.Skip(2).First()); + } - Assert.IsFalse(result); - } + [TestMethod] + public void Add_WithinCapacity_ProperlyAdds() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); - [TestMethod] - public void ContainsDefaultRefType_NotYetFull_ReturnsFalse() - { - var buffer = new CircularBuffer(3); - buffer.Add(new TestRefType(Guid.NewGuid())); - buffer.Add(new TestRefType(Guid.NewGuid())); + Assert.AreEqual(1, buffer.Skip(0).First()); + Assert.AreEqual(2, buffer.Skip(1).First()); + } - var result = buffer.Contains(default(TestRefType)); + [TestMethod] + public void Add_MoreThanCapacityTwice_ProperlyCircles() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + buffer.Add(5); + buffer.Add(6); + buffer.Add(7); + buffer.Add(8); + + Assert.AreEqual(6, buffer.Skip(0).First()); + Assert.AreEqual(7, buffer.Skip(1).First()); + Assert.AreEqual(8, buffer.Skip(2).First()); + } - Assert.IsFalse(result); + [TestMethod] + public void Add_ToArray_ProperlyWorks() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + buffer.Add(5); + buffer.Add(6); + buffer.Add(7); + buffer.Add(8); + + var result = buffer.ToArray(); + + Assert.AreEqual(3, result.Length); + Assert.AreEqual(6, result.Skip(0).First()); + Assert.AreEqual(7, result.Skip(1).First()); + Assert.AreEqual(8, result.Skip(2).First()); + } } - [TestMethod] - public void ContainsRefType_ValueDoesNotExist_ReturnsFalse() + [TestClass] + public class Enumeration { - var buffer = new CircularBuffer(3); - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); - var id4 = Guid.NewGuid(); + [TestMethod] + public void Sum_ProperlySums() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + buffer.Add(5); + buffer.Add(6); + buffer.Add(7); + buffer.Add(8); + + var result = buffer.Sum(); + Assert.AreEqual(21 /*6+7+8*/, result); + } - buffer.Add(new TestRefType(id1)); - buffer.Add(new TestRefType(id2)); - buffer.Add(new TestRefType(id3)); - buffer.Add(new TestRefType(id4)); - + [TestMethod] + public void Enumerate_ForEach_ProperlyEnumerates() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); - var result = buffer.Contains(new TestRefType(id1)); + var i = 2; + foreach (var item in buffer) + { + Assert.AreEqual(i, item); + i++; + } + } - Assert.IsFalse(result); - } + [TestMethod] + public void Enumerate_Modify_ThrowsException() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); - [TestMethod] - public void ContainsRefType_ValueExists_ReturnsTrue() - { - var buffer = new CircularBuffer(3); - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); - var id4 = Guid.NewGuid(); + bool thrown = false; - buffer.Add(new TestRefType(id1)); - buffer.Add(new TestRefType(id2)); - buffer.Add(new TestRefType(id3)); - buffer.Add(new TestRefType(id4)); + try + { + foreach (var item in buffer) + { + buffer.Add(5); + } + } + catch (InvalidOperationException) + { + if (thrown) Assert.Fail("Thrown was set more than once"); + thrown = true; + } + Assert.IsTrue(thrown); + } - Assert.IsTrue(buffer.Contains(new TestRefType(id2))); - Assert.IsTrue(buffer.Contains(new TestRefType(id3))); - Assert.IsTrue(buffer.Contains(new TestRefType(id4))); + [TestMethod] + public void Enumerate_Reset_ProperlyResets() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + + var en = buffer.GetEnumerator(); + en.MoveNext(); + Assert.AreEqual(1, en.Current); + en.MoveNext(); + Assert.AreEqual(2, en.Current); + en.Reset(); + en.MoveNext(); + Assert.AreEqual(1, en.Current); + en.MoveNext(); + Assert.AreEqual(2, en.Current); + } } - [TestMethod] - public void ContainsRefType_NullExists_ReturnsTrue() + [TestClass] + public class Contains { - var buffer = new CircularBuffer(3); - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); + [TestMethod] + public void ContainsInt_ValueExists_ReturnsTrue() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + buffer.Add(5); + buffer.Add(6); + buffer.Add(7); + buffer.Add(8); + + Assert.IsTrue(buffer.Contains(6)); + Assert.IsTrue(buffer.Contains(7)); + Assert.IsTrue(buffer.Contains(8)); + } - buffer.Add(new TestRefType(id1)); - buffer.Add(new TestRefType(id2)); - buffer.Add(null); - buffer.Add(new TestRefType(id3)); + [TestMethod] + public void ContainsInt_ValueDoesNotExist_ReturnsFalse() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); + buffer.Add(5); + buffer.Add(6); + buffer.Add(7); + buffer.Add(8); + + var result = buffer.Contains(3); + + Assert.IsFalse(result); + } + [TestMethod] + public void ContainsDefaultInt_NotYetFull_ReturnsFalse() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); - Assert.IsTrue(buffer.Contains(null)); - } + var result = buffer.Contains(default(int)); - [TestMethod] - public void ContainsRefType_NullDoesNotExist_ReturnsTrue() - { - var buffer = new CircularBuffer(3); - var id1 = Guid.NewGuid(); - var id2 = Guid.NewGuid(); - var id3 = Guid.NewGuid(); + Assert.IsFalse(result); + } - buffer.Add(new TestRefType(id1)); - buffer.Add(new TestRefType(id2)); - buffer.Add(new TestRefType(id3)); + [TestMethod] + public void ContainsDefaultRefType_NotYetFull_ReturnsFalse() + { + var buffer = new CircularBuffer(3); + buffer.Add(new TestRefType(Guid.NewGuid())); + buffer.Add(new TestRefType(Guid.NewGuid())); + var result = buffer.Contains(default(TestRefType)); - Assert.IsFalse(buffer.Contains(null)); - } + Assert.IsFalse(result); + } - [TestMethod] - public void Enumerate_ForEach_ProperlyEnumerates() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); - - var i = 2; - foreach (var item in buffer) + [TestMethod] + public void ContainsRefType_ValueDoesNotExist_ReturnsFalse() { - Assert.AreEqual(i, item); - i++; - } - } + var buffer = new CircularBuffer(3); + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); + var id4 = Guid.NewGuid(); - [TestMethod] - public void Enumerate_Modify_ThrowsException() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); + buffer.Add(new TestRefType(id1)); + buffer.Add(new TestRefType(id2)); + buffer.Add(new TestRefType(id3)); + buffer.Add(new TestRefType(id4)); + + + var result = buffer.Contains(new TestRefType(id1)); - bool thrown = false; + Assert.IsFalse(result); + } - try + [TestMethod] + public void ContainsRefType_ValueExists_ReturnsTrue() { - foreach (var item in buffer) - { - buffer.Add(5); - } + var buffer = new CircularBuffer(3); + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); + var id4 = Guid.NewGuid(); + + buffer.Add(new TestRefType(id1)); + buffer.Add(new TestRefType(id2)); + buffer.Add(new TestRefType(id3)); + buffer.Add(new TestRefType(id4)); + + + Assert.IsTrue(buffer.Contains(new TestRefType(id2))); + Assert.IsTrue(buffer.Contains(new TestRefType(id3))); + Assert.IsTrue(buffer.Contains(new TestRefType(id4))); } - catch (InvalidOperationException) + + [TestMethod] + public void ContainsRefType_NullExists_ReturnsTrue() { - if (thrown) Assert.Fail("Thrown was set more than once"); - thrown = true; + var buffer = new CircularBuffer(3); + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); + + buffer.Add(new TestRefType(id1)); + buffer.Add(new TestRefType(id2)); + buffer.Add(null); + buffer.Add(new TestRefType(id3)); + + + Assert.IsTrue(buffer.Contains(null)); } - Assert.IsTrue(thrown); - } + [TestMethod] + public void ContainsRefType_NullDoesNotExist_ReturnsTrue() + { + var buffer = new CircularBuffer(3); + var id1 = Guid.NewGuid(); + var id2 = Guid.NewGuid(); + var id3 = Guid.NewGuid(); - [TestMethod] - public void Enumerate_Reset_ProperlyResets() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - - var en = buffer.GetEnumerator(); - en.MoveNext(); - Assert.AreEqual(1, en.Current); - en.MoveNext(); - Assert.AreEqual(2, en.Current); - en.Reset(); - en.MoveNext(); - Assert.AreEqual(1, en.Current); - en.MoveNext(); - Assert.AreEqual(2, en.Current); - } + buffer.Add(new TestRefType(id1)); + buffer.Add(new TestRefType(id2)); + buffer.Add(new TestRefType(id3)); - [TestMethod] - public void CopyTo_WithinCapacity_OnlyCopiesAddedItems() + + Assert.IsFalse(buffer.Contains(null)); + } + } + + [TestClass] + public class CopyTo { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); + [TestMethod] + public void CopyTo_WithinCapacity_OnlyCopiesAddedItems() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); - var result = new int[2]; + var result = new int[2]; - buffer.CopyTo(result, 0); + buffer.CopyTo(result, 0); - Assert.AreEqual(1, result[0]); - Assert.AreEqual(2, result[1]); - } + Assert.AreEqual(1, result[0]); + Assert.AreEqual(2, result[1]); + } - [TestMethod] - public void CopyTo_OverCapacity_RetainsOrder() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); + [TestMethod] + public void CopyTo_OverCapacity_RetainsOrder() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); - var result = new int[3]; + var result = new int[3]; - buffer.CopyTo(result, 0); + buffer.CopyTo(result, 0); - Assert.AreEqual(2, result[0]); - Assert.AreEqual(3, result[1]); - Assert.AreEqual(4, result[2]); - } + Assert.AreEqual(2, result[0]); + Assert.AreEqual(3, result[1]); + Assert.AreEqual(4, result[2]); + } - [TestMethod] - public void CopyToWithIndex_OverCapacity_RetainsOrder() - { - var buffer = new CircularBuffer(3); - buffer.Add(1); - buffer.Add(2); - buffer.Add(3); - buffer.Add(4); + [TestMethod] + public void CopyToWithIndex_OverCapacity_RetainsOrder() + { + var buffer = new CircularBuffer(3); + buffer.Add(1); + buffer.Add(2); + buffer.Add(3); + buffer.Add(4); - var result = new int[5]; + var result = new int[5]; - buffer.CopyTo(result, 2); + buffer.CopyTo(result, 2); - Assert.AreEqual(0, result[0]); - Assert.AreEqual(0, result[1]); - Assert.AreEqual(2, result[2]); - Assert.AreEqual(3, result[3]); - Assert.AreEqual(4, result[4]); + Assert.AreEqual(0, result[0]); + Assert.AreEqual(0, result[1]); + Assert.AreEqual(2, result[2]); + Assert.AreEqual(3, result[3]); + Assert.AreEqual(4, result[4]); + } } - [TestMethod] - [Ignore] // This test runs for ages, only run if needed - public void Add_HugeNumber_WorksCorrectly() + + [TestClass] + public class Misc { - var buffer = new CircularBuffer(100000); - var random = new Random(); + [TestMethod] + [Ignore] // This test runs for ages, only run if needed + public void Add_HugeNumber_WorksCorrectly() + { + var buffer = new CircularBuffer(100000); + var random = new Random(); - long lastValue = 0; + long lastValue = 0; - for (long i = 0; i < int.MaxValue + 1000L; i++) - { - lastValue++; - buffer.Add(lastValue); - } + for (long i = 0; i < int.MaxValue + 1000L; i++) + { + lastValue++; + buffer.Add(lastValue); + } - long testValue = lastValue - buffer.Count + 1; - foreach (var item in buffer) - { - Assert.AreEqual(testValue, item); - testValue++; + long testValue = lastValue - buffer.Count + 1; + foreach (var item in buffer) + { + Assert.AreEqual(testValue, item); + testValue++; + } } } diff --git a/mstum.utils/CircularBuffer.cs b/mstum.utils/CircularBuffer.cs index 1d8b651..a0fd13d 100644 --- a/mstum.utils/CircularBuffer.cs +++ b/mstum.utils/CircularBuffer.cs @@ -2,6 +2,9 @@ /* The MIT License (MIT) * Copyright (c) 2011 Michael Stum, http://www.Stum.de * + * Contributions by + * Aaron Navarro - https://github.com/anavarro9731 + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights @@ -54,7 +57,7 @@ public class CircularBuffer : ICollection /// /// The backing store /// - private readonly T[] _store; + private T[] _store; /// /// The size of _store @@ -198,13 +201,40 @@ public bool IsReadOnly } /// - /// This operation is not implemented in the buffer. + /// Removes an item from the collection. If the item removed occurs at or before the current position the current position is moved back by one. /// /// /// public bool Remove(T item) { - throw new InvalidOperationException("Removing of Elements is not possible."); + if (Contains(item)) + { + T[] _newStore = new T[_capacity]; + + int? skippedAt = null; + + EqualityComparer comparer = EqualityComparer.Default; + for (int i = 0; i < _size; i++) + { + _newStore[(skippedAt != null ? i - 1 : i)] = _store[i]; + + if (comparer.Equals(_store[i], item)) + { + skippedAt = i; + continue; + } + } + _size--; + if (_start >= skippedAt && _start > 0) + { + _start--; + } + _store = _newStore; + _version++; + + return true; + } + return false; } ///