PageRenderTime 32ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/class/corlib/System/String.cs

https://bitbucket.org/danipen/mono
C# | 3169 lines | 2543 code | 502 blank | 124 comment | 986 complexity | f1d195da3f6195b94314a15acac66761 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. //
  2. // System.String.cs
  3. //
  4. // Authors:
  5. // Patrik Torstensson
  6. // Jeffrey Stedfast (fejj@ximian.com)
  7. // Dan Lewis (dihlewis@yahoo.co.uk)
  8. // Sebastien Pouliot <sebastien@ximian.com>
  9. // Marek Safar (marek.safar@seznam.cz)
  10. // Andreas Nahr (Classdevelopment@A-SoftTech.com)
  11. //
  12. // (C) 2001 Ximian, Inc. http://www.ximian.com
  13. // Copyright (C) 2004-2005 Novell (http://www.novell.com)
  14. // Copyright (c) 2012 Xamarin, Inc (http://www.xamarin.com)
  15. //
  16. // Permission is hereby granted, free of charge, to any person obtaining
  17. // a copy of this software and associated documentation files (the
  18. // "Software"), to deal in the Software without restriction, including
  19. // without limitation the rights to use, copy, modify, merge, publish,
  20. // distribute, sublicense, and/or sell copies of the Software, and to
  21. // permit persons to whom the Software is furnished to do so, subject to
  22. // the following conditions:
  23. //
  24. // The above copyright notice and this permission notice shall be
  25. // included in all copies or substantial portions of the Software.
  26. //
  27. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  28. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  29. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  30. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  31. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  32. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  33. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  34. //
  35. //
  36. //
  37. // This class contains all implementation for culture-insensitive methods.
  38. // Culture-sensitive methods are implemented in the System.Globalization or
  39. // Mono.Globalization namespace.
  40. //
  41. // Ensure that argument checks on methods don't overflow
  42. //
  43. using System.Text;
  44. using System.Collections;
  45. using System.Globalization;
  46. using System.Runtime.CompilerServices;
  47. using System.Collections.Generic;
  48. using System.Runtime.ConstrainedExecution;
  49. using System.Runtime.InteropServices;
  50. using Mono.Globalization.Unicode;
  51. namespace System
  52. {
  53. [Serializable]
  54. [ComVisible (true)]
  55. [StructLayout (LayoutKind.Sequential)]
  56. public sealed class String : IConvertible, ICloneable, IEnumerable, IComparable, IComparable<String>, IEquatable <String>, IEnumerable<char>
  57. {
  58. [NonSerialized] private int length;
  59. [NonSerialized] private char start_char;
  60. public static readonly String Empty = "";
  61. internal static readonly int LOS_limit = GetLOSLimit ();
  62. public static unsafe bool Equals (string a, string b)
  63. {
  64. if ((a as object) == (b as object))
  65. return true;
  66. if (a == null || b == null)
  67. return false;
  68. int len = a.length;
  69. if (len != b.length)
  70. return false;
  71. fixed (char* s1 = &a.start_char, s2 = &b.start_char) {
  72. char* s1_ptr = s1;
  73. char* s2_ptr = s2;
  74. while (len >= 8) {
  75. if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
  76. ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1] ||
  77. ((int*)s1_ptr)[2] != ((int*)s2_ptr)[2] ||
  78. ((int*)s1_ptr)[3] != ((int*)s2_ptr)[3])
  79. return false;
  80. s1_ptr += 8;
  81. s2_ptr += 8;
  82. len -= 8;
  83. }
  84. if (len >= 4) {
  85. if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0] ||
  86. ((int*)s1_ptr)[1] != ((int*)s2_ptr)[1])
  87. return false;
  88. s1_ptr += 4;
  89. s2_ptr += 4;
  90. len -= 4;
  91. }
  92. if (len > 1) {
  93. if (((int*)s1_ptr)[0] != ((int*)s2_ptr)[0])
  94. return false;
  95. s1_ptr += 2;
  96. s2_ptr += 2;
  97. len -= 2;
  98. }
  99. return len == 0 || *s1_ptr == *s2_ptr;
  100. }
  101. }
  102. public static bool operator == (String a, String b)
  103. {
  104. return Equals (a, b);
  105. }
  106. public static bool operator != (String a, String b)
  107. {
  108. return !Equals (a, b);
  109. }
  110. [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
  111. public override bool Equals (Object obj)
  112. {
  113. return Equals (this, obj as String);
  114. }
  115. [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
  116. public bool Equals (String value)
  117. {
  118. return Equals (this, value);
  119. }
  120. [IndexerName ("Chars")]
  121. public unsafe char this [int index] {
  122. get {
  123. if (index < 0 || index >= length)
  124. throw new IndexOutOfRangeException ();
  125. fixed (char* c = &start_char)
  126. return c[index];
  127. }
  128. }
  129. public Object Clone ()
  130. {
  131. return this;
  132. }
  133. public TypeCode GetTypeCode ()
  134. {
  135. return TypeCode.String;
  136. }
  137. public unsafe void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
  138. {
  139. if (destination == null)
  140. throw new ArgumentNullException ("destination");
  141. if (sourceIndex < 0)
  142. throw new ArgumentOutOfRangeException ("sourceIndex", "Cannot be negative");
  143. if (destinationIndex < 0)
  144. throw new ArgumentOutOfRangeException ("destinationIndex", "Cannot be negative.");
  145. if (count < 0)
  146. throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
  147. if (sourceIndex > Length - count)
  148. throw new ArgumentOutOfRangeException ("sourceIndex", "sourceIndex + count > Length");
  149. if (destinationIndex > destination.Length - count)
  150. throw new ArgumentOutOfRangeException ("destinationIndex", "destinationIndex + count > destination.Length");
  151. fixed (char* dest = destination, src = this)
  152. CharCopy (dest + destinationIndex, src + sourceIndex, count);
  153. }
  154. public char[] ToCharArray ()
  155. {
  156. return ToCharArray (0, length);
  157. }
  158. public unsafe char[] ToCharArray (int startIndex, int length)
  159. {
  160. if (startIndex < 0)
  161. throw new ArgumentOutOfRangeException ("startIndex", "< 0");
  162. if (length < 0)
  163. throw new ArgumentOutOfRangeException ("length", "< 0");
  164. if (startIndex > this.length - length)
  165. throw new ArgumentOutOfRangeException ("startIndex", "Must be greater than the length of the string.");
  166. char[] tmp = new char [length];
  167. fixed (char* dest = tmp, src = this)
  168. CharCopy (dest, src + startIndex, length);
  169. return tmp;
  170. }
  171. public String [] Split (params char [] separator)
  172. {
  173. return Split (separator, int.MaxValue, 0);
  174. }
  175. public String[] Split (char[] separator, int count)
  176. {
  177. return Split (separator, count, 0);
  178. }
  179. [ComVisible (false)]
  180. public String[] Split (char[] separator, StringSplitOptions options)
  181. {
  182. return Split (separator, Int32.MaxValue, options);
  183. }
  184. [ComVisible (false)]
  185. public String[] Split (char[] separator, int count, StringSplitOptions options)
  186. {
  187. if (count < 0)
  188. throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
  189. if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
  190. throw new ArgumentException ("Illegal enum value: " + options + ".");
  191. if (Length == 0 && (options & StringSplitOptions.RemoveEmptyEntries) != 0)
  192. return EmptyArray<string>.Value;
  193. if (count <= 1) {
  194. return count == 0 ?
  195. EmptyArray<string>.Value :
  196. new String[1] { this };
  197. }
  198. return SplitByCharacters (separator, count, options != 0);
  199. }
  200. [ComVisible (false)]
  201. public String[] Split (string[] separator, StringSplitOptions options)
  202. {
  203. return Split (separator, Int32.MaxValue, options);
  204. }
  205. [ComVisible (false)]
  206. public String[] Split (string[] separator, int count, StringSplitOptions options)
  207. {
  208. if (count < 0)
  209. throw new ArgumentOutOfRangeException ("count", "Count cannot be less than zero.");
  210. if ((options != StringSplitOptions.None) && (options != StringSplitOptions.RemoveEmptyEntries))
  211. throw new ArgumentException ("Illegal enum value: " + options + ".");
  212. if (count <= 1) {
  213. return count == 0 ?
  214. EmptyArray<string>.Value :
  215. new String[1] { this };
  216. }
  217. bool removeEmpty = (options & StringSplitOptions.RemoveEmptyEntries) != 0;
  218. if (separator == null || separator.Length == 0)
  219. return SplitByCharacters (null, count, removeEmpty);
  220. if (Length == 0 && removeEmpty)
  221. return EmptyArray<string>.Value;
  222. List<String> arr = new List<String> ();
  223. int pos = 0;
  224. int matchCount = 0;
  225. while (pos < this.Length) {
  226. int matchIndex = -1;
  227. int matchPos = Int32.MaxValue;
  228. // Find the first position where any of the separators matches
  229. for (int i = 0; i < separator.Length; ++i) {
  230. string sep = separator [i];
  231. if (sep == null || sep.Length == 0)
  232. continue;
  233. int match = IndexOfOrdinalUnchecked (sep, pos, length - pos);
  234. if (match >= 0 && match < matchPos) {
  235. matchIndex = i;
  236. matchPos = match;
  237. }
  238. }
  239. if (matchIndex == -1)
  240. break;
  241. if (!(matchPos == pos && removeEmpty)) {
  242. if (arr.Count == count - 1)
  243. break;
  244. arr.Add (this.Substring (pos, matchPos - pos));
  245. }
  246. pos = matchPos + separator [matchIndex].Length;
  247. matchCount ++;
  248. }
  249. if (matchCount == 0)
  250. return new String [] { this };
  251. // string contained only separators
  252. if (removeEmpty && matchCount != 0 && pos == this.Length && arr.Count == 0)
  253. return EmptyArray<string>.Value;
  254. if (!(removeEmpty && pos == this.Length))
  255. arr.Add (this.Substring (pos));
  256. return arr.ToArray ();
  257. }
  258. // .NET 2.0 compatibility only
  259. #if !NET_4_0 && !MOBILE
  260. static readonly char[] WhiteChars = {
  261. (char) 0x9, (char) 0xA, (char) 0xB, (char) 0xC, (char) 0xD,
  262. (char) 0x85, (char) 0x1680, (char) 0x2028, (char) 0x2029,
  263. (char) 0x20, (char) 0xA0, (char) 0x2000, (char) 0x2001, (char) 0x2002, (char) 0x2003, (char) 0x2004,
  264. (char) 0x2005, (char) 0x2006, (char) 0x2007, (char) 0x2008, (char) 0x2009, (char) 0x200A, (char) 0x200B,
  265. (char) 0x3000, (char) 0xFEFF
  266. };
  267. #endif
  268. unsafe string[] SplitByCharacters (char[] sep, int count, bool removeEmpty)
  269. {
  270. #if !NET_4_0 && !MOBILE
  271. if (sep == null || sep.Length == 0)
  272. sep = WhiteChars;
  273. #endif
  274. int[] split_points = null;
  275. int total_points = 0;
  276. --count;
  277. if (sep == null || sep.Length == 0) {
  278. fixed (char* src = this) {
  279. char* src_ptr = src;
  280. int len = Length;
  281. while (len > 0) {
  282. if (char.IsWhiteSpace (*src_ptr++)) {
  283. if (split_points == null) {
  284. split_points = new int[8];
  285. } else if (split_points.Length == total_points) {
  286. Array.Resize (ref split_points, split_points.Length * 2);
  287. }
  288. split_points[total_points++] = Length - len;
  289. if (total_points == count && !removeEmpty)
  290. break;
  291. }
  292. --len;
  293. }
  294. }
  295. } else {
  296. fixed (char* src = this) {
  297. fixed (char* sep_src = sep) {
  298. char* src_ptr = src;
  299. char* sep_ptr_end = sep_src + sep.Length;
  300. int len = Length;
  301. while (len > 0) {
  302. char* sep_ptr = sep_src;
  303. do {
  304. if (*sep_ptr++ == *src_ptr) {
  305. if (split_points == null) {
  306. split_points = new int[8];
  307. } else if (split_points.Length == total_points) {
  308. Array.Resize (ref split_points, split_points.Length * 2);
  309. }
  310. split_points[total_points++] = Length - len;
  311. if (total_points == count && !removeEmpty)
  312. len = 0;
  313. break;
  314. }
  315. } while (sep_ptr != sep_ptr_end);
  316. ++src_ptr;
  317. --len;
  318. }
  319. }
  320. }
  321. }
  322. if (total_points == 0)
  323. return new string[] { this };
  324. var res = new string[Math.Min (total_points, count) + 1];
  325. int prev_index = 0;
  326. int i = 0;
  327. if (!removeEmpty) {
  328. for (; i < total_points; ++i) {
  329. var start = split_points[i];
  330. res[i] = SubstringUnchecked (prev_index, start - prev_index);
  331. prev_index = start + 1;
  332. }
  333. res[i] = SubstringUnchecked (prev_index, Length - prev_index);
  334. } else {
  335. int used = 0;
  336. int length;
  337. for (; i < total_points; ++i) {
  338. var start = split_points[i];
  339. length = start - prev_index;
  340. if (length != 0) {
  341. if (used == count)
  342. break;
  343. res[used++] = SubstringUnchecked (prev_index, length);
  344. }
  345. prev_index = start + 1;
  346. }
  347. length = Length - prev_index;
  348. if (length != 0)
  349. res[used++] = SubstringUnchecked (prev_index, length);
  350. if (used != res.Length)
  351. Array.Resize (ref res, used);
  352. }
  353. return res;
  354. }
  355. public String Substring (int startIndex)
  356. {
  357. if (startIndex == 0)
  358. return this;
  359. if (startIndex < 0 || startIndex > this.length)
  360. throw new ArgumentOutOfRangeException ("startIndex");
  361. return SubstringUnchecked (startIndex, this.length - startIndex);
  362. }
  363. public String Substring (int startIndex, int length)
  364. {
  365. if (length < 0)
  366. throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
  367. if (startIndex < 0)
  368. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
  369. if (startIndex > this.length)
  370. throw new ArgumentOutOfRangeException ("startIndex", "Cannot exceed length of string.");
  371. if (startIndex > this.length - length)
  372. throw new ArgumentOutOfRangeException ("length", "startIndex + length cannot exceed length of string.");
  373. if (startIndex == 0 && length == this.length)
  374. return this;
  375. return SubstringUnchecked (startIndex, length);
  376. }
  377. // This method is used by StringBuilder.ToString() and is expected to
  378. // always create a new string object (or return String.Empty).
  379. internal unsafe String SubstringUnchecked (int startIndex, int length)
  380. {
  381. if (length == 0)
  382. return Empty;
  383. string tmp = InternalAllocateStr (length);
  384. fixed (char* dest = tmp, src = this) {
  385. CharCopy (dest, src + startIndex, length);
  386. }
  387. return tmp;
  388. }
  389. public String Trim ()
  390. {
  391. if (length == 0)
  392. return Empty;
  393. int start = FindNotWhiteSpace (0, length, 1);
  394. if (start == length)
  395. return Empty;
  396. int end = FindNotWhiteSpace (length - 1, start, -1);
  397. int newLength = end - start + 1;
  398. if (newLength == length)
  399. return this;
  400. return SubstringUnchecked (start, newLength);
  401. }
  402. public String Trim (params char[] trimChars)
  403. {
  404. if (trimChars == null || trimChars.Length == 0)
  405. return Trim ();
  406. if (length == 0)
  407. return Empty;
  408. int start = FindNotInTable (0, length, 1, trimChars);
  409. if (start == length)
  410. return Empty;
  411. int end = FindNotInTable (length - 1, start, -1, trimChars);
  412. int newLength = end - start + 1;
  413. if (newLength == length)
  414. return this;
  415. return SubstringUnchecked (start, newLength);
  416. }
  417. public String TrimStart (params char[] trimChars)
  418. {
  419. if (length == 0)
  420. return Empty;
  421. int start;
  422. if (trimChars == null || trimChars.Length == 0)
  423. start = FindNotWhiteSpace (0, length, 1);
  424. else
  425. start = FindNotInTable (0, length, 1, trimChars);
  426. if (start == 0)
  427. return this;
  428. return SubstringUnchecked (start, length - start);
  429. }
  430. public String TrimEnd (params char[] trimChars)
  431. {
  432. if (length == 0)
  433. return Empty;
  434. int end;
  435. if (trimChars == null || trimChars.Length == 0)
  436. end = FindNotWhiteSpace (length - 1, -1, -1);
  437. else
  438. end = FindNotInTable (length - 1, -1, -1, trimChars);
  439. end++;
  440. if (end == length)
  441. return this;
  442. return SubstringUnchecked (0, end);
  443. }
  444. unsafe int FindNotWhiteSpace (int pos, int target, int change)
  445. {
  446. #if NET_4_0
  447. fixed (char* src = this) {
  448. while (pos != target) {
  449. if (!char.IsWhiteSpace (src[pos]))
  450. return pos;
  451. pos += change;
  452. }
  453. }
  454. #else
  455. while (pos != target) {
  456. char c = this[pos];
  457. if (c < 0x85) {
  458. if (c != 0x20) {
  459. if (c < 0x9 || c > 0xD)
  460. return pos;
  461. }
  462. }
  463. else {
  464. if (c != 0xA0 && c != 0xFEFF && c != 0x3000) {
  465. if (c != 0x85 && c != 0x1680 && c != 0x2028 && c != 0x2029)
  466. if (c < 0x2000 || c > 0x200B)
  467. return pos;
  468. }
  469. }
  470. pos += change;
  471. }
  472. #endif
  473. return pos;
  474. }
  475. private unsafe int FindNotInTable (int pos, int target, int change, char[] table)
  476. {
  477. fixed (char* tablePtr = table, thisPtr = this) {
  478. while (pos != target) {
  479. char c = thisPtr[pos];
  480. int x = 0;
  481. while (x < table.Length) {
  482. if (c == tablePtr[x])
  483. break;
  484. x++;
  485. }
  486. if (x == table.Length)
  487. return pos;
  488. pos += change;
  489. }
  490. }
  491. return pos;
  492. }
  493. public static int Compare (String strA, String strB)
  494. {
  495. return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, CompareOptions.None);
  496. }
  497. public static int Compare (String strA, String strB, bool ignoreCase)
  498. {
  499. return CultureInfo.CurrentCulture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  500. }
  501. public static int Compare (String strA, String strB, bool ignoreCase, CultureInfo culture)
  502. {
  503. if (culture == null)
  504. throw new ArgumentNullException ("culture");
  505. return culture.CompareInfo.Compare (strA, strB, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  506. }
  507. public static int Compare (String strA, int indexA, String strB, int indexB, int length)
  508. {
  509. return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
  510. }
  511. public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase)
  512. {
  513. return Compare (strA, indexA, strB, indexB, length, ignoreCase, CultureInfo.CurrentCulture);
  514. }
  515. public static int Compare (String strA, int indexA, String strB, int indexB, int length, bool ignoreCase, CultureInfo culture)
  516. {
  517. if (culture == null)
  518. throw new ArgumentNullException ("culture");
  519. if ((indexA > strA.Length) || (indexB > strB.Length) || (indexA < 0) || (indexB < 0) || (length < 0))
  520. throw new ArgumentOutOfRangeException ();
  521. if (length == 0)
  522. return 0;
  523. if (strA == null) {
  524. if (strB == null) {
  525. return 0;
  526. } else {
  527. return -1;
  528. }
  529. }
  530. else if (strB == null) {
  531. return 1;
  532. }
  533. CompareOptions compopts;
  534. if (ignoreCase)
  535. compopts = CompareOptions.IgnoreCase;
  536. else
  537. compopts = CompareOptions.None;
  538. // Need to cap the requested length to the
  539. // length of the string, because
  540. // CompareInfo.Compare will insist that length
  541. // <= (string.Length - offset)
  542. int len1 = length;
  543. int len2 = length;
  544. if (length > (strA.Length - indexA)) {
  545. len1 = strA.Length - indexA;
  546. }
  547. if (length > (strB.Length - indexB)) {
  548. len2 = strB.Length - indexB;
  549. }
  550. // ENHANCE: Might call internal_compare_switch directly instead of doing all checks twice
  551. return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, compopts);
  552. }
  553. public static int Compare (string strA, string strB, StringComparison comparisonType)
  554. {
  555. switch (comparisonType) {
  556. case StringComparison.CurrentCulture:
  557. return Compare (strA, strB, false, CultureInfo.CurrentCulture);
  558. case StringComparison.CurrentCultureIgnoreCase:
  559. return Compare (strA, strB, true, CultureInfo.CurrentCulture);
  560. case StringComparison.InvariantCulture:
  561. return Compare (strA, strB, false, CultureInfo.InvariantCulture);
  562. case StringComparison.InvariantCultureIgnoreCase:
  563. return Compare (strA, strB, true, CultureInfo.InvariantCulture);
  564. case StringComparison.Ordinal:
  565. return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
  566. case StringComparison.OrdinalIgnoreCase:
  567. return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
  568. default:
  569. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  570. throw new ArgumentException (msg, "comparisonType");
  571. }
  572. }
  573. public static int Compare (string strA, int indexA, string strB, int indexB, int length, StringComparison comparisonType)
  574. {
  575. switch (comparisonType) {
  576. case StringComparison.CurrentCulture:
  577. return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.CurrentCulture);
  578. case StringComparison.CurrentCultureIgnoreCase:
  579. return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.CurrentCulture);
  580. case StringComparison.InvariantCulture:
  581. return Compare (strA, indexA, strB, indexB, length, false, CultureInfo.InvariantCulture);
  582. case StringComparison.InvariantCultureIgnoreCase:
  583. return Compare (strA, indexA, strB, indexB, length, true, CultureInfo.InvariantCulture);
  584. case StringComparison.Ordinal:
  585. return CompareOrdinal (strA, indexA, strB, indexB, length);
  586. case StringComparison.OrdinalIgnoreCase:
  587. return CompareOrdinalCaseInsensitive (strA, indexA, strB, indexB, length);
  588. default:
  589. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  590. throw new ArgumentException (msg, "comparisonType");
  591. }
  592. }
  593. public static bool Equals (string a, string b, StringComparison comparisonType)
  594. {
  595. return Compare (a, b, comparisonType) == 0;
  596. }
  597. public bool Equals (string value, StringComparison comparisonType)
  598. {
  599. return Compare (value, this, comparisonType) == 0;
  600. }
  601. public static int Compare (string strA, string strB, CultureInfo culture, CompareOptions options)
  602. {
  603. if (culture == null)
  604. throw new ArgumentNullException ("culture");
  605. return culture.CompareInfo.Compare (strA, strB, options);
  606. }
  607. public static int Compare (string strA, int indexA, string strB, int indexB, int length, CultureInfo culture, CompareOptions options)
  608. {
  609. if (culture == null)
  610. throw new ArgumentNullException ("culture");
  611. int len1 = length;
  612. int len2 = length;
  613. if (length > (strA.Length - indexA))
  614. len1 = strA.Length - indexA;
  615. if (length > (strB.Length - indexB))
  616. len2 = strB.Length - indexB;
  617. return culture.CompareInfo.Compare (strA, indexA, len1, strB, indexB, len2, options);
  618. }
  619. public int CompareTo (Object value)
  620. {
  621. if (value == null)
  622. return 1;
  623. if (!(value is String))
  624. throw new ArgumentException ();
  625. return String.Compare (this, (String) value);
  626. }
  627. public int CompareTo (String strB)
  628. {
  629. if (strB == null)
  630. return 1;
  631. return Compare (this, strB);
  632. }
  633. public static int CompareOrdinal (String strA, String strB)
  634. {
  635. return CompareOrdinalUnchecked (strA, 0, Int32.MaxValue, strB, 0, Int32.MaxValue);
  636. }
  637. public static int CompareOrdinal (String strA, int indexA, String strB, int indexB, int length)
  638. {
  639. if (strA != null && strB != null)
  640. {
  641. if (indexA > strA.Length || indexA < 0)
  642. throw new ArgumentOutOfRangeException ("indexA");
  643. if (indexB > strB.Length || indexB < 0)
  644. throw new ArgumentOutOfRangeException ("indexB");
  645. if (length < 0)
  646. throw new ArgumentOutOfRangeException ("length");
  647. }
  648. return CompareOrdinalUnchecked (strA, indexA, length, strB, indexB, length);
  649. }
  650. internal static int CompareOrdinalCaseInsensitive (String strA, int indexA, String strB, int indexB, int length)
  651. {
  652. if (strA != null && strB != null)
  653. {
  654. if (indexA > strA.Length || indexA < 0)
  655. throw new ArgumentOutOfRangeException ("indexA");
  656. if (indexB > strB.Length || indexB < 0)
  657. throw new ArgumentOutOfRangeException ("indexB");
  658. if (length < 0)
  659. throw new ArgumentOutOfRangeException ("length");
  660. }
  661. return CompareOrdinalCaseInsensitiveUnchecked (strA, indexA, length, strB, indexB, length);
  662. }
  663. internal static unsafe int CompareOrdinalUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
  664. {
  665. if (strA == null) {
  666. return strB == null ? 0 : -1;
  667. }
  668. if (strB == null) {
  669. return 1;
  670. }
  671. int lengthA = Math.Min (lenA, strA.Length - indexA);
  672. int lengthB = Math.Min (lenB, strB.Length - indexB);
  673. if (lengthA == lengthB && indexA == indexB && Object.ReferenceEquals (strA, strB))
  674. return 0;
  675. fixed (char* aptr = strA, bptr = strB) {
  676. char* ap = aptr + indexA;
  677. char* end = ap + Math.Min (lengthA, lengthB);
  678. char* bp = bptr + indexB;
  679. while (ap < end) {
  680. if (*ap != *bp)
  681. return *ap - *bp;
  682. ap++;
  683. bp++;
  684. }
  685. return lengthA - lengthB;
  686. }
  687. }
  688. //
  689. // Fastest method for internal case insensitive comparison
  690. //
  691. internal static int CompareOrdinalCaseInsensitiveUnchecked (string strA, string strB)
  692. {
  693. return CompareOrdinalCaseInsensitiveUnchecked (strA, 0, int.MaxValue, strB, 0, int.MaxValue);
  694. }
  695. internal static unsafe int CompareOrdinalCaseInsensitiveUnchecked (String strA, int indexA, int lenA, String strB, int indexB, int lenB)
  696. {
  697. // Same as above, but checks versus uppercase characters
  698. if (strA == null) {
  699. return strB == null ? 0 : -1;
  700. }
  701. if (strB == null) {
  702. return 1;
  703. }
  704. int lengthA = Math.Min (lenA, strA.Length - indexA);
  705. int lengthB = Math.Min (lenB, strB.Length - indexB);
  706. if (lengthA == lengthB && Object.ReferenceEquals (strA, strB))
  707. return 0;
  708. fixed (char* aptr = strA, bptr = strB) {
  709. char* ap = aptr + indexA;
  710. char* end = ap + Math.Min (lengthA, lengthB);
  711. char* bp = bptr + indexB;
  712. while (ap < end) {
  713. if (*ap != *bp) {
  714. char c1 = Char.ToUpperInvariant (*ap);
  715. char c2 = Char.ToUpperInvariant (*bp);
  716. if (c1 != c2)
  717. return c1 - c2;
  718. }
  719. ap++;
  720. bp++;
  721. }
  722. return lengthA - lengthB;
  723. }
  724. }
  725. public bool EndsWith (String value)
  726. {
  727. if (value == null)
  728. throw new ArgumentNullException ("value");
  729. return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
  730. }
  731. public bool EndsWith (String value, bool ignoreCase, CultureInfo culture)
  732. {
  733. if (value == null)
  734. throw new ArgumentNullException ("value");
  735. if (culture == null)
  736. culture = CultureInfo.CurrentCulture;
  737. return culture.CompareInfo.IsSuffix (this, value,
  738. ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  739. }
  740. // Following methods are culture-insensitive
  741. public int IndexOfAny (char [] anyOf)
  742. {
  743. if (anyOf == null)
  744. throw new ArgumentNullException ();
  745. if (this.length == 0)
  746. return -1;
  747. return IndexOfAnyUnchecked (anyOf, 0, this.length);
  748. }
  749. public int IndexOfAny (char [] anyOf, int startIndex)
  750. {
  751. if (anyOf == null)
  752. throw new ArgumentNullException ();
  753. if (startIndex < 0 || startIndex > this.length)
  754. throw new ArgumentOutOfRangeException ();
  755. return IndexOfAnyUnchecked (anyOf, startIndex, this.length - startIndex);
  756. }
  757. public int IndexOfAny (char [] anyOf, int startIndex, int count)
  758. {
  759. if (anyOf == null)
  760. throw new ArgumentNullException ();
  761. if (startIndex < 0 || startIndex > this.length)
  762. throw new ArgumentOutOfRangeException ();
  763. if (count < 0 || startIndex > this.length - count)
  764. throw new ArgumentOutOfRangeException ("count", "Count cannot be negative, and startIndex + count must be less than length of the string.");
  765. return IndexOfAnyUnchecked (anyOf, startIndex, count);
  766. }
  767. private unsafe int IndexOfAnyUnchecked (char[] anyOf, int startIndex, int count)
  768. {
  769. if (anyOf.Length == 0)
  770. return -1;
  771. if (anyOf.Length == 1)
  772. return IndexOfUnchecked (anyOf[0], startIndex, count);
  773. fixed (char* any = anyOf) {
  774. int highest = *any;
  775. int lowest = *any;
  776. char* end_any_ptr = any + anyOf.Length;
  777. char* any_ptr = any;
  778. while (++any_ptr != end_any_ptr) {
  779. if (*any_ptr > highest) {
  780. highest = *any_ptr;
  781. continue;
  782. }
  783. if (*any_ptr < lowest)
  784. lowest = *any_ptr;
  785. }
  786. fixed (char* start = &start_char) {
  787. char* ptr = start + startIndex;
  788. char* end_ptr = ptr + count;
  789. while (ptr != end_ptr) {
  790. if (*ptr > highest || *ptr < lowest) {
  791. ptr++;
  792. continue;
  793. }
  794. if (*ptr == *any)
  795. return (int)(ptr - start);
  796. any_ptr = any;
  797. while (++any_ptr != end_any_ptr) {
  798. if (*ptr == *any_ptr)
  799. return (int)(ptr - start);
  800. }
  801. ptr++;
  802. }
  803. }
  804. }
  805. return -1;
  806. }
  807. public int IndexOf (string value, StringComparison comparisonType)
  808. {
  809. return IndexOf (value, 0, this.Length, comparisonType);
  810. }
  811. public int IndexOf (string value, int startIndex, StringComparison comparisonType)
  812. {
  813. return IndexOf (value, startIndex, this.Length - startIndex, comparisonType);
  814. }
  815. public int IndexOf (string value, int startIndex, int count, StringComparison comparisonType)
  816. {
  817. switch (comparisonType) {
  818. case StringComparison.CurrentCulture:
  819. return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
  820. case StringComparison.CurrentCultureIgnoreCase:
  821. return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
  822. case StringComparison.InvariantCulture:
  823. return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.None);
  824. case StringComparison.InvariantCultureIgnoreCase:
  825. return CultureInfo.InvariantCulture.CompareInfo.IndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
  826. case StringComparison.Ordinal:
  827. return IndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
  828. case StringComparison.OrdinalIgnoreCase:
  829. return IndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
  830. default:
  831. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  832. throw new ArgumentException (msg, "comparisonType");
  833. }
  834. }
  835. internal int IndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
  836. {
  837. if (value == null)
  838. throw new ArgumentNullException ("value");
  839. if (startIndex < 0)
  840. throw new ArgumentOutOfRangeException ("startIndex");
  841. if (count < 0 || (this.length - startIndex) < count)
  842. throw new ArgumentOutOfRangeException ("count");
  843. if (options == CompareOptions.Ordinal)
  844. return IndexOfOrdinalUnchecked (value, startIndex, count);
  845. return IndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
  846. }
  847. internal unsafe int IndexOfOrdinalUnchecked (string value)
  848. {
  849. return IndexOfOrdinalUnchecked (value, 0, length);
  850. }
  851. internal unsafe int IndexOfOrdinalUnchecked (string value, int startIndex, int count)
  852. {
  853. int valueLen = value.Length;
  854. if (count < valueLen)
  855. return -1;
  856. if (valueLen <= 1) {
  857. if (valueLen == 1)
  858. return IndexOfUnchecked (value[0], startIndex, count);
  859. return startIndex;
  860. }
  861. fixed (char* thisptr = this, valueptr = value) {
  862. char* ap = thisptr + startIndex;
  863. char* thisEnd = ap + count - valueLen + 1;
  864. while (ap != thisEnd) {
  865. if (*ap == *valueptr) {
  866. for (int i = 1; i < valueLen; i++) {
  867. if (ap[i] != valueptr[i])
  868. goto NextVal;
  869. }
  870. return (int)(ap - thisptr);
  871. }
  872. NextVal:
  873. ap++;
  874. }
  875. }
  876. return -1;
  877. }
  878. internal unsafe int IndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
  879. {
  880. int valueLen = value.Length;
  881. if (count < valueLen)
  882. return -1;
  883. if (valueLen == 0)
  884. return startIndex;
  885. fixed (char* thisptr = this, valueptr = value) {
  886. char* ap = thisptr + startIndex;
  887. char* thisEnd = ap + count - valueLen + 1;
  888. while (ap != thisEnd) {
  889. for (int i = 0; i < valueLen; i++) {
  890. if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
  891. goto NextVal;
  892. }
  893. return (int)(ap - thisptr);
  894. NextVal:
  895. ap++;
  896. }
  897. }
  898. return -1;
  899. }
  900. public int LastIndexOf (string value, StringComparison comparisonType)
  901. {
  902. if (this.Length == 0)
  903. return value.Length == 0 ? 0 : -1;
  904. else
  905. return LastIndexOf (value, this.Length - 1, this.Length, comparisonType);
  906. }
  907. public int LastIndexOf (string value, int startIndex, StringComparison comparisonType)
  908. {
  909. return LastIndexOf (value, startIndex, startIndex + 1, comparisonType);
  910. }
  911. public int LastIndexOf (string value, int startIndex, int count, StringComparison comparisonType)
  912. {
  913. switch (comparisonType) {
  914. case StringComparison.CurrentCulture:
  915. return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
  916. case StringComparison.CurrentCultureIgnoreCase:
  917. return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
  918. case StringComparison.InvariantCulture:
  919. return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.None);
  920. case StringComparison.InvariantCultureIgnoreCase:
  921. return CultureInfo.InvariantCulture.CompareInfo.LastIndexOf (this, value, startIndex, count, CompareOptions.IgnoreCase);
  922. case StringComparison.Ordinal:
  923. return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.Ordinal);
  924. case StringComparison.OrdinalIgnoreCase:
  925. return LastIndexOfOrdinal (value, startIndex, count, CompareOptions.OrdinalIgnoreCase);
  926. default:
  927. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  928. throw new ArgumentException (msg, "comparisonType");
  929. }
  930. }
  931. internal int LastIndexOfOrdinal (string value, int startIndex, int count, CompareOptions options)
  932. {
  933. if (value == null)
  934. throw new ArgumentNullException ("value");
  935. if (this.Length == 0)
  936. return value.Length == 0 ? 0 : -1;
  937. if (value.Length == 0)
  938. return Math.Min (this.Length - 1, startIndex);
  939. if (startIndex < 0 || startIndex > length)
  940. throw new ArgumentOutOfRangeException ("startIndex");
  941. if (count < 0 || (startIndex < count - 1))
  942. throw new ArgumentOutOfRangeException ("count");
  943. if (options == CompareOptions.Ordinal)
  944. return LastIndexOfOrdinalUnchecked (value, startIndex, count);
  945. return LastIndexOfOrdinalIgnoreCaseUnchecked (value, startIndex, count);
  946. }
  947. internal unsafe int LastIndexOfOrdinalUnchecked (string value, int startIndex, int count)
  948. {
  949. int valueLen = value.Length;
  950. if (count < valueLen)
  951. return -1;
  952. if (valueLen <= 1) {
  953. if (valueLen == 1)
  954. return LastIndexOfUnchecked (value[0], startIndex, count);
  955. return startIndex;
  956. }
  957. fixed (char* thisptr = this, valueptr = value) {
  958. char* ap = thisptr + startIndex - valueLen + 1;
  959. char* thisEnd = ap - count + valueLen - 1;
  960. while (ap != thisEnd) {
  961. if (*ap == *valueptr) {
  962. for (int i = 1; i < valueLen; i++) {
  963. if (ap[i] != valueptr[i])
  964. goto NextVal;
  965. }
  966. return (int)(ap - thisptr);
  967. }
  968. NextVal:
  969. ap--;
  970. }
  971. }
  972. return -1;
  973. }
  974. internal unsafe int LastIndexOfOrdinalIgnoreCaseUnchecked (string value, int startIndex, int count)
  975. {
  976. int valueLen = value.Length;
  977. if (count < valueLen)
  978. return -1;
  979. if (valueLen == 0)
  980. return startIndex;
  981. fixed (char* thisptr = this, valueptr = value) {
  982. char* ap = thisptr + startIndex - valueLen + 1;
  983. char* thisEnd = ap - count + valueLen - 1;
  984. while (ap != thisEnd) {
  985. for (int i = 0; i < valueLen; i++) {
  986. if (Char.ToUpperInvariant (ap[i]) != Char.ToUpperInvariant (valueptr[i]))
  987. goto NextVal;
  988. }
  989. return (int)(ap - thisptr);
  990. NextVal:
  991. ap--;
  992. }
  993. }
  994. return -1;
  995. }
  996. // Following methods are culture-insensitive
  997. public int IndexOf (char value)
  998. {
  999. if (this.length == 0)
  1000. return -1;
  1001. return IndexOfUnchecked (value, 0, this.length);
  1002. }
  1003. public int IndexOf (char value, int startIndex)
  1004. {
  1005. if (startIndex < 0)
  1006. throw new ArgumentOutOfRangeException ("startIndex", "< 0");
  1007. if (startIndex > this.length)
  1008. throw new ArgumentOutOfRangeException ("startIndex", "startIndex > this.length");
  1009. if ((startIndex == 0 && this.length == 0) || (startIndex == this.length))
  1010. return -1;
  1011. return IndexOfUnchecked (value, startIndex, this.length - startIndex);
  1012. }
  1013. public int IndexOf (char value, int startIndex, int count)
  1014. {
  1015. if (startIndex < 0 || startIndex > this.length)
  1016. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be< 0");
  1017. if (count < 0)
  1018. throw new ArgumentOutOfRangeException ("count", "< 0");
  1019. if (startIndex > this.length - count)
  1020. throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
  1021. if ((startIndex == 0 && this.length == 0) || (startIndex == this.length) || (count == 0))
  1022. return -1;
  1023. return IndexOfUnchecked (value, startIndex, count);
  1024. }
  1025. internal unsafe int IndexOfUnchecked (char value, int startIndex, int count)
  1026. {
  1027. // It helps JIT compiler to optimize comparison
  1028. int value_32 = (int)value;
  1029. fixed (char* start = &start_char) {
  1030. char* ptr = start + startIndex;
  1031. char* end_ptr = ptr + (count >> 3 << 3);
  1032. while (ptr != end_ptr) {
  1033. if (*ptr == value_32)
  1034. return (int)(ptr - start);
  1035. if (ptr[1] == value_32)
  1036. return (int)(ptr - start + 1);
  1037. if (ptr[2] == value_32)
  1038. return (int)(ptr - start + 2);
  1039. if (ptr[3] == value_32)
  1040. return (int)(ptr - start + 3);
  1041. if (ptr[4] == value_32)
  1042. return (int)(ptr - start + 4);
  1043. if (ptr[5] == value_32)
  1044. return (int)(ptr - start + 5);
  1045. if (ptr[6] == value_32)
  1046. return (int)(ptr - start + 6);
  1047. if (ptr[7] == value_32)
  1048. return (int)(ptr - start + 7);
  1049. ptr += 8;
  1050. }
  1051. end_ptr += count & 0x07;
  1052. while (ptr != end_ptr) {
  1053. if (*ptr == value_32)
  1054. return (int)(ptr - start);
  1055. ptr++;
  1056. }
  1057. return -1;
  1058. }
  1059. }
  1060. internal unsafe int IndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
  1061. {
  1062. if (length == 0)
  1063. return -1;
  1064. int end = startIndex + count;
  1065. char c = Char.ToUpperInvariant (value);
  1066. fixed (char* s = &start_char) {
  1067. for (int i = startIndex; i < end; i++)
  1068. if (Char.ToUpperInvariant (s [i]) == c)
  1069. return i;
  1070. }
  1071. return -1;
  1072. }
  1073. // Following methods are culture-sensitive
  1074. public int IndexOf (String value)
  1075. {
  1076. if (value == null)
  1077. throw new ArgumentNullException ("value");
  1078. if (value.length == 0)
  1079. return 0;
  1080. if (this.length == 0)
  1081. return -1;
  1082. return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, 0, length, CompareOptions.Ordinal);
  1083. }
  1084. public int IndexOf (String value, int startIndex)
  1085. {
  1086. return IndexOf (value, startIndex, this.length - startIndex);
  1087. }
  1088. public int IndexOf (String value, int startIndex, int count)
  1089. {
  1090. if (value == null)
  1091. throw new ArgumentNullException ("value");
  1092. if (startIndex < 0 || startIndex > this.length)
  1093. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should not exceed length of string.");
  1094. if (count < 0 || startIndex > this.length - count)
  1095. throw new ArgumentOutOfRangeException ("count", "Cannot be negative, and should point to location in string.");
  1096. if (value.length == 0)
  1097. return startIndex;
  1098. if (startIndex == 0 && this.length == 0)
  1099. return -1;
  1100. if (count == 0)
  1101. return -1;
  1102. return CultureInfo.CurrentCulture.CompareInfo.IndexOf (this, value, startIndex, count);
  1103. }
  1104. // Following methods are culture-insensitive
  1105. public int LastIndexOfAny (char [] anyOf)
  1106. {
  1107. if (anyOf == null)
  1108. throw new ArgumentNullException ();
  1109. if (this.length == 0)
  1110. return -1;
  1111. return LastIndexOfAnyUnchecked (anyOf, this.length - 1, this.length);
  1112. }
  1113. public int LastIndexOfAny (char [] anyOf, int startIndex)
  1114. {
  1115. if (anyOf == null)
  1116. throw new ArgumentNullException ();
  1117. if (this.length == 0)
  1118. return -1;
  1119. if (startIndex < 0 || startIndex >= this.length)
  1120. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
  1121. if (this.length == 0)
  1122. return -1;
  1123. return LastIndexOfAnyUnchecked (anyOf, startIndex, startIndex + 1);
  1124. }
  1125. public int LastIndexOfAny (char [] anyOf, int startIndex, int count)
  1126. {
  1127. if (anyOf == null)
  1128. throw new ArgumentNullException ();
  1129. if (this.length == 0)
  1130. return -1;
  1131. if ((startIndex < 0) || (startIndex >= this.Length))
  1132. throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
  1133. if ((count < 0) || (count > this.Length))
  1134. throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
  1135. if (startIndex - count + 1 < 0)
  1136. throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
  1137. if (this.length == 0)
  1138. return -1;
  1139. return LastIndexOfAnyUnchecked (anyOf, startIndex, count);
  1140. }
  1141. private unsafe int LastIndexOfAnyUnchecked (char [] anyOf, int startIndex, int count)
  1142. {
  1143. if (anyOf.Length == 1)
  1144. return LastIndexOfUnchecked (anyOf[0], startIndex, count);
  1145. fixed (char* start = this, testStart = anyOf) {
  1146. char* ptr = start + startIndex;
  1147. char* ptrEnd = ptr - count;
  1148. char* test;
  1149. char* testEnd = testStart + anyOf.Length;
  1150. while (ptr != ptrEnd) {
  1151. test = testStart;
  1152. while (test != testEnd) {
  1153. if (*test == *ptr)
  1154. return (int)(ptr - start);
  1155. test++;
  1156. }
  1157. ptr--;
  1158. }
  1159. return -1;
  1160. }
  1161. }
  1162. // Following methods are culture-insensitive
  1163. public int LastIndexOf (char value)
  1164. {
  1165. if (this.length == 0)
  1166. return -1;
  1167. return LastIndexOfUnchecked (value, this.length - 1, this.length);
  1168. }
  1169. public int LastIndexOf (char value, int startIndex)
  1170. {
  1171. return LastIndexOf (value, startIndex, startIndex + 1);
  1172. }
  1173. public int LastIndexOf (char value, int startIndex, int count)
  1174. {
  1175. if (this.length == 0)
  1176. return -1;
  1177. // >= for char (> for string)
  1178. if ((startIndex < 0) || (startIndex >= this.Length))
  1179. throw new ArgumentOutOfRangeException ("startIndex", "< 0 || >= this.Length");
  1180. if ((count < 0) || (count > this.Length))
  1181. throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
  1182. if (startIndex - count + 1 < 0)
  1183. throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
  1184. return LastIndexOfUnchecked (value, startIndex, count);
  1185. }
  1186. internal unsafe int LastIndexOfUnchecked (char value, int startIndex, int count)
  1187. {
  1188. // It helps JIT compiler to optimize comparison
  1189. int value_32 = (int)value;
  1190. fixed (char* start = &start_char) {
  1191. char* ptr = start + startIndex;
  1192. char* end_ptr = ptr - (count >> 3 << 3);
  1193. while (ptr != end_ptr) {
  1194. if (*ptr == value_32)
  1195. return (int)(ptr - start);
  1196. if (ptr[-1] == value_32)
  1197. return (int)(ptr - start) - 1;
  1198. if (ptr[-2] == value_32)
  1199. return (int)(ptr - start) - 2;
  1200. if (ptr[-3] == value_32)
  1201. return (int)(ptr - start) - 3;
  1202. if (ptr[-4] == value_32)
  1203. return (int)(ptr - start) - 4;
  1204. if (ptr[-5] == value_32)
  1205. return (int)(ptr - start) - 5;
  1206. if (ptr[-6] == value_32)
  1207. return (int)(ptr - start) - 6;
  1208. if (ptr[-7] == value_32)
  1209. return (int)(ptr - start) - 7;
  1210. ptr -= 8;
  1211. }
  1212. end_ptr -= count & 0x07;
  1213. while (ptr != end_ptr) {
  1214. if (*ptr == value_32)
  1215. return (int)(ptr - start);
  1216. ptr--;
  1217. }
  1218. return -1;
  1219. }
  1220. }
  1221. internal unsafe int LastIndexOfOrdinalIgnoreCase (char value, int startIndex, int count)
  1222. {
  1223. if (length == 0)
  1224. return -1;
  1225. int end = startIndex - count;
  1226. char c = Char.ToUpperInvariant (value);
  1227. fixed (char* s = &start_char) {
  1228. for (int i = startIndex; i > end; i--)
  1229. if (Char.ToUpperInvariant (s [i]) == c)
  1230. return i;
  1231. }
  1232. return -1;
  1233. }
  1234. // Following methods are culture-sensitive
  1235. public int LastIndexOf (String value)
  1236. {
  1237. return LastIndexOf (value, this.length - 1, this.length);
  1238. }
  1239. public int LastIndexOf (String value, int startIndex)
  1240. {
  1241. int max = startIndex;
  1242. if (max < this.Length)
  1243. max++;
  1244. return LastIndexOf (value, startIndex, max);
  1245. }
  1246. public int LastIndexOf (String value, int startIndex, int count)
  1247. {
  1248. if (value == null)
  1249. throw new ArgumentNullException ("value");
  1250. if (this.length == 0)
  1251. return value.Length == 0 ? 0 : -1;
  1252. // -1 > startIndex > for string (0 > startIndex >= for char)
  1253. if ((startIndex < -1) || (startIndex > this.Length))
  1254. throw new ArgumentOutOfRangeException ("startIndex", "< 0 || > this.Length");
  1255. if ((count < 0) || (count > this.Length))
  1256. throw new ArgumentOutOfRangeException ("count", "< 0 || > this.Length");
  1257. if (startIndex - count + 1 < 0)
  1258. throw new ArgumentOutOfRangeException ("startIndex - count + 1 < 0");
  1259. if (value.Length == 0)
  1260. return Math.Min (this.Length - 1, startIndex);
  1261. if (startIndex == 0 && this.length == 0)
  1262. return -1;
  1263. // This check is needed to match undocumented MS behaviour
  1264. if (this.length == 0 && value.length > 0)
  1265. return -1;
  1266. if (count == 0)
  1267. return -1;
  1268. if (startIndex == this.Length)
  1269. startIndex--;
  1270. return CultureInfo.CurrentCulture.CompareInfo.LastIndexOf (this, value, startIndex, count);
  1271. }
  1272. public bool Contains (String value)
  1273. {
  1274. return IndexOf (value) != -1;
  1275. }
  1276. public static bool IsNullOrEmpty (String value)
  1277. {
  1278. return (value == null) || (value.Length == 0);
  1279. }
  1280. public string Normalize ()
  1281. {
  1282. return Normalization.Normalize (this, 0);
  1283. }
  1284. public string Normalize (NormalizationForm normalizationForm)
  1285. {
  1286. switch (normalizationForm) {
  1287. default:
  1288. return Normalization.Normalize (this, 0);
  1289. case NormalizationForm.FormD:
  1290. return Normalization.Normalize (this, 1);
  1291. case NormalizationForm.FormKC:
  1292. return Normalization.Normalize (this, 2);
  1293. case NormalizationForm.FormKD:
  1294. return Normalization.Normalize (this, 3);
  1295. }
  1296. }
  1297. public bool IsNormalized ()
  1298. {
  1299. return Normalization.IsNormalized (this, 0);
  1300. }
  1301. public bool IsNormalized (NormalizationForm normalizationForm)
  1302. {
  1303. switch (normalizationForm) {
  1304. default:
  1305. return Normalization.IsNormalized (this, 0);
  1306. case NormalizationForm.FormD:
  1307. return Normalization.IsNormalized (this, 1);
  1308. case NormalizationForm.FormKC:
  1309. return Normalization.IsNormalized (this, 2);
  1310. case NormalizationForm.FormKD:
  1311. return Normalization.IsNormalized (this, 3);
  1312. }
  1313. }
  1314. public string Remove (int startIndex)
  1315. {
  1316. if (startIndex < 0)
  1317. throw new ArgumentOutOfRangeException ("startIndex", "StartIndex can not be less than zero");
  1318. if (startIndex >= this.length)
  1319. throw new ArgumentOutOfRangeException ("startIndex", "StartIndex must be less than the length of the string");
  1320. return Remove (startIndex, this.length - startIndex);
  1321. }
  1322. public String PadLeft (int totalWidth)
  1323. {
  1324. return PadLeft (totalWidth, ' ');
  1325. }
  1326. public unsafe String PadLeft (int totalWidth, char paddingChar)
  1327. {
  1328. //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
  1329. if (totalWidth < 0)
  1330. throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
  1331. if (totalWidth < this.length)
  1332. return this;
  1333. if (totalWidth == 0)
  1334. return Empty;
  1335. String tmp = InternalAllocateStr (totalWidth);
  1336. fixed (char* dest = tmp, src = this) {
  1337. char* padPos = dest;
  1338. char* padTo = dest + (totalWidth - length);
  1339. while (padPos != padTo)
  1340. *padPos++ = paddingChar;
  1341. CharCopy (padTo, src, length);
  1342. }
  1343. return tmp;
  1344. }
  1345. public String PadRight (int totalWidth)
  1346. {
  1347. return PadRight (totalWidth, ' ');
  1348. }
  1349. public unsafe String PadRight (int totalWidth, char paddingChar)
  1350. {
  1351. //LAMESPEC: MSDN Doc says this is reversed for RtL languages, but this seems to be untrue
  1352. if (totalWidth < 0)
  1353. throw new ArgumentOutOfRangeException ("totalWidth", "< 0");
  1354. if (totalWidth < this.length)
  1355. return this;
  1356. if (totalWidth == 0)
  1357. return Empty;
  1358. String tmp = InternalAllocateStr (totalWidth);
  1359. fixed (char* dest = tmp, src = this) {
  1360. CharCopy (dest, src, length);
  1361. char* padPos = dest + length;
  1362. char* padTo = dest + totalWidth;
  1363. while (padPos != padTo)
  1364. *padPos++ = paddingChar;
  1365. }
  1366. return tmp;
  1367. }
  1368. public bool StartsWith (String value)
  1369. {
  1370. if (value == null)
  1371. throw new ArgumentNullException ("value");
  1372. return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
  1373. }
  1374. [ComVisible (false)]
  1375. public bool StartsWith (string value, StringComparison comparisonType)
  1376. {
  1377. if (value == null)
  1378. throw new ArgumentNullException ("value");
  1379. switch (comparisonType) {
  1380. case StringComparison.CurrentCulture:
  1381. return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
  1382. case StringComparison.CurrentCultureIgnoreCase:
  1383. return CultureInfo.CurrentCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
  1384. case StringComparison.InvariantCulture:
  1385. return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.None);
  1386. case StringComparison.InvariantCultureIgnoreCase:
  1387. return CultureInfo.InvariantCulture.CompareInfo.IsPrefix (this, value, CompareOptions.IgnoreCase);
  1388. case StringComparison.Ordinal:
  1389. return StartsWithOrdinalUnchecked (value);
  1390. case StringComparison.OrdinalIgnoreCase:
  1391. return StartsWithOrdinalCaseInsensitiveUnchecked (value);
  1392. default:
  1393. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  1394. throw new ArgumentException (msg, "comparisonType");
  1395. }
  1396. }
  1397. internal bool StartsWithOrdinalUnchecked (string value)
  1398. {
  1399. return length >= value.length && CompareOrdinalUnchecked (this, 0, value.length, value, 0, value.length) == 0;
  1400. }
  1401. internal bool StartsWithOrdinalCaseInsensitiveUnchecked (string value)
  1402. {
  1403. return length >= value.Length && CompareOrdinalCaseInsensitiveUnchecked (this, 0, value.length, value, 0, value.length) == 0;
  1404. }
  1405. [ComVisible (false)]
  1406. public bool EndsWith (string value, StringComparison comparisonType)
  1407. {
  1408. if (value == null)
  1409. throw new ArgumentNullException ("value");
  1410. switch (comparisonType) {
  1411. case StringComparison.CurrentCulture:
  1412. return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
  1413. case StringComparison.CurrentCultureIgnoreCase:
  1414. return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
  1415. case StringComparison.InvariantCulture:
  1416. return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.None);
  1417. case StringComparison.InvariantCultureIgnoreCase:
  1418. return CultureInfo.InvariantCulture.CompareInfo.IsSuffix (this, value, CompareOptions.IgnoreCase);
  1419. case StringComparison.Ordinal:
  1420. return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.Ordinal);
  1421. case StringComparison.OrdinalIgnoreCase:
  1422. return CultureInfo.CurrentCulture.CompareInfo.IsSuffix (this, value, CompareOptions.OrdinalIgnoreCase);
  1423. default:
  1424. string msg = Locale.GetText ("Invalid value '{0}' for StringComparison", comparisonType);
  1425. thr

Large files files are truncated, but you can click here to view the full file