< Summary

Class:Azure.Core.Http.Multipart.StringSegment
Assembly:Azure.Storage.Blobs.Batch
File(s):C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\Shared\StringSegment.cs
Covered lines:0
Uncovered lines:140
Coverable lines:140
Total lines:724
Line coverage:0% (0 of 140)
Covered branches:0
Total branches:112
Branch coverage:0% (0 of 112)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-0%100%
.ctor(...)-0%0%
.ctor(...)-0%0%
get_Buffer()-0%100%
get_Offset()-0%100%
get_Length()-0%100%
get_Value()-0%0%
get_HasValue()-0%100%
get_Item(...)-0%0%
AsSpan()-0%100%
AsMemory()-0%100%
Compare(...)-0%0%
Equals(...)-0%0%
Equals(...)-0%100%
Equals(...)-0%0%
Equals(...)-0%100%
Equals(...)-0%100%
Equals(...)-0%0%
GetHashCode()-0%0%
op_Equality(...)-0%100%
op_Inequality(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
op_Implicit(...)-0%100%
StartsWith(...)-0%0%
EndsWith(...)-0%0%
Substring(...)-0%100%
Substring(...)-0%0%
Subsegment(...)-0%100%
Subsegment(...)-0%0%
IndexOf(...)-0%0%
IndexOf(...)-0%100%
IndexOf(...)-0%100%
IndexOfAny(...)-0%0%
IndexOfAny(...)-0%100%
IndexOfAny(...)-0%100%
LastIndexOf(...)-0%0%
Trim()-0%100%
TrimStart()-0%0%
TrimEnd()-0%0%
Split(...)-0%100%
IsNullOrEmpty(...)-0%0%
ToString()-0%0%
ThrowInvalidArguments(...)-0%100%
ThrowInvalidArguments(...)-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\storage\Azure.Storage.Blobs.Batch\src\Shared\StringSegment.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4// Copied from https://github.com/aspnet/Extensions/tree/master/src/Primitives/src
 5using System;
 6using System.Runtime.CompilerServices;
 7
 8#pragma warning disable CA1307  // Equals Locale
 9#pragma warning disable IDE0041 // Null check can be simplified
 10
 11namespace Azure.Core.Http.Multipart
 12{
 13    /// <summary>
 14    /// An optimized representation of a substring.
 15    /// </summary>
 16    internal readonly struct StringSegment : IEquatable<StringSegment>, IEquatable<string>
 17    {
 18        /// <summary>
 19        /// A <see cref="StringSegment"/> for <see cref="string.Empty"/>.
 20        /// </summary>
 021        public static readonly StringSegment Empty = string.Empty;
 22
 23        /// <summary>
 24        /// Initializes an instance of the <see cref="StringSegment"/> struct.
 25        /// </summary>
 26        /// <param name="buffer">
 27        /// The original <see cref="string"/>. The <see cref="StringSegment"/> includes the whole <see cref="string"/>.
 28        /// </param>
 29        public StringSegment(string buffer)
 30        {
 031            Buffer = buffer;
 032            Offset = 0;
 033            Length = buffer?.Length ?? 0;
 034        }
 35
 36        /// <summary>
 37        /// Initializes an instance of the <see cref="StringSegment"/> struct.
 38        /// </summary>
 39        /// <param name="buffer">The original <see cref="string"/> used as buffer.</param>
 40        /// <param name="offset">The offset of the segment within the <paramref name="buffer"/>.</param>
 41        /// <param name="length">The length of the segment.</param>
 42        /// <exception cref="ArgumentNullException">
 43        /// <paramref name="buffer"/> is <code>null</code>.
 44        /// </exception>
 45        /// <exception cref="ArgumentOutOfRangeException">
 46        /// <paramref name="offset"/> or <paramref name="length"/> is less than zero, or <paramref name="offset"/> +
 47        /// <paramref name="length"/> is greater than the number of characters in <paramref name="buffer"/>.
 48        /// </exception>
 49        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 50        public StringSegment(string buffer, int offset, int length)
 51        {
 52            // Validate arguments, check is minimal instructions with reduced branching for inlinable fast-path
 53            // Negative values discovered though conversion to high values when converted to unsigned
 54            // Failure should be rare and location determination and message is delegated to failure functions
 055            if (buffer == null || (uint)offset > (uint)buffer.Length || (uint)length > (uint)(buffer.Length - offset))
 56            {
 057                ThrowInvalidArguments(buffer, offset, length);
 58            }
 59
 060            Buffer = buffer;
 061            Offset = offset;
 062            Length = length;
 063        }
 64
 65        /// <summary>
 66        /// Gets the <see cref="string"/> buffer for this <see cref="StringSegment"/>.
 67        /// </summary>
 068        public string Buffer { get; }
 69
 70        /// <summary>
 71        /// Gets the offset within the buffer for this <see cref="StringSegment"/>.
 72        /// </summary>
 073        public int Offset { get; }
 74
 75        /// <summary>
 76        /// Gets the length of this <see cref="StringSegment"/>.
 77        /// </summary>
 078        public int Length { get; }
 79
 80        /// <summary>
 81        /// Gets the value of this segment as a <see cref="string"/>.
 82        /// </summary>
 83        public string Value
 84        {
 85            get
 86            {
 087                if (HasValue)
 88                {
 089                    return Buffer.Substring(Offset, Length);
 90                }
 91                else
 92                {
 093                    return null;
 94                }
 95            }
 96        }
 97
 98        /// <summary>
 99        /// Gets whether this <see cref="StringSegment"/> contains a valid value.
 100        /// </summary>
 101        public bool HasValue
 102        {
 0103            get { return Buffer != null; }
 104        }
 105
 106        /// <summary>
 107        /// Gets the <see cref="char"/> at a specified position in the current <see cref="StringSegment"/>.
 108        /// </summary>
 109        /// <param name="index">The offset into the <see cref="StringSegment"/></param>
 110        /// <returns>The <see cref="char"/> at a specified position.</returns>
 111        /// <exception cref="ArgumentOutOfRangeException">
 112        /// <paramref name="index"/> is greater than or equal to <see cref="Length"/> or less than zero.
 113        /// </exception>
 114        public char this[int index]
 115        {
 116            get
 117            {
 0118                if ((uint)index >= (uint)Length)
 119                {
 0120                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
 121                }
 122
 0123                return Buffer[Offset + index];
 124            }
 125        }
 126
 127        /// <summary>
 128        /// Gets a <see cref="ReadOnlySpan{T}"/> from the current <see cref="StringSegment"/>.
 129        /// </summary>
 130        /// <returns>The <see cref="ReadOnlySpan{T}"/> from this <see cref="StringSegment"/>.</returns>
 0131        public ReadOnlySpan<char> AsSpan() => Buffer.AsSpan(Offset, Length);
 132
 133        /// <summary>
 134        /// Gets a <see cref="ReadOnlyMemory{T}"/> from the current <see cref="StringSegment"/>.
 135        /// </summary>
 136        /// <returns>The <see cref="ReadOnlyMemory{T}"/> from this <see cref="StringSegment"/>.</returns>
 0137        public ReadOnlyMemory<char> AsMemory() => Buffer.AsMemory(Offset, Length);
 138
 139        /// <summary>
 140        /// Compares substrings of two specified <see cref="StringSegment"/> objects using the specified rules,
 141        /// and returns an integer that indicates their relative position in the sort order.
 142        /// </summary>
 143        /// <param name="a">The first <see cref="StringSegment"/> to compare.</param>
 144        /// <param name="b">The second <see cref="StringSegment"/> to compare.</param>
 145        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the comparison.</par
 146        /// <returns>
 147        /// A 32-bit signed integer indicating the lexical relationship between the two comparands.
 148        /// The value is negative if <paramref name="a"/> is less than <paramref name="b"/>, 0 if the two comparands are
 149        /// and positive if <paramref name="a"/> is greater than <paramref name="b"/>.
 150        /// </returns>
 151        public static int Compare(StringSegment a, StringSegment b, StringComparison comparisonType)
 152        {
 0153            var minLength = Math.Min(a.Length, b.Length);
 0154            var diff = string.Compare(a.Buffer, a.Offset, b.Buffer, b.Offset, minLength, comparisonType);
 0155            if (diff == 0)
 156            {
 0157                diff = a.Length - b.Length;
 158            }
 159
 0160            return diff;
 161        }
 162
 163        /// <inheritdoc />
 164        public override bool Equals(object obj)
 165        {
 0166            if (ReferenceEquals(null, obj))
 167            {
 0168                return false;
 169            }
 170
 0171            return obj is StringSegment segment && Equals(segment);
 172        }
 173
 174        /// <summary>
 175        /// Indicates whether the current object is equal to another object of the same type.
 176        /// </summary>
 177        /// <param name="other">An object to compare with this object.</param>
 178        /// <returns><code>true</code> if the current object is equal to the other parameter; otherwise, <code>false</co
 0179        public bool Equals(StringSegment other) => Equals(other, StringComparison.Ordinal);
 180
 181        /// <summary>
 182        /// Indicates whether the current object is equal to another object of the same type.
 183        /// </summary>
 184        /// <param name="other">An object to compare with this object.</param>
 185        /// <param name="comparisonType">One of the enumeration values that specifies the rules to use in the comparison
 186        /// <returns><code>true</code> if the current object is equal to the other parameter; otherwise, <code>false</co
 187        public bool Equals(StringSegment other, StringComparison comparisonType)
 188        {
 0189            if (Length != other.Length)
 190            {
 0191                return false;
 192            }
 193
 0194            return string.Compare(Buffer, Offset, other.Buffer, other.Offset, other.Length, comparisonType) == 0;
 195        }
 196
 197        // This handles StringSegment.Equals(string, StringSegment, StringComparison) and StringSegment.Equals(StringSeg
 198        // via the implicit type converter
 199        /// <summary>
 200        /// Determines whether two specified <see cref="StringSegment"/> objects have the same value. A parameter specif
 201        /// sort rules used in the comparison.
 202        /// </summary>
 203        /// <param name="a">The first <see cref="StringSegment"/> to compare.</param>
 204        /// <param name="b">The second <see cref="StringSegment"/> to compare.</param>
 205        /// <param name="comparisonType">One of the enumeration values that specifies the rules for the comparison.</par
 206        /// <returns><code>true</code> if the objects are equal; otherwise, <code>false</code>.</returns>
 207        public static bool Equals(StringSegment a, StringSegment b, StringComparison comparisonType)
 208        {
 0209            return a.Equals(b, comparisonType);
 210        }
 211
 212        /// <summary>
 213        /// Checks if the specified <see cref="string"/> is equal to the current <see cref="StringSegment"/>.
 214        /// </summary>
 215        /// <param name="text">The <see cref="string"/> to compare with the current <see cref="StringSegment"/>.</param>
 216        /// <returns><code>true</code> if the specified <see cref="string"/> is equal to the current <see cref="StringSe
 217        public bool Equals(string text)
 218        {
 0219            return Equals(text, StringComparison.Ordinal);
 220        }
 221
 222        /// <summary>
 223        /// Checks if the specified <see cref="string"/> is equal to the current <see cref="StringSegment"/>.
 224        /// </summary>
 225        /// <param name="text">The <see cref="string"/> to compare with the current <see cref="StringSegment"/>.</param>
 226        /// <param name="comparisonType">One of the enumeration values that specifies the rules to use in the comparison
 227        /// <returns><code>true</code> if the specified <see cref="string"/> is equal to the current <see cref="StringSe
 228        /// <exception cref="ArgumentNullException">
 229        /// <paramref name="text"/> is <code>null</code>.
 230        /// </exception>
 231        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 232        public bool Equals(string text, StringComparison comparisonType)
 233        {
 0234            if (text == null)
 235            {
 0236                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
 237            }
 238
 0239            var textLength = text.Length;
 0240            if (!HasValue || Length != textLength)
 241            {
 0242                return false;
 243            }
 244
 0245            return string.Compare(Buffer, Offset, text, 0, textLength, comparisonType) == 0;
 246        }
 247
 248        /// <inheritdoc />
 249        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 250        public override int GetHashCode()
 251        {
 252#if NETCOREAPP
 253            return string.GetHashCode(AsSpan());
 254#elif NETSTANDARD
 255            // This GetHashCode is expensive since it allocates on every call.
 256            // However this is required to ensure we retain any behavior (such as hash code randomization) that
 257            // string.GetHashCode has.
 0258            return Value?.GetHashCode() ?? 0;
 259#else
 260#error Target frameworks need to be updated.
 261#endif
 262
 263        }
 264
 265        /// <summary>
 266        /// Checks if two specified <see cref="StringSegment"/> have the same value.
 267        /// </summary>
 268        /// <param name="left">The first <see cref="StringSegment"/> to compare, or <code>null</code>.</param>
 269        /// <param name="right">The second <see cref="StringSegment"/> to compare, or <code>null</code>.</param>
 270        /// <returns><code>true</code> if the value of <paramref name="left"/> is the same as the value of <paramref nam
 0271        public static bool operator ==(StringSegment left, StringSegment right) => left.Equals(right);
 272
 273        /// <summary>
 274        /// Checks if two specified <see cref="StringSegment"/> have different values.
 275        /// </summary>
 276        /// <param name="left">The first <see cref="StringSegment"/> to compare, or <code>null</code>.</param>
 277        /// <param name="right">The second <see cref="StringSegment"/> to compare, or <code>null</code>.</param>
 278        /// <returns><code>true</code> if the value of <paramref name="left"/> is different from the value of <paramref 
 0279        public static bool operator !=(StringSegment left, StringSegment right) => !left.Equals(right);
 280
 281        // PERF: Do NOT add a implicit converter from StringSegment to String. That would negate most of the perf safety
 282        /// <summary>
 283        /// Creates a new <see cref="StringSegment"/> from the given <see cref="string"/>.
 284        /// </summary>
 285        /// <param name="value">The <see cref="string"/> to convert to a <see cref="StringSegment"/></param>
 0286        public static implicit operator StringSegment(string value) => new StringSegment(value);
 287
 288        /// <summary>
 289        /// Creates a see <see cref="ReadOnlySpan{T}"/> from the given <see cref="StringSegment"/>.
 290        /// </summary>
 291        /// <param name="segment">The <see cref="StringSegment"/> to convert to a <see cref="ReadOnlySpan{T}"/>.</param>
 0292        public static implicit operator ReadOnlySpan<char>(StringSegment segment) => segment.AsSpan();
 293
 294        /// <summary>
 295        /// Creates a see <see cref="ReadOnlyMemory{T}"/> from the given <see cref="StringSegment"/>.
 296        /// </summary>
 297        /// <param name="segment">The <see cref="StringSegment"/> to convert to a <see cref="ReadOnlyMemory{T}"/>.</para
 0298        public static implicit operator ReadOnlyMemory<char>(StringSegment segment) => segment.AsMemory();
 299
 300        /// <summary>
 301        /// Checks if the beginning of this <see cref="StringSegment"/> matches the specified <see cref="string"/> when 
 302        /// </summary>
 303        /// <param name="text">The <see cref="string"/>to compare.</param>
 304        /// <param name="comparisonType">One of the enumeration values that specifies the rules to use in the comparison
 305        /// <returns><code>true</code> if <paramref name="text"/> matches the beginning of this <see cref="StringSegment
 306        /// <exception cref="ArgumentNullException">
 307        /// <paramref name="text"/> is <code>null</code>.
 308        /// </exception>
 309        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 310        public bool StartsWith(string text, StringComparison comparisonType)
 311        {
 0312            if (text == null)
 313            {
 0314                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
 315            }
 316
 0317            var result = false;
 0318            var textLength = text.Length;
 319
 0320            if (HasValue && Length >= textLength)
 321            {
 0322                result = string.Compare(Buffer, Offset, text, 0, textLength, comparisonType) == 0;
 323            }
 324
 0325            return result;
 326        }
 327
 328        /// <summary>
 329        /// Checks if the end of this <see cref="StringSegment"/> matches the specified <see cref="string"/> when compar
 330        /// </summary>
 331        /// <param name="text">The <see cref="string"/>to compare.</param>
 332        /// <param name="comparisonType">One of the enumeration values that specifies the rules to use in the comparison
 333        /// <returns><code>true</code> if <paramref name="text"/> matches the end of this <see cref="StringSegment"/>; o
 334        /// <exception cref="ArgumentNullException">
 335        /// <paramref name="text"/> is <code>null</code>.
 336        /// </exception>
 337        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 338        public bool EndsWith(string text, StringComparison comparisonType)
 339        {
 0340            if (text == null)
 341            {
 0342                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
 343            }
 344
 0345            var result = false;
 0346            var textLength = text.Length;
 0347            var comparisonLength = Offset + Length - textLength;
 348
 0349            if (HasValue && comparisonLength > 0)
 350            {
 0351                result = string.Compare(Buffer, comparisonLength, text, 0, textLength, comparisonType) == 0;
 352            }
 353
 0354            return result;
 355        }
 356
 357        /// <summary>
 358        /// Retrieves a substring from this <see cref="StringSegment"/>.
 359        /// The substring starts at the position specified by <paramref name="offset"/> and has the remaining length.
 360        /// </summary>
 361        /// <param name="offset">The zero-based starting character position of a substring in this <see cref="StringSegm
 362        /// <returns>A <see cref="string"/> that is equivalent to the substring of remaining length that begins at
 363        /// <paramref name="offset"/> in this <see cref="StringSegment"/></returns>
 364        /// <exception cref="ArgumentOutOfRangeException">
 365        /// <paramref name="offset"/> is greater than or equal to <see cref="Length"/> or less than zero.
 366        /// </exception>
 0367        public string Substring(int offset) => Substring(offset, Length - offset);
 368
 369        /// <summary>
 370        /// Retrieves a substring from this <see cref="StringSegment"/>.
 371        /// The substring starts at the position specified by <paramref name="offset"/> and has the specified <paramref 
 372        /// </summary>
 373        /// <param name="offset">The zero-based starting character position of a substring in this <see cref="StringSegm
 374        /// <param name="length">The number of characters in the substring.</param>
 375        /// <returns>A <see cref="string"/> that is equivalent to the substring of length <paramref name="length"/> that
 376        /// <paramref name="offset"/> in this <see cref="StringSegment"/></returns>
 377        /// <exception cref="ArgumentOutOfRangeException">
 378        /// <paramref name="offset"/> or <paramref name="length"/> is less than zero, or <paramref name="offset"/> + <pa
 379        /// greater than <see cref="Length"/>.
 380        /// </exception>
 381        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 382        public string Substring(int offset, int length)
 383        {
 0384            if (!HasValue || offset < 0 || length < 0 || (uint)(offset + length) > (uint)Length)
 385            {
 0386                ThrowInvalidArguments(offset, length);
 387            }
 388
 0389            return Buffer.Substring(Offset + offset, length);
 390        }
 391
 392        /// <summary>
 393        /// Retrieves a <see cref="StringSegment"/> that represents a substring from this <see cref="StringSegment"/>.
 394        /// The <see cref="StringSegment"/> starts at the position specified by <paramref name="offset"/>.
 395        /// </summary>
 396        /// <param name="offset">The zero-based starting character position of a substring in this <see cref="StringSegm
 397        /// <returns>A <see cref="StringSegment"/> that begins at <paramref name="offset"/> in this <see cref="StringSeg
 398        /// whose length is the remainder.</returns>
 399        /// <exception cref="ArgumentOutOfRangeException">
 400        /// <paramref name="offset"/> is greater than or equal to <see cref="Length"/> or less than zero.
 401        /// </exception>
 0402        public StringSegment Subsegment(int offset) => Subsegment(offset, Length - offset);
 403
 404        /// <summary>
 405        /// Retrieves a <see cref="StringSegment"/> that represents a substring from this <see cref="StringSegment"/>.
 406        /// The <see cref="StringSegment"/> starts at the position specified by <paramref name="offset"/> and has the sp
 407        /// </summary>
 408        /// <param name="offset">The zero-based starting character position of a substring in this <see cref="StringSegm
 409        /// <param name="length">The number of characters in the substring.</param>
 410        /// <returns>A <see cref="StringSegment"/> that is equivalent to the substring of length <paramref name="length"
 411        /// <exception cref="ArgumentOutOfRangeException">
 412        /// <paramref name="offset"/> or <paramref name="length"/> is less than zero, or <paramref name="offset"/> + <pa
 413        /// greater than <see cref="Length"/>.
 414        /// </exception>
 415        public StringSegment Subsegment(int offset, int length)
 416        {
 0417            if (!HasValue || offset < 0 || length < 0 || (uint)(offset + length) > (uint)Length)
 418            {
 0419                ThrowInvalidArguments(offset, length);
 420            }
 421
 0422            return new StringSegment(Buffer, Offset + offset, length);
 423        }
 424
 425        /// <summary>
 426        /// Gets the zero-based index of the first occurrence of the character <paramref name="c"/> in this <see cref="S
 427        /// The search starts at <paramref name="start"/> and examines a specified number of <paramref name="count"/> ch
 428        /// </summary>
 429        /// <param name="c">The Unicode character to seek.</param>
 430        /// <param name="start">The zero-based index position at which the search starts. </param>
 431        /// <param name="count">The number of characters to examine.</param>
 432        /// <returns>The zero-based index position of <paramref name="c"/> from the beginning of the <see cref="StringSe
 433        /// <exception cref="ArgumentOutOfRangeException">
 434        /// <paramref name="start"/> or <paramref name="count"/> is less than zero, or <paramref name="start"/> + <param
 435        /// greater than <see cref="Length"/>.
 436        /// </exception>
 437        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 438        public int IndexOf(char c, int start, int count)
 439        {
 0440            var offset = Offset + start;
 441
 0442            if (!HasValue || start < 0 || (uint)offset > (uint)Buffer.Length)
 443            {
 0444                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
 445            }
 446
 0447            if (count < 0)
 448            {
 0449                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
 450            }
 451
 0452            var index = Buffer.IndexOf(c, offset, count);
 0453            if (index != -1)
 454            {
 0455                index -= Offset;
 456            }
 457
 0458            return index;
 459        }
 460
 461        /// <summary>
 462        /// Gets the zero-based index of the first occurrence of the character <paramref name="c"/> in this <see cref="S
 463        /// The search starts at <paramref name="start"/>.
 464        /// </summary>
 465        /// <param name="c">The Unicode character to seek.</param>
 466        /// <param name="start">The zero-based index position at which the search starts. </param>
 467        /// <returns>The zero-based index position of <paramref name="c"/> from the beginning of the <see cref="StringSe
 468        /// <exception cref="ArgumentOutOfRangeException">
 469        /// <paramref name="start"/> is greater than or equal to <see cref="Length"/> or less than zero.
 470        /// </exception>
 0471        public int IndexOf(char c, int start) => IndexOf(c, start, Length - start);
 472
 473        /// <summary>
 474        /// Gets the zero-based index of the first occurrence of the character <paramref name="c"/> in this <see cref="S
 475        /// </summary>
 476        /// <param name="c">The Unicode character to seek.</param>
 477        /// <returns>The zero-based index position of <paramref name="c"/> from the beginning of the <see cref="StringSe
 0478        public int IndexOf(char c) => IndexOf(c, 0, Length);
 479
 480        /// <summary>
 481        /// Reports the zero-based index of the first occurrence in this instance of any character in a specified array
 482        /// of Unicode characters. The search starts at a specified character position and examines a specified number
 483        /// of character positions.
 484        /// </summary>
 485        /// <param name="anyOf">A Unicode character array containing one or more characters to seek.</param>
 486        /// <param name="startIndex">The search starting position.</param>
 487        /// <param name="count">The number of character positions to examine.</param>
 488        /// <returns>The zero-based index position of the first occurrence in this instance where any character in <para
 489        /// was found; -1 if no character in <paramref name="anyOf"/> was found.</returns>
 490        /// <exception cref="ArgumentNullException">
 491        /// <paramref name="anyOf"/> is <code>null</code>.
 492        /// </exception>
 493        /// <exception cref="ArgumentOutOfRangeException">
 494        /// <paramref name="startIndex"/> or <paramref name="count"/> is less than zero, or <paramref name="startIndex"/
 495        /// greater than <see cref="Length"/>.
 496        /// </exception>
 497        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 498        public int IndexOfAny(char[] anyOf, int startIndex, int count)
 499        {
 0500            var index = -1;
 501
 0502            if (HasValue)
 503            {
 0504                if (startIndex < 0 || Offset + startIndex > Buffer.Length)
 505                {
 0506                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
 507                }
 508
 0509                if (count < 0 || Offset + startIndex + count > Buffer.Length)
 510                {
 0511                    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count);
 512                }
 513
 0514                index = Buffer.IndexOfAny(anyOf, Offset + startIndex, count);
 0515                if (index != -1)
 516                {
 0517                    index -= Offset;
 518                }
 519            }
 520
 0521            return index;
 522        }
 523
 524        /// <summary>
 525        /// Reports the zero-based index of the first occurrence in this instance of any character in a specified array
 526        /// of Unicode characters. The search starts at a specified character position.
 527        /// </summary>
 528        /// <param name="anyOf">A Unicode character array containing one or more characters to seek.</param>
 529        /// <param name="startIndex">The search starting position.</param>
 530        /// <returns>The zero-based index position of the first occurrence in this instance where any character in <para
 531        /// was found; -1 if no character in <paramref name="anyOf"/> was found.</returns>
 532        /// <exception cref="ArgumentOutOfRangeException">
 533        /// <paramref name="startIndex"/> is greater than or equal to <see cref="Length"/> or less than zero.
 534        /// </exception>
 535        public int IndexOfAny(char[] anyOf, int startIndex)
 536        {
 0537            return IndexOfAny(anyOf, startIndex, Length - startIndex);
 538        }
 539
 540        /// <summary>
 541        /// Reports the zero-based index of the first occurrence in this instance of any character in a specified array
 542        /// of Unicode characters.
 543        /// </summary>
 544        /// <param name="anyOf">A Unicode character array containing one or more characters to seek.</param>
 545        /// <returns>The zero-based index position of the first occurrence in this instance where any character in <para
 546        /// was found; -1 if no character in <paramref name="anyOf"/> was found.</returns>
 547        public int IndexOfAny(char[] anyOf)
 548        {
 0549            return IndexOfAny(anyOf, 0, Length);
 550        }
 551
 552        /// <summary>
 553        /// Reports the zero-based index position of the last occurrence of a specified Unicode character within this in
 554        /// </summary>
 555        /// <param name="value">The Unicode character to seek.</param>
 556        /// <returns>The zero-based index position of value if that character is found, or -1 if it is not.</returns>
 557        public int LastIndexOf(char value)
 558        {
 0559            var index = -1;
 560
 0561            if (HasValue)
 562            {
 0563                index = Buffer.LastIndexOf(value, Offset + Length - 1, Length);
 0564                if (index != -1)
 565                {
 0566                    index -= Offset;
 567                }
 568            }
 569
 0570            return index;
 571        }
 572
 573        /// <summary>
 574        /// Removes all leading and trailing whitespaces.
 575        /// </summary>
 576        /// <returns>The trimmed <see cref="StringSegment"/>.</returns>
 0577        public StringSegment Trim() => TrimStart().TrimEnd();
 578
 579        /// <summary>
 580        /// Removes all leading whitespaces.
 581        /// </summary>
 582        /// <returns>The trimmed <see cref="StringSegment"/>.</returns>
 583        public unsafe StringSegment TrimStart()
 584        {
 0585            var trimmedStart = Offset;
 0586            var length = Offset + Length;
 587
 0588            fixed (char* p = Buffer)
 589            {
 0590                while (trimmedStart < length)
 591                {
 0592                    var c = p[trimmedStart];
 593
 0594                    if (!char.IsWhiteSpace(c))
 595                    {
 596                        break;
 597                    }
 598
 0599                    trimmedStart++;
 600                }
 601            }
 602
 0603            return new StringSegment(Buffer, trimmedStart, length - trimmedStart);
 604        }
 605
 606        /// <summary>
 607        /// Removes all trailing whitespaces.
 608        /// </summary>
 609        /// <returns>The trimmed <see cref="StringSegment"/>.</returns>
 610        public unsafe StringSegment TrimEnd()
 611        {
 0612            var offset = Offset;
 0613            var trimmedEnd = offset + Length - 1;
 614
 0615            fixed (char* p = Buffer)
 616            {
 0617                while (trimmedEnd >= offset)
 618                {
 0619                    var c = p[trimmedEnd];
 620
 0621                    if (!char.IsWhiteSpace(c))
 622                    {
 623                        break;
 624                    }
 625
 0626                    trimmedEnd--;
 627                }
 628            }
 629
 0630            return new StringSegment(Buffer, offset, trimmedEnd - offset + 1);
 631        }
 632
 633        /// <summary>
 634        /// Splits a string into <see cref="StringSegment"/>s that are based on the characters in an array.
 635        /// </summary>
 636        /// <param name="chars">A character array that delimits the substrings in this string, an empty array that
 637        /// contains no delimiters, or null.</param>
 638        /// <returns>An <see cref="StringTokenizer"/> whose elements contain the <see cref="StringSegment"/>s from this 
 639        /// that are delimited by one or more characters in <paramref name="chars"/>.</returns>
 640        public StringTokenizer Split(char[] chars)
 641        {
 0642            return new StringTokenizer(this, chars);
 643        }
 644
 645        /// <summary>
 646        /// Indicates whether the specified <see cref="StringSegment"/> is null or an Empty string.
 647        /// </summary>
 648        /// <param name="value">The <see cref="StringSegment"/> to test.</param>
 649        /// <returns></returns>
 650        public static bool IsNullOrEmpty(StringSegment value)
 651        {
 0652            var res = false;
 653
 0654            if (!value.HasValue || value.Length == 0)
 655            {
 0656                res = true;
 657            }
 658
 0659            return res;
 660        }
 661
 662        /// <summary>
 663        /// Returns the <see cref="string"/> represented by this <see cref="StringSegment"/> or <code>String.Empty</code
 664        /// </summary>
 665        /// <returns>The <see cref="string"/> represented by this <see cref="StringSegment"/> or <code>String.Empty</cod
 666        public override string ToString()
 667        {
 0668            return Value ?? string.Empty;
 669        }
 670
 671        // Methods that do no return (i.e. throw) are not inlined
 672        // https://github.com/dotnet/coreclr/pull/6103
 673        private static void ThrowInvalidArguments(string buffer, int offset, int length)
 674        {
 675            // Only have single throw in method so is marked as "does not return" and isn't inlined to caller
 0676            throw GetInvalidArgumentsException();
 677
 678            Exception GetInvalidArgumentsException()
 679            {
 0680                if (buffer == null)
 681                {
 0682                    return ThrowHelper.GetArgumentNullException(ExceptionArgument.buffer);
 683                }
 684
 0685                if (offset < 0)
 686                {
 0687                    return ThrowHelper.GetArgumentOutOfRangeException(ExceptionArgument.offset);
 688                }
 689
 0690                if (length < 0)
 691                {
 0692                    return ThrowHelper.GetArgumentOutOfRangeException(ExceptionArgument.length);
 693                }
 694
 0695                return ThrowHelper.GetArgumentException(ExceptionResource.Argument_InvalidOffsetLength);
 696            }
 697        }
 698
 699        private void ThrowInvalidArguments(int offset, int length)
 700        {
 0701            throw GetInvalidArgumentsException(HasValue);
 702
 703            Exception GetInvalidArgumentsException(bool hasValue)
 704            {
 0705                if (!hasValue)
 706                {
 0707                    return ThrowHelper.GetArgumentOutOfRangeException(ExceptionArgument.offset);
 708                }
 709
 0710                if (offset < 0)
 711                {
 0712                    return ThrowHelper.GetArgumentOutOfRangeException(ExceptionArgument.offset);
 713                }
 714
 0715                if (length < 0)
 716                {
 0717                    return ThrowHelper.GetArgumentOutOfRangeException(ExceptionArgument.length);
 718                }
 719
 0720                return ThrowHelper.GetArgumentException(ExceptionResource.Argument_InvalidOffsetLengthStringSegment);
 721            }
 722        }
 723    }
 724}