Advertisement
Gaddy

StringFast - optimized C# string class

Dec 31st, 2015
4,119
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.69 KB | None | 0 0
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5.  
  6. ///<summary>
  7. /// Mutable String class, optimized for speed and memory allocations while retrieving the final result as a string.
  8. /// Similar use than StringBuilder, but avoid a lot of allocations done by StringBuilder (conversion of int and float to string, frequent capacity change, etc.)
  9. /// Author: Nicolas Gadenne [email protected]
  10. ///</summary>
  11. public class StringFast
  12. {
  13.     ///<summary>Immutable string. Generated at last moment, only if needed</summary>
  14.     private string m_stringGenerated = "";
  15.     ///<summary>Is m_stringGenerated is up to date ?</summary>
  16.     private bool m_isStringGenerated = false;
  17.  
  18.     ///<summary>Working mutable string</summary>
  19.     private char[] m_buffer = null;
  20.     private int m_bufferPos = 0;
  21.     private int m_charsCapacity = 0;
  22.  
  23.     ///<summary>Temporary string used for the Replace method</summary>
  24.     private List<char> m_replacement = null;
  25.  
  26.     private object m_valueControl = null;
  27.     private int m_valueControlInt = int.MinValue;
  28.  
  29.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  30.  
  31.     public StringFast( int initialCapacity = 32 )
  32.     {
  33.         m_buffer = new char[ m_charsCapacity = initialCapacity ];
  34.     }
  35.  
  36.     public bool IsEmpty()
  37.     {
  38.         return (m_isStringGenerated ? (m_stringGenerated == null) : (m_bufferPos == 0));
  39.     }
  40.  
  41.     ///<summary>Return the string</summary>
  42.     public override string ToString()
  43.     {
  44.         if( !m_isStringGenerated ) // Regenerate the immutable string if needed
  45.         {
  46.             m_stringGenerated = new string( m_buffer, 0, m_bufferPos );
  47.             m_isStringGenerated = true;
  48.         }
  49.         return m_stringGenerated;
  50.     }
  51.  
  52.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  53.     // Value controls methods: use a value to check if the string has to be regenerated.
  54.  
  55.     ///<summary>Return true if the valueControl has changed (and update it)</summary>
  56.     public bool IsModified( int newControlValue )
  57.     {
  58.         bool changed = (newControlValue != m_valueControlInt);
  59.         if( changed )
  60.             m_valueControlInt = newControlValue;
  61.         return changed;
  62.     }
  63.  
  64.     ///<summary>Return true if the valueControl has changed (and update it)</summary>
  65.     public bool IsModified( object newControlValue )
  66.     {
  67.         bool changed = !(newControlValue.Equals( m_valueControl ));
  68.         if( changed )
  69.             m_valueControl = newControlValue;
  70.         return changed;
  71.     }
  72.  
  73.  
  74.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  75.     // Set methods:
  76.  
  77.     ///<summary>Set a string, no memorry allocation</summary>
  78.     public void Set( string str )
  79.     {
  80.         // We fill the m_chars list to manage future appends, but we also directly set the final stringGenerated
  81.         Clear();
  82.         Append( str );
  83.         m_stringGenerated = str;
  84.         m_isStringGenerated = true;
  85.     }
  86.     ///<summary>Caution, allocate some memory</summary>
  87.     public void Set( object str )
  88.     {
  89.         Set( str.ToString() );
  90.     }
  91.  
  92.     ///<summary>Append several params: no memory allocation unless params are of object type</summary>
  93.     public void Set<T1, T2>( T1 str1, T2 str2 )
  94.     {
  95.         Clear();
  96.         Append( str1 ); Append( str2 );
  97.     }
  98.     public void Set<T1, T2, T3>( T1 str1, T2 str2, T3 str3 )
  99.     {
  100.         Clear();
  101.         Append( str1 ); Append( str2 ); Append( str3 );
  102.     }
  103.     public void Set<T1, T2, T3, T4>( T1 str1, T2 str2, T3 str3, T4 str4 )
  104.     {
  105.         Clear();
  106.         Append( str1 ); Append( str2 ); Append( str3 ); Append( str4 );
  107.     }
  108.     ///<summary>Allocate a little memory (20 byte)</summary>
  109.     public void Set( params object[] str )
  110.     {
  111.         Clear();
  112.         for( int i=0; i<str.Length; i++ )
  113.             Append( str[ i ] );
  114.     }
  115.  
  116.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  117.     // Append methods, to build the string without allocation
  118.  
  119.     ///<summary>Reset the m_char array</summary>
  120.     public StringFast Clear()
  121.     {
  122.         m_bufferPos = 0;
  123.         m_isStringGenerated = false;
  124.         return this;
  125.     }
  126.  
  127.     ///<summary>Append a string without memory allocation</summary>
  128.     public StringFast Append( string value )
  129.     {
  130.         ReallocateIFN( value.Length );
  131.         int n = value.Length;
  132.         for( int i=0; i<n; i++ )
  133.             m_buffer[ m_bufferPos + i ] = value[ i ];
  134.         m_bufferPos += n;
  135.         m_isStringGenerated = false;
  136.         return this;
  137.     }
  138.     ///<summary>Append an object.ToString(), allocate some memory</summary>
  139.     public StringFast Append( object value )
  140.     {
  141.         Append( value.ToString() );
  142.         return this;
  143.     }
  144.  
  145.     ///<summary>Append an int without memory allocation</summary>
  146.     public StringFast Append( int value )
  147.     {
  148.         // Allocate enough memory to handle any int number
  149.         ReallocateIFN( 16 );
  150.  
  151.         // Handle the negative case
  152.         if( value < 0 )
  153.         {
  154.             value = -value;
  155.             m_buffer[ m_bufferPos++ ] = '-';
  156.         }
  157.  
  158.         // Copy the digits in reverse order
  159.         int nbChars = 0;
  160.         do
  161.         {
  162.             m_buffer[ m_bufferPos++ ] = (char)('0' + value%10);
  163.             value /= 10;
  164.             nbChars++;
  165.         } while( value != 0 );
  166.  
  167.         // Reverse the result
  168.         for( int i=nbChars/2-1; i>=0; i-- )
  169.         {
  170.             char c = m_buffer[ m_bufferPos-i-1 ];
  171.             m_buffer[ m_bufferPos-i-1 ] = m_buffer[ m_bufferPos-nbChars+i ];
  172.             m_buffer[ m_bufferPos-nbChars+i ] = c;
  173.         }
  174.         m_isStringGenerated = false;
  175.         return this;
  176.     }
  177.  
  178.     ///<summary>Append a float without memory allocation.</summary>
  179.     public StringFast Append( float valueF )
  180.     {
  181.         double value = valueF;
  182.         m_isStringGenerated = false;
  183.         ReallocateIFN( 32 ); // Check we have enough buffer allocated to handle any float number
  184.  
  185.         // Handle the 0 case
  186.         if( value == 0 )
  187.         {
  188.             m_buffer[ m_bufferPos++ ] = '0';
  189.             return this;
  190.         }
  191.  
  192.         // Handle the negative case
  193.         if( value < 0 )
  194.         {
  195.             value = -value;
  196.             m_buffer[ m_bufferPos++ ] = '-';
  197.         }
  198.  
  199.         // Get the 7 meaningful digits as a long
  200.         int nbDecimals = 0;
  201.         while( value < 1000000 )
  202.         {
  203.             value *= 10;
  204.             nbDecimals++;
  205.         }
  206.         long valueLong = (long)System.Math.Round( value );
  207.  
  208.         // Parse the number in reverse order
  209.         int nbChars = 0;
  210.         bool isLeadingZero = true;
  211.         while( valueLong != 0 || nbDecimals >= 0 )
  212.         {
  213.             // We stop removing leading 0 when non-0 or decimal digit
  214.             if( valueLong%10 != 0 || nbDecimals <= 0 )
  215.                 isLeadingZero = false;
  216.  
  217.             // Write the last digit (unless a leading zero)
  218.             if( !isLeadingZero )
  219.                 m_buffer[ m_bufferPos + (nbChars++) ] = (char)('0' + valueLong%10);
  220.  
  221.             // Add the decimal point
  222.             if( --nbDecimals == 0 && !isLeadingZero )
  223.                 m_buffer[ m_bufferPos + (nbChars++) ] = '.';
  224.  
  225.             valueLong /= 10;
  226.         }
  227.         m_bufferPos += nbChars;
  228.        
  229.         // Reverse the result
  230.         for( int i=nbChars/2-1; i>=0; i-- )
  231.         {
  232.             char c = m_buffer[ m_bufferPos-i-1 ];
  233.             m_buffer[ m_bufferPos-i-1 ] = m_buffer[ m_bufferPos-nbChars+i ];
  234.             m_buffer[ m_bufferPos-nbChars+i ] = c;
  235.         }
  236.  
  237.         return this;
  238.     }
  239.  
  240.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  241.  
  242.     ///<summary>Replace all occurences of a string by another one</summary>
  243.     public StringFast Replace( string oldStr, string newStr )
  244.     {
  245.         if( m_bufferPos == 0 )
  246.             return this;
  247.  
  248.         if( m_replacement == null )
  249.             m_replacement = new List<char>();
  250.  
  251.         // Create the new string into m_replacement
  252.         for( int i=0; i<m_bufferPos; i++ )
  253.         {
  254.             bool isToReplace = false;
  255.             if( m_buffer[ i ] == oldStr[ 0 ] ) // If first character found, check for the rest of the string to replace
  256.             {
  257.                 int k=1;
  258.                 while( k < oldStr.Length && m_buffer[ i+k ] == oldStr[ k ] )
  259.                     k++;
  260.                 isToReplace = (k >= oldStr.Length);
  261.             }
  262.             if( isToReplace ) // Do the replacement
  263.             {
  264.                 i += oldStr.Length-1;
  265.                 if( newStr != null )
  266.                     for( int k=0; k<newStr.Length; k++ )
  267.                         m_replacement.Add( newStr[ k ] );
  268.             }
  269.             else // No replacement, copy the old character
  270.                 m_replacement.Add( m_buffer[ i ] );
  271.         }
  272.  
  273.         // Copy back the new string into m_chars
  274.         ReallocateIFN( m_replacement.Count - m_bufferPos );
  275.         for( int k=0; k<m_replacement.Count; k++ )
  276.             m_buffer[ k ] = m_replacement[ k ];
  277.         m_bufferPos = m_replacement.Count;
  278.         m_replacement.Clear();
  279.         m_isStringGenerated = false;
  280.         return this;
  281.     }
  282.  
  283.     // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  284.  
  285.     private void ReallocateIFN( int nbCharsToAdd )
  286.     {
  287.         if( m_bufferPos + nbCharsToAdd > m_charsCapacity )
  288.         {
  289.             m_charsCapacity = System.Math.Max( m_charsCapacity + nbCharsToAdd, m_charsCapacity * 2 );
  290.             char[] newChars = new char[ m_charsCapacity ];
  291.             m_buffer.CopyTo( newChars, 0 );
  292.             m_buffer = newChars;
  293.         }
  294.     }
  295.  
  296. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement