// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Diagnostics.Contracts; using System.Runtime.CompilerServices; #if NETSTANDARD using System.Runtime.InteropServices; #endif using CommunityToolkit.HighPerformance.Enumerables; using CommunityToolkit.HighPerformance.Helpers.Internals; namespace CommunityToolkit.HighPerformance; /// /// Helpers for working with the type. /// public static class StringExtensions { /// /// Returns a reference to the first element within a given , with no bounds checks. /// /// The input instance. /// A reference to the first element within , or the location it would have used, if is empty. /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to perform checks in case the returned value is dereferenced. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref char DangerousGetReference(this string text) { #if NETCOREAPP3_1_OR_GREATER return ref Unsafe.AsRef(text.GetPinnableReference()); #else return ref MemoryMarshal.GetReference(text.AsSpan()); #endif } /// /// Returns a reference to an element at a specified index within a given , with no bounds checks. /// /// The input instance. /// The index of the element to retrieve within . /// A reference to the element within at the index specified by . /// This method doesn't do any bounds checks, therefore it is responsibility of the caller to ensure the parameter is valid. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref char DangerousGetReferenceAt(this string text, int i) { #if NETCOREAPP3_1_OR_GREATER ref char r0 = ref Unsafe.AsRef(text.GetPinnableReference()); #else ref char r0 = ref MemoryMarshal.GetReference(text.AsSpan()); #endif ref char ri = ref Unsafe.Add(ref r0, (nint)(uint)i); return ref ri; } /// /// Counts the number of occurrences of a given character into a target instance. /// /// The input instance to read. /// The character to look for. /// The number of occurrences of in . [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Count(this string text, char c) { ref char r0 = ref text.DangerousGetReference(); nint length = (nint)(uint)text.Length; return (int)SpanHelper.Count(ref r0, length, c); } /// /// Enumerates the items in the input instance, as pairs of value/index values. /// This extension should be used directly within a loop: /// /// string text = "Hello, world!"; /// /// foreach (var item in text.Enumerate()) /// { /// // Access the index and value of each item here... /// int index = item.Index; /// char value = item.Value; /// } /// /// The compiler will take care of properly setting up the loop with the type returned from this method. /// /// The source to enumerate. /// A wrapper type that will handle the value/index enumeration for . /// The returned value shouldn't be used directly: use this extension in a loop. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpanEnumerable Enumerate(this string text) { return new(text.AsSpan()); } /// /// Tokenizes the values in the input instance using a specified separator. /// This extension should be used directly within a loop: /// /// string text = "Hello, world!"; /// /// foreach (var token in text.Tokenize(',')) /// { /// // Access the tokens here... /// } /// /// The compiler will take care of properly setting up the loop with the type returned from this method. /// /// The source to tokenize. /// The separator character to use. /// A wrapper type that will handle the tokenization for . /// The returned value shouldn't be used directly: use this extension in a loop. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpanTokenizer Tokenize(this string text, char separator) { return new(text.AsSpan(), separator); } /// /// Gets a content hash from the input instance using the Djb2 algorithm. /// For more info, see the documentation for . /// /// The source to enumerate. /// The Djb2 value for the input instance. /// The Djb2 hash is fully deterministic and with no random components. [Pure] [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe int GetDjb2HashCode(this string text) { ref char r0 = ref text.DangerousGetReference(); nint length = (nint)(uint)text.Length; return SpanHelper.GetDjb2HashCode(ref r0, length); } }