@grain/stdlib 0.5.12 → 0.6.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 (155) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/LICENSE +1 -1
  3. package/README.md +25 -2
  4. package/array.gr +1512 -199
  5. package/array.md +2032 -94
  6. package/bigint.gr +239 -140
  7. package/bigint.md +450 -106
  8. package/buffer.gr +595 -102
  9. package/buffer.md +903 -145
  10. package/bytes.gr +401 -110
  11. package/bytes.md +551 -63
  12. package/char.gr +228 -49
  13. package/char.md +373 -7
  14. package/exception.gr +26 -12
  15. package/exception.md +29 -5
  16. package/float32.gr +130 -109
  17. package/float32.md +185 -57
  18. package/float64.gr +112 -99
  19. package/float64.md +185 -57
  20. package/hash.gr +47 -37
  21. package/hash.md +21 -3
  22. package/int16.gr +430 -0
  23. package/int16.md +618 -0
  24. package/int32.gr +200 -269
  25. package/int32.md +254 -289
  26. package/int64.gr +142 -225
  27. package/int64.md +254 -289
  28. package/int8.gr +511 -0
  29. package/int8.md +786 -0
  30. package/json.gr +2084 -0
  31. package/json.md +608 -0
  32. package/list.gr +120 -68
  33. package/list.md +125 -80
  34. package/map.gr +560 -57
  35. package/map.md +672 -56
  36. package/marshal.gr +239 -227
  37. package/marshal.md +36 -4
  38. package/number.gr +626 -676
  39. package/number.md +738 -153
  40. package/option.gr +33 -35
  41. package/option.md +58 -42
  42. package/package.json +2 -2
  43. package/path.gr +148 -187
  44. package/path.md +47 -96
  45. package/pervasives.gr +75 -416
  46. package/pervasives.md +85 -180
  47. package/priorityqueue.gr +433 -74
  48. package/priorityqueue.md +422 -54
  49. package/queue.gr +362 -80
  50. package/queue.md +433 -38
  51. package/random.gr +67 -75
  52. package/random.md +68 -40
  53. package/range.gr +135 -63
  54. package/range.md +198 -43
  55. package/rational.gr +284 -0
  56. package/rational.md +545 -0
  57. package/regex.gr +933 -1066
  58. package/regex.md +59 -60
  59. package/result.gr +23 -25
  60. package/result.md +54 -39
  61. package/runtime/atof/common.gr +78 -82
  62. package/runtime/atof/common.md +22 -10
  63. package/runtime/atof/decimal.gr +102 -127
  64. package/runtime/atof/decimal.md +28 -7
  65. package/runtime/atof/lemire.gr +56 -71
  66. package/runtime/atof/lemire.md +9 -1
  67. package/runtime/atof/parse.gr +83 -110
  68. package/runtime/atof/parse.md +12 -2
  69. package/runtime/atof/slow.gr +28 -35
  70. package/runtime/atof/slow.md +9 -1
  71. package/runtime/atof/table.gr +19 -18
  72. package/runtime/atof/table.md +10 -2
  73. package/runtime/atoi/parse.gr +153 -136
  74. package/runtime/atoi/parse.md +50 -1
  75. package/runtime/bigint.gr +410 -517
  76. package/runtime/bigint.md +71 -57
  77. package/runtime/compare.gr +176 -85
  78. package/runtime/compare.md +31 -1
  79. package/runtime/dataStructures.gr +144 -32
  80. package/runtime/dataStructures.md +267 -31
  81. package/runtime/debugPrint.gr +34 -15
  82. package/runtime/debugPrint.md +37 -5
  83. package/runtime/equal.gr +53 -52
  84. package/runtime/equal.md +30 -1
  85. package/runtime/exception.gr +38 -47
  86. package/runtime/exception.md +10 -8
  87. package/runtime/gc.gr +23 -152
  88. package/runtime/gc.md +13 -17
  89. package/runtime/malloc.gr +31 -31
  90. package/runtime/malloc.md +11 -3
  91. package/runtime/numberUtils.gr +191 -172
  92. package/runtime/numberUtils.md +17 -9
  93. package/runtime/numbers.gr +1695 -1021
  94. package/runtime/numbers.md +1098 -134
  95. package/runtime/string.gr +540 -242
  96. package/runtime/string.md +76 -6
  97. package/runtime/unsafe/constants.gr +30 -13
  98. package/runtime/unsafe/constants.md +80 -0
  99. package/runtime/unsafe/conv.gr +55 -28
  100. package/runtime/unsafe/conv.md +41 -9
  101. package/runtime/unsafe/memory.gr +10 -30
  102. package/runtime/unsafe/memory.md +15 -19
  103. package/runtime/unsafe/tags.gr +37 -21
  104. package/runtime/unsafe/tags.md +88 -8
  105. package/runtime/unsafe/wasmf32.gr +30 -36
  106. package/runtime/unsafe/wasmf32.md +64 -56
  107. package/runtime/unsafe/wasmf64.gr +30 -36
  108. package/runtime/unsafe/wasmf64.md +64 -56
  109. package/runtime/unsafe/wasmi32.gr +49 -66
  110. package/runtime/unsafe/wasmi32.md +102 -94
  111. package/runtime/unsafe/wasmi64.gr +52 -79
  112. package/runtime/unsafe/wasmi64.md +108 -100
  113. package/runtime/utils/printing.gr +13 -15
  114. package/runtime/utils/printing.md +11 -3
  115. package/runtime/wasi.gr +294 -295
  116. package/runtime/wasi.md +62 -42
  117. package/set.gr +574 -64
  118. package/set.md +634 -54
  119. package/stack.gr +181 -64
  120. package/stack.md +271 -42
  121. package/string.gr +453 -533
  122. package/string.md +241 -151
  123. package/uint16.gr +369 -0
  124. package/uint16.md +585 -0
  125. package/uint32.gr +470 -0
  126. package/uint32.md +737 -0
  127. package/uint64.gr +471 -0
  128. package/uint64.md +737 -0
  129. package/uint8.gr +369 -0
  130. package/uint8.md +585 -0
  131. package/uri.gr +1093 -0
  132. package/uri.md +477 -0
  133. package/{sys → wasi}/file.gr +914 -500
  134. package/{sys → wasi}/file.md +454 -50
  135. package/wasi/process.gr +292 -0
  136. package/{sys → wasi}/process.md +164 -6
  137. package/wasi/random.gr +77 -0
  138. package/wasi/random.md +80 -0
  139. package/{sys → wasi}/time.gr +15 -22
  140. package/{sys → wasi}/time.md +5 -5
  141. package/immutablearray.gr +0 -929
  142. package/immutablearray.md +0 -1038
  143. package/immutablemap.gr +0 -493
  144. package/immutablemap.md +0 -479
  145. package/immutablepriorityqueue.gr +0 -360
  146. package/immutablepriorityqueue.md +0 -291
  147. package/immutableset.gr +0 -498
  148. package/immutableset.md +0 -449
  149. package/runtime/debug.gr +0 -2
  150. package/runtime/debug.md +0 -6
  151. package/runtime/unsafe/errors.gr +0 -36
  152. package/runtime/unsafe/errors.md +0 -204
  153. package/sys/process.gr +0 -254
  154. package/sys/random.gr +0 -79
  155. package/sys/random.md +0 -66
package/string.gr CHANGED
@@ -1,16 +1,21 @@
1
1
  /**
2
- * @module String: Utilities for working with strings.
3
- * @example import String from "string"
2
+ * Utilities for working with strings.
3
+ *
4
+ * @example from "string" include String
4
5
  *
5
6
  * @since v0.2.0
6
7
  * @history v0.1.0: Originally named `strings`
7
8
  * @history v0.2.0: Renamed to `string`
8
9
  */
