//
// 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
//
using System.Runtime.Serialization;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace System.Text {
[Serializable]
- [MonoTODO ("Fix serialization compatibility with MS.NET")]
- public sealed class StringBuilder
-#if NET_2_0
- : ISerializable
-#endif
+ [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 = Int32.MaxValue;
+ private int _maxCapacity;
private const int constDefaultCapacity = 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)
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)
- capacity = constDefaultCapacity;
+ if (capacity == 0) {
+ if (maxCapacity > constDefaultCapacity)
+ capacity = constDefaultCapacity;
+ else
+ _str = _cached_str = String.Empty;
+ }
+ _maxCapacity = maxCapacity;
- _str = String.InternalAllocateStr ((length > capacity) ? length : capacity);
+ if (_str == null)
+ _str = String.InternalAllocateStr ((length > capacity) ? length : capacity);
if (length > 0)
- String.InternalStrcpy(_str, 0, value, startIndex, length);
+ String.CharCopy (_str, 0, value, startIndex, length);
_length = length;
}
public StringBuilder(int capacity) : this (String.Empty, 0, 0, capacity) {}
- public StringBuilder(int capacity, int maxCapacity) : this (String.Empty, 0, 0, capacity) {
- 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.");
-
- _maxCapacity = maxCapacity;
- }
+ 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, 0, value.Length, capacity) {}
+ public StringBuilder( string value, int capacity) : this(value == null ? "" : value, 0, value == null ? 0 : value.Length, capacity) {}
public int MaxCapacity {
get {
- // MS runtime always returns Int32.MaxValue.
return _maxCapacity;
}
}
public int Capacity {
get {
if (_str.Length == 0)
- return constDefaultCapacity;
+ return Math.Min (_maxCapacity, constDefaultCapacity);
return _str.Length;
}
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);
}
}
_length = value;
} else {
// Expand the capacity to the new length and
- // pad the string with spaces.
-
- // LAMESPEC: The spec says to put the spaces on the
- // left of the string however the MS implementation
- // puts them on the right. We'll do that for
- // compatibility (!)
- Append(' ', value - _length);
+ // pad the string with NULL characters.
+ Append('\0', value - _length);
}
}
}
return _cached_str;
// If we only have a half-full buffer we return a new string.
- if (_length < (_str.Length >> 1))
+ if (_length < (_str.Length >> 1) || (_str.Length > string.LOS_limit && _length <= string.LOS_limit))
{
- _cached_str = _str.Substring(0, _length);
+ // 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;
}
if (startIndex < 0 || length < 0 || startIndex > _length - length)
throw new ArgumentOutOfRangeException();
- return _str.Substring (startIndex, length);
+ // 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)
public bool Equals (StringBuilder sb)
{
+ if (((object)sb) == null)
+ return false;
+
if (_length == sb.Length && _str == sb._str )
return true;
// Copy everything after the 'removed' part to the start
// of the removed part and truncate the sLength
if (_length - (startIndex + length) > 0)
- String.InternalStrcpy (_str, startIndex, _str, startIndex + length, _length - (startIndex + length));
+ String.CharCopy (_str, startIndex, _str, startIndex + length, _length - (startIndex + length));
_length -= length;
if (oldValue.Length == 0)
throw new ArgumentException ("The old value cannot be zero length.");
- // TODO: OPTIMIZE!
- string replace = _str.Substring(startIndex, count).Replace(oldValue, newValue);
+ 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));
- string end = _str.Substring (startIndex + count, _length - startIndex - 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);
- String.InternalStrcpy (_str, startIndex, replace);
- String.InternalStrcpy (_str, startIndex + replace.Length, end);
+ // copy middle part back into _str
+ String.CharCopy (_str, startIndex, replace, 0, replace.Length);
_length = replace.Length + (_length - count);
if (null != _cached_str || _str.Length < needed_cap)
InternalEnsureCapacity (needed_cap);
- String.InternalStrcpy (_str, _length, value);
- _length += value.Length;
+ String.CharCopy (_str, _length, value, 0, value.Length);
+ _length = needed_cap;
return this;
}
if (null != _cached_str || _str.Length < needed_cap)
InternalEnsureCapacity (needed_cap);
- String.InternalStrcpy (_str, _length, value);
- _length += value.Length;
+ String.CharCopy (_str, _length, value, 0, value.Length);
+ _length = needed_cap;
return this;
}
InternalEnsureCapacity (needed_cap);
_str.InternalSetChar(_length, value);
- _length++;
+ _length = needed_cap;
return this;
}
if ((charCount < 0 || startIndex < 0) || (startIndex > value.Length - charCount))
throw new ArgumentOutOfRangeException();
-
- InternalEnsureCapacity (_length + charCount);
+ int needed_cap = _length + charCount;
+ InternalEnsureCapacity (needed_cap);
- String.InternalStrcpy (_str, _length, value, startIndex, charCount);
- _length += charCount;
+ String.CharCopy (_str, _length, value, startIndex, charCount);
+ _length = needed_cap;
return this;
}
if (null != _cached_str || _str.Length < needed_cap)
InternalEnsureCapacity (needed_cap);
- String.InternalStrcpy (_str, _length, value, startIndex, count);
+ String.CharCopy (_str, _length, value, startIndex, count);
- _length += count;
+ _length = needed_cap;
return this;
}
-#if NET_2_0
+#if NET_4_0 || MOONLIGHT || MOBILE
+ public StringBuilder Clear ()
+ {
+ Length = 0;
+ return this;
+ }
+#endif
+
+ [ComVisible (false)]
public StringBuilder AppendLine ()
{
return Append (System.Environment.NewLine);
}
+ [ComVisible (false)]
public StringBuilder AppendLine (string value)
{
return Append (value).Append (System.Environment.NewLine);
}
-#endif
-
- public StringBuilder AppendFormat (string format, object arg0)
- {
- return AppendFormat (null, format, new object [] { arg0 });
- }
public StringBuilder AppendFormat (string format, params object[] args)
{
return this;
}
- public StringBuilder AppendFormat (string format, object arg0, object arg1)
+#if MOONLIGHT
+ internal
+#else
+ public
+#endif
+ StringBuilder AppendFormat (string format, object arg0)
+ {
+ return AppendFormat (null, format, new object [] { arg0 });
+ }
+
+#if MOONLIGHT
+ internal
+#else
+ public
+#endif
+ 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)
+#if MOONLIGHT
+ internal
+#else
+ public
+#endif
+ StringBuilder AppendFormat (string format, object arg0, object arg1, object arg2)
{
return AppendFormat (null, format, new object [] { arg0, arg1, arg2 });
}
InternalEnsureCapacity (_length + value.Length);
// Move everything to the right of the insert point across
- String.InternalStrcpy (_str, index + value.Length, _str, index, _length - index);
+ String.CharCopyReverse (_str, index + value.Length, _str, index, _length - index);
// Copy in stuff from the insert buffer
- String.InternalStrcpy (_str, index, value);
+ String.CharCopy (_str, index, value, 0, value.Length);
_length += value.Length;
InternalEnsureCapacity (_length + 1);
// Move everything to the right of the insert point across
- String.InternalStrcpy (_str, index + 1, _str, index, _length - index);
+ String.CharCopyReverse (_str, index + 1, _str, index, _length - index);
_str.InternalSetChar (index, value);
_length++;
string tmp = String.InternalAllocateStr (capacity);
if (_length > 0)
- String.InternalStrcpy (tmp, 0, _str, 0, _length);
+ String.CharCopy (tmp, 0, _str, 0, _length);
_str = tmp;
}
_cached_str = null;
}
-#if NET_2_0
+ [ComVisible (false)]
public void CopyTo (int sourceIndex, char [] destination, int destinationIndex, int count)
{
if (destination == null)
_maxCapacity = Int32.MaxValue;
Capacity = info.GetInt32 ("Capacity");
}
-#endif
}
}