#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