Fastest Versus Safest
Bruce: OK, so we’ve optimized this code, and we’re going to get a giant performance benefit. Anything else?
Jane: What happens if you pass GetToken an empty string?
Bruce: Why would you do that?
Joe: Maybe just out of orneriness.
Jane: Or maybe you’re parsing a file a line at a time, and some lines are blank. I just want to know whether you’re handling boundaries.
Bruce: I guess I’m not sure what would happen. I suppose a user might also pass an empty string for the separators. What else could go wrong?
Archaeologist’s Note: An interminable discussion about error handling has been edited out. Here is the end of that discussion.
Bruce: OK, so we end up with this:
Function GetToken5(sTarget As String, sSeps As String) As String
‘ Note that sSave and iStart must be static from call to call
‘ If first call, make copy of string
Static sSave As String, iStart As Integer, cSave As Integer
‘ Assume failure
GetToken5 = sEmpty
If sTarget <> sEmpty Then
iStart = 1
sSave = sTarget
cSave = Len(sSave)
Else
If sSave = sEmpty Then Exit Function
End If
‘ Find start of next token
Dim iNew As Integer
iNew = StrSpan2(sSave, iStart, sSeps)
If iNew Then
‘ Set position to start of token
iStart = iNew
Else
‘ If no new token, return empty string
sSave = sEmpty
Exit Function
End If
‘ Find end of token
iNew = StrBreak2(sSave, iStart, sSeps)
If iNew = 0 Then
‘ If no end of token, set to end of string
iNew = cSave + 1
End If
‘ Cut token out of sTarget string
GetToken5 = Mid$(sSave, iStart, iNew - iStart)
‘ Set new starting position
iStart = iNew
End Function
Bruce: But isn’t this error-handling code going to slow us down?
Joe: So what?
Jane: I never thought I’d hear you say that.
Joe: Well, speed is important, but it isn’t everything. If all I wanted was speed, I could write GetToken like this:
Function GetToken(sTarget As String, sSeps As String) As String
GetToken = sEmpty
End Function
That would really be fast. I could parse huge files in no time at all.
Bruce: But it doesn’t work.
Joe: It works as well as the original if you pass it invalid arguments—better, in some cases.
Mary: OK, OK. We get the point.
Joe: It doesn’t matter how fast your code is if it doesn’t work.
Archaeologist’s Note: At this point, the code review broke down into arguments about language features and proposals for enhancements. Suffice it to say that the discussion resulted in at least one variation of GetToken, named GetQToken, which recognized each quoted string as a single token. Another version, named GetOptToken, was discussed but not implemented. It would have recognized command-line options initiated with the forward slash or the hyphen character, skipped the option characters, and returned the value of the argument. You can find modern translations of the parsing routines (including GetQToken) in PARSE.BAS. The global class PARSE.CLS in the VBCore component delegates its implementation to PARSE.BAS. The parsing functions are used in several samples, including GLOBWIZ.VBP.