< Summary

Class:GDX.StringExtensions
Assembly:GDX
File(s):D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/StringExtensions.cs
Covered lines:264
Uncovered lines:0
Coverable lines:264
Total lines:739
Line coverage:100% (264 of 264)
Covered branches:0
Total branches:0
Covered methods:22
Total methods:22
Method coverage:100% (22 of 22)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
StringExtensions()0%110100%
Concatenate(...)0%770100%
Decrypt(...)0%770100%
Encrypt(...)0%660100%
GetAfterFirst(...)0%220100%
GetAfterLast(...)0%220100%
GetBeforeFirst(...)0%220100%
GetBeforeLast(...)0%220100%
GetStableHashCode(...)0%440100%
GetStableLowerCaseHashCode(...)0%880100%
GetStableUpperCaseHashCode(...)0%880100%
HasLowerCase(...)0%550100%
HasUpperCase(...)0%550100%
IsBooleanValue(...)0%10100100%
IsBooleanPositiveValue(...)0%660100%
IsIntegerValue(...)0%10100100%
IsNumeric(...)0%12120100%
CountOccurence(...)0%440100%
PartialMatch(...)0%440100%
PartialMatch(...)0%440100%
SplitCamelCase(...)0%110100%
StripNonAscii(...)0%110100%

File(s)

D:/BuildAgent/work/GDX-Documentation/Projects/GDX_Development/Packages/com.dotbunny.gdx/GDX/StringExtensions.cs

