#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.Runtime.InteropServices; namespace Nuclex.Support { /// View into a section of a string without copying said string /// /// /// The design of this class pretty much mirrors that of the /// class found in the .NET framework, but is /// specialized to be used for strings, which can not be expressed as arrays but /// share a lot of the characteristics of an array. /// /// /// In certain situations, passing a StringSegment instead of the actual copied /// substring is useful. For example, the caller might want to know from which /// index of the original string the substring was taken. Used internally in parsers, /// it can also prevent needless string copying and garbage generation. /// /// #if !NO_SERIALIZATION [Serializable, StructLayout(LayoutKind.Sequential)] #endif public struct StringSegment { /// /// Initializes a new instance of the class that delimits /// all the elements in the specified string /// /// String that will be wrapped /// String is null public StringSegment(string text) { if(text == null) { // questionable, but matches behavior of ArraySegment class throw new ArgumentNullException("text", "Text must not be null"); } this.text = text; this.offset = 0; this.count = text.Length; } /// /// Initializes a new instance of the class that delimits /// the specified range of the elements in the specified string /// /// The string 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 StringSegment(string text, int offset, int count) { if(text == null) { // questionable, but matches behavior of ArraySegment class throw new ArgumentNullException("text"); } 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 > (text.Length - offset)) { throw new ArgumentException( "Invalid argument, specified offset and count exceed string length" ); } this.text = text; this.offset = offset; this.count = count; } /// /// Gets the original string containing the range of elements that the string /// segment delimits /// /// /// The original string that was passed to the constructor, and that contains the range /// delimited by the /// public string Text { get { return this.text; } } /// /// Gets the position of the first element in the range delimited by the string segment, /// relative to the start of the original string /// /// /// The position of the first element in the range delimited by the /// , relative to the start of the original string /// public int Offset { get { return this.offset; } } /// /// Gets the number of elements in the range delimited by the string 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() { return this.text.GetHashCode() ^ this.offset ^ this.count; } /// /// 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 StringSegment) && this.Equals((StringSegment)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(StringSegment other) { return (other.text == this.text) && (other.offset == this.offset) && (other.count == this.count); } /// /// 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 ==(StringSegment left, StringSegment 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 !=(StringSegment left, StringSegment right) { return !(left == right); } /// Returns a string representation of the string segment /// The string representation of the string segment public override string ToString() { return this.text.Substring(this.offset, this.count); } /// String wrapped by the string segment private string text; /// Offset in the original string the segment begins at private int offset; /// Number of characters in the segment private int count; } } // namespace Nuclex.Support