String.FromChars feature request

Monkey Forums/Monkey Programming/String.FromChars feature request

ziggy(Posted 2013) [#1]
With current String.FromChars implementation it is complicated to make an efficient String Builder that preallocates space to speed up concatenation. Could we have another overload with a Count parameter to prevent having to resize the integer array or having to resize the resulting string? It looks like a very easy addition and it would be handy sometimes.


marksibly(Posted 2013) [#2]
Not quite sure what your're getting at - can you post some pseudo code?


ziggy(Posted 2013) [#3]
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).


marksibly(Posted 2013) [#4]
Is there any reason you can't use a StringStack? This is what I generally use for a 'string builder', eg:

Local buf:=New StringStack
For Local i:=0 Until something
   buf.Push GetAString()
Next
Return buf.Join()



ziggy(Posted 2013) [#5]
Basically, I didn't know if existed. Will take a look! Thanks!


ziggy(Posted 2013) [#6]
Ok, I've given the StringStack a try and it seems to work as fast as my string builder (not sure which is faster as basically I can't tell the difference) so I'll stick with that.

Thanks Mark!