-
Notifications
You must be signed in to change notification settings - Fork 5
/
FastInt64.pas
369 lines (361 loc) · 13.3 KB
/
FastInt64.pas
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
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
unit FastInt64;
interface
function IntToStr32(Value: Integer): AnsiString;
function IntToStr64(Value: Int64): AnsiString;
implementation
const
TwoDigitLookup : packed array[0..99] of array[1..2] of Char =
('00','01','02','03','04','05','06','07','08','09',
'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');
function IntToStr32(Value: Integer): AnsiString;
asm
push ebx
push edi
push esi
mov ebx, eax {Value}
sar ebx, 31 {0 for +ve Value or -1 for -ve Value}
xor eax, ebx
sub eax, ebx {ABS(Value)}
mov esi, 10 {Max Digits in Result}
mov edi, edx {@Result}
cmp eax, 10
sbb esi, 0
cmp eax, 100
sbb esi, 0
cmp eax, 1000
sbb esi, 0
cmp eax, 10000
sbb esi, 0
cmp eax, 100000
sbb esi, 0
cmp eax, 1000000
sbb esi, 0
cmp eax, 10000000
sbb esi, 0
cmp eax, 100000000
sbb esi, 0
cmp eax, 1000000000
sbb esi, ebx {Digits (Including Sign Character)}
mov ecx, [edx] {Result}
test ecx, ecx
je @@NewStr {Create New String for Result}
cmp dword ptr [ecx-8], 1
jne @@ChangeStr {Reference Count <> 1}
cmp esi, [ecx-4]
je @@LengthOk {Existing Length = Required Length}
sub ecx, 8 {Allocation Address}
push eax {ABS(Value)}
push ecx
mov eax, esp
lea edx, [esi+9] {New Allocation Size}
call system.@ReallocMem {Reallocate Result String}
pop ecx
pop eax {ABS(Value)}
add ecx, 8 {Result}
mov [ecx-4], esi {Set New Length}
mov byte ptr [ecx+esi], 0 {Add Null Terminator}
mov [edi], ecx {Set Result Address}
jmp @@LengthOk
@@ChangeStr:
mov edx, dword ptr [ecx-8] {Reference Count}
add edx, 1
jz @@NewStr {RefCount = -1 (String Constant)}
lock dec dword ptr [ecx-8] {Decrement Existing Reference Count}
@@NewStr:
push eax {ABS(Value)}
mov eax, esi {Length}
call system.@NewAnsiString
mov [edi], eax {Set Result Address}
mov ecx, eax {Result}
pop eax {ABS(Value)}
@@LengthOk:
mov byte ptr [ecx], '-' {Store '-' Character (May be Overwritten)}
add esi, ebx {Digits (Excluding Sign Character)}
sub ecx, ebx {Destination of 1st Digit}
sub esi, 2 {Digits (Excluding Sign Character) - 2}
jle @@FinalDigits {1 or 2 Digit Value}
cmp esi, 8 {10 Digit Value?}
jne @@SetResult {Not a 10 Digit Value}
sub eax, 2000000000 {Digit 10 must be either '1' or '2'}
mov dl, '2'
jnc @@SetDigit10 {Digit 10 = '2'}
mov dl, '1' {Digit 10 = '1'}
add eax, 1000000000
@@SetDigit10:
mov [ecx], dl {Save Digit 10}
mov esi, 7 {9 Digits Remaining}
add ecx, 1 {Destination of 2nd Digit}
@@SetResult:
mov edi, $28F5C29 {((2^32)+100-1)/100}
@@Loop:
mov ebx, eax {Dividend}
mul edi {EDX = Dividend DIV 100}
mov eax, edx {Set Next Dividend}
imul edx, -200 {-2 * (100 * Dividend DIV 100)}
movzx edx, word ptr [TwoDigitLookup+ebx*2+edx] {Dividend MOD 100 in ASCII}
mov [ecx+esi], dx
sub esi, 2
jg @@Loop {Loop Until 1 or 2 Digits Remaining}
@@FinalDigits:
pop esi
pop edi
pop ebx
jnz @@LastDigit
movzx eax, word ptr [TwoDigitLookup+eax*2]
mov [ecx], ax {Save Final 2 Digits}
ret
@@LastDigit:
or al , '0' {Ascii Adjustment}
mov [ecx], al {Save Final Digit}
end;
function IntToStr64(Value: Int64): AnsiString;
asm
push ebx
mov ecx, [ebp+8] {Low Integer of Value}
mov edx, [ebp+12] {High Integer of Value}
xor ebp, ebp {Clear Sign Flag (EBP Already Pushed)}
mov ebx, ecx {Low Integer of Value}
test edx, edx
jnl @@AbsValue
mov ebp, 1 {EBP = 1 for -ve Value or 0 for +ve Value}
neg ecx
adc edx, 0
neg edx
@@AbsValue: {EDX:ECX = Abs(Value)}
jnz @@Large
test ecx, ecx
js @@Large
mov edx, eax {@Result}
mov eax, ebx {Low Integer of Value}
call IntToStr32 {Call Fastest Integer IntToStr Function}
pop ebx
@@Exit:
pop ebp {Restore Stack and Exit}
ret 8
@@Large:
push edi
push esi
mov edi, eax
xor ebx, ebx
xor eax, eax
@@Test15: {Test for 15 or More Digits}
cmp edx, $00005af3 {100000000000000 div $100000000}
jne @@Check15
cmp ecx, $107a4000 {100000000000000 mod $100000000}
@@Check15:
jb @@Test13
@@Test17: {Test for 17 or More Digits}
cmp edx, $002386f2 {10000000000000000 div $100000000}
jne @@Check17
cmp ecx, $6fc10000 {10000000000000000 mod $100000000}
@@Check17:
jb @@Test15or16
@@Test19: {Test for 19 Digits}
cmp edx, $0de0b6b3 {1000000000000000000 div $100000000}
jne @@Check19
cmp ecx, $a7640000 {1000000000000000000 mod $100000000}
@@Check19:
jb @@Test17or18
mov al, 19
jmp @@SetLength
@@Test17or18: {17 or 18 Digits}
mov bl, 18
cmp edx, $01634578 {100000000000000000 div $100000000}
jne @@SetLen
cmp ecx, $5d8a0000 {100000000000000000 mod $100000000}
jmp @@SetLen
@@Test15or16: {15 or 16 Digits}
mov bl, 16
cmp edx, $00038d7e {1000000000000000 div $100000000}
jne @@SetLen
cmp ecx, $a4c68000 {1000000000000000 mod $100000000}
jmp @@SetLen
@@Test13: {Test for 13 or More Digits}
cmp edx, $000000e8 {1000000000000 div $100000000}
jne @@Check13
cmp ecx, $d4a51000 {1000000000000 mod $100000000}
@@Check13:
jb @@Test11
@@Test13or14: {13 or 14 Digits}
mov bl, 14
cmp edx, $00000918 {10000000000000 div $100000000}
jne @@SetLen
cmp ecx, $4e72a000 {10000000000000 mod $100000000}
jmp @@SetLen
@@Test11: {10, 11 or 12 Digits}
cmp edx, $02 {10000000000 div $100000000}
jne @@Check11
cmp ecx, $540be400 {10000000000 mod $100000000}
@@Check11:
mov bl, 11
jb @@SetLen {10 Digits}
@@Test11or12: {11 or 12 Digits}
mov bl, 12
cmp edx, $17 {100000000000 div $100000000}
jne @@SetLen
cmp ecx, $4876e800 {100000000000 mod $100000000}
@@SetLen:
sbb eax, 0 {Adjust for Odd/Evem Digit Count}
add eax, ebx
@@SetLength: {Abs(Value) in EDX:ECX, Digits in EAX}
push ecx {Save Abs(Value)}
push edx
lea edx, [eax+ebp] {Digits Needed (Including Sign Character)}
mov ecx, [edi] {@Result}
mov esi, edx {Digits Needed (Including Sign Character)}
test ecx, ecx
je @@NewStr {Create New String for Result}
cmp dword ptr [ecx-8], 1
jne @@ChangeStr {Reference Count <> 1}
cmp esi, [ecx-4]
je @@LengthOk {Existing Length = Required Length}
sub ecx, 8 {Allocation Address}
push eax {ABS(Value)}
push ecx
mov eax, esp
lea edx, [esi+9] {New Allocation Size}
call system.@ReallocMem {Reallocate Result String}
pop ecx
pop eax {ABS(Value)}
add ecx, 8 {@Result}
mov [ecx-4], esi {Set New Length}
mov byte ptr [ecx+esi], 0 {Add Null Terminator}
mov [edi], ecx {Set Result Address}
jmp @@LengthOk
@@ChangeStr:
mov edx, dword ptr [ecx-8] {Reference Count}
add edx, 1
jz @@NewStr {RefCount = -1 (String Constant)}
lock dec dword ptr [ecx-8] {Decrement Existing Reference Count}
@@NewStr:
push eax {ABS(Value)}
mov eax, esi {Length}
call system.@NewAnsiString
mov [edi], eax {Set Result Address}
mov ecx, eax {@Result}
pop eax {ABS(Value)}
@@LengthOk:
mov edi, [edi] {@Result}
sub esi, ebp {Digits Needed (Excluding Sign Character)}
mov byte ptr [edi], '-' {Store '-' Character (May be Overwritten)}
add edi, ebp {Destination of 1st Digit}
pop edx {Restore Abs(Value)}
pop eax
cmp esi, 17
jl @@LessThan17Digits {Digits < 17}
je @@SetDigit17 {Digits = 17}
cmp esi, 18
je @@SetDigit18 {Digits = 18}
mov cl, '0' - 1
mov ebx, $a7640000 {1000000000000000000 mod $100000000}
mov ebp, $0de0b6b3 {1000000000000000000 div $100000000}
@@CalcDigit19:
add ecx, 1
sub eax, ebx
sbb edx, ebp
jnc @@CalcDigit19
add eax, ebx
adc edx, ebp
mov [edi], cl
add edi, 1
@@SetDigit18:
mov cl, '0' - 1
mov ebx, $5d8a0000 {100000000000000000 mod $100000000}
mov ebp, $01634578 {100000000000000000 div $100000000}
@@CalcDigit18:
add ecx, 1
sub eax, ebx
sbb edx, ebp
jnc @@CalcDigit18
add eax, ebx
adc edx, ebp
mov [edi], cl
add edi, 1
@@SetDigit17:
mov cl, '0' - 1
mov ebx, $6fc10000 {10000000000000000 mod $100000000}
mov ebp, $002386f2 {10000000000000000 div $100000000}
@@CalcDigit17:
add ecx, 1
sub eax, ebx
sbb edx, ebp
jnc @@CalcDigit17
add eax, ebx
adc edx, ebp
mov [edi], cl
add edi, 1 {Update Destination}
mov esi, 16 {Set 16 Digits Left}
@@LessThan17Digits: {Process Next 8 Digits}
mov ecx, 100000000 {EDX:EAX = Abs(Value) = Dividend}
div ecx
mov ebp, eax {Dividend DIV 100000000}
mov ebx, edx
mov eax, edx {Dividend MOD 100000000}
mov edx, $51EB851F
mul edx
shr edx, 5 {Dividend DIV 100}
mov eax, edx {Set Next Dividend}
lea edx, [edx*4+edx]
lea edx, [edx*4+edx]
shl edx, 2 {Dividend DIV 100 * 100}
sub ebx, edx {Remainder (0..99)}
movzx ebx, word ptr [TwoDigitLookup+ebx*2]
shl ebx, 16
mov edx, $51EB851F
mov ecx, eax {Dividend}
mul edx
shr edx, 5 {Dividend DIV 100}
mov eax, edx
lea edx, [edx*4+edx]
lea edx, [edx*4+edx]
shl edx, 2 {Dividend DIV 100 * 100}
sub ecx, edx {Remainder (0..99)}
or bx, word ptr [TwoDigitLookup+ecx*2]
mov [edi+esi-4], ebx {Store 4 Digits}
mov ebx, eax
mov edx, $51EB851F
mul edx
shr edx, 5 {EDX := Dividend DIV 100}
lea eax, [edx*4+edx]
lea eax, [eax*4+eax]
shl eax, 2 {EDX = Dividend DIV 100 * 100}
sub ebx, eax {Remainder (0..99)}
movzx ebx, word ptr [TwoDigitLookup+ebx*2]
movzx ecx, word ptr [TwoDigitLookup+edx*2]
shl ebx, 16
or ebx, ecx
mov [edi+esi-8], ebx {Store 4 Digits}
mov eax, ebp {Remainder}
sub esi, 10 {Digits Left - 2}
jz @@Last2Digits
@@SmallLoop: {Process Remaining Digits}
mov edx, $28F5C29 {((2^32)+100-1)/100}
mov ebx, eax {Dividend}
mul edx
mov eax, edx {Set Next Dividend}
imul edx, -200
movzx edx, word ptr [TwoDigitLookup+ebx*2+edx] {Dividend MOD 100 in ASCII}
mov [edi+esi], dx
sub esi, 2
jg @@SmallLoop {Repeat Until Less than 2 Digits Remaining}
jz @@Last2Digits
or al , '0' {Ascii Adjustment}
mov [edi], al {Save Final Digit}
jmp @@Done
@@Last2Digits:
movzx eax, word ptr [TwoDigitLookup+eax*2]
mov [edi], ax {Save Final 2 Digits}
@@Done:
pop esi
pop edi
pop ebx
end;
end.