The Labs \ Source Viewer \ SSCLI \ System.Text \ StringBuilder

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  StringBuilder
  18. **
  19. **
  20. ** Purpose: implementation of the StringBuilder
  21. ** class.
  22. **
  23. ===========================================================*/
  24. namespace System.Text
  25. {
  26.     using System.Text;
  27.     using System.Runtime.Serialization;
  28.     using System;
  29.     using System.Runtime.CompilerServices;
  30.     using System.Threading;
  31.     using System.Globalization;
  32.    
  33.     // This class represents a mutable string. It is convenient for situations in
  34.     // which it is desirable to modify a string, perhaps by removing, replacing, or
  35.     // inserting characters, without creating a new String subsequent to
  36.     // each modification.
  37.     //
  38.     // The methods contained within this class do not return a new StringBuilder
  39.     // object unless specified otherwise. This class may be used in conjunction with the String
  40.     // class to carry out modifications upon strings.
  41.     //
  42.     // When passing null into a constructor in VJ and VC, the null
  43.     // should be explicitly type cast.
  44.     // For Example:
  45.     // StringBuilder sb1 = new StringBuilder((StringBuilder)null);
  46.     // StringBuilder sb2 = new StringBuilder((String)null);
  47.     // Console.WriteLine(sb1);
  48.     // Console.WriteLine(sb2);
  49.     //
  50.     [System.Runtime.InteropServices.ComVisible(true)]
  51.     [Serializable()]
  52.     public sealed class StringBuilder : ISerializable
  53.     {
  54.        
  55.        
  56.         //
  57.         //
  58.         // CLASS VARIABLES
  59.         //
  60.         //
  61.         internal IntPtr m_currentThread = Thread.InternalGetCurrentThread();
  62.         internal int m_MaxCapacity = 0;
  63.         //
  64.         // making m_StringValue volatile to guarantee reading order in some places.
  65.         //
  66.         internal volatile string m_StringValue = null;
  67.        
  68.        
  69.         //
  70.         //
  71.         // STATIC CONSTANTS
  72.         //
  73.         //
  74.         internal const int DefaultCapacity = 16;
  75.         private const string CapacityField = "Capacity";
  76.         private const string MaxCapacityField = "m_MaxCapacity";
  77.         private const string StringValueField = "m_StringValue";
  78.         private const string ThreadIDField = "m_currentThread";
  79.        
  80.         //
  81.         //
  82.         //CONSTRUCTORS
  83.         //
  84.         //
  85.        
  86.         // Creates a new empty string builder (i.e., it represents String.Empty)
  87.         // with the default capacity (16 characters).
  88.         public StringBuilder() : this(DefaultCapacity)
  89.         {
  90.         }
  91.        
  92.         // Create a new empty string builder (i.e., it represents String.Empty)
  93.         // with the specified capacity.
  94.         public StringBuilder(int capacity) : this(String.Empty, capacity)
  95.         {
  96.         }
  97.        
  98.        
  99.         // Creates a new string builder from the specified string. If value
  100.         // is a null String (i.e., if it represents String.NullString)
  101.         // then the new string builder will also be null (i.e., it will also represent
  102.         // String.NullString).
  103.         //
  104.         public StringBuilder(string value) : this(value, DefaultCapacity)
  105.         {
  106.         }
  107.        
  108.         // Creates a new string builder from the specified string with the specified
  109.         // capacity. If value is a null String (i.e., if it represents
  110.         // String.NullString) then the new string builder will also be null
  111.         // (i.e., it will also represent String.NullString).
  112.         // The maximum number of characters this string may contain is set by capacity.
  113.         //
  114.         public StringBuilder(string value, int capacity) : this(value, 0, ((value != null) ? value.Length : 0), capacity)
  115.         {
  116.         }
  117.        
  118.         // Creates a new string builder from the specifed substring with the specified
  119.         // capacity. The maximum number of characters is set by capacity.
  120.         //
  121.        
  122.         public StringBuilder(string value, int startIndex, int length, int capacity)
  123.         {
  124.             if (capacity < 0) {
  125.                 throw new ArgumentOutOfRangeException("capacity", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
  126.             }
  127.             if (length < 0) {
  128.                 throw new ArgumentOutOfRangeException("length", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum"), "length"));
  129.             }
  130.            
  131.             if (startIndex < 0) {
  132.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  133.             }
  134.            
  135.             if (value == null) {
  136.                 value = String.Empty;
  137.             }
  138.            
  139.             if (startIndex > value.Length - length) {
  140.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_IndexLength"));
  141.             }
  142.            
  143.             m_MaxCapacity = Int32.MaxValue;
  144.            
  145.             if (capacity == 0) {
  146.                 capacity = DefaultCapacity;
  147.             }
  148.            
  149.             while (capacity < length) {
  150.                 capacity *= 2;
  151.                 // If we overflow, we should just use length as capacity.
  152.                 // There is no reason we should throw an exception in this case if the system is able
  153.                 // to allocate the string.
  154.                 if (capacity < 0) {
  155.                     capacity = length;
  156.                     break;
  157.                 }
  158.             }
  159.            
  160.             m_StringValue = String.GetStringForStringBuilder(value, startIndex, length, capacity);
  161.         }
  162.        
  163.         // Creates an empty StringBuilder with a minimum capacity of capacity
  164.         // and a maximum capacity of maxCapacity.
  165.         public StringBuilder(int capacity, int maxCapacity)
  166.         {
  167.             if (capacity > maxCapacity) {
  168.                 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
  169.             }
  170.             if (maxCapacity < 1) {
  171.                 throw new ArgumentOutOfRangeException("maxCapacity", Environment.GetResourceString("ArgumentOutOfRange_SmallMaxCapacity"));
  172.             }
  173.            
  174.             if (capacity < 0) {
  175.                 throw new ArgumentOutOfRangeException("capacity", String.Format(CultureInfo.CurrentCulture, Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
  176.             }
  177.             if (capacity == 0) {
  178.                 capacity = Math.Min(DefaultCapacity, maxCapacity);
  179.             }
  180.            
  181.             m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
  182.             m_MaxCapacity = maxCapacity;
  183.            
  184.         }
  185.        
  186.         private StringBuilder(SerializationInfo info, StreamingContext context)
  187.         {
  188.             if (info == null)
  189.                 throw new ArgumentNullException("info");
  190.            
  191.             int persistedCapacity = 0;
  192.             string persistedString = null;
  193.             int persistedMaxCapacity = Int32.MaxValue;
  194.             bool capacityPresent = false;
  195.            
  196.             // Get the data
  197.             SerializationInfoEnumerator enumerator = info.GetEnumerator();
  198.             while (enumerator.MoveNext()) {
  199.                 switch (enumerator.Name) {
  200.                     case MaxCapacityField:
  201.                         persistedMaxCapacity = info.GetInt32(MaxCapacityField);
  202.                         break;
  203.                     case StringValueField:
  204.                         persistedString = info.GetString(StringValueField);
  205.                         break;
  206.                     case CapacityField:
  207.                         persistedCapacity = info.GetInt32(CapacityField);
  208.                         capacityPresent = true;
  209.                         break;
  210.                     default:
  211.                         // Note: deliberately ignore "m_currentThread" which earlier
  212.                         // verisions incorrectly persisted.
  213.                         // Ignore other fields for forward compatability.
  214.                         break;
  215.                 }
  216.                
  217.             }
  218.            
  219.             // Check values and set defaults
  220.             if (persistedString == null) {
  221.                 persistedString = String.Empty;
  222.             }
  223.             if (persistedMaxCapacity < 1 || persistedString.Length > persistedMaxCapacity) {
  224.                 throw new SerializationException(Environment.GetResourceString("Serialization_StringBuilderMaxCapacity"));
  225.             }
  226.            
  227.             if (!capacityPresent) {
  228.                 // StringBuilder in V1.X did not persist the Capacity, so this is a valid legacy code path.
  229.                 persistedCapacity = DefaultCapacity;
  230.                 if (persistedCapacity < persistedString.Length) {
  231.                     persistedCapacity = persistedString.Length;
  232.                 }
  233.                 if (persistedCapacity > persistedMaxCapacity) {
  234.                     persistedCapacity = persistedMaxCapacity;
  235.                 }
  236.             }
  237.             if (persistedCapacity < 0 || persistedCapacity < persistedString.Length || persistedCapacity > persistedMaxCapacity) {
  238.                 throw new SerializationException(Environment.GetResourceString("Serialization_StringBuilderCapacity"));
  239.             }
  240.            
  241.             // Assign
  242.             m_MaxCapacity = persistedMaxCapacity;
  243.             m_StringValue = String.GetStringForStringBuilder(persistedString, 0, persistedString.Length, persistedCapacity);
  244.             VerifyClassInvariant();
  245.         }
  246.        
  247.         void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  248.         {
  249.             if (info == null) {
  250.                 throw new ArgumentNullException("info");
  251.             }
  252.            
  253.             VerifyClassInvariant();
  254.            
  255.             info.AddValue(MaxCapacityField, m_MaxCapacity);
  256.             info.AddValue(CapacityField, Capacity);
  257.             info.AddValue(StringValueField, m_StringValue);
  258.             // Note: persist "m_currentThread" to be compatible with old versions
  259.             info.AddValue(ThreadIDField, 0);
  260.         }
  261.        
  262.         [System.Diagnostics.Conditional("_DEBUG")]
  263.         private void VerifyClassInvariant()
  264.         {
  265.             BCLDebug.Assert(m_MaxCapacity >= 1, "Invalid StringBuilder");
  266.             BCLDebug.Assert(Capacity >= 0 && Capacity <= m_MaxCapacity, "Invalid StringBuilder");
  267.             BCLDebug.Assert(m_StringValue != null && Capacity >= m_StringValue.Length, "Invalid StringBuilder");
  268.         }
  269.        
  270.         private string GetThreadSafeString(out IntPtr tid)
  271.         {
  272.             // Following two reads (m_StringValue, m_currentThread) needs to happen in order.
  273.             // This is guaranteed by making the fields volatile.
  274.             // See ReplaceString method for details.
  275.            
  276.             string temp = m_StringValue;
  277.             tid = Thread.InternalGetCurrentThread();
  278.             if (m_currentThread == tid)
  279.                 return temp;
  280.             return String.GetStringForStringBuilder(temp, temp.Capacity);
  281.         }
  282.        
  283.         public int Capacity {
  284.             get { return m_StringValue.Capacity; }
  285.             //-1 to account for terminating null.
  286.             set {
  287.                 IntPtr tid;
  288.                 string currentString = GetThreadSafeString(out tid);
  289.                
  290.                 if (value < 0) {
  291.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
  292.                 }
  293.                
  294.                 if (value < currentString.Length) {
  295.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
  296.                 }
  297.                
  298.                 if (value > MaxCapacity) {
  299.                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
  300.                 }
  301.                
  302.                 int currCapacity = currentString.Capacity;
  303.                 //If we already have the correct capacity, bail out early.
  304.                 if (value != currCapacity) {
  305.                     //Allocate a new String with the capacity and copy all of our old characters
  306.                     //into it. We've already guaranteed that our String will fit within that capacity.
  307.                     //We don't need to worry about the COW bit because we're always allocating a new String.
  308.                     string newString = String.GetStringForStringBuilder(currentString, value);
  309.                     ReplaceString(tid, newString);
  310.                 }
  311.             }
  312.         }
  313.        
  314.         public int MaxCapacity {
  315.             get { return m_MaxCapacity; }
  316.         }
  317.        
  318.        
  319.         // Read-Only Property
  320.         // Ensures that the capacity of this string builder is at least the specified value.
  321.         // If capacity is greater than the capacity of this string builder, then the capacity
  322.         // is set to capacity; otherwise the capacity is unchanged.
  323.         //
  324.         public int EnsureCapacity(int capacity)
  325.         {
  326.             if (capacity < 0) {
  327.                 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedPosCapacity"));
  328.             }
  329.            
  330.             IntPtr tid;
  331.             string currentString = GetThreadSafeString(out tid);
  332.            
  333.             //If we need more space or the COW bit is set, copy the buffer.
  334.             if (!NeedsAllocation(currentString, capacity)) {
  335.                 return currentString.Capacity;
  336.             }
  337.            
  338.             string newString = GetNewString(currentString, capacity);
  339.             ReplaceString(tid, newString);
  340.             return newString.Capacity;
  341.         }
  342.        
  343.         public override string ToString()
  344.         {
  345.             //
  346.             // We assume that their read of m_currentThread will always occur after read of m_StringValue.
  347.             // If these reads get re-ordered then it is possible to get a currentString owned by some other
  348.             // (mutating) thread and yet think, according to currentThread, that such was not the case.
  349.             // This is acheived by marking m_StringValue as volatile.
  350.             //
  351.             string currentString = m_StringValue;
  352.             IntPtr currentThread = m_currentThread;
  353.            
  354.             //
  355.             // Note calling ToString the second time or from a different thread will cause allocation of a new string.
  356.             // If we do not make a copy if currentThread is IntPtr.Zero, we will have following race:
  357.             //
  358.             // (1) Thread T1 completes a mutation of the string and will become the owner.
  359.             // T1 then starts another mutation operation and
  360.             // A thread interleaving happens at this point.
  361.             // (2) Thread T2 starts a ToString operation. T2 reads m_StringValue into its local currentString variable.
  362.             // A thread interleaving happens at this point.
  363.             // (3) Thread T3 finshes a mutation of the string in the StringBuilder , performing the ReplaceString call.
  364.             // Thread T3 then starts a ToString operation. Assuming the string is not wasting excessive space,
  365.             // T3 will proceeds to call ClearPostNullChar, registers NOBODY as the owner, and returns the string.
  366.             // A thread interleaving happens at this point.
  367.             // (4) Thread T2 resumes execution. T2 reads m_currentThread and sees that NOBODY is the registered owner
  368.             // Assuming its currentString is not wasting excessive space, T2 will return the same string that thread T1 is
  369.             // in the middle of mutating.
  370.             //
  371.             if (currentThread != Thread.InternalGetCurrentThread()) {
  372.                 return String.InternalCopy(currentString);
  373.             }
  374.            
  375.             if ((2 * currentString.Length) < currentString.ArrayLength) {
  376.                 return String.InternalCopy(currentString);
  377.             }
  378.            
  379.             currentString.ClearPostNullChar();
  380.             m_currentThread = IntPtr.Zero;
  381.             return currentString;
  382.         }
  383.        
  384.         // Converts a substring of this string builder to a String.
  385.         public string ToString(int startIndex, int length)
  386.         {
  387.             // We here enforce the policy copying underlying String data in all cases, since StringBuilder
  388.             // uses the the internal m_StringValue reference mutably.
  389.             return m_StringValue.InternalSubStringWithChecks(startIndex, length, true);
  390.         }
  391.        
  392.         // Sets the length of the String in this buffer. If length is less than the current
  393.         // instance, the StringBuilder is truncated. If length is greater than the current
  394.         // instance, nulls are appended. The capacity is adjusted to be the same as the length.
  395.        
  396.         public int Length {
  397.             get { return m_StringValue.Length; }
  398.             set {
  399.                 IntPtr tid;
  400.                 string currentString = GetThreadSafeString(out tid);
  401.                
  402.                 if (value == 0) {
  403.                     //the user is trying to clear the string
  404.                     currentString.SetLength(0);
  405.                     ReplaceString(tid, currentString);
  406.                     return;
  407.                 }
  408.                
  409.                 int currentLength = currentString.Length;
  410.                 int newlength = value;
  411.                 //If our length is less than 0 or greater than our Maximum capacity, bail.
  412.                 if (newlength < 0) {
  413.                     throw new ArgumentOutOfRangeException("newlength", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  414.                 }
  415.                
  416.                 if (newlength > MaxCapacity) {
  417.                     throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
  418.                 }
  419.                
  420.                 //Jump out early if our requested length our currentlength.
  421.                 //This will be a pretty rare branch.
  422.                 if (newlength == currentLength) {
  423.                     return;
  424.                 }
  425.                
  426.                
  427.                 //If the StringBuilder has never been converted to a string, simply set the length
  428.                 //without allocating a new string.
  429.                 if (newlength <= currentString.Capacity) {
  430.                     if (newlength > currentLength) {
  431.                         for (int i = currentLength; i < newlength; i++)
  432.                             // This is a rare case anyway.
  433.                             currentString.InternalSetCharNoBoundsCheck(i, '\0');
  434.                     }
  435.                    
  436.                     currentString.InternalSetCharNoBoundsCheck(newlength, '\0');
  437.                     //Null terminate.
  438.                     currentString.SetLength(newlength);
  439.                     ReplaceString(tid, currentString);
  440.                    
  441.                     return;
  442.                 }
  443.                
  444.                 // CopyOnWrite set we need to allocate a String
  445.                 int newCapacity = (newlength > currentString.Capacity) ? newlength : currentString.Capacity;
  446.                 string newString = String.GetStringForStringBuilder(currentString, newCapacity);
  447.                
  448.                 //We know exactly how many characters we need, so embed that knowledge in the String.
  449.                 newString.SetLength(newlength);
  450.                 ReplaceString(tid, newString);
  451.             }
  452.         }
  453.        
  454.         [System.Runtime.CompilerServices.IndexerName("Chars")]
  455.         public char this[int index]
  456.         {
  457.             get { return m_StringValue[index]; }
  458.             set {
  459.                 IntPtr tid;
  460.                 string currentString = GetThreadSafeString(out tid);
  461.                 currentString.SetChar(index, value);
  462.                 ReplaceString(tid, currentString);
  463.             }
  464.         }
  465.        
  466.         // Appends a character at the end of this string builder. The capacity is adjusted as needed.
  467.         public StringBuilder Append(char value, int repeatCount)
  468.         {
  469.             if (repeatCount == 0) {
  470.                 return this;
  471.             }
  472.             if (repeatCount < 0) {
  473.                 throw new ArgumentOutOfRangeException("repeatCount", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
  474.             }
  475.            
  476.            
  477.             IntPtr tid;
  478.             string currentString = GetThreadSafeString(out tid);
  479.            
  480.             int currentLength = currentString.Length;
  481.             int requiredLength = currentLength + repeatCount;
  482.            
  483.             if (requiredLength < 0)
  484.                 throw new OutOfMemoryException();
  485.            
  486.             if (!NeedsAllocation(currentString, requiredLength)) {
  487.                 currentString.AppendInPlace(value, repeatCount, currentLength);
  488.                 ReplaceString(tid, currentString);
  489.                 return this;
  490.             }
  491.            
  492.             string newString = GetNewString(currentString, requiredLength);
  493.             newString.AppendInPlace(value, repeatCount, currentLength);
  494.             ReplaceString(tid, newString);
  495.             return this;
  496.         }
  497.        
  498.         // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed.
  499.         public StringBuilder Append(char[] value, int startIndex, int charCount)
  500.         {
  501.             int requiredLength;
  502.            
  503.             if (value == null) {
  504.                 if (startIndex == 0 && charCount == 0) {
  505.                     return this;
  506.                 }
  507.                 throw new ArgumentNullException("value");
  508.             }
  509.            
  510.             if (charCount == 0) {
  511.                 return this;
  512.             }
  513.            
  514.             if (startIndex < 0) {
  515.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
  516.             }
  517.             if (charCount < 0) {
  518.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
  519.             }
  520.             if (charCount > value.Length - startIndex) {
  521.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  522.             }
  523.            
  524.             IntPtr tid;
  525.             string currentString = GetThreadSafeString(out tid);
  526.            
  527.             int currentLength = currentString.Length;
  528.             requiredLength = currentLength + charCount;
  529.             if (NeedsAllocation(currentString, requiredLength)) {
  530.                 string newString = GetNewString(currentString, requiredLength);
  531.                 newString.AppendInPlace(value, startIndex, charCount, currentLength);
  532.                 ReplaceString(tid, newString);
  533.             }
  534.             else {
  535.                 currentString.AppendInPlace(value, startIndex, charCount, currentLength);
  536.                 ReplaceString(tid, currentString);
  537.             }
  538.            
  539.             return this;
  540.         }
  541.        
  542.         // Appends a copy of this string at the end of this string builder.
  543.         public StringBuilder Append(string value)
  544.         {
  545.             //If the value being added is null, eat the null
  546.             //and return.
  547.             if (value == null) {
  548.                 return this;
  549.             }
  550.            
  551.             IntPtr tid;
  552.             // hand inlining of GetThreadSafeString
  553.             string currentString = m_StringValue;
  554.             tid = Thread.InternalGetCurrentThread();
  555.             if (m_currentThread != tid)
  556.                 currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);
  557.            
  558.             int currentLength = currentString.Length;
  559.            
  560.             int requiredLength = currentLength + value.Length;
  561.            
  562.             if (NeedsAllocation(currentString, requiredLength)) {
  563.                 string newString = GetNewString(currentString, requiredLength);
  564.                 newString.AppendInPlace(value, currentLength);
  565.                 ReplaceString(tid, newString);
  566.             }
  567.             else {
  568.                 currentString.AppendInPlace(value, currentLength);
  569.                 ReplaceString(tid, currentString);
  570.             }
  571.            
  572.             return this;
  573.         }
  574.        
  575.         unsafe internal StringBuilder Append(char* value, int count)
  576.         {
  577.             //If the value being added is null, eat the null
  578.             //and return.
  579.             if (value == null) {
  580.                 return this;
  581.             }
  582.            
  583.            
  584.             IntPtr tid;
  585.             string currentString = GetThreadSafeString(out tid);
  586.             int currentLength = currentString.Length;
  587.            
  588.             int requiredLength = currentLength + count;
  589.            
  590.             if (NeedsAllocation(currentString, requiredLength)) {
  591.                 string newString = GetNewString(currentString, requiredLength);
  592.                 newString.AppendInPlace(value, count, currentLength);
  593.                 ReplaceString(tid, newString);
  594.             }
  595.             else {
  596.                 currentString.AppendInPlace(value, count, currentLength);
  597.                 ReplaceString(tid, currentString);
  598.             }
  599.            
  600.             return this;
  601.         }
  602.        
  603.        
  604.         private bool NeedsAllocation(string currentString, int requiredLength)
  605.         {
  606.             //<= accounts for the terminating 0 which we require on strings.
  607.             return (currentString.ArrayLength <= requiredLength);
  608.         }
  609.        
  610.         private string GetNewString(string currentString, int requiredLength)
  611.         {
  612.             int newCapacity;
  613.             int maxCapacity = m_MaxCapacity;
  614.            
  615.             if (requiredLength < 0) {
  616.                 throw new OutOfMemoryException();
  617.             }
  618.            
  619.             if (requiredLength > maxCapacity) {
  620.                 throw new ArgumentOutOfRangeException("requiredLength", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
  621.             }
  622.            
  623.             newCapacity = (currentString.Capacity)*2;
  624.             // To force a predicatable growth of 160,320 etc. for testing purposes
  625.             if (newCapacity < requiredLength) {
  626.                 newCapacity = requiredLength;
  627.             }
  628.            
  629.             if (newCapacity > maxCapacity) {
  630.                 newCapacity = maxCapacity;
  631.             }
  632.            
  633.             if (newCapacity <= 0) {
  634.                 throw new ArgumentOutOfRangeException("newCapacity", Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
  635.             }
  636.            
  637.             return String.GetStringForStringBuilder(currentString, newCapacity);
  638.         }
  639.        
  640.         private void ReplaceString(IntPtr tid, string value)
  641.         {
  642.             BCLDebug.Assert(value != null, "[StringBuilder.ReplaceString]value!=null");
  643.            
  644.             // Following two writes (m_currentThread, m_StringValue) needs to happen in order.
  645.             // This is guaranteed by making the fields volatile.
  646.             //
  647.             // If two threads are modifying the same StringBuilder at the same time,
  648.             // we don't want them to use the same StringValue. This is done by comparing
  649.             // owner thread id against current thread id in GetThreadSafeString.
  650.             // Here we need to guarantee the ordering of writes to make sure if the another thread
  651.             // see the change we have done to m_StringValue, it will see the change to m_currentThread as well.
  652.             //
  653.             m_currentThread = tid;
  654.             // new owner
  655.             m_StringValue = value;
  656.         }
  657.        
  658.         // Appends a copy of the characters in value from startIndex to startIndex +
  659.         // count at the end of this string builder.
  660.         public StringBuilder Append(string value, int startIndex, int count)
  661.         {
  662.             //If the value being added is null, eat the null
  663.             //and return.
  664.             if (value == null) {
  665.                 if (startIndex == 0 && count == 0) {
  666.                     return this;
  667.                 }
  668.                 throw new ArgumentNullException("value");
  669.             }
  670.            
  671.             if (count <= 0) {
  672.                 if (count == 0) {
  673.                     return this;
  674.                 }
  675.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
  676.             }
  677.            
  678.             if (startIndex < 0 || (startIndex > value.Length - count)) {
  679.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  680.             }
  681.            
  682.             IntPtr tid;
  683.             string currentString = GetThreadSafeString(out tid);
  684.             int currentLength = currentString.Length;
  685.            
  686.             int requiredLength = currentLength + count;
  687.            
  688.             if (NeedsAllocation(currentString, requiredLength)) {
  689.                 string newString = GetNewString(currentString, requiredLength);
  690.                 newString.AppendInPlace(value, startIndex, count, currentLength);
  691.                 ReplaceString(tid, newString);
  692.             }
  693.             else {
  694.                 currentString.AppendInPlace(value, startIndex, count, currentLength);
  695.                 ReplaceString(tid, currentString);
  696.             }
  697.            
  698.             return this;
  699.         }
  700.        
  701.         [System.Runtime.InteropServices.ComVisible(false)]
  702.         public StringBuilder AppendLine()
  703.         {
  704.             return Append(Environment.NewLine);
  705.         }
  706.        
  707.         [System.Runtime.InteropServices.ComVisible(false)]
  708.         public StringBuilder AppendLine(string value)
  709.         {
  710.             Append(value);
  711.             return Append(Environment.NewLine);
  712.         }
  713.        
  714.         [System.Runtime.InteropServices.ComVisible(false)]
  715.         public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count)
  716.         {
  717.             if (destination == null) {
  718.                 throw new ArgumentNullException("destination");
  719.             }
  720.            
  721.             if (count < 0) {
  722.                 throw new ArgumentOutOfRangeException(Environment.GetResourceString("Arg_NegativeArgCount"), "count");
  723.             }
  724.            
  725.             if (destinationIndex < 0) {
  726.                 throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum", "destinationIndex"), "destinationIndex");
  727.             }
  728.            
  729.             if (destinationIndex > destination.Length - count) {
  730.                 throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_OffsetOut"));
  731.             }
  732.            
  733.             IntPtr tid;
  734.             string currentString = GetThreadSafeString(out tid);
  735.             int currentLength = currentString.Length;
  736.            
  737.             if (sourceIndex < 0 || sourceIndex > currentLength) {
  738.                 throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_Index"));
  739.             }
  740.            
  741.             if (sourceIndex > currentLength - count) {
  742.                 throw new ArgumentException(Environment.GetResourceString("Arg_LongerThanSrcString"));
  743.             }
  744.            
  745.             // following fixed statement will throw exception for empty array.
  746.             if (count == 0) {
  747.                 return;
  748.             }
  749.            
  750.             unsafe {
  751.                 fixed (char* dest = &destination[destinationIndex], tsrc = currentString) {
  752.                     char* src = tsrc + sourceIndex;
  753.                     Buffer.memcpyimpl((byte*)src, (byte*)dest, count * 2);
  754.                 }
  755.             }
  756.         }
  757.        
  758.         // Inserts multiple copies of a string into this string builder at the specified position.
  759.         // Existing characters are shifted to make room for the new text.
  760.         // The capacity is adjusted as needed. If value equals String.Empty, this
  761.         // string builder is not changed.
  762.         //
  763.         unsafe public StringBuilder Insert(int index, string value, int count)
  764.         {
  765.             IntPtr tid;
  766.             string currentString = GetThreadSafeString(out tid);
  767.             int currentLength = currentString.Length;
  768.            
  769.             //Range check the index.
  770.             if (index < 0 || index > currentLength) {
  771.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  772.             }
  773.            
  774.             if (count < 0) {
  775.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
  776.             }
  777.            
  778.             //If value is null, empty or count is 0, do nothing. This is ECMA standard.
  779.             if (value == null || value.Length == 0 || count == 0) {
  780.                 return this;
  781.             }
  782.            
  783.             //Calculate the new length, ensure that we have the space and set the space variable for this buffer
  784.             int requiredLength;
  785.             try {
  786.                 requiredLength = checked(currentLength + (value.Length * count));
  787.             }
  788.             catch (OverflowException) {
  789.                 throw new OutOfMemoryException();
  790.             }
  791.            
  792.             if (NeedsAllocation(currentString, requiredLength)) {
  793.                 string newString = GetNewString(currentString, requiredLength);
  794.                 newString.InsertInPlace(index, value, count, currentLength, requiredLength);
  795.                 ReplaceString(tid, newString);
  796.             }
  797.             else {
  798.                 currentString.InsertInPlace(index, value, count, currentLength, requiredLength);
  799.                 ReplaceString(tid, currentString);
  800.             }
  801.             return this;
  802.         }
  803.        
  804.        
  805.        
  806.         // Property.
  807.         // Removes the specified characters from this string builder.
  808.         // The length of this string builder is reduced by
  809.         // length, but the capacity is unaffected.
  810.         //
  811.         public StringBuilder Remove(int startIndex, int length)
  812.         {
  813.             IntPtr tid;
  814.             string currentString = GetThreadSafeString(out tid);
  815.             int currentLength = currentString.Length;
  816.            
  817.             if (length < 0) {
  818.                 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
  819.             }
  820.            
  821.             if (startIndex < 0) {
  822.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  823.             }
  824.            
  825.             if (length > currentLength - startIndex) {
  826.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  827.             }
  828.            
  829.             currentString.RemoveInPlace(startIndex, length, currentLength);
  830.             ReplaceString(tid, currentString);
  831.            
  832.             return this;
  833.         }
  834.        
  835.        
  836.         //
  837.         //
  838.         // PUBLIC INSTANCE FUNCTIONS
  839.         //
  840.         //
  841.        
  842. /*====================================Append====================================
  843.         **
  844.         ==============================================================================*/       
  845.         // Appends a boolean to the end of this string builder.
  846.         // The capacity is adjusted as needed.
  847.         public StringBuilder Append(bool value)
  848.         {
  849.             return Append(value.ToString());
  850.         }
  851.        
  852.         // Appends an sbyte to this string builder.
  853.         // The capacity is adjusted as needed.
  854.         [CLSCompliant(false)]
  855.         public StringBuilder Append(sbyte value)
  856.         {
  857.             return Append(value.ToString(CultureInfo.CurrentCulture));
  858.         }
  859.        
  860.         // Appends a ubyte to this string builder.
  861.         // The capacity is adjusted as needed.
  862.         public StringBuilder Append(byte value)
  863.         {
  864.             return Append(value.ToString(CultureInfo.CurrentCulture));
  865.         }
  866.        
  867.         // Appends a character at the end of this string builder. The capacity is adjusted as needed.
  868.         public StringBuilder Append(char value)
  869.         {
  870.             IntPtr tid;
  871.            
  872.             // hand inlining of GetThreadSafeString
  873.             string currentString = m_StringValue;
  874.             tid = Thread.InternalGetCurrentThread();
  875.             if (m_currentThread != tid)
  876.                 currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);
  877.            
  878.             int currentLength = currentString.Length;
  879.             if (!NeedsAllocation(currentString, currentLength + 1)) {
  880.                 currentString.AppendInPlace(value, currentLength);
  881.                 ReplaceString(tid, currentString);
  882.                 return this;
  883.             }
  884.            
  885.             string newString = GetNewString(currentString, currentLength + 1);
  886.             newString.AppendInPlace(value, currentLength);
  887.             ReplaceString(tid, newString);
  888.             return this;
  889.         }
  890.        
  891.         // Appends a short to this string builder.
  892.         // The capacity is adjusted as needed.
  893.         public StringBuilder Append(short value)
  894.         {
  895.             return Append(value.ToString(CultureInfo.CurrentCulture));
  896.         }
  897.        
  898.         // Appends an int to this string builder.
  899.         // The capacity is adjusted as needed.
  900.         public StringBuilder Append(int value)
  901.         {
  902.             return Append(value.ToString(CultureInfo.CurrentCulture));
  903.         }
  904.        
  905.         // Appends a long to this string builder.
  906.         // The capacity is adjusted as needed.
  907.         public StringBuilder Append(long value)
  908.         {
  909.             return Append(value.ToString(CultureInfo.CurrentCulture));
  910.         }
  911.        
  912.         // Appends a float to this string builder.
  913.         // The capacity is adjusted as needed.
  914.         public StringBuilder Append(float value)
  915.         {
  916.             return Append(value.ToString(CultureInfo.CurrentCulture));
  917.         }
  918.        
  919.         // Appends a double to this string builder.
  920.         // The capacity is adjusted as needed.
  921.         public StringBuilder Append(double value)
  922.         {
  923.             return Append(value.ToString(CultureInfo.CurrentCulture));
  924.         }
  925.        
  926.         public StringBuilder Append(decimal value)
  927.         {
  928.             return Append(value.ToString(CultureInfo.CurrentCulture));
  929.         }
  930.        
  931.         // Appends an ushort to this string builder.
  932.         // The capacity is adjusted as needed.
  933.         [CLSCompliant(false)]
  934.         public StringBuilder Append(ushort value)
  935.         {
  936.             return Append(value.ToString(CultureInfo.CurrentCulture));
  937.         }
  938.        
  939.         // Appends an uint to this string builder.
  940.         // The capacity is adjusted as needed.
  941.         [CLSCompliant(false)]
  942.         public StringBuilder Append(uint value)
  943.         {
  944.             return Append(value.ToString(CultureInfo.CurrentCulture));
  945.         }
  946.        
  947.         // Appends an unsigned long to this string builder.
  948.         // The capacity is adjusted as needed.
  949.        
  950.         [CLSCompliant(false)]
  951.         public StringBuilder Append(ulong value)
  952.         {
  953.             return Append(value.ToString(CultureInfo.CurrentCulture));
  954.         }
  955.        
  956.         // Appends an Object to this string builder.
  957.         // The capacity is adjusted as needed.
  958.         public StringBuilder Append(object value)
  959.         {
  960.             if (null == value) {
  961.                 //Appending null is now a no-op.
  962.                 return this;
  963.             }
  964.             return Append(value.ToString());
  965.         }
  966.        
  967.         // Appends all of the characters in value to the current instance.
  968.         public StringBuilder Append(char[] value)
  969.         {
  970.             if (null == value) {
  971.                 return this;
  972.             }
  973.            
  974.             int valueLength = value.Length;
  975.            
  976.             IntPtr tid;
  977.             string currentString = GetThreadSafeString(out tid);
  978.            
  979.             int currentLength = currentString.Length;
  980.             int requiredLength = currentLength + value.Length;
  981.             if (NeedsAllocation(currentString, requiredLength)) {
  982.                 string newString = GetNewString(currentString, requiredLength);
  983.                 newString.AppendInPlace(value, 0, valueLength, currentLength);
  984.                 ReplaceString(tid, newString);
  985.             }
  986.             else {
  987.                 currentString.AppendInPlace(value, 0, valueLength, currentLength);
  988.                 ReplaceString(tid, currentString);
  989.             }
  990.             return this;
  991.         }
  992.        
  993. /*====================================Insert====================================
  994.         **
  995.         ==============================================================================*/       
  996.        
  997.         // Returns a reference to the StringBuilder with ; value inserted into
  998.         // the buffer at index. Existing characters are shifted to make room for the new text.
  999.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1000.         // StringBuilder is not changed.
  1001.         //
  1002.         public StringBuilder Insert(int index, string value)
  1003.         {
  1004.             if (value == null)
  1005.                 return Insert(index, value, 0);
  1006.             else
  1007.                 // This is to do the index validation
  1008.                 return Insert(index, value, 1);
  1009.         }
  1010.        
  1011.         // Returns a reference to the StringBuilder with ; value inserted into
  1012.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1013.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1014.         // StringBuilder is not changed.
  1015.         //
  1016.         public StringBuilder Insert(int index, bool value)
  1017.         {
  1018.             return Insert(index, value.ToString(), 1);
  1019.         }
  1020.        
  1021.         // Returns a reference to the StringBuilder with ; value inserted into
  1022.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1023.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1024.         // StringBuilder is not changed.
  1025.         //
  1026.         [CLSCompliant(false)]
  1027.         public StringBuilder Insert(int index, sbyte value)
  1028.         {
  1029.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1030.         }
  1031.        
  1032.         // Returns a reference to the StringBuilder with ; value inserted into
  1033.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1034.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1035.         // StringBuilder is not changed.
  1036.         //
  1037.         public StringBuilder Insert(int index, byte value)
  1038.         {
  1039.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1040.         }
  1041.        
  1042.         // Returns a reference to the StringBuilder with ; value inserted into
  1043.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1044.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1045.         // StringBuilder is not changed.
  1046.         //
  1047.         public StringBuilder Insert(int index, short value)
  1048.         {
  1049.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1050.         }
  1051.        
  1052.         // Returns a reference to the StringBuilder with ; value inserted into
  1053.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1054.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1055.         // StringBuilder is not changed.
  1056.         //
  1057.         public StringBuilder Insert(int index, char value)
  1058.         {
  1059.             return Insert(index, Char.ToString(value), 1);
  1060.         }
  1061.        
  1062.         // Returns a reference to the StringBuilder with ; value inserted into
  1063.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1064.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1065.         // StringBuilder is not changed.
  1066.         //
  1067.         public StringBuilder Insert(int index, char[] value)
  1068.         {
  1069.             if (null == value) {
  1070.                 return Insert(index, value, 0, 0);
  1071.             }
  1072.             return Insert(index, value, 0, value.Length);
  1073.         }
  1074.        
  1075.         // Returns a reference to the StringBuilder with charCount characters from
  1076.         // value inserted into the buffer at index. Existing characters are shifted
  1077.         // to make room for the new text and capacity is adjusted as required. If value is null, the StringBuilder
  1078.         // is unchanged. Characters are taken from value starting at position startIndex.
  1079.         public StringBuilder Insert(int index, char[] value, int startIndex, int charCount)
  1080.         {
  1081.             IntPtr tid;
  1082.             string currentString = GetThreadSafeString(out tid);
  1083.             int currentLength = currentString.Length;
  1084.            
  1085.             //Range check the index.
  1086.             if (index < 0 || index > currentLength) {
  1087.                 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1088.             }
  1089.            
  1090.             //If they passed in a null char array, just jump out quickly.
  1091.             if (value == null) {
  1092.                 if (startIndex == 0 && charCount == 0) {
  1093.                     return this;
  1094.                 }
  1095.                 throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_String"));
  1096.             }
  1097.            
  1098.            
  1099.             //Range check the array.
  1100.             if (startIndex < 0) {
  1101.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
  1102.             }
  1103.            
  1104.             if (charCount < 0) {
  1105.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
  1106.             }
  1107.            
  1108.             if (startIndex > value.Length - charCount) {
  1109.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1110.             }
  1111.            
  1112.             if (charCount == 0) {
  1113.                 // There is no data to insert, so just return.
  1114.                 return this;
  1115.             }
  1116.            
  1117.             int requiredLength = currentLength + charCount;
  1118.             if (NeedsAllocation(currentString, requiredLength)) {
  1119.                 string newString = GetNewString(currentString, requiredLength);
  1120.                 newString.InsertInPlace(index, value, startIndex, charCount, currentLength, requiredLength);
  1121.                 ReplaceString(tid, newString);
  1122.             }
  1123.             else {
  1124.                 currentString.InsertInPlace(index, value, startIndex, charCount, currentLength, requiredLength);
  1125.                 ReplaceString(tid, currentString);
  1126.             }
  1127.             return this;
  1128.         }
  1129.        
  1130.         // Returns a reference to the StringBuilder with ; value inserted into
  1131.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1132.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1133.         // StringBuilder is not changed.
  1134.         //
  1135.         public StringBuilder Insert(int index, int value)
  1136.         {
  1137.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1138.         }
  1139.        
  1140.         // Returns a reference to the StringBuilder with ; value inserted into
  1141.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1142.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1143.         // StringBuilder is not changed.
  1144.         //
  1145.         public StringBuilder Insert(int index, long value)
  1146.         {
  1147.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1148.         }
  1149.        
  1150.         // Returns a reference to the StringBuilder with ; value inserted into
  1151.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1152.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1153.         // StringBuilder is not changed.
  1154.         //
  1155.         public StringBuilder Insert(int index, float value)
  1156.         {
  1157.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1158.         }
  1159.        
  1160.        
  1161.         // Returns a reference to the StringBuilder with ; value inserted into
  1162.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1163.         // The capacity is adjusted as needed. If value equals String.Empty, the
  1164.         // StringBuilder is not changed.
  1165.         //
  1166.         public StringBuilder Insert(int index, double value)
  1167.         {
  1168.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1169.         }
  1170.        
  1171.         public StringBuilder Insert(int index, decimal value)
  1172.         {
  1173.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1174.         }
  1175.        
  1176.         // Returns a reference to the StringBuilder with value inserted into
  1177.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1178.         // The capacity is adjusted as needed.
  1179.         //
  1180.         [CLSCompliant(false)]
  1181.         public StringBuilder Insert(int index, ushort value)
  1182.         {
  1183.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1184.         }
  1185.        
  1186.        
  1187.         // Returns a reference to the StringBuilder with value inserted into
  1188.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1189.         // The capacity is adjusted as needed.
  1190.         //
  1191.         [CLSCompliant(false)]
  1192.         public StringBuilder Insert(int index, uint value)
  1193.         {
  1194.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1195.         }
  1196.        
  1197.         // Returns a reference to the StringBuilder with value inserted into
  1198.         // the buffer at index. Existing characters are shifted to make room for the new text.
  1199.         // The capacity is adjusted as needed.
  1200.         //
  1201.         [CLSCompliant(false)]
  1202.         public StringBuilder Insert(int index, ulong value)
  1203.         {
  1204.             return Insert(index, value.ToString(CultureInfo.CurrentCulture), 1);
  1205.         }
  1206.        
  1207.         // Returns a reference to this string builder with value inserted into
  1208.         // the buffer at index. Existing characters are shifted to make room for the
  1209.         // new text. The capacity is adjusted as needed. If value equals String.Empty, the
  1210.         // StringBuilder is not changed. No changes are made if value is null.
  1211.         //
  1212.         public StringBuilder Insert(int index, object value)
  1213.         {
  1214.             //If we get a null
  1215.             if (null == value) {
  1216.                 return this;
  1217.             }
  1218.             return Insert(index, value.ToString(), 1);
  1219.         }
  1220.        
  1221.         public StringBuilder AppendFormat(string format, object arg0)
  1222.         {
  1223.             return AppendFormat(null, format, new object[] {arg0});
  1224.         }
  1225.        
  1226.         public StringBuilder AppendFormat(string format, object arg0, object arg1)
  1227.         {
  1228.             return AppendFormat(null, format, new object[] {arg0, arg1});
  1229.         }
  1230.        
  1231.         public StringBuilder AppendFormat(string format, object arg0, object arg1, object arg2)
  1232.         {
  1233.             return AppendFormat(null, format, new object[] {arg0, arg1, arg2});
  1234.         }
  1235.        
  1236.         public StringBuilder AppendFormat(string format, params object[] args)
  1237.         {
  1238.             return AppendFormat(null, format, args);
  1239.         }
  1240.        
  1241.         private static void FormatError()
  1242.         {
  1243.             throw new FormatException(Environment.GetResourceString("Format_InvalidString"));
  1244.         }
  1245.        
  1246.         public StringBuilder AppendFormat(IFormatProvider provider, string format, params object[] args)
  1247.         {
  1248.             if (format == null || args == null) {
  1249.                 throw new ArgumentNullException((format == null) ? "format" : "args");
  1250.             }
  1251.             char[] chars = format.ToCharArray(0, format.Length);
  1252.             int pos = 0;
  1253.             int len = chars.Length;
  1254.             char ch = '\0';
  1255.            
  1256.             ICustomFormatter cf = null;
  1257.             if (provider != null) {
  1258.                 cf = (ICustomFormatter)provider.GetFormat(typeof(ICustomFormatter));
  1259.             }
  1260.            
  1261.             while (true) {
  1262.                 int p = pos;
  1263.                 int i = pos;
  1264.                 while (pos < len) {
  1265.                     ch = chars[pos];
  1266.                    
  1267.                     pos++;
  1268.                     if (ch == '}') {
  1269.                         if (pos < len && chars[pos] == '}')
  1270.                             pos++;
  1271.                         else
  1272.                             // Treat as escape character for }}
  1273.                             FormatError();
  1274.                     }
  1275.                    
  1276.                     if (ch == '{') {
  1277.                         if (pos < len && chars[pos] == '{')
  1278.                             pos++;
  1279.                         // Treat as escape character for {{
  1280.                         else {
  1281.                             pos--;
  1282.                             break;
  1283.                         }
  1284.                     }
  1285.                    
  1286.                     chars[i++] = ch;
  1287.                 }
  1288.                 if (i > p)
  1289.                     Append(chars, p, i - p);
  1290.                 if (pos == len)
  1291.                     break;
  1292.                 pos++;
  1293.                 if (pos == len || (ch = chars[pos]) < '0' || ch > '9')
  1294.                     FormatError();
  1295.                 int index = 0;
  1296.                 do {
  1297.                     index = index * 10 + ch - '0';
  1298.                     pos++;
  1299.                     if (pos == len)
  1300.                         FormatError();
  1301.                     ch = chars[pos];
  1302.                 }
  1303.                 while (ch >= '0' && ch <= '9' && index < 1000000);
  1304.                 if (index >= args.Length)
  1305.                     throw new FormatException(Environment.GetResourceString("Format_IndexOutOfRange"));
  1306.                 while (pos < len && (ch = chars[pos]) == ' ')
  1307.                     pos++;
  1308.                 bool leftJustify = false;
  1309.                 int width = 0;
  1310.                 if (ch == ',') {
  1311.                     pos++;
  1312.                     while (pos < len && chars[pos] == ' ')
  1313.                         pos++;
  1314.                    
  1315.                     if (pos == len)
  1316.                         FormatError();
  1317.                     ch = chars[pos];
  1318.                     if (ch == '-') {
  1319.                         leftJustify = true;
  1320.                         pos++;
  1321.                         if (pos == len)
  1322.                             FormatError();
  1323.                         ch = chars[pos];
  1324.                     }
  1325.                     if (ch < '0' || ch > '9')
  1326.                         FormatError();
  1327.                     do {
  1328.                         width = width * 10 + ch - '0';
  1329.                         pos++;
  1330.                         if (pos == len)
  1331.                             FormatError();
  1332.                         ch = chars[pos];
  1333.                     }
  1334.                     while (ch >= '0' && ch <= '9' && width < 1000000);
  1335.                 }
  1336.                
  1337.                 while (pos < len && (ch = chars[pos]) == ' ')
  1338.                     pos++;
  1339.                 object arg = args[index];
  1340.                 string fmt = null;
  1341.                 if (ch == ':') {
  1342.                     pos++;
  1343.                     p = pos;
  1344.                     i = pos;
  1345.                     while (true) {
  1346.                         if (pos == len)
  1347.                             FormatError();
  1348.                         ch = chars[pos];
  1349.                         pos++;
  1350.                         if (ch == '{') {
  1351.                             if (pos < len && chars[pos] == '{')
  1352.                                 pos++;
  1353.                             else
  1354.                                 // Treat as escape character for {{
  1355.                                 FormatError();
  1356.                         }
  1357.                         else if (ch == '}') {
  1358.                             if (pos < len && chars[pos] == '}')
  1359.                                 pos++;
  1360.                             // Treat as escape character for }}
  1361.                             else {
  1362.                                 pos--;
  1363.                                 break;
  1364.                             }
  1365.                         }
  1366.                        
  1367.                         chars[i++] = ch;
  1368.                     }
  1369.                     if (i > p)
  1370.                         fmt = new string(chars, p, i - p);
  1371.                 }
  1372.                 if (ch != '}')
  1373.                     FormatError();
  1374.                 pos++;
  1375.                 string s = null;
  1376.                 if (cf != null) {
  1377.                     s = cf.Format(fmt, arg, provider);
  1378.                 }
  1379.                
  1380.                 if (s == null) {
  1381.                     if (arg is IFormattable) {
  1382.                         s = ((IFormattable)arg).ToString(fmt, provider);
  1383.                     }
  1384.                     else if (arg != null) {
  1385.                         s = arg.ToString();
  1386.                     }
  1387.                 }
  1388.                
  1389.                 if (s == null)
  1390.                     s = String.Empty;
  1391.                 int pad = width - s.Length;
  1392.                 if (!leftJustify && pad > 0)
  1393.                     Append(' ', pad);
  1394.                 Append(s);
  1395.                 if (leftJustify && pad > 0)
  1396.                     Append(' ', pad);
  1397.             }
  1398.             return this;
  1399.         }
  1400.        
  1401.         // Returns a reference to the current StringBuilder with all instances of oldString
  1402.         // replaced with newString. If startIndex and count are specified,
  1403.         // we only replace strings completely contained in the range of startIndex to startIndex +
  1404.         // count. The strings to be replaced are checked on an ordinal basis (e.g. not culture aware). If
  1405.         // newValue is null, instances of oldValue are removed (e.g. replaced with nothing.).
  1406.         //
  1407.         public StringBuilder Replace(string oldValue, string newValue)
  1408.         {
  1409.             return Replace(oldValue, newValue, 0, Length);
  1410.         }
  1411.        
  1412.         [MethodImplAttribute(MethodImplOptions.InternalCall)]
  1413.         public extern StringBuilder Replace(string oldValue, string newValue, int startIndex, int count);
  1414.        
  1415.         public bool Equals(StringBuilder sb)
  1416.         {
  1417.             if (sb == null)
  1418.                 return false;
  1419.             return ((this.Capacity == sb.Capacity) && (this.MaxCapacity == sb.MaxCapacity) && (this.m_StringValue.Equals(sb.m_StringValue)));
  1420.         }
  1421.        
  1422.         // Returns a StringBuilder with all instances of oldChar replaced with
  1423.         // newChar. The size of the StringBuilder is unchanged because we're only
  1424.         // replacing characters. If startIndex and count are specified, we
  1425.         // only replace characters in the range from startIndex to startIndex+count
  1426.         //
  1427.         public StringBuilder Replace(char oldChar, char newChar)
  1428.         {
  1429.             return Replace(oldChar, newChar, 0, Length);
  1430.         }
  1431.         public StringBuilder Replace(char oldChar, char newChar, int startIndex, int count)
  1432.         {
  1433.             IntPtr tid;
  1434.             string currentString = GetThreadSafeString(out tid);
  1435.             int currentLength = currentString.Length;
  1436.            
  1437.             if ((uint)startIndex > (uint)currentLength) {
  1438.                 throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1439.             }
  1440.            
  1441.             if (count < 0 || startIndex > currentLength - count) {
  1442.                 throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index"));
  1443.             }
  1444.            
  1445.             BCLDebug.Assert(!NeedsAllocation(currentString, currentLength), "New allocation should not happen!");
  1446.             currentString.ReplaceCharInPlace(oldChar, newChar, startIndex, count, currentLength);
  1447.             ReplaceString(tid, currentString);
  1448.             return this;
  1449.         }
  1450.     }
  1451. }

Developer Fusion