@grain/stdlib 0.5.3 → 0.5.5
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 +61 -0
- package/array.gr +65 -57
- package/array.md +54 -6
- package/buffer.gr +71 -1
- package/buffer.md +142 -0
- package/bytes.gr +52 -3
- package/bytes.md +117 -0
- package/char.gr +23 -20
- package/char.md +18 -3
- package/immutablemap.gr +493 -0
- package/immutablemap.md +479 -0
- package/immutablepriorityqueue.gr +44 -16
- package/immutablepriorityqueue.md +44 -1
- package/immutableset.gr +498 -0
- package/immutableset.md +449 -0
- package/int32.gr +39 -37
- package/int32.md +6 -0
- package/int64.gr +39 -37
- package/int64.md +6 -0
- package/list.gr +33 -24
- package/list.md +39 -10
- package/map.gr +19 -28
- package/marshal.gr +4 -4
- package/number.gr +727 -26
- package/number.md +345 -23
- 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/priorityqueue.gr +25 -5
- package/priorityqueue.md +30 -0
- package/queue.gr +22 -7
- package/queue.md +18 -1
- package/regex.gr +161 -65
- 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 +7 -7
- package/runtime/compare.gr +2 -1
- package/runtime/equal.gr +3 -2
- package/runtime/exception.gr +9 -5
- package/runtime/exception.md +8 -2
- package/runtime/gc.gr +2 -1
- package/runtime/malloc.gr +1 -3
- package/runtime/numberUtils.gr +13 -13
- package/runtime/numberUtils.md +6 -0
- package/runtime/numbers.gr +123 -39
- package/runtime/numbers.md +26 -0
- package/runtime/string.gr +4 -2
- 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/set.gr +25 -25
- package/stack.gr +14 -0
- package/stack.md +17 -0
- package/string.gr +313 -39
- package/string.md +99 -0
- package/sys/file.gr +1 -1
- package/sys/time.gr +4 -4
package/set.gr
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @module Set: A Set is an unordered collection of unique values. Operations on a Set mutate the internal state, so it never needs to be re-assigned.
|
|
3
3
|
* @example import Set from "set"
|
|
4
4
|
*
|
|
5
|
-
* @since
|
|
5
|
+
* @since v0.3.0
|
|
6
6
|
*/
|
|
7
7
|
import List from "list"
|
|
8
8
|
import Array from "array"
|
|
@@ -33,7 +33,7 @@ record Set<k> {
|
|
|
33
33
|
* @param size: The initial storage size of the set
|
|
34
34
|
* @returns An empty set with the given initial storage size
|
|
35
35
|
*
|
|
36
|
-
* @since
|
|
36
|
+
* @since v0.3.0
|
|
37
37
|
*/
|
|
38
38
|
export let makeSized = size => {
|
|
39
39
|
let buckets = Array.make(size, None)
|
|
@@ -44,7 +44,7 @@ export let makeSized = size => {
|
|
|
44
44
|
*
|
|
45
45
|
* @returns An empty set
|
|
46
46
|
*
|
|
47
|
-
* @since
|
|
47
|
+
* @since v0.3.0
|
|
48
48
|
*/
|
|
49
49
|
export let make = () => {
|
|
50
50
|
makeSized(16)
|
|
@@ -121,7 +121,7 @@ let rec nodeInBucket = (key, node) => {
|
|
|
121
121
|
* @param key: The value to add
|
|
122
122
|
* @param set: The set to update
|
|
123
123
|
*
|
|
124
|
-
* @since
|
|
124
|
+
* @since v0.3.0
|
|
125
125
|
*/
|
|
126
126
|
export let add = (key, set) => {
|
|
127
127
|
let buckets = set.buckets
|
|
@@ -154,7 +154,7 @@ export let add = (key, set) => {
|
|
|
154
154
|
* @param set: The set to search
|
|
155
155
|
* @returns `true` if the set contains the given value or `false` otherwise
|
|
156
156
|
*
|
|
157
|
-
* @since
|
|
157
|
+
* @since v0.3.0
|
|
158
158
|
*/
|
|
159
159
|
export let contains = (key, set) => {
|
|
160
160
|
let buckets = set.buckets
|
|
@@ -186,7 +186,7 @@ let rec removeInBucket = (key, node) => {
|
|
|
186
186
|
* @param key: The value to remove
|
|
187
187
|
* @param set: The set to update
|
|
188
188
|
*
|
|
189
|
-
* @since
|
|
189
|
+
* @since v0.3.0
|
|
190
190
|
*/
|
|
191
191
|
export let remove = (key, set) => {
|
|
192
192
|
let buckets = set.buckets
|
|
@@ -214,7 +214,7 @@ export let remove = (key, set) => {
|
|
|
214
214
|
* @param set: The set to inspect
|
|
215
215
|
* @returns The count of elements in the set
|
|
216
216
|
*
|
|
217
|
-
* @since
|
|
217
|
+
* @since v0.3.0
|
|
218
218
|
*/
|
|
219
219
|
export let size = set => {
|
|
220
220
|
set.size
|
|
@@ -226,7 +226,7 @@ export let size = set => {
|
|
|
226
226
|
* @param set: The set to inspect
|
|
227
227
|
* @returns `true` if the given set is empty or `false` otherwise
|
|
228
228
|
*
|
|
229
|
-
* @since
|
|
229
|
+
* @since v0.3.0
|
|
230
230
|
*/
|
|
231
231
|
export let isEmpty = set => {
|
|
232
232
|
size(set) == 0
|
|
@@ -237,7 +237,7 @@ export let isEmpty = set => {
|
|
|
237
237
|
*
|
|
238
238
|
* @param set: The set to reset
|
|
239
239
|
*
|
|
240
|
-
* @since
|
|
240
|
+
* @since v0.3.0
|
|
241
241
|
*/
|
|
242
242
|
export let clear = set => {
|
|
243
243
|
set.size = 0
|
|
@@ -263,7 +263,7 @@ let rec forEachBucket = (fn, node) => {
|
|
|
263
263
|
* @param fn: The iterator function to call with each element
|
|
264
264
|
* @param set: The set to iterate
|
|
265
265
|
*
|
|
266
|
-
* @since
|
|
266
|
+
* @since v0.3.0
|
|
267
267
|
* @history v0.5.0: Ensured the iterator function return type is always `Void`
|
|
268
268
|
*/
|
|
269
269
|
export let forEach = (fn, set) => {
|
|
@@ -288,7 +288,7 @@ let rec reduceEachBucket = (fn, node, acc) => {
|
|
|
288
288
|
* @param set: The set to iterate
|
|
289
289
|
* @returns The final accumulator returned from `fn`
|
|
290
290
|
*
|
|
291
|
-
* @since
|
|
291
|
+
* @since v0.3.0
|
|
292
292
|
*/
|
|
293
293
|
export let reduce = (fn, init, set) => {
|
|
294
294
|
let buckets = set.buckets
|
|
@@ -305,10 +305,10 @@ export let reduce = (fn, init, set) => {
|
|
|
305
305
|
* @param fn: The predicate function to indicate which elements to remove from the set, where returning `false` indicates the value should be removed
|
|
306
306
|
* @param set: The set to iterate
|
|
307
307
|
*
|
|
308
|
-
* @since
|
|
308
|
+
* @since v0.3.0
|
|
309
309
|
*/
|
|
310
|
-
export let filter = (
|
|
311
|
-
let keysToRemove = reduce((list, key) => if (!
|
|
310
|
+
export let filter = (fn, set) => {
|
|
311
|
+
let keysToRemove = reduce((list, key) => if (!fn(key)) {
|
|
312
312
|
[key, ...list]
|
|
313
313
|
} else {
|
|
314
314
|
list
|
|
@@ -324,10 +324,10 @@ export let filter = (predicate, set) => {
|
|
|
324
324
|
* @param fn: The predicate function to indicate which elements to remove from the set, where returning `true` indicates the value should be removed
|
|
325
325
|
* @param set: The set to iterate
|
|
326
326
|
*
|
|
327
|
-
* @since
|
|
327
|
+
* @since v0.3.0
|
|
328
328
|
*/
|
|
329
|
-
export let reject = (
|
|
330
|
-
filter(key => !
|
|
329
|
+
export let reject = (fn, set) => {
|
|
330
|
+
filter(key => !fn(key), set)
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
/**
|
|
@@ -336,7 +336,7 @@ export let reject = (predicate, set) => {
|
|
|
336
336
|
* @param set: The set to convert
|
|
337
337
|
* @returns A list containing all set values
|
|
338
338
|
*
|
|
339
|
-
* @since
|
|
339
|
+
* @since v0.3.0
|
|
340
340
|
*/
|
|
341
341
|
export let toList = set => {
|
|
342
342
|
reduce((list, key) => [key, ...list], [], set)
|
|
@@ -348,7 +348,7 @@ export let toList = set => {
|
|
|
348
348
|
* @param list: The list to convert
|
|
349
349
|
* @returns A set containing all list values
|
|
350
350
|
*
|
|
351
|
-
* @since
|
|
351
|
+
* @since v0.3.0
|
|
352
352
|
*/
|
|
353
353
|
export let fromList = list => {
|
|
354
354
|
let set = make()
|
|
@@ -364,7 +364,7 @@ export let fromList = list => {
|
|
|
364
364
|
* @param set: The set to convert
|
|
365
365
|
* @returns An array containing all set values
|
|
366
366
|
*
|
|
367
|
-
* @since
|
|
367
|
+
* @since v0.3.0
|
|
368
368
|
*/
|
|
369
369
|
export let toArray = set => {
|
|
370
370
|
Array.fromList(toList(set))
|
|
@@ -376,7 +376,7 @@ export let toArray = set => {
|
|
|
376
376
|
* @param array: The array to convert
|
|
377
377
|
* @returns A set containing all array values
|
|
378
378
|
*
|
|
379
|
-
* @since
|
|
379
|
+
* @since v0.3.0
|
|
380
380
|
*/
|
|
381
381
|
export let fromArray = array => {
|
|
382
382
|
let set = make()
|
|
@@ -393,7 +393,7 @@ export let fromArray = array => {
|
|
|
393
393
|
* @param set2: The second set to combine
|
|
394
394
|
* @returns A set containing all elements of both sets
|
|
395
395
|
*
|
|
396
|
-
* @since
|
|
396
|
+
* @since v0.3.0
|
|
397
397
|
*/
|
|
398
398
|
export let union = (set1, set2) => {
|
|
399
399
|
let set = make()
|
|
@@ -413,7 +413,7 @@ export let union = (set1, set2) => {
|
|
|
413
413
|
* @param set2: The second set to combine
|
|
414
414
|
* @returns A set containing only unshared elements from both sets
|
|
415
415
|
*
|
|
416
|
-
* @since
|
|
416
|
+
* @since v0.3.0
|
|
417
417
|
*/
|
|
418
418
|
export let diff = (set1, set2) => {
|
|
419
419
|
let set = make()
|
|
@@ -437,7 +437,7 @@ export let diff = (set1, set2) => {
|
|
|
437
437
|
* @param set2: The second set to combine
|
|
438
438
|
* @returns A set containing only shared elements from both sets
|
|
439
439
|
*
|
|
440
|
-
* @since
|
|
440
|
+
* @since v0.3.0
|
|
441
441
|
*/
|
|
442
442
|
export let intersect = (set1, set2) => {
|
|
443
443
|
let set = make()
|
|
@@ -461,7 +461,7 @@ export let intersect = (set1, set2) => {
|
|
|
461
461
|
* @param set: The set to inspect
|
|
462
462
|
* @returns The internal state of the set
|
|
463
463
|
*
|
|
464
|
-
* @since
|
|
464
|
+
* @since v0.3.0
|
|
465
465
|
*/
|
|
466
466
|
export let getInternalStats = set => {
|
|
467
467
|
(set.size, Array.length(set.buckets))
|
package/stack.gr
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @module Stack: An immutable stack implementation. A stack is a LIFO (last-in-first-out) data structure where new values are added, retrieved, and removed from the end.
|
|
3
3
|
* @example import Stack from "stack"
|
|
4
|
+
*
|
|
5
|
+
* @deprecated This module will be renamed to ImmutableStack in the v0.6.0 release of Grain.
|
|
4
6
|
*/
|
|
5
7
|
|
|
6
8
|
import List from "list"
|
|
@@ -20,10 +22,22 @@ record Stack<a> {
|
|
|
20
22
|
* @section Values: Functions and constants included in the Stack module.
|
|
21
23
|
*/
|
|
22
24
|
|
|
25
|
+
/**
|
|
26
|
+
* An empty stack.
|
|
27
|
+
*
|
|
28
|
+
* @since v0.5.4
|
|
29
|
+
*/
|
|
30
|
+
export let empty = {
|
|
31
|
+
let empty = { data: [], }
|
|
32
|
+
empty
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
/**
|
|
24
36
|
* Creates a new stack.
|
|
25
37
|
*
|
|
26
38
|
* @returns An empty stack
|
|
39
|
+
*
|
|
40
|
+
* @deprecated This will be removed in the v0.6.0 release of Grain.
|
|
27
41
|
*/
|
|
28
42
|
export let make = () => {
|
|
29
43
|
{ data: [], }
|
package/stack.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
title: Stack
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
+
> **Deprecated:** This module will be renamed to ImmutableStack in the v0.6.0 release of Grain.
|
|
6
|
+
|
|
5
7
|
An immutable stack implementation. A stack is a LIFO (last-in-first-out) data structure where new values are added, retrieved, and removed from the end.
|
|
6
8
|
|
|
7
9
|
```grain
|
|
@@ -24,8 +26,23 @@ Stacks are immutable data structures that store their data in a List.
|
|
|
24
26
|
|
|
25
27
|
Functions and constants included in the Stack module.
|
|
26
28
|
|
|
29
|
+
### Stack.**empty**
|
|
30
|
+
|
|
31
|
+
<details disabled>
|
|
32
|
+
<summary tabindex="-1">Added in <code>0.5.4</code></summary>
|
|
33
|
+
No other changes yet.
|
|
34
|
+
</details>
|
|
35
|
+
|
|
36
|
+
```grain
|
|
37
|
+
empty : Stack<a>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
An empty stack.
|
|
41
|
+
|
|
27
42
|
### Stack.**make**
|
|
28
43
|
|
|
44
|
+
> **Deprecated:** This will be removed in the v0.6.0 release of Grain.
|
|
45
|
+
|
|
29
46
|
```grain
|
|
30
47
|
make : () -> Stack<a>
|
|
31
48
|
```
|
package/string.gr
CHANGED
|
@@ -221,7 +221,7 @@ export let lastIndexOf = (search: String, string: String) => {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
@
|
|
224
|
+
@unsafe
|
|
225
225
|
let getCodePoint = (ptr: WasmI32) => {
|
|
226
226
|
// Algorithm from https://encoding.spec.whatwg.org/#utf-8-decoder
|
|
227
227
|
let (+) = WasmI32.add
|
|
@@ -872,6 +872,239 @@ export let endsWith = (search: String, string: String) => {
|
|
|
872
872
|
}
|
|
873
873
|
}
|
|
874
874
|
|
|
875
|
+
/**
|
|
876
|
+
* Replaces the first appearance of the search pattern in the string with the replacement value.
|
|
877
|
+
*
|
|
878
|
+
* @param searchPattern: The string to replace
|
|
879
|
+
* @param replacement: The replacement
|
|
880
|
+
* @param string: The string to change
|
|
881
|
+
* @returns A new string with the first occurrence of the search pattern replaced
|
|
882
|
+
*
|
|
883
|
+
* @example String.replaceFirst("🌾", "🌎", "Hello 🌾🌾") == "Hello 🌎🌾"
|
|
884
|
+
*
|
|
885
|
+
* @since v0.5.4
|
|
886
|
+
*/
|
|
887
|
+
@unsafe
|
|
888
|
+
export let replaceFirst =
|
|
889
|
+
(
|
|
890
|
+
searchPattern: String,
|
|
891
|
+
replacement: String,
|
|
892
|
+
string: String,
|
|
893
|
+
) => {
|
|
894
|
+
let (+) = WasmI32.add
|
|
895
|
+
let (-) = WasmI32.sub
|
|
896
|
+
let (>) = WasmI32.gtU
|
|
897
|
+
let (<) = WasmI32.ltU
|
|
898
|
+
let (==) = WasmI32.eq
|
|
899
|
+
|
|
900
|
+
let mut patternPtr = WasmI32.fromGrain(searchPattern)
|
|
901
|
+
let mut stringPtr = WasmI32.fromGrain(string)
|
|
902
|
+
let mut replacementPtr = WasmI32.fromGrain(replacement)
|
|
903
|
+
|
|
904
|
+
let patternLen = WasmI32.load(patternPtr, 4n)
|
|
905
|
+
let stringLen = WasmI32.load(stringPtr, 4n)
|
|
906
|
+
let replacementLen = WasmI32.load(replacementPtr, 4n)
|
|
907
|
+
// Bail if search str is longer than the string
|
|
908
|
+
if (stringLen < patternLen) {
|
|
909
|
+
string
|
|
910
|
+
} else {
|
|
911
|
+
patternPtr += 8n
|
|
912
|
+
stringPtr += 8n
|
|
913
|
+
replacementPtr += 8n
|
|
914
|
+
|
|
915
|
+
let mut found = false
|
|
916
|
+
// Search for an instance of the string
|
|
917
|
+
let mut foundIndex = -1n
|
|
918
|
+
let stringEndPtr = stringPtr + stringLen - patternLen + 1n
|
|
919
|
+
for (let mut i = stringPtr; i < stringEndPtr; i += 1n) {
|
|
920
|
+
// check for match
|
|
921
|
+
foundIndex += 1n
|
|
922
|
+
if (Memory.compare(i, patternPtr, patternLen) == 0n) {
|
|
923
|
+
found = true
|
|
924
|
+
break
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (found) {
|
|
928
|
+
// Create the new string
|
|
929
|
+
let str = allocateString(stringLen - patternLen + replacementLen)
|
|
930
|
+
let strPtr = str + 8n
|
|
931
|
+
Memory.copy(strPtr, stringPtr, foundIndex)
|
|
932
|
+
Memory.copy(strPtr + foundIndex, replacementPtr, replacementLen)
|
|
933
|
+
Memory.copy(
|
|
934
|
+
strPtr + foundIndex + replacementLen,
|
|
935
|
+
stringPtr + foundIndex + patternLen,
|
|
936
|
+
stringLen - foundIndex
|
|
937
|
+
)
|
|
938
|
+
// Copy over the str
|
|
939
|
+
WasmI32.toGrain(str): String
|
|
940
|
+
} else {
|
|
941
|
+
string
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
/**
|
|
947
|
+
* Replaces the last appearance of the search pattern in the string with the replacement value.
|
|
948
|
+
*
|
|
949
|
+
* @param searchPattern: The string to replace
|
|
950
|
+
* @param replacement: The replacement
|
|
951
|
+
* @param string: The string to change
|
|
952
|
+
* @returns A new string with the last occurrence of the search pattern replaced
|
|
953
|
+
*
|
|
954
|
+
* @example String.replaceLast("🌾", "🌎", "Hello 🌾🌾") == "Hello 🌾🌎"
|
|
955
|
+
*
|
|
956
|
+
* @since v0.5.4
|
|
957
|
+
*/
|
|
958
|
+
@unsafe
|
|
959
|
+
export let replaceLast =
|
|
960
|
+
(
|
|
961
|
+
searchPattern: String,
|
|
962
|
+
replacement: String,
|
|
963
|
+
string: String,
|
|
964
|
+
) => {
|
|
965
|
+
let (+) = WasmI32.add
|
|
966
|
+
let (-) = WasmI32.sub
|
|
967
|
+
let (>) = WasmI32.gtU
|
|
968
|
+
let (<) = WasmI32.ltU
|
|
969
|
+
let (==) = WasmI32.eq
|
|
970
|
+
|
|
971
|
+
let mut patternPtr = WasmI32.fromGrain(searchPattern)
|
|
972
|
+
let mut stringPtr = WasmI32.fromGrain(string)
|
|
973
|
+
let mut replacementPtr = WasmI32.fromGrain(replacement)
|
|
974
|
+
|
|
975
|
+
let patternLen = WasmI32.load(patternPtr, 4n)
|
|
976
|
+
let stringLen = WasmI32.load(stringPtr, 4n)
|
|
977
|
+
let replacementLen = WasmI32.load(replacementPtr, 4n)
|
|
978
|
+
|
|
979
|
+
// Bail if search str is longer than the string
|
|
980
|
+
if (stringLen < patternLen) {
|
|
981
|
+
string
|
|
982
|
+
} else {
|
|
983
|
+
patternPtr += 8n
|
|
984
|
+
stringPtr += 8n
|
|
985
|
+
replacementPtr += 8n
|
|
986
|
+
|
|
987
|
+
let mut found = false
|
|
988
|
+
// Search for an instance of the string
|
|
989
|
+
let stringEndPtr = stringPtr + stringLen - patternLen
|
|
990
|
+
let mut foundIndex = stringLen - patternLen + 1n
|
|
991
|
+
for (let mut i = stringEndPtr; i > stringPtr - 1n; i -= 1n) {
|
|
992
|
+
// check for match
|
|
993
|
+
foundIndex -= 1n
|
|
994
|
+
if (Memory.compare(i, patternPtr, patternLen) == 0n) {
|
|
995
|
+
found = true
|
|
996
|
+
break
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
if (found) {
|
|
1000
|
+
// Create the new string
|
|
1001
|
+
let str = allocateString(stringLen - patternLen + replacementLen)
|
|
1002
|
+
let strPtr = str + 8n
|
|
1003
|
+
Memory.copy(strPtr, stringPtr, foundIndex)
|
|
1004
|
+
Memory.copy(strPtr + foundIndex, replacementPtr, replacementLen)
|
|
1005
|
+
Memory.copy(
|
|
1006
|
+
strPtr + foundIndex + replacementLen,
|
|
1007
|
+
stringPtr + foundIndex + patternLen,
|
|
1008
|
+
stringLen - foundIndex
|
|
1009
|
+
)
|
|
1010
|
+
// Copy over the str
|
|
1011
|
+
WasmI32.toGrain(str): String
|
|
1012
|
+
} else {
|
|
1013
|
+
string
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
/**
|
|
1019
|
+
* Replaces every appearance of the search pattern in the string with the replacement value.
|
|
1020
|
+
*
|
|
1021
|
+
* @param searchPattern: The string to replace
|
|
1022
|
+
* @param replacement: The replacement
|
|
1023
|
+
* @param string: The string to change
|
|
1024
|
+
* @returns A new string with each occurrence of the search pattern replaced
|
|
1025
|
+
*
|
|
1026
|
+
* @example String.replaceAll("🌾", "🌎", "Hello 🌾🌾") == "Hello 🌎🌎"
|
|
1027
|
+
*
|
|
1028
|
+
* @since v0.5.4
|
|
1029
|
+
*/
|
|
1030
|
+
@unsafe
|
|
1031
|
+
export let replaceAll =
|
|
1032
|
+
(
|
|
1033
|
+
searchPattern: String,
|
|
1034
|
+
replacement: String,
|
|
1035
|
+
string: String,
|
|
1036
|
+
) => {
|
|
1037
|
+
let (*) = WasmI32.mul
|
|
1038
|
+
let (+) = WasmI32.add
|
|
1039
|
+
let (-) = WasmI32.sub
|
|
1040
|
+
let (>) = WasmI32.gtU
|
|
1041
|
+
let (<) = WasmI32.ltU
|
|
1042
|
+
let (==) = WasmI32.eq
|
|
1043
|
+
let (>>) = WasmI32.shrS
|
|
1044
|
+
|
|
1045
|
+
let mut patternPtr = WasmI32.fromGrain(searchPattern)
|
|
1046
|
+
let mut stringPtr = WasmI32.fromGrain(string)
|
|
1047
|
+
let mut replacementPtr = WasmI32.fromGrain(replacement)
|
|
1048
|
+
|
|
1049
|
+
let patternLen = WasmI32.load(patternPtr, 4n)
|
|
1050
|
+
let stringLen = WasmI32.load(stringPtr, 4n)
|
|
1051
|
+
let replacementLen = WasmI32.load(replacementPtr, 4n)
|
|
1052
|
+
|
|
1053
|
+
// Bail if search str is longer than the string
|
|
1054
|
+
if (stringLen < patternLen) {
|
|
1055
|
+
string
|
|
1056
|
+
} else {
|
|
1057
|
+
patternPtr += 8n
|
|
1058
|
+
stringPtr += 8n
|
|
1059
|
+
replacementPtr += 8n
|
|
1060
|
+
|
|
1061
|
+
let mut found = false
|
|
1062
|
+
// Search for an instance of the string
|
|
1063
|
+
let stringEndPtr = stringPtr + stringLen - patternLen
|
|
1064
|
+
let mut foundIndex = stringLen - patternLen + 1n
|
|
1065
|
+
let mut foundIndexes = []
|
|
1066
|
+
let mut foundCount = 0n
|
|
1067
|
+
for (let mut i = stringEndPtr; i > stringPtr - 1n; i -= 1n) {
|
|
1068
|
+
// check for match
|
|
1069
|
+
foundIndex -= 1n
|
|
1070
|
+
if (Memory.compare(i, patternPtr, patternLen) == 0n) {
|
|
1071
|
+
found = true
|
|
1072
|
+
foundCount += 1n
|
|
1073
|
+
foundIndexes = [tagSimpleNumber(foundIndex), ...foundIndexes]
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
if (found) {
|
|
1077
|
+
// Create the new string
|
|
1078
|
+
let str = allocateString(
|
|
1079
|
+
stringLen - (patternLen - replacementLen) * foundCount
|
|
1080
|
+
)
|
|
1081
|
+
let mut strPtr = str + 8n
|
|
1082
|
+
let mut lastIndex = 0n
|
|
1083
|
+
while (true) {
|
|
1084
|
+
match (foundIndexes) {
|
|
1085
|
+
[idx, ...rest] => {
|
|
1086
|
+
let index = untagSimpleNumber(idx)
|
|
1087
|
+
// Copy the part before
|
|
1088
|
+
Memory.copy(strPtr, stringPtr + lastIndex, index - lastIndex)
|
|
1089
|
+
strPtr += index - lastIndex
|
|
1090
|
+
// Copy the part after
|
|
1091
|
+
Memory.copy(strPtr, replacementPtr, replacementLen)
|
|
1092
|
+
strPtr += replacementLen
|
|
1093
|
+
lastIndex = index + patternLen
|
|
1094
|
+
foundIndexes = rest
|
|
1095
|
+
},
|
|
1096
|
+
[] => break,
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
// Copy remaining string
|
|
1100
|
+
Memory.copy(strPtr, stringPtr + lastIndex, stringLen - lastIndex)
|
|
1101
|
+
WasmI32.toGrain(str): String
|
|
1102
|
+
} else {
|
|
1103
|
+
string
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
875
1108
|
// String->Byte encoding and helper functions:
|
|
876
1109
|
|
|
877
1110
|
// these are globals to avoid memory leaks
|
|
@@ -1349,33 +1582,33 @@ let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
|
|
|
1349
1582
|
match (encoding) {
|
|
1350
1583
|
UTF8 => {
|
|
1351
1584
|
bytesSize >= 3n &&
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1585
|
+
WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xEFn &&
|
|
1586
|
+
WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xBBn &&
|
|
1587
|
+
WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0xBFn
|
|
1355
1588
|
},
|
|
1356
1589
|
UTF16_BE => {
|
|
1357
1590
|
bytesSize >= 2n &&
|
|
1358
|
-
|
|
1359
|
-
|
|
1591
|
+
WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFEn &&
|
|
1592
|
+
WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFFn
|
|
1360
1593
|
},
|
|
1361
1594
|
UTF16_LE => {
|
|
1362
1595
|
bytesSize >= 2n &&
|
|
1363
|
-
|
|
1364
|
-
|
|
1596
|
+
WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFFn &&
|
|
1597
|
+
WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFEn
|
|
1365
1598
|
},
|
|
1366
1599
|
UTF32_BE => {
|
|
1367
1600
|
bytesSize >= 4n &&
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1601
|
+
WasmI32.load8U(ptr, _BYTES_OFFSET) == 0x00n &&
|
|
1602
|
+
WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0x00n &&
|
|
1603
|
+
WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0xFEn &&
|
|
1604
|
+
WasmI32.load8U(ptr + 3n, _BYTES_OFFSET) == 0xFFn
|
|
1372
1605
|
},
|
|
1373
1606
|
UTF32_LE => {
|
|
1374
1607
|
bytesSize >= 4n &&
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1608
|
+
WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFFn &&
|
|
1609
|
+
WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFEn &&
|
|
1610
|
+
WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0x00n &&
|
|
1611
|
+
WasmI32.load8U(ptr + 3n, _BYTES_OFFSET) == 0x00n
|
|
1379
1612
|
},
|
|
1380
1613
|
}
|
|
1381
1614
|
}
|
|
@@ -1612,7 +1845,7 @@ let decodeRangeHelp =
|
|
|
1612
1845
|
// high surrogate. next character is low srurrogate
|
|
1613
1846
|
let w1 = (w1 & 0x03FFn) << 10n
|
|
1614
1847
|
let w2 = (WasmI32.load8U(bytesPtr, 2n) << 8n |
|
|
1615
|
-
|
|
1848
|
+
WasmI32.load8U(bytesPtr, 3n)) &
|
|
1616
1849
|
0x03FFn
|
|
1617
1850
|
let codeWord = w1 + w2 + 0x10000n
|
|
1618
1851
|
// no problems, so go past both code words
|
|
@@ -1635,7 +1868,7 @@ let decodeRangeHelp =
|
|
|
1635
1868
|
// high surrogate. next character is low srurrogate
|
|
1636
1869
|
let w1 = (w1 & 0x03FFn) << 10n
|
|
1637
1870
|
let w2 = (WasmI32.load8U(bytesPtr, 3n) << 8n |
|
|
1638
|
-
|
|
1871
|
+
WasmI32.load8U(bytesPtr, 2n)) &
|
|
1639
1872
|
0x03FFn
|
|
1640
1873
|
//let uPrime = codePoint - 0x10000n
|
|
1641
1874
|
//let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
|
|
@@ -1867,30 +2100,71 @@ export let forEachCodePointi = (fn: (Number, Number) -> Void, str: String) => {
|
|
|
1867
2100
|
void
|
|
1868
2101
|
}
|
|
1869
2102
|
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
let
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
2103
|
+
@unsafe
|
|
2104
|
+
let trimString = (str: String, fromEnd: Bool) => {
|
|
2105
|
+
let (>>>) = WasmI32.shrU
|
|
2106
|
+
let (+) = WasmI32.add
|
|
2107
|
+
let (*) = WasmI32.mul
|
|
2108
|
+
let (-) = WasmI32.sub
|
|
2109
|
+
let (<) = WasmI32.ltU
|
|
2110
|
+
let (==) = WasmI32.eq
|
|
2111
|
+
let (!=) = WasmI32.ne
|
|
2112
|
+
|
|
2113
|
+
let mut stringPtr = WasmI32.fromGrain(str)
|
|
2114
|
+
let byteLength = WasmI32.load(stringPtr, 4n)
|
|
2115
|
+
stringPtr += 8n
|
|
2116
|
+
let mut i = 0n, offset = 1n
|
|
2117
|
+
if (fromEnd) {
|
|
2118
|
+
i = byteLength - 1n
|
|
2119
|
+
offset = -1n
|
|
1876
2120
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
//
|
|
2121
|
+
let mut count = 0n
|
|
2122
|
+
for (; i < byteLength; i += offset) {
|
|
2123
|
+
// Get the byte, not necessarily a full UTF-8 codepoint
|
|
2124
|
+
let byte = WasmI32.load8U(stringPtr, i)
|
|
2125
|
+
// TODO(#661): Use unicode whitespace property and unicode line terminator
|
|
2126
|
+
if (!fromEnd) {
|
|
2127
|
+
if (
|
|
2128
|
+
byte == 0xEFn && // Check for the first BOM byte
|
|
2129
|
+
// Check for the full BOM codepoint, 0xEFBBBF. Bytes are reversed because wasm is little-endian
|
|
2130
|
+
WasmI32.load(stringPtr, i - 1n) >>> 8n == 0xBFBBEFn
|
|
2131
|
+
) {
|
|
2132
|
+
i += 2n
|
|
2133
|
+
count += 3n
|
|
2134
|
+
continue
|
|
2135
|
+
}
|
|
2136
|
+
} else {
|
|
2137
|
+
if (
|
|
2138
|
+
byte == 0xBFn && // Check for the last BOM byte
|
|
2139
|
+
// Check for the full BOM codepoint, 0xEFBBBF. Bytes are reversed because wasm is little-endian
|
|
2140
|
+
WasmI32.load(stringPtr, i - 3n) >>> 8n == 0xBFBBEFn
|
|
2141
|
+
) {
|
|
2142
|
+
i -= 2n
|
|
2143
|
+
count += 3n
|
|
2144
|
+
continue
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
1880
2147
|
if (
|
|
1881
|
-
//
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2148
|
+
byte != 0x20n && // Space
|
|
2149
|
+
byte != 0x0Dn && // LF
|
|
2150
|
+
byte != 0x0An && // CR
|
|
2151
|
+
byte != 0x09n && // Tab
|
|
2152
|
+
byte != 0x0Bn && // LINE TABULATION
|
|
2153
|
+
byte != 0x0Cn && // FORM FEED (FF)
|
|
2154
|
+
byte != 0xA0n // No Break Space
|
|
2155
|
+
) {
|
|
2156
|
+
break
|
|
2157
|
+
}
|
|
2158
|
+
count += 1n
|
|
1892
2159
|
}
|
|
1893
|
-
|
|
2160
|
+
let str = allocateString(byteLength - count)
|
|
2161
|
+
// Copy the string
|
|
2162
|
+
if (fromEnd) {
|
|
2163
|
+
Memory.copy(str + 8n, stringPtr, byteLength - count)
|
|
2164
|
+
} else {
|
|
2165
|
+
Memory.copy(str + 8n, stringPtr + count, byteLength - count)
|
|
2166
|
+
}
|
|
2167
|
+
WasmI32.toGrain(str): String
|
|
1894
2168
|
}
|
|
1895
2169
|
/**
|
|
1896
2170
|
* Trims the beginning of a string—removing any leading whitespace characters.
|