String building in REALbasic
In REALbasic, all Strings are immutable. That is, they cannot be changed in any way. When you do something like this:
MyString = MyString + SomeOtherString
the framework is doing a lot of heavy lifting behind the scenes.
First, a new string must be created that is big enough to hold both source strings. Next, the source strings are both copied into the newly-created string. Finally, the memory behind the original MyString is released and the new string becomes MyString. It's mighty convenient, but comes with a performance penalty. The penalty adds up fast if the strings are large and/or you're doing it thousands of times.
Surely, there's a faster way to append one string to another? The MemoryBlock class comes to mind immediately. MemoryBlocks aren't immutable, after all, but they are somewhat unwieldy compared to Strings.
Enter the BinaryStream class. You may remember the BinaryStream from such tasks as reading and writing to files on the disk, but the BinaryStream is quite a bit more versatile. Among other things, a BinaryStream can be use to read and write directly to/from a MemoryBlock. If you write beyond the end of the stream, the underlying MemoryBlock is enlarged to accommodate the new data just as a file would be. What's more, the BinaryStream will increase the size of the MemoryBlock in chunks, meaning as you Write to the BinaryStream, fewer re-size operations must be done on the underlying MemoryBlock.
This makes the BinaryStream+MemoryBlock pattern several orders of magnitude faster than string concatenation.
Syntax
Creating a MemoryBlock-backed BinaryStream is very simple, only slightly more complicated that String concatenation. First, you create a MemoryBlock of the desired size; for an empty stream create a zero-length MemoryBlock, or pass an existing MemoryBlock to access/alter its contents using the BinaryStream. Once you have the MemoryBlock pass it to BinaryStream.Constructor:
Dim MyMemoryBlock As New MemoryBlock(0) ' pass the MemoryBlock to BinaryStream.Constructor Dim MyStream As New BinaryStream(MyMemoryBlock) MyStream.Write("Hello world!") MyStream.Close
or,
Dim MyMemoryBlock As MemoryBlock = "Hello world!" Dim MyStream As New BinaryStream(MyMemoryBlock) ' set the current offset in the MemoryBlock MyStream.Position = MyStream.Length - 1 ' read one character from the MemoryBlock at the current offest Dim char As String = MyStream.Read(1)
And that's it. Two lines of code and you've got a mutable string buffer that acts like a file stream but exists entirely in RAM.
Example and Benchmark
Suppose you have a situation where you want to continuously grow a string by appending new data to it. So you write a method something like the following snippet, using string concatenation:
Function Foo(Count As Integer) As String Dim output As String For i As Integer = 0 To Count output = output + "Foo! " ' Concatenating Next Return output End Function
It works perfectly fine, but it's pretty slow: 100,000 iterations takes about 10 seconds on my PC (with profiling on.)
The BinaryStream+MemoryBlock pattern blows concatenation out the the water. The following revised snippet performs 100,000 iterations in **0.03 seconds** and yields the same output.
Function Foo(Count As Integer) As String Dim output As New MemoryBlock(0) Dim outstream As New BinaryStream(output) For i As Integer = 0 To Count outstream.Write("Foo! ") ' Append to the MemoryBlock Next outstream.Close ' always clean up Return output End Function
See here for a less contrived example of string-building.