In the process of trying to build a serializable data structure, I found myself building large strings, which gets very slow because VBA copies a string every time concatenation is performed.To alleviate this, referring to Dynamic Array and Java's StringBuilder interface, I cobbled together a unicode clsStringBuilder class.This isn't a very big chunk of code, but I'd be interested in any advice about edge cases that I maybe haven't considered, unexpected copying behavior that VBA might be performing behind my back which I could avoid, or corrections to coding style (or lack thereof).Option Compare DatabaseOption Explicit'******'* v2 *'******Private Declare Sub CopyMemory Lib kernel32.dll Alias RtlMoveMemory (ByVal dst As Long, ByVal src As Long, ByVal Length As Long)Private Const DEFAULT_CAPACITY As Long = &H10Private m_currLen As LongPrivate m_stringBuffer() As BytePrivate Sub Class_Initialize()    ReDim m_stringBuffer(0 To (DEFAULT_CAPACITY * 2) - 1) 'Each unicode character is 2 bytesEnd SubPublic Function Append(strString As String) As clsStringBuilderOn Error GoTo derp    If m_currLen + LenB(strString) < UBound(m_stringBuffer) Then        CopyMemory VarPtr(m_stringBuffer(m_currLen)), StrPtr(strString), LenB(strString)    Else        If m_currLen + LenB(strString) < UBound(m_stringBuffer) * 2 Then            Expand        Else            Expand m_currLen + LenB(strString)        End If        CopyMemory VarPtr(m_stringBuffer(m_currLen)), StrPtr(strString), LenB(strString)    End If    m_currLen = m_currLen + LenB(strString)    Set Append = Me    Exit Functionderp:    Stop    ResumeEnd FunctionPublic Property Get Length() As Long    Length = m_currLen / 2End PropertyPublic Property Get Capacity() As Long    Capacity = UBound(m_stringBuffer)End PropertyPrivate Sub Expand(Optional newSize As Long = 0)    If newSize <> 0 Then        ReDim Preserve m_stringBuffer(0 To newSize - 1)    Else        ReDim Preserve m_stringBuffer(0 To (UBound(m_stringBuffer) * 2) + 1)    End IfEnd SubPublic Function toString() As String    toString = Mid(m_stringBuffer, 1, m_currLen / 2)End FunctionHere is a test:Public Sub Main()    Dim sb As clsStringBuilder    Set sb = New clsStringBuilder    Dim strString As String    Dim i As Long    Dim StartTime As Double    'VBA String    StartTime = MicroTimer()    For i = 0 To 100000        strString = strString + Hello World;    Next    Debug.Print The VBA String took:  & Round(MicroTimer - StartTime, 3) &  seconds    'StringBuilder    StartTime = MicroTimer()    For i = 0 To 100000        sb.Append Hello World;    Next    Debug.Print The Stringbuilder took:  & Round(MicroTimer - StartTime, 3) &  seconds    'Are the strings the same?    Debug.Print StrComp(strString, sb.toString, vbBinaryCompare)End SubHere is Microsoft's MicroTimer function, which can be found here:Private Declare Function getFrequency Lib kernel32 _Alias QueryPerformanceFrequency (cyFrequency As Currency) As LongPrivate Declare Function getTickCount Lib kernel32 _Alias QueryPerformanceCounter (cyTickCount As Currency) As LongFunction MicroTimer() As Double'' Returns seconds.    Dim cyTicks1 As Currency    Static cyFrequency As Currency    '    MicroTimer = 0' Get frequency.    If cyFrequency = 0 Then getFrequency cyFrequency' Get ticks.    getTickCount cyTicks1' Seconds    If cyFrequency Then MicroTimer = cyTicks1 / cyFrequencyEnd Function
A lightning-fast StringBuilder
strings;vba
I like this, a lot. It's.. brilliant. Like .NET String objects, VBA strings are immutable, which means like in .NET, when the quick brown fox gets appended with jumps over and then the lazy dog, it's 4 strings that have been generated, and thus the first one got copied 3 times; a VBA StringBuilder class is therefore definitely welcome to any VBA toolkit!This is some serious code you've got here. Let's take a look.So you've called the class clsStringBuilder. I know where you're coming from, but there's no real reason for this cls Hungarian prefix - I'd remove it, and call the class StringBuilder.'******'* v2 *'******Don't bother with that. I know version control is natively near-impossible with VBA, but there is no need to version code in comments nonetheless; do you actually maintain the version number? Why bother? Just remove it, it's useless clutter.Private Const DEFAULT_CAPACITY As Long = &H10Why not 16? Hexadecimal 10 is 16 right? I think it would be clearer to use a decimal notation. Actually that capacity is potentially confusing, especially given the hex notation. Is &H10 the number of bytes? Characters?ReDim m_stringBuffer(0 To (DEFAULT_CAPACITY * 2) - 1) 'Each unicode character is 2 bytesAh, characters then. How about calling the constant DEFAULT_CHARACTER_CAPACITY? Nah, too long.. and I personally don't like the YELLCASE, I'd just call it InitialCharacterCapacity, but I've seen other people use ALL CAPS for constants - as long as you're consistent, it works :)BTW that's a good comment you have there, but I wouldn't bother specifying unicode characters; it gets confusing when the IDE itself only supports ANSI strings!I don't like prefixes and abbreviated names, so m_currLen would become currentLength and m_stringBuffer would become stringBufffer, or just buffer.Actually since currentLength is in bytes, I'd call it currentByteLength, so as to avoid question marks when it comes to this:Public Property Get Length() As Long    Length = m_currLen / 2End PropertyPublic Function Append(strString As String) As clsStringBuilderstrString, really? I doesn't get any more Hungarian than that! Also you should know that parameters are passed ByRef by default - I'd change the signature to this:Public Function Append(ByVal value As String) As StringBuilderThe error handling isn't optimal - if anything blows up, you're bringing up the IDE for the end user to scratch their head and debug your code! That's not production-ready:derp:    Stop    Resumederp means nothing to me - I like following a template like this:Public Sub Foo()    On Error GoTo CleanFail    'implementationCleanExit:    Exit SubCleanFail:    'handle error    Resume CleanExitEnd SubYou might also want to make sure Expand doesn't actually shrink the buffer. I think. ;)Lastly, I'm not sure I understand why toString isn't following the convention and named in PascalCase like every public method - ToString would be better-looking.Good job!