-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBigStringWalker.lss
303 lines (246 loc) · 8.64 KB
/
BigStringWalker.lss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
%REM
Class BigStringWalker
Created Aug 17, 2017 by Sam Sirry
Description: Very fast Mid$() alternative for LotusScript for working with large Strings.
%END REM
Public Class BigStringWalker
Private Chars() As String
Private ElementSize As Long 'the minimum length of each array element, in order to contain the whole big string.
Private LenBSTotal As Long
Private HighestUsedIndex As Long 'the index of the last used element in the internal array (Chars).
'const:
Private MinBound As Long
Private MaxBound As Long
Sub New(BigString As String)
ReDim Chars(0 To 0)
MinBound = -32768
MaxBound = 32767
Call Load(BigString)
End Sub
Public Sub Load(BigString As String)
LenBSTotal = Len(BigString)
If LenBSTotal = 0 Then
ElementSize = 1
ReDim Chars(MinBound To MinBound)
HighestUsedIndex = MinBound
Exit Sub
End If
ElementSize = Fix(LenBSTotal/(MaxBound - MinBound +1))
If Fraction(LenBSTotal/(MaxBound - MinBound +1)) > 0 Then
ElementSize = ElementSize +1
End If
If ElementSize = 1 Then
ReDim Chars(MinBound To (MinBound + LenBSTotal - 1))
Else
ReDim Chars(MinBound To MaxBound)
End If
Dim LastIndex As Long
Lastindex = MinBound -1
Call Load_Internal(BigString, LastIndex, 1)
HighestUsedIndex = LastIndex
End Sub
%REM
Sub Load_Internal
Created Aug 17, 2017 by Sam Sirry
Description: Recursive proc for chunking the big string and filling up the array
%END REM
Private Sub Load_Internal(BigString As String, LastIndex As Long, ByVal HowDeep As Long)
'MinChunkSize determines at which length we stop recursive calling and start processing the part in hand using Mid$() in a loop.
' smaller number means more iterations and recursive calls, but less Mid$() calls.
' so, it's call-stack vs Mid$()-calls balance. If set too small, may cause an Out of Stack Space error.
Const MinChunkSize = 128
Dim LenBS As Long
Dim PartsCount
LenBS = Len(BigString)
If LenBS <= ElementSize Then GoTo SplitToChars
If LenBS <= MinChunkSize Then GoTo SplitToChars
If HowDeep < 1 Then HowDeep = 1
PartsCount = 8 * HowDeep
Dim Part As String
Dim PartSize As Long
Dim SizeInSingle As Single
SizeInSingle = LenBS/PartsCount
PartSize = Fix(SizeInSingle)
If Fraction(SizeInSingle) > 0 Then
PartSize = PartSize + 1
End If
Dim i As Long
Dim start As Long
start = 1
For i = 1 To PartsCount
If start > LenBS Then Exit For
Part = Mid$(BigString, start, PartSize)
Call Me.Load_Internal(Part, LastIndex, HowDeep +1)
Part = "" 'free mem
start = start + PartSize
Next
GoTo Quit
SplitToChars:
'When we're here BigString isn't really big anymore.
Part = BigString 'make a local copy to work on
Dim LenLastIndex As Long
Dim LenPart As Long
If LastIndex < MinBound Then
LenLastIndex = ElementSize
Else
LenLastIndex = Len(Chars(LastIndex))
End If
'fill up any remaining space in the last used array element.
LenPart = Len(Part)
If LenLastIndex < ElementSize Then
Dim remaining As Long
remaining = ElementSize-LenLastIndex
If LenPart > remaining Then
Chars(LastIndex) = Chars(LastIndex) & Left$(Part, ElementSize-LenLastIndex)
Part = Right$(Part, LenPart - (ElementSize-LenLastIndex))
LenPart = Len(Part)
Else
Chars(LastIndex) = Chars(LastIndex) & Part
Part = ""
LenPart = 0
End If
End If
If LenPart = 0 Then GoTo Quit
Dim PartsInSingle As Single
PartsInSingle = LenPart / ElementSize
PartsCount = Fix(LenPart / ElementSize)
If Fraction(PartsInSingle) > 0 Then
PartsCount = PartsCount + 1
End If
start = 1
For i = 1 To PartsCount
LastIndex = LastIndex + 1
Chars(LastIndex) = Mid$(Part, start, ElementSize)
start = start + ElementSize
Next
Quit:
End Sub
%REM
Property Get Size
Created Aug 17, 2017 by Sam Sirry
Description: The number of characters in the string.
%END REM
Public Property Get Size As Long
Size = LenBSTotal
End Property
%REM
Function FullString
Created Aug 17, 2017 by Sam Sirry
Description: Returns the full original string
%END REM
Public Function FullString As String
FullString = Join(Chars, "")
End Function
%REM
Function IsValidStringIndex
Created Aug 18, 2017 by Sam Sirry
Description:
%END REM
Private Function IsValidStringIndex(ByVal StringIndex As Long) As Boolean
If LenBSTotal = 0 And Stringindex = 0 Then
IsValidStringIndex = True
ElseIf ElementSize = 0 Then
IsValidStringIndex = False
ElseIf StringIndex < 0 Then
IsValidStringIndex = False
ElseIf StringIndex > (LenBSTotal-1) Then
IsValidStringIndex = False
Else
IsValidStringIndex = True
End If
End Function
%REM
Function ArrayIndexOfStringIndex
Created Aug 18, 2017 by Sam Sirry
Description: This proc does no validation of input. Caller must valdate before calling.
%END REM
Private Function ArrayIndexOfStringIndex(ByVal StringIndex As Long) As Long
ArrayIndexOfStringIndex = MinBound + Fix(StringIndex/ElementSize)
End Function
%REM
Function CharAt
Created Aug 17, 2017 by Sam Sirry
Description: Returns a single character ***Index is Zero-Based***
%END REM
Public Function CharAt(ByVal StringIndex As Long) As String
%REM
If LenBSTotal = 0 Then Exit Function
If index < 0 Then Error 5, "The index may not be negative."
%END REM
If Not IsValidStringIndex(StringIndex) Then
Error 5, "The StringIndex value is not valid, or this object's instance is not initialized."
Exit Function
End If
If StringIndex > LenBSTotal-1 Then Exit Function
If ElementSize = 1 Then
CharAt = Chars(ArrayIndexOfStringIndex(StringIndex))
Else
Dim charindex As Long
charindex = StringIndex Mod ElementSize
CharAt = Mid$(Chars(ArrayIndexOfStringIndex(StringIndex)), charindex+1, 1)
End If
End Function
%REM
Function SubString
Created Aug 18, 2017 by Sam Sirry
Description: Returns a part of the BigString. indexes are zero-based. when start index = end index the return is empty string.
%END REM
Public Function SubString(ByVal start_idx As Long, ByVal end_idx As Long) As String
Dim firstElement As Long
Dim lastElement As Long
If Not IsValidStringIndex(start_idx) Then
Error 5, "The start_idx value is not valid."
Exit Function
End If
If start_idx = end_idx Then Exit Function 'return empty string.
If end_idx > LenBSTotal Then
end_idx = LenBSTotal-1 +1 '+1 is added because when start=end the return is nothing.
End If
If end_idx < start_idx Then Error 5, "end_idx may not be smaller than start_idx." 'start_idx can't be past the bigstring length coz it's been validated earlier.
If end_idx - start_idx = 1 Then
SubString = Me.CharAt(start_idx)
End If
firstElement = ArrayIndexOfStringIndex(start_idx)
lastElement = ArrayIndexOfStringIndex(end_idx)
Dim charindex As Long
If firstElement = lastElement Then
charindex = start_idx Mod ElementSize
SubString = Mid$(Chars(firstElement), charindex+1, (end_idx - start_idx))
Else
Dim i As Long
'only use Buffer when necessary. This makes a big difference if the caller requests too many small substrings in a loop.
If lastElement - firstElement > 128 Then '128 wasn't selected with any imperical study. it's purely based on personal estimation.
Dim sb As Buffer
Set sb = New Buffer
charindex = start_idx Mod ElementSize
Call sb.append(Mid$(Chars(firstElement), charindex+1))
For i = (firstElement+1) To (lastElement-1)
Call sb.append(Chars(i))
Next
charindex = end_idx Mod ElementSize
If charindex > 0 Then Call sb.append(Left$(Chars(lastElement), charindex))
SubString = sb.toString()
Else
Dim s_out As String
charindex = start_idx Mod ElementSize
s_out = Mid$(Chars(firstElement), charindex+1)
For i = (firstElement+1) To (lastElement-1)
s_out = s_out & Chars(i)
Next
charindex = end_idx Mod ElementSize
If charindex > 0 Then s_out = s_out & Left$(Chars(lastElement), charindex)
SubString = s_out
End If
End If
End Function
%REM
Function Slice
Created Aug 18, 2017 by Sam Sirry
Description: Returns a part of the BigString. start index is zero-based. size is the number of characters to be returned.
%END REM
Public Function Slice(ByVal start_idx As Long, ByVal size As Long) As String
Dim end_idx As Long
end_idx = start_idx + size
Slice = SubString(start_idx, end_idx)
End Function
End Class