#LineLine coverage
 1// Copyright (c) 2020-2022 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System;
 6using System.IO;
 7using System.Runtime.CompilerServices;
 8using System.Runtime.ConstrainedExecution;
 9using System.Security;
 10using System.Security.Cryptography;
 11using System.Text;
 12using System.Text.RegularExpressions;
 13using GDX.Collections.Generic;
 14
 15namespace GDX
 16{
 17    /// <summary>
 18    ///     <see cref="string" /> Based Extension Methods
 19    /// </summary>
 20    [VisualScriptingCompatible(2)]
 21    public static class StringExtensions
 22    {
 23        /// <summary>
 24        ///     The ASCII decimal value shift required to change the case of a letter.
 25        /// </summary>
 26        public const int AsciiCaseShift = 32;
 27
 28        /// <summary>
 29        ///     The ASCII decimal value for a.
 30        /// </summary>
 31        public const int AsciiLowerCaseStart = 97;
 32
 33        /// <summary>
 34        ///     The ASCII decimal value for lowercase z.
 35        /// </summary>
 36        public const int AsciiLowerCaseEnd = 122;
 37
 38        /// <summary>
 39        ///     The ASCII decimal value for the number sign -.
 40        /// </summary>
 41        public const int AsciiNumberSign = 45;
 42
 43        /// <summary>
 44        ///     The ASCII decimal value for the decimal (.).
 45        /// </summary>
 46        public const int AsciiNumberDecimal = 46;
 47
 48        /// <summary>
 49        ///     The ASCII decimal value for the , separator.
 50        /// </summary>
 51        public const int AsciiNumberSeparator = 47;
 52
 53        /// <summary>
 54        ///     The ASCII decimal value for 0.
 55        /// </summary>
 56        public const int AsciiNumberStart = 48;
 57
 58        /// <summary>
 59        ///     The ASCII decimal value for 9.
 60        /// </summary>
 61        public const int AsciiNumberEnd = 57;
 62
 63        /// <summary>
 64        ///     The ASCII decimal value for uppercase A.
 65        /// </summary>
 66        public const int AsciiUpperCaseStart = 65;
 67
 68        /// <summary>
 69        ///     The ASCII  decimal value for uppercase Z.
 70        /// </summary>
 71        public const int AsciiUpperCaseEnd = 90;
 72
 73        /// <summary>
 74        ///     The default encryption key used when none is provided to the encryption related extensions.
 75        /// </summary>
 76        /// <remarks>
 77        ///     You can change this at runtime during some sort of initialization pass to being something unique to your
 78        ///     but it is not absolutely necessary. This must be a multiple of 8 bytes.
 79        /// </remarks>
 280        public static byte[] EncryptionDefaultKey = Encoding.UTF8.GetBytes("Awesome!");
 81
 82        /// <summary>
 83        ///     The IV (Initialization Vector) provided to the <see cref="DESCryptoServiceProvider" />.
 84        /// </summary>
 85        /// <remarks>
 86        ///     You can change this at runtime during some sort of initialization pass to being something unique to your
 87        ///     but it is not absolutely necessary. This must be a multiple of 8 bytes.
 88        /// </remarks>
 289        public static byte[] EncryptionInitializationVector = Encoding.UTF8.GetBytes("dotBunny");
 90
 91        /// <summary>
 92        ///     Concatenate an array of strings into one unified string.
 93        /// </summary>
 94        /// <param name="pieces">An array of strings</param>
 95        /// <param name="delimiter">An optional string which to use between <paramref name="pieces"/> when combining.</p
 96        /// <param name="trailingDelimiter">Should a trailing <paramref name="delimiter"/> be appended?</param>
 97        /// <returns>A concatenated <see cref="string"/>.</returns>
 98        public static string Concatenate(this string[] pieces, string delimiter = null, bool trailingDelimiter = false)
 299        {
 2100            StringBuilder builder = new StringBuilder();
 101
 2102            int count = pieces.Length;
 2103            bool hasDelimiter = delimiter != null;
 2104            int tail = count - 1;
 105
 2106            if (trailingDelimiter)
 1107            {
 8108                for (int i = 0; i < count; i++)
 3109                {
 3110                    builder.Append(pieces[i]);
 3111                    if (hasDelimiter)
 3112                    {
 3113                        builder.Append(delimiter);
 3114                    }
 3115                }
 1116            }
 117            else
 1118            {
 8119                for (int i = 0; i < count; i++)
 3120                {
 3121                    builder.Append(pieces[i]);
 3122                    if (hasDelimiter && i != tail)
 2123                    {
 2124                        builder.Append(delimiter);
 2125                    }
 3126                }
 1127            }
 2128            return builder.ToString();
 2129        }
 130
 131        /// <summary>
 132        ///     Decrypt an encrypted <see cref="string" /> created by <see cref="Encrypt" />.
 133        /// </summary>
 134        /// <remarks>This will have quite a few allocations.</remarks>
 135        /// <param name="encryptedString">The encrypted <see cref="string" />.</param>
 136        /// <param name="encryptionKey">The key used to encrypt the <see cref="string" />.</param>
 137        /// <returns>The decrypted <see cref="string" />.</returns>
 138        public static string Decrypt(this string encryptedString, byte[] encryptionKey = null)
 2139        {
 2140            DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider
 141            {
 142                Mode = CipherMode.ECB,
 143                Padding = PaddingMode.PKCS7,
 144                Key = encryptionKey != null && encryptionKey.Length > 0 ? encryptionKey : EncryptionDefaultKey,
 145                IV = EncryptionInitializationVector
 146            };
 2147            using MemoryStream stream = new MemoryStream(Convert.FromBase64String(encryptedString));
 2148            using CryptoStream cs = new CryptoStream(stream, desProvider.CreateDecryptor(), CryptoStreamMode.Read);
 2149            using StreamReader sr = new StreamReader(cs, Encoding.UTF8);
 2150            return sr.ReadToEnd();
 2151        }
 152
 153        /// <summary>
 154        ///     Encrypt a <see cref="string" /> utilizing a <see cref="DESCryptoServiceProvider" />.
 155        /// </summary>
 156        /// <remarks>This will have quite a few allocations.</remarks>
 157        /// <param name="decryptedString">The original <see cref="string" />.</param>
 158        /// <param name="encryptionKey">
 159        ///     The key to be used when encrypting the <see cref="string" />.  This must be a
 160        ///     multiple of 8 bytes.
 161        /// </param>
 162        /// <returns>The encrypted <see cref="string" />.</returns>
 163        public static string Encrypt(this string decryptedString, byte[] encryptionKey = null)
 3164        {
 3165            DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider
 166            {
 167                Mode = CipherMode.ECB,
 168                Padding = PaddingMode.PKCS7,
 169                Key = encryptionKey != null && encryptionKey.Length > 0 ? encryptionKey : EncryptionDefaultKey,
 170                IV = EncryptionInitializationVector
 171            };
 3172            using MemoryStream stream = new MemoryStream();
 3173            using CryptoStream cs = new CryptoStream(stream, desProvider.CreateEncryptor(), CryptoStreamMode.Write);
 3174            byte[] data = Encoding.Default.GetBytes(decryptedString);
 3175            cs.Write(data, 0, data.Length);
 3176            cs.FlushFinalBlock();
 3177            return Convert.ToBase64String(stream.ToArray());
 3178        }
 179
 180        /// <summary>
 181        ///     Get the <see cref="string" /> after the first identified <paramref name="splitString" /> in
 182        ///     <paramref name="targetString" />.
 183        /// </summary>
 184        /// <param name="targetString">The target <see cref="string" /> to look in.</param>
 185        /// <param name="splitString">The divider which the <paramref name="targetString" /> should be split on.</param>
 186        /// <param name="comparison">Specifies the culture, case, and sort rules to be used.</param>
 187        /// <returns>
 188        ///     The content following the <paramref name="splitString" />, or <c>null</c> if none is found.
 189        /// </returns>
 190        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 191        public static string GetAfterFirst(this string targetString, string splitString,
 192            StringComparison comparison = StringComparison.Ordinal)
 1193        {
 1194            int splitIndex = targetString.IndexOf(splitString, 0, comparison);
 1195            return splitIndex < 0 ? null : targetString.Substring(splitIndex + splitString.Length);
 1196        }
 197
 198        /// <summary>
 199        ///     Get the <see cref="string" /> after the last identified <paramref name="splitString" /> in
 200        ///     <paramref name="targetString" />.
 201        /// </summary>
 202        /// <param name="targetString">The target <see cref="string" /> to look in.</param>
 203        /// <param name="splitString">The divider which the <paramref name="targetString" /> should be split on.</param>
 204        /// <param name="comparison">Specifies the culture, case, and sort rules to be used.</param>
 205        /// <returns>
 206        ///     The content following the <paramref name="splitString" />, or <c>null</c> if none is found.
 207        /// </returns>
 208        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 209        public static string GetAfterLast(this string targetString, string splitString,
 210            StringComparison comparison = StringComparison.Ordinal)
 1211        {
 1212            int splitIndex = targetString.LastIndexOf(splitString, targetString.Length - 1, comparison);
 1213            return splitIndex < 0 ? null : targetString.Substring(splitIndex + splitString.Length);
 1214        }
 215
 216        /// <summary>
 217        ///     Get the <see cref="string" /> before the first identified <paramref name="splitString" /> in
 218        ///     <paramref name="targetString" />.
 219        /// </summary>
 220        /// <param name="targetString">The target <see cref="string" /> to look in.</param>
 221        /// <param name="splitString">The divider which the <paramref name="targetString" /> should be split on.</param>
 222        /// <param name="comparison">Specifies the culture, case, and sort rules to be used.</param>
 223        /// <returns>The content before the <paramref name="splitString" />, or <c>null</c> if none is found.</returns>
 224        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 225        public static string GetBeforeFirst(this string targetString, string splitString,
 226            StringComparison comparison = StringComparison.Ordinal)
 1227        {
 1228            int splitIndex = targetString.IndexOf(splitString, 0, comparison);
 1229            return splitIndex < 0 ? null : targetString.Substring(0, splitIndex);
 1230        }
 231
 232        /// <summary>
 233        ///     Get the <see cref="string" /> before the last identified <paramref name="splitString" /> in
 234        ///     <paramref name="targetString" />.
 235        /// </summary>
 236        /// <param name="targetString">The target <see cref="string" /> to look in.</param>
 237        /// <param name="splitString">The divider which the <paramref name="targetString" /> should be split on.</param>
 238        /// <param name="comparison">Specifies the culture, case, and sort rules to be used.</param>
 239        /// <returns>The content before the <paramref name="splitString" />, or <c>null</c> if none is found.</returns>
 240        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 241        public static string GetBeforeLast(this string targetString, string splitString,
 242            StringComparison comparison = StringComparison.Ordinal)
 1243        {
 1244            int splitIndex = targetString.LastIndexOf(splitString, targetString.Length - 1, comparison);
 1245            return splitIndex < 0 ? null : targetString.Substring(0, splitIndex);
 1246        }
 247
 248        /// <summary>
 249        ///     <para>
 250        ///         Get the stable hash code value of <paramref name="targetString" />.
 251        ///     </para>
 252        /// </summary>
 253        /// <remarks>
 254        ///     This loosely based on the Fowler–Noll–Vo (FNV) hash function. It's value will be identical
 255        ///     to the value produced natively by processing a <see cref="string" /> with
 256        ///     <see cref="string.GetHashCode()" />, but with no allocations and no virtual calls.
 257        /// </remarks>
 258        /// <param name="targetString">The target <see cref="string" />.</param>
 259        /// <returns>A <see cref="int" /> value.</returns>
 260        [SecuritySafeCritical]
 261        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 262        public static unsafe int GetStableHashCode(this string targetString)
 231263        {
 231264            fixed (char* src = targetString)
 231265            {
 231266                int hash1 = 5381;
 231267                int hash2 = hash1;
 268                int c;
 231269                char* s = src;
 270
 271                // Get character
 1247272                while ((c = s[0]) != 0)
 1169273                {
 274                    // Add to Hash #1
 1169275                    hash1 = ((hash1 << 5) + hash1) ^ c;
 276
 277                    // Get our second character
 1169278                    c = s[1];
 279
 1169280                    if (c == 0)
 153281                    {
 153282                        break;
 283                    }
 284
 1016285                    hash2 = ((hash2 << 5) + hash2) ^ c;
 1016286                    s += 2;
 1016287                }
 288
 231289                return hash1 + hash2 * 1566083941;
 290            }
 231291        }
 292
 293        /// <summary>
 294        ///     <para>
 295        ///         Get the stable hash code value of <paramref name="targetString" /> (converted to an uppercase
 296        ///         <see cref="string" />).
 297        ///     </para>
 298        /// </summary>
 299        /// <remarks>
 300        ///     This loosely based on the Fowler–Noll–Vo (FNV) hash function. It's value will be identical
 301        ///     to the value produced natively by processing a <see cref="string" /> with
 302        ///     <see cref="string.ToLower()" />.<see cref="string.GetHashCode()" />, but with no
 303        ///     allocations.
 304        /// </remarks>
 305        /// <param name="targetString">The target <see cref="string" />.</param>
 306        /// <returns>A <see cref="int" /> value.</returns>
 307        [SecuritySafeCritical]
 308        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 309        public static unsafe int GetStableLowerCaseHashCode(this string targetString)
 11310        {
 11311            fixed (char* src = targetString)
 11312            {
 11313                int hash1 = 5381;
 11314                int hash2 = hash1;
 315                int c;
 11316                char* s = src;
 317
 318                // Get character
 64319                while ((c = s[0]) != 0)
 61320                {
 321                    // Check character value and shift it if necessary (32)
 61322                    if (c >= AsciiUpperCaseStart && c <= AsciiUpperCaseEnd)
 20323                    {
 20324                        c ^= AsciiCaseShift;
 20325                    }
 326
 327                    // Add to Hash #1
 61328                    hash1 = ((hash1 << 5) + hash1) ^ c;
 329
 330                    // Get our second character
 61331                    c = s[1];
 332
 61333                    if (c == 0)
 8334                    {
 8335                        break;
 336                    }
 337
 338                    // Check character value and shift it if necessary (32)
 53339                    if (c >= AsciiUpperCaseStart && c <= AsciiUpperCaseEnd)
 13340                    {
 13341                        c ^= AsciiCaseShift;
 13342                    }
 343
 53344                    hash2 = ((hash2 << 5) + hash2) ^ c;
 53345                    s += 2;
 53346                }
 347
 11348                return hash1 + hash2 * 1566083941;
 349            }
 11350        }
 351
 352        /// <summary>
 353        ///     <para>
 354        ///         Get the stable hash code value of <paramref name="targetString" /> (converted to an uppercase
 355        ///         <see cref="string" />).
 356        ///     </para>
 357        /// </summary>
 358        /// <remarks>
 359        ///     This loosely based on the Fowler–Noll–Vo (FNV) hash function. It's value will be identical
 360        ///     to the value produced natively by processing a <see cref="string" /> with
 361        ///     <see cref="string.ToUpper()" />.<see cref="string.GetHashCode()" />, but with no
 362        ///     allocations.
 363        /// </remarks>
 364        /// <param name="targetString">The target <see cref="string" />.</param>
 365        /// <returns>A <see cref="int" /> value.</returns>
 366        [SecuritySafeCritical]
 367        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 368        public static unsafe int GetStableUpperCaseHashCode(this string targetString)
 30369        {
 30370            fixed (char* src = targetString)
 30371            {
 30372                int hash1 = 5381;
 30373                int hash2 = hash1;
 374                int c;
 30375                char* s = src;
 376
 377                // Get character
 171378                while ((c = s[0]) != 0)
 146379                {
 380                    // Check character value and shift it if necessary (32)
 146381                    if (c >= AsciiLowerCaseStart && c <= AsciiLowerCaseEnd)
 67382                    {
 67383                        c ^= AsciiCaseShift;
 67384                    }
 385
 386                    // Add to Hash #1
 146387                    hash1 = ((hash1 << 5) + hash1) ^ c;
 388
 389                    // Get our second character
 146390                    c = s[1];
 391
 146392                    if (c == 0)
 5393                    {
 5394                        break;
 395                    }
 396
 397                    // Check character value and shift it if necessary (32)
 141398                    if (c >= AsciiLowerCaseStart && c <= AsciiLowerCaseEnd)
 116399                    {
 116400                        c ^= AsciiCaseShift;
 116401                    }
 402
 141403                    hash2 = ((hash2 << 5) + hash2) ^ c;
 141404                    s += 2;
 141405                }
 406
 30407                return hash1 + hash2 * 1566083941;
 408            }
 30409        }
 410
 411        /// <summary>
 412        ///     Determine if there are any lowercase letters in the provided <paramref name="targetString" />.
 413        /// </summary>
 414        /// <param name="targetString">The target <see cref="string" />.</param>
 415        /// <returns>true/false if lowercase letters were found.</returns>
 416        [SecuritySafeCritical]
 417        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 418        public static unsafe bool HasLowerCase(this string targetString)
 2419        {
 2420            fixed (char* src = targetString)
 2421            {
 422                int c;
 2423                char* s = src;
 424
 425                // Get character
 24426                while ((c = s[0]) != 0)
 23427                {
 23428                    if (c >= AsciiLowerCaseStart && c <= AsciiLowerCaseEnd)
 1429                    {
 1430                        return true;
 431                    }
 432
 22433                    s += 1;
 22434                }
 1435            }
 436
 1437            return false;
 2438        }
 439
 440        /// <summary>
 441        ///     Determine if there are any uppercase letters in the provided <paramref name="targetString" />.
 442        /// </summary>
 443        /// <param name="targetString">The target <see cref="string" />.</param>
 444        /// <returns>true/false if uppercase letters were found.</returns>
 445        [SecuritySafeCritical]
 446        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 447        public static unsafe bool HasUpperCase(this string targetString)
 2448        {
 2449            fixed (char* src = targetString)
 2450            {
 451                int c;
 2452                char* s = src;
 453
 454                // Get character
 23455                while ((c = s[0]) != 0)
 22456                {
 22457                    if (c >= AsciiUpperCaseStart && c <= AsciiUpperCaseEnd)
 1458                    {
 1459                        return true;
 460                    }
 461
 21462                    s += 1;
 21463                }
 1464            }
 465
 1466            return false;
 2467        }
 468
 469        /// <summary>
 470        ///     Determine if the <paramref name="targetString" /> represents a boolean value arrangement.
 471        /// </summary>
 472        /// <param name="targetString">The target <see cref="string" />.</param>
 473        /// <returns>true/false if the <paramref name="targetString" /> can be evaluated as a boolean value.</returns>
 474        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 475        public static bool IsBooleanValue(this string targetString)
 2476        {
 477            // Get an optimized hash value
 2478            int hash = targetString.GetStableLowerCaseHashCode();
 479
 480            // Check
 2481            switch (hash)
 482            {
 483                case -971704226: // true
 484                case -1685090051: // false
 485                case 372029325: // 1
 486                case 372029326: // 0
 487                case -1273338385: // yes
 488                case 1496915069: // no
 489                case -1231968287: // on
 490                case -870054309: // off
 1491                    return true;
 492            }
 493
 1494            return false;
 2495        }
 496
 497        /// <summary>
 498        ///     Determine if the <paramref name="targetString" /> represents a positive boolean value arrangement.
 499        /// </summary>
 500        /// <example>
 501        ///     Useful method when trying to parse data for branching.
 502        ///     <code>
 503        ///         if(data["set"].IsBooleanPositiveValue())
 504        ///         {
 505        ///             ShouldBlueBox();
 506        ///         }
 507        ///     </code>
 508        /// </example>
 509        /// <param name="targetString">The target <see cref="string" />.</param>
 510        /// <returns>true/false if the <paramref name="targetString" /> can be evaluated as a positive boolean value.</r
 511        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 512        public static bool IsBooleanPositiveValue(this string targetString)
 2513        {
 2514            int hash = targetString.GetStableLowerCaseHashCode();
 2515            switch (hash)
 516            {
 517                case -971704226: // true
 518                case 372029325: // 1
 519                case -1273338385: // yes
 520                case -1231968287: // on
 1521                    return true;
 522                default:
 1523                    return false;
 524            }
 2525        }
 526
 527        /// <summary>
 528        ///     Determine if the <paramref name="targetString" /> is an <see cref="int" /> value.
 529        /// </summary>
 530        /// <remarks>
 531        ///     This method is meant for when you do not actually need the value returned, merely an evaluation if
 532        ///     the provided <paramref name="targetString" /> is an <see cref="int" />. This does not qualify
 533        ///     <see cref="float" /> values positively.
 534        /// </remarks>
 535        /// <param name="targetString">The target <see cref="string" />.</param>
 536        /// <returns>true/false if it contains an <see cref="int" />.</returns>
 537        [SecuritySafeCritical]
 538        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 539        public static unsafe bool IsIntegerValue(this string targetString)
 5540        {
 5541            fixed (char* src = targetString)
 5542            {
 5543                char* s = src;
 5544                int c = s[0];
 545
 546                // Nothing
 5547                if (c == 0)
 1548                {
 1549                    return false;
 550                }
 551
 552                // Check first character
 4553                if (c != AsciiNumberSign && (c < AsciiNumberStart || c > AsciiNumberEnd))
 1554                {
 1555                    return false;
 556                }
 557
 558                // Get character
 12559                while ((c = s[1]) != 0)
 10560                {
 10561                    if (c < AsciiNumberStart || c > AsciiNumberEnd)
 1562                    {
 1563                        return false;
 564                    }
 565
 9566                    s += 1;
 9567                }
 2568            }
 569
 2570            return true;
 5571        }
 572
 573        /// <summary>
 574        ///     Is the <paramref name="targetString" /> a numeric value.
 575        /// </summary>
 576        /// <remarks>
 577        ///     <para>
 578        ///         The following requirements must be met to be considered a valid number in this method:
 579        ///     </para>
 580        ///     <list type="bullet">
 581        ///         <item>
 582        ///             <description>
 583        ///                 The first character may be an indicator of its sign, an explicit acceptance of <c>-</c> is m
 584        ///                 prefixed with <c>+</c>, the number will be found invalid.
 585        ///             </description>
 586        ///         </item>
 587        ///         <item>
 588        ///             <description>A single decimal point <c>.</c> may be present in the <paramref name="targetString"
 589        ///         </item>
 590        ///         <item>
 591        ///             <description>No alphabet characters are present in the <paramref name="targetString"/>.</descrip
 592        ///         </item>
 593        ///     </list>
 594        /// </remarks>
 595        /// <param name="targetString">The target <see cref="string" />.</param>
 596        /// <returns>true/false if the <paramref name="targetString" /> qualifies as a numeric value.</returns>
 597        [SecuritySafeCritical]
 598        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 599        public static unsafe bool IsNumeric(this string targetString)
 3169600        {
 3169601            fixed (char* src = targetString)
 3169602            {
 3169603                char* s = src;
 3169604                int c = s[0];
 3169605                bool hasDecimal = false;
 606
 607                // Nothing
 3169608                if (c == 0)
 1609                {
 1610                    return false;
 611                }
 612
 613                // Check first character
 3168614                if (c != AsciiNumberSign && (c < AsciiNumberStart || c > AsciiNumberEnd))
 2353615                {
 2353616                    return false;
 617                }
 618
 619                // Get character
 1640620                while ((c = s[1]) != 0)
 826621                {
 826622                    if (c < AsciiNumberStart || c > AsciiNumberEnd)
 3623                    {
 3624                        if (c == AsciiNumberDecimal && !hasDecimal)
 2625                        {
 2626                            hasDecimal = true;
 2627                            s += 1;
 2628                            continue;
 629                        }
 630
 1631                        return false;
 632                    }
 633
 823634                    s += 1;
 823635                }
 814636            }
 637
 814638            return true;
 3169639        }
 640
 641        /// <summary>
 642        ///     Counts the number of times the needle (<paramref name="targetCharacter"/>) appears in the haystack (<par
 643        /// </summary>
 644        /// <remarks>Specifically created to avoid using LINQ and avoid an allocation.</remarks>
 645        /// <param name="targetString">The haystack.</param>
 646        /// <param name="targetCharacter">The needle.</param>
 647        /// <returns>The number of times <paramref name="targetCharacter"/> is found in <paramref name="targetString"/>.
 648        [SecuritySafeCritical]
 649        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 650        public static unsafe int CountOccurence(this string targetString, char targetCharacter)
 2651        {
 2652            int counter = 0;
 2653            fixed (char* src = targetString)
 2654            {
 655                int c;
 2656                char* s = src;
 54657                while ((c = s[0]) != 0)
 52658                {
 52659                    if (c == targetCharacter)
 2660                    {
 2661                        counter++;
 2662                    }
 52663                    s += 1;
 52664                }
 2665            }
 666
 2667            return counter;
 2668        }
 669
 670        /// <summary>
 671        ///     Does the <paramref name="haystack"/> partially contain the <paramref name="needle"/>?
 672        /// </summary>
 673        /// <param name="haystack">An array of <see cref="string"/>s.</param>
 674        /// <param name="needle">The <see cref="string"/> that is being looked for.</param>
 675        /// <returns>true/false if found.</returns>
 676        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 677        public static bool PartialMatch(this string[] haystack, string needle)
 3678        {
 4679            if (haystack == null) return false;
 680
 2681            int count = haystack.Length;
 14682            for (int i = 0; i < count; i++)
 6683            {
 6684                if (haystack[i].Contains(needle))
 1685                {
 1686                    return true;
 687                }
 5688            }
 689
 1690            return false;
 3691        }
 692
 693        /// <summary>
 694        ///     Does the <paramref name="haystack"/> partially contain the <paramref name="needle"/>?
 695        /// </summary>
 696        /// <param name="haystack">A <see cref="SimpleList{T}"/> of <see cref="string"/>s.</param>
 697        /// <param name="needle">The <see cref="string"/> that is being looked for.</param>
 698        /// <returns>true/false if found.</returns>
 699        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 700        public static bool PartialMatch(this SimpleList<string> haystack, string needle)
 3701        {
 4702            if (haystack.Count == 0) return false;
 703
 2704            int count = haystack.Count;
 14705            for (int i = 0; i < count; i++)
 6706            {
 6707                if (haystack.Array[i].Contains(needle))
 1708                {
 1709                    return true;
 710                }
 5711            }
 712
 1713            return false;
 3714        }
 715
 716        /// <summary>
 717        ///     Create a new string, splitting an existing string up based on camel case formatting.
 718        /// </summary>
 719        /// <param name="targetString">The target <see cref="string" />.</param>
 720        /// <param name="divider">The <see cref="string" /> to put in between the split <see cref="string" />.</param>
 721        /// <returns>A new <see cref="string" />.</returns>
 722        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 723        public static string SplitCamelCase(this string targetString, string divider = " ")
 2724        {
 2725            return Regex.Replace(targetString, "([A-Z])", $"{divider}$1", RegexOptions.None).Trim();
 2726        }
 727
 728        /// <summary>
 729        ///     Remove non ASCII characters from a <see cref="string"/>.
 730        /// </summary>
 731        /// <param name="targetString">The <see cref="string"/> to be cleaned.</param>
 732        /// <returns>A <see cref="string"/> without ASCII characters.</returns>
 733        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 734        public static string StripNonAscii(this string targetString)
 36596735        {
 36596736            return Regex.Replace(targetString, @"[^\u0000-\u007F]+", string.Empty);
 36596737        }
 738    }
 739}