String.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  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.Diagnostics.CodeAnalysis;
  9. using System.Globalization;
  10. using System.Runtime.CompilerServices;
  11. using System.Runtime.InteropServices;
  12. using System.Runtime.Versioning;
  13. using System.Text;
  14. using Internal.Runtime.CompilerServices;
  15. namespace System
  16. {
  17. // The String class represents a static string of characters. Many of
  18. // the string methods perform some type of transformation on the current
  19. // instance and return the result as a new string. As with arrays, character
  20. // positions (indices) are zero-based.
  21. [Serializable]
  22. [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
  23. public sealed partial class String : IComparable, IEnumerable, IConvertible, IEnumerable<char>, IComparable<string?>,
  24. // IEquatable<string> is invariant by design. However, the lack of covariance means that String?
  25. // couldn't be used in places constrained to T : IEquatable<String>. As a workaround, until the
  26. // language provides a mechanism for this, we make the generic type argument oblivious, in conjunction
  27. // with making all such constraints oblivious as well.
  28. #nullable disable
  29. IEquatable<string>,
  30. #nullable restore
  31. ICloneable
  32. {
  33. /*
  34. * CONSTRUCTORS
  35. *
  36. * Defining a new constructor for string-like types (like String) requires changes both
  37. * to the managed code below and to the native VM code. See the comment at the top of
  38. * src/vm/ecall.cpp for instructions on how to add new overloads.
  39. */
  40. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  41. public extern String(char[] value);
  42. #if PROJECTN
  43. [DependencyReductionRoot]
  44. #endif
  45. #if !CORECLR
  46. static
  47. #endif
  48. private string Ctor(char[]? value)
  49. {
  50. if (value == null || value.Length == 0)
  51. return Empty;
  52. string result = FastAllocateString(value.Length);
  53. unsafe
  54. {
  55. fixed (char* dest = &result._firstChar, source = value)
  56. wstrcpy(dest, source, value.Length);
  57. }
  58. return result;
  59. }
  60. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  61. public extern String(char[] value, int startIndex, int length);
  62. #if PROJECTN
  63. [DependencyReductionRoot]
  64. #endif
  65. #if !CORECLR
  66. static
  67. #endif
  68. private string Ctor(char[] value, int startIndex, int length)
  69. {
  70. if (value == null)
  71. throw new ArgumentNullException(nameof(value));
  72. if (startIndex < 0)
  73. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  74. if (length < 0)
  75. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  76. if (startIndex > value.Length - length)
  77. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
  78. if (length == 0)
  79. return Empty;
  80. string result = FastAllocateString(length);
  81. unsafe
  82. {
  83. fixed (char* dest = &result._firstChar, source = value)
  84. wstrcpy(dest, source + startIndex, length);
  85. }
  86. return result;
  87. }
  88. [CLSCompliant(false)]
  89. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  90. public extern unsafe String(char* value);
  91. #if PROJECTN
  92. [DependencyReductionRoot]
  93. #endif
  94. #if !CORECLR
  95. static
  96. #endif
  97. private unsafe string Ctor(char* ptr)
  98. {
  99. if (ptr == null)
  100. return Empty;
  101. int count = wcslen(ptr);
  102. if (count == 0)
  103. return Empty;
  104. string result = FastAllocateString(count);
  105. fixed (char* dest = &result._firstChar)
  106. wstrcpy(dest, ptr, count);
  107. return result;
  108. }
  109. [CLSCompliant(false)]
  110. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  111. public extern unsafe String(char* value, int startIndex, int length);
  112. #if PROJECTN
  113. [DependencyReductionRoot]
  114. #endif
  115. #if !CORECLR
  116. static
  117. #endif
  118. private unsafe string Ctor(char* ptr, int startIndex, int length)
  119. {
  120. if (length < 0)
  121. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  122. if (startIndex < 0)
  123. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  124. char* pStart = ptr + startIndex;
  125. // overflow check
  126. if (pStart < ptr)
  127. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
  128. if (length == 0)
  129. return Empty;
  130. if (ptr == null)
  131. throw new ArgumentOutOfRangeException(nameof(ptr), SR.ArgumentOutOfRange_PartialWCHAR);
  132. string result = FastAllocateString(length);
  133. fixed (char* dest = &result._firstChar)
  134. wstrcpy(dest, pStart, length);
  135. return result;
  136. }
  137. [CLSCompliant(false)]
  138. [MethodImpl(MethodImplOptions.InternalCall)]
  139. public extern unsafe String(sbyte* value);
  140. #if PROJECTN
  141. [DependencyReductionRoot]
  142. #endif
  143. #if !CORECLR
  144. static
  145. #endif
  146. private unsafe string Ctor(sbyte* value)
  147. {
  148. byte* pb = (byte*)value;
  149. if (pb == null)
  150. return Empty;
  151. int numBytes = strlen((byte*)value);
  152. return CreateStringForSByteConstructor(pb, numBytes);
  153. }
  154. [CLSCompliant(false)]
  155. [MethodImpl(MethodImplOptions.InternalCall)]
  156. public extern unsafe String(sbyte* value, int startIndex, int length);
  157. #if PROJECTN
  158. [DependencyReductionRoot]
  159. #endif
  160. #if !CORECLR
  161. static
  162. #endif
  163. private unsafe string Ctor(sbyte* value, int startIndex, int length)
  164. {
  165. if (startIndex < 0)
  166. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  167. if (length < 0)
  168. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NegativeLength);
  169. if (value == null)
  170. {
  171. if (length == 0)
  172. return Empty;
  173. throw new ArgumentNullException(nameof(value));
  174. }
  175. byte* pStart = (byte*)(value + startIndex);
  176. // overflow check
  177. if (pStart < value)
  178. throw new ArgumentOutOfRangeException(nameof(value), SR.ArgumentOutOfRange_PartialWCHAR);
  179. return CreateStringForSByteConstructor(pStart, length);
  180. }
  181. // Encoder for String..ctor(sbyte*) and String..ctor(sbyte*, int, int)
  182. private static unsafe string CreateStringForSByteConstructor(byte *pb, int numBytes)
  183. {
  184. Debug.Assert(numBytes >= 0);
  185. Debug.Assert(pb <= (pb + numBytes));
  186. if (numBytes == 0)
  187. return Empty;
  188. #if PLATFORM_WINDOWS
  189. int numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, (char*)null, 0);
  190. if (numCharsRequired == 0)
  191. throw new ArgumentException(SR.Arg_InvalidANSIString);
  192. string newString = FastAllocateString(numCharsRequired);
  193. fixed (char *pFirstChar = &newString._firstChar)
  194. {
  195. numCharsRequired = Interop.Kernel32.MultiByteToWideChar(Interop.Kernel32.CP_ACP, Interop.Kernel32.MB_PRECOMPOSED, pb, numBytes, pFirstChar, numCharsRequired);
  196. }
  197. if (numCharsRequired == 0)
  198. throw new ArgumentException(SR.Arg_InvalidANSIString);
  199. return newString;
  200. #else
  201. return Encoding.UTF8.GetString(pb, numBytes);
  202. #endif
  203. }
  204. [CLSCompliant(false)]
  205. [MethodImpl(MethodImplOptions.InternalCall)]
  206. public extern unsafe String(sbyte* value, int startIndex, int length, Encoding enc);
  207. #if PROJECTN
  208. [DependencyReductionRoot]
  209. #endif
  210. #if !CORECLR
  211. static
  212. #endif
  213. private unsafe string Ctor(sbyte* value, int startIndex, int length, Encoding? enc)
  214. {
  215. if (enc == null)
  216. return new string(value, startIndex, length);
  217. if (length < 0)
  218. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_NeedNonNegNum);
  219. if (startIndex < 0)
  220. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_StartIndex);
  221. if (value == null)
  222. {
  223. if (length == 0)
  224. return Empty;
  225. throw new ArgumentNullException(nameof(value));
  226. }
  227. byte* pStart = (byte*)(value + startIndex);
  228. // overflow check
  229. if (pStart < value)
  230. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_PartialWCHAR);
  231. return enc.GetString(new ReadOnlySpan<byte>(pStart, length));
  232. }
  233. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  234. public extern String(char c, int count);
  235. #if PROJECTN
  236. [DependencyReductionRoot]
  237. #endif
  238. #if !CORECLR
  239. static
  240. #endif
  241. private string Ctor(char c, int count)
  242. {
  243. if (count <= 0)
  244. {
  245. if (count == 0)
  246. return Empty;
  247. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
  248. }
  249. string result = FastAllocateString(count);
  250. if (c != '\0') // Fast path null char string
  251. {
  252. unsafe
  253. {
  254. fixed (char* dest = &result._firstChar)
  255. {
  256. uint cc = (uint)((c << 16) | c);
  257. uint* dmem = (uint*)dest;
  258. if (count >= 4)
  259. {
  260. count -= 4;
  261. do
  262. {
  263. dmem[0] = cc;
  264. dmem[1] = cc;
  265. dmem += 2;
  266. count -= 4;
  267. } while (count >= 0);
  268. }
  269. if ((count & 2) != 0)
  270. {
  271. *dmem = cc;
  272. dmem++;
  273. }
  274. if ((count & 1) != 0)
  275. ((char*)dmem)[0] = c;
  276. }
  277. }
  278. }
  279. return result;
  280. }
  281. [MethodImplAttribute(MethodImplOptions.InternalCall)]
  282. public extern String(ReadOnlySpan<char> value);
  283. #if PROJECTN
  284. [DependencyReductionRoot]
  285. #endif
  286. #if !CORECLR
  287. static
  288. #endif
  289. private unsafe string Ctor(ReadOnlySpan<char> value)
  290. {
  291. if (value.Length == 0)
  292. return Empty;
  293. string result = FastAllocateString(value.Length);
  294. Buffer.Memmove(ref result._firstChar, ref MemoryMarshal.GetReference(value), (uint)value.Length);
  295. return result;
  296. }
  297. public static string Create<TState>(int length, TState state, SpanAction<char, TState> action)
  298. {
  299. if (action == null)
  300. throw new ArgumentNullException(nameof(action));
  301. if (length <= 0)
  302. {
  303. if (length == 0)
  304. return Empty;
  305. throw new ArgumentOutOfRangeException(nameof(length));
  306. }
  307. string result = FastAllocateString(length);
  308. action(new Span<char>(ref result.GetRawStringData(), length), state);
  309. return result;
  310. }
  311. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  312. public static implicit operator ReadOnlySpan<char>(string? value) =>
  313. value != null ? new ReadOnlySpan<char>(ref value.GetRawStringData(), value.Length) : default;
  314. public object Clone()
  315. {
  316. return this;
  317. }
  318. public static unsafe string Copy(string str)
  319. {
  320. if (str == null)
  321. throw new ArgumentNullException(nameof(str));
  322. string result = FastAllocateString(str.Length);
  323. fixed (char* dest = &result._firstChar, src = &str._firstChar)
  324. wstrcpy(dest, src, str.Length);
  325. return result;
  326. }
  327. // Converts a substring of this string to an array of characters. Copies the
  328. // characters of this string beginning at position sourceIndex and ending at
  329. // sourceIndex + count - 1 to the character array buffer, beginning
  330. // at destinationIndex.
  331. //
  332. public unsafe void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
  333. {
  334. if (destination == null)
  335. throw new ArgumentNullException(nameof(destination));
  336. if (count < 0)
  337. throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NegativeCount);
  338. if (sourceIndex < 0)
  339. throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_Index);
  340. if (count > Length - sourceIndex)
  341. throw new ArgumentOutOfRangeException(nameof(sourceIndex), SR.ArgumentOutOfRange_IndexCount);
  342. if (destinationIndex > destination.Length - count || destinationIndex < 0)
  343. throw new ArgumentOutOfRangeException(nameof(destinationIndex), SR.ArgumentOutOfRange_IndexCount);
  344. fixed (char* src = &_firstChar, dest = destination)
  345. wstrcpy(dest + destinationIndex, src + sourceIndex, count);
  346. }
  347. // Returns the entire string as an array of characters.
  348. public unsafe char[] ToCharArray()
  349. {
  350. if (Length == 0)
  351. return Array.Empty<char>();
  352. char[] chars = new char[Length];
  353. fixed (char* src = &_firstChar, dest = &chars[0])
  354. wstrcpy(dest, src, Length);
  355. return chars;
  356. }
  357. // Returns a substring of this string as an array of characters.
  358. //
  359. public unsafe char[] ToCharArray(int startIndex, int length)
  360. {
  361. // Range check everything.
  362. if (startIndex < 0 || startIndex > Length || startIndex > Length - length)
  363. throw new ArgumentOutOfRangeException(nameof(startIndex), SR.ArgumentOutOfRange_Index);
  364. if (length <= 0)
  365. {
  366. if (length == 0)
  367. return Array.Empty<char>();
  368. throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index);
  369. }
  370. char[] chars = new char[length];
  371. fixed (char* src = &_firstChar, dest = &chars[0])
  372. wstrcpy(dest, src + startIndex, length);
  373. return chars;
  374. }
  375. [NonVersionable]
  376. public static bool IsNullOrEmpty([NotNullWhen(false)] string? value)
  377. {
  378. // Using 0u >= (uint)value.Length rather than
  379. // value.Length == 0 as it will elide the bounds check to
  380. // the first char: value[0] if that is performed following the test
  381. // for the same test cost.
  382. // Ternary operator returning true/false prevents redundant asm generation:
  383. // https://github.com/dotnet/coreclr/issues/914
  384. return (value == null || 0u >= (uint)value.Length) ? true : false;
  385. }
  386. public static bool IsNullOrWhiteSpace([NotNullWhen(false)] string? value)
  387. {
  388. if (value == null) return true;
  389. for (int i = 0; i < value.Length; i++)
  390. {
  391. if (!char.IsWhiteSpace(value[i])) return false;
  392. }
  393. return true;
  394. }
  395. /// <summary>
  396. /// Returns a reference to the first element of the String. If the string is null, an access will throw a NullReferenceException.
  397. /// </summary>
  398. [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
  399. [NonVersionable]
  400. public ref readonly char GetPinnableReference() => ref _firstChar;
  401. internal ref char GetRawStringData() => ref _firstChar;
  402. // Helper for encodings so they can talk to our buffer directly
  403. // stringLength must be the exact size we'll expect
  404. internal static unsafe string CreateStringFromEncoding(
  405. byte* bytes, int byteLength, Encoding encoding)
  406. {
  407. Debug.Assert(bytes != null);
  408. Debug.Assert(byteLength >= 0);
  409. // Get our string length
  410. int stringLength = encoding.GetCharCount(bytes, byteLength);
  411. Debug.Assert(stringLength >= 0, "stringLength >= 0");
  412. // They gave us an empty string if they needed one
  413. // 0 bytelength might be possible if there's something in an encoder
  414. if (stringLength == 0)
  415. return Empty;
  416. string s = FastAllocateString(stringLength);
  417. fixed (char* pTempChars = &s._firstChar)
  418. {
  419. int doubleCheck = encoding.GetChars(bytes, byteLength, pTempChars, stringLength);
  420. Debug.Assert(stringLength == doubleCheck,
  421. "Expected encoding.GetChars to return same length as encoding.GetCharCount");
  422. }
  423. return s;
  424. }
  425. // This is only intended to be used by char.ToString.
  426. // It is necessary to put the code in this class instead of Char, since _firstChar is a private member.
  427. // Making _firstChar internal would be dangerous since it would make it much easier to break String's immutability.
  428. internal static string CreateFromChar(char c)
  429. {
  430. string result = FastAllocateString(1);
  431. result._firstChar = c;
  432. return result;
  433. }
  434. internal static string CreateFromChar(char c1, char c2)
  435. {
  436. string result = FastAllocateString(2);
  437. result._firstChar = c1;
  438. Unsafe.Add(ref result._firstChar, 1) = c2;
  439. return result;
  440. }
  441. internal static unsafe void wstrcpy(char* dmem, char* smem, int charCount)
  442. {
  443. Buffer.Memmove((byte*)dmem, (byte*)smem, ((uint)charCount) * 2);
  444. }
  445. // Returns this string.
  446. public override string ToString()
  447. {
  448. return this;
  449. }
  450. // Returns this string.
  451. public string ToString(IFormatProvider? provider)
  452. {
  453. return this;
  454. }
  455. public CharEnumerator GetEnumerator()
  456. {
  457. return new CharEnumerator(this);
  458. }
  459. IEnumerator<char> IEnumerable<char>.GetEnumerator()
  460. {
  461. return new CharEnumerator(this);
  462. }
  463. IEnumerator IEnumerable.GetEnumerator()
  464. {
  465. return new CharEnumerator(this);
  466. }
  467. /// <summary>
  468. /// Returns an enumeration of <see cref="Rune"/> from this string.
  469. /// </summary>
  470. /// <remarks>
  471. /// Invalid sequences will be represented in the enumeration by <see cref="Rune.ReplacementChar"/>.
  472. /// </remarks>
  473. public StringRuneEnumerator EnumerateRunes()
  474. {
  475. return new StringRuneEnumerator(this);
  476. }
  477. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  478. internal static unsafe int wcslen(char* ptr)
  479. {
  480. // IndexOf processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond the null terminator.
  481. int length = SpanHelpers.IndexOf(ref *ptr, '\0', int.MaxValue);
  482. if (length < 0)
  483. {
  484. ThrowMustBeNullTerminatedString();
  485. }
  486. return length;
  487. }
  488. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  489. internal static unsafe int strlen(byte* ptr)
  490. {
  491. // IndexOf processes memory in aligned chunks, and thus it won't crash even if it accesses memory beyond the null terminator.
  492. int length = SpanHelpers.IndexOf(ref *ptr, (byte)'\0', int.MaxValue);
  493. if (length < 0)
  494. {
  495. ThrowMustBeNullTerminatedString();
  496. }
  497. return length;
  498. }
  499. [DoesNotReturn]
  500. private static void ThrowMustBeNullTerminatedString()
  501. {
  502. throw new ArgumentException(SR.Arg_MustBeNullTerminatedString);
  503. }
  504. //
  505. // IConvertible implementation
  506. //
  507. public TypeCode GetTypeCode()
  508. {
  509. return TypeCode.String;
  510. }
  511. bool IConvertible.ToBoolean(IFormatProvider? provider)
  512. {
  513. return Convert.ToBoolean(this, provider);
  514. }
  515. char IConvertible.ToChar(IFormatProvider? provider)
  516. {
  517. return Convert.ToChar(this, provider);
  518. }
  519. sbyte IConvertible.ToSByte(IFormatProvider? provider)
  520. {
  521. return Convert.ToSByte(this, provider);
  522. }
  523. byte IConvertible.ToByte(IFormatProvider? provider)
  524. {
  525. return Convert.ToByte(this, provider);
  526. }
  527. short IConvertible.ToInt16(IFormatProvider? provider)
  528. {
  529. return Convert.ToInt16(this, provider);
  530. }
  531. ushort IConvertible.ToUInt16(IFormatProvider? provider)
  532. {
  533. return Convert.ToUInt16(this, provider);
  534. }
  535. int IConvertible.ToInt32(IFormatProvider? provider)
  536. {
  537. return Convert.ToInt32(this, provider);
  538. }
  539. uint IConvertible.ToUInt32(IFormatProvider? provider)
  540. {
  541. return Convert.ToUInt32(this, provider);
  542. }
  543. long IConvertible.ToInt64(IFormatProvider? provider)
  544. {
  545. return Convert.ToInt64(this, provider);
  546. }
  547. ulong IConvertible.ToUInt64(IFormatProvider? provider)
  548. {
  549. return Convert.ToUInt64(this, provider);
  550. }
  551. float IConvertible.ToSingle(IFormatProvider? provider)
  552. {
  553. return Convert.ToSingle(this, provider);
  554. }
  555. double IConvertible.ToDouble(IFormatProvider? provider)
  556. {
  557. return Convert.ToDouble(this, provider);
  558. }
  559. decimal IConvertible.ToDecimal(IFormatProvider? provider)
  560. {
  561. return Convert.ToDecimal(this, provider);
  562. }
  563. DateTime IConvertible.ToDateTime(IFormatProvider? provider)
  564. {
  565. return Convert.ToDateTime(this, provider);
  566. }
  567. object IConvertible.ToType(Type type, IFormatProvider? provider)
  568. {
  569. return Convert.DefaultToType((IConvertible)this, type, provider);
  570. }
  571. // Normalization Methods
  572. // These just wrap calls to Normalization class
  573. public bool IsNormalized()
  574. {
  575. return IsNormalized(NormalizationForm.FormC);
  576. }
  577. public bool IsNormalized(NormalizationForm normalizationForm)
  578. {
  579. if (this.IsAscii())
  580. {
  581. // If its ASCII && one of the 4 main forms, then its already normalized
  582. if (normalizationForm == NormalizationForm.FormC ||
  583. normalizationForm == NormalizationForm.FormKC ||
  584. normalizationForm == NormalizationForm.FormD ||
  585. normalizationForm == NormalizationForm.FormKD)
  586. return true;
  587. }
  588. return Normalization.IsNormalized(this, normalizationForm);
  589. }
  590. public string Normalize()
  591. {
  592. return Normalize(NormalizationForm.FormC);
  593. }
  594. public string Normalize(NormalizationForm normalizationForm)
  595. {
  596. if (this.IsAscii())
  597. {
  598. // If its ASCII && one of the 4 main forms, then its already normalized
  599. if (normalizationForm == NormalizationForm.FormC ||
  600. normalizationForm == NormalizationForm.FormKC ||
  601. normalizationForm == NormalizationForm.FormD ||
  602. normalizationForm == NormalizationForm.FormKD)
  603. return this;
  604. }
  605. return Normalization.Normalize(this, normalizationForm);
  606. }
  607. private unsafe bool IsAscii()
  608. {
  609. fixed (char* str = &_firstChar)
  610. {
  611. return ASCIIUtility.GetIndexOfFirstNonAsciiChar(str, (uint)Length) == (uint)Length;
  612. }
  613. }
  614. }
  615. }