@grain/stdlib 0.4.6 → 0.5.0

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.
Files changed (82) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/array.gr +18 -18
  3. package/array.md +18 -18
  4. package/bigint.gr +497 -0
  5. package/bigint.md +811 -0
  6. package/buffer.gr +49 -213
  7. package/buffer.md +24 -17
  8. package/bytes.gr +100 -202
  9. package/bytes.md +19 -0
  10. package/char.gr +63 -133
  11. package/exception.md +6 -0
  12. package/float32.gr +39 -78
  13. package/float64.gr +43 -78
  14. package/hash.gr +37 -37
  15. package/int32.gr +152 -198
  16. package/int32.md +104 -0
  17. package/int64.gr +151 -197
  18. package/int64.md +104 -0
  19. package/list.gr +467 -70
  20. package/list.md +1141 -0
  21. package/map.gr +192 -7
  22. package/map.md +525 -0
  23. package/number.gr +30 -54
  24. package/number.md +3 -3
  25. package/option.md +1 -1
  26. package/package.json +3 -3
  27. package/pervasives.gr +499 -59
  28. package/pervasives.md +1116 -0
  29. package/queue.gr +4 -0
  30. package/queue.md +10 -0
  31. package/random.gr +196 -0
  32. package/random.md +179 -0
  33. package/regex.gr +1833 -842
  34. package/regex.md +11 -11
  35. package/result.md +1 -1
  36. package/runtime/bigint.gr +2045 -0
  37. package/runtime/bigint.md +326 -0
  38. package/runtime/dataStructures.gr +99 -278
  39. package/runtime/dataStructures.md +391 -0
  40. package/runtime/debug.md +6 -0
  41. package/runtime/equal.gr +5 -23
  42. package/runtime/equal.md +6 -0
  43. package/runtime/exception.md +30 -0
  44. package/runtime/gc.gr +20 -3
  45. package/runtime/gc.md +36 -0
  46. package/runtime/malloc.gr +13 -11
  47. package/runtime/malloc.md +55 -0
  48. package/runtime/numberUtils.gr +91 -41
  49. package/runtime/numberUtils.md +54 -0
  50. package/runtime/numbers.gr +1043 -391
  51. package/runtime/numbers.md +300 -0
  52. package/runtime/string.gr +136 -230
  53. package/runtime/string.md +24 -0
  54. package/runtime/stringUtils.gr +58 -38
  55. package/runtime/stringUtils.md +6 -0
  56. package/runtime/unsafe/constants.gr +17 -0
  57. package/runtime/unsafe/constants.md +72 -0
  58. package/runtime/unsafe/conv.md +71 -0
  59. package/runtime/unsafe/errors.md +204 -0
  60. package/runtime/unsafe/memory.md +54 -0
  61. package/runtime/unsafe/printWasm.md +24 -0
  62. package/runtime/unsafe/tags.gr +9 -8
  63. package/runtime/unsafe/tags.md +120 -0
  64. package/runtime/unsafe/wasmf32.md +168 -0
  65. package/runtime/unsafe/wasmf64.md +168 -0
  66. package/runtime/unsafe/wasmi32.md +282 -0
  67. package/runtime/unsafe/wasmi64.md +300 -0
  68. package/runtime/utils/printing.gr +62 -0
  69. package/runtime/utils/printing.md +18 -0
  70. package/runtime/wasi.gr +1 -1
  71. package/runtime/wasi.md +839 -0
  72. package/set.gr +17 -8
  73. package/set.md +24 -21
  74. package/stack.gr +3 -3
  75. package/stack.md +4 -6
  76. package/string.gr +194 -329
  77. package/string.md +3 -3
  78. package/sys/file.gr +245 -429
  79. package/sys/process.gr +27 -45
  80. package/sys/random.gr +47 -16
  81. package/sys/random.md +38 -0
  82. package/sys/time.gr +11 -27
package/string.gr CHANGED
@@ -9,10 +9,12 @@
9
9
  import WasmI32 from "runtime/unsafe/wasmi32"
10
10
  import Memory from "runtime/unsafe/memory"
11
11
  import Exception from "runtime/exception"
12
+ import Conv from "runtime/unsafe/conv"
12
13
  import {
13
14
  tagSimpleNumber,
15
+ tagChar,
16
+ untagChar,
14
17
  allocateArray,
15
- allocateChar,
16
18
  allocateString,
17
19
  allocateBytes,
18
20
  } from "runtime/dataStructures"
@@ -32,6 +34,8 @@ export enum Encoding {
32
34
  UTF32_LE,
33
35
  }
34
36
 
37
+ exception MalformedUnicode
38
+
35
39
  /**
36
40
  * @section Values: Functions for working with the String data type.
37
41
  */
