diff --git a/csharp/Platform.Collections.Tests/SegmentTests.cs b/csharp/Platform.Collections.Tests/SegmentTests.cs new file mode 100644 index 00000000..c6f76c06 --- /dev/null +++ b/csharp/Platform.Collections.Tests/SegmentTests.cs @@ -0,0 +1,183 @@ +using System; +using System.Linq; +using Xunit; +using Platform.Collections.Segments; + +namespace Platform.Collections.Tests +{ + public static class SegmentTests + { + [Fact] + public static void EmptySegmentTest() + { + var empty = Segment.Empty; + Assert.Equal(0, empty.Length); + Assert.Equal(0, empty.Count); + Assert.Equal(0, empty.Offset); + Assert.NotNull(empty.Base); + } + + [Fact] + public static void ArrayConstructorTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array); + + Assert.Equal(array, segment.Array); + Assert.Equal(array, segment.Base); + Assert.Equal(0, segment.Offset); + Assert.Equal(5, segment.Length); + Assert.Equal(5, segment.Count); + } + + [Fact] + public static void ArrayWithOffsetAndCountConstructorTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 3); + + Assert.Equal(array, segment.Array); + Assert.Equal(array, segment.Base); + Assert.Equal(1, segment.Offset); + Assert.Equal(3, segment.Length); + Assert.Equal(3, segment.Count); + + // Test indexing + Assert.Equal(2, segment[0]); + Assert.Equal(3, segment[1]); + Assert.Equal(4, segment[2]); + } + + [Fact] + public static void ArrayConstructorValidationTest() + { + Assert.Throws(() => new Segment((int[])null)); + Assert.Throws(() => new Segment(null, 0, 0)); + + var array = new int[] { 1, 2, 3 }; + Assert.Throws(() => new Segment(array, -1, 1)); + Assert.Throws(() => new Segment(array, 0, -1)); + Assert.Throws(() => new Segment(array, 2, 3)); // offset + count > array.Length + } + + [Fact] + public static void SliceTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 4); // [2, 3, 4, 5] + + // Slice from index 1 to end + var slice1 = segment.Slice(1); + Assert.Equal(2, slice1.Offset); // 1 + 1 + Assert.Equal(3, slice1.Length); // 4 - 1 + Assert.Equal(3, slice1[0]); // array[2] + Assert.Equal(4, slice1[1]); // array[3] + Assert.Equal(5, slice1[2]); // array[4] + + // Slice with specific count + var slice2 = segment.Slice(1, 2); + Assert.Equal(2, slice2.Offset); // 1 + 1 + Assert.Equal(2, slice2.Length); // specified count + Assert.Equal(3, slice2[0]); // array[2] + Assert.Equal(4, slice2[1]); // array[3] + } + + [Fact] + public static void SliceValidationTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 3); + + Assert.Throws(() => segment.Slice(-1)); + Assert.Throws(() => segment.Slice(4)); // index > Length + Assert.Throws(() => segment.Slice(0, -1)); + Assert.Throws(() => segment.Slice(2, 2)); // index + count > Length + } + + [Fact] + public static void ToArrayTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 3); // [2, 3, 4] + + var result = segment.ToArray(); + + Assert.Equal(3, result.Length); + Assert.Equal(2, result[0]); + Assert.Equal(3, result[1]); + Assert.Equal(4, result[2]); + + // Ensure it's a copy, not the same array + Assert.NotSame(array, result); + + // Modify original array and ensure copy is unchanged + array[2] = 99; + Assert.Equal(3, result[1]); // Should still be 3 + } + + [Fact] + public static void ArrayPropertyTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array); + + Assert.Same(array, segment.Array); + + // Test with IList (not array) + var list = new System.Collections.Generic.List { 1, 2, 3 }; + var listSegment = new Segment(list, 0, 2); + Assert.Null(listSegment.Array); // Should return null for non-array IList + } + + [Fact] + public static void EnumerationTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 3); // [2, 3, 4] + + var result = segment.ToArray(); + var expected = new int[] { 2, 3, 4 }; + + Assert.True(result.SequenceEqual(expected)); + } + + [Fact] + public static void CountPropertyCompatibilityTest() + { + var array = new int[] { 1, 2, 3, 4, 5 }; + var segment = new Segment(array, 1, 3); + + // Count should be the same as Length (ArraySegment compatibility) + Assert.Equal(segment.Length, segment.Count); + Assert.Equal(3, segment.Count); + } + + [Fact] + public static void ArraySegmentSemanticCompatibilityTest() + { + var array = new int[] { 10, 20, 30, 40, 50 }; + + // Test behavior similar to ArraySegment + var segment1 = new Segment(array); // Entire array + var segment2 = new Segment(array, 2, 2); // [30, 40] + + // ArraySegment-like properties + Assert.Same(array, segment1.Array); + Assert.Same(array, segment2.Array); + Assert.Equal(0, segment1.Offset); + Assert.Equal(2, segment2.Offset); + Assert.Equal(5, segment1.Count); + Assert.Equal(2, segment2.Count); + + // ArraySegment-like methods + var slice = segment1.Slice(1, 3); // [20, 30, 40] + Assert.Equal(3, slice.Count); + Assert.Equal(20, slice[0]); + Assert.Equal(30, slice[1]); + Assert.Equal(40, slice[2]); + + var copyArray = slice.ToArray(); + Assert.Equal(new int[] { 20, 30, 40 }, copyArray); + } + } +} \ No newline at end of file diff --git a/csharp/Platform.Collections/Segments/Segment.cs b/csharp/Platform.Collections/Segments/Segment.cs index 7f62bb51..6dbc737d 100644 --- a/csharp/Platform.Collections/Segments/Segment.cs +++ b/csharp/Platform.Collections/Segments/Segment.cs @@ -16,6 +16,15 @@ namespace Platform.Collections.Segments /// The segment elements type.Тип элементов сегмента. public class Segment : IEquatable>, IList { + /// + /// Gets an empty segment. + /// Возвращает пустой сегмент. + /// + public static Segment Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new Segment(new T[0], 0, 0); + } /// /// Gets the original list (this segment is a part of it). /// Возвращает исходный список (частью которого является этот сегмент). @@ -44,6 +53,17 @@ public int Length get; } + /// + /// Gets the original array when the base is an array. Compatible with ArraySegment semantics. + /// Возвращает исходный массив, когда основой является массив. Совместим с семантикой ArraySegment. + /// + public T[] Array + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Base as T[]; + } + + /// /// Initializes a new instance of the class, using the list, of the segment and its . /// Инициализирует новый экземпляр класса , используя список , сегмента и его . @@ -58,6 +78,43 @@ public Segment(IList @base, int offset, int length) Offset = offset; Length = length; } + + /// + /// Initializes a new instance of the class that delimits the entire array. Compatible with ArraySegment constructor. + /// Инициализирует новый экземпляр класса , который разграничивает весь массив. Совместим с конструктором ArraySegment. + /// + /// The array to wrap in the segment.Массив для обертывания в сегмент. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Segment(T[] array) + { + Base = array ?? throw new ArgumentNullException(nameof(array)); + Offset = 0; + Length = array.Length; + } + + /// + /// Initializes a new instance of the class that delimits a range of elements in an array. Compatible with ArraySegment constructor. + /// Инициализирует новый экземпляр класса , который разграничивает диапазон элементов в массиве. Совместим с конструктором ArraySegment. + /// + /// The array to wrap in the segment.Массив для обертывания в сегмент. + /// The zero-based index of the first element in the range delimited by the array segment.Отсчитываемый от нуля индекс первого элемента в диапазоне, разделенном сегментом массива. + /// The number of elements in the range delimited by the array segment.Количество элементов в диапазоне, разделенном сегментом массива. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Segment(T[] array, int offset, int count) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (offset < 0) + throw new ArgumentOutOfRangeException(nameof(offset)); + if (count < 0) + throw new ArgumentOutOfRangeException(nameof(count)); + if (offset + count > array.Length) + throw new ArgumentException("Offset and count exceed array bounds."); + + Base = array; + Offset = offset; + Length = count; + } /// /// Gets the hash code of the current instance. @@ -96,6 +153,50 @@ public Segment(IList @base, int offset, int length) [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) => obj is Segment other ? Equals(other) : false; + /// + /// Forms a slice out of the current segment that begins at a specified index. Compatible with ArraySegment semantics. + /// Формирует срез из текущего сегмента, который начинается с указанного индекса. Совместим с семантикой ArraySegment. + /// + /// The index at which to begin the slice.Индекс, с которого начинается срез. + /// A segment that consists of all elements of the current segment from to the end.Сегмент, который состоит из всех элементов текущего сегмента от до конца. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Segment Slice(int index) + { + if (index < 0 || index > Length) + throw new ArgumentOutOfRangeException(nameof(index)); + return new Segment(Base, Offset + index, Length - index); + } + + /// + /// Forms a slice out of the current segment starting at a specified index for a specified length. Compatible with ArraySegment semantics. + /// Формирует срез из текущего сегмента, начиная с указанного индекса для указанной длины. Совместим с семантикой ArraySegment. + /// + /// The index at which to begin the slice.Индекс, с которого начинается срез. + /// The desired length for the slice.Желаемая длина среза. + /// A segment that consists of elements from the current segment starting at .Сегмент, который состоит из элементов текущего сегмента, начиная с . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Segment Slice(int index, int count) + { + if (index < 0 || index > Length) + throw new ArgumentOutOfRangeException(nameof(index)); + if (count < 0 || index + count > Length) + throw new ArgumentOutOfRangeException(nameof(count)); + return new Segment(Base, Offset + index, count); + } + + /// + /// Copies the contents of this segment into a new array. Compatible with ArraySegment semantics. + /// Копирует содержимое этого сегмента в новый массив. Совместим с семантикой ArraySegment. + /// + /// An array containing copies of the elements of the current segment.Массив, содержащий копии элементов текущего сегмента. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] ToArray() + { + var result = new T[Length]; + CopyTo(result, 0); + return result; + } + #region IList ///