Yes of course!
Class StringBuilder
Method New()
buffer = New Int[0]
End Method
Method Concat(str:String)
Local dataSize:Int = buffer.Length
If dataSize = 0 Then dataSize = Capacity
While dataSize < length + str.Length
dataSize *= 2
Wend
If dataSize > buffer.Length Then
buffer = buffer.Resize(dataSize) 'This allocation is reduced compared to String+= operation.
EndIf
Print "dataSize is " + dataSize + " and buffer size is " + buffer.Length
For Local index:Int = length Until length + str.Length
buffer[index] = str[index - length]
Next
length += str.Length
End Method
Method ToString:String()
Return String.FromChars(buffer[ .. length])
End Method
Method Clear()
length = 0
End Method
Method Length()
Return length
End Method
Method BufferSize:Int()
Return buffer.Length
End Method
Private
Field buffer:Int[]
Field length:Int = 0
Const Capacity:Int = 128
End Class
This is a very minimal working example.
You can concat as many strings to the StringBuilder (avoiding the creation of a new String each time you do so) and then call a ToString to get the "built" string after all the concatenation operations. The ToString function could be much better if it allowed to be written like this:
Method ToString:String()
Return String.FromChars(buffer, length)
End Method
I'm building a parser for the background compiler and it gets very slow when it has to concatenate lots of characters on a string. Doing with the StringBuilder is several orders of magnitude faster, but I see the final data copying as something very easy to prevent and not needed if we could just give a length to the FromChars function.
When I use this to read very big strings, it gets slow:
'summary: Prompt for user input in the system console window and resturns a String
Function Input:String(prompt:String=">")
Local c:Int, result:String
For Local i:Int = 0 until prompt.Length
fputc(prompt[i],stdout)
end
fflush(stdout)
c = fgetc(stdin)
While c <> 10 And c <> 13
result += String.FromChar(c)
c=fgetc(stdin);
Wend
fflush(stdin)
Return result;
End
The same using a StringBuilder performs a lot better.
Function Input:String(prompt:String=">")
Local c:Int, result:String
For Local i:Int = 0 until prompt.Length
fputc(prompt[i],stdout)
end
fflush(stdout)
c = fgetc(stdin)
If SB = Null Then SB = New StringBuilder
SB.Clear()
While c <> 10 And c <> 13
'result += String.FromChar(c)
SB.Concat(String.FromChar(c));
c=fgetc(stdin);
Wend
fflush(stdin)
Return SB.ToString() 'result;
End
I gess, when using lots of ToString operations on a Stringbuilder class, if the creation of copies of data was prevented more, it would be worth it (maybe not, not sure).
|