PageRenderTime 66ms CodeModel.GetById 18ms 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
  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. throw new ArgumentException (msg, "comparisonType");
  1426. }
  1427. }
  1428. public bool StartsWith (String value, bool ignoreCase, CultureInfo culture)
  1429. {
  1430. if (culture == null)
  1431. culture = CultureInfo.CurrentCulture;
  1432. return culture.CompareInfo.IsPrefix (this, value, ignoreCase ? CompareOptions.IgnoreCase : CompareOptions.None);
  1433. }
  1434. // Following method is culture-insensitive
  1435. public unsafe String Replace (char oldChar, char newChar)
  1436. {
  1437. if (this.length == 0 || oldChar == newChar)
  1438. return this;
  1439. int start_pos = IndexOfUnchecked (oldChar, 0, this.length);
  1440. if (start_pos == -1)
  1441. return this;
  1442. if (start_pos < 4)
  1443. start_pos = 0;
  1444. string tmp = InternalAllocateStr (length);
  1445. fixed (char* dest = tmp, src = &start_char) {
  1446. if (start_pos != 0)
  1447. CharCopy (dest, src, start_pos);
  1448. char* end_ptr = dest + length;
  1449. char* dest_ptr = dest + start_pos;
  1450. char* src_ptr = src + start_pos;
  1451. while (dest_ptr != end_ptr) {
  1452. if (*src_ptr == oldChar)
  1453. *dest_ptr = newChar;
  1454. else
  1455. *dest_ptr = *src_ptr;
  1456. ++src_ptr;
  1457. ++dest_ptr;
  1458. }
  1459. }
  1460. return tmp;
  1461. }
  1462. // culture-insensitive using ordinal search (See testcase StringTest.ReplaceStringCultureTests)
  1463. public String Replace (String oldValue, String newValue)
  1464. {
  1465. // LAMESPEC: According to MSDN the following method is culture-sensitive but this seems to be incorrect
  1466. // LAMESPEC: Result is undefined if result length is longer than maximum string length
  1467. if (oldValue == null)
  1468. throw new ArgumentNullException ("oldValue");
  1469. if (oldValue.Length == 0)
  1470. throw new ArgumentException ("oldValue is the empty string.");
  1471. if (this.Length == 0)
  1472. return this;
  1473. if (newValue == null)
  1474. newValue = Empty;
  1475. return ReplaceUnchecked (oldValue, newValue);
  1476. }
  1477. private unsafe String ReplaceUnchecked (String oldValue, String newValue)
  1478. {
  1479. if (oldValue.length > length)
  1480. return this;
  1481. if (oldValue.length == 1 && newValue.length == 1) {
  1482. return Replace (oldValue[0], newValue[0]);
  1483. // ENHANCE: It would be possible to special case oldValue.length == newValue.length
  1484. // because the length of the result would be this.length and length calculation unneccesary
  1485. }
  1486. const int maxValue = 200; // Allocate 800 byte maximum
  1487. int* dat = stackalloc int[maxValue];
  1488. fixed (char* source = this, replace = newValue) {
  1489. int i = 0, count = 0;
  1490. while (i < length) {
  1491. int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
  1492. if (found < 0)
  1493. break;
  1494. else {
  1495. if (count < maxValue)
  1496. dat[count++] = found;
  1497. else
  1498. return ReplaceFallback (oldValue, newValue, maxValue);
  1499. }
  1500. i = found + oldValue.length;
  1501. }
  1502. if (count == 0)
  1503. return this;
  1504. int nlen = this.length + ((newValue.length - oldValue.length) * count);
  1505. String tmp = InternalAllocateStr (nlen);
  1506. int curPos = 0, lastReadPos = 0;
  1507. fixed (char* dest = tmp) {
  1508. for (int j = 0; j < count; j++) {
  1509. int precopy = dat[j] - lastReadPos;
  1510. CharCopy (dest + curPos, source + lastReadPos, precopy);
  1511. curPos += precopy;
  1512. lastReadPos = dat[j] + oldValue.length;
  1513. CharCopy (dest + curPos, replace, newValue.length);
  1514. curPos += newValue.length;
  1515. }
  1516. CharCopy (dest + curPos, source + lastReadPos, length - lastReadPos);
  1517. }
  1518. return tmp;
  1519. }
  1520. }
  1521. private String ReplaceFallback (String oldValue, String newValue, int testedCount)
  1522. {
  1523. int lengthEstimate = this.length + ((newValue.length - oldValue.length) * testedCount);
  1524. StringBuilder sb = new StringBuilder (lengthEstimate);
  1525. for (int i = 0; i < length;) {
  1526. int found = IndexOfOrdinalUnchecked (oldValue, i, length - i);
  1527. if (found < 0) {
  1528. sb.Append (SubstringUnchecked (i, length - i));
  1529. break;
  1530. }
  1531. sb.Append (SubstringUnchecked (i, found - i));
  1532. sb.Append (newValue);
  1533. i = found + oldValue.Length;
  1534. }
  1535. return sb.ToString ();
  1536. }
  1537. public unsafe String Remove (int startIndex, int count)
  1538. {
  1539. if (startIndex < 0)
  1540. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
  1541. if (count < 0)
  1542. throw new ArgumentOutOfRangeException ("count", "Cannot be negative.");
  1543. if (startIndex > this.length - count)
  1544. throw new ArgumentOutOfRangeException ("count", "startIndex + count > this.length");
  1545. String tmp = InternalAllocateStr (this.length - count);
  1546. fixed (char *dest = tmp, src = this) {
  1547. char *dst = dest;
  1548. CharCopy (dst, src, startIndex);
  1549. int skip = startIndex + count;
  1550. dst += startIndex;
  1551. CharCopy (dst, src + skip, length - skip);
  1552. }
  1553. return tmp;
  1554. }
  1555. public String ToLower ()
  1556. {
  1557. return ToLower (CultureInfo.CurrentCulture);
  1558. }
  1559. public String ToLower (CultureInfo culture)
  1560. {
  1561. if (culture == null)
  1562. throw new ArgumentNullException ("culture");
  1563. if (culture.LCID == 0x007F) // Invariant
  1564. return ToLowerInvariant ();
  1565. return culture.TextInfo.ToLower (this);
  1566. }
  1567. public unsafe String ToLowerInvariant ()
  1568. {
  1569. if (length == 0)
  1570. return Empty;
  1571. string tmp = InternalAllocateStr (length);
  1572. fixed (char* source = &start_char, dest = tmp) {
  1573. char* destPtr = (char*)dest;
  1574. char* sourcePtr = (char*)source;
  1575. for (int n = 0; n < length; n++) {
  1576. *destPtr = Char.ToLowerInvariant (*sourcePtr);
  1577. sourcePtr++;
  1578. destPtr++;
  1579. }
  1580. }
  1581. return tmp;
  1582. }
  1583. public String ToUpper ()
  1584. {
  1585. return ToUpper (CultureInfo.CurrentCulture);
  1586. }
  1587. public String ToUpper (CultureInfo culture)
  1588. {
  1589. if (culture == null)
  1590. throw new ArgumentNullException ("culture");
  1591. if (culture.LCID == 0x007F) // Invariant
  1592. return ToUpperInvariant ();
  1593. return culture.TextInfo.ToUpper (this);
  1594. }
  1595. public unsafe String ToUpperInvariant ()
  1596. {
  1597. if (length == 0)
  1598. return Empty;
  1599. string tmp = InternalAllocateStr (length);
  1600. fixed (char* source = &start_char, dest = tmp) {
  1601. char* destPtr = (char*)dest;
  1602. char* sourcePtr = (char*)source;
  1603. for (int n = 0; n < length; n++) {
  1604. *destPtr = Char.ToUpperInvariant (*sourcePtr);
  1605. sourcePtr++;
  1606. destPtr++;
  1607. }
  1608. }
  1609. return tmp;
  1610. }
  1611. public override String ToString ()
  1612. {
  1613. return this;
  1614. }
  1615. public String ToString (IFormatProvider provider)
  1616. {
  1617. return this;
  1618. }
  1619. public static String Format (String format, Object arg0)
  1620. {
  1621. return Format (null, format, new Object[] {arg0});
  1622. }
  1623. public static String Format (String format, Object arg0, Object arg1)
  1624. {
  1625. return Format (null, format, new Object[] {arg0, arg1});
  1626. }
  1627. public static String Format (String format, Object arg0, Object arg1, Object arg2)
  1628. {
  1629. return Format (null, format, new Object[] {arg0, arg1, arg2});
  1630. }
  1631. public static string Format (string format, params object[] args)
  1632. {
  1633. return Format (null, format, args);
  1634. }
  1635. public static string Format (IFormatProvider provider, string format, params object[] args)
  1636. {
  1637. StringBuilder b = FormatHelper (null, provider, format, args);
  1638. return b.ToString ();
  1639. }
  1640. internal static StringBuilder FormatHelper (StringBuilder result, IFormatProvider provider, string format, params object[] args)
  1641. {
  1642. if (format == null)
  1643. throw new ArgumentNullException ("format");
  1644. if (args == null)
  1645. throw new ArgumentNullException ("args");
  1646. if (result == null) {
  1647. /* Try to approximate the size of result to avoid reallocations */
  1648. int i, len;
  1649. len = 0;
  1650. for (i = 0; i < args.Length; ++i) {
  1651. string s = args [i] as string;
  1652. if (s != null)
  1653. len += s.length;
  1654. else
  1655. break;
  1656. }
  1657. if (i == args.Length)
  1658. result = new StringBuilder (len + format.length);
  1659. else
  1660. result = new StringBuilder ();
  1661. }
  1662. int ptr = 0;
  1663. int start = ptr;
  1664. var formatter = provider != null ? provider.GetFormat (typeof (ICustomFormatter)) as ICustomFormatter : null;
  1665. while (ptr < format.length) {
  1666. char c = format[ptr ++];
  1667. if (c == '{') {
  1668. result.Append (format, start, ptr - start - 1);
  1669. // check for escaped open bracket
  1670. if (format[ptr] == '{') {
  1671. start = ptr ++;
  1672. continue;
  1673. }
  1674. // parse specifier
  1675. int n, width;
  1676. bool left_align;
  1677. string arg_format;
  1678. ParseFormatSpecifier (format, ref ptr, out n, out width, out left_align, out arg_format);
  1679. if (n >= args.Length)
  1680. throw new FormatException ("Index (zero based) must be greater than or equal to zero and less than the size of the argument list.");
  1681. // format argument
  1682. object arg = args[n];
  1683. string str;
  1684. if (arg == null)
  1685. str = Empty;
  1686. else if (formatter != null)
  1687. str = formatter.Format (arg_format, arg, provider);
  1688. else
  1689. str = null;
  1690. if (str == null) {
  1691. if (arg is IFormattable)
  1692. str = ((IFormattable)arg).ToString (arg_format, provider);
  1693. else
  1694. str = arg.ToString ();
  1695. }
  1696. // pad formatted string and append to result
  1697. if (width > str.length) {
  1698. const char padchar = ' ';
  1699. int padlen = width - str.length;
  1700. if (left_align) {
  1701. result.Append (str);
  1702. result.Append (padchar, padlen);
  1703. }
  1704. else {
  1705. result.Append (padchar, padlen);
  1706. result.Append (str);
  1707. }
  1708. } else {
  1709. result.Append (str);
  1710. }
  1711. start = ptr;
  1712. }
  1713. else if (c == '}' && ptr < format.length && format[ptr] == '}') {
  1714. result.Append (format, start, ptr - start - 1);
  1715. start = ptr ++;
  1716. }
  1717. else if (c == '}') {
  1718. throw new FormatException ("Input string was not in a correct format.");
  1719. }
  1720. }
  1721. if (start < format.length)
  1722. result.Append (format, start, format.Length - start);
  1723. return result;
  1724. }
  1725. public unsafe static String Copy (String str)
  1726. {
  1727. if (str == null)
  1728. throw new ArgumentNullException ("str");
  1729. int length = str.length;
  1730. String tmp = InternalAllocateStr (length);
  1731. if (length != 0) {
  1732. fixed (char *dest = tmp, src = str) {
  1733. CharCopy (dest, src, length);
  1734. }
  1735. }
  1736. return tmp;
  1737. }
  1738. public static String Concat (Object arg0)
  1739. {
  1740. if (arg0 == null)
  1741. return Empty;
  1742. return arg0.ToString ();
  1743. }
  1744. public static String Concat (Object arg0, Object arg1)
  1745. {
  1746. return Concat ((arg0 != null) ? arg0.ToString () : null, (arg1 != null) ? arg1.ToString () : null);
  1747. }
  1748. public static String Concat (Object arg0, Object arg1, Object arg2)
  1749. {
  1750. string s1, s2, s3;
  1751. if (arg0 == null)
  1752. s1 = Empty;
  1753. else
  1754. s1 = arg0.ToString ();
  1755. if (arg1 == null)
  1756. s2 = Empty;
  1757. else
  1758. s2 = arg1.ToString ();
  1759. if (arg2 == null)
  1760. s3 = Empty;
  1761. else
  1762. s3 = arg2.ToString ();
  1763. return Concat (s1, s2, s3);
  1764. }
  1765. [CLSCompliant(false)]
  1766. public static String Concat (Object arg0, Object arg1, Object arg2,
  1767. Object arg3, __arglist)
  1768. {
  1769. string s1, s2, s3, s4;
  1770. if (arg0 == null)
  1771. s1 = Empty;
  1772. else
  1773. s1 = arg0.ToString ();
  1774. if (arg1 == null)
  1775. s2 = Empty;
  1776. else
  1777. s2 = arg1.ToString ();
  1778. if (arg2 == null)
  1779. s3 = Empty;
  1780. else
  1781. s3 = arg2.ToString ();
  1782. ArgIterator iter = new ArgIterator (__arglist);
  1783. int argCount = iter.GetRemainingCount();
  1784. StringBuilder sb = new StringBuilder ();
  1785. if (arg3 != null)
  1786. sb.Append (arg3.ToString ());
  1787. for (int i = 0; i < argCount; i++) {
  1788. TypedReference typedRef = iter.GetNextArg ();
  1789. sb.Append (TypedReference.ToObject (typedRef));
  1790. }
  1791. s4 = sb.ToString ();
  1792. return Concat (s1, s2, s3, s4);
  1793. }
  1794. public unsafe static String Concat (String str0, String str1)
  1795. {
  1796. if (str0 == null || str0.Length == 0) {
  1797. if (str1 == null || str1.Length == 0)
  1798. return Empty;
  1799. return str1;
  1800. }
  1801. if (str1 == null || str1.Length == 0)
  1802. return str0;
  1803. String tmp = InternalAllocateStr (str0.length + str1.length);
  1804. fixed (char *dest = tmp, src = str0)
  1805. CharCopy (dest, src, str0.length);
  1806. fixed (char *dest = tmp, src = str1)
  1807. CharCopy (dest + str0.Length, src, str1.length);
  1808. return tmp;
  1809. }
  1810. public unsafe static String Concat (String str0, String str1, String str2)
  1811. {
  1812. if (str0 == null || str0.Length == 0){
  1813. if (str1 == null || str1.Length == 0){
  1814. if (str2 == null || str2.Length == 0)
  1815. return Empty;
  1816. return str2;
  1817. } else {
  1818. if (str2 == null || str2.Length == 0)
  1819. return str1;
  1820. }
  1821. str0 = Empty;
  1822. } else {
  1823. if (str1 == null || str1.Length == 0){
  1824. if (str2 == null || str2.Length == 0)
  1825. return str0;
  1826. else
  1827. str1 = Empty;
  1828. } else {
  1829. if (str2 == null || str2.Length == 0)
  1830. str2 = Empty;
  1831. }
  1832. }
  1833. String tmp = InternalAllocateStr (str0.length + str1.length + str2.length);
  1834. if (str0.Length != 0) {
  1835. fixed (char *dest = tmp, src = str0) {
  1836. CharCopy (dest, src, str0.length);
  1837. }
  1838. }
  1839. if (str1.Length != 0) {
  1840. fixed (char *dest = tmp, src = str1) {
  1841. CharCopy (dest + str0.Length, src, str1.length);
  1842. }
  1843. }
  1844. if (str2.Length != 0) {
  1845. fixed (char *dest = tmp, src = str2) {
  1846. CharCopy (dest + str0.Length + str1.Length, src, str2.length);
  1847. }
  1848. }
  1849. return tmp;
  1850. }
  1851. public unsafe static String Concat (String str0, String str1, String str2, String str3)
  1852. {
  1853. if (str0 == null && str1 == null && str2 == null && str3 == null)
  1854. return Empty;
  1855. if (str0 == null)
  1856. str0 = Empty;
  1857. if (str1 == null)
  1858. str1 = Empty;
  1859. if (str2 == null)
  1860. str2 = Empty;
  1861. if (str3 == null)
  1862. str3 = Empty;
  1863. String tmp = InternalAllocateStr (str0.length + str1.length + str2.length + str3.length);
  1864. if (str0.Length != 0) {
  1865. fixed (char *dest = tmp, src = str0) {
  1866. CharCopy (dest, src, str0.length);
  1867. }
  1868. }
  1869. if (str1.Length != 0) {
  1870. fixed (char *dest = tmp, src = str1) {
  1871. CharCopy (dest + str0.Length, src, str1.length);
  1872. }
  1873. }
  1874. if (str2.Length != 0) {
  1875. fixed (char *dest = tmp, src = str2) {
  1876. CharCopy (dest + str0.Length + str1.Length, src, str2.length);
  1877. }
  1878. }
  1879. if (str3.Length != 0) {
  1880. fixed (char *dest = tmp, src = str3) {
  1881. CharCopy (dest + str0.Length + str1.Length + str2.Length, src, str3.length);
  1882. }
  1883. }
  1884. return tmp;
  1885. }
  1886. public static String Concat (params Object[] args)
  1887. {
  1888. if (args == null)
  1889. throw new ArgumentNullException ("args");
  1890. int argLen = args.Length;
  1891. if (argLen == 0)
  1892. return Empty;
  1893. string [] strings = new string [argLen];
  1894. int len = 0;
  1895. for (int i = 0; i < argLen; i++) {
  1896. if (args[i] != null) {
  1897. strings[i] = args[i].ToString ();
  1898. len += strings[i].length;
  1899. }
  1900. }
  1901. return ConcatInternal (strings, len);
  1902. }
  1903. public static String Concat (params String[] values)
  1904. {
  1905. if (values == null)
  1906. throw new ArgumentNullException ("values");
  1907. int len = 0;
  1908. for (int i = 0; i < values.Length; i++) {
  1909. String s = values[i];
  1910. if (s != null)
  1911. len += s.length;
  1912. }
  1913. return ConcatInternal (values, len);
  1914. }
  1915. private static unsafe String ConcatInternal (String[] values, int length)
  1916. {
  1917. if (length == 0)
  1918. return Empty;
  1919. String tmp = InternalAllocateStr (length);
  1920. fixed (char* dest = tmp) {
  1921. int pos = 0;
  1922. for (int i = 0; i < values.Length; i++) {
  1923. String source = values[i];
  1924. if (source != null) {
  1925. fixed (char* src = source) {
  1926. CharCopy (dest + pos, src, source.length);
  1927. }
  1928. pos += source.Length;
  1929. }
  1930. }
  1931. }
  1932. return tmp;
  1933. }
  1934. public unsafe String Insert (int startIndex, String value)
  1935. {
  1936. if (value == null)
  1937. throw new ArgumentNullException ("value");
  1938. if (startIndex < 0 || startIndex > this.length)
  1939. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative and must be less than or equal to length of string.");
  1940. if (value.Length == 0)
  1941. return this;
  1942. if (this.Length == 0)
  1943. return value;
  1944. String tmp = InternalAllocateStr (this.length + value.length);
  1945. fixed (char *dest = tmp, src = this, val = value) {
  1946. char *dst = dest;
  1947. CharCopy (dst, src, startIndex);
  1948. dst += startIndex;
  1949. CharCopy (dst, val, value.length);
  1950. dst += value.length;
  1951. CharCopy (dst, src + startIndex, length - startIndex);
  1952. }
  1953. return tmp;
  1954. }
  1955. public static string Intern (string str)
  1956. {
  1957. if (str == null)
  1958. throw new ArgumentNullException ("str");
  1959. return InternalIntern (str);
  1960. }
  1961. public static string IsInterned (string str)
  1962. {
  1963. if (str == null)
  1964. throw new ArgumentNullException ("str");
  1965. return InternalIsInterned (str);
  1966. }
  1967. #if NET_4_0
  1968. public static string Join (string separator, params string [] value)
  1969. #else
  1970. public static string Join (string separator, string [] value)
  1971. #endif
  1972. {
  1973. if (value == null)
  1974. throw new ArgumentNullException ("value");
  1975. if (separator == null)
  1976. separator = Empty;
  1977. return JoinUnchecked (separator, value, 0, value.Length);
  1978. }
  1979. public static string Join (string separator, string[] value, int startIndex, int count)
  1980. {
  1981. if (value == null)
  1982. throw new ArgumentNullException ("value");
  1983. if (startIndex < 0)
  1984. throw new ArgumentOutOfRangeException ("startIndex", "< 0");
  1985. if (count < 0)
  1986. throw new ArgumentOutOfRangeException ("count", "< 0");
  1987. if (startIndex > value.Length - count)
  1988. throw new ArgumentOutOfRangeException ("startIndex", "startIndex + count > value.length");
  1989. if (startIndex == value.Length)
  1990. return Empty;
  1991. if (separator == null)
  1992. separator = Empty;
  1993. return JoinUnchecked (separator, value, startIndex, count);
  1994. }
  1995. private static unsafe string JoinUnchecked (string separator, string[] value, int startIndex, int count)
  1996. {
  1997. // Unchecked parameters
  1998. // startIndex, count must be >= 0; startIndex + count must be <= value.length
  1999. // separator and value must not be null
  2000. int length = 0;
  2001. int maxIndex = startIndex + count;
  2002. // Precount the number of characters that the resulting string will have
  2003. for (int i = startIndex; i < maxIndex; i++) {
  2004. String s = value[i];
  2005. if (s != null)
  2006. length += s.length;
  2007. }
  2008. length += separator.length * (count - 1);
  2009. if (length <= 0)
  2010. return Empty;
  2011. String tmp = InternalAllocateStr (length);
  2012. maxIndex--;
  2013. fixed (char* dest = tmp, sepsrc = separator) {
  2014. // Copy each string from value except the last one and add a separator for each
  2015. int pos = 0;
  2016. for (int i = startIndex; i < maxIndex; i++) {
  2017. String source = value[i];
  2018. if (source != null) {
  2019. if (source.Length > 0) {
  2020. fixed (char* src = source)
  2021. CharCopy (dest + pos, src, source.Length);
  2022. pos += source.Length;
  2023. }
  2024. }
  2025. if (separator.Length > 0) {
  2026. CharCopy (dest + pos, sepsrc, separator.Length);
  2027. pos += separator.Length;
  2028. }
  2029. }
  2030. // Append last string that does not get an additional separator
  2031. String sourceLast = value[maxIndex];
  2032. if (sourceLast != null) {
  2033. if (sourceLast.Length > 0) {
  2034. fixed (char* src = sourceLast)
  2035. CharCopy (dest + pos, src, sourceLast.Length);
  2036. }
  2037. }
  2038. }
  2039. return tmp;
  2040. }
  2041. bool IConvertible.ToBoolean (IFormatProvider provider)
  2042. {
  2043. return Convert.ToBoolean (this, provider);
  2044. }
  2045. byte IConvertible.ToByte (IFormatProvider provider)
  2046. {
  2047. return Convert.ToByte (this, provider);
  2048. }
  2049. char IConvertible.ToChar (IFormatProvider provider)
  2050. {
  2051. return Convert.ToChar (this, provider);
  2052. }
  2053. DateTime IConvertible.ToDateTime (IFormatProvider provider)
  2054. {
  2055. return Convert.ToDateTime (this, provider);
  2056. }
  2057. decimal IConvertible.ToDecimal (IFormatProvider provider)
  2058. {
  2059. return Convert.ToDecimal (this, provider);
  2060. }
  2061. double IConvertible.ToDouble (IFormatProvider provider)
  2062. {
  2063. return Convert.ToDouble (this, provider);
  2064. }
  2065. short IConvertible.ToInt16 (IFormatProvider provider)
  2066. {
  2067. return Convert.ToInt16 (this, provider);
  2068. }
  2069. int IConvertible.ToInt32 (IFormatProvider provider)
  2070. {
  2071. return Convert.ToInt32 (this, provider);
  2072. }
  2073. long IConvertible.ToInt64 (IFormatProvider provider)
  2074. {
  2075. return Convert.ToInt64 (this, provider);
  2076. }
  2077. sbyte IConvertible.ToSByte (IFormatProvider provider)
  2078. {
  2079. return Convert.ToSByte (this, provider);
  2080. }
  2081. float IConvertible.ToSingle (IFormatProvider provider)
  2082. {
  2083. return Convert.ToSingle (this, provider);
  2084. }
  2085. object IConvertible.ToType (Type targetType, IFormatProvider provider)
  2086. {
  2087. if (targetType == null)
  2088. throw new ArgumentNullException ("type");
  2089. return Convert.ToType (this, targetType, provider, false);
  2090. }
  2091. ushort IConvertible.ToUInt16 (IFormatProvider provider)
  2092. {
  2093. return Convert.ToUInt16 (this, provider);
  2094. }
  2095. uint IConvertible.ToUInt32 (IFormatProvider provider)
  2096. {
  2097. return Convert.ToUInt32 (this, provider);
  2098. }
  2099. ulong IConvertible.ToUInt64 (IFormatProvider provider)
  2100. {
  2101. return Convert.ToUInt64 (this, provider);
  2102. }
  2103. public int Length {
  2104. get {
  2105. return length;
  2106. }
  2107. }
  2108. public CharEnumerator GetEnumerator ()
  2109. {
  2110. return new CharEnumerator (this);
  2111. }
  2112. IEnumerator<char> IEnumerable<char>.GetEnumerator ()
  2113. {
  2114. return new CharEnumerator (this);
  2115. }
  2116. IEnumerator IEnumerable.GetEnumerator ()
  2117. {
  2118. return new CharEnumerator (this);
  2119. }
  2120. private static void ParseFormatSpecifier (string str, ref int ptr, out int n, out int width,
  2121. out bool left_align, out string format)
  2122. {
  2123. int max = str.Length;
  2124. // parses format specifier of form:
  2125. // N,[\ +[-]M][:F]}
  2126. //
  2127. // where:
  2128. // N = argument number (non-negative integer)
  2129. n = ParseDecimal (str, ref ptr);
  2130. if (n < 0)
  2131. throw new FormatException ("Input string was not in a correct format.");
  2132. // M = width (non-negative integer)
  2133. if (ptr < max && str[ptr] == ',') {
  2134. // White space between ',' and number or sign.
  2135. ++ptr;
  2136. while (ptr < max && Char.IsWhiteSpace (str [ptr]))
  2137. ++ptr;
  2138. int start = ptr;
  2139. format = str.Substring (start, ptr - start);
  2140. left_align = (ptr < max && str [ptr] == '-');
  2141. if (left_align)
  2142. ++ ptr;
  2143. width = ParseDecimal (str, ref ptr);
  2144. if (width < 0)
  2145. throw new FormatException ("Input string was not in a correct format.");
  2146. }
  2147. else {
  2148. width = 0;
  2149. left_align = false;
  2150. format = Empty;
  2151. }
  2152. // F = argument format (string)
  2153. if (ptr < max && str[ptr] == ':') {
  2154. int start = ++ ptr;
  2155. while (ptr < max && str[ptr] != '}')
  2156. ++ ptr;
  2157. format += str.Substring (start, ptr - start);
  2158. }
  2159. else
  2160. format = null;
  2161. if ((ptr >= max) || str[ptr ++] != '}')
  2162. throw new FormatException ("Input string was not in a correct format.");
  2163. }
  2164. private static int ParseDecimal (string str, ref int ptr)
  2165. {
  2166. int p = ptr;
  2167. int n = 0;
  2168. int max = str.Length;
  2169. while (p < max) {
  2170. char c = str[p];
  2171. if (c < '0' || '9' < c)
  2172. break;
  2173. n = n * 10 + c - '0';
  2174. ++ p;
  2175. }
  2176. if (p == ptr || p == max)
  2177. return -1;
  2178. ptr = p;
  2179. return n;
  2180. }
  2181. internal unsafe void InternalSetChar (int idx, char val)
  2182. {
  2183. if ((uint) idx >= (uint) Length)
  2184. throw new ArgumentOutOfRangeException ("idx");
  2185. fixed (char * pStr = &start_char)
  2186. {
  2187. pStr [idx] = val;
  2188. }
  2189. }
  2190. internal unsafe void InternalSetLength (int newLength)
  2191. {
  2192. if (newLength > length)
  2193. throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
  2194. // zero terminate, we can pass string objects directly via pinvoke
  2195. // we also zero the rest of the string, since the new GC needs to be
  2196. // able to handle the changing size (it will skip the 0 bytes).
  2197. fixed (char * pStr = &start_char) {
  2198. char *p = pStr + newLength;
  2199. char *end = pStr + length;
  2200. while (p < end) {
  2201. p [0] = '\0';
  2202. p++;
  2203. }
  2204. }
  2205. length = newLength;
  2206. }
  2207. [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
  2208. // When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
  2209. public unsafe override int GetHashCode ()
  2210. {
  2211. fixed (char * c = this) {
  2212. char * cc = c;
  2213. char * end = cc + length - 1;
  2214. int h = 0;
  2215. for (;cc < end; cc += 2) {
  2216. h = (h << 5) - h + *cc;
  2217. h = (h << 5) - h + cc [1];
  2218. }
  2219. ++end;
  2220. if (cc < end)
  2221. h = (h << 5) - h + *cc;
  2222. return h;
  2223. }
  2224. }
  2225. #if NET_4_0
  2226. [ComVisible(false)]
  2227. public static string Concat (IEnumerable<string> values)
  2228. {
  2229. if (values == null)
  2230. throw new ArgumentNullException ("values");
  2231. var stringList = new List<string> ();
  2232. int len = 0;
  2233. foreach (var v in values){
  2234. if (v == null)
  2235. continue;
  2236. len += v.Length;
  2237. stringList.Add (v);
  2238. }
  2239. return ConcatInternal (stringList.ToArray (), len);
  2240. }
  2241. [ComVisibleAttribute(false)]
  2242. public static string Concat<T> (IEnumerable<T> values)
  2243. {
  2244. if (values == null)
  2245. throw new ArgumentNullException ("values");
  2246. var stringList = new List<string> ();
  2247. int len = 0;
  2248. foreach (var v in values){
  2249. string sr = v.ToString ();
  2250. len += sr.Length;
  2251. stringList.Add (sr);
  2252. }
  2253. return ConcatInternal (stringList.ToArray (), len);
  2254. }
  2255. [ComVisibleAttribute(false)]
  2256. public static string Join (string separator, IEnumerable<string> values)
  2257. {
  2258. if (separator == null)
  2259. return Concat (values);
  2260. if (values == null)
  2261. throw new ArgumentNullException ("values");
  2262. var stringList = new List<string> ();
  2263. foreach (var v in values)
  2264. stringList.Add (v);
  2265. return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
  2266. }
  2267. [ComVisibleAttribute(false)]
  2268. public static string Join (string separator, params object [] values)
  2269. {
  2270. if (separator == null)
  2271. return Concat (values);
  2272. if (values == null)
  2273. throw new ArgumentNullException ("values");
  2274. var strCopy = new string [values.Length];
  2275. int i = 0;
  2276. foreach (var v in values)
  2277. strCopy [i++] = v.ToString ();
  2278. return JoinUnchecked (separator, strCopy, 0, strCopy.Length);
  2279. }
  2280. [ComVisible (false)]
  2281. public static string Join<T> (string separator, IEnumerable<T> values)
  2282. {
  2283. if (separator == null)
  2284. return Concat<T> (values);
  2285. if (values == null)
  2286. throw new ArgumentNullException ("values");
  2287. var stringList = new List<string> ();
  2288. foreach (var v in values)
  2289. stringList.Add (v.ToString ());
  2290. return JoinUnchecked (separator, stringList.ToArray (), 0, stringList.Count);
  2291. }
  2292. public static bool IsNullOrWhiteSpace (string value)
  2293. #else
  2294. internal static bool IsNullOrWhiteSpace (string value)
  2295. #endif
  2296. {
  2297. if ((value == null) || (value.Length == 0))
  2298. return true;
  2299. foreach (char c in value)
  2300. if (!Char.IsWhiteSpace (c))
  2301. return false;
  2302. return true;
  2303. }
  2304. internal unsafe int GetCaseInsensitiveHashCode ()
  2305. {
  2306. fixed (char * c = this) {
  2307. char * cc = c;
  2308. char * end = cc + length - 1;
  2309. int h = 0;
  2310. for (;cc < end; cc += 2) {
  2311. h = (h << 5) - h + Char.ToUpperInvariant (*cc);
  2312. h = (h << 5) - h + Char.ToUpperInvariant (cc [1]);
  2313. }
  2314. ++end;
  2315. if (cc < end)
  2316. h = (h << 5) - h + Char.ToUpperInvariant (*cc);
  2317. return h;
  2318. }
  2319. }
  2320. // Certain constructors are redirected to CreateString methods with
  2321. // matching argument list. The this pointer should not be used.
  2322. private unsafe String CreateString (sbyte* value)
  2323. {
  2324. if (value == null)
  2325. return Empty;
  2326. byte* bytes = (byte*) value;
  2327. int length = 0;
  2328. try {
  2329. while (bytes++ [0] != 0)
  2330. length++;
  2331. } catch (NullReferenceException) {
  2332. throw new ArgumentOutOfRangeException ("ptr", "Value does not refer to a valid string.");
  2333. }
  2334. return CreateString (value, 0, length, null);
  2335. }
  2336. private unsafe String CreateString (sbyte* value, int startIndex, int length)
  2337. {
  2338. return CreateString (value, startIndex, length, null);
  2339. }
  2340. private unsafe String CreateString (sbyte* value, int startIndex, int length, Encoding enc)
  2341. {
  2342. if (length < 0)
  2343. throw new ArgumentOutOfRangeException ("length", "Non-negative number required.");
  2344. if (startIndex < 0)
  2345. throw new ArgumentOutOfRangeException ("startIndex", "Non-negative number required.");
  2346. if (value + startIndex < value)
  2347. throw new ArgumentOutOfRangeException ("startIndex", "Value, startIndex and length do not refer to a valid string.");
  2348. if (enc == null) {
  2349. if (value == null)
  2350. throw new ArgumentNullException ("value");
  2351. if (length == 0)
  2352. return Empty;
  2353. enc = Encoding.Default;
  2354. }
  2355. byte [] bytes = new byte [length];
  2356. if (length != 0)
  2357. fixed (byte* bytePtr = bytes)
  2358. try {
  2359. memcpy (bytePtr, (byte*) (value + startIndex), length);
  2360. } catch (NullReferenceException) {
  2361. throw new ArgumentOutOfRangeException ("ptr", "Value, startIndex and length do not refer to a valid string.");
  2362. }
  2363. // GetString () is called even when length == 0
  2364. return enc.GetString (bytes);
  2365. }
  2366. unsafe string CreateString (char *value)
  2367. {
  2368. if (value == null)
  2369. return Empty;
  2370. char *p = value;
  2371. int i = 0;
  2372. while (*p != 0) {
  2373. ++i;
  2374. ++p;
  2375. }
  2376. string result = InternalAllocateStr (i);
  2377. if (i != 0) {
  2378. fixed (char *dest = result) {
  2379. CharCopy (dest, value, i);
  2380. }
  2381. }
  2382. return result;
  2383. }
  2384. unsafe string CreateString (char *value, int startIndex, int length)
  2385. {
  2386. if (length == 0)
  2387. return Empty;
  2388. if (value == null)
  2389. throw new ArgumentNullException ("value");
  2390. if (startIndex < 0)
  2391. throw new ArgumentOutOfRangeException ("startIndex");
  2392. if (length < 0)
  2393. throw new ArgumentOutOfRangeException ("length");
  2394. string result = InternalAllocateStr (length);
  2395. fixed (char *dest = result) {
  2396. CharCopy (dest, value + startIndex, length);
  2397. }
  2398. return result;
  2399. }
  2400. unsafe string CreateString (char [] val, int startIndex, int length)
  2401. {
  2402. if (val == null)
  2403. throw new ArgumentNullException ("value");
  2404. if (startIndex < 0)
  2405. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative.");
  2406. if (length < 0)
  2407. throw new ArgumentOutOfRangeException ("length", "Cannot be negative.");
  2408. if (startIndex > val.Length - length)
  2409. throw new ArgumentOutOfRangeException ("startIndex", "Cannot be negative, and should be less than length of string.");
  2410. if (length == 0)
  2411. return Empty;
  2412. string result = InternalAllocateStr (length);
  2413. fixed (char *dest = result, src = val) {
  2414. CharCopy (dest, src + startIndex, length);
  2415. }
  2416. return result;
  2417. }
  2418. unsafe string CreateString (char [] val)
  2419. {
  2420. if (val == null || val.Length == 0)
  2421. return Empty;
  2422. string result = InternalAllocateStr (val.Length);
  2423. fixed (char *dest = result, src = val) {
  2424. CharCopy (dest, src, val.Length);
  2425. }
  2426. return result;
  2427. }
  2428. unsafe string CreateString (char c, int count)
  2429. {
  2430. if (count < 0)
  2431. throw new ArgumentOutOfRangeException ("count");
  2432. if (count == 0)
  2433. return Empty;
  2434. string result = InternalAllocateStr (count);
  2435. fixed (char *dest = result) {
  2436. char *p = dest;
  2437. char *end = p + count;
  2438. while (p < end) {
  2439. *p = c;
  2440. p++;
  2441. }
  2442. }
  2443. return result;
  2444. }
  2445. /* helpers used by the runtime as well as above or eslewhere in corlib */
  2446. internal static unsafe void memset (byte *dest, int val, int len)
  2447. {
  2448. if (len < 8) {
  2449. while (len != 0) {
  2450. *dest = (byte)val;
  2451. ++dest;
  2452. --len;
  2453. }
  2454. return;
  2455. }
  2456. if (val != 0) {
  2457. val = val | (val << 8);
  2458. val = val | (val << 16);
  2459. }
  2460. // align to 4
  2461. int rest = (int)dest & 3;
  2462. if (rest != 0) {
  2463. rest = 4 - rest;
  2464. len -= rest;
  2465. do {
  2466. *dest = (byte)val;
  2467. ++dest;
  2468. --rest;
  2469. } while (rest != 0);
  2470. }
  2471. while (len >= 16) {
  2472. ((int*)dest) [0] = val;
  2473. ((int*)dest) [1] = val;
  2474. ((int*)dest) [2] = val;
  2475. ((int*)dest) [3] = val;
  2476. dest += 16;
  2477. len -= 16;
  2478. }
  2479. while (len >= 4) {
  2480. ((int*)dest) [0] = val;
  2481. dest += 4;
  2482. len -= 4;
  2483. }
  2484. // tail bytes
  2485. while (len > 0) {
  2486. *dest = (byte)val;
  2487. dest++;
  2488. len--;
  2489. }
  2490. }
  2491. static unsafe void memcpy4 (byte *dest, byte *src, int size) {
  2492. /*while (size >= 32) {
  2493. // using long is better than int and slower than double
  2494. // FIXME: enable this only on correct alignment or on platforms
  2495. // that can tolerate unaligned reads/writes of doubles
  2496. ((double*)dest) [0] = ((double*)src) [0];
  2497. ((double*)dest) [1] = ((double*)src) [1];
  2498. ((double*)dest) [2] = ((double*)src) [2];
  2499. ((double*)dest) [3] = ((double*)src) [3];
  2500. dest += 32;
  2501. src += 32;
  2502. size -= 32;
  2503. }*/
  2504. while (size >= 16) {
  2505. ((int*)dest) [0] = ((int*)src) [0];
  2506. ((int*)dest) [1] = ((int*)src) [1];
  2507. ((int*)dest) [2] = ((int*)src) [2];
  2508. ((int*)dest) [3] = ((int*)src) [3];
  2509. dest += 16;
  2510. src += 16;
  2511. size -= 16;
  2512. }
  2513. while (size >= 4) {
  2514. ((int*)dest) [0] = ((int*)src) [0];
  2515. dest += 4;
  2516. src += 4;
  2517. size -= 4;
  2518. }
  2519. while (size > 0) {
  2520. ((byte*)dest) [0] = ((byte*)src) [0];
  2521. dest += 1;
  2522. src += 1;
  2523. --size;
  2524. }
  2525. }
  2526. static unsafe void memcpy2 (byte *dest, byte *src, int size) {
  2527. while (size >= 8) {
  2528. ((short*)dest) [0] = ((short*)src) [0];
  2529. ((short*)dest) [1] = ((short*)src) [1];
  2530. ((short*)dest) [2] = ((short*)src) [2];
  2531. ((short*)dest) [3] = ((short*)src) [3];
  2532. dest += 8;
  2533. src += 8;
  2534. size -= 8;
  2535. }
  2536. while (size >= 2) {
  2537. ((short*)dest) [0] = ((short*)src) [0];
  2538. dest += 2;
  2539. src += 2;
  2540. size -= 2;
  2541. }
  2542. if (size > 0)
  2543. ((byte*)dest) [0] = ((byte*)src) [0];
  2544. }
  2545. static unsafe void memcpy1 (byte *dest, byte *src, int size) {
  2546. while (size >= 8) {
  2547. ((byte*)dest) [0] = ((byte*)src) [0];
  2548. ((byte*)dest) [1] = ((byte*)src) [1];
  2549. ((byte*)dest) [2] = ((byte*)src) [2];
  2550. ((byte*)dest) [3] = ((byte*)src) [3];
  2551. ((byte*)dest) [4] = ((byte*)src) [4];
  2552. ((byte*)dest) [5] = ((byte*)src) [5];
  2553. ((byte*)dest) [6] = ((byte*)src) [6];
  2554. ((byte*)dest) [7] = ((byte*)src) [7];
  2555. dest += 8;
  2556. src += 8;
  2557. size -= 8;
  2558. }
  2559. while (size >= 2) {
  2560. ((byte*)dest) [0] = ((byte*)src) [0];
  2561. ((byte*)dest) [1] = ((byte*)src) [1];
  2562. dest += 2;
  2563. src += 2;
  2564. size -= 2;
  2565. }
  2566. if (size > 0)
  2567. ((byte*)dest) [0] = ((byte*)src) [0];
  2568. }
  2569. internal static unsafe void memcpy (byte *dest, byte *src, int size) {
  2570. // FIXME: if pointers are not aligned, try to align them
  2571. // so a faster routine can be used. Handle the case where
  2572. // the pointers can't be reduced to have the same alignment
  2573. // (just ignore the issue on x86?)
  2574. if ((((int)dest | (int)src) & 3) != 0) {
  2575. if (((int)dest & 1) != 0 && ((int)src & 1) != 0 && size >= 1) {
  2576. dest [0] = src [0];
  2577. ++dest;
  2578. ++src;
  2579. --size;
  2580. }
  2581. if (((int)dest & 2) != 0 && ((int)src & 2) != 0 && size >= 2) {
  2582. ((short*)dest) [0] = ((short*)src) [0];
  2583. dest += 2;
  2584. src += 2;
  2585. size -= 2;
  2586. }
  2587. if ((((int)dest | (int)src) & 1) != 0) {
  2588. memcpy1 (dest, src, size);
  2589. return;
  2590. }
  2591. if ((((int)dest | (int)src) & 2) != 0) {
  2592. memcpy2 (dest, src, size);
  2593. return;
  2594. }
  2595. }
  2596. memcpy4 (dest, src, size);
  2597. }
  2598. internal static unsafe void CharCopy (char *dest, char *src, int count) {
  2599. // Same rules as for memcpy, but with the premise that
  2600. // chars can only be aligned to even addresses if their
  2601. // enclosing types are correctly aligned
  2602. if ((((int)(byte*)dest | (int)(byte*)src) & 3) != 0) {
  2603. if (((int)(byte*)dest & 2) != 0 && ((int)(byte*)src & 2) != 0 && count > 0) {
  2604. ((short*)dest) [0] = ((short*)src) [0];
  2605. dest++;
  2606. src++;
  2607. count--;
  2608. }
  2609. if ((((int)(byte*)dest | (int)(byte*)src) & 2) != 0) {
  2610. memcpy2 ((byte*)dest, (byte*)src, count * 2);
  2611. return;
  2612. }
  2613. }
  2614. memcpy4 ((byte*)dest, (byte*)src, count * 2);
  2615. }
  2616. internal static unsafe void CharCopyReverse (char *dest, char *src, int count)
  2617. {
  2618. dest += count;
  2619. src += count;
  2620. for (int i = count; i > 0; i--) {
  2621. dest--;
  2622. src--;
  2623. *dest = *src;
  2624. }
  2625. }
  2626. internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
  2627. {
  2628. fixed (char* dest = target, src = source)
  2629. CharCopy (dest + targetIndex, src + sourceIndex, count);
  2630. }
  2631. internal static unsafe void CharCopy (String target, int targetIndex, Char[] source, int sourceIndex, int count)
  2632. {
  2633. fixed (char* dest = target, src = source)
  2634. CharCopy (dest + targetIndex, src + sourceIndex, count);
  2635. }
  2636. // Use this method if you cannot block copy from left to right (e.g. because you are coping within the same string)
  2637. internal static unsafe void CharCopyReverse (String target, int targetIndex, String source, int sourceIndex, int count)
  2638. {
  2639. fixed (char* dest = target, src = source)
  2640. CharCopyReverse (dest + targetIndex, src + sourceIndex, count);
  2641. }
  2642. [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
  2643. unsafe public extern String (char *value);
  2644. [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
  2645. unsafe public extern String (char *value, int startIndex, int length);
  2646. [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
  2647. unsafe public extern String (sbyte *value);
  2648. [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
  2649. unsafe public extern String (sbyte *value, int startIndex, int length);
  2650. [CLSCompliant (false), MethodImplAttribute (MethodImplOptions.InternalCall)]
  2651. unsafe public extern String (sbyte *value, int startIndex, int length, Encoding enc);
  2652. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2653. public extern String (char [] value, int startIndex, int length);
  2654. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2655. public extern String (char [] value);
  2656. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2657. public extern String (char c, int count);
  2658. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2659. internal extern static String InternalAllocateStr (int length);
  2660. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2661. private extern static string InternalIntern (string str);
  2662. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2663. private extern static string InternalIsInterned (string str);
  2664. [MethodImplAttribute (MethodImplOptions.InternalCall)]
  2665. private extern static int GetLOSLimit ();
  2666. }
  2667. }