@grain/stdlib 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/array.gr +60 -57
- package/array.md +24 -6
- package/buffer.gr +71 -1
- package/buffer.md +142 -0
- package/bytes.gr +52 -3
- package/bytes.md +117 -0
- package/char.gr +21 -18
- package/char.md +18 -3
- package/immutablepriorityqueue.gr +13 -13
- package/int32.gr +39 -37
- package/int32.md +6 -0
- package/int64.gr +39 -37
- package/int64.md +6 -0
- package/list.gr +31 -22
- package/list.md +39 -10
- package/map.gr +19 -28
- package/number.gr +81 -5
- package/number.md +64 -2
- package/option.gr +30 -26
- package/option.md +12 -0
- package/package.json +1 -1
- package/path.gr +787 -0
- package/path.md +727 -0
- package/pervasives.gr +3 -4
- package/pervasives.md +6 -1
- package/queue.gr +11 -9
- package/queue.md +2 -0
- package/regex.gr +76 -3
- package/regex.md +70 -0
- package/result.gr +24 -20
- package/result.md +12 -0
- package/runtime/atof/common.gr +198 -0
- package/runtime/atof/common.md +243 -0
- package/runtime/atof/decimal.gr +663 -0
- package/runtime/atof/decimal.md +59 -0
- package/runtime/atof/lemire.gr +264 -0
- package/runtime/atof/lemire.md +6 -0
- package/runtime/atof/parse.gr +615 -0
- package/runtime/atof/parse.md +12 -0
- package/runtime/atof/slow.gr +238 -0
- package/runtime/atof/slow.md +6 -0
- package/runtime/atof/table.gr +2016 -0
- package/runtime/atof/table.md +12 -0
- package/runtime/{stringUtils.gr → atoi/parse.gr} +1 -1
- package/runtime/{stringUtils.md → atoi/parse.md} +1 -1
- package/runtime/bigint.gr +3 -3
- package/runtime/equal.gr +1 -1
- package/runtime/numberUtils.gr +2 -2
- package/runtime/numberUtils.md +6 -0
- package/runtime/numbers.gr +4 -4
- package/runtime/unsafe/conv.gr +21 -41
- package/runtime/unsafe/conv.md +0 -3
- package/runtime/unsafe/printWasm.gr +4 -40
- package/runtime/utils/printing.gr +3 -3
- package/stack.gr +4 -2
- package/stack.md +2 -0
- package/string.gr +1 -1
- package/sys/time.gr +4 -4
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
// This module was based on Rust's dec2flt
|
|
2
|
+
// https://github.com/rust-lang/rust/blob/1cbc45942d5c0f6eb5d94e3b10762ba541958035/library/core/src/num/dec2flt/parse.rs
|
|
3
|
+
// Rust's MIT license is provided below:
|
|
4
|
+
/*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any
|
|
6
|
+
* person obtaining a copy of this software and associated
|
|
7
|
+
* documentation files (the "Software"), to deal in the
|
|
8
|
+
* Software without restriction, including without
|
|
9
|
+
* limitation the rights to use, copy, modify, merge,
|
|
10
|
+
* publish, distribute, sublicense, and/or sell copies of
|
|
11
|
+
* the Software, and to permit persons to whom the Software
|
|
12
|
+
* is furnished to do so, subject to the following
|
|
13
|
+
* conditions:
|
|
14
|
+
*
|
|
15
|
+
* The above copyright notice and this permission notice
|
|
16
|
+
* shall be included in all copies or substantial portions
|
|
17
|
+
* of the Software.
|
|
18
|
+
*
|
|
19
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
20
|
+
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
|
21
|
+
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
|
22
|
+
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
|
23
|
+
* SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
24
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
25
|
+
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
26
|
+
* IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
27
|
+
* DEALINGS IN THE SOFTWARE.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import WasmI32 from "runtime/unsafe/wasmi32"
|
|
31
|
+
import WasmI64 from "runtime/unsafe/wasmi64"
|
|
32
|
+
import WasmF64 from "runtime/unsafe/wasmf64"
|
|
33
|
+
import { newInt32, newInt64, newFloat64 } from "runtime/dataStructures"
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
_CHAR_CODE_UNDERSCORE,
|
|
37
|
+
_CHAR_CODE_PLUS,
|
|
38
|
+
_CHAR_CODE_MINUS,
|
|
39
|
+
_CHAR_CODE_DOT,
|
|
40
|
+
_CHAR_CODE_0,
|
|
41
|
+
_CHAR_CODE_E,
|
|
42
|
+
_CHAR_CODE_e,
|
|
43
|
+
_CHAR_CODE_A,
|
|
44
|
+
_CHAR_CODE_Z,
|
|
45
|
+
_CHAR_CODE_a,
|
|
46
|
+
_CHAR_CODE_f,
|
|
47
|
+
_CHAR_CODE_i,
|
|
48
|
+
_CHAR_CODE_n,
|
|
49
|
+
_CHAR_CODE_t,
|
|
50
|
+
_CHAR_CODE_y,
|
|
51
|
+
_MIN_EXPONENT_FAST_PATH,
|
|
52
|
+
_MAX_EXPONENT_DISGUISED_FAST_PATH,
|
|
53
|
+
_MAX_EXPONENT_FAST_PATH,
|
|
54
|
+
_MAX_MANTISSA_FAST_PATH,
|
|
55
|
+
is8Digits,
|
|
56
|
+
getPowers10,
|
|
57
|
+
getPowers10FastPath,
|
|
58
|
+
biasedFpToNumber,
|
|
59
|
+
fpNan,
|
|
60
|
+
fpInf,
|
|
61
|
+
} from "./common"
|
|
62
|
+
|
|
63
|
+
import { computeFloat } from "./lemire"
|
|
64
|
+
import { parseLongMantissa } from "./slow"
|
|
65
|
+
|
|
66
|
+
// Try to parse 8 digits at a time:
|
|
67
|
+
// https://johnnylee-sde.github.io/Fast-numeric-string-to-int/
|
|
68
|
+
@unsafe
|
|
69
|
+
let parse8Digits = (digits: WasmI64) => {
|
|
70
|
+
let (+) = WasmI64.add
|
|
71
|
+
let (-) = WasmI64.sub
|
|
72
|
+
let (*) = WasmI64.mul
|
|
73
|
+
let (>>) = WasmI64.shrU
|
|
74
|
+
let (&) = WasmI64.and
|
|
75
|
+
let (|) = WasmI64.or
|
|
76
|
+
let (==) = WasmI64.eq
|
|
77
|
+
|
|
78
|
+
let _MASK = 0x0000_00FF_0000_00FFN
|
|
79
|
+
|
|
80
|
+
let a = digits - 0x3030_3030_3030_3030N
|
|
81
|
+
let b = a * 10N + (a >> 8N)
|
|
82
|
+
let c = (b & _MASK) * 0x000F_4240_0000_0064N
|
|
83
|
+
let d = (b >> 16N & _MASK) * 0x0000_2710_0000_0001N
|
|
84
|
+
|
|
85
|
+
(c + d) >> 32N
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@unsafe
|
|
89
|
+
let parseScientificExponent = (ptr: WasmI32, offset: WasmI32, len: WasmI32) => {
|
|
90
|
+
let (+) = WasmI32.add
|
|
91
|
+
let (-) = WasmI32.sub
|
|
92
|
+
let (>=) = WasmI32.geU
|
|
93
|
+
let (<) = WasmI32.ltU
|
|
94
|
+
let (==) = WasmI32.eq
|
|
95
|
+
let (!=) = WasmI32.ne
|
|
96
|
+
|
|
97
|
+
let mut offset = offset
|
|
98
|
+
let mut exponent = 0n
|
|
99
|
+
let mut negative = false
|
|
100
|
+
|
|
101
|
+
// Parse the optional leading sign
|
|
102
|
+
match (WasmI32.load8U(ptr, offset)) {
|
|
103
|
+
fst when fst == _CHAR_CODE_PLUS => {
|
|
104
|
+
offset += 1n
|
|
105
|
+
negative = false
|
|
106
|
+
},
|
|
107
|
+
fst when fst == _CHAR_CODE_MINUS => {
|
|
108
|
+
offset += 1n
|
|
109
|
+
negative = true
|
|
110
|
+
},
|
|
111
|
+
_ => void,
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (offset >= len) {
|
|
115
|
+
Err("Invalid exponent")
|
|
116
|
+
} else {
|
|
117
|
+
// Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
|
|
118
|
+
while (offset < len) {
|
|
119
|
+
let char = WasmI32.load8U(ptr, offset)
|
|
120
|
+
let digit = char - 0x30n
|
|
121
|
+
if (digit >= 0n && digit < 10n) {
|
|
122
|
+
if (WasmI32.ltS(exponent, 0x10000n)) {
|
|
123
|
+
exponent = WasmI32.add(digit, WasmI32.mul(10n, exponent))
|
|
124
|
+
}
|
|
125
|
+
offset += 1n
|
|
126
|
+
} else if (char == _CHAR_CODE_UNDERSCORE) {
|
|
127
|
+
offset += 1n
|
|
128
|
+
} else {
|
|
129
|
+
break
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (offset != len) {
|
|
133
|
+
Err("Invalid exponent")
|
|
134
|
+
} else if (negative) {
|
|
135
|
+
Ok(WasmI32.toGrain(newInt32(WasmI32.sub(0n, exponent))): Int32)
|
|
136
|
+
} else {
|
|
137
|
+
Ok(WasmI32.toGrain(newInt32(exponent)): Int32)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@unsafe
|
|
143
|
+
let parseFloatToParts = (string: String) => {
|
|
144
|
+
let (+) = WasmI32.add
|
|
145
|
+
let (-) = WasmI32.sub
|
|
146
|
+
let (>) = WasmI32.gtU
|
|
147
|
+
let (>=) = WasmI32.geU
|
|
148
|
+
let (<) = WasmI32.ltU
|
|
149
|
+
let (==) = WasmI32.eq
|
|
150
|
+
let (!=) = WasmI32.ne
|
|
151
|
+
|
|
152
|
+
let ptr = WasmI32.fromGrain(string)
|
|
153
|
+
match (WasmI32.load(ptr, 4n)) {
|
|
154
|
+
// Invalid string
|
|
155
|
+
0n => Err("Invalid string"),
|
|
156
|
+
// Continue to parse the string
|
|
157
|
+
len => {
|
|
158
|
+
let ptr = ptr + 8n
|
|
159
|
+
|
|
160
|
+
let mut i = 0n
|
|
161
|
+
let mut mantissa = 0N
|
|
162
|
+
let mut exponent = 0n
|
|
163
|
+
let mut numDigits = 0n
|
|
164
|
+
let mut numDigitsAfterDot = 0n
|
|
165
|
+
let mut manyDigits = false
|
|
166
|
+
|
|
167
|
+
// Parse the optional leading sign
|
|
168
|
+
let mut negative = false
|
|
169
|
+
match (WasmI32.load8U(ptr, i)) {
|
|
170
|
+
fst when fst == _CHAR_CODE_PLUS => {
|
|
171
|
+
i += 1n
|
|
172
|
+
negative = false
|
|
173
|
+
},
|
|
174
|
+
fst when fst == _CHAR_CODE_MINUS => {
|
|
175
|
+
i += 1n
|
|
176
|
+
negative = true
|
|
177
|
+
},
|
|
178
|
+
_ => void,
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
|
|
182
|
+
while (i < len) {
|
|
183
|
+
let char = WasmI32.load8U(ptr, i)
|
|
184
|
+
let digit = char - _CHAR_CODE_0
|
|
185
|
+
if (digit >= 0n && digit < 10n) {
|
|
186
|
+
mantissa = WasmI64.add(
|
|
187
|
+
WasmI64.mul(mantissa, 10N),
|
|
188
|
+
WasmI64.extendI32U(digit)
|
|
189
|
+
)
|
|
190
|
+
numDigits += 1n
|
|
191
|
+
i += 1n
|
|
192
|
+
} else if (char == _CHAR_CODE_UNDERSCORE) {
|
|
193
|
+
i += 1n
|
|
194
|
+
} else {
|
|
195
|
+
break
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
let digitsBeforeDecimal = numDigits
|
|
200
|
+
|
|
201
|
+
// Handle the dot
|
|
202
|
+
match (WasmI32.load8U(ptr, i)) {
|
|
203
|
+
c when c == _CHAR_CODE_DOT => {
|
|
204
|
+
i += 1n
|
|
205
|
+
let dotStartIndex = i
|
|
206
|
+
// Parse chunks of 8 digits
|
|
207
|
+
while (WasmI32.leU(i + 8n, len)) {
|
|
208
|
+
let digits = WasmI64.load(ptr, i)
|
|
209
|
+
if (is8Digits(digits)) {
|
|
210
|
+
mantissa = WasmI64.add(
|
|
211
|
+
parse8Digits(digits),
|
|
212
|
+
WasmI64.mul(mantissa, 100000000N)
|
|
213
|
+
)
|
|
214
|
+
i += 8n
|
|
215
|
+
numDigitsAfterDot += 8n
|
|
216
|
+
} else {
|
|
217
|
+
break
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Parse digits '0'-'9' until we hit a non-digit byte or reach the end of the string
|
|
221
|
+
while (i < len) {
|
|
222
|
+
let char = WasmI32.load8U(ptr, i)
|
|
223
|
+
let digit = char - _CHAR_CODE_0
|
|
224
|
+
if (digit >= 0n && digit < 10n) {
|
|
225
|
+
mantissa = WasmI64.add(
|
|
226
|
+
WasmI64.extendI32U(digit),
|
|
227
|
+
WasmI64.mul(mantissa, 10N)
|
|
228
|
+
)
|
|
229
|
+
i += 1n
|
|
230
|
+
numDigitsAfterDot += 1n
|
|
231
|
+
} else if (char == _CHAR_CODE_UNDERSCORE) {
|
|
232
|
+
i += 1n
|
|
233
|
+
} else {
|
|
234
|
+
break
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
exponent = 0n - numDigitsAfterDot
|
|
238
|
+
},
|
|
239
|
+
_ => void,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
numDigits += numDigitsAfterDot
|
|
243
|
+
|
|
244
|
+
match (numDigits) {
|
|
245
|
+
n when n == 0n => Err("Invalid float"),
|
|
246
|
+
_ => {
|
|
247
|
+
let _MAX_MANTISSA_DIGITS = 19n // 10^19 fits in uint64
|
|
248
|
+
let _MIN_19DIGIT_INT = 100_0000_0000_0000_0000N
|
|
249
|
+
|
|
250
|
+
// Parse scientific notation
|
|
251
|
+
let exponentResult = match (WasmI32.load8U(ptr, i)) {
|
|
252
|
+
c when c == _CHAR_CODE_E || c == _CHAR_CODE_e => {
|
|
253
|
+
i += 1n
|
|
254
|
+
parseScientificExponent(ptr, i, len)
|
|
255
|
+
},
|
|
256
|
+
_ => {
|
|
257
|
+
if (i != len) {
|
|
258
|
+
Err("Invalid float")
|
|
259
|
+
} else {
|
|
260
|
+
Ok(0l)
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
match (exponentResult) {
|
|
266
|
+
Ok(exponentNumber) => {
|
|
267
|
+
let exponentNumber = WasmI32.load(
|
|
268
|
+
WasmI32.fromGrain(exponentNumber),
|
|
269
|
+
8n
|
|
270
|
+
)
|
|
271
|
+
exponent += exponentNumber
|
|
272
|
+
|
|
273
|
+
// Check to see if we need to truncate
|
|
274
|
+
if (numDigits > _MAX_MANTISSA_DIGITS) {
|
|
275
|
+
manyDigits = false
|
|
276
|
+
numDigits -= _MAX_MANTISSA_DIGITS
|
|
277
|
+
let mut i = 0n
|
|
278
|
+
while (i < len) {
|
|
279
|
+
let c = WasmI32.load8U(ptr, i)
|
|
280
|
+
if (c == _CHAR_CODE_DOT || c == _CHAR_CODE_0) {
|
|
281
|
+
let n = c - (_CHAR_CODE_0 - 1n)
|
|
282
|
+
numDigits -= if (WasmI32.ltS(n, 0n)) {
|
|
283
|
+
0n
|
|
284
|
+
} else if (WasmI32.gtS(n, 255n)) {
|
|
285
|
+
255n
|
|
286
|
+
} else {
|
|
287
|
+
n
|
|
288
|
+
}
|
|
289
|
+
i += 1n
|
|
290
|
+
} else if (c == _CHAR_CODE_UNDERSCORE) {
|
|
291
|
+
continue
|
|
292
|
+
} else {
|
|
293
|
+
break
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Attempt to parse 19 digits, the most that can fit in an i64
|
|
298
|
+
let mut digitsParsed = 0n
|
|
299
|
+
if (WasmI32.gtS(numDigits, 0n)) {
|
|
300
|
+
manyDigits = true
|
|
301
|
+
mantissa = 0N
|
|
302
|
+
|
|
303
|
+
let mut i = 0n
|
|
304
|
+
while (WasmI64.ltU(mantissa, _MIN_19DIGIT_INT)) {
|
|
305
|
+
if (i < len) {
|
|
306
|
+
let char = WasmI32.load8U(ptr, i)
|
|
307
|
+
let digit = char - _CHAR_CODE_0
|
|
308
|
+
if (digit >= 0n && digit < 10n) {
|
|
309
|
+
mantissa = WasmI64.add(
|
|
310
|
+
WasmI64.mul(mantissa, 10N),
|
|
311
|
+
WasmI64.extendI32U(digit)
|
|
312
|
+
)
|
|
313
|
+
i += 1n
|
|
314
|
+
digitsParsed += 1n
|
|
315
|
+
} else if (char == _CHAR_CODE_UNDERSCORE) {
|
|
316
|
+
i += 1n
|
|
317
|
+
} else {
|
|
318
|
+
break
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
break
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
exponent = if (WasmI64.geU(mantissa, _MIN_19DIGIT_INT)) {
|
|
326
|
+
digitsBeforeDecimal - digitsParsed
|
|
327
|
+
} else {
|
|
328
|
+
// From Rust: https://github.com/rust-lang/rust/blob/e960b5e7749e95c6a6b2fdec7250a48105664efb/library/core/src/num/dec2flt/parse.rs#L179
|
|
329
|
+
// the next byte must be present and be '.'
|
|
330
|
+
// We know this is true because we had more than 19
|
|
331
|
+
// digits previously, so we overflowed a 64-bit integer,
|
|
332
|
+
// but parsing only the integral digits produced less
|
|
333
|
+
// than 19 digits. That means we must have a decimal
|
|
334
|
+
// point, and at least 1 fractional digit.
|
|
335
|
+
i += 1n
|
|
336
|
+
let mut fractionalDigitsParsed = 0n
|
|
337
|
+
while (WasmI64.ltU(mantissa, _MIN_19DIGIT_INT)) {
|
|
338
|
+
if (i < len) {
|
|
339
|
+
let char = WasmI32.load8U(ptr, i)
|
|
340
|
+
let digit = char - _CHAR_CODE_0
|
|
341
|
+
if (digit < 10n) {
|
|
342
|
+
mantissa = WasmI64.add(
|
|
343
|
+
WasmI64.mul(mantissa, 10N),
|
|
344
|
+
WasmI64.extendI32U(digit)
|
|
345
|
+
)
|
|
346
|
+
i += 1n
|
|
347
|
+
fractionalDigitsParsed += 1n
|
|
348
|
+
} else if (char == _CHAR_CODE_UNDERSCORE) {
|
|
349
|
+
i += 1n
|
|
350
|
+
} else {
|
|
351
|
+
break
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
break
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
0n - fractionalDigitsParsed
|
|
358
|
+
}
|
|
359
|
+
// Add back the explicit part
|
|
360
|
+
exponent += exponentNumber
|
|
361
|
+
}
|
|
362
|
+
Ok(
|
|
363
|
+
(
|
|
364
|
+
WasmI32.toGrain(newInt32(exponent)): Int32,
|
|
365
|
+
WasmI32.toGrain(newInt64(mantissa)): Int64,
|
|
366
|
+
negative,
|
|
367
|
+
manyDigits,
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
} else {
|
|
371
|
+
Ok(
|
|
372
|
+
(
|
|
373
|
+
WasmI32.toGrain(newInt32(exponent)): Int32,
|
|
374
|
+
WasmI32.toGrain(newInt64(mantissa)): Int64,
|
|
375
|
+
negative,
|
|
376
|
+
manyDigits,
|
|
377
|
+
)
|
|
378
|
+
)
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
Err(err) => Err(err),
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
}
|
|
385
|
+
},
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
@unsafe
|
|
390
|
+
let parseInfNan = s => {
|
|
391
|
+
let (+) = WasmI32.add
|
|
392
|
+
let (-) = WasmI32.sub
|
|
393
|
+
let (<) = WasmI32.ltS
|
|
394
|
+
let (>=) = WasmI32.geS
|
|
395
|
+
let (<=) = WasmI32.leS
|
|
396
|
+
let (==) = WasmI32.eq
|
|
397
|
+
|
|
398
|
+
let ptr = WasmI32.fromGrain(s)
|
|
399
|
+
match (WasmI32.load(ptr, 4n)) {
|
|
400
|
+
// Invalid string
|
|
401
|
+
0n => Err("Invalid string"),
|
|
402
|
+
// Continue to parse
|
|
403
|
+
len => {
|
|
404
|
+
let ptr = ptr + 8n
|
|
405
|
+
|
|
406
|
+
let mut i = 0n
|
|
407
|
+
let mut mantissa = 0N
|
|
408
|
+
let mut exponent = 0n
|
|
409
|
+
let mut numDigits = 0n
|
|
410
|
+
let mut numDigitsAfterDot = 0n
|
|
411
|
+
let mut manyDigits = false
|
|
412
|
+
|
|
413
|
+
// Parse the optional leading sign
|
|
414
|
+
let mut negative = false
|
|
415
|
+
match (WasmI32.load8U(ptr, i)) {
|
|
416
|
+
fst when fst == _CHAR_CODE_PLUS => {
|
|
417
|
+
i += 1n
|
|
418
|
+
negative = false
|
|
419
|
+
},
|
|
420
|
+
fst when fst == _CHAR_CODE_MINUS => {
|
|
421
|
+
i += 1n
|
|
422
|
+
negative = true
|
|
423
|
+
},
|
|
424
|
+
_ => void,
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (len - i < 3n) {
|
|
428
|
+
Err("Invalid string")
|
|
429
|
+
} else {
|
|
430
|
+
let mut c1 = WasmI32.load8U(ptr + i, 0n)
|
|
431
|
+
if (c1 >= _CHAR_CODE_A && c1 <= _CHAR_CODE_Z) {
|
|
432
|
+
c1 += 0x20n
|
|
433
|
+
}
|
|
434
|
+
let mut c2 = WasmI32.load8U(ptr + i, 1n)
|
|
435
|
+
if (c2 >= _CHAR_CODE_A && c2 <= _CHAR_CODE_Z) {
|
|
436
|
+
c2 += 0x20n
|
|
437
|
+
}
|
|
438
|
+
let mut c3 = WasmI32.load8U(ptr + i, 2n)
|
|
439
|
+
if (c3 >= _CHAR_CODE_A && c3 <= _CHAR_CODE_Z) {
|
|
440
|
+
c3 += 0x20n
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (
|
|
444
|
+
len - i == 3n &&
|
|
445
|
+
c1 == _CHAR_CODE_n &&
|
|
446
|
+
c2 == _CHAR_CODE_a &&
|
|
447
|
+
c3 == _CHAR_CODE_n
|
|
448
|
+
) {
|
|
449
|
+
Ok((fpNan(), negative))
|
|
450
|
+
} else if (
|
|
451
|
+
c1 == _CHAR_CODE_i && c2 == _CHAR_CODE_n && c3 == _CHAR_CODE_f
|
|
452
|
+
) {
|
|
453
|
+
if (len - i == 3n) {
|
|
454
|
+
Ok((fpInf(), negative))
|
|
455
|
+
} else if (len - i == 8n) {
|
|
456
|
+
let mut c4 = WasmI32.load8U(ptr + i, 3n)
|
|
457
|
+
if (c4 >= _CHAR_CODE_A && c4 <= _CHAR_CODE_Z) {
|
|
458
|
+
c4 += 0x20n
|
|
459
|
+
}
|
|
460
|
+
let mut c5 = WasmI32.load8U(ptr + i, 4n)
|
|
461
|
+
if (c5 >= _CHAR_CODE_A && c5 <= _CHAR_CODE_Z) {
|
|
462
|
+
c5 += 0x20n
|
|
463
|
+
}
|
|
464
|
+
let mut c6 = WasmI32.load8U(ptr + i, 5n)
|
|
465
|
+
if (c6 >= _CHAR_CODE_A && c6 <= _CHAR_CODE_Z) {
|
|
466
|
+
c6 += 0x20n
|
|
467
|
+
}
|
|
468
|
+
let mut c7 = WasmI32.load8U(ptr + i, 6n)
|
|
469
|
+
if (c7 >= _CHAR_CODE_A && c7 <= _CHAR_CODE_Z) {
|
|
470
|
+
c7 += 0x20n
|
|
471
|
+
}
|
|
472
|
+
let mut c8 = WasmI32.load8U(ptr + i, 7n)
|
|
473
|
+
if (c8 >= _CHAR_CODE_A && c8 <= _CHAR_CODE_Z) {
|
|
474
|
+
c8 += 0x20n
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (
|
|
478
|
+
c4 == _CHAR_CODE_i &&
|
|
479
|
+
c5 == _CHAR_CODE_n &&
|
|
480
|
+
c6 == _CHAR_CODE_i &&
|
|
481
|
+
c7 == _CHAR_CODE_t &&
|
|
482
|
+
c8 == _CHAR_CODE_y
|
|
483
|
+
) {
|
|
484
|
+
Ok((fpInf(), negative))
|
|
485
|
+
} else {
|
|
486
|
+
Err("Invalid string")
|
|
487
|
+
}
|
|
488
|
+
} else {
|
|
489
|
+
Err("Invalid string")
|
|
490
|
+
}
|
|
491
|
+
} else {
|
|
492
|
+
Err("Invalid string")
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
@unsafe
|
|
500
|
+
export let isFastPath =
|
|
501
|
+
(
|
|
502
|
+
exponent: WasmI32,
|
|
503
|
+
mantissa: WasmI64,
|
|
504
|
+
negative: Bool,
|
|
505
|
+
manyDigits: Bool,
|
|
506
|
+
) => {
|
|
507
|
+
let (<=) = WasmI32.leS
|
|
508
|
+
let (<<) = WasmI64.shl
|
|
509
|
+
|
|
510
|
+
_MIN_EXPONENT_FAST_PATH <= exponent &&
|
|
511
|
+
exponent <= _MAX_EXPONENT_DISGUISED_FAST_PATH &&
|
|
512
|
+
WasmI64.leU(mantissa, _MAX_MANTISSA_FAST_PATH) &&
|
|
513
|
+
!manyDigits
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
@unsafe
|
|
517
|
+
export let parseFloat = (string: String) => {
|
|
518
|
+
let (!=) = WasmI32.ne
|
|
519
|
+
|
|
520
|
+
match (parseFloatToParts(string)) {
|
|
521
|
+
Ok((exponent, mantissa, negative, manyDigits)) => {
|
|
522
|
+
let exponent = WasmI32.load(WasmI32.fromGrain(exponent), 8n)
|
|
523
|
+
let mantissa = WasmI64.load(WasmI32.fromGrain(mantissa), 8n)
|
|
524
|
+
let floatOpt = if (isFastPath(exponent, mantissa, negative, manyDigits)) {
|
|
525
|
+
let (<=) = WasmI32.leS
|
|
526
|
+
if (exponent <= _MAX_EXPONENT_FAST_PATH) {
|
|
527
|
+
// normal fast path
|
|
528
|
+
let (*) = WasmF64.mul
|
|
529
|
+
let (/) = WasmF64.div
|
|
530
|
+
let (<) = WasmI32.leS
|
|
531
|
+
|
|
532
|
+
let mantissa = WasmF64.convertI64U(mantissa)
|
|
533
|
+
let n = if (exponent < 0n) {
|
|
534
|
+
mantissa / getPowers10FastPath(WasmI32.mul(exponent, -1n))
|
|
535
|
+
} else {
|
|
536
|
+
mantissa * getPowers10FastPath(exponent)
|
|
537
|
+
}
|
|
538
|
+
if (negative) {
|
|
539
|
+
Some(WasmI32.toGrain(newFloat64(n * -1.W)): Number)
|
|
540
|
+
} else {
|
|
541
|
+
Some(WasmI32.toGrain(newFloat64(n)): Number)
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
// disguised fast path
|
|
545
|
+
let (-) = WasmI32.sub
|
|
546
|
+
let (*) = WasmF64.mul
|
|
547
|
+
let (>) = WasmI64.gtS
|
|
548
|
+
|
|
549
|
+
let shift = exponent - _MAX_EXPONENT_FAST_PATH
|
|
550
|
+
|
|
551
|
+
let mantissa = WasmI64.mul(
|
|
552
|
+
mantissa,
|
|
553
|
+
WasmI64.extendI32U(getPowers10(shift))
|
|
554
|
+
)
|
|
555
|
+
if (mantissa > _MAX_MANTISSA_FAST_PATH) {
|
|
556
|
+
None
|
|
557
|
+
} else {
|
|
558
|
+
assert WasmI64.leS(mantissa, _MAX_MANTISSA_FAST_PATH)
|
|
559
|
+
|
|
560
|
+
let mantissa = WasmF64.convertI64U(mantissa)
|
|
561
|
+
let n = mantissa * getPowers10FastPath(_MAX_EXPONENT_FAST_PATH)
|
|
562
|
+
if (negative) {
|
|
563
|
+
Some(WasmI32.toGrain(newFloat64(n * -1.W)): Number)
|
|
564
|
+
} else {
|
|
565
|
+
Some(WasmI32.toGrain(newFloat64(n)): Number)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
None
|
|
571
|
+
}
|
|
572
|
+
// Check if fast path worked
|
|
573
|
+
match (floatOpt) {
|
|
574
|
+
Some(n) => Ok(n),
|
|
575
|
+
None => {
|
|
576
|
+
// From Rust:
|
|
577
|
+
// If significant digits were truncated, then we can have rounding error
|
|
578
|
+
// only if `mantissa + 1` produces a different result. We also avoid
|
|
579
|
+
// redundantly using the Eisel-Lemire algorithm if it was unable to
|
|
580
|
+
// correctly round on the first pass.
|
|
581
|
+
|
|
582
|
+
let mut fp = computeFloat(WasmI64.extendI32S(exponent), mantissa)
|
|
583
|
+
let f = WasmI64.load(WasmI32.fromGrain(fp.f), 8n)
|
|
584
|
+
let e = WasmI32.load(WasmI32.fromGrain(fp.e), 8n)
|
|
585
|
+
if (manyDigits && WasmI32.geS(e, 0n)) {
|
|
586
|
+
let fp2 = computeFloat(
|
|
587
|
+
WasmI64.extendI32S(exponent),
|
|
588
|
+
WasmI64.add(mantissa, 1N)
|
|
589
|
+
)
|
|
590
|
+
let f2 = WasmI64.load(WasmI32.fromGrain(fp2.f), 8n)
|
|
591
|
+
let e2 = WasmI32.load(WasmI32.fromGrain(fp2.e), 8n)
|
|
592
|
+
if (e != e2 || WasmI64.ne(f, f2)) {
|
|
593
|
+
fp.e = -1l
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Unable to correctly round the float using the Eisel-Lemire algorithm.
|
|
598
|
+
// Fallback to a slower, but always correct algorithm.
|
|
599
|
+
let e = WasmI32.load(WasmI32.fromGrain(fp.e), 8n)
|
|
600
|
+
if (WasmI32.ltS(e, 0n)) {
|
|
601
|
+
fp = parseLongMantissa(string)
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
Ok(biasedFpToNumber(fp, negative))
|
|
605
|
+
},
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
Err(str) => {
|
|
609
|
+
match (parseInfNan(string)) {
|
|
610
|
+
Ok((fp, negative)) => Ok(biasedFpToNumber(fp, negative)),
|
|
611
|
+
_ => Err(str),
|
|
612
|
+
}
|
|
613
|
+
},
|
|
614
|
+
}
|
|
615
|
+
}
|