9
- import WasmI32 from "runtime/unsafe/wasmi32"
10
- import Memory from "runtime/unsafe/memory"
11
- import Exception from "runtime/exception"
12
- import Conv from "runtime/unsafe/conv"
13
- import {
10
+ module String
11
+
12
+ from "char" include Char
13
+ from "runtime/unsafe/wasmi32" include WasmI32
14
+ from "runtime/unsafe/memory" include Memory
15
+ from "runtime/exception" include Exception
16
+ from "runtime/unsafe/conv" include Conv
17
+ from "runtime/dataStructures" include DataStructures
18
+ use DataStructures.{
14
19
  untagSimpleNumber,
15
20
  tagSimpleNumber,
16
21
  tagChar,
@@ -18,16 +23,12 @@ import {
18
23
  allocateArray,
19
24
  allocateString,
20
25
  allocateBytes,
21
- } from "runtime/dataStructures"
22
-
23
- /**
24
- * @section Types: Type declarations included in the String module.
25
- */
26
+ }
26
27
 
27
28
  /**
28
29
  * Byte encodings
29
30
  */
30
- export enum Encoding {
31
+ provide enum Encoding {
31
32
  UTF8,
32
33
  UTF16_BE,
33
34
  UTF16_LE,
@@ -37,10 +38,6 @@ export enum Encoding {
37
38
 
38
39
  exception MalformedUnicode
39
40
 
40
- /**
41
- * @section Values: Functions for working with the String data type.
42
- */
43
-
44
41
  /**
45
42
  * Concatenate two strings.
46
43
  *
@@ -52,7 +49,7 @@ exception MalformedUnicode
52
49
  *
53
50
  * @since v0.2.0
54
51
  */
55
- export let concat = (++)
52
+ provide let concat = (++)
56
53
 
57
54
  /**
58
55
  * Returns the character length of the input string.
@@ -65,20 +62,21 @@ export let concat = (++)
65
62
  * @since v0.1.0
66
63
  */
67
64
  @unsafe
68
- export let length = (string: String) => {
65
+ provide let length = (string: String) => {
66
+ use WasmI32.{ (+), (&), (!=) }
69
67
  let string = WasmI32.fromGrain(string)
70
68
  let size = WasmI32.load(string, 4n)
71
69
 
72
70
  let mut len = 0n
73
- let mut ptr = WasmI32.add(string, 8n)
74
- let end = WasmI32.add(ptr, size)
71
+ let mut ptr = string + 8n
72
+ let end = ptr + size
75
73
 
76
74
  while (WasmI32.ltU(ptr, end)) {
77
75
  let byte = WasmI32.load8U(ptr, 0n)
78
- if (WasmI32.ne(WasmI32.and(byte, 0xC0n), 0x80n)) {
79
- len = WasmI32.add(len, 1n)
76
+ if ((byte & 0xC0n) != 0x80n) {
77
+ len += 1n
80
78
  }
81
- ptr = WasmI32.add(ptr, 1n)
79
+ ptr += 1n
82
80
  }
83
81
 
84
82
  Conv.wasmI32ToNumber(len)
@@ -95,11 +93,26 @@ export let length = (string: String) => {
95
93
  * @since v0.1.0
96
94
  */
97
95
  @unsafe
98
- export let byteLength = (string: String) => {
96
+ provide let byteLength = (string: String) => {
99
97
  let string = WasmI32.fromGrain(string)
100
98
  Conv.wasmI32ToNumber(WasmI32.load(string, 4n))
101
99
  }
102
100
 
101
+ /**
102
+ * Determines if the string contains no characters.
103
+ *
104
+ * @param string: The string to inspect
105
+ * @returns `true` if the string is empty and `false` otherwise
106
+ *
107
+ * @since v0.6.0
108
+ */
109
+ @unsafe
110
+ provide let isEmpty = (string: String) => {
111
+ use WasmI32.{ (==) }
112
+ let strPtr = WasmI32.fromGrain(string)
113
+ WasmI32.load(strPtr, 4n) == 0n
114
+ }
115
+
103
116
  /**
104
117
  * Finds the first position of a substring in the input string.
105
118
  *
@@ -112,54 +125,40 @@ export let byteLength = (string: String) => {
112
125
  * @since v0.3.0
113
126
  */
114
127
  @unsafe
115
- export let indexOf = (search: String, string: String) => {
128
+ provide let indexOf = (search: String, string: String) => {
116
129
  let search = WasmI32.fromGrain(search)
117
130
  let string = WasmI32.fromGrain(string)
118
131
 
119
132
  let size = WasmI32.load(string, 4n)
120
133
  let psize = WasmI32.load(search, 4n)
121
-
122
- let (>) = WasmI32.gtU
123
- let (<) = WasmI32.ltU
124
- let (==) = WasmI32.eq
125
- let (+) = WasmI32.add
126
- let (-) = WasmI32.sub
127
- let (&) = WasmI32.and
134
+ use WasmI32.{ (+), (-), (&), ltU as (<), gtU as (>), (==) }
128
135
 
129
136
  if (psize > size) {
130
- None
131
- } else {
132
- let mut idx = 0n
133
- let mut ptr = string + 8n
134
- let mut pptr = search + 8n
135
- let end = ptr + size - psize + 1n
136
-
137
- let mut result = -1n
137
+ return None
138
+ }
139
+ let mut idx = 0n
140
+ let mut ptr = string + 8n
141
+ let pptr = search + 8n
142
+ let end = ptr + size - psize + 1n
138
143
 
139
- while (ptr < end) {
140
- if (Memory.compare(ptr, pptr, psize) == 0n) {
141
- result = idx
142
- break
143
- }
144
- idx += 1n
145
- let byte = WasmI32.load8U(ptr, 0n)
146
- if ((byte & 0x80n) == 0x00n) {
147
- ptr += 1n
148
- } else if ((byte & 0xF0n) == 0xF0n) {
149
- ptr += 4n
150
- } else if ((byte & 0xE0n) == 0xE0n) {
151
- ptr += 3n
152
- } else {
153
- ptr += 2n
154
- }
144
+ while (ptr < end) {
145
+ if (Memory.compare(ptr, pptr, psize) == 0n) {
146
+ return Some(tagSimpleNumber(idx))
155
147
  }
156
-
157
- if (result == -1n) {
158
- None
148
+ idx += 1n
149
+ let byte = WasmI32.load8U(ptr, 0n)
150
+ if ((byte & 0x80n) == 0x00n) {
151
+ ptr += 1n
152
+ } else if ((byte & 0xF0n) == 0xF0n) {
153
+ ptr += 4n
154
+ } else if ((byte & 0xE0n) == 0xE0n) {
155
+ ptr += 3n
159
156
  } else {
160
- Some(tagSimpleNumber(result))
157
+ ptr += 2n
161
158
  }
162
159
  }
160
+
161
+ return None
163
162
  }
164
163
 
165
164
  /**
@@ -174,63 +173,45 @@ export let indexOf = (search: String, string: String) => {
174
173
  * @since v0.5.3
175
174
  */
176
175
  @unsafe
177
- export let lastIndexOf = (search: String, string: String) => {
176
+ provide let lastIndexOf = (search: String, string: String) => {
178
177
  // The last possible index in the string given the length of the search
179
178
  let lastIndex = length(string) - length(search)
180
179
 
181
- let (>) = WasmI32.gtU
182
- let (>=) = WasmI32.geU
183
- let (==) = WasmI32.eq
184
- let (!=) = WasmI32.ne
185
- let (+) = WasmI32.add
186
- let (-) = WasmI32.sub
187
- let (&) = WasmI32.and
180
+ use WasmI32.{ (+), (-), (&), gtU as (>), geU as (>=), (==), (!=) }
188
181
 
189
182
  let search = WasmI32.fromGrain(search)
190
183
  let string = WasmI32.fromGrain(string)
191
184
  let searchSize = WasmI32.load(search, 4n)
192
185
  let stringSize = WasmI32.load(string, 4n)
193
186
  if (searchSize > stringSize) {
194
- None
195
- } else {
196
- let mut matchIndex = -1n
197
- let mut stringIndex = untagSimpleNumber(lastIndex)
198
- let searchPtr = search + 8n
199
- let stringStartPtr = string + 8n
200
- for (
201
- let mut stringPtr = stringStartPtr + stringSize - searchSize;
202
- stringPtr >= stringStartPtr;
203
- stringPtr -= 1n
204
- ) {
205
- let byte = WasmI32.load8U(stringPtr, 0n)
206
- if ((byte & 0xC0n) == 0x80n) {
207
- // We're not at the start of a codepoint, so move on
208
- continue
209
- }
210
- if (Memory.compare(stringPtr, searchPtr, searchSize) == 0n) {
211
- matchIndex = stringIndex
212
- break
213
- }
214
- stringIndex -= 1n
187
+ return None
188
+ }
189
+ let mut stringIndex = untagSimpleNumber(lastIndex)
190
+ let searchPtr = search + 8n
191
+ let stringStartPtr = string + 8n
192
+ for (
193
+ let mut stringPtr = stringStartPtr + stringSize - searchSize;
194
+ stringPtr >= stringStartPtr;
195
+ stringPtr -= 1n
196
+ ) {
197
+ let byte = WasmI32.load8U(stringPtr, 0n)
198
+ if ((byte & 0xC0n) == 0x80n) {
199
+ // We're not at the start of a codepoint, so move on
200
+ continue
215
201
  }
216
- if (matchIndex == -1n) {
217
- None
218
- } else {
219
- Some(tagSimpleNumber(matchIndex))
202
+ if (Memory.compare(stringPtr, searchPtr, searchSize) == 0n) {
203
+ return Some(tagSimpleNumber(stringIndex))
220
204
  }
205
+ stringIndex -= 1n
221
206
  }
207
+
208
+ return None
222
209
  }
223
210
 
224
211
  @unsafe
225
212
  let getCodePoint = (ptr: WasmI32) => {
226
213
  // Algorithm from https://encoding.spec.whatwg.org/#utf-8-decoder
227
- let (+) = WasmI32.add
228
- let (==) = WasmI32.eq
229
- let (>=) = WasmI32.geU
230
- let (<=) = WasmI32.leU
231
- let (<<) = WasmI32.shl
232
- let (&) = WasmI32.and
233
- let (|) = WasmI32.or
214
+ use WasmI32.{ (+), (&), (|), (<<), leU as (<=), geU as (>=), (==) }
234
215
 
235
216
  let mut codePoint = 0n
236
217
  let mut bytesSeen = 0n
@@ -240,15 +221,12 @@ let getCodePoint = (ptr: WasmI32) => {
240
221
 
241
222
  let mut offset = 0n
242
223
 
243
- let mut result = 0n
244
-
245
224
  while (true) {
246
225
  let byte = WasmI32.load8U(ptr + offset, 0n)
247
226
  offset += 1n
248
227
  if (bytesNeeded == 0n) {
249
228
  if (byte >= 0x00n && byte <= 0x7Fn) {
250
- result = byte
251
- break
229
+ return byte
252
230
  } else if (byte >= 0xC2n && byte <= 0xDFn) {
253
231
  bytesNeeded = 1n
254
232
  codePoint = byte & 0x1Fn
@@ -275,11 +253,10 @@ let getCodePoint = (ptr: WasmI32) => {
275
253
  codePoint = codePoint << 6n | byte & 0x3Fn
276
254
  bytesSeen += 1n
277
255
  if (bytesSeen == bytesNeeded) {
278
- result = codePoint
279
- break
256
+ return codePoint
280
257
  }
281
258
  }
282
- result
259
+ return 0n
283
260
  }
284
261
 
285
262
  @unsafe
@@ -288,22 +265,16 @@ let charAtHelp = (position, string: String) => {
288
265
  fail "Invalid offset: " ++ toString(position)
289
266
  }
290
267
  // Implementation is similar to explodeHelp, but doesn't perform unneeded memory allocations
291
- let (>>>) = WasmI32.shrU
292
- let (+) = WasmI32.add
293
- let (&) = WasmI32.and
294
- let (<) = WasmI32.ltU
295
- let (==) = WasmI32.eq
268
+ use WasmI32.{ (+), (&), (>>>), ltU as (<), (==) }
296
269
  let size = WasmI32.fromGrain(byteLength(string)) >>> 1n
297
270
  let position = WasmI32.fromGrain(position) >>> 1n
298
271
  let string = WasmI32.fromGrain(string)
299
272
  let mut ptr = string + 8n
300
273
  let end = ptr + size
301
274
  let mut counter = 0n
302
- let mut result = 0n
303
275
  while (ptr < end) {
304
276
  if (counter == position) {
305
- result = getCodePoint(ptr)
306
- break
277
+ return getCodePoint(ptr)
307
278
  }
308
279
  let byte = WasmI32.load8U(ptr, 0n)
309
280
  let n = if ((byte & 0x80n) == 0x00n) {
@@ -318,10 +289,8 @@ let charAtHelp = (position, string: String) => {
318
289
  counter += 1n
319
290
  ptr += n
320
291
  }
321
- if (WasmI32.eqz(WasmI32.fromGrain(result))) {
322
- fail "charAt: should be impossible (please report)"
323
- }
324
- result
292
+
293
+ fail "charAt: should be impossible (please report)"
325
294
  }
326
295
 
327
296
  /**
@@ -331,12 +300,15 @@ let charAtHelp = (position, string: String) => {
331
300
  * @param string: The string to search
332
301
  * @returns The character code at the provided position
333
302
  *
303
+ * @throws MalformedUnicode: When the `string` is malformed
304
+ * @throws Failure(String): When the `position` is out of bounds
305
+ *
334
306
  * @example String.charCodeAt(5, "Hello world") == 32
335
307
  *
336
308
  * @since v0.5.3
337
309
  */
338
310
  @unsafe
339
- export let charCodeAt = (position, string: String) => {
311
+ provide let charCodeAt = (position, string: String) => {
340
312
  tagSimpleNumber(charAtHelp(position, string))
341
313
  }
342
314
 
@@ -347,22 +319,21 @@ export let charCodeAt = (position, string: String) => {
347
319
  * @param string: The string to search
348
320
  * @returns The character at the provided position
349
321
  *
322
+ * @throws MalformedUnicode: When the `string` is malformed
323
+ * @throws Failure(String): When the `position` is out of bounds
324
+ *
350
325
  * @example String.charAt(5, "Hello world") == ' '
351
326
  *
352
327
  * @since v0.4.0
353
328
  */
354
329
  @unsafe
355
- export let charAt = (position, string: String) => {
330
+ provide let charAt = (position, string: String) => {
356
331
  tagChar(charAtHelp(position, string))
357
332
  }
358
333
 
359
334
  @unsafe
360
335
  let explodeHelp = (s: String, chars) => {
361
- let (>>>) = WasmI32.shrU
362
- let (+) = WasmI32.add
363
- let (&) = WasmI32.and
364
- let (<) = WasmI32.ltU
365
- let (==) = WasmI32.eq
336
+ use WasmI32.{ (+), (&), (>>>), ltU as (<), (==) }
366
337
 
367
338
  let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
368
339
  let len = WasmI32.fromGrain(length(s)) >>> 1n
@@ -409,12 +380,14 @@ let explodeHelp = (s: String, chars) => {
409
380
  * @param string: The string to split
410
381
  * @returns An array containing all characters in the string
411
382
  *
383
+ * @throws MalformedUnicode: When the `string` is malformed
384
+ *
412
385
  * @example String.explode("Hello") == [> 'H', 'e', 'l', 'l', 'o']
413
386
  *
414
387
  * @since v0.3.0
415
388
  */
416
389
  @unsafe
417
- export let explode = string => {
390
+ provide let explode = string => {
418
391
  WasmI32.toGrain(explodeHelp(string, true)): Array<Char>
419
392
  }
420
393
 
@@ -429,17 +402,19 @@ export let explode = string => {
429
402
  * @since v0.3.0
430
403
  */
431
404
  @unsafe
432
- export let implode = (arr: Array<Char>) => {
433
- let (+) = WasmI32.add
434
- let (-) = WasmI32.sub
435
- let (*) = WasmI32.mul
436
- let (<) = WasmI32.ltU
437
- let (>) = WasmI32.gtU
438
- let (<=) = WasmI32.leU
439
- let (<<) = WasmI32.shl
440
- let (>>>) = WasmI32.shrU
441
- let (&) = WasmI32.and
442
- let (|) = WasmI32.or
405
+ provide let implode = (arr: Array<Char>) => {
406
+ use WasmI32.{
407
+ (+),
408
+ (-),
409
+ (*),
410
+ (&),
411
+ (|),
412
+ (<<),
413
+ (>>>),
414
+ ltU as (<),
415
+ gtU as (>),
416
+ leU as (<=),
417
+ }
443
418
 
444
419
  let arrLength = WasmI32.load(WasmI32.fromGrain(arr), 4n)
445
420
 
@@ -483,11 +458,11 @@ export let implode = (arr: Array<Char>) => {
483
458
  count = 3n
484
459
  marker = 0xF0n
485
460
  }
486
- WasmI32.store8(str + offset, (usv >>> 6n * count) + marker, 0n)
461
+ WasmI32.store8(str + offset, (usv >>> (6n * count)) + marker, 0n)
487
462
  offset += 1n
488
463
 
489
464
  while (count > 0n) {
490
- let temp = usv >>> 6n * (count - 1n)
465
+ let temp = usv >>> (6n * (count - 1n))
491
466
  WasmI32.store8(str + offset, 0x80n | temp & 0x3Fn, 0n)
492
467
  count -= 1n
493
468
  offset += 1n
@@ -499,7 +474,7 @@ export let implode = (arr: Array<Char>) => {
499
474
  }
500
475
 
501
476
  // Helper to get the length in constant time without depending on Array
502
- primitive arrayLength: Array<a> -> Number = "@array.length"
477
+ primitive arrayLength = "@array.length"
503
478
 
504
479
  /**
505
480
  * Create a string that is the given string reversed.
@@ -511,7 +486,7 @@ primitive arrayLength: Array<a> -> Number = "@array.length"
511
486
  *
512
487
  * @since v0.4.5
513
488
  */
514
- export let reverse = string => {
489
+ provide let reverse = string => {
515
490
  let mut arr = explode(string)
516
491
  let len = arrayLength(arr)
517
492
  let halfLen = len / 2
@@ -532,18 +507,13 @@ export let reverse = string => {
532
507
  * @param string: The string to split
533
508
  * @returns An array of substrings from the initial string
534
509
  *
510
+ * @throws MalformedUnicode: When the `string` is malformed
511
+ *
535
512
  * @example String.split(" ", "Hello world") == [> "Hello", "world"]
536
513
  */
537
514
  @unsafe
538
- export let split = (separator: String, string: String) => {
539
- let (+) = WasmI32.add
540
- let (-) = WasmI32.sub
541
- let (==) = WasmI32.eq
542
- let (<) = WasmI32.ltU
543
- let (>) = WasmI32.gtU
544
- let (<<) = WasmI32.shl
545
- let (>>) = WasmI32.shrS
546
- let (&) = WasmI32.and
515
+ provide let split = (separator: String, string: String) => {
516
+ use WasmI32.{ (+), (-), (&), (<<), (>>), ltU as (<), gtU as (>), (==) }
547
517
 
548
518
  let size = WasmI32.fromGrain(byteLength(string)) >> 1n
549
519
  let psize = WasmI32.fromGrain(byteLength(separator)) >> 1n
@@ -620,27 +590,25 @@ export let split = (separator: String, string: String) => {
620
590
  * Get a portion of a string.
621
591
  *
622
592
  * @param start: The start position of the substring
623
- * @param to: The end position of the substring, exclusive
593
+ * @param end: The end position of the substring, exclusive
624
594
  * @param string: The input string
625
595
  * @returns The substring from the initial string
626
596
  *
627
- * @example String.slice(0, 5, "Hello world") == "Hello"
597
+ * @throws InvalidArgument(String): When the `start` index is not an integer
598
+ * @throws InvalidArgument(String): When the `to` index is not an integer
599
+ * @throws IndexOutOfBounds: When `start` is out of bounds
600
+ * @throws IndexOutOfBounds: When `end` is out of bounds
601
+ * @throws InvalidArgument(String): When `start` is greater than `end`
602
+ *
603
+ * @example String.slice(0, end=5, "Hello world") == "Hello"
604
+ * @example String.slice(0, "Hello world") == "Hello world"
628
605
  *
629
606
  * @since v0.1.0
607
+ * @history v0.6.0: Default `end` to the String length
630
608
  */
631
609
  @unsafe
632
- export let slice = (start: Number, to: Number, string: String) => {
633
- let (+) = WasmI32.add
634
- let (-) = WasmI32.sub
635
- let (==) = WasmI32.eq
636
- let (!=) = WasmI32.ne
637
- let (<) = WasmI32.ltS
638
- let (>) = WasmI32.gtS
639
- let (<<) = WasmI32.shl
640
- let (>>) = WasmI32.shrS
641
- let (&) = WasmI32.and
642
- let startOrig = start
643
- let toOrig = to
610
+ provide let slice = (start: Number, end=length(string), string: String) => {
611
+ use WasmI32.{ (+), (-), (&), (<<), (>>), (<), (>), (==), (!=) }
644
612
 
645
613
  let len = WasmI32.fromGrain(length(string)) >> 1n
646
614
  let size = WasmI32.fromGrain(byteLength(string)) >> 1n
@@ -653,7 +621,7 @@ export let slice = (start: Number, to: Number, string: String) => {
653
621
  }
654
622
  start = start >> 1n
655
623
 
656
- let mut to = WasmI32.fromGrain(to)
624
+ let mut to = WasmI32.fromGrain(end)
657
625
  if ((to & 1n) != 1n) {
658
626
  throw InvalidArgument("Invalid end index")
659
627
  }
@@ -667,7 +635,7 @@ export let slice = (start: Number, to: Number, string: String) => {
667
635
  }
668
636
 
669
637
  if (start > len || to > len) {
670
- throw Exception.IndexOutOfBounds
638
+ throw IndexOutOfBounds
671
639
  }
672
640
 
673
641
  if (to < start) {
@@ -721,20 +689,21 @@ export let slice = (start: Number, to: Number, string: String) => {
721
689
  * @since v0.1.0
722
690
  */
723
691
  @unsafe
724
- export let contains = (search: String, string: String) => {
692
+ provide let contains = (search: String, string: String) => {
725
693
  // "Not So Naive" string search algorithm
726
694
  // searching phase in O(nm) time complexity
727
695
  // slightly (by coefficient) sub-linear in the average case
728
696
  // http://igm.univ-mlv.fr/~lecroq/string/node13.html#SECTION00130
729
-
730
- let (+) = WasmI32.add
731
- let (-) = WasmI32.sub
732
- let (==) = WasmI32.eq
733
- let (!=) = WasmI32.ne
734
- let (<) = WasmI32.ltU
735
- let (<=) = WasmI32.leU
736
- let (>) = WasmI32.gtU
737
- let (>>) = WasmI32.shrS
697
+ use WasmI32.{
698
+ (+),
699
+ (-),
700
+ (>>),
701
+ ltU as (<),
702
+ gtU as (>),
703
+ leU as (<=),
704
+ (==),
705
+ (!=),
706
+ }
738
707
  let pOrig = search
739
708
  let sOrig = string
740
709
 
@@ -747,56 +716,58 @@ export let contains = (search: String, string: String) => {
747
716
  string += 8n
748
717
  search += 8n
749
718
 
750
- let mut j = 0n, k = 0n, ell = 0n
719
+ let mut j = 0n
720
+ and k = 0n
721
+ and ell = 0n
751
722
 
752
723
  if (m > n) {
753
724
  // Bail if pattern length is longer than input length
754
- false
755
- } else if (m < 2n) {
725
+ return false
726
+ }
727
+
728
+ if (m < 2n) {
756
729
  // Handle very small patterns
757
730
  if (m == 0n) {
758
- true
759
- } else {
760
- let pat = WasmI32.load8U(search, 0n)
761
- let mut result = false
762
- while (j < n) {
763
- if (pat == WasmI32.load8U(string + j, 0n)) {
764
- result = true
765
- break
766
- } else {
767
- j += 1n
768
- }
731
+ return true
732
+ }
733
+
734
+ let pat = WasmI32.load8U(search, 0n)
735
+ while (j < n) {
736
+ if (pat == WasmI32.load8U(string + j, 0n)) {
737
+ return true
738
+ } else {
739
+ j += 1n
769
740
  }
770
- result
771
741
  }
742
+
743
+ return false
744
+ }
745
+
746
+ // NSM preprocessing
747
+ if (WasmI32.load8U(search, 0n) == WasmI32.load8U(search, 1n)) {
748
+ k = 2n
749
+ ell = 1n
772
750
  } else {
773
- // NSM preprocessing
774
- if (WasmI32.load8U(search, 0n) == WasmI32.load8U(search, 1n)) {
775
- k = 2n
776
- ell = 1n
777
- } else {
778
- k = 1n
779
- ell = 2n
780
- }
751
+ k = 1n
752
+ ell = 2n
753
+ }
781
754
 
782
- let mut result = false
783
- // NSM searching
784
- while (j <= n - m) {
785
- if (WasmI32.load8U(search, 1n) != WasmI32.load8U(string + j, 1n)) {
786
- j += k
787
- } else {
788
- if (
789
- Memory.compare(search + 2n, string + j + 2n, m - 2n) == 0n &&
790
- WasmI32.load8U(search, 0n) == WasmI32.load8U(string + j, 0n)
791
- ) {
792
- result = true
793
- break
794
- }
795
- j += ell
755
+ // NSM searching
756
+ while (j <= n - m) {
757
+ if (WasmI32.load8U(search, 1n) != WasmI32.load8U(string + j, 1n)) {
758
+ j += k
759
+ } else {
760
+ if (
761
+ Memory.compare(search + 2n, string + j + 2n, m - 2n) == 0n &&
762
+ WasmI32.load8U(search, 0n) == WasmI32.load8U(string + j, 0n)
763
+ ) {
764
+ return true
796
765
  }
766
+ j += ell
797
767
  }
798
- result
799
768
  }
769
+
770
+ return false
800
771
  }
801
772
 
802
773
  /**
@@ -811,10 +782,8 @@ export let contains = (search: String, string: String) => {
811
782
  * @since v0.1.0
812
783
  */
813
784
  @unsafe
814
- export let startsWith = (search: String, string: String) => {
815
- let (+) = WasmI32.add
816
- let (>) = WasmI32.gtU
817
- let (==) = WasmI32.eq
785
+ provide let startsWith = (search: String, string: String) => {
786
+ use WasmI32.{ (+), gtU as (>), (==) }
818
787
  let pOrig = search
819
788
  let sOrig = string
820
789
 
@@ -847,11 +816,8 @@ export let startsWith = (search: String, string: String) => {
847
816
  * @since v0.1.0
848
817
  */
849
818
  @unsafe
850
- export let endsWith = (search: String, string: String) => {
851
- let (+) = WasmI32.add
852
- let (-) = WasmI32.sub
853
- let (>) = WasmI32.gtU
854
- let (==) = WasmI32.eq
819
+ provide let endsWith = (search: String, string: String) => {
820
+ use WasmI32.{ (+), (-), gtU as (>), (==) }
855
821
  let pOrig = search
856
822
  let sOrig = string
857
823
 
@@ -885,17 +851,12 @@ export let endsWith = (search: String, string: String) => {
885
851
  * @since v0.5.4
886
852
  */
887
853
  @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
854
+ provide let replaceFirst = (
855
+ searchPattern: String,
856
+ replacement: String,
857
+ string: String,
858
+ ) => {
859
+ use WasmI32.{ (+), (-), gtU as (>), ltU as (<), (==) }
899
860
 
900
861
  let mut patternPtr = WasmI32.fromGrain(searchPattern)
901
862
  let mut stringPtr = WasmI32.fromGrain(string)
@@ -906,25 +867,19 @@ export let replaceFirst =
906
867
  let replacementLen = WasmI32.load(replacementPtr, 4n)
907
868
  // Bail if search str is longer than the string
908
869
  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) {
870
+ return string
871
+ }
872
+ patternPtr += 8n
873
+ stringPtr += 8n
874
+ replacementPtr += 8n
875
+
876
+ // Search for an instance of the string
877
+ let mut foundIndex = -1n
878
+ let stringEndPtr = stringPtr + stringLen - patternLen + 1n
879
+ for (let mut i = stringPtr; i < stringEndPtr; i += 1n) {
880
+ // check for match
881
+ foundIndex += 1n
882
+ if (Memory.compare(i, patternPtr, patternLen) == 0n) {
928
883
  // Create the new string
929
884
  let str = allocateString(stringLen - patternLen + replacementLen)
930
885
  let strPtr = str + 8n
@@ -936,11 +891,11 @@ export let replaceFirst =
936
891
  stringLen - foundIndex
937
892
  )
938
893
  // Copy over the str
939
- WasmI32.toGrain(str): String
940
- } else {
941
- string
894
+ return WasmI32.toGrain(str): String
942
895
  }
943
896
  }
897
+
898
+ return string
944
899
  }
945
900
 
946
901
  /**
@@ -956,17 +911,12 @@ export let replaceFirst =
956
911
  * @since v0.5.4
957
912
  */
958
913
  @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
914
+ provide let replaceLast = (
915
+ searchPattern: String,
916
+ replacement: String,
917
+ string: String,
918
+ ) => {
919
+ use WasmI32.{ (+), (-), gtU as (>), ltU as (<), (==) }
970
920
 
971
921
  let mut patternPtr = WasmI32.fromGrain(searchPattern)
972
922
  let mut stringPtr = WasmI32.fromGrain(string)
@@ -978,25 +928,20 @@ export let replaceLast =
978
928
 
979
929
  // Bail if search str is longer than the string
980
930
  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) {
931
+ return string
932
+ }
933
+ patternPtr += 8n
934
+ stringPtr += 8n
935
+ replacementPtr += 8n
936
+
937
+ let mut found = false
938
+ // Search for an instance of the string
939
+ let stringEndPtr = stringPtr + stringLen - patternLen
940
+ let mut foundIndex = stringLen - patternLen + 1n
941
+ for (let mut i = stringEndPtr; i > stringPtr - 1n; i -= 1n) {
942
+ // check for match
943
+ foundIndex -= 1n
944
+ if (Memory.compare(i, patternPtr, patternLen) == 0n) {
1000
945
  // Create the new string
1001
946
  let str = allocateString(stringLen - patternLen + replacementLen)
1002
947
  let strPtr = str + 8n
@@ -1008,11 +953,11 @@ export let replaceLast =
1008
953
  stringLen - foundIndex
1009
954
  )
1010
955
  // Copy over the str
1011
- WasmI32.toGrain(str): String
1012
- } else {
1013
- string
956
+ return WasmI32.toGrain(str): String
1014
957
  }
1015
958
  }
959
+
960
+ return string
1016
961
  }
1017
962
 
1018
963
  /**
@@ -1028,19 +973,12 @@ export let replaceLast =
1028
973
  * @since v0.5.4
1029
974
  */
1030
975
  @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
976
+ provide let replaceAll = (
977
+ searchPattern: String,
978
+ replacement: String,
979
+ string: String,
980
+ ) => {
981
+ use WasmI32.{ (+), (-), (*), (>>), gtU as (>), ltU as (<), (==) }
1044
982
 
1045
983
  let mut patternPtr = WasmI32.fromGrain(searchPattern)
1046
984
  let mut stringPtr = WasmI32.fromGrain(string)
@@ -1071,6 +1009,8 @@ export let replaceAll =
1071
1009
  found = true
1072
1010
  foundCount += 1n
1073
1011
  foundIndexes = [tagSimpleNumber(foundIndex), ...foundIndexes]
1012
+ foundIndex -= patternLen - 1n
1013
+ i -= patternLen - 1n
1074
1014
  }
1075
1015
  }
1076
1016
  if (found) {
@@ -1114,13 +1054,14 @@ let _OFFSET_NAME = "offset"
1114
1054
 
1115
1055
  @unsafe
1116
1056
  let grainToWasmNumber = (num, name) => {
1057
+ use WasmI32.{ (&), (>>), (<) }
1117
1058
  let num = WasmI32.fromGrain(num)
1118
- if (WasmI32.eqz(WasmI32.and(num, 1n))) {
1059
+ if (WasmI32.eqz(num & 1n)) {
1119
1060
  let str = " argument must be an integer"
1120
1061
  throw Exception.InvalidArgument(name ++ str)
1121
1062
  }
1122
- let num = WasmI32.shrS(num, 1n)
1123
- if (WasmI32.ltS(num, 0n)) {
1063
+ let num = num >> 1n
1064
+ if (num < 0n) {
1124
1065
  let str = " argument must be non-negative"
1125
1066
  throw Exception.InvalidArgument(name ++ str)
1126
1067
  }
@@ -1129,12 +1070,7 @@ let grainToWasmNumber = (num, name) => {
1129
1070
 
1130
1071
  @unsafe
1131
1072
  let utf16Length = (s: String) => {
1132
- let (>>>) = WasmI32.shrU
1133
- let (<<) = WasmI32.shl
1134
- let (+) = WasmI32.add
1135
- let (&) = WasmI32.and
1136
- let (<) = WasmI32.ltU
1137
- let (==) = WasmI32.eq
1073
+ use WasmI32.{ (+), (&), (>>>), (<<), ltU as (<), (==) }
1138
1074
 
1139
1075
  let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
1140
1076
  let len = WasmI32.fromGrain(length(s)) >>> 1n
@@ -1184,22 +1120,14 @@ let mut _BYTES_SIZE_OFFSET = 4n
1184
1120
  let mut _BYTES_OFFSET = 8n
1185
1121
 
1186
1122
  @unsafe
1187
- let encodeAtHelp =
1188
- (
1189
- string: String,
1190
- encoding: Encoding,
1191
- includeBom: Bool,
1192
- dest: Bytes,
1193
- destPos: Number,
1194
- ) => {
1195
- let (>>>) = WasmI32.shrU
1196
- let (-) = WasmI32.sub
1197
- let (&) = WasmI32.and
1198
- let (<) = WasmI32.ltU
1199
- let (>) = WasmI32.gtS
1200
- let (<=) = WasmI32.leU
1201
- let (==) = WasmI32.eq
1202
- let (+) = WasmI32.add
1123
+ let encodeAtHelp = (
1124
+ string: String,
1125
+ encoding: Encoding,
1126
+ includeBom: Bool,
1127
+ dest: Bytes,
1128
+ destPos: Number,
1129
+ ) => {
1130
+ use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), (>), leU as (<=), (==) }
1203
1131
  let byteSize = WasmI32.fromGrain(byteLength(string)) >>> 1n
1204
1132
  let len = WasmI32.fromGrain(length(string)) >>> 1n
1205
1133
 
@@ -1217,7 +1145,7 @@ let encodeAtHelp =
1217
1145
  match (encoding) {
1218
1146
  UTF8 => {
1219
1147
  if (bytesIdx + 3n > destSize) {
1220
- throw Exception.IndexOutOfBounds
1148
+ throw IndexOutOfBounds
1221
1149
  }
1222
1150
  WasmI32.store8(bytes + bytesIdx, 0xEFn, _BYTES_OFFSET)
1223
1151
  WasmI32.store8(bytes + bytesIdx + 1n, 0xBBn, _BYTES_OFFSET)
@@ -1226,7 +1154,7 @@ let encodeAtHelp =
1226
1154
  },
1227
1155
  UTF16_BE => {
1228
1156
  if (bytesIdx + 2n > destSize) {
1229
- throw Exception.IndexOutOfBounds
1157
+ throw IndexOutOfBounds
1230
1158
  }
1231
1159
  WasmI32.store8(bytes + bytesIdx, 0xFEn, _BYTES_OFFSET)
1232
1160
  WasmI32.store8(bytes + bytesIdx + 1n, 0xFFn, _BYTES_OFFSET)
@@ -1234,7 +1162,7 @@ let encodeAtHelp =
1234
1162
  },
1235
1163
  UTF16_LE => {
1236
1164
  if (bytesIdx + 2n > destSize) {
1237
- throw Exception.IndexOutOfBounds
1165
+ throw IndexOutOfBounds
1238
1166
  }
1239
1167
  WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_OFFSET)
1240
1168
  WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_OFFSET)
@@ -1242,7 +1170,7 @@ let encodeAtHelp =
1242
1170
  },
1243
1171
  UTF32_BE => {
1244
1172
  if (bytesIdx + 4n > destSize) {
1245
- throw Exception.IndexOutOfBounds
1173
+ throw IndexOutOfBounds
1246
1174
  }
1247
1175
  WasmI32.store8(bytes + bytesIdx, 0n, _BYTES_OFFSET)
1248
1176
  WasmI32.store8(bytes + bytesIdx + 1n, 0n, _BYTES_OFFSET)
@@ -1252,7 +1180,7 @@ let encodeAtHelp =
1252
1180
  },
1253
1181
  UTF32_LE => {
1254
1182
  if (bytesIdx + 4n > destSize) {
1255
- throw Exception.IndexOutOfBounds
1183
+ throw IndexOutOfBounds
1256
1184
  }
1257
1185
  WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_OFFSET)
1258
1186
  WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_OFFSET)
@@ -1269,7 +1197,7 @@ let encodeAtHelp =
1269
1197
  // the target encoding is UTF8 as well, then copy the entire memory range
1270
1198
  // in bulk. No need to iterate individual characters.
1271
1199
  if (bytesIdx + byteSize > destSize) {
1272
- throw Exception.IndexOutOfBounds
1200
+ throw IndexOutOfBounds
1273
1201
  }
1274
1202
  Memory.copy(bytes + bytesIdx + _BYTES_OFFSET, ptr, byteSize)
1275
1203
  },
@@ -1291,7 +1219,7 @@ let encodeAtHelp =
1291
1219
  // With the optimization above for bulk memory copy, this match
1292
1220
  // should never occur for the UTF8 case.
1293
1221
  if (bytesIdx + n > destSize) {
1294
- throw Exception.IndexOutOfBounds
1222
+ throw IndexOutOfBounds
1295
1223
  }
1296
1224
  Memory.copy(bytes + bytesIdx + _BYTES_OFFSET, ptr, n)
1297
1225
  bytesIdx += n
@@ -1301,7 +1229,7 @@ let encodeAtHelp =
1301
1229
  if (codePoint <= 0xFFFFn) {
1302
1230
  // <hi><lo>
1303
1231
  if (bytesIdx + 2n > destSize) {
1304
- throw Exception.IndexOutOfBounds
1232
+ throw IndexOutOfBounds
1305
1233
  }
1306
1234
  WasmI32.store8(
1307
1235
  bytes + bytesIdx,
@@ -1317,13 +1245,11 @@ let encodeAtHelp =
1317
1245
  } else {
1318
1246
  // https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF
1319
1247
  if (bytesIdx + 4n > destSize) {
1320
- throw Exception.IndexOutOfBounds
1248
+ throw IndexOutOfBounds
1321
1249
  }
1322
1250
  let uPrime = codePoint - 0x10000n
1323
- let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) +
1324
- 0xD800n // High surrogate
1325
- let w2 = (uPrime & 0b00000000001111111111n) +
1326
- 0xDC00n // Low surrogate
1251
+ let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
1252
+ let w2 = (uPrime & 0b00000000001111111111n) + 0xDC00n // Low surrogate
1327
1253
  WasmI32.store8(
1328
1254
  bytes + bytesIdx,
1329
1255
  (w1 & 0xff00n) >>> 8n,
@@ -1343,7 +1269,7 @@ let encodeAtHelp =
1343
1269
  let codePoint = getCodePoint(ptr)
1344
1270
  if (codePoint <= 0xFFFFn) {
1345
1271
  if (bytesIdx + 2n > destSize) {
1346
- throw Exception.IndexOutOfBounds
1272
+ throw IndexOutOfBounds
1347
1273
  }
1348
1274
  // <lo><hi>
1349
1275
  WasmI32.store8(bytes + bytesIdx, codePoint & 0xffn, _BYTES_OFFSET)
@@ -1356,13 +1282,11 @@ let encodeAtHelp =
1356
1282
  } else {
1357
1283
  // https://en.wikipedia.org/wiki/UTF-16#Code_points_from_U+010000_to_U+10FFFF
1358
1284
  if (bytesIdx + 4n > destSize) {
1359
- throw Exception.IndexOutOfBounds
1285
+ throw IndexOutOfBounds
1360
1286
  }
1361
1287
  let uPrime = codePoint - 0x10000n
1362
- let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) +
1363
- 0xD800n // High surrogate
1364
- let w2 = (uPrime & 0b00000000001111111111n) +
1365
- 0xDC00n // Low surrogate
1288
+ let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
1289
+ let w2 = (uPrime & 0b00000000001111111111n) + 0xDC00n // Low surrogate
1366
1290
  WasmI32.store8(bytes + bytesIdx, w1 & 0xffn, _BYTES_OFFSET)
1367
1291
  WasmI32.store8(
1368
1292
  bytes + bytesIdx + 1n,
@@ -1380,7 +1304,7 @@ let encodeAtHelp =
1380
1304
  },
1381
1305
  UTF32_BE => {
1382
1306
  if (bytesIdx + 4n > destSize) {
1383
- throw Exception.IndexOutOfBounds
1307
+ throw IndexOutOfBounds
1384
1308
  }
1385
1309
  let codePoint = getCodePoint(ptr)
1386
1310
  WasmI32.store8(
@@ -1407,7 +1331,7 @@ let encodeAtHelp =
1407
1331
  },
1408
1332
  UTF32_LE => {
1409
1333
  if (bytesIdx + 4n > destSize) {
1410
- throw Exception.IndexOutOfBounds
1334
+ throw IndexOutOfBounds
1411
1335
  }
1412
1336
  let codePoint = getCodePoint(ptr)
1413
1337
  WasmI32.store8(bytes + bytesIdx, codePoint & 0xffn, _BYTES_OFFSET)
@@ -1438,92 +1362,63 @@ let encodeAtHelp =
1438
1362
  }
1439
1363
 
1440
1364
  /**
1441
- * Encodes the given string into a byte sequence at the supplied position, excluding any byte-order marker, using the encoding scheme provided.
1365
+ * Encodes the given string into a byte sequence at the supplied position using the encoding scheme provided.
1442
1366
  *
1443
1367
  * @param string: The input string
1444
1368
  * @param encoding: The encoding to use
1445
1369
  * @param dest: The byte sequence that will be copied
1446
1370
  * @param destPos: The location in the byte sequence to write the output
1371
+ * @param includeBom: Whether or not to include a byte order marker (false by default)
1447
1372
  * @returns A copy of the input bytes with the encoded string replaced at the given position
1448
1373
  *
1449
- * @since v0.4.0
1450
- */
1451
- export let encodeAt = (string, encoding, dest, destPos) => {
1452
- encodeAtHelp(string, encoding, false, dest, destPos)
1453
- }
1454
-
1455
- /**
1456
- * Encodes the given string into a byte sequence at the supplied position, including any byte-order marker, using the encoding scheme provided.
1457
- *
1458
- * @param string: The input string
1459
- * @param encoding: The encoding to use
1460
- * @param dest: The byte sequence that will be copied
1461
- * @param destPos: The location in the byte sequence to write the output
1462
- * @returns A copy of the input bytes with the encoded string replaced at the given position
1374
+ * @throws InvalidArgument(String): When `destPos` is not an integer
1375
+ * @throws InvalidArgument(String): When `destPos` is negative
1463
1376
  *
1464
1377
  * @since v0.4.0
1378
+ * @history v0.6.0: Added `includeBom` default argument
1465
1379
  */
1466
- export let encodeAtWithBom = (string, encoding, dest, destPos) => {
1467
- encodeAtHelp(string, encoding, true, dest, destPos)
1380
+ provide let encodeAt = (string, encoding, dest, destPos, includeBom=false) => {
1381
+ encodeAtHelp(string, encoding, includeBom, dest, destPos)
1468
1382
  }
1469
1383
 
1470
1384
  @unsafe
1471
1385
  let encodeHelp = (string: String, encoding: Encoding, includeBom: Bool) => {
1472
- let size = encodedLength(string, encoding) +
1473
- (if (includeBom) {
1474
- match (encoding) {
1475
- UTF8 => 3,
1476
- UTF16_LE => 2,
1477
- UTF16_BE => 2,
1478
- UTF32_LE => 4,
1479
- UTF32_BE => 4,
1480
- }
1481
- } else {
1482
- 0
1483
- })
1484
- let (>>>) = WasmI32.shrU
1386
+ let size = encodedLength(string, encoding) + (if (includeBom) {
1387
+ match (encoding) {
1388
+ UTF8 => 3,
1389
+ UTF16_LE => 2,
1390
+ UTF16_BE => 2,
1391
+ UTF32_LE => 4,
1392
+ UTF32_BE => 4,
1393
+ }
1394
+ } else {
1395
+ 0
1396
+ })
1397
+ use WasmI32.{ (>>>) }
1485
1398
  let bytes = WasmI32.toGrain(allocateBytes(WasmI32.fromGrain(size) >>> 1n))
1486
1399
  encodeAtHelp(string, encoding, includeBom, bytes, 0)
1487
1400
  }
1488
1401
 
1489
1402
  /**
1490
- * Encodes the given string using the given encoding scheme, excluding any byte-order marker.
1403
+ * Encodes the given string using the given encoding scheme.
1491
1404
  *
1492
1405
  * @param string: The input string
1493
1406
  * @param encoding: The encoding to use
1407
+ * @param includeBom: Whether or not to include a byte order marker (false by default)
1494
1408
  * @returns The byte representation of the string in the given encoding
1495
1409
  *
1496
1410
  * @since v0.4.0
1411
+ * @history v0.6.0: Added `includeBom` default argument
1497
1412
  */
1498
- export let encode = (string: String, encoding: Encoding) => {
1499
- encodeHelp(string, encoding, false)
1500
- }
1501
-
1502
- /**
1503
- * Encodes the given string using the given encoding scheme, including any byte-order marker.
1504
- *
1505
- * @param string: The input string
1506
- * @param encoding: The encoding to use
1507
- * @returns The byte representation of the string in the given encoding
1508
- *
1509
- * @since v0.4.0
1510
- */
1511
- export let encodeWithBom = (string: String, encoding: Encoding) => {
1512
- encodeHelp(string, encoding, true)
1413
+ provide let encode = (string: String, encoding: Encoding, includeBom=false) => {
1414
+ encodeHelp(string, encoding, includeBom)
1513
1415
  }
1514
1416
 
1515
1417
  // Byte->String decoding and helper functions:
1516
1418
 
1517
1419
  @unsafe
1518
1420
  let writeUtf8CodePoint = (ptr, codePoint) => {
1519
- let (>>>) = WasmI32.shrU
1520
- let (-) = WasmI32.sub
1521
- let (&) = WasmI32.and
1522
- let (|) = WasmI32.or
1523
- let (+) = WasmI32.add
1524
- let (<) = WasmI32.ltU
1525
- let (<=) = WasmI32.leU
1526
- let (==) = WasmI32.eq
1421
+ use WasmI32.{ (+), (-), (&), (|), (>>>), ltU as (<), leU as (<=), (==) }
1527
1422
  if (codePoint <= 0x007Fn) {
1528
1423
  // Code points in the ASCII range are written as just one byte with the
1529
1424
  // leading bit equal to zero (0xxxxxxx). Just store the value as one byte
@@ -1573,9 +1468,7 @@ let writeUtf8CodePoint = (ptr, codePoint) => {
1573
1468
 
1574
1469
  @unsafe
1575
1470
  let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1576
- let (+) = WasmI32.add
1577
- let (==) = WasmI32.eq
1578
- let (>=) = WasmI32.geU
1471
+ use WasmI32.{ (+), geU as (>=), (==) }
1579
1472
  let ptr = WasmI32.fromGrain(bytes)
1580
1473
  let bytesSize = WasmI32.load(ptr, 4n)
1581
1474
  let ptr = ptr + start
@@ -1614,25 +1507,26 @@ let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1614
1507
  }
1615
1508
 
1616
1509
  @unsafe
1617
- let decodedLength =
1618
- (
1619
- bytes: Bytes,
1620
- encoding: Encoding,
1621
- start: WasmI32,
1622
- size: WasmI32,
1623
- ) => {
1624
- let (+) = WasmI32.add
1625
- let (-) = WasmI32.sub
1626
- let (==) = WasmI32.eq
1627
- let (!=) = WasmI32.ne
1628
- let (<) = WasmI32.ltU
1629
- let (>) = WasmI32.gtU
1630
- let (|) = WasmI32.or
1631
- let (&) = WasmI32.and
1632
- let (>>>) = WasmI32.shrU
1633
- let (<<) = WasmI32.shl
1634
- let (>=) = WasmI32.geU
1635
- let (<=) = WasmI32.leU
1510
+ let decodedLength = (
1511
+ bytes: Bytes,
1512
+ encoding: Encoding,
1513
+ start: WasmI32,
1514
+ size: WasmI32,
1515
+ ) => {
1516
+ use WasmI32.{
1517
+ (+),
1518
+ (-),
1519
+ (&),
1520
+ (|),
1521
+ (>>>),
1522
+ (<<),
1523
+ ltU as (<),
1524
+ gtU as (>),
1525
+ geU as (>=),
1526
+ leU as (<=),
1527
+ (==),
1528
+ (!=),
1529
+ }
1636
1530
  let ptr = WasmI32.fromGrain(bytes)
1637
1531
  let bytesSize = {
1638
1532
  let tmp = WasmI32.load(ptr, 4n) - start
@@ -1783,24 +1677,25 @@ let decodedLength =
1783
1677
  }
1784
1678
 
1785
1679
  @unsafe
1786
- let decodeRangeHelp =
1787
- (
1788
- bytes: Bytes,
1789
- encoding: Encoding,
1790
- skipBom: Bool,
1791
- start: Number,
1792
- size: Number,
1793
- ) => {
1794
- let (+) = WasmI32.add
1795
- let (-) = WasmI32.sub
1796
- let (<) = WasmI32.ltU
1797
- let (|) = WasmI32.or
1798
- let (&) = WasmI32.and
1799
- let (>>>) = WasmI32.shrU
1800
- let (<<) = WasmI32.shl
1801
- let (>=) = WasmI32.geU
1802
- let (<=) = WasmI32.leU
1803
- let (==) = WasmI32.eq
1680
+ let decodeRangeHelp = (
1681
+ bytes: Bytes,
1682
+ encoding: Encoding,
1683
+ skipBom: Bool,
1684
+ start: Number,
1685
+ size: Number,
1686
+ ) => {
1687
+ use WasmI32.{
1688
+ (+),
1689
+ (-),
1690
+ (&),
1691
+ (|),
1692
+ (>>>),
1693
+ (<<),
1694
+ ltU as (<),
1695
+ geU as (>=),
1696
+ leU as (<=),
1697
+ (==),
1698
+ }
1804
1699
  let start = grainToWasmNumber(start, _START_NAME)
1805
1700
  let size = grainToWasmNumber(size, _SIZE_NAME)
1806
1701
  let hasBom = bytesHaveBom(bytes, encoding, start)
@@ -1844,8 +1739,10 @@ let decodeRangeHelp =
1844
1739
  let codeWord = if (w1 >= 0xD800n && w1 <= 0xDBFFn) {
1845
1740
  // high surrogate. next character is low srurrogate
1846
1741
  let w1 = (w1 & 0x03FFn) << 10n
1847
- let w2 = (WasmI32.load8U(bytesPtr, 2n) << 8n |
1848
- WasmI32.load8U(bytesPtr, 3n)) &
1742
+ let w2 = (
1743
+ WasmI32.load8U(bytesPtr, 2n) << 8n |
1744
+ WasmI32.load8U(bytesPtr, 3n)
1745
+ ) &
1849
1746
  0x03FFn
1850
1747
  let codeWord = w1 + w2 + 0x10000n
1851
1748
  // no problems, so go past both code words
@@ -1867,8 +1764,10 @@ let decodeRangeHelp =
1867
1764
  let codeWord = if (w1 >= 0xD800n && w1 <= 0xDBFFn) {
1868
1765
  // high surrogate. next character is low srurrogate
1869
1766
  let w1 = (w1 & 0x03FFn) << 10n
1870
- let w2 = (WasmI32.load8U(bytesPtr, 3n) << 8n |
1871
- WasmI32.load8U(bytesPtr, 2n)) &
1767
+ let w2 = (
1768
+ WasmI32.load8U(bytesPtr, 3n) << 8n |
1769
+ WasmI32.load8U(bytesPtr, 2n)
1770
+ ) &
1872
1771
  0x03FFn
1873
1772
  //let uPrime = codePoint - 0x10000n
1874
1773
  //let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
@@ -1912,45 +1811,31 @@ let decodeRangeHelp =
1912
1811
  }
1913
1812
 
1914
1813
  /**
1915
- * Decodes the given byte sequence of the specified range into a string, excluding any byte-order marker, using encoding scheme provided.
1814
+ * Decodes the given byte sequence of the specified range into a string using the encoding scheme provided.
1916
1815
  *
1917
1816
  * @param bytes: The input bytes
1918
1817
  * @param encoding: The encoding to use
1919
1818
  * @param start: The byte offset to begin decoding from
1920
1819
  * @param size: The maximum number of bytes to decode
1820
+ * @param keepBom: Whether or not to include a byte order marker (false by default)
1921
1821
  * @returns The decoded string
1922
1822
  *
1923
- * @since v0.4.0
1924
- */
1925
- export let decodeRange =
1926
- (
1927
- bytes: Bytes,
1928
- encoding: Encoding,
1929
- start: Number,
1930
- size: Number,
1931
- ) => {
1932
- decodeRangeHelp(bytes, encoding, true, start, size)
1933
- }
1934
-
1935
- /**
1936
- * Decodes the given byte sequence of the specified range into a string, including any byte-order marker, using encoding scheme provided.
1937
- *
1938
- * @param bytes: The input bytes
1939
- * @param encoding: The encoding to use
1940
- * @param start: The byte offset to begin decoding from
1941
- * @param size: The maximum number of bytes to decode
1942
- * @returns The decoded string
1823
+ * @throws InvalidArgument(String): When `start` is not an integer
1824
+ * @throws InvalidArgument(String): When `start` is negative
1825
+ * @throws InvalidArgument(String): When `size` is not an integer
1826
+ * @throws InvalidArgument(String): When `size` is negative
1943
1827
  *
1944
1828
  * @since v0.4.0
1829
+ * @history v0.6.0: Added `keepBom` default argument
1945
1830
  */
1946
- export let decodeRangeKeepBom =
1947
- (
1948
- bytes: Bytes,
1949
- encoding: Encoding,
1950
- start: Number,
1951
- size: Number,
1952
- ) => {
1953
- decodeRangeHelp(bytes, encoding, false, start, size)
1831
+ provide let decodeRange = (
1832
+ bytes: Bytes,
1833
+ encoding: Encoding,
1834
+ start: Number,
1835
+ size: Number,
1836
+ keepBom=false,
1837
+ ) => {
1838
+ decodeRangeHelp(bytes, encoding, !keepBom, start, size)
1954
1839
  }
1955
1840
 
1956
1841
  @unsafe
@@ -1961,29 +1846,18 @@ let decodeHelp = (bytes: Bytes, encoding: Encoding, skipBom: Bool) => {
1961
1846
  }
1962
1847
 
1963
1848
  /**
1964
- * Decodes the given byte sequence into a string using the given encoding scheme, excluding any byte-order marker.
1849
+ * Decodes the given byte sequence into a string using the given encoding scheme.
1965
1850
  *
1966
1851
  * @param bytes: The input bytes
1967
1852
  * @param encoding: The encoding to use
1853
+ * @param keepBom: Whether or not to include a byte order marker (false by default)
1968
1854
  * @returns The decoded string
1969
1855
  *
1970
1856
  * @since v0.4.0
1857
+ * @history v0.6.0: Added `keepBom` default argument
1971
1858
  */
1972
- export let decode = (bytes: Bytes, encoding: Encoding) => {
1973
- decodeHelp(bytes, encoding, true)
1974
- }
1975
-
1976
- /**
1977
- * Decodes the given byte sequence into a string using the given encoding scheme, including any byte-order marker.
1978
- *
1979
- * @param bytes: The input bytes
1980
- * @param encoding: The encoding to use
1981
- * @returns The decoded string
1982
- *
1983
- * @since v0.4.0
1984
- */
1985
- export let decodeKeepBom = (bytes: Bytes, encoding: Encoding) => {
1986
- decodeHelp(bytes, encoding, false)
1859
+ provide let decode = (bytes: Bytes, encoding: Encoding, keepBom=false) => {
1860
+ decodeHelp(bytes, encoding, !keepBom)
1987
1861
  }
1988
1862
 
1989
1863
  /**
@@ -1997,14 +1871,8 @@ export let decodeKeepBom = (bytes: Bytes, encoding: Encoding) => {
1997
1871
  * @since v0.4.0
1998
1872
  */
1999
1873
  @unsafe
2000
- export let forEachCodePoint = (fn: Number -> Void, str: String) => {
2001
- let (>>>) = WasmI32.shrU
2002
- let (-) = WasmI32.sub
2003
- let (&) = WasmI32.and
2004
- let (<) = WasmI32.ltU
2005
- let (<=) = WasmI32.leU
2006
- let (==) = WasmI32.eq
2007
- let (+) = WasmI32.add
1874
+ provide let forEachCodePoint = (fn: Number => Void, str: String) => {
1875
+ use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
2008
1876
 
2009
1877
  let strPtr = WasmI32.fromGrain(str)
2010
1878
 
@@ -2055,14 +1923,8 @@ export let forEachCodePoint = (fn: Number -> Void, str: String) => {
2055
1923
  * @since v0.4.0
2056
1924
  */
2057
1925
  @unsafe
2058
- export let forEachCodePointi = (fn: (Number, Number) -> Void, str: String) => {
2059
- let (>>>) = WasmI32.shrU
2060
- let (-) = WasmI32.sub
2061
- let (&) = WasmI32.and
2062
- let (<) = WasmI32.ltU
2063
- let (<=) = WasmI32.leU
2064
- let (==) = WasmI32.eq
2065
- let (+) = WasmI32.add
1926
+ provide let forEachCodePointi = (fn: (Number, Number) => Void, str: String) => {
1927
+ use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
2066
1928
 
2067
1929
  let strPtr = WasmI32.fromGrain(str)
2068
1930
 
@@ -2101,19 +1963,10 @@ export let forEachCodePointi = (fn: (Number, Number) -> Void, str: String) => {
2101
1963
  }
2102
1964
 
2103
1965
  @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
1966
+ let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
1967
+ use WasmI32.{ (+), (-), (*), (>>>), ltU as (<), (==), (!=) }
1968
+ let mut i = 0n
1969
+ and offset = 1n
2117
1970
  if (fromEnd) {
2118
1971
  i = byteLength - 1n
2119
1972
  offset = -1n
@@ -2157,14 +2010,7 @@ let trimString = (str: String, fromEnd: Bool) => {
2157
2010
  }
2158
2011
  count += 1n
2159
2012
  }
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
2013
+ count
2168
2014
  }
2169
2015
  /**
2170
2016
  * Trims the beginning of a string—removing any leading whitespace characters.
@@ -2176,7 +2022,17 @@ let trimString = (str: String, fromEnd: Bool) => {
2176
2022
  *
2177
2023
  * @since v0.4.2
2178
2024
  */
2179
- export let trimStart = (string: String) => trimString(string, false)
2025
+ @unsafe
2026
+ provide let trimStart = (string: String) => {
2027
+ use WasmI32.{ (-), (+) }
2028
+ let mut stringPtr = WasmI32.fromGrain(string)
2029
+ let byteLength = WasmI32.load(stringPtr, 4n)
2030
+ stringPtr += 8n
2031
+ let count = trimString(stringPtr, byteLength, false)
2032
+ let str = allocateString(byteLength - count)
2033
+ Memory.copy(str + 8n, stringPtr + count, byteLength - count)
2034
+ WasmI32.toGrain(str): String
2035
+ }
2180
2036
  /**
2181
2037
  * Trims the end of a string—removing any trailing whitespace characters.
2182
2038
  *
@@ -2187,7 +2043,17 @@ export let trimStart = (string: String) => trimString(string, false)
2187
2043
  *
2188
2044
  * @since v0.4.2
2189
2045
  */
2190
- export let trimEnd = (string: String) => trimString(string, true)
2046
+ @unsafe
2047
+ provide let trimEnd = (string: String) => {
2048
+ use WasmI32.{ (-), (+) }
2049
+ let mut stringPtr = WasmI32.fromGrain(string)
2050
+ let byteLength = WasmI32.load(stringPtr, 4n)
2051
+ stringPtr += 8n
2052
+ let count = trimString(stringPtr, byteLength, true)
2053
+ let str = allocateString(byteLength - count)
2054
+ Memory.copy(str + 8n, stringPtr, byteLength - count)
2055
+ WasmI32.toGrain(str): String
2056
+ }
2191
2057
  /**
2192
2058
  * Trims a string—removing all leading and trailing whitespace characters.
2193
2059
  *
@@ -2198,4 +2064,58 @@ export let trimEnd = (string: String) => trimString(string, true)
2198
2064
  *
2199
2065
  * @since v0.4.2
2200
2066
  */
2201
- export let trim = (string: String) => trimEnd(trimStart(string))
2067
+ @unsafe
2068
+ provide let trim = (string: String) => {
2069
+ use WasmI32.{ (-), (+), (==) }
2070
+ let mut stringPtr = WasmI32.fromGrain(string)
2071
+ let byteLength = WasmI32.load(stringPtr, 4n)
2072
+ stringPtr += 8n
2073
+ let startCount = trimString(stringPtr, byteLength, false)
2074
+ if (startCount == byteLength) return ""
2075
+ let endCount = trimString(stringPtr, byteLength, true)
2076
+ let str = allocateString(byteLength - startCount - endCount)
2077
+ Memory.copy(
2078
+ str + 8n,
2079
+ stringPtr + startCount,
2080
+ byteLength - startCount - endCount
2081
+ )
2082
+ return WasmI32.toGrain(str): String
2083
+ }
2084
+
2085
+ /**
2086
+ * Converts all ASCII uppercase characters in the string to lowercase.
2087
+ *
2088
+ * @param string: The string to convert
2089
+ * @returns The lowercased string
2090
+ *
2091
+ * @example assert String.toAsciiLowercase("aBc123") == "abc123"
2092
+ *
2093
+ * @since v0.6.0
2094
+ */
2095
+ provide let toAsciiLowercase = string => {
2096
+ let chars = explode(string)
2097
+ let len = arrayLength(chars)
2098
+ for (let mut i = 0; i < len; i += 1) {
2099
+ chars[i] = Char.toAsciiLowercase(chars[i])
2100
+ }
2101
+ implode(chars)
2102
+ }
2103
+
2104
+ /**
2105
+ * Converts all ASCII lowercase characters in the string to uppercase.
2106
+ *
2107
+ * @param string: The string to convert
2108
+ * @returns The uppercased string
2109
+ *
2110
+ * @example assert String.toAsciiUppercase("aBc123") == "ABC123"
2111
+ *
2112
+ * @since v0.6.0
2113
+ */
2114
+ provide let toAsciiUppercase = string => {
2115
+ let chars = explode(string)
2116
+ let len = arrayLength(chars)
2117
+ for (let mut i = 0; i < len; i += 1) {
2118
+ chars[i] = Char.toAsciiUppercase(chars[i])
2119
+ }
2120
+ implode(chars)
2121
+ }