#region Apache License 2.0 /* Nuclex .NET Framework Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #endregion // Apache License 2.0 using System; using System.Collections.Generic; using System.Runtime.InteropServices; namespace Nuclex.Support.Collections { /// View into a section of an IList<T> without copying said string /// /// Type of elements that are stored in the list the segment references /// /// /// /// The design of this class pretty much mirrors that of the /// class found in the .NET framework, but is /// specialized to be used for IList<T>, which can not be cast to arrays /// directly (and loses type safety). /// /// /// In certain situations, passing a ListSegment instead of storing the selected /// elements in a new list is useful. For example, the caller might want to know /// from which index of the original list the section was taken. When the original /// list needs to be modified, for example in a sorting algorithm, the list segment /// can be used to specify a region for the algorithm to work on while still accessing /// the original list. /// /// #if !NO_SERIALIZATION [Serializable, StructLayout(LayoutKind.Sequential)] #endif public struct ListSegment { /// /// Initializes a new instance of the class /// that delimits all the elements in the specified string /// /// List that will be wrapped /// String is null public ListSegment(IList list) { if(list == null) { // questionable, but matches behavior of ArraySegment class throw new ArgumentNullException("text", "Text must not be null"); } this.list = list; this.offset = 0; this.count = list.Count; } /// /// Initializes a new instance of the class /// that delimits the specified range of the elements in the specified string /// /// The list containing the range of elements to delimit /// The zero-based index of the first element in the range /// The number of elements in the range /// /// Offset or count is less than 0 /// /// /// Offset and count do not specify a valid range in array /// /// String is null public ListSegment(IList list, int offset, int count) { if(list == null) { // questionable, but matches behavior of ArraySegment class throw new ArgumentNullException("list"); } if(offset < 0) { throw new ArgumentOutOfRangeException( "offset", "Argument out of range, non-negative number required" ); } if(count < 0) { throw new ArgumentOutOfRangeException( "count", "Argument out of range, non-negative number required" ); } if(count > (list.Count - offset)) { throw new ArgumentException( "Invalid argument, specified offset and count exceed list size" ); } this.list = list; this.offset = offset; this.count = count; } /// /// Gets the original list containing the range of elements that the list /// segment delimits /// /// /// The original list that was passed to the constructor, and that contains the range /// delimited by the /// public IList List { get { return this.list; } } /// /// Gets the position of the first element in the range delimited by the list segment, /// relative to the start of the original list /// /// /// The position of the first element in the range delimited by the /// , relative to the start of the original list /// public int Offset { get { return this.offset; } } /// /// Gets the number of elements in the range delimited by the list segment /// /// /// The number of elements in the range delimited by /// the /// public int Count { get { return this.count; } } /// Returns the hash code for the current instance /// A 32-bit signed integer hash code public override int GetHashCode() { int hashCode = this.offset ^ this.count; for(int index = 0; index < this.count; ++index) { hashCode ^= this.list[index + this.offset].GetHashCode(); } return hashCode; } /// /// Determines whether the specified object is equal to the current instance /// /// /// True if the specified object is a structure /// and is equal to the current instance; otherwise, false /// /// The object to be compared with the current instance public override bool Equals(object other) { return (other is ListSegment) && this.Equals((ListSegment)other); } /// /// Determines whether the specified /// structure is equal to the current instance /// /// /// True if the specified structure is equal /// to the current instance; otherwise, false /// /// /// The structure to be compared with /// the current instance /// public bool Equals(ListSegment other) { if(other.count != this.count) { return false; } if(ReferenceEquals(other.list, this.list)) { return (other.offset == this.offset); } else { var comparer = Comparer.Default; for(int index = 0; index < this.count; ++index) { int difference = comparer.Compare( other.list[index + other.offset], this.list[index + this.offset] ); if(difference != 0) { return false; } } } return true; } /// /// Indicates whether two structures are equal /// /// True if a is equal to b; otherwise, false /// /// The structure on the left side of /// the equality operator /// /// /// The structure on the right side of /// the equality operator /// public static bool operator ==(ListSegment left, ListSegment right) { return left.Equals(right); } /// /// Indicates whether two structures are unequal /// /// True if a is not equal to b; otherwise, false /// /// The structure on the left side of /// the inequality operator /// /// /// The structure on the right side of /// the inequality operator /// public static bool operator !=(ListSegment left, ListSegment right) { return !(left == right); } /// Returns a string representation of the list segment /// The string representation of the list segment public override string ToString() { var builder = new System.Text.StringBuilder(); builder.Append("ListSegment {"); for(int index = 0; index < Math.Min(this.count, 10); ++index) { if(index == 0) { builder.Append(" "); } else { builder.Append(", "); } builder.Append(this.list[index + this.offset].ToString()); } if(this.count >= 11) { builder.Append(", ... }"); } else { builder.Append(" }"); } return builder.ToString(); } /// Returns a new list containing only the elements in the list segment /// A new list containing only the elements in the list segment public List ToList() { if(this.count == 0) { return new List(capacity: 0); } else { var newList = new List(capacity: this.count); { int endIndex = this.offset + this.count; for(int index = this.offset; index < endIndex; ++index) { newList.Add(this.list[index]); } } return newList; } } /// List wrapped by the list segment private IList list; /// Offset in the original list the segment begins at private int offset; /// Number of elements in the segment private int count; } } // namespace Nuclex.Support.Collections