2
0

StringBuilder.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
  2. //
  3. // System.Text.StringBuilder
  4. //
  5. // Author: Marcin Szczepanski ([email protected])
  6. //
  7. // TODO: Make sure the coding complies to the ECMA draft, there's some
  8. // variable names that probably don't (like sString)
  9. //
  10. using System.Runtime.CompilerServices;
  11. namespace System.Text {
  12. [Serializable]
  13. public sealed class StringBuilder {
  14. private const int defaultCapacity = 16;
  15. private int sCapacity;
  16. private int sLength;
  17. private char[] sString;
  18. private int sMaxCapacity = Int32.MaxValue;
  19. public StringBuilder(string value, int startIndex, int length, int capacity) {
  20. // first, check the parameters and throw appropriate exceptions if needed
  21. if(null==value) {
  22. throw new System.ArgumentNullException("value");
  23. }
  24. // make sure startIndex is zero or positive
  25. if(startIndex < 0) {
  26. throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex cannot be less than zero.");
  27. }
  28. // make sure length is zero or positive
  29. if(length < 0) {
  30. throw new System.ArgumentOutOfRangeException("length", length, "Length cannot be less than zero.");
  31. }
  32. // make sure startIndex and length give a valid substring of value
  33. if(startIndex + (length -1) > (value.Length - 1) ) {
  34. throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");
  35. }
  36. // the capacity must be at least as big as the default capacity
  37. sCapacity = Math.Max(capacity, defaultCapacity);
  38. // LAMESPEC: what to do if capacity is too small to hold the substring?
  39. // Like the MS implementation, double the capacity until it is large enough
  40. while (sCapacity < length) {
  41. // However, take care not to double if that would make the number
  42. // larger than what an int can hold
  43. if (sCapacity <= Int32.MaxValue / 2) {
  44. sCapacity *= 2;
  45. }
  46. else{
  47. sCapacity = Int32.MaxValue;
  48. }
  49. }
  50. sString = new char[sCapacity];
  51. sLength = length;
  52. // if the length is not zero, then we have to copy some characters
  53. if (sLength > 0) {
  54. // Copy the correct number of characters into the internal array
  55. value.CopyTo (startIndex, sString, 0, sLength);
  56. }
  57. }
  58. public StringBuilder () : this(String.Empty, 0, 0, 0) {}
  59. public StringBuilder( int capacity ) : this(String.Empty, 0, 0, capacity) {}
  60. public StringBuilder( int capacity, int maxCapacity ) : this(String.Empty, 0, 0, capacity) {
  61. if(capacity > maxCapacity) {
  62. throw new System.ArgumentOutOfRangeException("capacity", "Capacity exceeds maximum capacity.");
  63. }
  64. sMaxCapacity = maxCapacity;
  65. }
  66. public StringBuilder( string value ) : this(value, 0, value == null ? 0 : value.Length, value == null? 0 : value.Length) {
  67. }
  68. public StringBuilder( string value, int capacity) : this(value, 0, value.Length, capacity) {}
  69. public int MaxCapacity {
  70. get {
  71. // MS runtime always returns Int32.MaxValue.
  72. return sMaxCapacity;
  73. }
  74. }
  75. public int Capacity {
  76. get {
  77. return sCapacity;
  78. }
  79. set {
  80. if( value < sLength ) {
  81. throw new ArgumentException( "Capacity must be > length" );
  82. } else {
  83. char[] tString = new char[value];
  84. Array.Copy( sString, tString, sLength );
  85. sString = tString;
  86. sCapacity = sString.Length;
  87. }
  88. }
  89. }
  90. public int Length {
  91. get {
  92. return sLength;
  93. }
  94. set {
  95. if( value < 0 || value > MaxCapacity) {
  96. throw new ArgumentOutOfRangeException();
  97. } else {
  98. if( value < sLength ) {
  99. // Truncate current string at value
  100. // LAMESPEC: The spec is unclear as to what to do
  101. // with the capacity when truncating the string.
  102. //
  103. // Don't change the capacity, as this is what
  104. // the MS implementation does.
  105. sLength = value;
  106. } else {
  107. // Expand the capacity to the new length and
  108. // pad the string with spaces.
  109. // LAMESPEC: The spec says to put the spaces on the
  110. // left of the string however the MS implementation
  111. // puts them on the right. We'll do that for
  112. // compatibility (!)
  113. char[] tString = new char[ value ];
  114. int padLength = value - sLength;
  115. string padding = new String( ' ', padLength );
  116. Array.Copy( sString, tString, sLength );
  117. padding.CopyTo (0, tString, sLength, padLength);
  118. sString = tString;
  119. sLength = sString.Length;
  120. sCapacity = value;
  121. }
  122. }
  123. }
  124. }
  125. [IndexerName("Chars")]
  126. public char this[ int index ] {
  127. get {
  128. if( index >= sLength || index < 0 ) {
  129. throw new IndexOutOfRangeException();
  130. }
  131. return sString[ index ];
  132. }
  133. set {
  134. if( index >= sLength || index < 0 ) {
  135. throw new IndexOutOfRangeException();
  136. }
  137. sString[ index ] = value;
  138. }
  139. }
  140. public override string ToString() {
  141. return ToString(0, sLength);
  142. }
  143. public string ToString( int startIndex, int length ) {
  144. if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {
  145. throw new ArgumentOutOfRangeException();
  146. }
  147. return new String( sString, startIndex, length );
  148. }
  149. public int EnsureCapacity( int capacity ) {
  150. if( capacity < 0 ) {
  151. throw new ArgumentOutOfRangeException(
  152. "Capacity must be greater than 0." );
  153. }
  154. if( capacity <= sCapacity ) {
  155. return sCapacity;
  156. } else {
  157. Capacity = capacity;
  158. return sCapacity;
  159. }
  160. }
  161. public bool Equals( StringBuilder sb ) {
  162. if(sLength == sb.Length && this.ToString() == sb.ToString() ) {
  163. return true;
  164. } else {
  165. return false;
  166. }
  167. }
  168. public StringBuilder Remove (int startIndex, int length)
  169. {
  170. if( startIndex < 0 || length < 0 || startIndex + length > sLength )
  171. throw new ArgumentOutOfRangeException();
  172. // Copy everything after the 'removed' part to the start
  173. // of the removed part and truncate the sLength
  174. Array.Copy (sString, startIndex + length, sString, startIndex,
  175. sLength - (startIndex + length));
  176. sLength -= length;
  177. return this;
  178. }
  179. public StringBuilder Replace( char oldChar, char newChar ) {
  180. return Replace( oldChar, newChar, 0, sLength);
  181. }
  182. public StringBuilder Replace( char oldChar, char newChar, int startIndex, int count ) {
  183. if( startIndex + count > sLength || startIndex < 0 || count < 0 ) {
  184. throw new ArgumentOutOfRangeException();
  185. }
  186. for( int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
  187. if( this[replaceIterate] == oldChar ) {
  188. this[replaceIterate] = newChar;
  189. }
  190. }
  191. return this;
  192. }
  193. public StringBuilder Replace( string oldValue, string newValue ) {
  194. return Replace( oldValue, newValue, 0, sLength );
  195. }
  196. public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count ) {
  197. string startString = this.ToString();
  198. StringBuilder newStringB = new StringBuilder();
  199. if( oldValue == null ) {
  200. throw new ArgumentNullException(
  201. "The old value cannot be null.");
  202. }
  203. if( startIndex < 0 || count < 0 || startIndex + count > sLength ) {
  204. throw new ArgumentOutOfRangeException();
  205. }
  206. if( oldValue.Length == 0 ) {
  207. throw new ArgumentException(
  208. "The old value cannot be zero length.");
  209. }
  210. int nextIndex = startIndex; // Where to start the next search
  211. int lastIndex = nextIndex; // Where the last search finished
  212. while( nextIndex != -1 ) {
  213. nextIndex = startString.IndexOf( oldValue, lastIndex);
  214. if( nextIndex != -1 ) {
  215. // The MS implementation won't replace a substring
  216. // if that substring goes over the "count"
  217. // boundary, so we'll make sure the behaviour
  218. // here is the same.
  219. if( nextIndex + oldValue.Length <= startIndex + count ) {
  220. // Add everything to the left of the old
  221. // string
  222. newStringB.Append( startString.Substring( lastIndex, nextIndex - lastIndex ) );
  223. // Add the replacement string
  224. newStringB.Append( newValue );
  225. // Set the next start point to the
  226. // end of the last match
  227. lastIndex = nextIndex + oldValue.Length;
  228. } else {
  229. // We're past the "count" we're supposed to replace within
  230. nextIndex = -1;
  231. newStringB.Append(
  232. startString.Substring( lastIndex ) );
  233. }
  234. } else {
  235. // Append everything left over
  236. newStringB.Append( startString.Substring( lastIndex ) );
  237. }
  238. }
  239. sCapacity = newStringB.sCapacity;
  240. sString = newStringB.sString;
  241. sLength = newStringB.sLength;
  242. return this;
  243. }
  244. /* The Append Methods */
  245. public StringBuilder Append( char[] value ) {
  246. if( sLength + value.Length > sCapacity ) {
  247. // Need more capacity, double the capacity StringBuilder
  248. // and make sure we have at least enough for the value
  249. // if that's going to go over double.
  250. Capacity = value.Length + ( sCapacity + sCapacity);
  251. }
  252. Array.Copy( value, 0, sString, sLength, value.Length );
  253. sLength += value.Length;
  254. return this;
  255. }
  256. public StringBuilder Append( string value ) {
  257. if( value != null ) {
  258. int new_size = sLength + value.Length;
  259. if (new_size > sCapacity)
  260. Capacity = value.Length + sCapacity * 2;
  261. value.CopyTo (0, sString, sLength, value.Length);
  262. sLength = new_size;
  263. return this;
  264. } else {
  265. return null;
  266. }
  267. }
  268. public StringBuilder Append( bool value ) {
  269. return Append (value.ToString());
  270. }
  271. public StringBuilder Append( byte value ) {
  272. return Append (value.ToString());
  273. }
  274. public StringBuilder Append( decimal value ) {
  275. return Append (value.ToString());
  276. }
  277. public StringBuilder Append( double value ) {
  278. return Append (value.ToString());
  279. }
  280. public StringBuilder Append( short value ) {
  281. return Append (value.ToString());
  282. }
  283. public StringBuilder Append( int value ) {
  284. return Append (value.ToString());
  285. }
  286. public StringBuilder Append( long value ) {
  287. return Append (value.ToString());
  288. }
  289. public StringBuilder Append( object value ) {
  290. return Append (value.ToString());
  291. }
  292. [CLSCompliant(false)]
  293. public StringBuilder Append( sbyte value ) {
  294. return Append (value.ToString());
  295. }
  296. public StringBuilder Append( float value ) {
  297. return Append (value.ToString());
  298. }
  299. [CLSCompliant(false)]
  300. public StringBuilder Append( ushort value ) {
  301. return Append (value.ToString());
  302. }
  303. [CLSCompliant(false)]
  304. public StringBuilder Append( uint value ) {
  305. return Append (value.ToString());
  306. }
  307. [CLSCompliant(false)]
  308. public StringBuilder Append( ulong value ) {
  309. return Append (value.ToString());
  310. }
  311. public StringBuilder Append( char value ) {
  312. if( sLength + 1 > sCapacity ) {
  313. // Need more capacity, double the capacity StringBuilder
  314. // and make sure we have at least enough for the value
  315. // if that's going to go over double.
  316. Capacity = 1 + ( sCapacity + sCapacity);
  317. }
  318. sString [sLength] = value;
  319. sLength++;
  320. return this;
  321. }
  322. public StringBuilder Append( char value, int repeatCount ) {
  323. if( repeatCount < 0 ) {
  324. throw new ArgumentOutOfRangeException();
  325. }
  326. return Append( new String( value, repeatCount) );
  327. }
  328. public StringBuilder Append( char[] value, int startIndex, int charCount ) {
  329. if( (charCount < 0 || startIndex < 0) ||
  330. ( charCount + startIndex > value.Length ) ) {
  331. throw new ArgumentOutOfRangeException();
  332. }
  333. if( value == null ) {
  334. if( !(startIndex == 0 && charCount == 0) ) {
  335. throw new ArgumentNullException();
  336. } else {
  337. return this;
  338. }
  339. } else {
  340. char[] appendChars = new char[ charCount ];
  341. Array.Copy( value, startIndex, appendChars, 0, charCount );
  342. return Append( appendChars );
  343. }
  344. }
  345. public StringBuilder Append( string value, int startIndex, int count ) {
  346. if( (count < 0 || startIndex < 0) ||
  347. ( startIndex + count > value.Length ) ) {
  348. throw new ArgumentOutOfRangeException();
  349. }
  350. return Append (value.Substring (startIndex, count));
  351. }
  352. public StringBuilder AppendFormat (string format, object arg0 )
  353. {
  354. string result = String.Format (format, arg0);
  355. return Append (result);
  356. }
  357. public StringBuilder AppendFormat (string format, params object[] args )
  358. {
  359. string result = String.Format (format, args);
  360. return Append (result);
  361. }
  362. public StringBuilder AppendFormat (IFormatProvider provider,
  363. string format,
  364. params object[] args)
  365. {
  366. string result = String.Format (provider, format, args);
  367. return Append (result);
  368. }
  369. public StringBuilder AppendFormat (string format, object arg0, object arg1 )
  370. {
  371. string result = String.Format (format, arg0, arg1);
  372. return Append (result);
  373. }
  374. public StringBuilder AppendFormat (string format, object arg0, object arg1, object arg2 )
  375. {
  376. string result = String.Format (format, arg0, arg1, arg2);
  377. return Append (result);
  378. }
  379. /* The Insert Functions */
  380. public StringBuilder Insert( int index, char[] value ) {
  381. if( index > sLength || index < 0) {
  382. throw new ArgumentOutOfRangeException();
  383. }
  384. if( value == null || value.Length == 0 ) {
  385. return this;
  386. } else {
  387. // Check we have the capacity to insert this array
  388. if( sCapacity < sLength + value.Length ) {
  389. Capacity = value.Length + ( sCapacity + sCapacity );
  390. }
  391. // Move everything to the right of the insert point across
  392. Array.Copy( sString, index, sString, index + value.Length, sLength - index);
  393. // Copy in stuff from the insert buffer
  394. Array.Copy( value, 0, sString, index, value.Length );
  395. sLength += value.Length;
  396. return this;
  397. }
  398. }
  399. public StringBuilder Insert( int index, string value ) {
  400. if (index > sLength || index < 0)
  401. throw new ArgumentOutOfRangeException ("index");
  402. if (value == null || value.Length == 0)
  403. return this;
  404. int len = value.Length;
  405. // Check we have the capacity to insert this array
  406. if (sCapacity < sLength + len)
  407. Capacity = len + ( sCapacity + sCapacity );
  408. // Move everything to the right of the insert point across
  409. Array.Copy (sString, index, sString, index + len, sLength - index);
  410. value.CopyTo (0, sString, index, len);
  411. sLength += len;
  412. return this;
  413. }
  414. public StringBuilder Insert( int index, bool value ) {
  415. return Insert( index, value.ToString());
  416. }
  417. public StringBuilder Insert( int index, byte value ) {
  418. return Insert( index, value.ToString());
  419. }
  420. public StringBuilder Insert( int index, char value) {
  421. char[] insertChar = new char[1];
  422. insertChar[0] = value;
  423. return Insert( index, insertChar );
  424. }
  425. public StringBuilder Insert( int index, decimal value ) {
  426. return Insert( index, value.ToString() );
  427. }
  428. public StringBuilder Insert( int index, double value ) {
  429. return Insert( index, value.ToString() );
  430. }
  431. public StringBuilder Insert( int index, short value ) {
  432. return Insert( index, value.ToString() );
  433. }
  434. public StringBuilder Insert( int index, int value ) {
  435. return Insert( index, value.ToString() );
  436. }
  437. public StringBuilder Insert( int index, long value ) {
  438. return Insert( index, value.ToString() );
  439. }
  440. public StringBuilder Insert( int index, object value ) {
  441. return Insert( index, value.ToString() );
  442. }
  443. [CLSCompliant(false)]
  444. public StringBuilder Insert( int index, sbyte value ) {
  445. return Insert( index, value.ToString() );
  446. }
  447. public StringBuilder Insert( int index, float value ) {
  448. return Insert( index, value.ToString() );
  449. }
  450. [CLSCompliant(false)]
  451. public StringBuilder Insert( int index, ushort value ) {
  452. return Insert( index, value.ToString() );
  453. }
  454. [CLSCompliant(false)]
  455. public StringBuilder Insert( int index, uint value ) {
  456. return Insert( index, value.ToString() );
  457. }
  458. [CLSCompliant(false)]
  459. public StringBuilder Insert( int index, ulong value ) {
  460. return Insert( index, value.ToString() );
  461. }
  462. public StringBuilder Insert( int index, string value, int count ) {
  463. if ( count < 0 ) {
  464. throw new ArgumentOutOfRangeException();
  465. }
  466. if( value != null ) {
  467. if( value != "" ) {
  468. for( int insertCount = 0; insertCount < count;
  469. insertCount++ ) {
  470. Insert( index, value );
  471. }
  472. }
  473. }
  474. return this;
  475. }
  476. public StringBuilder Insert( int index, char[] value, int startIndex,
  477. int charCount ) {
  478. if( value != null ) {
  479. if( charCount < 0 || startIndex < 0 || startIndex + charCount > value.Length ) {
  480. throw new ArgumentOutOfRangeException();
  481. }
  482. char[] insertChars = new char[ charCount ];
  483. Array.Copy( value, startIndex, insertChars, 0, charCount );
  484. return Insert( index, insertChars );
  485. } else {
  486. return this;
  487. }
  488. }
  489. }
  490. }