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
.Serialization
;
40 using System
.Runtime
.CompilerServices
;
41 using System
.Runtime
.InteropServices
;
43 namespace System
.Text
{
49 [MonoTODO ("Serialization format not compatible with .NET")]
50 public sealed class StringBuilder
57 private string _cached_str
;
59 private int _maxCapacity
= Int32
.MaxValue
;
60 private const int constDefaultCapacity
= 16;
62 public StringBuilder(string value, int startIndex
, int length
, int capacity
)
64 // first, check the parameters and throw appropriate exceptions if needed
68 // make sure startIndex is zero or positive
70 throw new System
.ArgumentOutOfRangeException ("startIndex", startIndex
, "StartIndex cannot be less than zero.");
72 // make sure length is zero or positive
74 throw new System
.ArgumentOutOfRangeException ("length", length
, "Length cannot be less than zero.");
77 throw new System
.ArgumentOutOfRangeException ("capacity", capacity
, "capacity must be greater than zero.");
79 // make sure startIndex and length give a valid substring of value
80 // re-ordered to avoid possible integer overflow
81 if (startIndex
> value.Length
- length
)
82 throw new System
.ArgumentOutOfRangeException ("startIndex", startIndex
, "StartIndex and length must refer to a location within the string.");
85 capacity
= constDefaultCapacity
;
87 _str
= String
.InternalAllocateStr ((length
> capacity
) ? length
: capacity
);
89 String
.InternalStrcpy(_str
, 0, value, startIndex
, length
);
94 public StringBuilder () : this (null) {}
96 public StringBuilder(int capacity
) : this (String
.Empty
, 0, 0, capacity
) {}
98 public StringBuilder(int capacity
, int maxCapacity
) : this (String
.Empty
, 0, 0, capacity
) {
100 throw new System
.ArgumentOutOfRangeException ("maxCapacity", "maxCapacity is less than one.");
101 if (capacity
> maxCapacity
)
102 throw new System
.ArgumentOutOfRangeException ("capacity", "Capacity exceeds maximum capacity.");
104 _maxCapacity
= maxCapacity
;
107 public StringBuilder (string value)
110 * This is an optimization to avoid allocating the internal string
111 * until the first Append () call.
112 * The runtime pinvoke marshalling code needs to be aware of this.
117 _length
= value.Length
;
118 _str
= _cached_str
= value;
121 public StringBuilder( string value, int capacity
) : this(value, 0, value.Length
, capacity
) {}
123 public int MaxCapacity
{
125 // MS runtime always returns Int32.MaxValue.
130 public int Capacity
{
132 if (_str
.Length
== 0)
133 return constDefaultCapacity
;
140 throw new ArgumentException( "Capacity must be larger than length" );
142 InternalEnsureCapacity(value);
152 if( value < 0 || value > _maxCapacity
)
153 throw new ArgumentOutOfRangeException();
155 if (value == _length
)
158 if (value < _length
) {
159 // LAMESPEC: The spec is unclear as to what to do
160 // with the capacity when truncating the string.
162 // Do as MS, keep the capacity
164 // Make sure that we invalidate any cached string.
165 InternalEnsureCapacity (value);
168 // Expand the capacity to the new length and
169 // pad the string with NULL characters.
170 Append('\0', value - _length
);
175 [IndexerName("Chars")]
176 public char this [int index
] {
178 if (index
>= _length
|| index
< 0)
179 throw new IndexOutOfRangeException();
185 if (index
>= _length
|| index
< 0)
186 throw new IndexOutOfRangeException();
188 if (null != _cached_str
)
189 InternalEnsureCapacity (_length
);
191 _str
.InternalSetChar (index
, value);
195 public override string ToString ()
200 if (null != _cached_str
)
203 // If we only have a half-full buffer we return a new string.
204 if (_length
< (_str
.Length
>> 1))
206 _cached_str
= _str
.Substring(0, _length
);
211 _str
.InternalSetLength(_length
);
216 public string ToString (int startIndex
, int length
)
218 // re-ordered to avoid possible integer overflow
219 if (startIndex
< 0 || length
< 0 || startIndex
> _length
- length
)
220 throw new ArgumentOutOfRangeException();
222 return _str
.Substring (startIndex
, length
);
225 public int EnsureCapacity (int capacity
)
228 throw new ArgumentOutOfRangeException ("Capacity must be greater than 0." );
230 if( capacity
<= _str
.Length
)
233 InternalEnsureCapacity (capacity
);
238 public bool Equals (StringBuilder sb
)
240 if (_length
== sb
.Length
&& _str
== sb
._str
)
246 public StringBuilder
Remove (int startIndex
, int length
)
248 // re-ordered to avoid possible integer overflow
249 if (startIndex
< 0 || length
< 0 || startIndex
> _length
- length
)
250 throw new ArgumentOutOfRangeException();
252 if (null != _cached_str
)
253 InternalEnsureCapacity (_length
);
255 // Copy everything after the 'removed' part to the start
256 // of the removed part and truncate the sLength
257 if (_length
- (startIndex
+ length
) > 0)
258 String
.InternalStrcpy (_str
, startIndex
, _str
, startIndex
+ length
, _length
- (startIndex
+ length
));
265 public StringBuilder
Replace (char oldChar
, char newChar
)
267 return Replace( oldChar
, newChar
, 0, _length
);
270 public StringBuilder
Replace (char oldChar
, char newChar
, int startIndex
, int count
)
272 // re-ordered to avoid possible integer overflow
273 if (startIndex
> _length
- count
|| startIndex
< 0 || count
< 0)
274 throw new ArgumentOutOfRangeException();
276 if (null != _cached_str
)
277 InternalEnsureCapacity (_str
.Length
);
279 for (int replaceIterate
= startIndex
; replaceIterate
< startIndex
+ count
; replaceIterate
++ ) {
280 if( _str
[replaceIterate
] == oldChar
)
281 _str
.InternalSetChar (replaceIterate
, newChar
);
287 public StringBuilder
Replace( string oldValue
, string newValue
) {
288 return Replace (oldValue
, newValue
, 0, _length
);
291 public StringBuilder
Replace( string oldValue
, string newValue
, int startIndex
, int count
)
293 if (oldValue
== null)
294 throw new ArgumentNullException ("The old value cannot be null.");
296 if (startIndex
< 0 || count
< 0 || startIndex
> _length
- count
)
297 throw new ArgumentOutOfRangeException ();
299 if (oldValue
.Length
== 0)
300 throw new ArgumentException ("The old value cannot be zero length.");
303 string replace
= _str
.Substring(startIndex
, count
).Replace(oldValue
, newValue
);
305 InternalEnsureCapacity (replace
.Length
+ (_length
- count
));
307 string end
= _str
.Substring (startIndex
+ count
, _length
- startIndex
- count
);
309 String
.InternalStrcpy (_str
, startIndex
, replace
);
310 String
.InternalStrcpy (_str
, startIndex
+ replace
.Length
, end
);
312 _length
= replace
.Length
+ (_length
- count
);
318 /* The Append Methods */
319 public StringBuilder
Append (char[] value)
324 int needed_cap
= _length
+ value.Length
;
325 if (null != _cached_str
|| _str
.Length
< needed_cap
)
326 InternalEnsureCapacity (needed_cap
);
328 String
.InternalStrcpy (_str
, _length
, value);
329 _length
= needed_cap
;
334 public StringBuilder
Append (string value)
339 if (_length
== 0 && value.Length
< _maxCapacity
&& value.Length
> _str
.Length
) {
340 _length
= value.Length
;
341 _str
= _cached_str
= value;
345 int needed_cap
= _length
+ value.Length
;
346 if (null != _cached_str
|| _str
.Length
< needed_cap
)
347 InternalEnsureCapacity (needed_cap
);
349 String
.InternalStrcpy (_str
, _length
, value);
350 _length
= needed_cap
;
354 public StringBuilder
Append (bool value) {
355 return Append (value.ToString());
358 public StringBuilder
Append (byte value) {
359 return Append (value.ToString());
362 public StringBuilder
Append (decimal value) {
363 return Append (value.ToString());
366 public StringBuilder
Append (double value) {
367 return Append (value.ToString());
370 public StringBuilder
Append (short value) {
371 return Append (value.ToString());
374 public StringBuilder
Append (int value) {
375 return Append (value.ToString());
378 public StringBuilder
Append (long value) {
379 return Append (value.ToString());
382 public StringBuilder
Append (object value) {
386 return Append (value.ToString());
389 [CLSCompliant(false)]
390 public StringBuilder
Append (sbyte value) {
391 return Append (value.ToString());
394 public StringBuilder
Append (float value) {
395 return Append (value.ToString());
398 [CLSCompliant(false)]
399 public StringBuilder
Append (ushort value) {
400 return Append (value.ToString());
403 [CLSCompliant(false)]
404 public StringBuilder
Append (uint value) {
405 return Append (value.ToString());
408 [CLSCompliant(false)]
409 public StringBuilder
Append (ulong value) {
410 return Append (value.ToString());
413 public StringBuilder
Append (char value)
415 int needed_cap
= _length
+ 1;
416 if (null != _cached_str
|| _str
.Length
< needed_cap
)
417 InternalEnsureCapacity (needed_cap
);
419 _str
.InternalSetChar(_length
, value);
420 _length
= needed_cap
;
425 public StringBuilder
Append (char value, int repeatCount
)
427 if( repeatCount
< 0 )
428 throw new ArgumentOutOfRangeException();
430 InternalEnsureCapacity (_length
+ repeatCount
);
432 for (int i
= 0; i
< repeatCount
; i
++)
433 _str
.InternalSetChar (_length
++, value);
438 public StringBuilder
Append( char[] value, int startIndex
, int charCount
)
441 if (!(startIndex
== 0 && charCount
== 0))
442 throw new ArgumentNullException ("value");
447 if ((charCount
< 0 || startIndex
< 0) || (startIndex
> value.Length
- charCount
))
448 throw new ArgumentOutOfRangeException();
450 int needed_cap
= _length
+ charCount
;
451 InternalEnsureCapacity (needed_cap
);
453 String
.InternalStrcpy (_str
, _length
, value, startIndex
, charCount
);
454 _length
= needed_cap
;
459 public StringBuilder
Append (string value, int startIndex
, int count
)
462 if (startIndex
!= 0 && count
!= 0)
463 throw new ArgumentNullException ("value");
468 if ((count
< 0 || startIndex
< 0) || (startIndex
> value.Length
- count
))
469 throw new ArgumentOutOfRangeException();
471 int needed_cap
= _length
+ count
;
472 if (null != _cached_str
|| _str
.Length
< needed_cap
)
473 InternalEnsureCapacity (needed_cap
);
475 String
.InternalStrcpy (_str
, _length
, value, startIndex
, count
);
477 _length
= needed_cap
;
484 public StringBuilder
AppendLine ()
486 return Append (System
.Environment
.NewLine
);
490 public StringBuilder
AppendLine (string value)
492 return Append (value).Append (System
.Environment
.NewLine
);
496 public StringBuilder
AppendFormat (string format
, object arg0
)
498 return AppendFormat (null, format
, new object [] { arg0 }
);
501 public StringBuilder
AppendFormat (string format
, params object[] args
)
503 return AppendFormat (null, format
, args
);
506 public StringBuilder
AppendFormat (IFormatProvider provider
,
508 params object[] args
)
510 String
.FormatHelper (this, provider
, format
, args
);
514 public StringBuilder
AppendFormat (string format
, object arg0
, object arg1
)
516 return AppendFormat (null, format
, new object [] { arg0, arg1 }
);
519 public StringBuilder
AppendFormat (string format
, object arg0
, object arg1
, object arg2
)
521 return AppendFormat (null, format
, new object [] { arg0, arg1, arg2 }
);
524 /* The Insert Functions */
526 public StringBuilder
Insert (int index
, char[] value)
528 return Insert (index
, new string (value));
531 public StringBuilder
Insert (int index
, string value)
533 if( index
> _length
|| index
< 0)
534 throw new ArgumentOutOfRangeException();
536 if (value == null || value.Length
== 0)
539 InternalEnsureCapacity (_length
+ value.Length
);
541 // Move everything to the right of the insert point across
542 String
.InternalStrcpy (_str
, index
+ value.Length
, _str
, index
, _length
- index
);
544 // Copy in stuff from the insert buffer
545 String
.InternalStrcpy (_str
, index
, value);
547 _length
+= value.Length
;
552 public StringBuilder
Insert( int index
, bool value ) {
553 return Insert (index
, value.ToString());
556 public StringBuilder
Insert( int index
, byte value ) {
557 return Insert (index
, value.ToString());
560 public StringBuilder
Insert( int index
, char value)
562 if (index
> _length
|| index
< 0)
563 throw new ArgumentOutOfRangeException ("index");
565 InternalEnsureCapacity (_length
+ 1);
567 // Move everything to the right of the insert point across
568 String
.InternalStrcpy (_str
, index
+ 1, _str
, index
, _length
- index
);
570 _str
.InternalSetChar (index
, value);
576 public StringBuilder
Insert( int index
, decimal value ) {
577 return Insert (index
, value.ToString());
580 public StringBuilder
Insert( int index
, double value ) {
581 return Insert (index
, value.ToString());
584 public StringBuilder
Insert( int index
, short value ) {
585 return Insert (index
, value.ToString());
588 public StringBuilder
Insert( int index
, int value ) {
589 return Insert (index
, value.ToString());
592 public StringBuilder
Insert( int index
, long value ) {
593 return Insert (index
, value.ToString());
596 public StringBuilder
Insert( int index
, object value ) {
597 return Insert (index
, value.ToString());
600 [CLSCompliant(false)]
601 public StringBuilder
Insert( int index
, sbyte value ) {
602 return Insert (index
, value.ToString() );
605 public StringBuilder
Insert (int index
, float value) {
606 return Insert (index
, value.ToString() );
609 [CLSCompliant(false)]
610 public StringBuilder
Insert (int index
, ushort value) {
611 return Insert (index
, value.ToString() );
614 [CLSCompliant(false)]
615 public StringBuilder
Insert (int index
, uint value) {
616 return Insert ( index
, value.ToString() );
619 [CLSCompliant(false)]
620 public StringBuilder
Insert (int index
, ulong value) {
621 return Insert ( index
, value.ToString() );
624 public StringBuilder
Insert (int index
, string value, int count
)
626 // LAMESPEC: The spec says to throw an exception if
627 // count < 0, while MS throws even for count < 1!
629 throw new ArgumentOutOfRangeException();
631 if (value != null && value != String
.Empty
)
632 for (int insertCount
= 0; insertCount
< count
; insertCount
++)
633 Insert( index
, value );
638 public StringBuilder
Insert (int index
, char [] value, int startIndex
, int charCount
)
641 if (startIndex
== 0 && charCount
== 0)
644 throw new ArgumentNullException ("value");
647 if (charCount
< 0 || startIndex
< 0 || startIndex
> value.Length
- charCount
)
648 throw new ArgumentOutOfRangeException ();
650 return Insert (index
, new String (value, startIndex
, charCount
));
653 private void InternalEnsureCapacity (int size
)
655 if (size
> _str
.Length
|| (object) _cached_str
== (object) _str
) {
656 int capacity
= _str
.Length
;
658 // Try double buffer, if that doesn't work, set the length as capacity
659 if (size
> capacity
) {
661 // The first time a string is appended, we just set _cached_str
662 // and _str to it. This allows us to do some optimizations.
663 // Below, we take this into account.
664 if ((object) _cached_str
== (object) _str
&& capacity
< constDefaultCapacity
)
665 capacity
= constDefaultCapacity
;
667 capacity
= capacity
<< 1;
671 if (capacity
>= Int32
.MaxValue
|| capacity
< 0)
672 capacity
= Int32
.MaxValue
;
674 if (capacity
> _maxCapacity
&& size
<= _maxCapacity
)
675 capacity
= _maxCapacity
;
677 if (capacity
> _maxCapacity
)
678 throw new ArgumentOutOfRangeException ("size", "capacity was less than the current size.");
681 string tmp
= String
.InternalAllocateStr (capacity
);
683 String
.InternalStrcpy (tmp
, 0, _str
, 0, _length
);
693 public void CopyTo (int sourceIndex
, char [] destination
, int destinationIndex
, int count
)
695 if (destination
== null)
696 throw new ArgumentNullException ("destination");
697 if ((Length
- count
< sourceIndex
) ||
698 (destination
.Length
-count
< destinationIndex
) ||
699 (sourceIndex
< 0 || destinationIndex
< 0 || count
< 0))
700 throw new ArgumentOutOfRangeException ();
702 for (int i
= 0; i
< count
; i
++)
703 destination
[destinationIndex
+i
] = _str
[sourceIndex
+i
];
706 void ISerializable
.GetObjectData (SerializationInfo info
, StreamingContext context
)
708 info
.AddValue ("m_MaxCapacity", _maxCapacity
);
709 info
.AddValue ("Capacity", Capacity
);
710 info
.AddValue ("m_StringValue", ToString ());
711 info
.AddValue ("m_currentThread", 0);
714 StringBuilder (SerializationInfo info
, StreamingContext context
)
716 string s
= info
.GetString ("m_StringValue");
720 _str
= _cached_str
= s
;
722 _maxCapacity
= info
.GetInt32 ("m_MaxCapacity");
723 if (_maxCapacity
< 0)
724 _maxCapacity
= Int32
.MaxValue
;
725 Capacity
= info
.GetInt32 ("Capacity");