String.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Buffers;
  5. using System.Collections;
  6. using System.Collections.Generic;
  7. using System.Diagnostics;
  8. using System.Globalization;
  9. using System.Runtime.CompilerServices;
  10. using System.Runtime.InteropServices;
  11. using System.Runtime.Versioning;
  12. using System.Text;
  13. using Internal.Runtime.CompilerServices;
  14. namespace System
  15. {
  16. // The String class represents a static string of characters. Many of
  17. // the string methods perform some type of transformation on the current
  18. // instance and return the result as a new string. As with arrays, character
  19. // positions (indices) are zero-based.
  20. [Serializable]
  21. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  22. public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable<char>, IComparable<string>, IEquatable<string>, ICloneable
  23. {
  24. // String constructors
  25. // These are special. The implementation methods for these have a different signature from the
  26. // declared constructors.
  27. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  28. public extern String(char[] value);
  29. #if PROJECTN
  30. [DependencyReductionRoot]
  31. #endif
  32. #if !CORECLR
  33. static
  34. #endif
  35. private string Ctor(char[] value)
  36. {
  37. if (value == null || value.Length == 0)
  38. return Empty;
  39. string result = FastAllocateString(value.Length);
  40. unsafe
  41. {
  42. fixed (char* dest = &result._firstChar, source = value)
  43. wstrcpy(dest, source, value.Length);
  44. }
  45. return result;
  46. }
  47. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  48. public extern String(char[] value, int startIndex, int length);
  49. #if PROJECTN
  50. [DependencyReductionRoot]
  51. #endif
  52. #if !CORECLR
  53. static
  54. #endif
  55. private string Ctor(char[] value, int startIndex, int length)
  56. {
  57. if (value == null)
  58. throw new ArgumentNullException(nameof(value));
  59. if (startIndex < 0)
  60. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  61. if (length < 0)
  62. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  63. if (startIndex > value.Length - length)
  64. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
  65. if (length == 0)
  66. return Empty;
  67. string result = FastAllocateString(length);
  68. unsafe
  69. {
  70. fixed (char* dest = &result._firstChar, source = value)
  71. wstrcpy(dest, source + startIndex, length);
  72. }
  73. return result;
  74. }
  75. [CLSCompliant(false)]
  76. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  77. public extern unsafe String(char* value);
  78. #if PROJECTN
  79. [DependencyReductionRoot]
  80. #endif
  81. #if !CORECLR
  82. static
  83. #endif
  84. private unsafe string Ctor(char* ptr)
  85. {
  86. if (ptr == null)
  87. return Empty;
  88. int count = wcslen(ptr);
  89. if (count == 0)
  90. return Empty;
  91. string result = FastAllocateString(count);
  92. fixed (char* dest = &result._firstChar)
  93. wstrcpy(dest, ptr, count);
  94. return result;
  95. }
  96. [CLSCompliant(false)]
  97. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  98. public extern unsafe String(char* value, int startIndex, int length);
  99. #if PROJECTN
  100. [DependencyReductionRoot]
  101. #endif
  102. #if !CORECLR
  103. static
  104. #endif
  105. private unsafe string Ctor(char* ptr, int startIndex, int length)
  106. {
  107. if (length < 0)
  108. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  109. if (startIndex < 0)
  110. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  111. char* pStart = ptr + startIndex;
  112. // overflow check
  113. if (pStart < ptr)
  114. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
  115. if (length == 0)
  116. return Empty;
  117. if (ptr == null)
  118. throw new ArgumentOutOfRangeException(nameof(ptr), SR.ArgumentOutOfRange_PartialWCHAR);
  119. string result = FastAllocateString(length);
  120. fixed (char* dest = &result._firstChar)
  121. wstrcpy(dest, pStart, length);
  122. return result;
  123. }
  124. [CLSCompliant(false)]
  125. [MethodImpl(MethodImplOptions.InternalCall)]
  126. public extern unsafe String(sbyte* value);
  127. #if PROJECTN
  128. [DependencyReductionRoot]
  129. #endif
  130. #if !CORECLR
  131. static
  132. #endif
  133. private unsafe string Ctor(sbyte* value)
  134. {
  135. byte* pb = (byte*)value;
  136. if (pb == null)
  137. return Empty;
  138. int numBytes = strlen((byte*)value);
  139. return CreateStringForSByteConstructor(pb, numBytes);
  140. }
  141. [CLSCompliant(false)]
  142. [MethodImpl(MethodImplOptions.InternalCall)]
  143. public extern unsafe String(sbyte* value, int startIndex, int length);
  144. #if PROJECTN
  145. [DependencyReductionRoot]
  146. #endif
  147. #if !CORECLR
  148. static
  149. #endif
  150. private unsafe string Ctor(sbyte* value, int startIndex, int length)
  151. {
  152. if (startIndex < 0)
  153. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  154. if (length < 0)
  155. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  156. if (value == null)
  157. {
  158. if (length == 0)
  159. return Empty;
  160. throw new ArgumentNullException(nameof(value));
  161. }
  162. byte* pStart = (byte*)(value + startIndex);
  163. // overflow check
  164. if (pStart < value)
  165. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_PartialWCHAR);
  166. return CreateStringForSByteConstructor(pStart, length);
  167. }
  168. // Encoder for String..ctor(sbyte*) and String..ctor(sbyte*, int, int)
  169. private static unsafe string CreateStringForSByteConstructor(byte *pb, int numBytes)
  170. {
  171. Debug.Assert(numBytes >= 0);
  172. Debug.Assert(pb <= (pb + numBytes));
  173. if (numBytes == 0)
  174. return Empty;
  175. #if PLATFORM_WINDOWS
  176. int numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, (char*)null, 0);
  177. if (numCharsRequired == 0)
  178. throw new ArgumentException(SR.Arg_InvalidANSIString);
  179. string newString = FastAllocateString(numCharsRequired);
  180. fixed (char *pFirstChar = &newString._firstChar)
  181. {
  182. numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, pFirstChar, numCharsRequired);
  183. }
  184. if (numCharsRequired == 0)
  185. throw new ArgumentException(SR.Arg_InvalidANSIString);
  186. return newString;
  187. #else
  188. return Encoding.UTF8.GetString(pb, numBytes);
  189. #endif
  190. }
  191. [CLSCompliant(false)]
  192. [MethodImpl(MethodImplOptions.InternalCall)]
  193. public extern unsafe String(sbyte* value, int startIndex, int length, Encoding enc);
  194. #if PROJECTN
  195. [DependencyReductionRoot]
  196. #endif
  197. #if !CORECLR
  198. static
  199. #endif
  200. private unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding enc)
  201. {
  202. if (enc == null)
  203. return new string(value, startIndex, length);
  204. if (length < 0)
  205. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
  206. if (startIndex < 0)
  207. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  208. if (value == null)
  209. {
  210. if (length == 0)
  211. return Empty;
  212. throw new ArgumentNullException(nameof(value));
  213. }
  214. byte* pStart = (byte*)(value + startIndex);
  215. // overflow check
  216. if (pStart < value)
  217. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
  218. return enc.GetString(new ReadOnlySpan<byte>(pStart, length));
  219. }
  220. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  221. public extern String(char c, int count);
  222. #if PROJECTN
  223. [DependencyReductionRoot]
  224. #endif
  225. #if !CORECLR
  226. static
  227. #endif
  228. private string Ctor(char c, int count)
  229. {
  230. if (count <= 0)
  231. {
  232. if (count == 0)
  233. return Empty;
  234. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
  235. }
  236. string result = FastAllocateString(count);
  237. if (c != '\0') // Fast path null char string
  238. {
  239. unsafe
  240. {
  241. fixed (char* dest = &result._firstChar)
  242. {
  243. uint cc = (uint)((c << 16) | c);
  244. uint* dmem = (uint*)dest;
  245. if (count >= 4)
  246. {
  247. count -= 4;
  248. do
  249. {
  250. dmem[0] = cc;
  251. dmem[1] = cc;
  252. dmem += 2;
  253. count -= 4;
  254. } while (count >= 0);
  255. }
  256. if ((count & 2) != 0)
  257. {
  258. *dmem = cc;
  259. dmem++;
  260. }
  261. if ((count & 1) != 0)
  262. ((char*)dmem)[0] = c;
  263. }
  264. }
  265. }
  266. return result;
  267. }
  268. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  269. public extern String(ReadOnlySpan<char> value);
  270. #if PROJECTN
  271. [DependencyReductionRoot]
  272. #endif
  273. #if !CORECLR
  274. static
  275. #endif
  276. private unsafe string Ctor(ReadOnlySpan<char> value)
  277. {
  278. if (value.Length == 0)
  279. return Empty;
  280. string result = FastAllocateString(value.Length);
  281. fixed (char* dest = &result._firstChar, src = &MemoryMarshal.GetReference(value))
  282. wstrcpy(dest, src, value.Length);
  283. return result;
  284. }
  285. public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
  286. {
  287. if (action == null)
  288. throw new ArgumentNullException(nameof(action));
  289. if (length <= 0)
  290. {
  291. if (length == 0)
  292. return Empty;
  293. throw new ArgumentOutOfRangeException(nameof(length));
  294. }
  295. string result = FastAllocateString(length);
  296. action(new Span<char>(ref result.GetRawStringData(), length), state);
  297. return result;
  298. }
  299. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  300. public static implicit operator ReadOnlySpan<char>(string value) =>
  301. value != null ? new ReadOnlySpan<char>(ref value.GetRawStringData(), value.Length) : default;
  302. public object Clone()
  303. {
  304. return this;
  305. }
  306. public static unsafe string Copy(string str)
  307. {
  308. if (str == null)
  309. throw new ArgumentNullException(nameof(str));
  310. string result = FastAllocateString(str.Length);
  311. fixed (char* dest = &result._firstChar, src = &str._firstChar)
  312. wstrcpy(dest, src, str.Length);
  313. return result;
  314. }
  315. // Converts a substring of this string to an array of characters. Copies the
  316. // characters of this string beginning at position sourceIndex and ending at
  317. // sourceIndex + count - 1 to the character array buffer, beginning
  318. // at destinationIndex.
  319. //
  320. public unsafe void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
  321. {
  322. if (destination == null)
  323. throw new ArgumentNullException(nameof(destination));
  324. if (count < 0)
  325. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
  326. if (sourceIndex < 0)
  327. throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
  328. if (count > Length - sourceIndex)
  329. throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_IndexCount);
  330. if (destinationIndex > destination.Length - count || destinationIndex < 0)
  331. throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_IndexCount);
  332. fixed (char* src = &_firstChar, dest = destination)
  333. wstrcpy(dest + destinationIndex, src + sourceIndex, count);
  334. }
  335. // Returns the entire string as an array of characters.
  336. public unsafe char[] ToCharArray()
  337. {
  338. if (Length == 0)
  339. return Array.Empty<char>();
  340. char[] chars = new char[Length];
  341. fixed (char* src = &_firstChar, dest = &chars[0])
  342. wstrcpy(dest, src, Length);
  343. return chars;
  344. }
  345. // Returns a substring of this string as an array of characters.
  346. //
  347. public unsafe char[] ToCharArray(int startIndex, int length)
  348. {
  349. // Range check everything.
  350. if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
  351. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
  352. if (length <= 0)
  353. {
  354. if (length == 0)
  355. return Array.Empty<char>();
  356. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
  357. }
  358. char[] chars = new char[length];
  359. fixed (char* src = &_firstChar, dest = &chars[0])
  360. wstrcpy(dest, src + startIndex, length);
  361. return chars;
  362. }
  363. [NonVersionable]
  364. public static bool IsNullOrEmpty(string value)
  365. {
  366. // Using 0u >= (uint)value.Length rather than
  367. // value.Length == 0 as it will elide the bounds check to
  368. // the first char: value[0] if that is performed following the test
  369. // for the same test cost.
  370. // Ternary operator returning true/false prevents redundant asm generation:
  371. // https://github.com/dotnet/coreclr/issues/914
  372. return (value == null || 0u >= (uint)value.Length) ? true : false;
  373. }
  374. [System.Runtime.CompilerServices.IndexerName("Chars")]
  375. public char this[Index index]
  376. {
  377. get
  378. {
  379. int actualIndex = index.GetOffset(Length);
  380. return this[actualIndex];
  381. }
  382. }
  383. [System.Runtime.CompilerServices.IndexerName("Chars")]
  384. public string this[Range range] => Substring(range);
  385. public static bool IsNullOrWhiteSpace(string value)
  386. {
  387. if (value == null) return true;
  388. for (int i = 0; i < value.Length; i++)
  389. {
  390. if (!char.IsWhiteSpace(value[i])) return false;
  391. }
  392. return true;
  393. }
  394. internal ref char GetRawStringData() => ref _firstChar;
  395. // Helper for encodings so they can talk to our buffer directly
  396. // stringLength must be the exact size we'll expect
  397. internal static unsafe string CreateStringFromEncoding(
  398. byte* bytes, int byteLength, Encoding encoding)
  399. {
  400. Debug.Assert(bytes != null);
  401. Debug.Assert(byteLength >= 0);
  402. // Get our string length
  403. int stringLength = encoding.GetCharCount(bytes, byteLength, null);
  404. Debug.Assert(stringLength >= 0, "stringLength >= 0");
  405. // They gave us an empty string if they needed one
  406. // 0 bytelength might be possible if there's something in an encoder
  407. if (stringLength == 0)
  408. return Empty;
  409. string s = FastAllocateString(stringLength);
  410. fixed (char* pTempChars = &s._firstChar)
  411. {
  412. int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength, null);
  413. Debug.Assert(stringLength == doubleCheck,
  414. "Expected encoding.GetChars to return same length as encoding.GetCharCount");
  415. }
  416. return s;
  417. }
  418. // This is only intended to be used by char.ToString.
  419. // It is necessary to put the code in this class instead of Char, since _firstChar is a private member.
  420. // Making _firstChar internal would be dangerous since it would make it much easier to break String's immutability.
  421. internal static string CreateFromChar(char c)
  422. {
  423. string result = FastAllocateString(1);
  424. result._firstChar = c;
  425. return result;
  426. }
  427. internal static string CreateFromChar(char c1, char c2)
  428. {
  429. string result = FastAllocateString(2);
  430. result._firstChar = c1;
  431. Unsafe.Add(ref result._firstChar, 1) = c2;
  432. return result;
  433. }
  434. internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
  435. {
  436. Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
  437. }
  438. // Returns this string.
  439. public override string ToString()
  440. {
  441. return this;
  442. }
  443. // Returns this string.
  444. public string ToString(IFormatProvider provider)
  445. {
  446. return this;
  447. }
  448. public CharEnumerator GetEnumerator()
  449. {
  450. return new CharEnumerator(this);
  451. }
  452. IEnumerator<char> IEnumerable<char>.GetEnumerator()
  453. {
  454. return new CharEnumerator(this);
  455. }
  456. IEnumerator IEnumerable.GetEnumerator()
  457. {
  458. return new CharEnumerator(this);
  459. }
  460. /// <summary>
  461. /// Returns an enumeration of <see cref="Rune"/> from this string.
  462. /// </summary>
  463. /// <remarks>
  464. /// Invalid sequences will be represented in the enumeration by <see cref="Rune.ReplacementChar"/>.
  465. /// </remarks>
  466. public StringRuneEnumerator EnumerateRunes()
  467. {
  468. return new StringRuneEnumerator(this);
  469. }
  470. internal static unsafe int wcslen(char* ptr)
  471. {
  472. char* end = ptr;
  473. // First make sure our pointer is aligned on a word boundary
  474. int alignment = IntPtr.Size - 1;
  475. // If ptr is at an odd address (e.g. 0x5), this loop will simply iterate all the way
  476. while (((uint)end & (uint)alignment) != 0)
  477. {
  478. if (*end == 0) goto FoundZero;
  479. end++;
  480. }
  481. #if !BIT64
  482. // The following code is (somewhat surprisingly!) significantly faster than a naive loop,
  483. // at least on x86 and the current jit.
  484. // The loop condition below works because if "end[0] & end[1]" is non-zero, that means
  485. // neither operand can have been zero. If is zero, we have to look at the operands individually,
  486. // but we hope this going to fairly rare.
  487. // In general, it would be incorrect to access end[1] if we haven't made sure
  488. // end[0] is non-zero. However, we know the ptr has been aligned by the loop above
  489. // so end[0] and end[1] must be in the same word (and therefore page), so they're either both accessible, or both not.
  490. while ((end[0] & end[1]) != 0 || (end[0] != 0 && end[1] != 0))
  491. {
  492. end += 2;
  493. }
  494. Debug.Assert(end[0] == 0 || end[1] == 0);
  495. if (end[0] != 0) end++;
  496. #else // !BIT64
  497. // Based on https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord
  498. // 64-bit implementation: process 1 ulong (word) at a time
  499. // What we do here is add 0x7fff from each of the
  500. // 4 individual chars within the ulong, using MagicMask.
  501. // If the char > 0 and < 0x8001, it will have its high bit set.
  502. // We then OR with MagicMask, to set all the other bits.
  503. // This will result in all bits set (ulong.MaxValue) for any
  504. // char that fits the above criteria, and something else otherwise.
  505. // Note that for any char > 0x8000, this will be a false
  506. // positive and we will fallback to the slow path and
  507. // check each char individually. This is OK though, since
  508. // we optimize for the common case (ASCII chars, which are < 0x80).
  509. // NOTE: We can access a ulong a time since the ptr is aligned,
  510. // and therefore we're only accessing the same word/page. (See notes
  511. // for the 32-bit version above.)
  512. const ulong MagicMask = 0x7fff7fff7fff7fff;
  513. while (true)
  514. {
  515. ulong word = *(ulong*)end;
  516. word += MagicMask; // cause high bit to be set if not zero, and <= 0x8000
  517. word |= MagicMask; // set everything besides the high bits
  518. if (word == ulong.MaxValue) // 0xffff...
  519. {
  520. // all of the chars have their bits set (and therefore none can be 0)
  521. end += 4;
  522. continue;
  523. }
  524. // at least one of them didn't have their high bit set!
  525. // go through each char and check for 0.
  526. if (end[0] == 0) goto EndAt0;
  527. if (end[1] == 0) goto EndAt1;
  528. if (end[2] == 0) goto EndAt2;
  529. if (end[3] == 0) goto EndAt3;
  530. // if we reached here, it was a false positive-- just continue
  531. end += 4;
  532. }
  533. EndAt3: end++;
  534. EndAt2: end++;
  535. EndAt1: end++;
  536. EndAt0:
  537. #endif // !BIT64
  538. FoundZero:
  539. Debug.Assert(*end == 0);
  540. int count = (int)(end - ptr);
  541. #if BIT64
  542. // Check for overflow
  543. if (ptr + count != end)
  544. throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
  545. #else
  546. Debug.Assert(ptr + count == end);
  547. #endif
  548. return count;
  549. }
  550. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  551. internal static unsafe int strlen(byte* ptr)
  552. {
  553. // IndexOf processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond the null terminator.
  554. int length = SpanHelpers.IndexOf(ref *ptr, (byte)'\0', int.MaxValue);
  555. if (length < 0)
  556. {
  557. ThrowMustBeNullTerminatedString();
  558. }
  559. return length;
  560. }
  561. private static void ThrowMustBeNullTerminatedString()
  562. {
  563. throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
  564. }
  565. //
  566. // IConvertible implementation
  567. //
  568. public TypeCode GetTypeCode()
  569. {
  570. return TypeCode.String;
  571. }
  572. bool IConvertible.ToBoolean(IFormatProvider provider)
  573. {
  574. return Convert.ToBoolean(this, provider);
  575. }
  576. char IConvertible.ToChar(IFormatProvider provider)
  577. {
  578. return Convert.ToChar(this, provider);
  579. }
  580. sbyte IConvertible.ToSByte(IFormatProvider provider)
  581. {
  582. return Convert.ToSByte(this, provider);
  583. }
  584. byte IConvertible.ToByte(IFormatProvider provider)
  585. {
  586. return Convert.ToByte(this, provider);
  587. }
  588. short IConvertible.ToInt16(IFormatProvider provider)
  589. {
  590. return Convert.ToInt16(this, provider);
  591. }
  592. ushort IConvertible.ToUInt16(IFormatProvider provider)
  593. {
  594. return Convert.ToUInt16(this, provider);
  595. }
  596. int IConvertible.ToInt32(IFormatProvider provider)
  597. {
  598. return Convert.ToInt32(this, provider);
  599. }
  600. uint IConvertible.ToUInt32(IFormatProvider provider)
  601. {
  602. return Convert.ToUInt32(this, provider);
  603. }
  604. long IConvertible.ToInt64(IFormatProvider provider)
  605. {
  606. return Convert.ToInt64(this, provider);
  607. }
  608. ulong IConvertible.ToUInt64(IFormatProvider provider)
  609. {
  610. return Convert.ToUInt64(this, provider);
  611. }
  612. float IConvertible.ToSingle(IFormatProvider provider)
  613. {
  614. return Convert.ToSingle(this, provider);
  615. }
  616. double IConvertible.ToDouble(IFormatProvider provider)
  617. {
  618. return Convert.ToDouble(this, provider);
  619. }
  620. decimal IConvertible.ToDecimal(IFormatProvider provider)
  621. {
  622. return Convert.ToDecimal(this, provider);
  623. }
  624. DateTime IConvertible.ToDateTime(IFormatProvider provider)
  625. {
  626. return Convert.ToDateTime(this, provider);
  627. }
  628. object IConvertible.ToType(Type type, IFormatProvider provider)
  629. {
  630. return Convert.DefaultToType((IConvertible)this, type, provider);
  631. }
  632. // Normalization Methods
  633. // These just wrap calls to Normalization class
  634. public bool IsNormalized()
  635. {
  636. return IsNormalized(NormalizationForm.FormC);
  637. }
  638. public bool IsNormalized(NormalizationForm normalizationForm)
  639. {
  640. #if CORECLR
  641. if (this.IsFastSort())
  642. {
  643. // If its FastSort && one of the 4 main forms, then its already normalized
  644. if (normalizationForm == NormalizationForm.FormC ||
  645. normalizationForm == NormalizationForm.FormKC ||
  646. normalizationForm == NormalizationForm.FormD ||
  647. normalizationForm == NormalizationForm.FormKD)
  648. return true;
  649. }
  650. #endif
  651. return Normalization.IsNormalized(this, normalizationForm);
  652. }
  653. public string Normalize()
  654. {
  655. return Normalize(NormalizationForm.FormC);
  656. }
  657. public string Normalize(NormalizationForm normalizationForm)
  658. {
  659. #if CORECLR
  660. if (this.IsAscii())
  661. {
  662. // If its FastSort && one of the 4 main forms, then its already normalized
  663. if (normalizationForm == NormalizationForm.FormC ||
  664. normalizationForm == NormalizationForm.FormKC ||
  665. normalizationForm == NormalizationForm.FormD ||
  666. normalizationForm == NormalizationForm.FormKD)
  667. return this;
  668. }
  669. #endif
  670. return Normalization.Normalize(this, normalizationForm);
  671. }
  672. }
  673. }