// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
/*============================================================
**
** Class: StringBuilder
**
**
**
** Purpose: A prototype implementation of the StringBuilder
** class.
**
** Date: December 8, 1997
** Last Updated: March 31, 1998
**
===========================================================*/
namespace System.Text {
using System.Text;
using System.Runtime.Serialization;
using System;
using System.Runtime.CompilerServices;
// This class represents a mutable string. It is convenient for situations in
// which it is desirable to modify a string, perhaps by removing, replacing, or
// inserting characters, without creating a new String subsequent to
// each modification.
//
// The methods contained within this class do not return a new StringBuilder
// object unless specified otherwise. This class may be used in conjunction with the String
// class to carry out modifications upon strings.
//
// When passing null into a constructor in VJ and VC, the null
// should be explicitly type cast.
// For Example:
// StringBuilder sb1 = new StringBuilder((StringBuilder)null);
// StringBuilder sb2 = new StringBuilder((String)null);
// Console.WriteLine(sb1);
// Console.WriteLine(sb2);
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder"]/*' />
[Serializable()] public sealed class StringBuilder {
//
//
// CLASS VARIABLES
//
//
internal int m_currentThread = InternalGetCurrentThread();
internal int m_MaxCapacity = 0;
internal String m_StringValue = null;
//
//
// STATIC CONSTANTS
//
//
internal const int DefaultCapacity = 16;
//
//
//CONSTRUCTORS
//
//
// Creates a new empty string builder (i.e., it represents String.Empty)
// with the default capacity (16 characters).
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder"]/*' />
public StringBuilder()
: this(DefaultCapacity) {
}
// Create a new empty string builder (i.e., it represents String.Empty)
// with the specified capacity.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder1"]/*' />
public StringBuilder(int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (capacity == 0) { // MakeFromString enforces this
capacity = DefaultCapacity;
}
m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
m_MaxCapacity = Int32.MaxValue;
}
// Creates a new string builder from the specified string. If value
// is a null String (i.e., if it represents String.NullString)
// then the new string builder will also be null (i.e., it will also represent
// String.NullString).
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder2"]/*' />
public StringBuilder(String value){
MakeFromString(value, 0, -1, -1);
}
// Creates a new string builder from the specified string with the specified
// capacity. If value is a null String (i.e., if it represents
// String.NullString) then the new string builder will also be null
// (i.e., it will also represent String.NullString).
// The maximum number of characters this string may contain is set by capacity.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder3"]/*' />
public StringBuilder(String value, int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
MakeFromString(value, 0, -1, capacity);
}
// Creates a new string builder from the specifed substring with the specified
// capacity. The maximum number of characters is set by capacity.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder4"]/*' />
public StringBuilder(String value, int startIndex, int length, int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (length<0) {
throw new ArgumentOutOfRangeException("length",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum"), "length"));
}
MakeFromString(value, startIndex, length, capacity);
}
// Creates an empty StringBuilder with a minimum capacity of capacity
// and a maximum capacity of maxCapacity.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder5"]/*' />
public StringBuilder(int capacity, int maxCapacity) {
if (capacity>maxCapacity) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
}
if (maxCapacity<1) {
throw new ArgumentOutOfRangeException("maxCapacity", Environment.GetResourceString("ArgumentOutOfRange_SmallMaxCapacity"));
}
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (capacity == 0) {
capacity = DefaultCapacity;
}
m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
m_MaxCapacity = maxCapacity;
}
private String GetThreadSafeString(out int tid) {
String temp = m_StringValue;
tid = InternalGetCurrentThread();
if (m_currentThread == tid)
return temp;
return String.GetStringForStringBuilder(temp, temp.Capacity);
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern static int InternalGetCurrentThread();
//
// Private native functions
//
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern void MakeFromString(String value, int startIndex, int length, int capacity);
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Capacity"]/*' />
public int Capacity {
get {return m_StringValue.Capacity;} //-1 to account for terminating null.
set {InternalSetCapacity(value);}
}
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.MaxCapacity"]/*' />
public int MaxCapacity {
get { return m_MaxCapacity; }
}
// Read-Only Property
// Ensures that the capacity of this string builder is at least the specified value.
// If capacity is greater than the capacity of this string builder, then the capacity
// is set to capacity; otherwise the capacity is unchanged.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.EnsureCapacity"]/*' />
public int EnsureCapacity(int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedPosCapacity"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
//If we need more space or the COW bit is set, copy the buffer.
if (!NeedsAllocation(currentString,capacity)) {
return currentString.Capacity;
}
String newString = GetNewString(currentString,capacity);
ReplaceString(tid,newString);
return newString.Capacity;
}
//Sets the capacity to be capacity. If capacity is less than the current
//instance an ArgumentException is thrown. If capacity is greater than the current
//instance, memory is allocated to allow the StringBuilder to grow.
//
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern int InternalSetCapacity(int capacity);
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString"]/*' />
public override String ToString() {
String currentString = m_StringValue;
int currentThread = m_currentThread;
if (currentThread != 0 && currentThread != InternalGetCurrentThread()) {
return String.InternalCopy(currentString);
}
if ((2 * currentString.Length) < currentString.ArrayLength) {
return String.InternalCopy(currentString);
}
currentString.ClearPostNullChar();
m_currentThread = 0;
return currentString;
}
// Converts a substring of this string builder to a String.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString1"]/*' />
public String ToString(int startIndex, int length) {
return m_StringValue.Substring(startIndex, length);
}
// Sets the length of the String in this buffer. If length is less than the current
// instance, the StringBuilder is truncated. If length is greater than the current
// instance, nulls are appended. The capacity is adjusted to be the same as the length.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Length"]/*' />
public int Length {
get {
return m_StringValue.Length;
}
set {
int tid;
String currentString = GetThreadSafeString(out tid);
if (value==0) { //the user is trying to clear the string
currentString.SetLength(0);
ReplaceString(tid,currentString);
return;
}
int currentLength = currentString.Length;
int newlength = value;
//If our length is less than 0 or greater than our Maximum capacity, bail.
if (newlength<0) {
throw new ArgumentOutOfRangeException("newlength", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
}
if (newlength>MaxCapacity) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
}
//Jump out early if our requested length our currentlength.
//This will be a pretty rare branch.
if (newlength == currentLength) {
return;
}
//If the StringBuilder has never been converted to a string, simply set the length
//without allocating a new string.
if (newlength <= currentString.Capacity) {
if (newlength > currentLength) {
for (int i = currentLength ; i < newlength; i++) // This is a rare case anyway.
currentString.InternalSetCharNoBoundsCheck(i,'/0');
}
currentString.InternalSetCharNoBoundsCheck(newlength,'/0'); //Null terminate.
currentString.SetLength(newlength);
ReplaceString(tid,currentString);
return;
}
// CopyOnWrite set we need to allocate a String
int newCapacity = (newlength>currentString.Capacity)?newlength:currentString.Capacity;
String newString = String.GetStringForStringBuilder(currentString, newCapacity);
//We know exactly how many characters we need, so embed that knowledge in the String.
newString.SetLength(newlength);
ReplaceString(tid,newString);
}
}
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.this"]/*' />
[System.Runtime.CompilerServices.IndexerName("Chars")]
public char this[int index] {
get {
return m_StringValue[index];
}
set {
int tid;
String currentString = GetThreadSafeString(out tid);
currentString.SetChar(index, value);
ReplaceString(tid,currentString);
}
}
// Appends a character at the end of this string builder. The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append"]/*' />
public StringBuilder Append(char value, int repeatCount) {
if (repeatCount==0) {
return this;
}
if (repeatCount<0) {
throw new ArgumentOutOfRangeException("repeatCount", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + repeatCount;
if (requiredLength < 0)
throw new OutOfMemoryException();
if (!NeedsAllocation(currentString,requiredLength)) {
currentString.AppendInPlace(value, repeatCount,currentLength);
ReplaceString(tid,currentString);
return this;
}
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, repeatCount,currentLength);
ReplaceString(tid,newString);
return this;
}
// Appends an array of characters at the end of this string builder. The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append1"]/*' />
public StringBuilder Append(char[] value, int startIndex, int charCount) {
int requiredLength;
if (value==null) {
if (startIndex==0 && charCount==0) {
return this;
}
throw new ArgumentNullException("value");
}
if (charCount==0) {
return this;
}
if (startIndex<0) {
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (charCount<0) {
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (charCount>value.Length-startIndex) {
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
requiredLength = currentLength + charCount;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, startIndex, charCount,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value, startIndex, charCount,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
// Appends a copy of this string at the end of this string builder.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append2"]/*' />
public StringBuilder Append(String value) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
return this;
}
int tid;
// hand inlining of GetThreadSafeString
String currentString = m_StringValue;
tid = InternalGetCurrentThread();
if (m_currentThread != tid)
currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);
int currentLength = currentString.Length;
int requiredLength = currentLength + value.Length;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
internal unsafe StringBuilder Append(char *value, int count) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
return this;
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + count;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, count,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value,count,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
private bool NeedsAllocation(String currentString,int requiredLength) {
//<= accounts for the terminating 0 which we require on strings.
return (currentString.ArrayLength<=requiredLength);
}
private String GetNewString(String currentString, int requiredLength) {
int newCapacity;
requiredLength++; //Include the terminating null.
if (requiredLength > m_MaxCapacity) {
throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"),
"requiredLength");
}
newCapacity = ( currentString.Capacity)*2; // To force a predicatable growth of 160,320 etc. for testing purposes
if (newCapacity<requiredLength) {
newCapacity = requiredLength;
}
if (newCapacity> m_MaxCapacity) {
newCapacity = m_MaxCapacity;
}
if (newCapacity<=0) {
throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
}
return String.GetStringForStringBuilder( currentString, newCapacity);
}
private void ReplaceString(int tid, String value) {
BCLDebug.Assert(value!=null, "[StringBuilder.ReplaceString]value!=null");
m_currentThread = tid; // new owner
m_StringValue = value;
}
// Appends a copy of the characters in value from startIndex to startIndex +
// count at the end of this string builder.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append3"]/*' />
public StringBuilder Append (String value, int startIndex, int count) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
if (startIndex==0 && count==0) {
return this;
}
throw new ArgumentNullException("value");
}
if (count<=0) {
if (count==0) {
return this;
}
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (startIndex<0 || (startIndex>value.Length - count)) {
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + count;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, startIndex, count, currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value, startIndex, count, currentLength);
ReplaceString(tid,currentString);
}
return this;
}
// Inserts multiple copies of a string into this string builder at the specified position.
// Existing characters are shifted to make room for the new text.
// The capacity is adjusted as needed. If value equals String.Empty, this
// string builder is not changed.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Insert"]/*' />
public unsafe StringBuilder Insert(int index, String value, int count) {
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
//If value isn't null, get all of our required values.
if (value == null) {
if (index == 0 && count == 0) {
return this;
}
throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_String"));
}
//Range check the index.
if (index < 0 || index > currentLength) {
throw new ArgumentOutOfRangeException("index",Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
if (count < 1) {
throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
//Calculate the new length, ensure that we have the space and set the space variable for this buffer
int requiredLength;
try {
requiredLength = checked(currentLength + (value.Length * count));
}
catch (Exception) {
throw new OutOfMemoryException();
}
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.InsertInPlace(index, value, count, currentLength, requiredLength);
ReplaceString(tid,newString);
}
else {
currentString.InsertInPlace(index, value, count, currentLength, requiredLength);
ReplaceString(tid,currentString);
}
return this;
}
// Property.
// Removes the specified characters from this string builder.
// The length of this string builder is reduced by
// length, but the capacity is unaffected.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Remove"]/*' />
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern StringBuilder Remove(int startIndex, int length);
//
//
// PUBLIC INSTANCE FUNCTIONS
//
//
/*====================================Append====================================
**
==============================================================================*/
// Appends a boolean to the end of this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append4"]/*' />
public StringBuilder Append(bool value) {
return Append(value.ToString());
}
// Appends an sbyte to this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append5"]/*' />
[CLSCompliant(false)]
public StringBuilder Append(sbyte value) {
return Append(value.ToString());
}
// Appends a ubyte to this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append6"]/*' />
public StringBuilder A
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
/*============================================================
**
** Class: StringBuilder
**
**
**
** Purpose: A prototype implementation of the StringBuilder
** class.
**
** Date: December 8, 1997
** Last Updated: March 31, 1998
**
===========================================================*/
namespace System.Text {
using System.Text;
using System.Runtime.Serialization;
using System;
using System.Runtime.CompilerServices;
// This class represents a mutable string. It is convenient for situations in
// which it is desirable to modify a string, perhaps by removing, replacing, or
// inserting characters, without creating a new String subsequent to
// each modification.
//
// The methods contained within this class do not return a new StringBuilder
// object unless specified otherwise. This class may be used in conjunction with the String
// class to carry out modifications upon strings.
//
// When passing null into a constructor in VJ and VC, the null
// should be explicitly type cast.
// For Example:
// StringBuilder sb1 = new StringBuilder((StringBuilder)null);
// StringBuilder sb2 = new StringBuilder((String)null);
// Console.WriteLine(sb1);
// Console.WriteLine(sb2);
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder"]/*' />
[Serializable()] public sealed class StringBuilder {
//
//
// CLASS VARIABLES
//
//
internal int m_currentThread = InternalGetCurrentThread();
internal int m_MaxCapacity = 0;
internal String m_StringValue = null;
//
//
// STATIC CONSTANTS
//
//
internal const int DefaultCapacity = 16;
//
//
//CONSTRUCTORS
//
//
// Creates a new empty string builder (i.e., it represents String.Empty)
// with the default capacity (16 characters).
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder"]/*' />
public StringBuilder()
: this(DefaultCapacity) {
}
// Create a new empty string builder (i.e., it represents String.Empty)
// with the specified capacity.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder1"]/*' />
public StringBuilder(int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (capacity == 0) { // MakeFromString enforces this
capacity = DefaultCapacity;
}
m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
m_MaxCapacity = Int32.MaxValue;
}
// Creates a new string builder from the specified string. If value
// is a null String (i.e., if it represents String.NullString)
// then the new string builder will also be null (i.e., it will also represent
// String.NullString).
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder2"]/*' />
public StringBuilder(String value){
MakeFromString(value, 0, -1, -1);
}
// Creates a new string builder from the specified string with the specified
// capacity. If value is a null String (i.e., if it represents
// String.NullString) then the new string builder will also be null
// (i.e., it will also represent String.NullString).
// The maximum number of characters this string may contain is set by capacity.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder3"]/*' />
public StringBuilder(String value, int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
MakeFromString(value, 0, -1, capacity);
}
// Creates a new string builder from the specifed substring with the specified
// capacity. The maximum number of characters is set by capacity.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder4"]/*' />
public StringBuilder(String value, int startIndex, int length, int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (length<0) {
throw new ArgumentOutOfRangeException("length",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum"), "length"));
}
MakeFromString(value, startIndex, length, capacity);
}
// Creates an empty StringBuilder with a minimum capacity of capacity
// and a maximum capacity of maxCapacity.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder5"]/*' />
public StringBuilder(int capacity, int maxCapacity) {
if (capacity>maxCapacity) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
}
if (maxCapacity<1) {
throw new ArgumentOutOfRangeException("maxCapacity", Environment.GetResourceString("ArgumentOutOfRange_SmallMaxCapacity"));
}
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity",
String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
}
if (capacity == 0) {
capacity = DefaultCapacity;
}
m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
m_MaxCapacity = maxCapacity;
}
private String GetThreadSafeString(out int tid) {
String temp = m_StringValue;
tid = InternalGetCurrentThread();
if (m_currentThread == tid)
return temp;
return String.GetStringForStringBuilder(temp, temp.Capacity);
}
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern static int InternalGetCurrentThread();
//
// Private native functions
//
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern void MakeFromString(String value, int startIndex, int length, int capacity);
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Capacity"]/*' />
public int Capacity {
get {return m_StringValue.Capacity;} //-1 to account for terminating null.
set {InternalSetCapacity(value);}
}
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.MaxCapacity"]/*' />
public int MaxCapacity {
get { return m_MaxCapacity; }
}
// Read-Only Property
// Ensures that the capacity of this string builder is at least the specified value.
// If capacity is greater than the capacity of this string builder, then the capacity
// is set to capacity; otherwise the capacity is unchanged.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.EnsureCapacity"]/*' />
public int EnsureCapacity(int capacity) {
if (capacity<0) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedPosCapacity"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
//If we need more space or the COW bit is set, copy the buffer.
if (!NeedsAllocation(currentString,capacity)) {
return currentString.Capacity;
}
String newString = GetNewString(currentString,capacity);
ReplaceString(tid,newString);
return newString.Capacity;
}
//Sets the capacity to be capacity. If capacity is less than the current
//instance an ArgumentException is thrown. If capacity is greater than the current
//instance, memory is allocated to allow the StringBuilder to grow.
//
[MethodImplAttribute(MethodImplOptions.InternalCall)]
internal extern int InternalSetCapacity(int capacity);
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString"]/*' />
public override String ToString() {
String currentString = m_StringValue;
int currentThread = m_currentThread;
if (currentThread != 0 && currentThread != InternalGetCurrentThread()) {
return String.InternalCopy(currentString);
}
if ((2 * currentString.Length) < currentString.ArrayLength) {
return String.InternalCopy(currentString);
}
currentString.ClearPostNullChar();
m_currentThread = 0;
return currentString;
}
// Converts a substring of this string builder to a String.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString1"]/*' />
public String ToString(int startIndex, int length) {
return m_StringValue.Substring(startIndex, length);
}
// Sets the length of the String in this buffer. If length is less than the current
// instance, the StringBuilder is truncated. If length is greater than the current
// instance, nulls are appended. The capacity is adjusted to be the same as the length.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Length"]/*' />
public int Length {
get {
return m_StringValue.Length;
}
set {
int tid;
String currentString = GetThreadSafeString(out tid);
if (value==0) { //the user is trying to clear the string
currentString.SetLength(0);
ReplaceString(tid,currentString);
return;
}
int currentLength = currentString.Length;
int newlength = value;
//If our length is less than 0 or greater than our Maximum capacity, bail.
if (newlength<0) {
throw new ArgumentOutOfRangeException("newlength", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
}
if (newlength>MaxCapacity) {
throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
}
//Jump out early if our requested length our currentlength.
//This will be a pretty rare branch.
if (newlength == currentLength) {
return;
}
//If the StringBuilder has never been converted to a string, simply set the length
//without allocating a new string.
if (newlength <= currentString.Capacity) {
if (newlength > currentLength) {
for (int i = currentLength ; i < newlength; i++) // This is a rare case anyway.
currentString.InternalSetCharNoBoundsCheck(i,'/0');
}
currentString.InternalSetCharNoBoundsCheck(newlength,'/0'); //Null terminate.
currentString.SetLength(newlength);
ReplaceString(tid,currentString);
return;
}
// CopyOnWrite set we need to allocate a String
int newCapacity = (newlength>currentString.Capacity)?newlength:currentString.Capacity;
String newString = String.GetStringForStringBuilder(currentString, newCapacity);
//We know exactly how many characters we need, so embed that knowledge in the String.
newString.SetLength(newlength);
ReplaceString(tid,newString);
}
}
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.this"]/*' />
[System.Runtime.CompilerServices.IndexerName("Chars")]
public char this[int index] {
get {
return m_StringValue[index];
}
set {
int tid;
String currentString = GetThreadSafeString(out tid);
currentString.SetChar(index, value);
ReplaceString(tid,currentString);
}
}
// Appends a character at the end of this string builder. The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append"]/*' />
public StringBuilder Append(char value, int repeatCount) {
if (repeatCount==0) {
return this;
}
if (repeatCount<0) {
throw new ArgumentOutOfRangeException("repeatCount", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + repeatCount;
if (requiredLength < 0)
throw new OutOfMemoryException();
if (!NeedsAllocation(currentString,requiredLength)) {
currentString.AppendInPlace(value, repeatCount,currentLength);
ReplaceString(tid,currentString);
return this;
}
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, repeatCount,currentLength);
ReplaceString(tid,newString);
return this;
}
// Appends an array of characters at the end of this string builder. The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append1"]/*' />
public StringBuilder Append(char[] value, int startIndex, int charCount) {
int requiredLength;
if (value==null) {
if (startIndex==0 && charCount==0) {
return this;
}
throw new ArgumentNullException("value");
}
if (charCount==0) {
return this;
}
if (startIndex<0) {
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (charCount<0) {
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (charCount>value.Length-startIndex) {
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
requiredLength = currentLength + charCount;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, startIndex, charCount,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value, startIndex, charCount,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
// Appends a copy of this string at the end of this string builder.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append2"]/*' />
public StringBuilder Append(String value) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
return this;
}
int tid;
// hand inlining of GetThreadSafeString
String currentString = m_StringValue;
tid = InternalGetCurrentThread();
if (m_currentThread != tid)
currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);
int currentLength = currentString.Length;
int requiredLength = currentLength + value.Length;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
internal unsafe StringBuilder Append(char *value, int count) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
return this;
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + count;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, count,currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value,count,currentLength);
ReplaceString(tid,currentString);
}
return this;
}
private bool NeedsAllocation(String currentString,int requiredLength) {
//<= accounts for the terminating 0 which we require on strings.
return (currentString.ArrayLength<=requiredLength);
}
private String GetNewString(String currentString, int requiredLength) {
int newCapacity;
requiredLength++; //Include the terminating null.
if (requiredLength > m_MaxCapacity) {
throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"),
"requiredLength");
}
newCapacity = ( currentString.Capacity)*2; // To force a predicatable growth of 160,320 etc. for testing purposes
if (newCapacity<requiredLength) {
newCapacity = requiredLength;
}
if (newCapacity> m_MaxCapacity) {
newCapacity = m_MaxCapacity;
}
if (newCapacity<=0) {
throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
}
return String.GetStringForStringBuilder( currentString, newCapacity);
}
private void ReplaceString(int tid, String value) {
BCLDebug.Assert(value!=null, "[StringBuilder.ReplaceString]value!=null");
m_currentThread = tid; // new owner
m_StringValue = value;
}
// Appends a copy of the characters in value from startIndex to startIndex +
// count at the end of this string builder.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append3"]/*' />
public StringBuilder Append (String value, int startIndex, int count) {
//If the value being added is null, eat the null
//and return.
if (value==null) {
if (startIndex==0 && count==0) {
return this;
}
throw new ArgumentNullException("value");
}
if (count<=0) {
if (count==0) {
return this;
}
throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
if (startIndex<0 || (startIndex>value.Length - count)) {
throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
int requiredLength = currentLength + count;
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.AppendInPlace(value, startIndex, count, currentLength);
ReplaceString(tid,newString);
} else {
currentString.AppendInPlace(value, startIndex, count, currentLength);
ReplaceString(tid,currentString);
}
return this;
}
// Inserts multiple copies of a string into this string builder at the specified position.
// Existing characters are shifted to make room for the new text.
// The capacity is adjusted as needed. If value equals String.Empty, this
// string builder is not changed.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Insert"]/*' />
public unsafe StringBuilder Insert(int index, String value, int count) {
int tid;
String currentString = GetThreadSafeString(out tid);
int currentLength = currentString.Length;
//If value isn't null, get all of our required values.
if (value == null) {
if (index == 0 && count == 0) {
return this;
}
throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_String"));
}
//Range check the index.
if (index < 0 || index > currentLength) {
throw new ArgumentOutOfRangeException("index",Environment.GetResourceString("ArgumentOutOfRange_Index"));
}
if (count < 1) {
throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
}
//Calculate the new length, ensure that we have the space and set the space variable for this buffer
int requiredLength;
try {
requiredLength = checked(currentLength + (value.Length * count));
}
catch (Exception) {
throw new OutOfMemoryException();
}
if (NeedsAllocation(currentString,requiredLength)) {
String newString = GetNewString(currentString,requiredLength);
newString.InsertInPlace(index, value, count, currentLength, requiredLength);
ReplaceString(tid,newString);
}
else {
currentString.InsertInPlace(index, value, count, currentLength, requiredLength);
ReplaceString(tid,currentString);
}
return this;
}
// Property.
// Removes the specified characters from this string builder.
// The length of this string builder is reduced by
// length, but the capacity is unaffected.
//
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Remove"]/*' />
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public extern StringBuilder Remove(int startIndex, int length);
//
//
// PUBLIC INSTANCE FUNCTIONS
//
//
/*====================================Append====================================
**
==============================================================================*/
// Appends a boolean to the end of this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append4"]/*' />
public StringBuilder Append(bool value) {
return Append(value.ToString());
}
// Appends an sbyte to this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append5"]/*' />
[CLSCompliant(false)]
public StringBuilder Append(sbyte value) {
return Append(value.ToString());
}
// Appends a ubyte to this string builder.
// The capacity is adjusted as needed.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append6"]/*' />
public StringBuilder A