├── .gitignore
├── Assets
├── FastString.cs
├── FastString.cs.meta
├── FastStringTest.cs
├── FastStringTest.cs.meta
├── FastStringTest.unity
└── FastStringTest.unity.meta
├── LICENSE
├── ProjectSettings
├── AudioManager.asset
├── ClusterInputManager.asset
├── DynamicsManager.asset
├── EditorBuildSettings.asset
├── EditorSettings.asset
├── GraphicsSettings.asset
├── InputManager.asset
├── NavMeshAreas.asset
├── NetworkManager.asset
├── Physics2DSettings.asset
├── ProjectSettings.asset
├── ProjectVersion.txt
├── QualitySettings.asset
├── TagManager.asset
├── TimeManager.asset
└── UnityConnectSettings.asset
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /[Ll]ibrary/
2 | /[Tt]emp/
3 | /[Oo]bj/
4 | /[Bb]uild/
5 | /[Bb]uilds/
6 | /Assets/AssetStoreTools*
7 |
8 | # Autogenerated VS/MD solution and project files
9 | ExportedObj/
10 | *.csproj
11 | *.unityproj
12 | *.sln
13 | *.suo
14 | *.tmp
15 | *.user
16 | *.userprefs
17 | *.pidb
18 | *.booproj
19 | *.svd
20 |
21 |
22 | # Unity3D generated meta files
23 | *.pidb.meta
24 |
25 | # Unity3D Generated File On Crash Reports
26 | sysinfo.txt
27 |
28 | # Builds
29 | *.apk
30 | *.unitypackage
31 |
--------------------------------------------------------------------------------
/Assets/FastString.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | ///
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 | ///
10 | public class FastString
11 | {
12 | ///Immutable string. Generated at last moment, only if needed
13 | private string m_stringGenerated = "";
14 | ///Is m_stringGenerated is up to date ?
15 | private bool m_isStringGenerated = false;
16 |
17 | ///Working mutable string
18 | private char[] m_buffer = null;
19 | private int m_bufferPos = 0;
20 | private int m_charsCapacity = 0;
21 |
22 | ///Temporary string used for the Replace method
23 | private List 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 | ///Return the string
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 | ///Return true if the valueControl has changed (and update it)
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 | ///Return true if the valueControl has changed (and update it)
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 | ///Set a string, no memorry allocation
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 | ///Caution, allocate some memory
81 | public void Set( object str )
82 | {
83 | Set( str.ToString() );
84 | }
85 |
86 | ///Append several params: no memory allocation unless params are of object type
87 | public void Set( T1 str1, T2 str2 )
88 | {
89 | Clear();
90 | Append( str1 ); Append( str2 );
91 | }
92 | public void Set( T1 str1, T2 str2, T3 str3 )
93 | {
94 | Clear();
95 | Append( str1 ); Append( str2 ); Append( str3 );
96 | }
97 | public void Set( T1 str1, T2 str2, T3 str3, T4 str4 )
98 | {
99 | Clear();
100 | Append( str1 ); Append( str2 ); Append( str3 ); Append( str4 );
101 | }
102 | ///Allocate a little memory (20 byte)
103 | public void Set( params object[] str )
104 | {
105 | Clear();
106 | for( int i=0; iReset the m_char array
113 | public FastString Clear()
114 | {
115 | m_bufferPos = 0;
116 | m_isStringGenerated = false;
117 | return this;
118 | }
119 |
120 | ///Append a string without memory allocation
121 | public FastString Append( string value )
122 | {
123 | ReallocateIFN( value.Length );
124 | int n = value.Length;
125 | for( int i=0; iAppend an object.ToString(), allocate some memory
132 | public FastString Append( object value )
133 | {
134 | Append( value.ToString() );
135 | return this;
136 | }
137 |
138 | ///Append an int without memory allocation
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 | ///Append a float without memory allocation.
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 | ///Replace all occurences of a string by another one
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();
241 |
242 | // Create the new string into m_replacement
243 | for( int i=0; i= 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 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 | }
--------------------------------------------------------------------------------
/Assets/FastString.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: c5d083d6e8d0cb44787efcd750b8a7d7
3 | timeCreated: 1493820788
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Assets/FastStringTest.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using UnityEngine.Profiling;
3 |
4 | public class FastStringTest : MonoBehaviour
5 | {
6 | private FastString m_strCustom = new FastString(64);
7 |
8 | private System.Text.StringBuilder m_strBuilder = new System.Text.StringBuilder(64);
9 |
10 | private delegate string Test();
11 |
12 | private string String_Added()
13 | {
14 | string str = "PI=" + Mathf.PI + "_373=" + 373;
15 | return str.Replace("373", "5428");
16 | }
17 |
18 | private string String_Concat()
19 | {
20 | return string.Concat("PI=", Mathf.PI, "_373=", 373).Replace("373", "5428");
21 | }
22 |
23 | private string StringBuilder()
24 | {
25 | m_strBuilder.Length = 0;
26 | m_strBuilder.Append("PI=").Append(Mathf.PI).Append("_373=").Append(373).Replace("373", "5428");
27 | return m_strBuilder.ToString();
28 | }
29 |
30 | private string FastString()
31 | {
32 | m_strCustom.Clear();
33 | m_strCustom.Append("PI=").Append(Mathf.PI).Append("_373=").Append(373).Replace("373", "5428");
34 | return m_strCustom.ToString();
35 | }
36 |
37 | private void RunTest(string testName, Test test)
38 | {
39 | Profiler.BeginSample(testName);
40 | string lastResult = null;
41 | for (int i = 0; i < 1000; i++)
42 | lastResult = test();
43 | Profiler.EndSample();
44 | Debug.Log( "Check test result: test=" + testName + " result='" + lastResult + "' (" + lastResult.Length + ")" );
45 | }
46 |
47 | private void Start()
48 | {
49 | Debug.Log("=================");
50 | RunTest("Test #1: string (+) ", String_Added);
51 | RunTest("Test #2: string (.concat) ", String_Concat);
52 | RunTest("Test #3: StringBuilder ", StringBuilder);
53 | RunTest("Test #4: FastString", FastString);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Assets/FastStringTest.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5ed4b9528a97ded49b30ba4e1ad16201
3 | timeCreated: 1493820788
4 | licenseType: Pro
5 | MonoImporter:
6 | serializedVersion: 2
7 | defaultReferences: []
8 | executionOrder: 0
9 | icon: {instanceID: 0}
10 | userData:
11 | assetBundleName:
12 | assetBundleVariant:
13 |
--------------------------------------------------------------------------------
/Assets/FastStringTest.unity:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/Assets/FastStringTest.unity
--------------------------------------------------------------------------------
/Assets/FastStringTest.unity.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e216bd9c88d5fc34794518120de7bb30
3 | timeCreated: 1493821317
4 | licenseType: Pro
5 | DefaultImporter:
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 snozbot
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/ProjectSettings/AudioManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/AudioManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/ClusterInputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/ClusterInputManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/DynamicsManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/DynamicsManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorBuildSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/EditorBuildSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/EditorSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/EditorSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/GraphicsSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/GraphicsSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/InputManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/InputManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/NavMeshAreas.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/NavMeshAreas.asset
--------------------------------------------------------------------------------
/ProjectSettings/NetworkManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/NetworkManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/Physics2DSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/Physics2DSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/ProjectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/ProjectSettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/ProjectVersion.txt:
--------------------------------------------------------------------------------
1 | m_EditorVersion: 5.6.0f3
2 |
--------------------------------------------------------------------------------
/ProjectSettings/QualitySettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/QualitySettings.asset
--------------------------------------------------------------------------------
/ProjectSettings/TagManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/TagManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/TimeManager.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/TimeManager.asset
--------------------------------------------------------------------------------
/ProjectSettings/UnityConnectSettings.asset:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/snozbot/FastString/e43f8323afd4aaaf30badd0e6292cd3ebaead70e/ProjectSettings/UnityConnectSettings.asset
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FastString
2 | Alternative to the [StringBuilder class](https://msdn.microsoft.com/en-us/library/system.text.stringbuilder(v=vs.110).aspx) for Unity games, with minimal memory allocation and faster performance.
3 | Based on [FastString](https://www.reddit.com/r/Unity3D/comments/3zz62z/alternative_to_stringbuilder_without_memory/) by Nicolas Gadenne of [Gaddy Games](http://gaddygames.com/site/)
4 |
5 | The class has been designed to be most useful in common game situations: a concatenation of a few string and data, then used by a Unity api method as an immutable string - every frame.
6 | It handles Append() and Replace() without doing any allocation, except for very rare capacity augmentation (contrary to StringBuilder which surprisingly does capacity change very often).
7 | It also appends float and int numbers without any allocation.
8 |
9 | The only common memory allocation is when you retrieve the final immutable string - but this is only done when required.
10 |
11 | # Running the tests
12 |
13 | A Unity project is provided for testing the performance.
14 |
15 | - To run the performance tests, open the FastStringTest scene and the Profiler ( Window > Profiler ).
16 | - Run the scene in the editor and then stop it again.
17 | - Enter 'Test' in the profile search to see the profile results for each technique.
18 |
--------------------------------------------------------------------------------