diff --git a/Nuclex.Support (x86).csproj b/Nuclex.Support (x86).csproj index 4f22580..0ba44f2 100644 --- a/Nuclex.Support (x86).csproj +++ b/Nuclex.Support (x86).csproj @@ -178,7 +178,10 @@ - + + StringHelper.cs + + diff --git a/Source/StringHelper.Test.cs b/Source/StringHelper.Test.cs index 5e18edc..ce9a8c4 100644 --- a/Source/StringHelper.Test.cs +++ b/Source/StringHelper.Test.cs @@ -30,8 +30,61 @@ namespace Nuclex.Support { /// Unit Test for the string helper class [TestFixture] - public class PathHelperTest { + public class StringHelperTest { + /// + /// Verifies that the IndexNotOfAny() method works identical to the framework's + /// implementation of the IndexOfAny() method, only inverted. + /// + [Test] + public void TestIndexNotOfAny() { + string positive = "xxxxxOOOOO"; + string negative = "OOOOOxxxxx"; + + Assert.AreEqual( + positive.IndexOfAny(new char[] { 'O' }), + StringHelper.IndexNotOfAny(negative, new char[] { 'O' }) + ); + } + + /// + /// Verifies that the LastIndexNotOfAny() method works identical to the framework's + /// implementation of the LastIndexOfAny() method, only inverted. + /// + [Test] + public void TestLastIndexNotOfAny() { + string positive = "xxxxxOOOOO"; + string negative = "OOOOOxxxxx"; + + Assert.AreEqual( + positive.LastIndexOfAny(new char[] { 'x' }), + StringHelper.LastIndexNotOfAny(negative, new char[] { 'x' }) + ); + } + + /// + /// Verifies that the IndexNotOfAny() method works with multiple characters + /// + [Test] + public void TestMultipleCharIndexNotOfAny() { + string haystack = "0123456789"; + + Assert.AreEqual( + 5, StringHelper.IndexNotOfAny(haystack, new char[] { '4', '3', '2', '1', '0' }) + ); + } + + /// + /// Verifies that the IndexNotOfAny() method works with multiple characters + /// + [Test] + public void TestMultipleCharLastIndexNotOfAny() { + string haystack = "0123456789"; + + Assert.AreEqual( + 4, StringHelper.LastIndexNotOfAny(haystack, new char[] { '9', '8', '7', '6', '5' }) + ); + } } diff --git a/Source/StringHelper.cs b/Source/StringHelper.cs index 75d60d7..ca39411 100644 --- a/Source/StringHelper.cs +++ b/Source/StringHelper.cs @@ -83,10 +83,9 @@ namespace Nuclex.Support { while(startIndex < count) { char character = haystack[startIndex]; - for(int anyIndex = 0; anyIndex < anyLength; ++anyIndex) { - if(character != anyNotOf[anyIndex]) { - return startIndex; - } + int index = Array.IndexOf(anyNotOf, character, 0, anyLength); + if(index == -1) { + return startIndex; } ++startIndex; @@ -152,10 +151,9 @@ namespace Nuclex.Support { while(startIndex > count) { char character = haystack[startIndex]; - for(int anyIndex = 0; anyIndex < anyLength; ++anyIndex) { - if(character != anyNotOf[anyIndex]) { - return startIndex; - } + int index = Array.IndexOf(anyNotOf, character, 0, anyLength); + if(index == -1) { + return startIndex; } --startIndex; diff --git a/Source/StringSegment.cs b/Source/StringSegment.cs new file mode 100644 index 0000000..bfb57ae --- /dev/null +++ b/Source/StringSegment.cs @@ -0,0 +1,197 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2008 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 { + + /// Delimits a section of a string + [Serializable, StructLayout(LayoutKind.Sequential)] + internal 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) { + throw new ArgumentNullException("text"); + } + + 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) { + throw new ArgumentNullException("array"); + } + 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((text.Length - offset) < count) { + 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 array 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 array segment, + /// relative to the start of the original array + /// + /// The position of the first element in the range delimited by the + /// , relative to the start of the original array + /// + public int Offset { + get { return this.offset; } + } + + /// + /// Gets the number of elements in the range delimited by the array 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); + } + + /// 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