@@ -43,9 +47,9 @@ export enum Encoding {
43
47
  * @param str2: The ending string
44
48
  * @returns The combined string
45
49
  *
46
- * @example String.concat("Foo", " Bar") == "FooBar"
50
+ * @example String.concat("Foo", "Bar") == "FooBar"
47
51
  *
48
- * @since v0.1.0
52
+ * @since v0.2.0
49
53
  */
50
54
  export let concat = (++)
51
55
 
@@ -59,8 +63,8 @@ export let concat = (++)
59
63
  *
60
64
  * @since v0.1.0
61
65
  */
62
- @disableGC
63
- export let rec length = (string: String) => {
66
+ @unsafe
67
+ export let length = (string: String) => {
64
68
  let string = WasmI32.fromGrain(string)
65
69
  let size = WasmI32.load(string, 4n)
66
70
 
@@ -76,18 +80,7 @@ export let rec length = (string: String) => {
76
80
  ptr = WasmI32.add(ptr, 1n)
77
81
  }
78
82
 
79
- let ret = tagSimpleNumber(len)
80
- Memory.decRef(WasmI32.fromGrain(length))
81
- Memory.decRef(WasmI32.fromGrain(string))
82
- ret
83
- }
84
-
85
- // @disableGC-safe wrapper
86
- @disableGC
87
- let wasmSafeLength = (s: String) => {
88
- Memory.incRef(WasmI32.fromGrain(length))
89
- Memory.incRef(WasmI32.fromGrain(s))
90
- length(s)
83
+ Conv.wasmI32ToNumber(len)
91
84
  }
92
85
 
93
86
  /**
@@ -100,21 +93,10 @@ let wasmSafeLength = (s: String) => {
100
93
  *
101
94
  * @since v0.1.0
102
95
  */
103
- @disableGC
104
- export let rec byteLength = (string: String) => {
96
+ @unsafe
97
+ export let byteLength = (string: String) => {
105
98
  let string = WasmI32.fromGrain(string)
106
- let ret = tagSimpleNumber(WasmI32.load(string, 4n))
107
- Memory.decRef(WasmI32.fromGrain(byteLength))
108
- Memory.decRef(WasmI32.fromGrain(string))
109
- ret
110
- }
111
-
112
- // @disableGC-safe wrapper
113
- @disableGC
114
- let wasmSafeByteLength = (string: String) => {
115
- Memory.incRef(WasmI32.fromGrain(byteLength))
116
- Memory.incRef(WasmI32.fromGrain(string))
117
- byteLength(string)
99
+ Conv.wasmI32ToNumber(WasmI32.load(string, 4n))
118
100
  }
119
101
 
120
102
  /**
@@ -128,8 +110,8 @@ let wasmSafeByteLength = (string: String) => {
128
110
  *
129
111
  * @since v0.3.0
130
112
  */
131
- @disableGC
132
- export let rec indexOf = (search: String, string: String) => {
113
+ @unsafe
114
+ export let indexOf = (search: String, string: String) => {
133
115
  let search = WasmI32.fromGrain(search)
134
116
  let string = WasmI32.fromGrain(string)
135
117
 
@@ -143,10 +125,8 @@ export let rec indexOf = (search: String, string: String) => {
143
125
  let (-) = WasmI32.sub
144
126
  let (&) = WasmI32.and
145
127
 
146
- let ret = if (psize > size) {
147
- let none = None
148
- Memory.incRef(WasmI32.fromGrain(none))
149
- none
128
+ if (psize > size) {
129
+ None
150
130
  } else {
151
131
  let mut idx = 0n
152
132
  let mut ptr = string + 8n
@@ -174,20 +154,73 @@ export let rec indexOf = (search: String, string: String) => {
174
154
  }
175
155
 
176
156
  if (result == -1n) {
177
- let none = None
178
- Memory.incRef(WasmI32.fromGrain(none))
179
- none
157
+ None
180
158
  } else {
181
- Memory.incRef(WasmI32.fromGrain(Some))
182
159
  Some(tagSimpleNumber(result))
183
160
  }
184
161
  }
185
- Memory.decRef(WasmI32.fromGrain(search))
186
- Memory.decRef(WasmI32.fromGrain(string))
187
- Memory.decRef(WasmI32.fromGrain(indexOf))
188
- ret
189
162
  }
190
163
 
164
+ @disableGC
165
+ let getCodePoint = (ptr: WasmI32) => {
166
+ // Algorithm from https://encoding.spec.whatwg.org/#utf-8-decoder
167
+ let (+) = WasmI32.add
168
+ let (==) = WasmI32.eq
169
+ let (>=) = WasmI32.geU
170
+ let (<=) = WasmI32.leU
171
+ let (<<) = WasmI32.shl
172
+ let (&) = WasmI32.and
173
+ let (|) = WasmI32.or
174
+
175
+ let mut codePoint = 0n
176
+ let mut bytesSeen = 0n
177
+ let mut bytesNeeded = 0n
178
+ let mut lowerBoundary = 0x80n
179
+ let mut upperBoundary = 0xBFn
180
+
181
+ let mut offset = 0n
182
+
183
+ let mut result = 0n
184
+
185
+ while (true) {
186
+ let byte = WasmI32.load8U(ptr + offset, 0n)
187
+ offset += 1n
188
+ if (bytesNeeded == 0n) {
189
+ if (byte >= 0x00n && byte <= 0x7Fn) {
190
+ result = byte
191
+ break
192
+ } else if (byte >= 0xC2n && byte <= 0xDFn) {
193
+ bytesNeeded = 1n
194
+ codePoint = byte & 0x1Fn
195
+ } else if (byte >= 0xE0n && byte <= 0xEFn) {
196
+ if (byte == 0xE0n) lowerBoundary = 0xA0n
197
+ if (byte == 0xEDn) upperBoundary = 0x9Fn
198
+ bytesNeeded = 2n
199
+ codePoint = byte & 0xFn
200
+ } else if (byte >= 0xF0n && byte <= 0xF4n) {
201
+ if (byte == 0xF0n) lowerBoundary = 0x90n
202
+ if (byte == 0xF4n) upperBoundary = 0x8Fn
203
+ bytesNeeded = 3n
204
+ codePoint = byte & 0x7n
205
+ } else {
206
+ throw MalformedUnicode
207
+ }
208
+ continue
209
+ }
210
+ if (!(lowerBoundary <= byte && byte <= upperBoundary)) {
211
+ throw MalformedUnicode
212
+ }
213
+ lowerBoundary = 0x80n
214
+ upperBoundary = 0xBFn
215
+ codePoint = codePoint << 6n | byte & 0x3Fn
216
+ bytesSeen += 1n
217
+ if (bytesSeen == bytesNeeded) {
218
+ result = codePoint
219
+ break
220
+ }
221
+ }
222
+ result
223
+ }
191
224
  /**
192
225
  * Get the character at the position in the input string.
193
226
  *
@@ -199,18 +232,9 @@ export let rec indexOf = (search: String, string: String) => {
199
232
  *
200
233
  * @since v0.4.0
201
234
  */
202
- @disableGC
203
- export let rec charAt = (position, string: String) => {
204
- Memory.incRef(WasmI32.fromGrain((<=)))
205
- if (
206
- wasmSafeLength(string) <= position ||
207
- {
208
- Memory.incRef(WasmI32.fromGrain((<)))
209
- position < 0
210
- }
211
- ) {
212
- Memory.incRef(WasmI32.fromGrain((++)))
213
- Memory.incRef(WasmI32.fromGrain(toString))
235
+ @unsafe
236
+ export let charAt = (position, string: String) => {
237
+ if (length(string) <= position || position < 0) {
214
238
  fail "Invalid offset: " ++ toString(position)
215
239
  }
216
240
  // Implementation is similar to explodeHelp, but doesn't perform unneeded memory allocations
@@ -219,15 +243,18 @@ export let rec charAt = (position, string: String) => {
219
243
  let (&) = WasmI32.and
220
244
  let (<) = WasmI32.ltU
221
245
  let (==) = WasmI32.eq
222
- let size = WasmI32.fromGrain(wasmSafeByteLength(string)) >>> 1n
223
- let len = WasmI32.fromGrain(wasmSafeLength(string)) >>> 1n
246
+ let size = WasmI32.fromGrain(byteLength(string)) >>> 1n
224
247
  let position = WasmI32.fromGrain(position) >>> 1n
225
248
  let string = WasmI32.fromGrain(string)
226
249
  let mut ptr = string + 8n
227
250
  let end = ptr + size
228
251
  let mut counter = 0n
229
- let mut result = 0n
252
+ let mut result = WasmI32.toGrain(0n): Char
230
253
  while (ptr < end) {
254
+ if (counter == position) {
255
+ result = tagChar(getCodePoint(ptr))
256
+ break
257
+ }
231
258
  let byte = WasmI32.load8U(ptr, 0n)
232
259
  let n = if ((byte & 0x80n) == 0x00n) {
233
260
  1n
@@ -238,25 +265,16 @@ export let rec charAt = (position, string: String) => {
238
265
  } else {
239
266
  2n
240
267
  }
241
- if (counter == position) {
242
- let c = allocateChar()
243
- Memory.copy(c + 4n, ptr, n)
244
- result = c
245
- break
246
- }
247
268
  counter += 1n
248
269
  ptr += n
249
270
  }
250
- if (result == 0n) {
271
+ if (WasmI32.eqz(WasmI32.fromGrain(result))) {
251
272
  fail "charAt: should be impossible (please report)"
252
273
  }
253
- let ret = WasmI32.toGrain(result): Char
254
- Memory.decRef(WasmI32.fromGrain(string))
255
- Memory.decRef(WasmI32.fromGrain(charAt))
256
- ret
274
+ result
257
275
  }
258
276
 
259
- @disableGC
277
+ @unsafe
260
278
  let explodeHelp = (s: String, chars) => {
261
279
  let (>>>) = WasmI32.shrU
262
280
  let (+) = WasmI32.add
@@ -264,8 +282,8 @@ let explodeHelp = (s: String, chars) => {
264
282
  let (<) = WasmI32.ltU
265
283
  let (==) = WasmI32.eq
266
284
 
267
- let size = WasmI32.fromGrain(wasmSafeByteLength(s)) >>> 1n
268
- let len = WasmI32.fromGrain(wasmSafeLength(s)) >>> 1n
285
+ let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
286
+ let len = WasmI32.fromGrain(length(s)) >>> 1n
269
287
 
270
288
  let s = WasmI32.fromGrain(s)
271
289
 
@@ -288,9 +306,7 @@ let explodeHelp = (s: String, chars) => {
288
306
  }
289
307
 
290
308
  let c = if (chars) {
291
- let c = allocateChar()
292
- Memory.copy(c + 4n, ptr, n)
293
- c
309
+ WasmI32.fromGrain(tagChar(getCodePoint(ptr)))
294
310
  } else {
295
311
  let s = allocateString(n)
296
312
  Memory.copy(s + 8n, ptr, n)
@@ -315,15 +331,9 @@ let explodeHelp = (s: String, chars) => {
315
331
  *
316
332
  * @since v0.3.0
317
333
  */
318
- @disableGC
319
- export let rec explode = string => {
320
- // `explodeHelp` and `string` do not need to be incRef'd as they are not
321
- // decRef'd in `explodeHelp`
322
- let ret = WasmI32.toGrain(explodeHelp(string, true)): (Array<Char>)
323
-
324
- Memory.decRef(WasmI32.fromGrain(string))
325
- Memory.decRef(WasmI32.fromGrain(explode))
326
- ret
334
+ @unsafe
335
+ export let explode = string => {
336
+ WasmI32.toGrain(explodeHelp(string, true)): Array<Char>
327
337
  }
328
338
 
329
339
  /**
@@ -336,32 +346,34 @@ export let rec explode = string => {
336
346
  *
337
347
  * @since v0.3.0
338
348
  */
339
- @disableGC
340
- export let rec implode = (arr: Array<Char>) => {
349
+ @unsafe
350
+ export let implode = (arr: Array<Char>) => {
341
351
  let (+) = WasmI32.add
342
- let (==) = WasmI32.eq
352
+ let (-) = WasmI32.sub
353
+ let (*) = WasmI32.mul
343
354
  let (<) = WasmI32.ltU
355
+ let (>) = WasmI32.gtU
356
+ let (<=) = WasmI32.leU
344
357
  let (<<) = WasmI32.shl
358
+ let (>>>) = WasmI32.shrU
345
359
  let (&) = WasmI32.and
360
+ let (|) = WasmI32.or
346
361
 
347
- let arr = WasmI32.fromGrain(arr)
348
-
349
- let arrLength = WasmI32.load(arr, 4n)
362
+ let arrLength = WasmI32.load(WasmI32.fromGrain(arr), 4n)
350
363
 
351
364
  let mut stringByteLength = 0n
352
365
 
353
366
  for (let mut i = 0n; i < arrLength; i += 1n) {
354
- let char = WasmI32.load(arr + (i << 2n), 8n)
355
- let byte = WasmI32.load8U(char, 4n)
367
+ let usv = untagChar(arr[tagSimpleNumber(i)])
356
368
 
357
- let n = if ((byte & 0x80n) == 0x00n) {
369
+ let n = if (usv <= 0x7Fn) {
358
370
  1n
359
- } else if ((byte & 0xF0n) == 0xF0n) {
360
- 4n
361
- } else if ((byte & 0xE0n) == 0xE0n) {
371
+ } else if (usv <= 0x07FFn) {
372
+ 2n
373
+ } else if (usv <= 0xFFFFn) {
362
374
  3n
363
375
  } else {
364
- 2n
376
+ 4n
365
377
  }
366
378
 
367
379
  stringByteLength += n
@@ -371,27 +383,37 @@ export let rec implode = (arr: Array<Char>) => {
371
383
  let mut offset = 8n
372
384
 
373
385
  for (let mut i = 0n; i < arrLength; i += 1n) {
374
- let char = WasmI32.load(arr + (i << 2n), 8n)
375
- let byte = WasmI32.load8U(char, 4n)
386
+ let usv = untagChar(arr[tagSimpleNumber(i)])
376
387
 
377
- let n = if ((byte & 0x80n) == 0x00n) {
378
- 1n
379
- } else if ((byte & 0xF0n) == 0xF0n) {
380
- 4n
381
- } else if ((byte & 0xE0n) == 0xE0n) {
382
- 3n
388
+ if (usv < 0x7Fn) {
389
+ WasmI32.store8(str + offset, usv, 0n)
390
+ offset += 1n
383
391
  } else {
384
- 2n
392
+ let mut count = 0n
393
+ let mut marker = 0n
394
+ if (usv <= 0x07FFn) {
395
+ count = 1n
396
+ marker = 0xC0n
397
+ } else if (usv <= 0xFFFFn) {
398
+ count = 2n
399
+ marker = 0xE0n
400
+ } else {
401
+ count = 3n
402
+ marker = 0xF0n
403
+ }
404
+ WasmI32.store8(str + offset, (usv >>> 6n * count) + marker, 0n)
405
+ offset += 1n
406
+
407
+ while (count > 0n) {
408
+ let temp = usv >>> 6n * (count - 1n)
409
+ WasmI32.store8(str + offset, 0x80n | temp & 0x3Fn, 0n)
410
+ count -= 1n
411
+ offset += 1n
412
+ }
385
413
  }
386
-
387
- Memory.copy(str + offset, char + 4n, n)
388
- offset += n
389
414
  }
390
415
 
391
- let ret = WasmI32.toGrain(str): String
392
- Memory.decRef(WasmI32.fromGrain(arr))
393
- Memory.decRef(WasmI32.fromGrain(implode))
394
- ret
416
+ WasmI32.toGrain(str): String
395
417
  }
396
418
 
397
419
  // Helper to get the length in constant time without depending on Array
@@ -430,8 +452,8 @@ export let reverse = string => {
430
452
  *
431
453
  * @example String.split(" ", "Hello world") == [> "Hello", "world"]
432
454
  */
433
- @disableGC
434
- export let rec split = (separator: String, string: String) => {
455
+ @unsafe
456
+ export let split = (separator: String, string: String) => {
435
457
  let (+) = WasmI32.add
436
458
  let (-) = WasmI32.sub
437
459
  let (==) = WasmI32.eq
@@ -441,16 +463,13 @@ export let rec split = (separator: String, string: String) => {
441
463
  let (>>) = WasmI32.shrS
442
464
  let (&) = WasmI32.and
443
465
 
444
- let size = WasmI32.fromGrain(wasmSafeByteLength(string)) >> 1n
445
- let psize = WasmI32.fromGrain(wasmSafeByteLength(separator)) >> 1n
466
+ let size = WasmI32.fromGrain(byteLength(string)) >> 1n
467
+ let psize = WasmI32.fromGrain(byteLength(separator)) >> 1n
446
468
 
447
- let ret = if (psize == 0n) {
448
- WasmI32.toGrain(explodeHelp(string, false)): (Array<String>)
469
+ if (psize == 0n) {
470
+ WasmI32.toGrain(explodeHelp(string, false)): Array<String>
449
471
  } else if (psize > size) {
450
- let string = WasmI32.fromGrain(string)
451
- let ptr = allocateArray(1n)
452
- WasmI32.store(ptr, Memory.incRef(string), 8n)
453
- WasmI32.toGrain(ptr): (Array<String>)
472
+ [> string]
454
473
  } else {
455
474
  let string = WasmI32.fromGrain(string)
456
475
  let separator = WasmI32.fromGrain(separator)
@@ -511,12 +530,8 @@ export let rec split = (separator: String, string: String) => {
511
530
  Memory.copy(lastStr + 8n, last, strSize)
512
531
  WasmI32.store(arr + arrIdx, lastStr, 8n)
513
532
 
514
- WasmI32.toGrain(arr): (Array<String>)
533
+ WasmI32.toGrain(arr): Array<String>
515
534
  }
516
- Memory.decRef(WasmI32.fromGrain(separator))
517
- Memory.decRef(WasmI32.fromGrain(string))
518
- Memory.decRef(WasmI32.fromGrain(split))
519
- ret
520
535
  }
521
536
 
522
537
  /**
@@ -531,8 +546,8 @@ export let rec split = (separator: String, string: String) => {
531
546
  *
532
547
  * @since v0.1.0
533
548
  */
534
- @disableGC
535
- export let rec slice = (start: Number, to: Number, string: String) => {
549
+ @unsafe
550
+ export let slice = (start: Number, to: Number, string: String) => {
536
551
  let (+) = WasmI32.add
537
552
  let (-) = WasmI32.sub
538
553
  let (==) = WasmI32.eq
@@ -545,8 +560,8 @@ export let rec slice = (start: Number, to: Number, string: String) => {
545
560
  let startOrig = start
546
561
  let toOrig = to
547
562
 
548
- let len = WasmI32.fromGrain(wasmSafeLength(string)) >> 1n
549
- let size = WasmI32.fromGrain(wasmSafeByteLength(string)) >> 1n
563
+ let len = WasmI32.fromGrain(length(string)) >> 1n
564
+ let size = WasmI32.fromGrain(byteLength(string)) >> 1n
550
565
 
551
566
  let string = WasmI32.fromGrain(string)
552
567
 
@@ -609,12 +624,7 @@ export let rec slice = (start: Number, to: Number, string: String) => {
609
624
 
610
625
  Memory.copy(newString + 8n, begin, newSize)
611
626
 
612
- let ret = WasmI32.toGrain(newString): String
613
- Memory.decRef(WasmI32.fromGrain(startOrig))
614
- Memory.decRef(WasmI32.fromGrain(toOrig))
615
- Memory.decRef(WasmI32.fromGrain(string))
616
- Memory.decRef(WasmI32.fromGrain(slice))
617
- ret
627
+ WasmI32.toGrain(newString): String
618
628
  }
619
629
 
620
630
  /**
@@ -628,8 +638,8 @@ export let rec slice = (start: Number, to: Number, string: String) => {
628
638
  *
629
639
  * @since v0.1.0
630
640
  */
631
- @disableGC
632
- export let rec contains = (search: String, string: String) => {
641
+ @unsafe
642
+ export let contains = (search: String, string: String) => {
633
643
  // "Not So Naive" string search algorithm
634
644
  // searching phase in O(nm) time complexity
635
645
  // slightly (by coefficient) sub-linear in the average case
@@ -646,8 +656,8 @@ export let rec contains = (search: String, string: String) => {
646
656
  let pOrig = search
647
657
  let sOrig = string
648
658
 
649
- let n = WasmI32.fromGrain(wasmSafeByteLength(string)) >> 1n
650
- let m = WasmI32.fromGrain(wasmSafeByteLength(search)) >> 1n
659
+ let n = WasmI32.fromGrain(byteLength(string)) >> 1n
660
+ let m = WasmI32.fromGrain(byteLength(search)) >> 1n
651
661
 
652
662
  let mut string = WasmI32.fromGrain(string)
653
663
  let mut search = WasmI32.fromGrain(search)
@@ -657,7 +667,7 @@ export let rec contains = (search: String, string: String) => {
657
667
 
658
668
  let mut j = 0n, k = 0n, ell = 0n
659
669
 
660
- let ret = if (m > n) {
670
+ if (m > n) {
661
671
  // Bail if pattern length is longer than input length
662
672
  false
663
673
  } else if (m < 2n) {
@@ -705,10 +715,6 @@ export let rec contains = (search: String, string: String) => {
705
715
  }
706
716
  result
707
717
  }
708
- Memory.decRef(WasmI32.fromGrain(pOrig))
709
- Memory.decRef(WasmI32.fromGrain(sOrig))
710
- Memory.decRef(WasmI32.fromGrain(contains))
711
- ret
712
718
  }
713
719
 
714
720
  /**
@@ -722,8 +728,8 @@ export let rec contains = (search: String, string: String) => {
722
728
  *
723
729
  * @since v0.1.0
724
730
  */
725
- @disableGC
726
- export let rec startsWith = (search: String, string: String) => {
731
+ @unsafe
732
+ export let startsWith = (search: String, string: String) => {
727
733
  let (+) = WasmI32.add
728
734
  let (>) = WasmI32.gtU
729
735
  let (==) = WasmI32.eq
@@ -740,15 +746,11 @@ export let rec startsWith = (search: String, string: String) => {
740
746
  search += 8n
741
747
 
742
748
  // Bail if pattern length is longer than input length
743
- let ret = if (m > n) {
749
+ if (m > n) {
744
750
  false
745
751
  } else {
746
752
  Memory.compare(search, string, m) == 0n
747
753
  }
748
- Memory.decRef(WasmI32.fromGrain(pOrig))
749
- Memory.decRef(WasmI32.fromGrain(sOrig))
750
- Memory.decRef(WasmI32.fromGrain(startsWith))
751
- ret
752
754
  }
753
755
 
754
756
  /**
@@ -762,8 +764,8 @@ export let rec startsWith = (search: String, string: String) => {
762
764
  *
763
765
  * @since v0.1.0
764
766
  */
765
- @disableGC
766
- export let rec endsWith = (search: String, string: String) => {
767
+ @unsafe
768
+ export let endsWith = (search: String, string: String) => {
767
769
  let (+) = WasmI32.add
768
770
  let (-) = WasmI32.sub
769
771
  let (>) = WasmI32.gtU
@@ -781,15 +783,11 @@ export let rec endsWith = (search: String, string: String) => {
781
783
  search += 8n
782
784
 
783
785
  // Bail if pattern length is longer than input length
784
- let ret = if (m > n) {
786
+ if (m > n) {
785
787
  false
786
788
  } else {
787
789
  Memory.compare(search, string + n - m, m) == 0n
788
790
  }
789
- Memory.decRef(WasmI32.fromGrain(pOrig))
790
- Memory.decRef(WasmI32.fromGrain(sOrig))
791
- Memory.decRef(WasmI32.fromGrain(endsWith))
792
- ret
793
791
  }
794
792
 
795
793
  // String->Byte encoding and helper functions:
@@ -799,30 +797,22 @@ let _START_NAME = "start"
799
797
  let _SIZE_NAME = "size"
800
798
  let _OFFSET_NAME = "offset"
801
799
 
802
- @disableGC
800
+ @unsafe
803
801
  let grainToWasmNumber = (num, name) => {
804
802
  let num = WasmI32.fromGrain(num)
805
803
  if (WasmI32.eqz(WasmI32.and(num, 1n))) {
806
- Memory.incRef(WasmI32.fromGrain(name))
807
- Memory.incRef(WasmI32.fromGrain((++)))
808
804
  let str = " argument must be an integer"
809
- Memory.incRef(WasmI32.fromGrain(str))
810
- Memory.incRef(WasmI32.fromGrain(Exception.InvalidArgument))
811
805
  throw Exception.InvalidArgument(name ++ str)
812
806
  }
813
807
  let num = WasmI32.shrS(num, 1n)
814
808
  if (WasmI32.ltS(num, 0n)) {
815
- Memory.incRef(WasmI32.fromGrain(name))
816
- Memory.incRef(WasmI32.fromGrain((++)))
817
809
  let str = " argument must be non-negative"
818
- Memory.incRef(WasmI32.fromGrain(str))
819
- Memory.incRef(WasmI32.fromGrain(Exception.InvalidArgument))
820
810
  throw Exception.InvalidArgument(name ++ str)
821
811
  }
822
812
  num
823
813
  }
824
814
 
825
- @disableGC
815
+ @unsafe
826
816
  let utf16Length = (s: String) => {
827
817
  let (>>>) = WasmI32.shrU
828
818
  let (<<) = WasmI32.shl
@@ -831,8 +821,8 @@ let utf16Length = (s: String) => {
831
821
  let (<) = WasmI32.ltU
832
822
  let (==) = WasmI32.eq
833
823
 
834
- let size = WasmI32.fromGrain(wasmSafeByteLength(s)) >>> 1n
835
- let len = WasmI32.fromGrain(wasmSafeLength(s)) >>> 1n
824
+ let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
825
+ let len = WasmI32.fromGrain(length(s)) >>> 1n
836
826
 
837
827
  let s = WasmI32.fromGrain(s)
838
828
 
@@ -862,101 +852,24 @@ let utf16Length = (s: String) => {
862
852
  tagSimpleNumber(size << 1n)
863
853
  }
864
854
 
865
- @disableGC
855
+ @unsafe
866
856
  let encodedLength = (s: String, encoding) => {
867
857
  match (encoding) {
868
- UTF32_BE => {
869
- Memory.incRef(WasmI32.fromGrain((*)))
870
- wasmSafeLength(s) * 4
871
- },
872
- UTF32_LE => {
873
- Memory.incRef(WasmI32.fromGrain((*)))
874
- wasmSafeLength(s) * 4
875
- },
858
+ UTF32_BE => length(s) * 4,
859
+ UTF32_LE => length(s) * 4,
876
860
  UTF16_BE => utf16Length(s),
877
861
  UTF16_LE => utf16Length(s),
878
- UTF8 => wasmSafeByteLength(s),
862
+ UTF8 => byteLength(s),
879
863
  }
880
864
  }
881
865
 
882
- exception MalformedUnicode
866
+ @unsafe
867
+ let mut _BYTES_SIZE_OFFSET = 4n
868
+ @unsafe
869
+ let mut _BYTES_OFFSET = 8n
883
870
 
884
- @disableGC
885
- let getCodePoint = (ptr: WasmI32) => {
886
- // Algorithm from https://encoding.spec.whatwg.org/#utf-8-decoder
887
- let (+) = WasmI32.add
888
- let (==) = WasmI32.eq
889
- let (>=) = WasmI32.geU
890
- let (<=) = WasmI32.leU
891
- let (<<) = WasmI32.shl
892
- let (&) = WasmI32.and
893
- let (|) = WasmI32.or
894
-
895
- let mut codePoint = 0n
896
- let mut bytesSeen = 0n
897
- let mut bytesNeeded = 0n
898
- let mut lowerBoundary = 0x80n
899
- let mut upperBoundary = 0xBFn
900
-
901
- let mut offset = 0n
902
-
903
- let mut result = 0n
904
-
905
- while (true) {
906
- let byte = WasmI32.load8U(ptr + offset, 0n)
907
- offset += 1n
908
- if (bytesNeeded == 0n) {
909
- if (byte >= 0x00n && byte <= 0x7Fn) {
910
- result = byte
911
- break
912
- } else if (byte >= 0xC2n && byte <= 0xDFn) {
913
- bytesNeeded = 1n
914
- codePoint = byte & 0x1Fn
915
- } else if (byte >= 0xE0n && byte <= 0xEFn) {
916
- if (byte == 0xE0n) lowerBoundary = 0xA0n
917
- if (byte == 0xEDn) upperBoundary = 0x9Fn
918
- bytesNeeded = 2n
919
- codePoint = byte & 0xFn
920
- } else if (byte >= 0xF0n && byte <= 0xF4n) {
921
- if (byte == 0xF0n) lowerBoundary = 0x90n
922
- if (byte == 0xF4n) upperBoundary = 0x8Fn
923
- bytesNeeded = 3n
924
- codePoint = byte & 0x7n
925
- } else {
926
- throw MalformedUnicode
927
- }
928
- continue
929
- }
930
- if (!(lowerBoundary <= byte && byte <= upperBoundary)) {
931
- throw MalformedUnicode
932
- }
933
- lowerBoundary = 0x80n
934
- upperBoundary = 0xBFn
935
- codePoint = codePoint << 6n | byte & 0x3Fn
936
- bytesSeen += 1n
937
- if (bytesSeen == bytesNeeded) {
938
- result = codePoint
939
- break
940
- }
941
- }
942
- result: WasmI32
943
- }
944
-
945
- // hack to avoid incRef on this pointer
946
- @disableGC
947
- let mut _BYTES_SIZE_OFFSET = 1n
948
- @disableGC
949
- let mut _BYTES_OFFSET = 1n
950
-
951
- @disableGC
952
- let initPtr = () => {
953
- _BYTES_SIZE_OFFSET = 4n
954
- _BYTES_OFFSET = 8n
955
- }
956
- initPtr()
957
-
958
- @disableGC
959
- let rec encodeAtHelp =
871
+ @unsafe
872
+ let encodeAtHelp =
960
873
  (
961
874
  string: String,
962
875
  encoding: Encoding,
@@ -972,8 +885,8 @@ let rec encodeAtHelp =
972
885
  let (<=) = WasmI32.leU
973
886
  let (==) = WasmI32.eq
974
887
  let (+) = WasmI32.add
975
- let byteSize = WasmI32.fromGrain(wasmSafeByteLength(string)) >>> 1n
976
- let len = WasmI32.fromGrain(wasmSafeLength(string)) >>> 1n
888
+ let byteSize = WasmI32.fromGrain(byteLength(string)) >>> 1n
889
+ let len = WasmI32.fromGrain(length(string)) >>> 1n
977
890
 
978
891
  let string = WasmI32.fromGrain(string)
979
892
 
@@ -1206,13 +1119,6 @@ let rec encodeAtHelp =
1206
1119
  },
1207
1120
  }
1208
1121
 
1209
- Memory.decRef(WasmI32.fromGrain(string))
1210
- Memory.decRef(WasmI32.fromGrain(encoding))
1211
- Memory.decRef(WasmI32.fromGrain(includeBom))
1212
- Memory.decRef(WasmI32.fromGrain(destPos))
1213
- Memory.decRef(WasmI32.fromGrain(encodeAtHelp))
1214
-
1215
- // We don't decRef `dest` because we're returning it
1216
1122
  dest
1217
1123
  }
1218
1124
 
@@ -1246,9 +1152,8 @@ export let encodeAtWithBom = (string, encoding, dest, destPos) => {
1246
1152
  encodeAtHelp(string, encoding, true, dest, destPos)
1247
1153
  }
1248
1154
 
1249
- @disableGC
1250
- let rec encodeHelp = (string: String, encoding: Encoding, includeBom: Bool) => {
1251
- Memory.incRef(WasmI32.fromGrain((+)))
1155
+ @unsafe
1156
+ let encodeHelp = (string: String, encoding: Encoding, includeBom: Bool) => {
1252
1157
  let size = encodedLength(string, encoding) +
1253
1158
  (if (includeBom) {
1254
1159
  match (encoding) {
@@ -1263,17 +1168,7 @@ let rec encodeHelp = (string: String, encoding: Encoding, includeBom: Bool) => {
1263
1168
  })
1264
1169
  let (>>>) = WasmI32.shrU
1265
1170
  let bytes = WasmI32.toGrain(allocateBytes(WasmI32.fromGrain(size) >>> 1n))
1266
- Memory.incRef(WasmI32.fromGrain(encodeAtHelp))
1267
- Memory.incRef(WasmI32.fromGrain(string))
1268
- Memory.incRef(WasmI32.fromGrain(encoding))
1269
- Memory.incRef(WasmI32.fromGrain(includeBom))
1270
- Memory.incRef(WasmI32.fromGrain(bytes))
1271
- let ret = encodeAtHelp(string, encoding, includeBom, bytes, 0)
1272
- Memory.decRef(WasmI32.fromGrain(string))
1273
- Memory.decRef(WasmI32.fromGrain(encoding))
1274
- Memory.decRef(WasmI32.fromGrain(includeBom))
1275
- Memory.decRef(WasmI32.fromGrain(encodeHelp))
1276
- ret
1171
+ encodeAtHelp(string, encoding, includeBom, bytes, 0)
1277
1172
  }
1278
1173
 
1279
1174
  /**
@@ -1304,7 +1199,7 @@ export let encodeWithBom = (string: String, encoding: Encoding) => {
1304
1199
 
1305
1200
  // Byte->String decoding and helper functions:
1306
1201
 
1307
- @disableGC
1202
+ @unsafe
1308
1203
  let writeUtf8CodePoint = (ptr, codePoint) => {
1309
1204
  let (>>>) = WasmI32.shrU
1310
1205
  let (-) = WasmI32.sub
@@ -1361,7 +1256,7 @@ let writeUtf8CodePoint = (ptr, codePoint) => {
1361
1256
  }
1362
1257
  }
1363
1258
 
1364
- @disableGC
1259
+ @unsafe
1365
1260
  let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1366
1261
  let (+) = WasmI32.add
1367
1262
  let (==) = WasmI32.eq
@@ -1403,7 +1298,7 @@ let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1403
1298
  }
1404
1299
  }
1405
1300
 
1406
- @disableGC
1301
+ @unsafe
1407
1302
  let decodedLength =
1408
1303
  (
1409
1304
  bytes: Bytes,
@@ -1572,8 +1467,8 @@ let decodedLength =
1572
1467
  }
1573
1468
  }
1574
1469
 
1575
- @disableGC
1576
- let rec decodeRangeHelp =
1470
+ @unsafe
1471
+ let decodeRangeHelp =
1577
1472
  (
1578
1473
  bytes: Bytes,
1579
1474
  encoding: Encoding,
@@ -1618,7 +1513,7 @@ let rec decodeRangeHelp =
1618
1513
  UTF32_BE => 4n,
1619
1514
  }
1620
1515
  }
1621
- let ret = if (stringSize == 0n) {
1516
+ if (stringSize == 0n) {
1622
1517
  WasmI32.toGrain(str): String
1623
1518
  } else {
1624
1519
  match (encoding) {
@@ -1699,12 +1594,6 @@ let rec decodeRangeHelp =
1699
1594
  }
1700
1595
  WasmI32.toGrain(str): String
1701
1596
  }
1702
- // bytes: Bytes, encoding: Encoding, skipBom: Bool, start: Number, size: Number
1703
- Memory.decRef(WasmI32.fromGrain(bytes))
1704
- Memory.decRef(WasmI32.fromGrain(encoding))
1705
- Memory.decRef(WasmI32.fromGrain(skipBom))
1706
- Memory.decRef(WasmI32.fromGrain(decodeRangeHelp))
1707
- ret
1708
1597
  }
1709
1598
 
1710
1599
  /**
@@ -1749,24 +1638,11 @@ export let decodeRangeKeepBom =
1749
1638
  decodeRangeHelp(bytes, encoding, false, start, size)
1750
1639
  }
1751
1640
 
1752
- @disableGC
1753
- let rec decodeHelp = (bytes: Bytes, encoding: Encoding, skipBom: Bool) => {
1641
+ @unsafe
1642
+ let decodeHelp = (bytes: Bytes, encoding: Encoding, skipBom: Bool) => {
1754
1643
  let bytesPtr = WasmI32.fromGrain(bytes)
1755
1644
  let bytesSize = WasmI32.load(bytesPtr, 4n)
1756
- Memory.incRef(WasmI32.fromGrain(decodeRangeHelp))
1757
- Memory.incRef(WasmI32.fromGrain(bytes))
1758
- Memory.incRef(WasmI32.fromGrain(encoding))
1759
- let ret = decodeRangeHelp(
1760
- bytes,
1761
- encoding,
1762
- skipBom,
1763
- 0,
1764
- tagSimpleNumber(bytesSize)
1765
- )
1766
- Memory.incRef(WasmI32.fromGrain(bytes))
1767
- Memory.incRef(WasmI32.fromGrain(encoding))
1768
- Memory.incRef(WasmI32.fromGrain(decodeHelp))
1769
- ret
1645
+ decodeRangeHelp(bytes, encoding, skipBom, 0, tagSimpleNumber(bytesSize))
1770
1646
  }
1771
1647
 
1772
1648
  /**
@@ -1805,8 +1681,8 @@ export let decodeKeepBom = (bytes: Bytes, encoding: Encoding) => {
1805
1681
  *
1806
1682
  * @since v0.4.0
1807
1683
  */
1808
- @disableGC
1809
- export let rec forEachCodePoint = (fn: Number -> Void, str: String) => {
1684
+ @unsafe
1685
+ export let forEachCodePoint = (fn: Number -> Void, str: String) => {
1810
1686
  let (>>>) = WasmI32.shrU
1811
1687
  let (-) = WasmI32.sub
1812
1688
  let (&) = WasmI32.and
@@ -1843,15 +1719,11 @@ export let rec forEachCodePoint = (fn: Number -> Void, str: String) => {
1843
1719
  // avoid heap allocations. `getCodePoint` will throw
1844
1720
  // MalformedUnicode exception for values exceeding this limit.
1845
1721
  let codePoint = getCodePoint(ptr)
1846
- Memory.incRef(WasmI32.fromGrain(fn))
1847
1722
  fn(tagSimpleNumber(codePoint))
1848
1723
 
1849
1724
  ptr += codePointByteCount
1850
1725
  idx += 1n
1851
1726
  }
1852
- Memory.decRef(WasmI32.fromGrain(fn))
1853
- Memory.decRef(WasmI32.fromGrain(str))
1854
- Memory.decRef(WasmI32.fromGrain(forEachCodePoint))
1855
1727
  void
1856
1728
  }
1857
1729
 
@@ -1867,12 +1739,8 @@ export let rec forEachCodePoint = (fn: Number -> Void, str: String) => {
1867
1739
  *
1868
1740
  * @since v0.4.0
1869
1741
  */
1870
- @disableGC
1871
- export let rec forEachCodePointi =
1872
- (
1873
- fn: (Number, Number) -> Void,
1874
- str: String,
1875
- ) => {
1742
+ @unsafe
1743
+ export let forEachCodePointi = (fn: (Number, Number) -> Void, str: String) => {
1876
1744
  let (>>>) = WasmI32.shrU
1877
1745
  let (-) = WasmI32.sub
1878
1746
  let (&) = WasmI32.and
@@ -1909,17 +1777,14 @@ export let rec forEachCodePointi =
1909
1777
  // avoid heap allocations. `getCodePoint` will throw
1910
1778
  // MalformedUnicode exception for values exceeding this limit.
1911
1779
  let codePoint = getCodePoint(ptr)
1912
- Memory.incRef(WasmI32.fromGrain(fn))
1913
1780
  fn(tagSimpleNumber(codePoint), tagSimpleNumber(idx))
1914
1781
 
1915
1782
  ptr += codePointByteCount
1916
1783
  idx += 1n
1917
1784
  }
1918
- Memory.decRef(WasmI32.fromGrain(fn))
1919
- Memory.decRef(WasmI32.fromGrain(str))
1920
- Memory.decRef(WasmI32.fromGrain(forEachCodePointi))
1921
1785
  void
1922
1786
  }
1787
+
1923
1788
  let trimString = (str: String, end: Bool) => {
1924
1789
  let chars = explode(str), charsLength = length(str)
1925
1790
  let mut i = 0, offset = 1