Skip to content

Commit 17ceebe

Browse files
PGS62timhall
authored andcommitted
json_BufferAppend replaced with clsStringAppend
Replaced calls to json_BufferAppend with simple class module clsStringAppend. Code is faster (x5), simpler and should work on Mac as well as Windows, though I have not tested that...
1 parent 0af3e4d commit 17ceebe

File tree

4 files changed

+308
-141
lines changed

4 files changed

+308
-141
lines changed

JsonConverter.bas

Lines changed: 43 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -140,18 +140,6 @@ End Type
140140
#End If
141141
' === End VBA-UTC
142142

143-
#If Mac Then
144-
#ElseIf VBA7 Then
145-
146-
Private Declare PtrSafe Sub json_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
147-
(json_MemoryDestination As Any, json_MemorySource As Any, ByVal json_ByteLength As Long)
148-
149-
#Else
150-
151-
Private Declare Sub json_CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _
152-
(json_MemoryDestination As Any, json_MemorySource As Any, ByVal json_ByteLength As Long)
153-
154-
#End If
155143

156144
Private Type json_Options
157145
' VBA only stores 15 significant digits, so any numbers larger than that are truncated
@@ -210,9 +198,7 @@ End Function
210198
' @return {String}
211199
''
212200
Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitespace As Variant, Optional ByVal json_CurrentIndentation As Long = 0) As String
213-
Dim json_buffer As String
214-
Dim json_BufferPosition As Long
215-
Dim json_BufferLength As Long
201+
Dim cSA As New clsStringAppend
216202
Dim json_Index As Long
217203
Dim json_LBound As Long
218204
Dim json_UBound As Long
@@ -271,7 +257,7 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
271257
End If
272258

273259
' Array
274-
json_BufferAppend json_buffer, "[", json_BufferPosition, json_BufferLength
260+
cSA.Append "["
275261

276262
On Error Resume Next
277263

@@ -286,21 +272,21 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
286272
json_IsFirstItem = False
287273
Else
288274
' Append comma to previous line
289-
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
275+
cSA.Append ","
290276
End If
291277

292278
If json_LBound2D >= 0 And json_UBound2D >= 0 Then
293279
' 2D Array
294280
If json_PrettyPrint Then
295-
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
281+
cSA.Append vbNewLine
296282
End If
297-
json_BufferAppend json_buffer, json_Indentation & "[", json_BufferPosition, json_BufferLength
283+
cSA.Append json_Indentation & "["
298284

299285
For json_Index2D = json_LBound2D To json_UBound2D
300286
If json_IsFirstItem2D Then
301287
json_IsFirstItem2D = False
302288
Else
303-
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
289+
cSA.Append ","
304290
End If
305291

306292
json_Converted = ConvertToJson(JsonValue(json_Index, json_Index2D), Whitespace, json_CurrentIndentation + 2)
@@ -317,14 +303,14 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
317303
json_Converted = vbNewLine & json_InnerIndentation & json_Converted
318304
End If
319305

320-
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
306+
cSA.Append json_Converted
321307
Next json_Index2D
322308

323309
If json_PrettyPrint Then
324-
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
310+
cSA.Append vbNewLine
325311
End If
326312

327-
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
313+
cSA.Append json_Indentation & "]"
328314
json_IsFirstItem2D = True
329315
Else
330316
' 1D Array
@@ -342,15 +328,15 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
342328
json_Converted = vbNewLine & json_Indentation & json_Converted
343329
End If
344330

345-
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
331+
cSA.Append json_Converted
346332
End If
347333
Next json_Index
348334
End If
349335

350336
On Error GoTo 0
351337

352338
If json_PrettyPrint Then
353-
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
339+
cSA.Append vbNewLine
354340

355341
If VBA.VarType(Whitespace) = VBA.vbString Then
356342
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
@@ -359,9 +345,9 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
359345
End If
360346
End If
361347

362-
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
348+
cSA.Append json_Indentation & "]"
363349

