PageRenderTime 42ms CodeModel.GetById 11ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 1ms

/mcs/class/corlib/System.Text/StringBuilder.cs

http://github.com/mono/mono
C# | 774 lines | 525 code | 171 blank | 78 comment | 172 complexity | 3ef06f0d7a9d15f615946346c1683cef MD5 | raw file
  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.
 15//
 16
 17//
 18// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
 19// Copyright 2011 Xamarin Inc
 20//
 21// Permission is hereby granted, free of charge, to any person obtaining
 22// a copy of this software and associated documentation files (the
 23// "Software"), to deal in the Software without restriction, including
 24// without limitation the rights to use, copy, modify, merge, publish,
 25// distribute, sublicense, and/or sell copies of the Software, and to
 26// permit persons to whom the Software is furnished to do so, subject to
 27// the following conditions:
 28// 
 29// The above copyright notice and this permission notice shall be
 30// included in all copies or substantial portions of the Software.
 31// 
 32// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 33// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 34// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 35// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 36// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 37// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 38// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 39//
 40using System.Runtime.Serialization;
 41using System.Runtime.CompilerServices;
 42using System.Runtime.InteropServices;
 43
 44namespace System.Text {
 45	
 46	[Serializable]
 47	[ComVisible (true)]
 48        [MonoLimitation ("Serialization format not compatible with .NET")]
 49	[StructLayout (LayoutKind.Sequential)]
 50	public sealed class StringBuilder : ISerializable
 51	{
 52		private int _length;
 53		private string _str;
 54		private string _cached_str;
 55		
 56		private int _maxCapacity;
 57		private const int constDefaultCapacity = 16;
 58
 59		public StringBuilder(string value, int startIndex, int length, int capacity) 
 60			: this (value, startIndex, length, capacity, Int32.MaxValue)
 61		{
 62		}
 63
 64		private StringBuilder(string value, int startIndex, int length, int capacity, int maxCapacity)
 65		{
 66			// first, check the parameters and throw appropriate exceptions if needed
 67			if (null == value)
 68				value = "";
 69
 70			// make sure startIndex is zero or positive
 71			if (startIndex < 0)
 72				throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex cannot be less than zero.");
 73
 74			// make sure length is zero or positive
 75			if(length < 0)
 76				throw new System.ArgumentOutOfRangeException ("length", length, "Length cannot be less than zero.");
 77
 78			if (capacity < 0)
 79				throw new System.ArgumentOutOfRangeException ("capacity", capacity, "capacity must be greater than zero.");
 80
 81			if (maxCapacity < 1)
 82				throw new System.ArgumentOutOfRangeException ("maxCapacity", "maxCapacity is less than one.");
 83			if (capacity > maxCapacity)
 84				throw new System.ArgumentOutOfRangeException ("capacity", "Capacity exceeds maximum capacity.");
 85
 86			// make sure startIndex and length give a valid substring of value
 87			// re-ordered to avoid possible integer overflow
 88			if (startIndex > value.Length - length)
 89				throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");
 90
 91			if (capacity == 0) {
 92				if (maxCapacity > constDefaultCapacity)
 93					capacity = constDefaultCapacity;
 94				else
 95					_str = _cached_str = String.Empty;
 96			}
 97			_maxCapacity = maxCapacity;
 98
 99			if (_str == null)
100				_str = String.InternalAllocateStr ((length > capacity) ? length : capacity);
101			if (length > 0)
102				String.CharCopy (_str, 0, value, startIndex, length);
103			
104			_length = length;
105		}
106
107		public StringBuilder () : this (null) {}
108
109		public StringBuilder(int capacity) : this (String.Empty, 0, 0, capacity) {}
110
111		public StringBuilder(int capacity, int maxCapacity) : this (String.Empty, 0, 0, capacity, maxCapacity) { }
112
113		public StringBuilder (string value)
114		{
115			/*
116			 * This is an optimization to avoid allocating the internal string
117			 * until the first Append () call.
118			 * The runtime pinvoke marshalling code needs to be aware of this.
119			 */
120			if (null == value)
121				value = "";
122			
123			_length = value.Length;
124			_str = _cached_str = value;
125			_maxCapacity = Int32.MaxValue;
126		}
127	
128		public StringBuilder( string value, int capacity) : this(value == null ? "" : value, 0, value == null ? 0 : value.Length, capacity) {}
129	
130		public int MaxCapacity {
131			get {
132				return _maxCapacity;
133			}
134		}
135
136		public int Capacity {
137			get {
138				if (_str.Length == 0)
139					return Math.Min (_maxCapacity, constDefaultCapacity);
140				
141				return _str.Length;
142			}
143
144			set {
145				if (value < _length)
146					throw new ArgumentException( "Capacity must be larger than length" );
147
148				if (value > _maxCapacity)
149					throw new ArgumentOutOfRangeException ("value", "Should be less than or equal to MaxCapacity");
150
151				InternalEnsureCapacity(value);
152			}
153		}
154
155		public int Length {
156			get {
157				return _length;
158			}
159
160			set {
161				if( value < 0 || value > _maxCapacity)
162					throw new ArgumentOutOfRangeException();
163
164				if (value == _length)
165					return;
166
167				if (value < _length) {
168					// LAMESPEC:  The spec is unclear as to what to do
169					// with the capacity when truncating the string.
170
171					// Do as MS, keep the capacity
172					
173					// Make sure that we invalidate any cached string.
174					InternalEnsureCapacity (value);
175					_length = value;
176				} else {
177					// Expand the capacity to the new length and
178					// pad the string with NULL characters.
179					Append('\0', value - _length);
180				}
181			}
182		}
183
184		[IndexerName("Chars")]
185		public char this [int index] {
186			get {
187				if (index >= _length || index < 0)
188					throw new IndexOutOfRangeException();
189
190				return _str [index];
191			} 
192
193			set {
194				if (index >= _length || index < 0)
195					throw new IndexOutOfRangeException();
196
197				if (null != _cached_str)
198					InternalEnsureCapacity (_length);
199				
200				_str.InternalSetChar (index, value);
201			}
202		}
203
204		public override string ToString () 
205		{
206			if (_length == 0)
207				return String.Empty;
208
209			if (null != _cached_str)
210				return _cached_str;
211
212			// If we only have a half-full buffer we return a new string.
213			if (_length < (_str.Length >> 1) || (_str.Length > string.LOS_limit && _length <= string.LOS_limit))
214			{
215				// use String.SubstringUnchecked instead of String.Substring
216				// as the former is guaranteed to create a new string object
217				_cached_str = _str.SubstringUnchecked (0, _length);
218				return _cached_str;
219			}
220
221			_cached_str = _str;
222			_str.InternalSetLength(_length);
223
224			return _str;
225		}
226
227		public string ToString (int startIndex, int length) 
228		{
229			// re-ordered to avoid possible integer overflow
230			if (startIndex < 0 || length < 0 || startIndex > _length - length)
231				throw new ArgumentOutOfRangeException();
232
233			// use String.SubstringUnchecked instead of String.Substring
234			// as the former is guaranteed to create a new string object
235			if (startIndex == 0 && length == _length)
236				return ToString ();
237			else
238				return _str.SubstringUnchecked (startIndex, length);
239		}
240
241		public int EnsureCapacity (int capacity) 
242		{
243			if (capacity < 0)
244				throw new ArgumentOutOfRangeException ("Capacity must be greater than 0." );
245
246			if( capacity <= _str.Length )
247				return _str.Length;
248
249			InternalEnsureCapacity (capacity);
250
251			return _str.Length;
252		}
253
254		public bool Equals (StringBuilder sb) 
255		{
256			if (((object)sb) == null)
257				return false;
258			
259			if (_length == sb.Length && _str == sb._str )
260				return true;
261
262			return false;
263		}
264
265		public StringBuilder Remove (int startIndex, int length)
266		{
267			// re-ordered to avoid possible integer overflow
268			if (startIndex < 0 || length < 0 || startIndex > _length - length)
269				throw new ArgumentOutOfRangeException();
270			
271			if (null != _cached_str)
272				InternalEnsureCapacity (_length);
273			
274			// Copy everything after the 'removed' part to the start 
275			// of the removed part and truncate the sLength
276			if (_length - (startIndex + length) > 0)
277				String.CharCopy (_str, startIndex, _str, startIndex + length, _length - (startIndex + length));
278
279			_length -= length;
280
281			return this;
282		}			       
283
284		public StringBuilder Replace (char oldChar, char newChar) 
285		{
286			return Replace( oldChar, newChar, 0, _length);
287		}
288
289		public StringBuilder Replace (char oldChar, char newChar, int startIndex, int count) 
290		{
291			// re-ordered to avoid possible integer overflow
292			if (startIndex > _length - count || startIndex < 0 || count < 0)
293				throw new ArgumentOutOfRangeException();
294
295			if (null != _cached_str)
296				InternalEnsureCapacity (_str.Length);
297
298			for (int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
299				if( _str [replaceIterate] == oldChar )
300					_str.InternalSetChar (replaceIterate, newChar);
301			}
302
303			return this;
304		}
305
306		public StringBuilder Replace( string oldValue, string newValue ) {
307			return Replace (oldValue, newValue, 0, _length);
308		}
309
310		public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count ) 
311		{
312			if (oldValue == null)
313				throw new ArgumentNullException ("The old value cannot be null.");
314
315			if (startIndex < 0 || count < 0 || startIndex > _length - count)
316				throw new ArgumentOutOfRangeException ();
317
318			if (oldValue.Length == 0)
319				throw new ArgumentException ("The old value cannot be zero length.");
320
321			string substr = _str.Substring(startIndex, count);
322			string replace = substr.Replace(oldValue, newValue);
323			// return early if no oldValue was found
324			if ((object) replace == (object) substr)
325				return this;
326
327			InternalEnsureCapacity (replace.Length + (_length - count));
328
329			// shift end part
330			if (replace.Length < count)
331				String.CharCopy (_str, startIndex + replace.Length, _str, startIndex + count, _length - startIndex  - count);
332			else if (replace.Length > count)
333				String.CharCopyReverse (_str, startIndex + replace.Length, _str, startIndex + count, _length - startIndex  - count);
334
335			// copy middle part back into _str
336			String.CharCopy (_str, startIndex, replace, 0, replace.Length);
337			
338			_length = replace.Length + (_length - count);
339
340			return this;
341		}
342
343		      
344		/* The Append Methods */
345		public StringBuilder Append (char[] value) 
346		{
347			if (value == null)
348				return this;
349
350			int needed_cap = _length + value.Length;
351			if (null != _cached_str || _str.Length < needed_cap)
352				InternalEnsureCapacity (needed_cap);
353			
354			String.CharCopy (_str, _length, value, 0, value.Length);
355			_length = needed_cap;
356
357			return this;
358		} 
359		
360		public StringBuilder Append (string value) 
361		{
362			if (value == null)
363				return this;
364			
365			if (_length == 0 && value.Length < _maxCapacity && value.Length > _str.Length) {
366				_length = value.Length;
367				_str = _cached_str = value;
368				return this;
369			}
370
371			int needed_cap = _length + value.Length;
372			if (null != _cached_str || _str.Length < needed_cap)
373				InternalEnsureCapacity (needed_cap);
374
375			String.CharCopy (_str, _length, value, 0, value.Length);
376			_length = needed_cap;
377			return this;
378		}
379
380		public StringBuilder Append (bool value) {
381			return Append (value.ToString());
382		}
383		
384		public StringBuilder Append (byte value) {
385			return Append (value.ToString());
386		}
387
388		public StringBuilder Append (decimal value) {
389			return Append (value.ToString());
390		}
391
392		public StringBuilder Append (double value) {
393			return Append (value.ToString());
394		}
395
396		public StringBuilder Append (short value) {
397			return Append (value.ToString());
398		}
399
400		public StringBuilder Append (int value) {
401			return Append (value.ToString());
402		}
403
404		public StringBuilder Append (long value) {
405			return Append (value.ToString());
406		}
407
408		public StringBuilder Append (object value) {
409			if (value == null)
410				return this;
411
412			return Append (value.ToString());
413		}
414
415		[CLSCompliant(false)]
416		public StringBuilder Append (sbyte value) {
417			return Append (value.ToString());
418		}
419
420		public StringBuilder Append (float value) {
421			return Append (value.ToString());
422		}
423
424		[CLSCompliant(false)]
425		public StringBuilder Append (ushort value) {
426			return Append (value.ToString());
427		}	
428		
429		[CLSCompliant(false)]
430		public StringBuilder Append (uint value) {
431			return Append (value.ToString());
432		}
433
434		[CLSCompliant(false)]
435		public StringBuilder Append (ulong value) {
436			return Append (value.ToString());
437		}
438
439		public StringBuilder Append (char value) 
440		{
441			int needed_cap = _length + 1;
442			if (null != _cached_str || _str.Length < needed_cap)
443				InternalEnsureCapacity (needed_cap);
444
445			_str.InternalSetChar(_length, value);
446			_length = needed_cap;
447
448			return this;
449		}
450
451		public StringBuilder Append (char value, int repeatCount) 
452		{
453			if( repeatCount < 0 )
454				throw new ArgumentOutOfRangeException();
455
456			InternalEnsureCapacity (_length + repeatCount);
457			
458			for (int i = 0; i < repeatCount; i++)
459				_str.InternalSetChar (_length++, value);
460
461			return this;
462		}
463
464		public StringBuilder Append( char[] value, int startIndex, int charCount ) 
465		{
466			if (value == null) {
467				if (!(startIndex == 0 && charCount == 0))
468					throw new ArgumentNullException ("value");
469
470				return this;
471			}
472
473			if ((charCount < 0 || startIndex < 0) || (startIndex > value.Length - charCount)) 
474				throw new ArgumentOutOfRangeException();
475			
476			int needed_cap = _length + charCount;
477			InternalEnsureCapacity (needed_cap);
478
479			String.CharCopy (_str, _length, value, startIndex, charCount);
480			_length = needed_cap;
481
482			return this;
483		}
484
485		public StringBuilder Append (string value, int startIndex, int count) 
486		{
487			if (value == null) {
488				if (startIndex != 0 && count != 0)
489					throw new ArgumentNullException ("value");
490					
491				return this;
492			}
493
494			if ((count < 0 || startIndex < 0) || (startIndex > value.Length - count))
495				throw new ArgumentOutOfRangeException();
496			
497			int needed_cap = _length + count;
498			if (null != _cached_str || _str.Length < needed_cap)
499				InternalEnsureCapacity (needed_cap);
500
501			String.CharCopy (_str, _length, value, startIndex, count);
502			
503			_length = needed_cap;
504
505			return this;
506		}
507
508#if NET_4_0 || MOONLIGHT || MOBILE
509		public StringBuilder Clear ()
510		{
511			Length = 0;
512			return this;
513		}
514#endif
515
516		[ComVisible (false)]
517		public StringBuilder AppendLine ()
518		{
519			return Append (System.Environment.NewLine);
520		}
521
522		[ComVisible (false)]
523		public StringBuilder AppendLine (string value)
524		{
525			return Append (value).Append (System.Environment.NewLine);
526		}
527
528		public StringBuilder AppendFormat (string format, params object[] args)
529		{
530			return AppendFormat (null, format, args);
531		}
532
533		public StringBuilder AppendFormat (IFormatProvider provider,
534						   string format,
535						   params object[] args)
536		{
537			String.FormatHelper (this, provider, format, args);
538			return this;
539		}
540
541#if MOONLIGHT
542		internal
543#else
544		public
545#endif
546		StringBuilder AppendFormat (string format, object arg0)
547		{
548			return AppendFormat (null, format, new object [] { arg0 });
549		}
550
551#if MOONLIGHT
552		internal
553#else
554		public
555#endif
556		StringBuilder AppendFormat (string format, object arg0, object arg1)
557		{
558			return AppendFormat (null, format, new object [] { arg0, arg1 });
559		}
560
561#if MOONLIGHT
562		internal
563#else
564		public
565#endif
566		StringBuilder AppendFormat (string format, object arg0, object arg1, object arg2)
567		{
568			return AppendFormat (null, format, new object [] { arg0, arg1, arg2 });
569		}
570
571		/*  The Insert Functions */
572		
573		public StringBuilder Insert (int index, char[] value) 
574		{
575			return Insert (index, new string (value));
576		}
577				
578		public StringBuilder Insert (int index, string value) 
579		{
580			if( index > _length || index < 0)
581				throw new ArgumentOutOfRangeException();
582
583			if (value == null || value.Length == 0)
584				return this;
585
586			InternalEnsureCapacity (_length + value.Length);
587
588			// Move everything to the right of the insert point across
589			String.CharCopyReverse (_str, index + value.Length, _str, index, _length - index);
590			
591			// Copy in stuff from the insert buffer
592			String.CharCopy (_str, index, value, 0, value.Length);
593			
594			_length += value.Length;
595
596			return this;
597		}
598
599		public StringBuilder Insert( int index, bool value ) {
600			return Insert (index, value.ToString());
601		}
602		
603		public StringBuilder Insert( int index, byte value ) {
604			return Insert (index, value.ToString());
605		}
606
607		public StringBuilder Insert( int index, char value) 
608		{
609			if (index > _length || index < 0)
610				throw new ArgumentOutOfRangeException ("index");
611
612			InternalEnsureCapacity (_length + 1);
613			
614			// Move everything to the right of the insert point across
615			String.CharCopyReverse (_str, index + 1, _str, index, _length - index);
616			
617			_str.InternalSetChar (index, value);
618			_length++;
619
620			return this;
621		}
622
623		public StringBuilder Insert( int index, decimal value ) {
624			return Insert (index, value.ToString());
625		}
626
627		public StringBuilder Insert( int index, double value ) {
628			return Insert (index, value.ToString());
629		}
630		
631		public StringBuilder Insert( int index, short value ) {
632			return Insert (index, value.ToString());
633		}
634
635		public StringBuilder Insert( int index, int value ) {
636			return Insert (index, value.ToString());
637		}
638
639		public StringBuilder Insert( int index, long value ) {
640			return Insert (index, value.ToString());
641		}
642	
643		public StringBuilder Insert( int index, object value ) {
644			return Insert (index, value.ToString());
645		}
646		
647		[CLSCompliant(false)]
648		public StringBuilder Insert( int index, sbyte value ) {
649			return Insert (index, value.ToString() );
650		}
651
652		public StringBuilder Insert (int index, float value) {
653			return Insert (index, value.ToString() );
654		}
655
656		[CLSCompliant(false)]
657		public StringBuilder Insert (int index, ushort value) {
658			return Insert (index, value.ToString() );
659		}
660
661		[CLSCompliant(false)]
662		public StringBuilder Insert (int index, uint value) {
663			return Insert ( index, value.ToString() );
664		}
665		
666		[CLSCompliant(false)]
667		public StringBuilder Insert (int index, ulong value) {
668			return Insert ( index, value.ToString() );
669		}
670
671		public StringBuilder Insert (int index, string value, int count) 
672		{
673			// LAMESPEC: The spec says to throw an exception if 
674			// count < 0, while MS throws even for count < 1!
675			if ( count < 0 )
676				throw new ArgumentOutOfRangeException();
677
678			if (value != null && value != String.Empty)
679				for (int insertCount = 0; insertCount < count; insertCount++)
680					Insert( index, value );
681
682			return this;
683		}
684
685		public StringBuilder Insert (int index, char [] value, int startIndex, int charCount)
686		{
687			if (value == null) {
688				if (startIndex == 0 && charCount == 0)
689					return this;
690
691				throw new ArgumentNullException ("value");
692			}
693
694			if (charCount < 0 || startIndex < 0 || startIndex > value.Length - charCount)
695				throw new ArgumentOutOfRangeException ();
696
697			return Insert (index, new String (value, startIndex, charCount));
698		}
699	
700		private void InternalEnsureCapacity (int size) 
701		{
702			if (size > _str.Length || (object) _cached_str == (object) _str) {
703				int capacity = _str.Length;
704
705				// Try double buffer, if that doesn't work, set the length as capacity
706				if (size > capacity) {
707					
708					// The first time a string is appended, we just set _cached_str
709					// and _str to it. This allows us to do some optimizations.
710					// Below, we take this into account.
711					if ((object) _cached_str == (object) _str && capacity < constDefaultCapacity)
712						capacity = constDefaultCapacity;
713					
714					capacity = capacity << 1;
715					if (size > capacity)
716						capacity = size;
717
718					if (capacity >= Int32.MaxValue || capacity < 0)
719						capacity = Int32.MaxValue;
720
721					if (capacity > _maxCapacity && size <= _maxCapacity)
722						capacity = _maxCapacity;
723					
724					if (capacity > _maxCapacity)
725						throw new ArgumentOutOfRangeException ("size", "capacity was less than the current size.");
726				}
727
728				string tmp = String.InternalAllocateStr (capacity);
729				if (_length > 0)
730					String.CharCopy (tmp, 0, _str, 0, _length);
731
732				_str = tmp;
733			}
734
735			_cached_str = null;
736		}
737
738		[ComVisible (false)]
739		public void CopyTo (int sourceIndex, char [] destination, int destinationIndex, int count)
740		{
741			if (destination == null)
742				throw new ArgumentNullException ("destination");
743			if ((Length - count < sourceIndex) ||
744			    (destination.Length -count < destinationIndex) ||
745			    (sourceIndex < 0 || destinationIndex < 0 || count < 0))
746				throw new ArgumentOutOfRangeException ();
747
748			for (int i = 0; i < count; i++)
749				destination [destinationIndex+i] = _str [sourceIndex+i];
750		}
751
752		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
753		{
754			info.AddValue ("m_MaxCapacity", _maxCapacity);
755			info.AddValue ("Capacity", Capacity);
756			info.AddValue ("m_StringValue", ToString ());
757			info.AddValue ("m_currentThread", 0);
758		}
759
760		StringBuilder (SerializationInfo info, StreamingContext context)
761		{
762			string s = info.GetString ("m_StringValue");
763			if (s == null)
764				s = "";
765			_length = s.Length;
766			_str = _cached_str = s;
767			
768			_maxCapacity = info.GetInt32 ("m_MaxCapacity");
769			if (_maxCapacity < 0)
770				_maxCapacity = Int32.MaxValue;
771			Capacity = info.GetInt32 ("Capacity");
772		}
773	}
774}