#region CPL License /* Nuclex Framework Copyright (C) 2002-2017 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion 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