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