String.cs 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634
  1. // -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2. //
  3. // System.String.cs
  4. //
  5. // Author:
  6. // Jeffrey Stedfast ([email protected])
  7. //
  8. // (C) 2001 Ximian, Inc. http://www.ximian.com
  9. //
  10. // FIXME: from what I gather from msdn, when a function is to return an empty string
  11. // we should be returning this.Empty - some methods do this and others don't.
  12. // FIXME: I didn't realise until later that `string' has a .Length method and so
  13. // I am missing some proper bounds-checking in some methods. Find these
  14. // instances and throw the ArgumentOutOfBoundsException at the programmer.
  15. // I like pelting programmers with ArgumentOutOfBoundsException's :-)
  16. // FIXME: The ToLower(), ToUpper(), and Compare(..., bool ignoreCase) methods
  17. // need to be made unicode aware.
  18. // FIXME: when you have a char carr[], does carr.Length include the terminating null char?
  19. using System;
  20. using System.Text;
  21. using System.Collections;
  22. using System.Globalization;
  23. namespace System {
  24. public sealed class String : IComparable, ICloneable, IConvertible, IEnumerable {
  25. public static readonly string Empty = "";
  26. private char[] c_str;
  27. private int length;
  28. // Constructors
  29. unsafe public String (char *value)
  30. {
  31. int i;
  32. // FIXME: can I do value.Length here?
  33. if (value == null) {
  34. this.length = 0;
  35. } else {
  36. for (i = 0; *(value + i) != '\0'; i++);
  37. this.length = i;
  38. }
  39. this.c_str = new char [this.length + 1];
  40. for (i = 0; i < this.length; i++)
  41. this.c_str[i] = *(value + i);
  42. this.c_str[i] = '\0';
  43. }
  44. public String (char[] value)
  45. {
  46. int i;
  47. // FIXME: value.Length includes the terminating null char?
  48. this.length = value != null ? strlen (value): 0;
  49. this.c_str = new char [this.length + 1];
  50. for (i = 0; i < this.length; i++)
  51. this.c_str[i] = value[i];
  52. this.c_str[i] = '\0';
  53. }
  54. unsafe public String (sbyte *value)
  55. {
  56. // FIXME: consider unicode?
  57. int i;
  58. // FIXME: can I do value.Length here? */
  59. if (value == null) {
  60. this.length = 0;
  61. } else {
  62. for (i = 0; *(value + i) != '\0'; i++);
  63. this.length = i;
  64. }
  65. this.c_str = new char [this.length + 1];
  66. for (i = 0; i < this.length; i++)
  67. this.c_str[i] = (char) *(value + i);
  68. this.c_str[i] = '\0';
  69. }
  70. public String (char c, int count)
  71. {
  72. int i;
  73. this.length = count;
  74. this.c_str = new char [count + 1];
  75. for (i = 0; i < count; i++)
  76. this.c_str[i] = c;
  77. this.c_str[i] = '\0';
  78. }
  79. unsafe public String (char *value, int startIndex, int length)
  80. {
  81. int i;
  82. if (value == null && startIndex != 0 && length != 0)
  83. throw new ArgumentNullException ();
  84. if (startIndex < 0 || length < 0)
  85. throw new ArgumentOutOfRangeException ();
  86. this.length = length;
  87. this.c_str = new char [length + 1];
  88. for (i = 0; i < length; i++)
  89. this.c_str[i] = *(value + startIndex + i);
  90. this.c_str[i] = '\0';
  91. }
  92. public String (char[] value, int startIndex, int length)
  93. {
  94. int i;
  95. if (value == null && startIndex != 0 && length != 0)
  96. throw new ArgumentNullException ();
  97. if (startIndex < 0 || length < 0)
  98. throw new ArgumentOutOfRangeException ();
  99. this.length = length;
  100. this.c_str = new char [length + 1];
  101. for (i = 0; i < length; i++)
  102. this.c_str[i] = value[startIndex + i];
  103. this.c_str[i] = '\0';
  104. }
  105. unsafe public String (sbyte *value, int startIndex, int length)
  106. {
  107. // FIXME: consider unicode?
  108. int i;
  109. if (value == null && startIndex != 0 && length != 0)
  110. throw new ArgumentNullException ();
  111. if (startIndex < 0 || length < 0)
  112. throw new ArgumentOutOfRangeException ();
  113. this.length = length;
  114. this.c_str = new char [length + 1];
  115. for (i = 0; i < length; i++)
  116. this.c_str[i] = (char) *(value + startIndex + i);
  117. this.c_str[i] = '\0';
  118. }
  119. unsafe public String (sbyte *value, int startIndex, int length, Encoding enc)
  120. {
  121. // FIXME: implement me
  122. }
  123. ~String ()
  124. {
  125. // FIXME: is there anything we need to do here?
  126. /*base.Finalize ();*/
  127. }
  128. // Properties
  129. public int Length {
  130. get {
  131. return this.length;
  132. }
  133. }
  134. // FIXME: is this correct syntax??
  135. public char this [int index] {
  136. get {
  137. if (index > this.length)
  138. throw new ArgumentOutOfRangeException ();
  139. return this.c_str[index];
  140. }
  141. }
  142. // Private helper methods
  143. private static int strlen (char[] str)
  144. {
  145. // FIXME: if str.Length includes terminating null char, then return (str.Length - 1)
  146. return str.Length;
  147. }
  148. private static char tolowerordinal (char c)
  149. {
  150. // FIXME: implement me
  151. return c;
  152. }
  153. private static bool is_lwsp (char c)
  154. {
  155. /* this comes from the msdn docs for String.Trim() */
  156. if ((c >= '\x9' && c <= '\xD') || c == '\x20' || c == '\xA0' ||
  157. (c >= '\x2000' && c <= '\x200B') || c == '\x3000' || c == '\xFEFF')
  158. return true;
  159. else
  160. return false;
  161. }
  162. private static int BoyerMoore (char[] haystack, string needle, int startIndex, int count)
  163. {
  164. /* (hopefully) Unicode-safe Boyer-Moore implementation */
  165. int[] skiptable = new int[65536]; /* our unicode-safe skip-table */
  166. int h, n, he, ne, hc, nc, i;
  167. if (haystack == null || needle == null)
  168. throw new ArgumentNullException ();
  169. /* if the search buffer is shorter than the pattern buffer, we can't match */
  170. if (count < needle.Length)
  171. return -1;
  172. /* return an instant match if the pattern is 0-length */
  173. if (needle.Length == 0)
  174. return startIndex;
  175. /* set a pointer at the end of each string */
  176. ne = needle.Length - 1; /* position of char before '\0' */
  177. he = startIndex + count; /* position of last valid char */
  178. /* init the skip table with the pattern length */
  179. for (i = 0; i < 65536; i++)
  180. skiptable[i] = needle.Length;
  181. /* set the skip value for the chars that *do* appear in the
  182. * pattern buffer (needle) to the distance from the index to
  183. * the end of the pattern buffer. */
  184. for (nc = 0; nc < ne; nc++)
  185. skiptable[(int) needle[nc]] = ne - nc;
  186. h = startIndex;
  187. while (count >= needle.Length) {
  188. hc = h + needle.Length - 1; /* set the haystack compare pointer */
  189. nc = ne; /* set the needle compare pointer */
  190. /* work our way backwards until they don't match */
  191. for (i = 0; nc > 0; nc--, hc--, i++)
  192. if (needle[nc] != haystack[hc])
  193. break;
  194. if (needle[nc] != haystack[hc]) {
  195. n = skiptable[(int) haystack[hc]] - i;
  196. h += n;
  197. count -= n;
  198. } else
  199. return h;
  200. }
  201. return -1;
  202. }
  203. // Methods
  204. public object Clone ()
  205. {
  206. // FIXME: implement me
  207. return null;
  208. }
  209. public static int Compare (string strA, string strB)
  210. {
  211. int i;
  212. /* Does this remind anyone of the nautilus string.h wrappers? ;-) */
  213. if (strA == null) {
  214. if (strB == null)
  215. return 0;
  216. else
  217. return -1;
  218. } else if (strB == null)
  219. return 1;
  220. for (i = 0; strA[i] == strB[i] && strA[i] != '\0'; i++);
  221. return ((int) (strA[i] - strB[i]));
  222. }
  223. public static int Compare (string strA, string strB, bool ignoreCase)
  224. {
  225. int i;
  226. if (!ignoreCase)
  227. return Compare (strA, strB);
  228. /*
  229. * And here I thought Eazel developers were on crack...
  230. * if a string is null it should pelt the programmer with
  231. * ArgumentNullExceptions, damnit!
  232. */
  233. if (strA == null) {
  234. if (strB == null)
  235. return 0;
  236. else
  237. return -1;
  238. } else if (strB == null)
  239. return 1;
  240. for (i = 0; strA[i] != '\0' && strB[i] != '\0'; i++) {
  241. if (Char.ToLower (strA[i]) != Char.ToLower (strB[i]))
  242. break;
  243. }
  244. return ((int) (strA[i] - strB[i]));
  245. }
  246. public static int Compare (string strA, string strB, bool ignoreCase, CultureInfo culture)
  247. {
  248. // FIXME: implement me
  249. return 0;
  250. }
  251. public static int Compare (string strA, int indexA, string strB, int indexB, int length)
  252. {
  253. int i;
  254. if (length < 0 || indexA < 0 || indexB < 0)
  255. throw new ArgumentOutOfRangeException ();
  256. if (indexA > strA.Length || indexB > strB.Length)
  257. throw new ArgumentOutOfRangeException ();
  258. /* And again with the ("" > null) logic... lord have mercy! */
  259. if (strA == null) {
  260. if (strB == null)
  261. return 0;
  262. else
  263. return -1;
  264. } else if (strB == null)
  265. return 1;
  266. for (i = 0; i < length - 1; i++) {
  267. if (strA[indexA + i] != strB[indexB + i])
  268. break;
  269. }
  270. return ((int) (strA[indexA + i] - strB[indexB + i]));
  271. }
  272. public static int Compare (string strA, int indexA, string strB, int indexB,
  273. int length, bool ignoreCase)
  274. {
  275. int i;
  276. if (!ignoreCase)
  277. return Compare (strA, indexA, strB, indexB, length);
  278. if (length < 0 || indexA < 0 || indexB < 0)
  279. throw new ArgumentOutOfRangeException ();
  280. if (indexA > strA.Length || indexB > strB.Length)
  281. throw new ArgumentOutOfRangeException ();
  282. /* When will the hurting stop!?!? */
  283. if (strA == null) {
  284. if (strB == null)
  285. return 0;
  286. else
  287. return -1;
  288. } else if (strB == null)
  289. return 1;
  290. for (i = 0; i < length - 1; i++) {
  291. if (Char.ToLower (strA[indexA + i]) != Char.ToLower (strB[indexB + i]))
  292. break;
  293. }
  294. return ((int) (strA[indexA + i] - strB[indexB + i]));
  295. }
  296. public static int Compare (string strA, int indexA, string strB, int indexB,
  297. int length, bool ignoreCase, CultureInfo culture)
  298. {
  299. if (culture == null)
  300. throw new ArgumentNullException ();
  301. if (length < 0 || indexA < 0 || indexB < 0)
  302. throw new ArgumentOutOfRangeException ();
  303. if (indexA > strA.Length || indexB > strB.Length)
  304. throw new ArgumentOutOfRangeException ();
  305. /* I can't take it anymore! */
  306. if (strA == null) {
  307. if (strB == null)
  308. return 0;
  309. else
  310. return -1;
  311. } else if (strB == null)
  312. return 1;
  313. // FIXME: implement me
  314. return 0;
  315. }
  316. public static int CompareOrdinal (string strA, string strB)
  317. {
  318. int i;
  319. /* Please God, make it stop! */
  320. if (strA == null) {
  321. if (strB == null)
  322. return 0;
  323. else
  324. return -1;
  325. } else if (strB == null)
  326. return 1;
  327. for (i = 0; strA[i] != '\0'; i++) {
  328. char cA, cB;
  329. cA = tolowerordinal (strA[i]);
  330. cB = tolowerordinal (strB[i]);
  331. if (cA != cB)
  332. break;
  333. }
  334. return ((int) (strA[i] - strB[i]));
  335. }
  336. public static int CompareOrdinal (string strA, int indexA, string strB, int indexB,
  337. int length)
  338. {
  339. int i;
  340. if (length < 0 || indexA < 0 || indexB < 0)
  341. throw new ArgumentOutOfRangeException ();
  342. /* Nooooooooo!! */
  343. if (strA == null) {
  344. if (strB == null)
  345. return 0;
  346. else
  347. return -1;
  348. } else if (strB == null)
  349. return 1;
  350. for (i = 0; i < length; i++) {
  351. char cA, cB;
  352. cA = tolowerordinal (strA[indexA + i]);
  353. cB = tolowerordinal (strB[indexB + i]);
  354. if (cA != cB)
  355. break;
  356. }
  357. return ((int) (strA[indexA + i] - strB[indexB + i]));
  358. }
  359. public int CompareTo (object obj)
  360. {
  361. return Compare (this, obj == null ? null : obj.ToString ());
  362. }
  363. public int CompareTo (string str)
  364. {
  365. return Compare (this, str);
  366. }
  367. public static string Concat (object arg)
  368. {
  369. return arg != null ? arg.ToString () : String.Empty;
  370. }
  371. public static string Concat (params object[] args)
  372. {
  373. string[] strings;
  374. char[] str;
  375. int len, i;
  376. if (args == null)
  377. throw new ArgumentNullException ();
  378. strings = new string [args.Length];
  379. len = 0;
  380. i = 0;
  381. foreach (object arg in args) {
  382. /* use Empty for each null argument */
  383. if (arg == null)
  384. strings[i] = String.Empty;
  385. else
  386. strings[i] = arg.ToString ();
  387. len += strings[i].Length;
  388. i++;
  389. }
  390. if (len == 0)
  391. return String.Empty;
  392. str = new char [len + 1];
  393. i = 0;
  394. for (int j = 0; j < strings.Length; j++)
  395. for (int k = 0; k < strings[j].Length; k++)
  396. str[i++] = strings[j][k];
  397. str[i] = '\0';
  398. return new String (str);
  399. }
  400. public static string Concat (params string[] values)
  401. {
  402. int len, i;
  403. char[] str;
  404. if (values == null)
  405. throw new ArgumentNullException ();
  406. len = 0;
  407. foreach (string value in values)
  408. len += value != null ? value.Length : 0;
  409. if (len == 0)
  410. return String.Empty;
  411. str = new char [len + 1];
  412. i = 0;
  413. foreach (string value in values) {
  414. if (value == null)
  415. continue;
  416. for (int j = 0; j < value.Length; j++)
  417. str[i++] = value[j];
  418. }
  419. str[i] = '\0';
  420. return new String (str);
  421. }
  422. public static string Concat (object arg0, object arg1)
  423. {
  424. string str0 = arg0 != null ? arg0.ToString () : String.Empty;
  425. string str1 = arg1 != null ? arg1.ToString () : String.Empty;
  426. return Concat (str0, str1);
  427. }
  428. public static string Concat (string str0, string str1)
  429. {
  430. char[] concat;
  431. int i, j, len;
  432. if (str0 == null)
  433. str0 = String.Empty;
  434. if (str1 == null)
  435. str1 = String.Empty;
  436. len = str0.Length + str1.Length;
  437. if (len == 0)
  438. return String.Empty;
  439. concat = new char [len + 1];
  440. for (i = 0; i < str0.Length; i++)
  441. concat[i] = str0[i];
  442. for (j = 0 ; j < str1.Length; j++)
  443. concat[i + j] = str1[j];
  444. concat[len] = '\0';
  445. return new String (concat);
  446. }
  447. public static string Concat (object arg0, object arg1, object arg2)
  448. {
  449. string str0 = arg0 != null ? arg0.ToString () : String.Empty;
  450. string str1 = arg1 != null ? arg1.ToString () : String.Empty;
  451. string str2 = arg2 != null ? arg2.ToString () : String.Empty;
  452. return Concat (str0, str1, str2);
  453. }
  454. public static string Concat (string str0, string str1, string str2)
  455. {
  456. char[] concat;
  457. int i, j, k, len;
  458. if (str0 == null)
  459. str0 = String.Empty;
  460. if (str1 == null)
  461. str1 = String.Empty;
  462. if (str2 == null)
  463. str2 = String.Empty;
  464. len = str0.Length + str1.Length + str2.Length;
  465. if (len == 0)
  466. return String.Empty;
  467. concat = new char [len + 1];
  468. for (i = 0; i < str0.Length; i++)
  469. concat[i] = str0[i];
  470. for (j = 0; j < str1.Length; j++)
  471. concat[i + j] = str1[j];
  472. for (k = 0; k < str2.Length; k++)
  473. concat[i + j + k] = str2[k];
  474. concat[len] = '\0';
  475. return new String (concat);
  476. }
  477. public static string Concat (string str0, string str1, string str2, string str3)
  478. {
  479. char[] concat;
  480. int i, j, k, l, len;
  481. if (str0 == null)
  482. str0 = String.Empty;
  483. if (str1 == null)
  484. str1 = String.Empty;
  485. if (str2 == null)
  486. str2 = String.Empty;
  487. if (str3 == null)
  488. str3 = String.Empty;
  489. len = str0.Length + str1.Length + str2.Length + str3.Length;
  490. if (len == 0)
  491. return String.Empty;
  492. concat = new char [len + 1];
  493. for (i = 0; i < str0.Length; i++)
  494. concat[i] = str0[i];
  495. for (j = 0; j < str1.Length; j++)
  496. concat[i + j] = str1[j];
  497. for (k = 0; k < str2.Length; k++)
  498. concat[i + j + k] = str2[k];
  499. for (l = 0; l < str3.Length; l++)
  500. concat[i + j + k + l] = str3[l];
  501. concat[len] = '\0';
  502. return new String (concat);
  503. }
  504. public static string Copy (string str)
  505. {
  506. // FIXME: how do I *copy* a string if I can only have 1 of each?
  507. if (str == null)
  508. throw new ArgumentNullException ();
  509. return str;
  510. }
  511. public void CopyTo (int sourceIndex, char[] destination, int destinationIndex, int count)
  512. {
  513. // LAMESPEC: should I null-terminate?
  514. int i;
  515. if (destination == null)
  516. throw new ArgumentNullException ();
  517. if (sourceIndex < 0 || destinationIndex < 0 || count < 0)
  518. throw new ArgumentOutOfRangeException ();
  519. if (sourceIndex + count > this.length)
  520. throw new ArgumentOutOfRangeException ();
  521. if (destinationIndex + count > destination.Length)
  522. throw new ArgumentOutOfRangeException ();
  523. for (i = 0; i < count; i++)
  524. destination[destinationIndex + i] = this.c_str[sourceIndex + i];
  525. }
  526. public bool EndsWith (string value)
  527. {
  528. bool endswith = true;
  529. int start, i;
  530. if (value == null)
  531. throw new ArgumentNullException ();
  532. start = this.length - value.Length;
  533. if (start < 0)
  534. return false;
  535. for (i = start; i < this.length && endswith; i++)
  536. endswith = this.c_str[i] == value[i - start];
  537. return endswith;
  538. }
  539. public override bool Equals (object obj)
  540. {
  541. if (!(obj is String))
  542. return false;
  543. return this == (String) obj;
  544. }
  545. public bool Equals (string value)
  546. {
  547. return this == value;
  548. }
  549. public static bool Equals (string a, string b)
  550. {
  551. return a == b;
  552. }
  553. public static string Format (string format, object arg0)
  554. {
  555. // FIXME: implement me
  556. return null;
  557. }
  558. public static string Format (string format, params object[] args)
  559. {
  560. // FIXME: implement me
  561. return null;
  562. }
  563. public static string Format (IFormatProvider provider, string format, params object[] args)
  564. {
  565. // FIXME: implement me
  566. return null;
  567. }
  568. public static string Format (string format, object arg0, object arg1)
  569. {
  570. // FIXME: implement me
  571. return null;
  572. }
  573. public static string Format (string format, object arg0, object arg1, object arg2)
  574. {
  575. // FIXME: implement me
  576. return null;
  577. }
  578. //public CharEnumerator GetEnumerator ()
  579. public IEnumerator GetEnumerator ()
  580. {
  581. // FIXME: implement me
  582. return null;
  583. }
  584. public override int GetHashCode ()
  585. {
  586. // FIXME: implement me
  587. return 0;
  588. }
  589. public new Type GetType ()
  590. {
  591. // FIXME: implement me
  592. return null;
  593. }
  594. public TypeCode GetTypeCode ()
  595. {
  596. // FIXME: implement me
  597. return 0;
  598. }
  599. public int IndexOf (char value)
  600. {
  601. return IndexOf (value, 0, this.length);
  602. }
  603. public int IndexOf (string value)
  604. {
  605. return IndexOf (value, 0, this.length);
  606. }
  607. public int IndexOf (char value, int startIndex)
  608. {
  609. return IndexOf (value, startIndex, this.length - startIndex);
  610. }
  611. public int IndexOf (string value, int startIndex)
  612. {
  613. return IndexOf (value, startIndex, this.length - startIndex);
  614. }
  615. public int IndexOf (char value, int startIndex, int count)
  616. {
  617. int i;
  618. if (startIndex < 0 || count < 0 || startIndex + count > this.length)
  619. throw new ArgumentOutOfRangeException ();
  620. for (i = startIndex; i - startIndex < count; i++)
  621. if (this.c_str[i] == value)
  622. return i;
  623. return -1;
  624. }
  625. public int IndexOf (string value, int startIndex, int count)
  626. {
  627. int i;
  628. if (value == null)
  629. throw new ArgumentNullException ();
  630. if (startIndex < 0 || count < 0 || startIndex + count > this.length)
  631. throw new ArgumentOutOfRangeException ();
  632. return BoyerMoore (this.c_str, value, startIndex, count);
  633. #if XXX
  634. for (i = startIndex; i - startIndex + value.Length <= count; ) {
  635. if (this.c_str[i] == value[0]) {
  636. bool equal = true;
  637. int j, nexti = 0;
  638. for (j = 1; equal && value[j] != '\0'; j++) {
  639. equal = this.c_str[i + j] == value[j];
  640. if (this.c_str[i + j] == value[0] && nexti == 0)
  641. nexti = i + j;
  642. }
  643. if (equal)
  644. return i;
  645. if (nexti != 0)
  646. i = nexti;
  647. else
  648. i += j;
  649. } else
  650. i++;
  651. }
  652. return -1;
  653. #endif
  654. }
  655. public int IndexOfAny (char[] values)
  656. {
  657. return IndexOfAny (values, 0, this.length);
  658. }
  659. public int IndexOfAny (char[] values, int startIndex)
  660. {
  661. return IndexOfAny (values, startIndex, this.length - startIndex);
  662. }
  663. public int IndexOfAny (char[] values, int startIndex, int count)
  664. {
  665. if (values == null)
  666. throw new ArgumentNullException ();
  667. if (startIndex < 0 || count < 0 || startIndex + count > this.length)
  668. throw new ArgumentOutOfRangeException ();
  669. for (int i = startIndex; i < startIndex + count; i++) {
  670. for (int j = 0; j < strlen (values); j++) {
  671. if (this.c_str[i] == values[j])
  672. return i;
  673. }
  674. }
  675. return -1;
  676. }
  677. public string Insert (int startIndex, string value)
  678. {
  679. char[] str;
  680. int i, j;
  681. if (value == null)
  682. throw new ArgumentNullException ();
  683. if (startIndex < 0 || startIndex > this.length)
  684. throw new ArgumentOutOfRangeException ();
  685. str = new char [value.Length + this.length + 1];
  686. for (i = 0; i < startIndex; i++)
  687. str[i] = this.c_str[i];
  688. for (j = 0; j < value.Length; j++)
  689. str[i + j] = value[j];
  690. for ( ; i < this.length; i++)
  691. str[i + j] = this.c_str[i];
  692. str[i + j] = '\0';
  693. return new String (str);
  694. }
  695. public static string Intern (string str)
  696. {
  697. if (str == null)
  698. throw new ArgumentNullException ();
  699. // FIXME: implement me
  700. return null;
  701. }
  702. public static string IsInterned (string str)
  703. {
  704. if (str == null)
  705. throw new ArgumentNullException ();
  706. // FIXME: implement me
  707. return null;
  708. }
  709. public static string Join (string separator, string[] value)
  710. {
  711. return Join (separator, value, 0, value.Length);
  712. }
  713. public static string Join (string separator, string[] value, int startIndex, int count)
  714. {
  715. // LAMESPEC: msdn doesn't specify what happens when separator is null
  716. int len, i, j, used;
  717. char[] str;
  718. if (separator == null || value == null)
  719. throw new ArgumentNullException ();
  720. if (startIndex + count > value.Length)
  721. throw new ArgumentOutOfRangeException ();
  722. len = 0;
  723. for (i = startIndex, used = 0; used < count; i++, used++) {
  724. if (i != startIndex)
  725. len += separator.Length;
  726. len += value[i].Length;
  727. }
  728. // We have no elements to join?
  729. if (i == startIndex)
  730. return String.Empty;
  731. str = new char [len + 1];
  732. for (i = 0; i < value[startIndex].Length; i++)
  733. str[i] = value[startIndex][i];
  734. used = 1;
  735. for (j = startIndex + 1; used < count; j++, used++) {
  736. int k;
  737. for (k = 0; k < separator.Length; k++)
  738. str[i++] = separator[k];
  739. for (k = 0; k < value[j].Length; k++)
  740. str[i++] = value[j][k];
  741. }
  742. str[i] = '\0';
  743. return new String (str);
  744. }
  745. public int LastIndexOf (char value)
  746. {
  747. for (int i = this.length; i >= 0; i--) {
  748. if (this.c_str[i] == value)
  749. return i;
  750. }
  751. return -1;
  752. }
  753. public int LastIndexOf (string value)
  754. {
  755. return LastIndexOf (value, this.length, this.length);
  756. }
  757. public int LastIndexOf (char value, int startIndex)
  758. {
  759. if (startIndex < 0 || startIndex > this.length)
  760. throw new ArgumentOutOfRangeException ();
  761. for (int i = startIndex; i >= 0; i--) {
  762. if (this.c_str[i] == value)
  763. return i;
  764. }
  765. return -1;
  766. }
  767. public int LastIndexOf (string value, int startIndex)
  768. {
  769. return LastIndexOf (value, startIndex, this.length);
  770. }
  771. public int LastIndexOf (char value, int startIndex, int count)
  772. {
  773. if (startIndex < 0 || count < 0)
  774. throw new ArgumentOutOfRangeException ();
  775. if (startIndex > this.length || startIndex - count < 0)
  776. throw new ArgumentOutOfRangeException ();
  777. for (int i = startIndex; i >= startIndex - count; i--) {
  778. if (this.c_str[i] == value)
  779. return i;
  780. }
  781. return -1;
  782. }
  783. public int LastIndexOf (string value, int startIndex, int count)
  784. {
  785. // LAMESPEC: currently I'm using startIndex as the 0-position in the comparison,
  786. // but maybe it's the end-position in MS's implementation?
  787. // msdn is unclear on this point. I think this is correct though.
  788. int i, len;
  789. if (value == null)
  790. throw new ArgumentNullException ();
  791. if (startIndex < 0 || startIndex > this.length)
  792. throw new ArgumentOutOfRangeException ();
  793. if (count < 0 || startIndex - count < 0)
  794. throw new ArgumentOutOfRangeException ();
  795. if (value == String.Empty)
  796. return startIndex;
  797. if (startIndex + value.Length > this.length) {
  798. /* just a little optimization */
  799. int start;
  800. start = this.length - value.Length;
  801. count -= startIndex - start;
  802. startIndex = start;
  803. }
  804. // FIXME: use a reversed-unicode-safe-Boyer-Moore?
  805. len = value.Length - 1;
  806. for (i = startIndex; i >= startIndex - count; i--) {
  807. if (this.c_str[i + len] == value[len]) {
  808. bool equal = true;
  809. int j;
  810. for (j = len - 1; equal && j >= 0; j--)
  811. equal = this.c_str[i + j] == value[j];
  812. if (equal)
  813. return i;
  814. }
  815. }
  816. return -1;
  817. }
  818. public int LastIndexOfAny (char[] values)
  819. {
  820. return LastIndexOfAny (values, this.length, this.length);
  821. }
  822. public int LastIndexOfAny (char[] values, int startIndex)
  823. {
  824. return LastIndexOfAny (values, startIndex, startIndex);
  825. }
  826. public int LastIndexOfAny (char[] values, int startIndex, int count)
  827. {
  828. int i;
  829. if (values == null)
  830. throw new ArgumentNullException ();
  831. if (startIndex < 0 || count < 0 || startIndex - count < 0)
  832. throw new ArgumentOutOfRangeException ();
  833. for (i = startIndex; i >= startIndex - count; i--) {
  834. for (int j = 0; j < strlen (values); j++) {
  835. if (this.c_str[i] == values[j])
  836. return i;
  837. }
  838. }
  839. return -1;
  840. }
  841. public string PadLeft (int totalWidth)
  842. {
  843. return PadLeft (totalWidth, ' ');
  844. }
  845. public string PadLeft (int totalWidth, char padChar)
  846. {
  847. char[] str;
  848. int i, j;
  849. if (totalWidth < 0)
  850. throw new ArgumentException ();
  851. str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
  852. for (i = 0; i < totalWidth - this.length; i++)
  853. str[i] = padChar;
  854. for (j = 0; j < this.length; i++, j++)
  855. str[i] = this.c_str[j];
  856. str[i] = '\0';
  857. return new String (str);
  858. }
  859. public string PadRight (int totalWidth)
  860. {
  861. return PadRight (totalWidth, ' ');
  862. }
  863. public string PadRight (int totalWidth, char padChar)
  864. {
  865. char[] str;
  866. int i;
  867. if (totalWidth < 0)
  868. throw new ArgumentException ();
  869. str = new char [totalWidth > this.length ? totalWidth : this.length + 1];
  870. for (i = 0; i < this.length; i++)
  871. str[i] = this.c_str[i];
  872. for ( ; i < str.Length; i++)
  873. str[i] = padChar;
  874. str[i] = '\0';
  875. return new String (str);
  876. }
  877. public string Remove (int startIndex, int count)
  878. {
  879. char[] str;
  880. int i, j, len;
  881. if (startIndex < 0 || count < 0 || startIndex + count > this.length)
  882. throw new ArgumentOutOfRangeException ();
  883. len = this.length - count;
  884. if (len == 0)
  885. return String.Empty;
  886. str = new char [len + 1];
  887. for (i = 0; i < startIndex; i++)
  888. str[i] = this.c_str[i];
  889. for (j = i + count; j < this.length; j++)
  890. str[i++] = this.c_str[j];
  891. str[i] = '\0';
  892. return new String (str);
  893. }
  894. public string Replace (char oldChar, char newChar)
  895. {
  896. char[] str;
  897. int i;
  898. str = new char [this.length + 1];
  899. for (i = 0; i < this.length; i++) {
  900. if (this.c_str[i] == oldChar)
  901. str[i] = newChar;
  902. else
  903. str[i] = this.c_str[i];
  904. }
  905. str[i] = '\0';
  906. return new String (str);
  907. }
  908. public string Replace (string oldValue, string newValue)
  909. {
  910. // LAMESPEC: msdn doesn't specify what to do if either args is null
  911. int index, len, i, j;
  912. char[] str;
  913. if (oldValue == null || newValue == null)
  914. throw new ArgumentNullException ();
  915. // Use IndexOf in case I later rewrite it to use Boyer-Moore
  916. index = IndexOf (oldValue, 0);
  917. if (index == -1) {
  918. // This is the easy one ;-)
  919. return Substring (0, this.length);
  920. }
  921. len = this.length - oldValue.Length + newValue.Length;
  922. if (len == 0)
  923. return String.Empty;
  924. str = new char [len + 1];
  925. for (i = 0; i < index; i++)
  926. str[i] = this.c_str[i];
  927. for (j = 0; j < newValue.Length; j++)
  928. str[i++] = newValue[j];
  929. for (j = index + oldValue.Length; j < this.length; j++)
  930. str[i++] = this.c_str[j];
  931. str[i] = '\0';
  932. return new String (str);
  933. }
  934. private int splitme (char[] separators, int startIndex)
  935. {
  936. /* this is basically a customized IndexOfAny() for the Split() methods */
  937. for (int i = startIndex; i < this.length; i++) {
  938. if (separators != null) {
  939. foreach (char sep in separators) {
  940. if (this.c_str[i] == sep)
  941. return i - startIndex;
  942. }
  943. } else if (is_lwsp (this.c_str[i])) {
  944. return i - startIndex;
  945. }
  946. }
  947. return -1;
  948. }
  949. public string[] Split (params char[] separator)
  950. {
  951. /**
  952. * split:
  953. * @separator: delimiting chars or null to split on whtspc
  954. *
  955. * Returns: 1. An array consisting of a single
  956. * element (@this) if none of the delimiting
  957. * chars appear in @this. 2. An array of
  958. * substrings which are delimited by one of
  959. * the separator chars. 3. An array of
  960. * substrings separated by whitespace if
  961. * @separator is null. The Empty string should
  962. * be returned wherever 2 delimiting chars are
  963. * adjacent.
  964. **/
  965. // FIXME: would using a Queue be better?
  966. string[] strings;
  967. ArrayList list;
  968. int index, len;
  969. list = new ArrayList ();
  970. for (index = 0, len = 0; index < this.length; index += len + 1) {
  971. len = splitme (separator, index);
  972. len = len > -1 ? len : this.length - index;
  973. if (len == 0) {
  974. list.Add (String.Empty);
  975. } else {
  976. char[] str;
  977. int i;
  978. str = new char [len + 1];
  979. for (i = 0; i < len; i++)
  980. str[i] = this.c_str[index + i];
  981. str[i] = '\0';
  982. list.Add (new String (str));
  983. }
  984. }
  985. strings = new string [list.Count];
  986. if (list.Count == 1) {
  987. /* special case for an array holding @this */
  988. strings[0] = this;
  989. } else {
  990. for (index = 0; index < list.Count; index++)
  991. strings[index] = (string) list[index];
  992. }
  993. return strings;
  994. }
  995. public string[] Split (char[] separator, int maxCount)
  996. {
  997. // FIXME: what to do if maxCount <= 0?
  998. // FIXME: would using Queue be better than ArrayList?
  999. string[] strings;
  1000. ArrayList list;
  1001. int index, len, used;
  1002. used = 0;
  1003. list = new ArrayList ();
  1004. for (index = 0, len = 0; index < this.length && used < maxCount; index += len + 1) {
  1005. len = splitme (separator, index);
  1006. len = len > -1 ? len : this.length - index;
  1007. if (len == 0) {
  1008. list.Add (String.Empty);
  1009. } else {
  1010. char[] str;
  1011. int i;
  1012. str = new char [len + 1];
  1013. for (i = 0; i < len; i++)
  1014. str[i] = this.c_str[index + i];
  1015. str[i] = '\0';
  1016. list.Add (new String (str));
  1017. }
  1018. used++;
  1019. }
  1020. /* fit the remaining chunk of the @this into it's own element */
  1021. if (index != this.length) {
  1022. char[] str;
  1023. int i;
  1024. str = new char [this.length - index + 1];
  1025. for (i = index; i < this.length; i++)
  1026. str[i - index] = this.c_str[i];
  1027. str[i - index] = '\0';
  1028. list.Add (new String (str));
  1029. }
  1030. strings = new string [list.Count];
  1031. if (list.Count == 1) {
  1032. /* special case for an array holding @this */
  1033. strings[0] = this;
  1034. } else {
  1035. for (index = 0; index < list.Count; index++)
  1036. strings[index] = (string) list[index];
  1037. }
  1038. return strings;
  1039. }
  1040. public bool StartsWith (string value)
  1041. {
  1042. bool startswith = true;
  1043. int i;
  1044. if (value == null)
  1045. throw new ArgumentNullException ();
  1046. if (value.Length > this.length)
  1047. return false;
  1048. for (i = 0; i < value.Length && startswith; i++)
  1049. startswith = startswith && value[i] == this.c_str[i];
  1050. return startswith;
  1051. }
  1052. public string Substring (int startIndex)
  1053. {
  1054. char[] str;
  1055. int i, len;
  1056. if (startIndex < 0 || startIndex > this.length)
  1057. throw new ArgumentOutOfRangeException ();
  1058. len = this.length - startIndex;
  1059. if (len == 0)
  1060. return String.Empty;
  1061. str = new char [len + 1];
  1062. for (i = startIndex; i < this.length; i++)
  1063. str[i - startIndex] = this.c_str[i];
  1064. str[i] = '\0';
  1065. return new String (str);
  1066. }
  1067. public string Substring (int startIndex, int length)
  1068. {
  1069. char[] str;
  1070. int i;
  1071. if (startIndex < 0 || length < 0 || startIndex + length > this.length)
  1072. throw new ArgumentOutOfRangeException ();
  1073. if (length == 0)
  1074. return String.Empty;
  1075. str = new char [length + 1];
  1076. for (i = startIndex; i < startIndex + length; i++)
  1077. str[i - startIndex] = this.c_str[i];
  1078. str[i] = '\0';
  1079. return new String (str);
  1080. }
  1081. public bool ToBoolean (IFormatProvider provider)
  1082. {
  1083. // FIXME: implement me
  1084. return false;
  1085. }
  1086. public byte ToByte (IFormatProvider provider)
  1087. {
  1088. // FIXME: implement me
  1089. return (byte) '\0';
  1090. }
  1091. public char ToChar (IFormatProvider provider)
  1092. {
  1093. // FIXME: implement me
  1094. return '\0';
  1095. }
  1096. public char[] ToCharArray ()
  1097. {
  1098. return ToCharArray (0, this.length);
  1099. }
  1100. public char[] ToCharArray (int startIndex, int length)
  1101. {
  1102. char[] chars;
  1103. int i;
  1104. if (startIndex < 0 || length < 0 || startIndex + length > this.length)
  1105. throw new ArgumentOutOfRangeException ();
  1106. chars = new char [length + 1];
  1107. for (i = startIndex; i < length; i++)
  1108. chars[i - startIndex] = this.c_str[i];
  1109. chars[length] = '\0';
  1110. return chars;
  1111. }
  1112. public DateTime ToDateTime (IFormatProvider provider)
  1113. {
  1114. // FIXME: implement me
  1115. return new DateTime (0);
  1116. }
  1117. public decimal ToDecimal (IFormatProvider provider)
  1118. {
  1119. // FIXME: implement me
  1120. return 0.0M;
  1121. }
  1122. public double ToDouble (IFormatProvider provider)
  1123. {
  1124. // FIXME: implement me
  1125. return 0.0;
  1126. }
  1127. public short ToInt16 (IFormatProvider provider)
  1128. {
  1129. // FIXME: implement me
  1130. return 0;
  1131. }
  1132. public int ToInt32 (IFormatProvider provider)
  1133. {
  1134. // FIXME: implement me
  1135. return 0;
  1136. }
  1137. public long ToInt64 (IFormatProvider provider)
  1138. {
  1139. // FIXME: implement me
  1140. return 0;
  1141. }
  1142. public string ToLower ()
  1143. {
  1144. char[] str;
  1145. int i;
  1146. str = new char [this.length + 1];
  1147. for (i = 0; i < this.length; i++)
  1148. str[i] = Char.ToLower (this.c_str[i]);
  1149. str[i] = '\0';
  1150. return new String (str);
  1151. }
  1152. public string ToLower (CultureInfo culture)
  1153. {
  1154. // FIXME: implement me
  1155. return null;
  1156. }
  1157. public sbyte ToSByte (IFormatProvider provider)
  1158. {
  1159. // FIXME: implement me
  1160. return 0;
  1161. }
  1162. public float ToSingle (IFormatProvider provider)
  1163. {
  1164. // FIXME: implement me
  1165. return 0.0F;
  1166. }
  1167. public override string ToString ()
  1168. {
  1169. return Substring (0, this.length);
  1170. }
  1171. public string ToString (IFormatProvider format)
  1172. {
  1173. // FIXME: implement me
  1174. return null;
  1175. }
  1176. public object ToType (Type conversionType, IFormatProvider provider)
  1177. {
  1178. // FIXME: implement me
  1179. return null;
  1180. }
  1181. public ushort ToUInt16 (IFormatProvider provider)
  1182. {
  1183. // FIXME: implement me
  1184. return 0;
  1185. }
  1186. public uint ToUInt32 (IFormatProvider provider)
  1187. {
  1188. // FIXME: implement me
  1189. return 0;
  1190. }
  1191. public ulong ToUInt64 (IFormatProvider provider)
  1192. {
  1193. // FIXME: implement me
  1194. return 0;
  1195. }
  1196. public string ToUpper ()
  1197. {
  1198. char[] str;
  1199. int i;
  1200. str = new char [this.length + 1];
  1201. for (i = 0; i < this.length; i++)
  1202. str[i] = Char.ToUpper (this.c_str[i]);
  1203. str[i] = '\0';
  1204. return new String (str);
  1205. }
  1206. public string ToUpper (CultureInfo culture)
  1207. {
  1208. // FIXME: implement me
  1209. return null;
  1210. }
  1211. public string Trim ()
  1212. {
  1213. return Trim (null);
  1214. }
  1215. public string Trim (params char[] trimChars)
  1216. {
  1217. int begin, end;
  1218. bool matches;
  1219. matches = true;
  1220. for (begin = 0; matches && begin < this.length; begin++) {
  1221. if (trimChars != null) {
  1222. matches = false;
  1223. foreach (char c in trimChars) {
  1224. matches = this.c_str[begin] == c;
  1225. if (matches)
  1226. break;
  1227. }
  1228. } else {
  1229. matches = is_lwsp (this.c_str[begin]);
  1230. }
  1231. }
  1232. matches = true;
  1233. for (end = this.length; end > begin; end--) {
  1234. if (trimChars != null) {
  1235. matches = false;
  1236. foreach (char c in trimChars) {
  1237. matches = this.c_str[end] == c;
  1238. if (matches)
  1239. break;
  1240. }
  1241. } else {
  1242. matches = is_lwsp (this.c_str[end]);
  1243. }
  1244. }
  1245. if (begin == end)
  1246. return String.Empty;
  1247. return Substring (begin, end - begin);
  1248. }
  1249. public string TrimEnd (params char[] trimChars)
  1250. {
  1251. bool matches = true;
  1252. int end;
  1253. for (end = this.length; end > 0; end--) {
  1254. if (trimChars != null) {
  1255. matches = false;
  1256. foreach (char c in trimChars) {
  1257. matches = this.c_str[end] == c;
  1258. if (matches)
  1259. break;
  1260. }
  1261. } else {
  1262. matches = is_lwsp (this.c_str[end]);
  1263. }
  1264. }
  1265. if (end == 0)
  1266. return String.Empty;
  1267. return Substring (0, end);
  1268. }
  1269. public string TrimStart (params char[] trimChars)
  1270. {
  1271. bool matches = true;
  1272. int begin;
  1273. for (begin = 0; matches && begin < this.length; begin++) {
  1274. if (trimChars != null) {
  1275. matches = false;
  1276. foreach (char c in trimChars) {
  1277. matches = this.c_str[begin] == c;
  1278. if (matches)
  1279. break;
  1280. }
  1281. } else {
  1282. matches = is_lwsp (this.c_str[begin]);
  1283. }
  1284. }
  1285. if (begin == this.length)
  1286. return String.Empty;
  1287. return Substring (begin, this.length - begin);
  1288. }
  1289. // Operators
  1290. public static bool operator ==(string a, string b)
  1291. {
  1292. if (a.length != b.length)
  1293. return false;
  1294. int l = a.length;
  1295. for (int i = 0; i < l; i++)
  1296. if (a.c_str [i] != b.c_str [i])
  1297. return false;
  1298. return true;
  1299. }
  1300. public static bool operator !=(string a, string b)
  1301. {
  1302. return !(a == b);
  1303. }
  1304. }
  1305. }