1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 // System.Text.StringBuilder
6 // Marcin Szczepanski (marcins@zipworld.com.au)
7 // Paolo Molaro (lupus@ximian.com)
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:
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
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
{
44 [MonoTODO ("Fix serialization compatibility with MS.NET")]
45 public sealed class StringBuilder
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
60 // make sure startIndex is zero or positive
62 throw new System
.ArgumentOutOfRangeException ("startIndex", startIndex
, "StartIndex cannot be less than zero.");
64 // make sure length is zero or positive
66 throw new System
.ArgumentOutOfRangeException ("length", length
, "Length cannot be less than zero.");
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.");
77 capacity
= constDefaultCapacity
;
79 _str
= String
.InternalAllocateStr ((length
> capacity
) ? length
: capacity
);
81 String
.InternalStrcpy(_str
, 0, value, startIndex
, 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
{
104 // MS runtime always returns Int32.MaxValue.
109 public int Capacity
{
116 throw new ArgumentException( "Capacity must be larger than length" );
118 InternalEnsureCapacity(value);
128 if( value < 0 || value > _maxCapacity
)
129 throw new ArgumentOutOfRangeException();
131 if (value == _length
)
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
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
150 Append(' ', value - _length
);
155 [IndexerName("Chars")]
156 public char this [int index
] {
158 if (index
>= _length
|| index
< 0)
159 throw new IndexOutOfRangeException();
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 ()
180 if (null != _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
);
191 _str
.InternalSetLength(_length
);
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
)
208 throw new ArgumentOutOfRangeException ("Capacity must be greater than 0." );
210 if( capacity
<= _str
.Length
)
213 InternalEnsureCapacity (capacity
);
218 public bool Equals (StringBuilder sb
)
220 if (_length
== sb
.Length
&& _str
== sb
._str
)
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
));
238 if (null != _cached_str
)
239 InternalEnsureCapacity (_length
);
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
);
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.");
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
);
294 /* The Append Methods */
295 public StringBuilder
Append (char[] value)
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
;
310 public StringBuilder
Append (string value)
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
;
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) {
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);
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);
408 public StringBuilder
Append( char[] value, int startIndex
, int charCount
)
411 if (!(startIndex
== 0 && charCount
== 0))
412 throw new ArgumentNullException ("value");
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
;
429 public StringBuilder
Append (string value, int startIndex
, int count
)
432 if (startIndex
!= 0 && count
!= 0)
433 throw new ArgumentNullException ("value");
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
);
453 public StringBuilder
AppendLine ()
455 return Append (System
.Environment
.NewLine
);
458 public StringBuilder
AppendLine (string value)
460 return Append (value).Append (System
.Environment
.NewLine
);
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
,
476 params object[] args
)
478 String
.FormatHelper (this, provider
, format
, args
);
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)
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
;
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);
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!
597 throw new ArgumentOutOfRangeException();
599 if (value != null && value != String
.Empty
)
600 for (int insertCount
= 0; insertCount
< count
; insertCount
++)
601 Insert( index
, value );
606 public StringBuilder
Insert (int index
, char [] value, int startIndex
, int charCount
)
609 if (startIndex
== 0 && charCount
== 0)
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
630 capacity
= capacity
<< 1;
634 if (capacity
>= Int32
.MaxValue
|| capacity
< 0)
635 capacity
= Int32
.MaxValue
;
638 string tmp
= String
.InternalAllocateStr (capacity
);
640 String
.InternalStrcpy (tmp
, 0, _str
, 0, _length
);