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.

Important NoteWhen using a BinaryStream and a MemoryBlock together, BinaryStream.Length will be the number of bytes contained in the MemoryBlock and MemoryBlock.Size will be the actual number of bytes reserved by the MemoryBlock. The Size of the MemoryBlock will always be greater-than or equal to the Length of the BinaryStream. Other BinaryStream properties and methods such as Position and EOF will work as expected. The MemoryBlock will be truncated when the BinaryStream is closed.

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.