--- /dev/null
+// -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+//
+// System.Text.StringBuilder
+//
+// Authors:
+// Marcin Szczepanski (marcins@zipworld.com.au)
+// Paolo Molaro (lupus@ximian.com)
+// Patrik Torstensson
+//
+// NOTE: In the case the buffer is only filled by 50% a new string
+// will be returned by ToString() is cached in the '_cached_str'
+// cache_string will also control if a string has been handed out
+// to via ToString(). If you are chaning the code make sure that
+// if you modify the string data set the cache_string to null.
+//
+
+//
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright 2011 Xamarin Inc
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+using System.Runtime.Serialization;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace System.Text {
+
+ [Serializable]
+ [ComVisible (true)]
+ [MonoLimitation ("Serialization format not compatible with .NET")]
+ [StructLayout (LayoutKind.Sequential)]
+ public sealed class StringBuilder : ISerializable
+ {
+ private int _length;
+ private string _str;
+ private string _cached_str;
+
+ private int _maxCapacity;
+ internal const int DefaultCapacity = 16;
+
+ public StringBuilder(string value, int startIndex, int length, int capacity)
+ : this (value, startIndex, length, capacity, Int32.MaxValue)
+ {
+ }
+
+ private StringBuilder(string value, int startIndex, int length, int capacity, int maxCapacity)
+ {
+ // first, check the parameters and throw appropriate exceptions if needed
+ if (null == value)
+ value = "";
+
+ // make sure startIndex is zero or positive
+ if (startIndex < 0)
+ throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex cannot be less than zero.");
+
+ // make sure length is zero or positive
+ if(length < 0)
+ throw new System.ArgumentOutOfRangeException ("length", length, "Length cannot be less than zero.");
+
+ if (capacity < 0)
+ throw new System.ArgumentOutOfRangeException ("capacity", capacity, "capacity must be greater than zero.");
+
+ if (maxCapacity < 1)
+ throw new System.ArgumentOutOfRangeException ("maxCapacity", "maxCapacity is less than one.");
+ if (capacity > maxCapacity)
+ throw new System.ArgumentOutOfRangeException ("capacity", "Capacity exceeds maximum capacity.");
+
+ // make sure startIndex and length give a valid substring of value
+ // re-ordered to avoid possible integer overflow
+ if (startIndex > value.Length - length)
+ throw new System.ArgumentOutOfRangeException ("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");
+
+ if (capacity == 0) {
+ if (maxCapacity > DefaultCapacity)
+ capacity = DefaultCapacity;
+ else
+ _str = _cached_str = String.Empty;
+ }
+ _maxCapacity = maxCapacity;
+
+ if (_str == null)
+ _str = String.InternalAllocateStr ((length > capacity) ? length : capacity);
+ if (length > 0)
+ String.CharCopy (_str, 0, value, startIndex, length);
+
+ _length = length;
+ }
+
+ public StringBuilder () : this (null) {}
+
+ public StringBuilder(int capacity) : this (String.Empty, 0, 0, capacity) {}
+
+ public StringBuilder(int capacity, int maxCapacity) : this (String.Empty, 0, 0, capacity, maxCapacity) { }
+
+ public StringBuilder (string value)
+ {
+ /*
+ * This is an optimization to avoid allocating the internal string
+ * until the first Append () call.
+ * The runtime pinvoke marshalling code needs to be aware of this.
+ */
+ if (null == value)
+ value = "";
+
+ _length = value.Length;
+ _str = _cached_str = value;
+ _maxCapacity = Int32.MaxValue;
+ }
+
+ public StringBuilder( string value, int capacity) : this(value == null ? "" : value, 0, value == null ? 0 : value.Length, capacity) {}
+
+ public int MaxCapacity {
+ get {
+ return _maxCapacity;
+ }
+ }
+
+ public int Capacity {
+ get {
+ if (_str.Length == 0)
+ return Math.Min (_maxCapacity, DefaultCapacity);
+
+ return _str.Length;
+ }
+
+ set {
+ if (value < _length)
+ throw new ArgumentException( "Capacity must be larger than length" );
+
+ if (value > _maxCapacity)
+ throw new ArgumentOutOfRangeException ("value", "Should be less than or equal to MaxCapacity");
+
+ InternalEnsureCapacity(value);
+ }
+ }
+
+ public int Length {
+ get {
+ return _length;
+ }
+
+ set {
+ if( value < 0 || value > _maxCapacity)
+ throw new ArgumentOutOfRangeException();
+
+ if (value == _length)
+ return;
+
+ if (value < _length) {
+ // LAMESPEC: The spec is unclear as to what to do
+ // with the capacity when truncating the string.
+
+ // Do as MS, keep the capacity
+
+ // Make sure that we invalidate any cached string.
+ InternalEnsureCapacity (value);
+ _length = value;
+ } else {
+ // Expand the capacity to the new length and
+ // pad the string with NULL characters.
+ Append('\0', value - _length);
+ }
+ }
+ }
+
+ [IndexerName("Chars")]
+ public char this [int index] {
+ get {
+ if (index >= _length || index < 0)
+ throw new IndexOutOfRangeException();
+
+ return _str [index];
+ }
+
+ set {
+ if (index >= _length || index < 0)
+ throw new IndexOutOfRangeException();
+
+ if (null != _cached_str)
+ InternalEnsureCapacity (_length);
+
+ _str.InternalSetChar (index, value);
+ }
+ }
+
+ public override string ToString ()
+ {
+ if (_length == 0)
+ return String.Empty;
+
+ if (null != _cached_str)
+ return _cached_str;
+
+ // If we only have a half-full buffer we return a new string.
+ if (_length < (_str.Length >> 1) || (_str.Length > string.LOS_limit && _length <= string.LOS_limit))
+ {
+ // use String.SubstringUnchecked instead of String.Substring
+ // as the former is guaranteed to create a new string object
+ _cached_str = _str.SubstringUnchecked (0, _length);
+ return _cached_str;
+ }
+
+ _cached_str = _str;
+ _str.InternalSetLength(_length);
+
+ return _str;
+ }
+
+ public string ToString (int startIndex, int length)
+ {
+ // re-ordered to avoid possible integer overflow
+ if (startIndex < 0 || length < 0 || startIndex > _length - length)
+ throw new ArgumentOutOfRangeException();
+
+ // use String.SubstringUnchecked instead of String.Substring
+ // as the former is guaranteed to create a new string object
+ if (startIndex == 0 && length == _length)
+ return ToString ();
+ else
+ return _str.SubstringUnchecked (startIndex, length);
+ }
+
+ public int EnsureCapacity (int capacity)
+ {
+ if (capacity < 0)
+ throw new ArgumentOutOfRangeException ("Capacity must be greater than 0." );
+
+ if( capacity <= _str.Length )
+ return _str.Length;
+
+ InternalEnsureCapacity (capacity);
+
+ return _str.Length;
+ }
+
+ public bool Equals (StringBuilder sb)
+ {
+ if (((object)sb) == null)
+ return false;
+
+ if (_length == sb.Length && _str == sb._str )
+ return true;
+
+ return false;
+ }
+
+ public StringBuilder Remove (int startIndex, int length)
+ {
+ // re-ordered to avoid possible integer overflow
+ if (startIndex < 0 || length < 0 || startIndex > _length - length)
+ throw new ArgumentOutOfRangeException();
+
+ if (null != _cached_str)
+ InternalEnsureCapacity (_length);
+
+ // Copy everything after the 'removed' part to the start
+ // of the removed part and truncate the sLength
+ if (_length - (startIndex + length) > 0)
+ String.CharCopy (_str, startIndex, _str, startIndex + length, _length - (startIndex + length));
+
+ _length -= length;
+
+ return this;
+ }
+
+ public StringBuilder Replace (char oldChar, char newChar)
+ {
+ return Replace( oldChar, newChar, 0, _length);
+ }
+
+ public StringBuilder Replace (char oldChar, char newChar, int startIndex, int count)
+ {
+ // re-ordered to avoid possible integer overflow
+ if (startIndex > _length - count || startIndex < 0 || count < 0)
+ throw new ArgumentOutOfRangeException();
+
+ if (null != _cached_str)
+ InternalEnsureCapacity (_str.Length);
+
+ for (int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
+ if( _str [replaceIterate] == oldChar )
+ _str.InternalSetChar (replaceIterate, newChar);
+ }
+
+ return this;
+ }
+
+ public StringBuilder Replace( string oldValue, string newValue ) {
+ return Replace (oldValue, newValue, 0, _length);
+ }
+
+ public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count )
+ {
+ if (oldValue == null)
+ throw new ArgumentNullException ("The old value cannot be null.");
+
+ if (startIndex < 0 || count < 0 || startIndex > _length - count)
+ throw new ArgumentOutOfRangeException ();
+
+ if (oldValue.Length == 0)
+ throw new ArgumentException ("The old value cannot be zero length.");
+
+ string substr = _str.Substring(startIndex, count);
+ string replace = substr.Replace(oldValue, newValue);
+ // return early if no oldValue was found
+ if ((object) replace == (object) substr)
+ return this;
+
+ InternalEnsureCapacity (replace.Length + (_length - count));
+
+ // shift end part
+ if (replace.Length < count)
+ String.CharCopy (_str, startIndex + replace.Length, _str, startIndex + count, _length - startIndex - count);
+ else if (replace.Length > count)
+ String.CharCopyReverse (_str, startIndex + replace.Length, _str, startIndex + count, _length - startIndex - count);
+
+ // copy middle part back into _str
+ String.CharCopy (_str, startIndex, replace, 0, replace.Length);
+
+ _length = replace.Length + (_length - count);
+
+ return this;
+ }
+
+
+ /* The Append Methods */
+ public StringBuilder Append (char[] value)
+ {
+ if (value == null)
+ return this;
+
+ int needed_cap = _length + value.Length;
+ if (null != _cached_str || _str.Length < needed_cap)
+ InternalEnsureCapacity (needed_cap);
+
+ String.CharCopy (_str, _length, value, 0, value.Length);
+ _length = needed_cap;
+
+ return this;
+ }
+
+ public StringBuilder Append (string value)
+ {
+ if (value == null)
+ return this;
+
+ if (_length == 0 && value.Length < _maxCapacity && value.Length > _str.Length) {
+ _length = value.Length;
+ _str = _cached_str = value;
+ return this;
+ }
+
+ int needed_cap = _length + value.Length;
+ if (null != _cached_str || _str.Length < needed_cap)
+ InternalEnsureCapacity (needed_cap);
+
+ String.CharCopy (_str, _length, value, 0, value.Length);
+ _length = needed_cap;
+ return this;
+ }
+
+ public StringBuilder Append (bool value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (byte value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (decimal value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (double value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (short value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (int value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (long value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (object value) {
+ if (value == null)
+ return this;
+
+ return Append (value.ToString());
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Append (sbyte value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (float value) {
+ return Append (value.ToString());
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Append (ushort value) {
+ return Append (value.ToString());
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Append (uint value) {
+ return Append (value.ToString());
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Append (ulong value) {
+ return Append (value.ToString());
+ }
+
+ public StringBuilder Append (char value)
+ {
+ int needed_cap = _length + 1;
+ if (null != _cached_str || _str.Length < needed_cap)
+ InternalEnsureCapacity (needed_cap);
+
+ _str.InternalSetChar(_length, value);
+ _length = needed_cap;
+
+ return this;
+ }
+
+ public StringBuilder Append (char value, int repeatCount)
+ {
+ if( repeatCount < 0 )
+ throw new ArgumentOutOfRangeException();
+
+ InternalEnsureCapacity (_length + repeatCount);
+
+ for (int i = 0; i < repeatCount; i++)
+ _str.InternalSetChar (_length++, value);
+
+ return this;
+ }
+
+ public StringBuilder Append( char[] value, int startIndex, int charCount )
+ {
+ if (value == null) {
+ if (!(startIndex == 0 && charCount == 0))
+ throw new ArgumentNullException ("value");
+
+ return this;
+ }
+
+ if ((charCount < 0 || startIndex < 0) || (startIndex > value.Length - charCount))
+ throw new ArgumentOutOfRangeException();
+
+ int needed_cap = _length + charCount;
+ InternalEnsureCapacity (needed_cap);
+
+ String.CharCopy (_str, _length, value, startIndex, charCount);
+ _length = needed_cap;
+
+ return this;
+ }
+
+ public StringBuilder Append (string value, int startIndex, int count)
+ {
+ if (value == null) {
+ if (startIndex != 0 && count != 0)
+ throw new ArgumentNullException ("value");
+
+ return this;
+ }
+
+ if ((count < 0 || startIndex < 0) || (startIndex > value.Length - count))
+ throw new ArgumentOutOfRangeException();
+
+ int needed_cap = _length + count;
+ if (null != _cached_str || _str.Length < needed_cap)
+ InternalEnsureCapacity (needed_cap);
+
+ String.CharCopy (_str, _length, value, startIndex, count);
+
+ _length = needed_cap;
+
+ return this;
+ }
+
+ public StringBuilder Clear ()
+ {
+ Length = 0;
+ return this;
+ }
+
+ [ComVisible (false)]
+ public StringBuilder AppendLine ()
+ {
+ return Append (System.Environment.NewLine);
+ }
+
+ [ComVisible (false)]
+ public StringBuilder AppendLine (string value)
+ {
+ return Append (value).Append (System.Environment.NewLine);
+ }
+
+ public StringBuilder AppendFormat (string format, params object[] args)
+ {
+ return AppendFormat (null, format, args);
+ }
+
+ public StringBuilder AppendFormat (IFormatProvider provider,
+ string format,
+ params object[] args)
+ {
+ String.FormatHelper (this, provider, format, args);
+ return this;
+ }
+
+ public
+ StringBuilder AppendFormat (string format, object arg0)
+ {
+ return AppendFormat (null, format, new object [] { arg0 });
+ }
+
+ public
+ StringBuilder AppendFormat (string format, object arg0, object arg1)
+ {
+ return AppendFormat (null, format, new object [] { arg0, arg1 });
+ }
+
+ public
+ StringBuilder AppendFormat (string format, object arg0, object arg1, object arg2)
+ {
+ return AppendFormat (null, format, new object [] { arg0, arg1, arg2 });
+ }
+
+ /* The Insert Functions */
+
+ public StringBuilder Insert (int index, char[] value)
+ {
+ return Insert (index, new string (value));
+ }
+
+ public StringBuilder Insert (int index, string value)
+ {
+ if( index > _length || index < 0)
+ throw new ArgumentOutOfRangeException();
+
+ if (value == null || value.Length == 0)
+ return this;
+
+ InternalEnsureCapacity (_length + value.Length);
+
+ // Move everything to the right of the insert point across
+ String.CharCopyReverse (_str, index + value.Length, _str, index, _length - index);
+
+ // Copy in stuff from the insert buffer
+ String.CharCopy (_str, index, value, 0, value.Length);
+
+ _length += value.Length;
+
+ return this;
+ }
+
+ public StringBuilder Insert( int index, bool value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, byte value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, char value)
+ {
+ if (index > _length || index < 0)
+ throw new ArgumentOutOfRangeException ("index");
+
+ InternalEnsureCapacity (_length + 1);
+
+ // Move everything to the right of the insert point across
+ String.CharCopyReverse (_str, index + 1, _str, index, _length - index);
+
+ _str.InternalSetChar (index, value);
+ _length++;
+
+ return this;
+ }
+
+ public StringBuilder Insert( int index, decimal value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, double value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, short value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, int value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, long value ) {
+ return Insert (index, value.ToString());
+ }
+
+ public StringBuilder Insert( int index, object value ) {
+ return Insert (index, value.ToString());
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Insert( int index, sbyte value ) {
+ return Insert (index, value.ToString() );
+ }
+
+ public StringBuilder Insert (int index, float value) {
+ return Insert (index, value.ToString() );
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Insert (int index, ushort value) {
+ return Insert (index, value.ToString() );
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Insert (int index, uint value) {
+ return Insert ( index, value.ToString() );
+ }
+
+ [CLSCompliant(false)]
+ public StringBuilder Insert (int index, ulong value) {
+ return Insert ( index, value.ToString() );
+ }
+
+ public StringBuilder Insert (int index, string value, int count)
+ {
+ // LAMESPEC: The spec says to throw an exception if
+ // count < 0, while MS throws even for count < 1!
+ if ( count < 0 )
+ throw new ArgumentOutOfRangeException();
+
+ if (value != null && value != String.Empty)
+ for (int insertCount = 0; insertCount < count; insertCount++)
+ Insert( index, value );
+
+ return this;
+ }
+
+ public StringBuilder Insert (int index, char [] value, int startIndex, int charCount)
+ {
+ if (value == null) {
+ if (startIndex == 0 && charCount == 0)
+ return this;
+
+ throw new ArgumentNullException ("value");
+ }
+
+ if (charCount < 0 || startIndex < 0 || startIndex > value.Length - charCount)
+ throw new ArgumentOutOfRangeException ();
+
+ return Insert (index, new String (value, startIndex, charCount));
+ }
+
+ private void InternalEnsureCapacity (int size)
+ {
+ if (size > _str.Length || (object) _cached_str == (object) _str) {
+ int capacity = _str.Length;
+
+ // Try double buffer, if that doesn't work, set the length as capacity
+ if (size > capacity) {
+
+ // The first time a string is appended, we just set _cached_str
+ // and _str to it. This allows us to do some optimizations.
+ // Below, we take this into account.
+ if ((object) _cached_str == (object) _str && capacity < DefaultCapacity)
+ capacity = DefaultCapacity;
+
+ capacity = capacity << 1;
+ if (size > capacity)
+ capacity = size;
+
+ if (capacity >= Int32.MaxValue || capacity < 0)
+ capacity = Int32.MaxValue;
+
+ if (capacity > _maxCapacity && size <= _maxCapacity)
+ capacity = _maxCapacity;
+
+ if (capacity > _maxCapacity)
+ throw new ArgumentOutOfRangeException ("size", "capacity was less than the current size.");
+ }
+
+ string tmp = String.InternalAllocateStr (capacity);
+ if (_length > 0)
+ String.CharCopy (tmp, 0, _str, 0, _length);
+
+ _str = tmp;
+ }
+
+ _cached_str = null;
+ }
+
+ [ComVisible (false)]
+ public void CopyTo (int sourceIndex, char [] destination, int destinationIndex, int count)
+ {
+ if (destination == null)
+ throw new ArgumentNullException ("destination");
+ if ((Length - count < sourceIndex) ||
+ (destination.Length -count < destinationIndex) ||
+ (sourceIndex < 0 || destinationIndex < 0 || count < 0))
+ throw new ArgumentOutOfRangeException ();
+
+ for (int i = 0; i < count; i++)
+ destination [destinationIndex+i] = _str [sourceIndex+i];
+ }
+
+ void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ info.AddValue ("m_MaxCapacity", _maxCapacity);
+ info.AddValue ("Capacity", Capacity);
+ info.AddValue ("m_StringValue", ToString ());
+ info.AddValue ("m_currentThread", 0);
+ }
+
+ StringBuilder (SerializationInfo info, StreamingContext context)
+ {
+ string s = info.GetString ("m_StringValue");
+ if (s == null)
+ s = "";
+ _length = s.Length;
+ _str = _cached_str = s;
+
+ _maxCapacity = info.GetInt32 ("m_MaxCapacity");
+ if (_maxCapacity < 0)
+ _maxCapacity = Int32.MaxValue;
+ Capacity = info.GetInt32 ("Capacity");
+ }
+ }
+}