364-
ConvertToJson = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
350+
ConvertToJson = cSA.Report
365351

366352
' Dictionary or Collection
367353
Case VBA.vbObject
@@ -375,7 +361,7 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
375361

376362
' Dictionary
377363
If VBA.TypeName(JsonValue) = "Dictionary" Then
378-
json_BufferAppend json_buffer, "{", json_BufferPosition, json_BufferLength
364+
cSA.Append "{"
379365
For Each json_Key In JsonValue.Keys
380366
' For Objects, undefined (Empty/Nothing) is not added to object
381367
json_Converted = ConvertToJson(JsonValue(json_Key), Whitespace, json_CurrentIndentation + 1)
@@ -389,7 +375,7 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
389375
If json_IsFirstItem Then
390376
json_IsFirstItem = False
391377
Else
392-
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
378+
cSA.Append ","
393379
End If
394380

395381
If json_PrettyPrint Then
@@ -398,12 +384,12 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
398384
json_Converted = """" & json_Key & """:" & json_Converted
399385
End If
400386

401-
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
387+
cSA.Append json_Converted
402388
End If
403389
Next json_Key
404390

405391
If json_PrettyPrint Then
406-
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
392+
cSA.Append vbNewLine
407393

408394
If VBA.VarType(Whitespace) = VBA.vbString Then
409395
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
@@ -412,16 +398,16 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
412398
End If
413399
End If
414400

415-
json_BufferAppend json_buffer, json_Indentation & "}", json_BufferPosition, json_BufferLength
401+
cSA.Append json_Indentation & "}"
416402

417403
' Collection
418404
ElseIf VBA.TypeName(JsonValue) = "Collection" Then
419-
json_BufferAppend json_buffer, "[", json_BufferPosition, json_BufferLength
405+
cSA.Append "["
420406
For Each json_Value In JsonValue
421407
If json_IsFirstItem Then
422408
json_IsFirstItem = False
423409
Else
424-
json_BufferAppend json_buffer, ",", json_BufferPosition, json_BufferLength
410+
cSA.Append ","
425411
End If
426412

427413
json_Converted = ConvertToJson(json_Value, Whitespace, json_CurrentIndentation + 1)
@@ -438,11 +424,11 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
438424
json_Converted = vbNewLine & json_Indentation & json_Converted
439425
End If
440426

441-
json_BufferAppend json_buffer, json_Converted, json_BufferPosition, json_BufferLength
427+
cSA.Append json_Converted
442428
Next json_Value
443429

444430
If json_PrettyPrint Then
445-
json_BufferAppend json_buffer, vbNewLine, json_BufferPosition, json_BufferLength
431+
cSA.Append vbNewLine
446432

