**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Text / StringBuilder.cs
blob63759ba93cad30ff36d14f05f435022c10e24889
1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2 //
3 // System.Text.StringBuilder
4 //
5 // Authors:
6 // Marcin Szczepanski (marcins@zipworld.com.au)
7 // Paolo Molaro (lupus@ximian.com)
8 // Patrik Torstensson
9 //
10 // NOTE: In the case the buffer is only filled by 50% a new string
11 // will be returned by ToString() is cached in the '_cached_str'
12 // cache_string will also control if a string has been handed out
13 // to via ToString(). If you are chaning the code make sure that
14 // if you modify the string data set the cache_string to null.
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 //
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 //
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
39 using System.Runtime.CompilerServices;
41 namespace System.Text {
43 [Serializable]
44 [MonoTODO ("Fix serialization compatibility with MS.NET")]
45 public sealed class StringBuilder
47 private int _length;
48 private string _str = null;
49 private string _cached_str = null;
51 private int _maxCapacity = Int32.MaxValue;
52 private const int constDefaultCapacity = 16;
54 public StringBuilder(string value, int startIndex, int length, int capacity)
56 // first, check the parameters and throw appropriate exceptions if needed
57 if (null == value)
58 value = "";
60 // make sure startIndex is zero or positive
61 if (startIndex < 0)
62 throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex cannot be less than zero.");
64 // make sure length is zero or positive
65 if(length < 0)
66 throw new System.ArgumentOutOfRangeException ("length", length, "Length cannot be less than zero.");
68 if (capacity < 0)
69 throw new System.ArgumentOutOfRangeException ("capacity", capacity, "capacity must be greater than zero.");
71 // make sure startIndex and length give a valid substring of value
72 // re-ordered to avoid possible integer overflow
73 if (startIndex > value.Length - length)
74 throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");
76 if (capacity == 0)
77 capacity = constDefaultCapacity;
79 _str = String.InternalAllocateStr ((length > capacity) ? length : capacity);
80 if (length > 0)
81 String.InternalStrcpy(_str, 0, value, startIndex, length);
83 _length = length;
86 public StringBuilder () : this (String.Empty, 0, 0, 0) {}
88 public StringBuilder(int capacity) : this (String.Empty, 0, 0, capacity) {}
90 public StringBuilder(int capacity, int maxCapacity) : this (String.Empty, 0, 0, capacity) {
91 if (capacity > maxCapacity)
92 throw new System.ArgumentOutOfRangeException ("capacity", "Capacity exceeds maximum capacity.");
94 _maxCapacity = maxCapacity;
97 public StringBuilder( string value ) : this(value, 0, value == null ? 0 : value.Length, value == null? 0 : value.Length) {
100 public StringBuilder( string value, int capacity) : this(value, 0, value.Length, capacity) {}
102 public int MaxCapacity {
103 get {
104 // MS runtime always returns Int32.MaxValue.
105 return _maxCapacity;
109 public int Capacity {
110 get {
111 return _str.Length;
114 set {
115 if (value < _length)
116 throw new ArgumentException( "Capacity must be larger than length" );
118 InternalEnsureCapacity(value);
122 public int Length {
123 get {
124 return _length;
127 set {
128 if( value < 0 || value > _maxCapacity)
129 throw new ArgumentOutOfRangeException();
131 if (value == _length)
132 return;
134 if( value < _length )
136 // LAMESPEC: The spec is unclear as to what to do
137 // with the capacity when truncating the string.
139 // Do as MS, keep the capacity
140 _length = value;
141 } else
143 // Expand the capacity to the new length and
144 // pad the string with spaces.
146 // LAMESPEC: The spec says to put the spaces on the
147 // left of the string however the MS implementation
148 // puts them on the right. We'll do that for
149 // compatibility (!)
150 Append(' ', value - _length);
155 [IndexerName("Chars")]
156 public char this [int index] {
157 get {
158 if (index >= _length || index < 0)
159 throw new IndexOutOfRangeException();
161 return _str [index];
164 set {
165 if (index >= _length || index < 0)
166 throw new IndexOutOfRangeException();
168 if (null != _cached_str)
169 InternalEnsureCapacity (_length);
171 _str.InternalSetChar (index, value);
175 public override string ToString ()
177 if (_length == 0)
178 return String.Empty;
180 if (null != _cached_str)
181 return _cached_str;
183 // If we only have a half-full buffer we return a new string.
184 if (_length < (_str.Length >> 1))
186 _cached_str = _str.Substring(0, _length);
187 return _cached_str;
190 _cached_str = _str;
191 _str.InternalSetLength(_length);
193 return _str;
196 public string ToString (int startIndex, int length)
198 // re-ordered to avoid possible integer overflow
199 if (startIndex < 0 || length < 0 || startIndex > _length - length)
200 throw new ArgumentOutOfRangeException();
202 return _str.Substring (startIndex, length);
205 public int EnsureCapacity (int capacity)
207 if (capacity < 0)
208 throw new ArgumentOutOfRangeException ("Capacity must be greater than 0." );
210 if( capacity <= _str.Length )
211 return _str.Length;
213 InternalEnsureCapacity (capacity);
215 return _str.Length;
218 public bool Equals (StringBuilder sb)
220 if (_length == sb.Length && _str == sb._str )
221 return true;
223 return false;
226 public StringBuilder Remove (int startIndex, int length)
228 // re-ordered to avoid possible integer overflow
229 if (startIndex < 0 || length < 0 || startIndex > _length - length)
230 throw new ArgumentOutOfRangeException();
232 // Copy everything after the 'removed' part to the start
233 // of the removed part and truncate the sLength
234 if (_length - (startIndex + length) > 0)
235 String.InternalStrcpy (_str, startIndex, _str, startIndex + length, _length - (startIndex + length));
237 _length -= length;
238 if (null != _cached_str)
239 InternalEnsureCapacity (_length);
241 return this;
244 public StringBuilder Replace (char oldChar, char newChar)
246 return Replace( oldChar, newChar, 0, _length);
249 public StringBuilder Replace (char oldChar, char newChar, int startIndex, int count)
251 // re-ordered to avoid possible integer overflow
252 if (startIndex > _length - count || startIndex < 0 || count < 0)
253 throw new ArgumentOutOfRangeException();
255 if (null != _cached_str)
256 InternalEnsureCapacity (_str.Length);
258 for (int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
259 if( _str [replaceIterate] == oldChar )
260 _str.InternalSetChar (replaceIterate, newChar);
263 return this;
266 public StringBuilder Replace( string oldValue, string newValue ) {
267 return Replace (oldValue, newValue, 0, _length);
270 public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count )
272 if (oldValue == null)
273 throw new ArgumentNullException ("The old value cannot be null.");
275 if (startIndex < 0 || count < 0 || startIndex > _length - count)
276 throw new ArgumentOutOfRangeException ();
278 if (oldValue.Length == 0)
279 throw new ArgumentException ("The old value cannot be zero length.");
281 // TODO: OPTIMIZE!
282 string replace = _str.Substring(startIndex, count).Replace(oldValue, newValue);
284 InternalEnsureCapacity (replace.Length + (_length - count));
286 String.InternalStrcpy (_str, startIndex, replace);
288 _length = replace.Length + (_length - count);
290 return this;
294 /* The Append Methods */
295 public StringBuilder Append (char[] value)
297 if (value == null)
298 return this;
300 int needed_cap = _length + value.Length;
301 if (null != _cached_str || _str.Length < needed_cap)
302 InternalEnsureCapacity (needed_cap);
304 String.InternalStrcpy (_str, _length, value);
305 _length += value.Length;
307 return this;
310 public StringBuilder Append (string value)
312 if (value == null)
313 return this;
315 int needed_cap = _length + value.Length;
316 if (null != _cached_str || _str.Length < needed_cap)
317 InternalEnsureCapacity (needed_cap);
319 String.InternalStrcpy (_str, _length, value);
320 _length += value.Length;
321 return this;
324 public StringBuilder Append (bool value) {
325 return Append (value.ToString());
328 public StringBuilder Append (byte value) {
329 return Append (value.ToString());
332 public StringBuilder Append (decimal value) {
333 return Append (value.ToString());
336 public StringBuilder Append (double value) {
337 return Append (value.ToString());
340 public StringBuilder Append (short value) {
341 return Append (value.ToString());
344 public StringBuilder Append (int value) {
345 return Append (value.ToString());
348 public StringBuilder Append (long value) {
349 return Append (value.ToString());
352 public StringBuilder Append (object value) {
353 if (value == null)
354 return this;
356 return Append (value.ToString());
359 [CLSCompliant(false)]
360 public StringBuilder Append (sbyte value) {
361 return Append (value.ToString());
364 public StringBuilder Append (float value) {
365 return Append (value.ToString());
368 [CLSCompliant(false)]
369 public StringBuilder Append (ushort value) {
370 return Append (value.ToString());
373 [CLSCompliant(false)]
374 public StringBuilder Append (uint value) {
375 return Append (value.ToString());
378 [CLSCompliant(false)]
379 public StringBuilder Append (ulong value) {
380 return Append (value.ToString());
383 public StringBuilder Append (char value)
385 int needed_cap = _length + 1;
386 if (null != _cached_str || _str.Length < needed_cap)
387 InternalEnsureCapacity (needed_cap);
389 _str.InternalSetChar(_length, value);
390 _length++;
392 return this;
395 public StringBuilder Append (char value, int repeatCount)
397 if( repeatCount < 0 )
398 throw new ArgumentOutOfRangeException();
400 InternalEnsureCapacity (_length + repeatCount);
402 for (int i = 0; i < repeatCount; i++)
403 _str.InternalSetChar (_length++, value);
405 return this;
408 public StringBuilder Append( char[] value, int startIndex, int charCount )
410 if (value == null) {
411 if (!(startIndex == 0 && charCount == 0))
412 throw new ArgumentNullException ("value");
414 return this;
417 if ((charCount < 0 || startIndex < 0) || (startIndex > value.Length - charCount))
418 throw new ArgumentOutOfRangeException();
421 InternalEnsureCapacity (_length + charCount);
423 String.InternalStrcpy (_str, _length, value, startIndex, charCount);
424 _length += charCount;
426 return this;
429 public StringBuilder Append (string value, int startIndex, int count)
431 if (value == null) {
432 if (startIndex != 0 && count != 0)
433 throw new ArgumentNullException ("value");
435 return this;
438 if ((count < 0 || startIndex < 0) || (startIndex > value.Length - count))
439 throw new ArgumentOutOfRangeException();
441 int needed_cap = _length + count;
442 if (null != _cached_str || _str.Length < needed_cap)
443 InternalEnsureCapacity (needed_cap);
445 String.InternalStrcpy (_str, _length, value, startIndex, count);
447 _length += count;
449 return this;
452 #if NET_2_0
453 public StringBuilder AppendLine ()
455 return Append (System.Environment.NewLine);
458 public StringBuilder AppendLine (string value)
460 return Append (value).Append (System.Environment.NewLine);
462 #endif
464 public StringBuilder AppendFormat (string format, object arg0)
466 return AppendFormat (null, format, new object [] { arg0 });
469 public StringBuilder AppendFormat (string format, params object[] args)
471 return AppendFormat (null, format, args);
474 public StringBuilder AppendFormat (IFormatProvider provider,
475 string format,
476 params object[] args)
478 String.FormatHelper (this, provider, format, args);
479 return this;
482 public StringBuilder AppendFormat (string format, object arg0, object arg1)
484 return AppendFormat (null, format, new object [] { arg0, arg1 });
487 public StringBuilder AppendFormat (string format, object arg0, object arg1, object arg2)
489 return AppendFormat (null, format, new object [] { arg0, arg1, arg2 });
492 /* The Insert Functions */
494 public StringBuilder Insert (int index, char[] value)
496 return Insert (index, new string (value));
499 public StringBuilder Insert (int index, string value)
501 if( index > _length || index < 0)
502 throw new ArgumentOutOfRangeException();
504 if (value == null || value.Length == 0)
505 return this;
507 InternalEnsureCapacity (_length + value.Length);
509 // Move everything to the right of the insert point across
510 String.InternalStrcpy (_str, index + value.Length, _str, index, _length - index);
512 // Copy in stuff from the insert buffer
513 String.InternalStrcpy (_str, index, value);
515 _length += value.Length;
517 return this;
520 public StringBuilder Insert( int index, bool value ) {
521 return Insert (index, value.ToString());
524 public StringBuilder Insert( int index, byte value ) {
525 return Insert (index, value.ToString());
528 public StringBuilder Insert( int index, char value)
530 if (index > _length || index < 0)
531 throw new ArgumentOutOfRangeException ("index");
533 InternalEnsureCapacity (_length + 1);
535 // Move everything to the right of the insert point across
536 String.InternalStrcpy (_str, index + 1, _str, index, _length - index);
538 _str.InternalSetChar (index, value);
539 _length++;
541 return this;
544 public StringBuilder Insert( int index, decimal value ) {
545 return Insert (index, value.ToString());
548 public StringBuilder Insert( int index, double value ) {
549 return Insert (index, value.ToString());
552 public StringBuilder Insert( int index, short value ) {
553 return Insert (index, value.ToString());
556 public StringBuilder Insert( int index, int value ) {
557 return Insert (index, value.ToString());
560 public StringBuilder Insert( int index, long value ) {
561 return Insert (index, value.ToString());
564 public StringBuilder Insert( int index, object value ) {
565 return Insert (index, value.ToString());
568 [CLSCompliant(false)]
569 public StringBuilder Insert( int index, sbyte value ) {
570 return Insert (index, value.ToString() );
573 public StringBuilder Insert (int index, float value) {
574 return Insert (index, value.ToString() );
577 [CLSCompliant(false)]
578 public StringBuilder Insert (int index, ushort value) {
579 return Insert (index, value.ToString() );
582 [CLSCompliant(false)]
583 public StringBuilder Insert (int index, uint value) {
584 return Insert ( index, value.ToString() );
587 [CLSCompliant(false)]
588 public StringBuilder Insert (int index, ulong value) {
589 return Insert ( index, value.ToString() );
592 public StringBuilder Insert (int index, string value, int count)
594 // LAMESPEC: The spec says to throw an exception if
595 // count < 0, while MS throws even for count < 1!
596 if ( count < 0 )
597 throw new ArgumentOutOfRangeException();
599 if (value != null && value != String.Empty)
600 for (int insertCount = 0; insertCount < count; insertCount++)
601 Insert( index, value );
603 return this;
606 public StringBuilder Insert (int index, char [] value, int startIndex, int charCount)
608 if (value == null) {
609 if (startIndex == 0 && charCount == 0)
610 return this;
612 throw new ArgumentNullException ("value");
615 if (charCount < 0 || startIndex < 0 || startIndex > value.Length - charCount)
616 throw new ArgumentOutOfRangeException ();
618 return Insert (index, new String (value, startIndex, charCount));
621 private void InternalEnsureCapacity (int size)
623 if (size > _str.Length || _cached_str == _str)
625 int capacity = _str.Length;
627 // Try double buffer, if that doesn't work, set the length as capacity
628 if (size > capacity)
630 capacity = capacity << 1;
631 if (size > capacity)
632 capacity = size;
634 if (capacity >= Int32.MaxValue || capacity < 0)
635 capacity = Int32.MaxValue;
638 string tmp = String.InternalAllocateStr (capacity);
639 if (_length > 0)
640 String.InternalStrcpy (tmp, 0, _str, 0, _length);
642 _str = tmp;
645 _cached_str = null;