447433
If VBA.VarType(Whitespace) = VBA.vbString Then
448434
json_Indentation = VBA.String$(json_CurrentIndentation, Whitespace)
@@ -451,10 +437,10 @@ Public Function ConvertToJson(ByVal JsonValue As Variant, Optional ByVal Whitesp
451437
End If
452438
End If
453439

454-
json_BufferAppend json_buffer, json_Indentation & "]", json_BufferPosition, json_BufferLength
440+
cSA.Append json_Indentation & "]"
455441
End If
456442

457-
ConvertToJson = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
443+
ConvertToJson = cSA.Report
458444
Case VBA.vbInteger, VBA.vbLong, VBA.vbSingle, VBA.vbDouble, VBA.vbCurrency, VBA.vbDecimal
459445
' Number (use decimals for numbers)
460446
ConvertToJson = VBA.Replace(JsonValue, ",", ".")
@@ -558,9 +544,7 @@ Private Function json_ParseString(json_String As String, ByRef json_Index As Lon
558544
Dim json_Quote As String
559545
Dim json_Char As String
560546
Dim json_Code As String
561-
Dim json_buffer As String
562-
Dim json_BufferPosition As Long
563-
Dim json_BufferLength As Long
547+
Dim cSA As New clsStringAppend
564548

565549
json_SkipSpaces json_String, json_Index
566550

@@ -579,36 +563,36 @@ Private Function json_ParseString(json_String As String, ByRef json_Index As Lon
579563

580564
Select Case json_Char
581565
Case """", "\", "/", "'"
582-
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
566+
cSA.Append json_Char
583567
json_Index = json_Index + 1
584568
Case "b"
585-
json_BufferAppend json_buffer, vbBack, json_BufferPosition, json_BufferLength
569+
cSA.Append vbBack
586570
json_Index = json_Index + 1
587571
Case "f"
588-
json_BufferAppend json_buffer, vbFormFeed, json_BufferPosition, json_BufferLength
572+
cSA.Append vbFormFeed
589573
json_Index = json_Index + 1
590574
Case "n"
591-
json_BufferAppend json_buffer, vbCrLf, json_BufferPosition, json_BufferLength
575+
cSA.Append vbCrLf
592576
json_Index = json_Index + 1
593577
Case "r"
594-
json_BufferAppend json_buffer, vbCr, json_BufferPosition, json_BufferLength
578+
cSA.Append vbCr
595579
json_Index = json_Index + 1
596580
Case "t"
597-
json_BufferAppend json_buffer, vbTab, json_BufferPosition, json_BufferLength
581+
cSA.Append vbTab
598582
json_Index = json_Index + 1
599583
Case "u"
600584
' Unicode character escape (e.g. \u00a9 = Copyright)
601585
json_Index = json_Index + 1
602586
json_Code = VBA.Mid$(json_String, json_Index, 4)
603-
json_BufferAppend json_buffer, VBA.ChrW(VBA.Val("&h" + json_Code)), json_BufferPosition, json_BufferLength
587+
cSA.Append VBA.ChrW(VBA.Val("&h" + json_Code))
604588
json_Index = json_Index + 4
605589
End Select
606590
Case json_Quote
607-
json_ParseString = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
591+
json_ParseString = cSA.Report
608592
json_Index = json_Index + 1
609593
Exit Function
610594
Case Else
611-
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
595+
cSA.Append json_Char
612596
json_Index = json_Index + 1
613597
End Select
614598
Loop
@@ -694,9 +678,7 @@ Private Function json_Encode(ByVal json_Text As Variant) As String
694678
Dim json_Index As Long
695679
Dim json_Char As String
696680
Dim json_AscCode As Long
697-
Dim json_buffer As String
698-
Dim json_BufferPosition As Long
699-
Dim json_BufferLength As Long
681+
Dim cSA As New clsStringAppend
700682

701683
For json_Index = 1 To VBA.Len(json_Text)
702684
json_Char = VBA.Mid$(json_Text, json_Index, 1)
@@ -743,10 +725,10 @@ Private Function json_Encode(ByVal json_Text As Variant) As String
743725
json_Char = "\u" & VBA.Right$("0000" & VBA.Hex$(json_AscCode), 4)
744726
End Select
745727

746-
json_BufferAppend json_buffer, json_Char, json_BufferPosition, json_BufferLength
728+
cSA.Append json_Char
747729
Next json_Index
748730

749-
json_Encode = json_BufferToString(json_buffer, json_BufferPosition, json_BufferLength)
731+
json_Encode = cSA.Report
750732
End Function
751733

752734
Private Function json_Peek(json_String As String, ByVal json_Index As Long, Optional json_NumberOfCharacters As Long = 1) As String
@@ -819,93 +801,7 @@ Private Function json_ParseErrorMessage(json_String As String, ByRef json_Index
819801
ErrorMessage
820802
End Function
821803

822-
Private Sub json_BufferAppend(ByRef json_buffer As String, _
823-
ByRef json_Append As Variant, _
824-
ByRef json_BufferPosition As Long, _
825-
ByRef json_BufferLength As Long)
826-
#If Mac Then
827-
json_buffer = json_buffer & json_Append
828-
#Else
829-
' VBA can be slow to append strings due to allocating a new string for each append
830-
' Instead of using the traditional append, allocate a large empty string and then copy string at append position
831-
'
832-
' Example:
833-
' Buffer: "abc "
834-
' Append: "def"
835-
' Buffer Position: 3
836-
' Buffer Length: 5
837-
'
838-
' Buffer position + Append length > Buffer length -> Append chunk of blank space to buffer
839-
' Buffer: "abc "
840-
' Buffer Length: 10
841-
'
842-
' Copy memory for "def" into buffer at position 3 (0-based)
843-
' Buffer: "abcdef "
844-
'
845-
' Approach based on cStringBuilder from vbAccelerator
846-
' http://www.vbaccelerator.com/home/VB/Code/Techniques/RunTime_Debug_Tracing/VB6_Tracer_Utility_zip_cStringBuilder_cls.asp
847-
848-
Dim json_AppendLength As Long
849-
Dim json_LengthPlusPosition As Long
850-
851-
json_AppendLength = VBA.LenB(json_Append)
852-
json_LengthPlusPosition = json_AppendLength + json_BufferPosition
853-
854-
If json_LengthPlusPosition > json_BufferLength Then
855-
' Appending would overflow buffer, add chunks until buffer is long enough
856-
Dim json_TemporaryLength As Long
857-
858-
json_TemporaryLength = json_BufferLength
859-
Do While json_TemporaryLength < json_LengthPlusPosition
860-
' Initially, initialize string with 255 characters,
861-
' then add large chunks (8192) after that
862-
'
863-
' Size: # Characters x 2 bytes / character
864-
If json_TemporaryLength = 0 Then
865-
json_TemporaryLength = json_TemporaryLength + 510
866-
Else
867-
json_TemporaryLength = json_TemporaryLength + 16384
868-
End If
869-
Loop
870-
871-
json_buffer = json_buffer & VBA.Space$((json_TemporaryLength - json_BufferLength) \ 2)
872-
json_BufferLength = json_TemporaryLength
873-
End If
874-
875-
' Copy memory from append to buffer at buffer position
876-
json_CopyMemory ByVal json_UnsignedAdd(StrPtr(json_buffer), _
877-
json_BufferPosition), _
878-
ByVal StrPtr(json_Append), _
879-
json_AppendLength
880-
881-
json_BufferPosition = json_BufferPosition + json_AppendLength
882-
#End If
883-
End Sub
884-
885-
Private Function json_BufferToString(ByRef json_buffer As String, ByVal json_BufferPosition As Long, ByVal json_BufferLength As Long) As String
886-
#If Mac Then
887-
json_BufferToString = json_buffer
888-
#Else
889-
If json_BufferPosition > 0 Then
890-
json_BufferToString = VBA.Left$(json_buffer, json_BufferPosition \ 2)
891-
End If
892-
#End If
893-
End Function
894-
895-
#If VBA7 Then
896-
Private Function json_UnsignedAdd(json_Start As LongPtr, json_Increment As Long) As LongPtr
897-
#Else
898-
Private Function json_UnsignedAdd(json_Start As Long, json_Increment As Long) As Long
899-
#End If
900804

901-
If json_Start And &H80000000 Then
902-
json_UnsignedAdd = json_Start + json_Increment
903-
ElseIf (json_Start Or &H80000000) < -json_Increment Then
904-
json_UnsignedAdd = json_Start + json_Increment
905-
Else
906-
json_UnsignedAdd = (json_Start + &H80000000) + (json_Increment + &H80000000)
907-
End If
908-
End Function
909805

910806
''
911807
' VBA-UTC v1.0.5
@@ -1169,3 +1065,9 @@ Private Function utc_SystemTimeToDate(utc_Value As utc_SYSTEMTIME) As Date
11691065
End Function
11701066

11711067
#End If
1068+
1069+
1070+
1071+
1072+
1073+

0 commit comments

Comments
 (0)