@grain/stdlib 0.6.5 → 0.7.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 (139) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/LICENSE +1 -1
  3. package/README.md +2 -2
  4. package/array.gr +55 -7
  5. package/array.md +123 -77
  6. package/bigint.md +30 -30
  7. package/buffer.gr +20 -53
  8. package/buffer.md +47 -47
  9. package/bytes.gr +111 -35
  10. package/bytes.md +111 -32
  11. package/char.gr +201 -99
  12. package/char.md +361 -34
  13. package/exception.gr +11 -11
  14. package/exception.md +26 -1
  15. package/float32.gr +327 -3
  16. package/float32.md +606 -19
  17. package/float64.gr +320 -3
  18. package/float64.md +606 -19
  19. package/fs.gr +1082 -0
  20. package/fs.md +630 -0
  21. package/hash.gr +142 -88
  22. package/hash.md +102 -14
  23. package/int16.md +23 -23
  24. package/int32.gr +25 -4
  25. package/int32.md +65 -30
  26. package/int64.gr +26 -1
  27. package/int64.md +65 -30
  28. package/int8.md +23 -23
  29. package/json.gr +366 -51
  30. package/json.md +418 -2
  31. package/list.gr +387 -49
  32. package/list.md +492 -69
  33. package/map.gr +20 -12
  34. package/map.md +44 -38
  35. package/marshal.gr +41 -40
  36. package/marshal.md +2 -2
  37. package/number.gr +159 -30
  38. package/number.md +215 -38
  39. package/option.md +21 -21
  40. package/package.json +5 -3
  41. package/path.gr +48 -0
  42. package/path.md +103 -12
  43. package/pervasives.gr +2 -2
  44. package/pervasives.md +37 -37
  45. package/priorityqueue.gr +7 -7
  46. package/priorityqueue.md +19 -19
  47. package/queue.gr +183 -29
  48. package/queue.md +296 -40
  49. package/random.md +6 -6
  50. package/range.gr +4 -4
  51. package/range.md +6 -6
  52. package/rational.md +16 -16
  53. package/regex.gr +52 -51
  54. package/regex.md +11 -11
  55. package/result.md +16 -16
  56. package/runtime/atof/common.md +39 -39
  57. package/runtime/atof/decimal.gr +6 -6
  58. package/runtime/atof/decimal.md +8 -8
  59. package/runtime/atof/lemire.gr +5 -5
  60. package/runtime/atof/lemire.md +1 -1
  61. package/runtime/atof/parse.gr +16 -16
  62. package/runtime/atof/parse.md +2 -2
  63. package/runtime/atof/slow.md +1 -1
  64. package/runtime/atof/table.md +2 -2
  65. package/runtime/atoi/parse.gr +3 -3
  66. package/runtime/atoi/parse.md +1 -1
  67. package/runtime/bigint.gr +15 -47
  68. package/runtime/bigint.md +54 -60
  69. package/runtime/compare.gr +2 -2
  70. package/runtime/compare.md +1 -1
  71. package/runtime/dataStructures.md +33 -33
  72. package/runtime/debugPrint.gr +4 -1
  73. package/runtime/debugPrint.md +9 -9
  74. package/runtime/equal.gr +99 -77
  75. package/runtime/equal.md +1 -1
  76. package/runtime/exception.gr +62 -82
  77. package/runtime/exception.md +62 -11
  78. package/runtime/gc.gr +39 -45
  79. package/runtime/gc.md +4 -4
  80. package/runtime/malloc.gr +7 -7
  81. package/runtime/malloc.md +4 -4
  82. package/runtime/math/kernel/cos.gr +70 -0
  83. package/runtime/math/kernel/cos.md +14 -0
  84. package/runtime/math/kernel/sin.gr +65 -0
  85. package/runtime/math/kernel/sin.md +14 -0
  86. package/runtime/math/kernel/tan.gr +136 -0
  87. package/runtime/math/kernel/tan.md +14 -0
  88. package/runtime/math/rempio2.gr +244 -0
  89. package/runtime/math/rempio2.md +14 -0
  90. package/runtime/math/trig.gr +130 -0
  91. package/runtime/math/trig.md +28 -0
  92. package/runtime/math/umuldi.gr +26 -0
  93. package/runtime/math/umuldi.md +14 -0
  94. package/runtime/numberUtils.gr +29 -29
  95. package/runtime/numberUtils.md +12 -12
  96. package/runtime/numbers.gr +373 -381
  97. package/runtime/numbers.md +79 -73
  98. package/runtime/string.gr +37 -105
  99. package/runtime/string.md +3 -9
  100. package/runtime/unsafe/constants.md +24 -24
  101. package/runtime/unsafe/conv.md +13 -13
  102. package/runtime/unsafe/memory.gr +24 -20
  103. package/runtime/unsafe/memory.md +27 -7
  104. package/runtime/unsafe/offsets.gr +36 -0
  105. package/runtime/unsafe/offsets.md +88 -0
  106. package/runtime/unsafe/panic.gr +28 -0
  107. package/runtime/unsafe/panic.md +14 -0
  108. package/runtime/unsafe/tags.md +32 -32
  109. package/runtime/unsafe/wasmf32.md +28 -28
  110. package/runtime/unsafe/wasmf64.md +28 -28
  111. package/runtime/unsafe/wasmi32.md +47 -47
  112. package/runtime/unsafe/wasmi64.md +50 -50
  113. package/runtime/utf8.gr +189 -0
  114. package/runtime/utf8.md +117 -0
  115. package/runtime/wasi.gr +4 -2
  116. package/runtime/wasi.md +138 -138
  117. package/set.gr +18 -11
  118. package/set.md +42 -36
  119. package/stack.gr +171 -2
  120. package/stack.md +297 -15
  121. package/string.gr +352 -557
  122. package/string.md +77 -34
  123. package/uint16.gr +81 -0
  124. package/uint16.md +183 -22
  125. package/uint32.gr +25 -4
  126. package/uint32.md +63 -28
  127. package/uint64.gr +25 -5
  128. package/uint64.md +63 -28
  129. package/uint8.gr +81 -0
  130. package/uint8.md +183 -22
  131. package/uri.gr +57 -53
  132. package/uri.md +11 -12
  133. package/wasi/file.gr +67 -59
  134. package/wasi/file.md +39 -39
  135. package/wasi/process.md +5 -5
  136. package/wasi/random.md +3 -3
  137. package/wasi/time.md +4 -4
  138. package/runtime/utils/printing.gr +0 -60
  139. package/runtime/utils/printing.md +0 -26
package/string.gr CHANGED
@@ -24,7 +24,38 @@ use DataStructures.{
24
24
  allocateString,
25
25
  allocateBytes,
26
26
  }
27
+ from "runtime/utf8" include Utf8
28
+ use Utf8.{
29
+ isLeadingByte,
30
+ utf8ByteCount,
31
+ usvEncodeLength,
32
+ getCodePoint,
33
+ writeUtf8CodePoint,
34
+ }
35
+ from "runtime/unsafe/offsets" include Offsets
36
+ use Offsets.{
37
+ _STR_LEN_OFFSET,
38
+ _STR_DATA_OFFSET,
39
+ _ARRAY_LEN_OFFSET,
40
+ _ARRAY_DATA_OFFSET,
41
+ _ARRAY_ITEM_SIZE,
42
+ _BYTES_LEN_OFFSET,
43
+ _BYTES_DATA_OFFSET,
44
+ }
45
+ from "runtime/unsafe/tags" include Tags
27
46
 
47
+ // Helpers
48
+ @unsafe
49
+ let convSimpleNumber = (x: Number, err: String) => {
50
+ use WasmI32.{ (&), (!=) }
51
+ let rawX = WasmI32.fromGrain(x)
52
+ if ((rawX & Tags._GRAIN_NUMBER_TAG_MASK) != Tags._GRAIN_NUMBER_TAG_TYPE) {
53
+ throw InvalidArgument(err)
54
+ }
55
+ untagSimpleNumber(x)
56
+ }
57
+ @unsafe
58
+ let rawByteLength = (ptr: WasmI32) => WasmI32.load(ptr, _STR_LEN_OFFSET)
28
59
  /**
29
60
  * Byte encodings
30
61
  */
@@ -63,20 +94,18 @@ provide let concat = (++)
63
94
  */
64
95
  @unsafe
65
96
  provide let length = (string: String) => {
66
- use WasmI32.{ (+), (&), (!=) }
67
- let stringPtr = WasmI32.fromGrain(string)
68
- let size = WasmI32.load(stringPtr, 4n)
97
+ use WasmI32.{ (+), (&), (!=), ltU as (<) }
98
+ let mut stringPtr = WasmI32.fromGrain(string)
99
+ let size = rawByteLength(stringPtr)
69
100
 
70
101
  let mut len = 0n
71
- let mut ptr = stringPtr + 8n
72
- let end = ptr + size
102
+ stringPtr += _STR_DATA_OFFSET
103
+ let end = stringPtr + size
73
104
 
74
- while (WasmI32.ltU(ptr, end)) {
75
- let byte = WasmI32.load8U(ptr, 0n)
76
- if ((byte & 0xC0n) != 0x80n) {
77
- len += 1n
78
- }
79
- ptr += 1n
105
+ while (stringPtr < end) {
106
+ let byte = WasmI32.load8U(stringPtr, 0n)
107
+ if (isLeadingByte(byte)) len += 1n
108
+ stringPtr += 1n
80
109
  }
81
110
 
82
111
  ignore(string)
@@ -97,7 +126,7 @@ provide let length = (string: String) => {
97
126
  @unsafe
98
127
  provide let byteLength = (string: String) => {
99
128
  let stringPtr = WasmI32.fromGrain(string)
100
- let result = Conv.wasmI32ToNumber(WasmI32.load(stringPtr, 4n))
129
+ let result = Conv.wasmI32ToNumber(rawByteLength(stringPtr))
101
130
  ignore(string)
102
131
  result
103
132
  }
@@ -114,7 +143,7 @@ provide let byteLength = (string: String) => {
114
143
  provide let isEmpty = (string: String) => {
115
144
  use WasmI32.{ (==) }
116
145
  let strPtr = WasmI32.fromGrain(string)
117
- let result = WasmI32.load(strPtr, 4n) == 0n
146
+ let result = rawByteLength(strPtr) == 0n
118
147
  ignore(string)
119
148
  result
120
149
  }
@@ -132,36 +161,26 @@ provide let isEmpty = (string: String) => {
132
161
  */
133
162
  @unsafe
134
163
  provide let indexOf = (search: String, string: String) => {
164
+ use WasmI32.{ (+), (-), ltU as (<), gtU as (>), (==) }
135
165
  let searchPtr = WasmI32.fromGrain(search)
136
166
  let stringPtr = WasmI32.fromGrain(string)
137
167
 
138
- let size = WasmI32.load(stringPtr, 4n)
139
- let psize = WasmI32.load(searchPtr, 4n)
140
- use WasmI32.{ (+), (-), (&), ltU as (<), gtU as (>), (==) }
168
+ let size = rawByteLength(stringPtr)
169
+ let psize = rawByteLength(searchPtr)
141
170
 
142
171
  if (psize > size) {
143
172
  return None
144
173
  }
145
- let mut idx = 0n
146
- let mut ptr = stringPtr + 8n
147
- let pptr = searchPtr + 8n
174
+ let mut ptr = stringPtr + _STR_DATA_OFFSET
175
+ let pptr = searchPtr + _STR_DATA_OFFSET
148
176
  let end = ptr + size - psize + 1n
149
177
 
150
- while (ptr < end) {
178
+ for (let mut i = 0n; ptr < end; i += 1n) {
151
179
  if (Memory.compare(ptr, pptr, psize) == 0n) {
152
- return Some(tagSimpleNumber(idx))
180
+ return Some(tagSimpleNumber(i))
153
181
  }
154
- idx += 1n
155
182
  let byte = WasmI32.load8U(ptr, 0n)
156
- if ((byte & 0x80n) == 0x00n) {
157
- ptr += 1n
158
- } else if ((byte & 0xF0n) == 0xF0n) {
159
- ptr += 4n
160
- } else if ((byte & 0xE0n) == 0xE0n) {
161
- ptr += 3n
162
- } else {
163
- ptr += 2n
164
- }
183
+ ptr += utf8ByteCount(byte)
165
184
  }
166
185
 
167
186
  ignore(search)
@@ -186,28 +205,26 @@ provide let lastIndexOf = (search: String, string: String) => {
186
205
  // The last possible index in the string given the length of the search
187
206
  let lastIndex = length(string) - length(search)
188
207
 
189
- use WasmI32.{ (+), (-), (&), gtU as (>), geU as (>=), (==), (!=) }
208
+ use WasmI32.{ (+), (-), gtU as (>), geU as (>=), (==), (!=) }
190
209
 
191
210
  let searchPtr = WasmI32.fromGrain(search)
192
211
  let stringPtr = WasmI32.fromGrain(string)
193
- let searchSize = WasmI32.load(searchPtr, 4n)
194
- let stringSize = WasmI32.load(stringPtr, 4n)
212
+ let searchSize = rawByteLength(searchPtr)
213
+ let stringSize = rawByteLength(stringPtr)
195
214
  if (searchSize > stringSize) {
196
215
  return None
197
216
  }
198
217
  let mut stringIndex = untagSimpleNumber(lastIndex)
199
- let searchPtr = searchPtr + 8n
200
- let stringStartPtr = stringPtr + 8n
218
+ let searchPtr = searchPtr + _STR_DATA_OFFSET
219
+ let stringStartPtr = stringPtr + _STR_DATA_OFFSET
201
220
  for (
202
221
  let mut stringPtr = stringStartPtr + stringSize - searchSize;
203
222
  stringPtr >= stringStartPtr;
204
223
  stringPtr -= 1n
205
224
  ) {
206
225
  let byte = WasmI32.load8U(stringPtr, 0n)
207
- if ((byte & 0xC0n) == 0x80n) {
208
- // We're not at the start of a codepoint, so move on
209
- continue
210
- }
226
+ // We're not at the start of a codepoint, so move on
227
+ if (!isLeadingByte(byte)) continue
211
228
  if (Memory.compare(stringPtr, searchPtr, searchSize) == 0n) {
212
229
  return Some(tagSimpleNumber(stringIndex))
213
230
  }
@@ -220,68 +237,17 @@ provide let lastIndexOf = (search: String, string: String) => {
220
237
  return None
221
238
  }
222
239
 
223
- @unsafe
224
- let getCodePoint = (ptr: WasmI32) => {
225
- // Algorithm from https://encoding.spec.whatwg.org/#utf-8-decoder
226
- use WasmI32.{ (+), (&), (|), (<<), leU as (<=), geU as (>=), (==) }
227
-
228
- let mut codePoint = 0n
229
- let mut bytesSeen = 0n
230
- let mut bytesNeeded = 0n
231
- let mut lowerBoundary = 0x80n
232
- let mut upperBoundary = 0xBFn
233
-
234
- let mut offset = 0n
235
-
236
- while (true) {
237
- let byte = WasmI32.load8U(ptr + offset, 0n)
238
- offset += 1n
239
- if (bytesNeeded == 0n) {
240
- if (byte >= 0x00n && byte <= 0x7Fn) {
241
- return byte
242
- } else if (byte >= 0xC2n && byte <= 0xDFn) {
243
- bytesNeeded = 1n
244
- codePoint = byte & 0x1Fn
245
- } else if (byte >= 0xE0n && byte <= 0xEFn) {
246
- if (byte == 0xE0n) lowerBoundary = 0xA0n
247
- if (byte == 0xEDn) upperBoundary = 0x9Fn
248
- bytesNeeded = 2n
249
- codePoint = byte & 0xFn
250
- } else if (byte >= 0xF0n && byte <= 0xF4n) {
251
- if (byte == 0xF0n) lowerBoundary = 0x90n
252
- if (byte == 0xF4n) upperBoundary = 0x8Fn
253
- bytesNeeded = 3n
254
- codePoint = byte & 0x7n
255
- } else {
256
- throw MalformedUnicode
257
- }
258
- continue
259
- }
260
- if (!(lowerBoundary <= byte && byte <= upperBoundary)) {
261
- throw MalformedUnicode
262
- }
263
- lowerBoundary = 0x80n
264
- upperBoundary = 0xBFn
265
- codePoint = codePoint << 6n | byte & 0x3Fn
266
- bytesSeen += 1n
267
- if (bytesSeen == bytesNeeded) {
268
- return codePoint
269
- }
270
- }
271
- return 0n
272
- }
273
-
274
240
  @unsafe
275
241
  let charAtHelp = (position, string: String) => {
276
242
  if (length(string) <= position || position < 0) {
277
243
  fail "Invalid offset: " ++ toString(position)
278
244
  }
279
245
  // Implementation is similar to explodeHelp, but doesn't perform unneeded memory allocations
280
- use WasmI32.{ (+), (&), (>>>), ltU as (<), (==) }
281
- let size = WasmI32.fromGrain(byteLength(string)) >>> 1n
282
- let position = WasmI32.fromGrain(position) >>> 1n
283
- let stringPre = WasmI32.fromGrain(string)
284
- let mut ptr = stringPre + 8n
246
+ use WasmI32.{ (+), ltU as (<), (==) }
247
+ let stringPtr = WasmI32.fromGrain(string)
248
+ let size = rawByteLength(stringPtr)
249
+ let position = untagSimpleNumber(position)
250
+ let mut ptr = stringPtr + _STR_DATA_OFFSET
285
251
  let end = ptr + size
286
252
  let mut counter = 0n
287
253
  while (ptr < end) {
@@ -289,17 +255,8 @@ let charAtHelp = (position, string: String) => {
289
255
  return getCodePoint(ptr)
290
256
  }
291
257
  let byte = WasmI32.load8U(ptr, 0n)
292
- let n = if ((byte & 0x80n) == 0x00n) {
293
- 1n
294
- } else if ((byte & 0xF0n) == 0xF0n) {
295
- 4n
296
- } else if ((byte & 0xE0n) == 0xE0n) {
297
- 3n
298
- } else {
299
- 2n
300
- }
258
+ ptr += utf8ByteCount(byte)
301
259
  counter += 1n
302
- ptr += n
303
260
  }
304
261
 
305
262
  ignore(string)
@@ -347,14 +304,13 @@ provide let charAt = (position, string: String) => {
347
304
 
348
305
  @unsafe
349
306
  let explodeHelp = (s: String, chars) => {
350
- use WasmI32.{ (+), (&), (>>>), ltU as (<), (==) }
351
-
352
- let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
353
- let len = WasmI32.fromGrain(length(s)) >>> 1n
307
+ use WasmI32.{ (+), ltU as (<), (==) }
354
308
 
355
309
  let sPtr = WasmI32.fromGrain(s)
310
+ let size = rawByteLength(sPtr)
311
+ let len = untagSimpleNumber(length(s))
356
312
 
357
- let mut ptr = sPtr + 8n
313
+ let mut ptr = sPtr + _STR_DATA_OFFSET
358
314
  let end = ptr + size
359
315
 
360
316
  let arr = allocateArray(len)
@@ -362,26 +318,18 @@ let explodeHelp = (s: String, chars) => {
362
318
 
363
319
  while (ptr < end) {
364
320
  let byte = WasmI32.load8U(ptr, 0n)
365
- let n = if ((byte & 0x80n) == 0x00n) {
366
- 1n
367
- } else if ((byte & 0xF0n) == 0xF0n) {
368
- 4n
369
- } else if ((byte & 0xE0n) == 0xE0n) {
370
- 3n
371
- } else {
372
- 2n
373
- }
321
+ let n = utf8ByteCount(byte)
374
322
 
375
323
  let c = if (chars) {
376
324
  WasmI32.fromGrain(tagChar(getCodePoint(ptr)))
377
325
  } else {
378
326
  let sPtr = allocateString(n)
379
- Memory.copy(sPtr + 8n, ptr, n)
327
+ Memory.copy(sPtr + _STR_DATA_OFFSET, ptr, n)
380
328
  sPtr
381
329
  }
382
330
 
383
- WasmI32.store(arr + arrIdx, c, 8n)
384
- arrIdx += 4n
331
+ WasmI32.store(arr + arrIdx, c, _ARRAY_DATA_OFFSET)
332
+ arrIdx += _ARRAY_ITEM_SIZE
385
333
  ptr += n
386
334
  }
387
335
 
@@ -432,58 +380,21 @@ provide let implode = (arr: Array<Char>) => {
432
380
  leU as (<=),
433
381
  }
434
382
 
435
- let arrLength = WasmI32.load(WasmI32.fromGrain(arr), 4n)
383
+ let arrLength = WasmI32.load(WasmI32.fromGrain(arr), _ARRAY_LEN_OFFSET)
436
384
 
437
385
  let mut stringByteLength = 0n
438
386
 
439
387
  for (let mut i = 0n; i < arrLength; i += 1n) {
440
388
  let usv = untagChar(arr[tagSimpleNumber(i)])
441
-
442
- let n = if (usv <= 0x7Fn) {
443
- 1n
444
- } else if (usv <= 0x07FFn) {
445
- 2n
446
- } else if (usv <= 0xFFFFn) {
447
- 3n
448
- } else {
449
- 4n
450
- }
451
-
452
- stringByteLength += n
389
+ stringByteLength += usvEncodeLength(usv)
453
390
  }
454
391
 
455
392
  let str = allocateString(stringByteLength)
456
- let mut offset = 8n
393
+ let mut offset = _STR_DATA_OFFSET
457
394
 
458
395
  for (let mut i = 0n; i < arrLength; i += 1n) {
459
396
  let usv = untagChar(arr[tagSimpleNumber(i)])
460
-
461
- if (usv < 0x7Fn) {
462
- WasmI32.store8(str + offset, usv, 0n)
463
- offset += 1n
464
- } else {
465
- let mut count = 0n
466
- let mut marker = 0n
467
- if (usv <= 0x07FFn) {
468
- count = 1n
469
- marker = 0xC0n
470
- } else if (usv <= 0xFFFFn) {
471
- count = 2n
472
- marker = 0xE0n
473
- } else {
474
- count = 3n
475
- marker = 0xF0n
476
- }
477
- WasmI32.store8(str + offset, (usv >>> (6n * count)) + marker, 0n)
478
- offset += 1n
479
-
480
- while (count > 0n) {
481
- let temp = usv >>> (6n * (count - 1n))
482
- WasmI32.store8(str + offset, 0x80n | temp & 0x3Fn, 0n)
483
- count -= 1n
484
- offset += 1n
485
- }
486
- }
397
+ offset += writeUtf8CodePoint(str + offset, usv)
487
398
  }
488
399
 
489
400
  WasmI32.toGrain(str): String
@@ -529,21 +440,21 @@ provide let reverse = string => {
529
440
  */
530
441
  @unsafe
531
442
  provide let split = (separator: String, string: String) => {
532
- use WasmI32.{ (+), (-), (&), (<<), (>>), ltU as (<), gtU as (>), (==) }
443
+ use WasmI32.{ (+), (-), (&), (<<), ltU as (<), gtU as (>), (==) }
533
444
 
534
- let size = WasmI32.fromGrain(byteLength(string)) >> 1n
535
- let psize = WasmI32.fromGrain(byteLength(separator)) >> 1n
445
+ let stringPtr = WasmI32.fromGrain(string)
446
+ let separatorPtr = WasmI32.fromGrain(separator)
447
+ let size = rawByteLength(stringPtr)
448
+ let psize = rawByteLength(separatorPtr)
536
449
 
537
450
  if (psize == 0n) {
538
451
  WasmI32.toGrain(explodeHelp(string, false)): Array<String>
539
452
  } else if (psize > size) {
540
453
  [> string]
541
454
  } else {
542
- let stringPtr = WasmI32.fromGrain(string)
543
- let separatorPtr = WasmI32.fromGrain(separator)
544
-
545
- let mut ptr = stringPtr + 8n
546
- let mut pptr = separatorPtr + 8n
455
+ let stringPtr = stringPtr + _STR_DATA_OFFSET
456
+ let mut ptr = stringPtr
457
+ let mut pptr = separatorPtr + _STR_DATA_OFFSET
547
458
  let end = ptr + size - psize + 1n
548
459
 
549
460
  let mut numStrings = 1n
@@ -553,18 +464,10 @@ provide let split = (separator: String, string: String) => {
553
464
  numStrings += 1n
554
465
  }
555
466
  let byte = WasmI32.load8U(ptr, 0n)
556
- if ((byte & 0x80n) == 0x00n) {
557
- ptr += 1n
558
- } else if ((byte & 0xF0n) == 0xF0n) {
559
- ptr += 4n
560
- } else if ((byte & 0xE0n) == 0xE0n) {
561
- ptr += 3n
562
- } else {
563
- ptr += 2n
564
- }
467
+ ptr += utf8ByteCount(byte)
565
468
  }
566
469
 
567
- ptr = stringPtr + 8n
470
+ ptr = stringPtr
568
471
  let mut last = ptr
569
472
  let arr = allocateArray(numStrings)
570
473
  let mut arrIdx = 0n
@@ -573,30 +476,22 @@ provide let split = (separator: String, string: String) => {
573
476
  if (Memory.compare(ptr, pptr, psize) == 0n) {
574
477
  let strSize = ptr - last
575
478
  let str = allocateString(strSize)
576
- Memory.copy(str + 8n, last, strSize)
577
- WasmI32.store(arr + arrIdx, str, 8n)
578
- arrIdx += 4n
479
+ Memory.copy(str + _STR_DATA_OFFSET, last, strSize)
480
+ WasmI32.store(arr + _ARRAY_DATA_OFFSET, str, arrIdx)
481
+ arrIdx += _ARRAY_ITEM_SIZE
579
482
  ptr += psize
580
483
  last = ptr
581
484
  continue
582
485
  }
583
486
  let byte = WasmI32.load8U(ptr, 0n)
584
- if ((byte & 0x80n) == 0x00n) {
585
- ptr += 1n
586
- } else if ((byte & 0xF0n) == 0xF0n) {
587
- ptr += 4n
588
- } else if ((byte & 0xE0n) == 0xE0n) {
589
- ptr += 3n
590
- } else {
591
- ptr += 2n
592
- }
487
+ ptr += utf8ByteCount(byte)
593
488
  }
594
489
 
595
490
  // Grab last string
596
- let strSize = stringPtr + 8n + size - last
491
+ let strSize = stringPtr + size - last
597
492
  let lastStr = allocateString(strSize)
598
- Memory.copy(lastStr + 8n, last, strSize)
599
- WasmI32.store(arr + arrIdx, lastStr, 8n)
493
+ Memory.copy(lastStr + _STR_DATA_OFFSET, last, strSize)
494
+ WasmI32.store(arr + arrIdx, lastStr, _ARRAY_DATA_OFFSET)
600
495
 
601
496
  ignore(separator)
602
497
  ignore(string)
@@ -627,24 +522,14 @@ provide let split = (separator: String, string: String) => {
627
522
  */
628
523
  @unsafe
629
524
  provide let slice = (start: Number, end=length(string), string: String) => {
630
- use WasmI32.{ (+), (-), (&), (<<), (>>), (<), (>), (==), (!=) }
631
-
632
- let len = WasmI32.fromGrain(length(string)) >> 1n
633
- let size = WasmI32.fromGrain(byteLength(string)) >> 1n
525
+ use WasmI32.{ (+), (-), (<), (>), (==) }
634
526
 
635
527
  let stringPtr = WasmI32.fromGrain(string)
528
+ let len = untagSimpleNumber(length(string))
529
+ let size = rawByteLength(stringPtr)
636
530
 
637
- let mut start = WasmI32.fromGrain(start)
638
- if ((start & 1n) != 1n) {
639
- throw InvalidArgument("Invalid start index")
640
- }
641
- start = start >> 1n
642
-
643
- let mut to = WasmI32.fromGrain(end)
644
- if ((to & 1n) != 1n) {
645
- throw InvalidArgument("Invalid end index")
646
- }
647
- to = to >> 1n
531
+ let mut start = convSimpleNumber(start, "Invalid start index")
532
+ let mut to = convSimpleNumber(end, "Invalid end index")
648
533
 
649
534
  if (start < 0n) {
650
535
  start += len
@@ -661,7 +546,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
661
546
  throw InvalidArgument("Start index exceeds end index")
662
547
  }
663
548
 
664
- let mut ptr = stringPtr + 8n
549
+ let mut ptr = stringPtr + _STR_DATA_OFFSET
665
550
  let mut begin = ptr
666
551
  let mut end = ptr
667
552
  let stop = ptr + size
@@ -669,7 +554,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
669
554
  let mut idx = 0n
670
555
  while (ptr < stop) {
671
556
  let byte = WasmI32.load8U(ptr, 0n)
672
- if ((byte & 0xC0n) != 0x80n) {
557
+ if (isLeadingByte(byte)) {
673
558
  if (idx == start) {
674
559
  begin = ptr
675
560
  }
@@ -682,7 +567,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
682
567
  ptr += 1n
683
568
  }
684
569
  if (to == len) {
685
- end = stringPtr + 8n + size
570
+ end = stringPtr + _STR_DATA_OFFSET + size
686
571
  }
687
572
  if (start == to) {
688
573
  begin = end
@@ -691,7 +576,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
691
576
  let newSize = end - begin
692
577
  let newString = allocateString(newSize)
693
578
 
694
- Memory.copy(newString + 8n, begin, newSize)
579
+ Memory.copy(newString + _STR_DATA_OFFSET, begin, newSize)
695
580
 
696
581
  ignore(string)
697
582
 
@@ -715,27 +600,18 @@ provide let contains = (search: String, string: String) => {
715
600
  // searching phase in O(nm) time complexity
716
601
  // slightly (by coefficient) sub-linear in the average case
717
602
  // http://igm.univ-mlv.fr/~lecroq/string/node13.html#SECTION00130
718
- use WasmI32.{
719
- (+),
720
- (-),
721
- (>>),
722
- ltU as (<),
723
- gtU as (>),
724
- leU as (<=),
725
- (==),
726
- (!=),
727
- }
603
+ use WasmI32.{ (+), (-), ltU as (<), gtU as (>), leU as (<=), (==), (!=) }
728
604
  let pOrig = search
729
605
  let sOrig = string
730
606
 
731
- let n = WasmI32.fromGrain(byteLength(string)) >> 1n
732
- let m = WasmI32.fromGrain(byteLength(search)) >> 1n
733
-
734
607
  let mut stringPtr = WasmI32.fromGrain(string)
735
608
  let mut searchPtr = WasmI32.fromGrain(search)
736
609
 
737
- stringPtr += 8n
738
- searchPtr += 8n
610
+ let n = rawByteLength(stringPtr)
611
+ let m = rawByteLength(searchPtr)
612
+
613
+ stringPtr += _STR_DATA_OFFSET
614
+ searchPtr += _STR_DATA_OFFSET
739
615
 
740
616
  let mut j = 0n
741
617
  and k = 0n
@@ -779,8 +655,8 @@ provide let contains = (search: String, string: String) => {
779
655
  j += k
780
656
  } else {
781
657
  if (
782
- Memory.compare(searchPtr + 2n, stringPtr + j + 2n, m - 2n) == 0n &&
783
- WasmI32.load8U(searchPtr, 0n) == WasmI32.load8U(stringPtr + j, 0n)
658
+ Memory.compare(searchPtr + 2n, stringPtr + j + 2n, m - 2n) == 0n
659
+ && WasmI32.load8U(searchPtr, 0n) == WasmI32.load8U(stringPtr + j, 0n)
784
660
  ) {
785
661
  return true
786
662
  }
@@ -814,11 +690,11 @@ provide let startsWith = (search: String, string: String) => {
814
690
  let mut searchPtr = WasmI32.fromGrain(search)
815
691
  let mut stringPtr = WasmI32.fromGrain(string)
816
692
 
817
- let n = WasmI32.load(stringPtr, 4n)
818
- let m = WasmI32.load(searchPtr, 4n)
693
+ let n = rawByteLength(stringPtr)
694
+ let m = rawByteLength(searchPtr)
819
695
 
820
- stringPtr += 8n
821
- searchPtr += 8n
696
+ stringPtr += _STR_DATA_OFFSET
697
+ searchPtr += _STR_DATA_OFFSET
822
698
 
823
699
  // Bail if pattern length is longer than input length
824
700
  let result = if (m > n) {
@@ -853,11 +729,11 @@ provide let endsWith = (search: String, string: String) => {
853
729
  let mut searchPtr = WasmI32.fromGrain(search)
854
730
  let mut stringPtr = WasmI32.fromGrain(string)
855
731
 
856
- let n = WasmI32.load(stringPtr, 4n)
857
- let m = WasmI32.load(searchPtr, 4n)
732
+ let n = rawByteLength(stringPtr)
733
+ let m = rawByteLength(searchPtr)
858
734
 
859
- stringPtr += 8n
860
- searchPtr += 8n
735
+ stringPtr += _STR_DATA_OFFSET
736
+ searchPtr += _STR_DATA_OFFSET
861
737
 
862
738
  // Bail if pattern length is longer than input length
863
739
  let result = if (m > n) {
@@ -896,16 +772,16 @@ provide let replaceFirst = (
896
772
  let mut stringPtr = WasmI32.fromGrain(string)
897
773
  let mut replacementPtr = WasmI32.fromGrain(replacement)
898
774
 
899
- let patternLen = WasmI32.load(patternPtr, 4n)
900
- let stringLen = WasmI32.load(stringPtr, 4n)
901
- let replacementLen = WasmI32.load(replacementPtr, 4n)
775
+ let patternLen = rawByteLength(patternPtr)
776
+ let stringLen = rawByteLength(stringPtr)
777
+ let replacementLen = rawByteLength(replacementPtr)
902
778
  // Bail if search str is longer than the string
903
779
  if (stringLen < patternLen) {
904
780
  return string
905
781
  }
906
- patternPtr += 8n
907
- stringPtr += 8n
908
- replacementPtr += 8n
782
+ patternPtr += _STR_DATA_OFFSET
783
+ stringPtr += _STR_DATA_OFFSET
784
+ replacementPtr += _STR_DATA_OFFSET
909
785
 
910
786
  // Search for an instance of the string
911
787
  let mut foundIndex = -1n
@@ -916,7 +792,7 @@ provide let replaceFirst = (
916
792
  if (Memory.compare(i, patternPtr, patternLen) == 0n) {
917
793
  // Create the new string
918
794
  let str = allocateString(stringLen - patternLen + replacementLen)
919
- let strPtr = str + 8n
795
+ let strPtr = str + _STR_DATA_OFFSET
920
796
  Memory.copy(strPtr, stringPtr, foundIndex)
921
797
  Memory.copy(strPtr + foundIndex, replacementPtr, replacementLen)
922
798
  Memory.copy(
@@ -960,17 +836,17 @@ provide let replaceLast = (
960
836
  let mut stringPtr = WasmI32.fromGrain(string)
961
837
  let mut replacementPtr = WasmI32.fromGrain(replacement)
962
838
 
963
- let patternLen = WasmI32.load(patternPtr, 4n)
964
- let stringLen = WasmI32.load(stringPtr, 4n)
965
- let replacementLen = WasmI32.load(replacementPtr, 4n)
839
+ let patternLen = rawByteLength(patternPtr)
840
+ let stringLen = rawByteLength(stringPtr)
841
+ let replacementLen = rawByteLength(replacementPtr)
966
842
 
967
843
  // Bail if search str is longer than the string
968
844
  if (stringLen < patternLen) {
969
845
  return string
970
846
  }
971
- patternPtr += 8n
972
- stringPtr += 8n
973
- replacementPtr += 8n
847
+ patternPtr += _STR_DATA_OFFSET
848
+ stringPtr += _STR_DATA_OFFSET
849
+ replacementPtr += _STR_DATA_OFFSET
974
850
 
975
851
  let mut found = false
976
852
  // Search for an instance of the string
@@ -982,7 +858,7 @@ provide let replaceLast = (
982
858
  if (Memory.compare(i, patternPtr, patternLen) == 0n) {
983
859
  // Create the new string
984
860
  let str = allocateString(stringLen - patternLen + replacementLen)
985
- let strPtr = str + 8n
861
+ let strPtr = str + _STR_DATA_OFFSET
986
862
  Memory.copy(strPtr, stringPtr, foundIndex)
987
863
  Memory.copy(strPtr + foundIndex, replacementPtr, replacementLen)
988
864
  Memory.copy(
@@ -1020,23 +896,23 @@ provide let replaceAll = (
1020
896
  replacement: String,
1021
897
  string: String,
1022
898
  ) => {
1023
- use WasmI32.{ (+), (-), (*), (>>), gtU as (>), ltU as (<), (==) }
899
+ use WasmI32.{ (+), (-), (*), gtU as (>), ltU as (<), (==) }
1024
900
 
1025
901
  let mut patternPtr = WasmI32.fromGrain(searchPattern)
1026
902
  let mut stringPtr = WasmI32.fromGrain(string)
1027
903
  let mut replacementPtr = WasmI32.fromGrain(replacement)
1028
904
 
1029
- let patternLen = WasmI32.load(patternPtr, 4n)
1030
- let stringLen = WasmI32.load(stringPtr, 4n)
1031
- let replacementLen = WasmI32.load(replacementPtr, 4n)
905
+ let patternLen = rawByteLength(patternPtr)
906
+ let stringLen = rawByteLength(stringPtr)
907
+ let replacementLen = rawByteLength(replacementPtr)
1032
908
 
1033
909
  // Bail if search str is longer than the string
1034
910
  let result = if (stringLen < patternLen) {
1035
911
  string
1036
912
  } else {
1037
- patternPtr += 8n
1038
- stringPtr += 8n
1039
- replacementPtr += 8n
913
+ patternPtr += _STR_DATA_OFFSET
914
+ stringPtr += _STR_DATA_OFFSET
915
+ replacementPtr += _STR_DATA_OFFSET
1040
916
 
1041
917
  let mut found = false
1042
918
  // Search for an instance of the string
@@ -1060,7 +936,7 @@ provide let replaceAll = (
1060
936
  let str = allocateString(
1061
937
  stringLen - (patternLen - replacementLen) * foundCount
1062
938
  )
1063
- let mut strPtr = str + 8n
939
+ let mut strPtr = str + _STR_DATA_OFFSET
1064
940
  let mut lastIndex = 0n
1065
941
  while (true) {
1066
942
  match (foundIndexes) {
@@ -1102,44 +978,30 @@ let _OFFSET_NAME = "offset"
1102
978
 
1103
979
  @unsafe
1104
980
  let grainToWasmNumber = (num, name) => {
1105
- use WasmI32.{ (&), (>>), (<) }
1106
- let num = WasmI32.fromGrain(num)
1107
- if (WasmI32.eqz(num & 1n)) {
1108
- let str = " argument must be an integer"
1109
- throw Exception.InvalidArgument(name ++ str)
981
+ use WasmI32.{ (<) }
982
+ let _num = convSimpleNumber(num, name ++ " argument must be an integer")
983
+ if (_num < 0n) {
984
+ throw InvalidArgument(name ++ " argument must be non-negative")
1110
985
  }
1111
- let num = num >> 1n
1112
- if (num < 0n) {
1113
- let str = " argument must be non-negative"
1114
- throw Exception.InvalidArgument(name ++ str)
1115
- }
1116
- num
986
+ ignore(num)
987
+ _num
1117
988
  }
1118
989
 
1119
990
  @unsafe
1120
991
  let utf16Length = (s: String) => {
1121
- use WasmI32.{ (+), (&), (>>>), (<<), ltU as (<), (==) }
1122
-
1123
- let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
1124
- let len = WasmI32.fromGrain(length(s)) >>> 1n
992
+ use WasmI32.{ (+), (<<), ltU as (<), (==) }
1125
993
 
1126
994
  let sPtr = WasmI32.fromGrain(s)
995
+ let size = rawByteLength(sPtr)
996
+ let len = untagSimpleNumber(length(s))
1127
997
 
1128
- let mut ptr = sPtr + 8n
998
+ let mut ptr = sPtr + _STR_DATA_OFFSET
1129
999
  let end = ptr + size
1130
1000
  let mut size = 0n // <- number of UTF-16 code words
1131
1001
 
1132
1002
  while (ptr < end) {
1133
1003
  let byte = WasmI32.load8U(ptr, 0n)
1134
- let n = if ((byte & 0x80n) == 0x00n) {
1135
- 1n
1136
- } else if ((byte & 0xF0n) == 0xF0n) {
1137
- 4n
1138
- } else if ((byte & 0xE0n) == 0xE0n) {
1139
- 3n
1140
- } else {
1141
- 2n
1142
- }
1004
+ let n = utf8ByteCount(byte)
1143
1005
  if (n == 4n) {
1144
1006
  size += 2n
1145
1007
  } else {
@@ -1164,11 +1026,6 @@ let encodedLength = (s: String, encoding) => {
1164
1026
  }
1165
1027
  }
1166
1028
 
1167
- @unsafe
1168
- let mut _BYTES_SIZE_OFFSET = 4n
1169
- @unsafe
1170
- let mut _BYTES_OFFSET = 8n
1171
-
1172
1029
  @unsafe
1173
1030
  let encodeAtHelp = (
1174
1031
  string: String,
@@ -1178,18 +1035,17 @@ let encodeAtHelp = (
1178
1035
  destPos: Number,
1179
1036
  ) => {
1180
1037
  use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), (>), leU as (<=), (==) }
1181
- let byteSize = WasmI32.fromGrain(byteLength(string)) >>> 1n
1182
- let len = WasmI32.fromGrain(length(string)) >>> 1n
1183
-
1184
1038
  let stringPtr = WasmI32.fromGrain(string)
1039
+ let byteSize = rawByteLength(stringPtr)
1040
+ let len = untagSimpleNumber(length(string))
1185
1041
 
1186
- let mut ptr = stringPtr + 8n
1042
+ let mut ptr = stringPtr + _STR_DATA_OFFSET
1187
1043
  let end = ptr + byteSize
1188
1044
 
1189
1045
  let bytes = WasmI32.fromGrain(dest)
1190
1046
  let mut bytesIdx = grainToWasmNumber(destPos, _OFFSET_NAME)
1191
1047
 
1192
- let destSize = WasmI32.load(bytes, _BYTES_SIZE_OFFSET)
1048
+ let destSize = WasmI32.load(bytes, _BYTES_LEN_OFFSET)
1193
1049
 
1194
1050
  if (includeBom) {
1195
1051
  match (encoding) {
@@ -1197,45 +1053,45 @@ let encodeAtHelp = (
1197
1053
  if (bytesIdx + 3n > destSize) {
1198
1054
  throw IndexOutOfBounds
1199
1055
  }
1200
- WasmI32.store8(bytes + bytesIdx, 0xEFn, _BYTES_OFFSET)
1201
- WasmI32.store8(bytes + bytesIdx + 1n, 0xBBn, _BYTES_OFFSET)
1202
- WasmI32.store8(bytes + bytesIdx + 2n, 0xBFn, _BYTES_OFFSET)
1056
+ WasmI32.store8(bytes + bytesIdx, 0xEFn, _BYTES_DATA_OFFSET)
1057
+ WasmI32.store8(bytes + bytesIdx + 1n, 0xBBn, _BYTES_DATA_OFFSET)
1058
+ WasmI32.store8(bytes + bytesIdx + 2n, 0xBFn, _BYTES_DATA_OFFSET)
1203
1059
  bytesIdx += 3n
1204
1060
  },
1205
1061
  UTF16_BE => {
1206
1062
  if (bytesIdx + 2n > destSize) {
1207
1063
  throw IndexOutOfBounds
1208
1064
  }
1209
- WasmI32.store8(bytes + bytesIdx, 0xFEn, _BYTES_OFFSET)
1210
- WasmI32.store8(bytes + bytesIdx + 1n, 0xFFn, _BYTES_OFFSET)
1065
+ WasmI32.store8(bytes + bytesIdx, 0xFEn, _BYTES_DATA_OFFSET)
1066
+ WasmI32.store8(bytes + bytesIdx + 1n, 0xFFn, _BYTES_DATA_OFFSET)
1211
1067
  bytesIdx += 2n
1212
1068
  },
1213
1069
  UTF16_LE => {
1214
1070
  if (bytesIdx + 2n > destSize) {
1215
1071
  throw IndexOutOfBounds
1216
1072
  }
1217
- WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_OFFSET)
1218
- WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_OFFSET)
1073
+ WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_DATA_OFFSET)
1074
+ WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_DATA_OFFSET)
1219
1075
  bytesIdx += 2n
1220
1076
  },
1221
1077
  UTF32_BE => {
1222
1078
  if (bytesIdx + 4n > destSize) {
1223
1079
  throw IndexOutOfBounds
1224
1080
  }
1225
- WasmI32.store8(bytes + bytesIdx, 0n, _BYTES_OFFSET)
1226
- WasmI32.store8(bytes + bytesIdx + 1n, 0n, _BYTES_OFFSET)
1227
- WasmI32.store8(bytes + bytesIdx + 2n, 0xFEn, _BYTES_OFFSET)
1228
- WasmI32.store8(bytes + bytesIdx + 3n, 0xFFn, _BYTES_OFFSET)
1081
+ WasmI32.store8(bytes + bytesIdx, 0n, _BYTES_DATA_OFFSET)
1082
+ WasmI32.store8(bytes + bytesIdx + 1n, 0n, _BYTES_DATA_OFFSET)
1083
+ WasmI32.store8(bytes + bytesIdx + 2n, 0xFEn, _BYTES_DATA_OFFSET)
1084
+ WasmI32.store8(bytes + bytesIdx + 3n, 0xFFn, _BYTES_DATA_OFFSET)
1229
1085
  bytesIdx += 4n
1230
1086
  },
1231
1087
  UTF32_LE => {
1232
1088
  if (bytesIdx + 4n > destSize) {
1233
1089
  throw IndexOutOfBounds
1234
1090
  }
1235
- WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_OFFSET)
1236
- WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_OFFSET)
1237
- WasmI32.store8(bytes + bytesIdx + 2n, 0n, _BYTES_OFFSET)
1238
- WasmI32.store8(bytes + bytesIdx + 3n, 0n, _BYTES_OFFSET)
1091
+ WasmI32.store8(bytes + bytesIdx, 0xFFn, _BYTES_DATA_OFFSET)
1092
+ WasmI32.store8(bytes + bytesIdx + 1n, 0xFEn, _BYTES_DATA_OFFSET)
1093
+ WasmI32.store8(bytes + bytesIdx + 2n, 0n, _BYTES_DATA_OFFSET)
1094
+ WasmI32.store8(bytes + bytesIdx + 3n, 0n, _BYTES_DATA_OFFSET)
1239
1095
  bytesIdx += 4n
1240
1096
  },
1241
1097
  }
@@ -1249,21 +1105,13 @@ let encodeAtHelp = (
1249
1105
  if (bytesIdx + byteSize > destSize) {
1250
1106
  throw IndexOutOfBounds
1251
1107
  }
1252
- Memory.copy(bytes + bytesIdx + _BYTES_OFFSET, ptr, byteSize)
1108
+ Memory.copy(bytes + bytesIdx + _BYTES_DATA_OFFSET, ptr, byteSize)
1253
1109
  },
1254
1110
  _ => {
1255
1111
  while (ptr < end) {
1256
1112
  let byte = WasmI32.load8U(ptr, 0n)
1257
1113
  // number of bytes spanning this UTF-8-encoded scalar value
1258
- let n = if ((byte & 0x80n) == 0x00n) {
1259
- 1n
1260
- } else if ((byte & 0xF0n) == 0xF0n) {
1261
- 4n
1262
- } else if ((byte & 0xE0n) == 0xE0n) {
1263
- 3n
1264
- } else {
1265
- 2n
1266
- }
1114
+ let n = utf8ByteCount(byte)
1267
1115
  match (encoding) {
1268
1116
  UTF8 => {
1269
1117
  // With the optimization above for bulk memory copy, this match
@@ -1271,7 +1119,7 @@ let encodeAtHelp = (
1271
1119
  if (bytesIdx + n > destSize) {
1272
1120
  throw IndexOutOfBounds
1273
1121
  }
1274
- Memory.copy(bytes + bytesIdx + _BYTES_OFFSET, ptr, n)
1122
+ Memory.copy(bytes + bytesIdx + _BYTES_DATA_OFFSET, ptr, n)
1275
1123
  bytesIdx += n
1276
1124
  },
1277
1125
  UTF16_BE => {
@@ -1284,12 +1132,12 @@ let encodeAtHelp = (
1284
1132
  WasmI32.store8(
1285
1133
  bytes + bytesIdx,
1286
1134
  (codePoint & 0xff00n) >>> 8n,
1287
- _BYTES_OFFSET
1135
+ _BYTES_DATA_OFFSET
1288
1136
  )
1289
1137
  WasmI32.store8(
1290
1138
  bytes + bytesIdx + 1n,
1291
1139
  codePoint & 0xffn,
1292
- _BYTES_OFFSET
1140
+ _BYTES_DATA_OFFSET
1293
1141
  )
1294
1142
  bytesIdx += 2n
1295
1143
  } else {
@@ -1303,15 +1151,23 @@ let encodeAtHelp = (
1303
1151
  WasmI32.store8(
1304
1152
  bytes + bytesIdx,
1305
1153
  (w1 & 0xff00n) >>> 8n,
1306
- _BYTES_OFFSET
1154
+ _BYTES_DATA_OFFSET
1155
+ )
1156
+ WasmI32.store8(
1157
+ bytes + bytesIdx + 1n,
1158
+ w1 & 0xffn,
1159
+ _BYTES_DATA_OFFSET
1307
1160
  )
1308
- WasmI32.store8(bytes + bytesIdx + 1n, w1 & 0xffn, _BYTES_OFFSET)
1309
1161
  WasmI32.store8(
1310
1162
  bytes + bytesIdx + 2n,
1311
1163
  (w2 & 0xff00n) >>> 8n,
1312
- _BYTES_OFFSET
1164
+ _BYTES_DATA_OFFSET
1165
+ )
1166
+ WasmI32.store8(
1167
+ bytes + bytesIdx + 3n,
1168
+ w2 & 0xffn,
1169
+ _BYTES_DATA_OFFSET
1313
1170
  )
1314
- WasmI32.store8(bytes + bytesIdx + 3n, w2 & 0xffn, _BYTES_OFFSET)
1315
1171
  bytesIdx += 4n
1316
1172
  }
1317
1173
  },
@@ -1322,11 +1178,15 @@ let encodeAtHelp = (
1322
1178
  throw IndexOutOfBounds
1323
1179
  }
1324
1180
  // <lo><hi>
1325
- WasmI32.store8(bytes + bytesIdx, codePoint & 0xffn, _BYTES_OFFSET)
1181
+ WasmI32.store8(
1182
+ bytes + bytesIdx,
1183
+ codePoint & 0xffn,
1184
+ _BYTES_DATA_OFFSET
1185
+ )
1326
1186
  WasmI32.store8(
1327
1187
  bytes + bytesIdx + 1n,
1328
1188
  (codePoint & 0xff00n) >>> 8n,
1329
- _BYTES_OFFSET
1189
+ _BYTES_DATA_OFFSET
1330
1190
  )
1331
1191
  bytesIdx += 2n
1332
1192
  } else {
@@ -1337,17 +1197,21 @@ let encodeAtHelp = (
1337
1197
  let uPrime = codePoint - 0x10000n
1338
1198
  let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
1339
1199
  let w2 = (uPrime & 0b00000000001111111111n) + 0xDC00n // Low surrogate
1340
- WasmI32.store8(bytes + bytesIdx, w1 & 0xffn, _BYTES_OFFSET)
1200
+ WasmI32.store8(bytes + bytesIdx, w1 & 0xffn, _BYTES_DATA_OFFSET)
1341
1201
  WasmI32.store8(
1342
1202
  bytes + bytesIdx + 1n,
1343
1203
  (w1 & 0xff00n) >>> 8n,
1344
- _BYTES_OFFSET
1204
+ _BYTES_DATA_OFFSET
1205
+ )
1206
+ WasmI32.store8(
1207
+ bytes + bytesIdx + 2n,
1208
+ w2 & 0xffn,
1209
+ _BYTES_DATA_OFFSET
1345
1210
  )
1346
- WasmI32.store8(bytes + bytesIdx + 2n, w2 & 0xffn, _BYTES_OFFSET)
1347
1211
  WasmI32.store8(
1348
1212
  bytes + bytesIdx + 3n,
1349
1213
  (w2 & 0xff00n) >>> 8n,
1350
- _BYTES_OFFSET
1214
+ _BYTES_DATA_OFFSET
1351
1215
  )
1352
1216
  bytesIdx += 4n
1353
1217
  }
@@ -1360,22 +1224,22 @@ let encodeAtHelp = (
1360
1224
  WasmI32.store8(
1361
1225
  bytes + bytesIdx,
1362
1226
  (codePoint & 0xff000000n) >>> 24n,
1363
- _BYTES_OFFSET
1227
+ _BYTES_DATA_OFFSET
1364
1228
  )
1365
1229
  WasmI32.store8(
1366
1230
  bytes + bytesIdx + 1n,
1367
1231
  (codePoint & 0xff0000n) >>> 16n,
1368
- _BYTES_OFFSET
1232
+ _BYTES_DATA_OFFSET
1369
1233
  )
1370
1234
  WasmI32.store8(
1371
1235
  bytes + bytesIdx + 2n,
1372
1236
  (codePoint & 0xff00n) >>> 8n,
1373
- _BYTES_OFFSET
1237
+ _BYTES_DATA_OFFSET
1374
1238
  )
1375
1239
  WasmI32.store8(
1376
1240
  bytes + bytesIdx + 3n,
1377
1241
  codePoint & 0xffn,
1378
- _BYTES_OFFSET
1242
+ _BYTES_DATA_OFFSET
1379
1243
  )
1380
1244
  bytesIdx += 4n
1381
1245
  },
@@ -1384,21 +1248,25 @@ let encodeAtHelp = (
1384
1248
  throw IndexOutOfBounds
1385
1249
  }
1386
1250
  let codePoint = getCodePoint(ptr)
1387
- WasmI32.store8(bytes + bytesIdx, codePoint & 0xffn, _BYTES_OFFSET)
1251
+ WasmI32.store8(
1252
+ bytes + bytesIdx,
1253
+ codePoint & 0xffn,
1254
+ _BYTES_DATA_OFFSET
1255
+ )
1388
1256
  WasmI32.store8(
1389
1257
  bytes + bytesIdx + 1n,
1390
1258
  (codePoint & 0xff00n) >>> 8n,
1391
- _BYTES_OFFSET
1259
+ _BYTES_DATA_OFFSET
1392
1260
  )
1393
1261
  WasmI32.store8(
1394
1262
  bytes + bytesIdx + 2n,
1395
1263
  (codePoint & 0xff0000n) >>> 16n,
1396
- _BYTES_OFFSET
1264
+ _BYTES_DATA_OFFSET
1397
1265
  )
1398
1266
  WasmI32.store8(
1399
1267
  bytes + bytesIdx + 3n,
1400
1268
  (codePoint & 0xff000000n) >>> 24n,
1401
- _BYTES_OFFSET
1269
+ _BYTES_DATA_OFFSET
1402
1270
  )
1403
1271
  bytesIdx += 4n
1404
1272
  },
@@ -1446,8 +1314,7 @@ let encodeHelp = (string: String, encoding: Encoding, includeBom: Bool) => {
1446
1314
  } else {
1447
1315
  0
1448
1316
  })
1449
- use WasmI32.{ (>>>) }
1450
- let bytes = WasmI32.toGrain(allocateBytes(WasmI32.fromGrain(size) >>> 1n))
1317
+ let bytes = WasmI32.toGrain(allocateBytes(untagSimpleNumber(size)))
1451
1318
  encodeAtHelp(string, encoding, includeBom, bytes, 0)
1452
1319
  }
1453
1320
 
@@ -1468,92 +1335,42 @@ provide let encode = (string: String, encoding: Encoding, includeBom=false) => {
1468
1335
 
1469
1336
  // Byte->String decoding and helper functions:
1470
1337
 
1471
- @unsafe
1472
- let writeUtf8CodePoint = (ptr, codePoint) => {
1473
- use WasmI32.{ (+), (-), (&), (|), (>>>), ltU as (<), leU as (<=), (==) }
1474
- if (codePoint <= 0x007Fn) {
1475
- // Code points in the ASCII range are written as just one byte with the
1476
- // leading bit equal to zero (0xxxxxxx). Just store the value as one byte
1477
- // directly. Note that the value is already guaranteed to start with most
1478
- // significant bit equal to zero because of the check in the if statement
1479
- // above, so there's no need to bit-mask it.
1480
- WasmI32.store8(ptr, codePoint, 0n)
1481
- 1n
1482
- } else if (codePoint <= 0x07FFn) {
1483
- // Code points in the range 0x0080..0x07FF are written as two bytes.
1484
- // The first byte has a three bit prefix of 110, followed by 5 bits of the
1485
- // codepoint. The second byte has a two bit prefix of 10, followed by 6 bits
1486
- // of the codepoint.
1487
- let high = codePoint >>> 6n & 0b000_11111n | 0b110_00000n
1488
- let low = codePoint & 0b00_111111n | 0b10_000000n
1489
- WasmI32.store8(ptr, high, 0n)
1490
- WasmI32.store8(ptr + 1n, low, 0n)
1491
- 2n
1492
- } else if (codePoint <= 0xFFFFn) {
1493
- // Code points in the range 0x0800..0xFFFF are written as three bytes.
1494
- // The first byte has a four bit prefix of 1110, followed by 4 bits of the
1495
- // codepoint. Remaining bytes each have a two bit prefix of 10, followed by
1496
- // 6 bits of the codepoint.
1497
- let high = codePoint >>> 12n & 0b0000_1111n | 0b1110_0000n
1498
- let mid = codePoint >>> 6n & 0b00_111111n | 0b10_000000n
1499
- let low = codePoint & 0b00_111111n | 0b10_000000n
1500
- WasmI32.store8(ptr, high, 0n)
1501
- WasmI32.store8(ptr + 1n, mid, 0n)
1502
- WasmI32.store8(ptr + 2n, low, 0n)
1503
- 3n
1504
- } else {
1505
- // Code points in the range 0x10000..0x10FFFF are written as four bytes.
1506
- // The first byte has a five bit prefix of 11110, followed by 3 bits of the
1507
- // codepoint. Remaining bytes each have a two bit prefix of 10, followed by
1508
- // 6 bits of the codepoint.
1509
- let high = codePoint >>> 18n & 0b00000_111n | 0b11110_000n
1510
- let mid1 = codePoint >>> 12n & 0b00_111111n | 0b10_000000n
1511
- let mid2 = codePoint >>> 6n & 0b00_111111n | 0b10_000000n
1512
- let low = codePoint & 0b00_111111n | 0b10_000000n
1513
- WasmI32.store8(ptr, high, 0n)
1514
- WasmI32.store8(ptr + 1n, mid1, 0n)
1515
- WasmI32.store8(ptr + 2n, mid2, 0n)
1516
- WasmI32.store8(ptr + 3n, low, 0n)
1517
- 4n
1518
- }
1519
- }
1520
-
1521
1338
  @unsafe
1522
1339
  let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1523
1340
  use WasmI32.{ (+), geU as (>=), (==) }
1524
1341
  let ptr = WasmI32.fromGrain(bytes)
1525
- let bytesSize = WasmI32.load(ptr, 4n)
1342
+ let bytesSize = WasmI32.load(ptr, _BYTES_LEN_OFFSET)
1526
1343
  let ptr = ptr + start
1527
1344
  let result = match (encoding) {
1528
1345
  UTF8 => {
1529
- bytesSize >= 3n &&
1530
- WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xEFn &&
1531
- WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xBBn &&
1532
- WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0xBFn
1346
+ bytesSize >= 3n
1347
+ && WasmI32.load8U(ptr, _BYTES_DATA_OFFSET) == 0xEFn
1348
+ && WasmI32.load8U(ptr + 1n, _BYTES_DATA_OFFSET) == 0xBBn
1349
+ && WasmI32.load8U(ptr + 2n, _BYTES_DATA_OFFSET) == 0xBFn
1533
1350
  },
1534
1351
  UTF16_BE => {
1535
- bytesSize >= 2n &&
1536
- WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFEn &&
1537
- WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFFn
1352
+ bytesSize >= 2n
1353
+ && WasmI32.load8U(ptr, _BYTES_DATA_OFFSET) == 0xFEn
1354
+ && WasmI32.load8U(ptr + 1n, _BYTES_DATA_OFFSET) == 0xFFn
1538
1355
  },
1539
1356
  UTF16_LE => {
1540
- bytesSize >= 2n &&
1541
- WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFFn &&
1542
- WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFEn
1357
+ bytesSize >= 2n
1358
+ && WasmI32.load8U(ptr, _BYTES_DATA_OFFSET) == 0xFFn
1359
+ && WasmI32.load8U(ptr + 1n, _BYTES_DATA_OFFSET) == 0xFEn
1543
1360
  },
1544
1361
  UTF32_BE => {
1545
- bytesSize >= 4n &&
1546
- WasmI32.load8U(ptr, _BYTES_OFFSET) == 0x00n &&
1547
- WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0x00n &&
1548
- WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0xFEn &&
1549
- WasmI32.load8U(ptr + 3n, _BYTES_OFFSET) == 0xFFn
1362
+ bytesSize >= 4n
1363
+ && WasmI32.load8U(ptr, _BYTES_DATA_OFFSET) == 0x00n
1364
+ && WasmI32.load8U(ptr + 1n, _BYTES_DATA_OFFSET) == 0x00n
1365
+ && WasmI32.load8U(ptr + 2n, _BYTES_DATA_OFFSET) == 0xFEn
1366
+ && WasmI32.load8U(ptr + 3n, _BYTES_DATA_OFFSET) == 0xFFn
1550
1367
  },
1551
1368
  UTF32_LE => {
1552
- bytesSize >= 4n &&
1553
- WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xFFn &&
1554
- WasmI32.load8U(ptr + 1n, _BYTES_OFFSET) == 0xFEn &&
1555
- WasmI32.load8U(ptr + 2n, _BYTES_OFFSET) == 0x00n &&
1556
- WasmI32.load8U(ptr + 3n, _BYTES_OFFSET) == 0x00n
1369
+ bytesSize >= 4n
1370
+ && WasmI32.load8U(ptr, _BYTES_DATA_OFFSET) == 0xFFn
1371
+ && WasmI32.load8U(ptr + 1n, _BYTES_DATA_OFFSET) == 0xFEn
1372
+ && WasmI32.load8U(ptr + 2n, _BYTES_DATA_OFFSET) == 0x00n
1373
+ && WasmI32.load8U(ptr + 3n, _BYTES_DATA_OFFSET) == 0x00n
1557
1374
  },
1558
1375
  }
1559
1376
 
@@ -1585,14 +1402,14 @@ let decodedLength = (
1585
1402
  }
1586
1403
  let ptr = WasmI32.fromGrain(bytes)
1587
1404
  let bytesSize = {
1588
- let tmp = WasmI32.load(ptr, 4n) - start
1405
+ let tmp = WasmI32.load(ptr, _BYTES_LEN_OFFSET) - start
1589
1406
  if (size < tmp) {
1590
1407
  size
1591
1408
  } else {
1592
1409
  tmp
1593
1410
  }
1594
1411
  }
1595
- let start = ptr + _BYTES_OFFSET + start
1412
+ let start = ptr + _BYTES_DATA_OFFSET + start
1596
1413
  let result = match (encoding) {
1597
1414
  UTF8 => bytesSize,
1598
1415
  UTF16_BE => {
@@ -1606,8 +1423,8 @@ let decodedLength = (
1606
1423
  let ret = if (ptr + 2n >= end) {
1607
1424
  throw MalformedUnicode
1608
1425
  } else {
1609
- let nextCodeWord = WasmI32.load8U(ptr, 2n) << 8n |
1610
- WasmI32.load8U(ptr, 3n)
1426
+ let nextCodeWord = WasmI32.load8U(ptr, 2n) << 8n
1427
+ | WasmI32.load8U(ptr, 3n)
1611
1428
  if (nextCodeWord < 0xDC00n || nextCodeWord > 0xDFFFn) {
1612
1429
  // high surrogate without low surrogate
1613
1430
  throw MalformedUnicode
@@ -1624,15 +1441,7 @@ let decodedLength = (
1624
1441
  ptr += 2n
1625
1442
  codeWord
1626
1443
  }
1627
- if (codeWord <= 0x007Fn) {
1628
- count += 1n
1629
- } else if (codeWord <= 0x07FFn) {
1630
- count += 2n
1631
- } else if (codeWord <= 0xFFFFn) {
1632
- count += 3n
1633
- } else {
1634
- count += 4n
1635
- }
1444
+ count += usvEncodeLength(codeWord)
1636
1445
  }
1637
1446
  count
1638
1447
  },
@@ -1647,8 +1456,8 @@ let decodedLength = (
1647
1456
  let ret = if (ptr + 2n >= end) {
1648
1457
  throw MalformedUnicode
1649
1458
  } else {
1650
- let nextCodeWord = WasmI32.load8U(ptr, 3n) << 8n |
1651
- WasmI32.load8U(ptr, 2n)
1459
+ let nextCodeWord = WasmI32.load8U(ptr, 3n) << 8n
1460
+ | WasmI32.load8U(ptr, 2n)
1652
1461
  if (nextCodeWord < 0xDC00n || nextCodeWord > 0xDFFFn) {
1653
1462
  // high surrogate without low surrogate
1654
1463
  throw MalformedUnicode
@@ -1665,15 +1474,7 @@ let decodedLength = (
1665
1474
  ptr += 2n
1666
1475
  codeWord
1667
1476
  }
1668
- if (codeWord <= 0x007Fn) {
1669
- count += 1n
1670
- } else if (codeWord <= 0x07FFn) {
1671
- count += 2n
1672
- } else if (codeWord <= 0xFFFFn) {
1673
- count += 3n
1674
- } else {
1675
- count += 4n
1676
- }
1477
+ count += usvEncodeLength(codeWord)
1677
1478
  }
1678
1479
  count
1679
1480
  },
@@ -1686,20 +1487,12 @@ let decodedLength = (
1686
1487
  let mut ptr = start
1687
1488
  let mut count = 0n
1688
1489
  while (ptr < end) {
1689
- let codeWord = WasmI32.load8U(ptr, 0n) << 24n |
1690
- WasmI32.load8U(ptr, 1n) << 16n |
1691
- WasmI32.load8U(ptr, 2n) << 8n |
1692
- WasmI32.load8U(ptr, 3n)
1490
+ let codeWord = WasmI32.load8U(ptr, 0n) << 24n
1491
+ | WasmI32.load8U(ptr, 1n) << 16n
1492
+ | WasmI32.load8U(ptr, 2n) << 8n
1493
+ | WasmI32.load8U(ptr, 3n)
1693
1494
  ptr += 4n
1694
- if (codeWord <= 0x007Fn) {
1695
- count += 1n
1696
- } else if (codeWord <= 0x07FFn) {
1697
- count += 2n
1698
- } else if (codeWord <= 0xFFFFn) {
1699
- count += 3n
1700
- } else {
1701
- count += 4n
1702
- }
1495
+ count += usvEncodeLength(codeWord)
1703
1496
  }
1704
1497
  count
1705
1498
  },
@@ -1712,20 +1505,12 @@ let decodedLength = (
1712
1505
  let mut ptr = start
1713
1506
  let mut count = 0n
1714
1507
  while (ptr < end) {
1715
- let codeWord = WasmI32.load8U(ptr, 3n) << 24n |
1716
- WasmI32.load8U(ptr, 2n) << 16n |
1717
- WasmI32.load8U(ptr, 1n) << 8n |
1718
- WasmI32.load8U(ptr, 0n)
1508
+ let codeWord = WasmI32.load8U(ptr, 3n) << 24n
1509
+ | WasmI32.load8U(ptr, 2n) << 16n
1510
+ | WasmI32.load8U(ptr, 1n) << 8n
1511
+ | WasmI32.load8U(ptr, 0n)
1719
1512
  ptr += 4n
1720
- if (codeWord <= 0x007Fn) {
1721
- count += 1n
1722
- } else if (codeWord <= 0x07FFn) {
1723
- count += 2n
1724
- } else if (codeWord <= 0xFFFFn) {
1725
- count += 3n
1726
- } else {
1727
- count += 4n
1728
- }
1513
+ count += usvEncodeLength(codeWord)
1729
1514
  }
1730
1515
  count
1731
1516
  },
@@ -1764,15 +1549,15 @@ let decodeRangeHelp = (
1764
1549
  let str = allocateString(stringSize)
1765
1550
  let mut bytesPtr = WasmI32.fromGrain(bytes)
1766
1551
  let bytesSize = {
1767
- let tmp = WasmI32.load(bytesPtr, 4n) - start
1552
+ let tmp = WasmI32.load(bytesPtr, _BYTES_LEN_OFFSET) - start
1768
1553
  if (size < tmp) {
1769
1554
  size
1770
1555
  } else {
1771
1556
  tmp
1772
1557
  }
1773
1558
  }
1774
- bytesPtr += _BYTES_OFFSET + start
1775
- let mut strPtr = str + 8n
1559
+ bytesPtr += _BYTES_DATA_OFFSET + start
1560
+ let mut strPtr = str + _STR_DATA_OFFSET
1776
1561
  let mut bomRead = false
1777
1562
  if (skipBom && hasBom) {
1778
1563
  bytesPtr += match (encoding) {
@@ -1794,16 +1579,16 @@ let decodeRangeHelp = (
1794
1579
  // NOTE: Because the size check passed, we know the string is well-formed
1795
1580
  let end = bytesPtr + bytesSize
1796
1581
  while (bytesPtr < end) {
1797
- let w1 = WasmI32.load8U(bytesPtr, 0n) << 8n |
1798
- WasmI32.load8U(bytesPtr, 1n)
1582
+ let w1 = WasmI32.load8U(bytesPtr, 0n) << 8n
1583
+ | WasmI32.load8U(bytesPtr, 1n)
1799
1584
  let codeWord = if (w1 >= 0xD800n && w1 <= 0xDBFFn) {
1800
1585
  // high surrogate. next character is low srurrogate
1801
1586
  let w1 = (w1 & 0x03FFn) << 10n
1802
1587
  let w2 = (
1803
- WasmI32.load8U(bytesPtr, 2n) << 8n |
1804
- WasmI32.load8U(bytesPtr, 3n)
1805
- ) &
1806
- 0x03FFn
1588
+ WasmI32.load8U(bytesPtr, 2n) << 8n
1589
+ | WasmI32.load8U(bytesPtr, 3n)
1590
+ )
1591
+ & 0x03FFn
1807
1592
  let codeWord = w1 + w2 + 0x10000n
1808
1593
  // no problems, so go past both code words
1809
1594
  bytesPtr += 4n
@@ -1819,16 +1604,16 @@ let decodeRangeHelp = (
1819
1604
  // NOTE: Because the size check passed, we know the string is well-formed
1820
1605
  let end = bytesPtr + bytesSize
1821
1606
  while (bytesPtr < end) {
1822
- let w1 = WasmI32.load8U(bytesPtr, 1n) << 8n |
1823
- WasmI32.load8U(bytesPtr, 0n)
1607
+ let w1 = WasmI32.load8U(bytesPtr, 1n) << 8n
1608
+ | WasmI32.load8U(bytesPtr, 0n)
1824
1609
  let codeWord = if (w1 >= 0xD800n && w1 <= 0xDBFFn) {
1825
1610
  // high surrogate. next character is low srurrogate
1826
1611
  let w1 = (w1 & 0x03FFn) << 10n
1827
1612
  let w2 = (
1828
- WasmI32.load8U(bytesPtr, 3n) << 8n |
1829
- WasmI32.load8U(bytesPtr, 2n)
1830
- ) &
1831
- 0x03FFn
1613
+ WasmI32.load8U(bytesPtr, 3n) << 8n
1614
+ | WasmI32.load8U(bytesPtr, 2n)
1615
+ )
1616
+ & 0x03FFn
1832
1617
  //let uPrime = codePoint - 0x10000n
1833
1618
  //let w1 = ((uPrime & 0b11111111110000000000n) >>> 10n) + 0xD800n // High surrogate
1834
1619
  //let w2 = (uPrime & 0b00000000001111111111n) + 0xDC00n // Low surrogate
@@ -1846,10 +1631,10 @@ let decodeRangeHelp = (
1846
1631
  UTF32_BE => {
1847
1632
  let end = bytesPtr + bytesSize
1848
1633
  while (bytesPtr < end) {
1849
- let codeWord = WasmI32.load8U(bytesPtr, 0n) << 24n |
1850
- WasmI32.load8U(bytesPtr, 1n) << 16n |
1851
- WasmI32.load8U(bytesPtr, 2n) << 8n |
1852
- WasmI32.load8U(bytesPtr, 3n)
1634
+ let codeWord = WasmI32.load8U(bytesPtr, 0n) << 24n
1635
+ | WasmI32.load8U(bytesPtr, 1n) << 16n
1636
+ | WasmI32.load8U(bytesPtr, 2n) << 8n
1637
+ | WasmI32.load8U(bytesPtr, 3n)
1853
1638
  bytesPtr += 4n
1854
1639
  strPtr += writeUtf8CodePoint(strPtr, codeWord)
1855
1640
  }
@@ -1857,10 +1642,10 @@ let decodeRangeHelp = (
1857
1642
  UTF32_LE => {
1858
1643
  let end = bytesPtr + bytesSize
1859
1644
  while (bytesPtr < end) {
1860
- let codeWord = WasmI32.load8U(bytesPtr, 3n) << 24n |
1861
- WasmI32.load8U(bytesPtr, 2n) << 16n |
1862
- WasmI32.load8U(bytesPtr, 1n) << 8n |
1863
- WasmI32.load8U(bytesPtr, 0n)
1645
+ let codeWord = WasmI32.load8U(bytesPtr, 3n) << 24n
1646
+ | WasmI32.load8U(bytesPtr, 2n) << 16n
1647
+ | WasmI32.load8U(bytesPtr, 1n) << 8n
1648
+ | WasmI32.load8U(bytesPtr, 0n)
1864
1649
  bytesPtr += 4n
1865
1650
  strPtr += writeUtf8CodePoint(strPtr, codeWord)
1866
1651
  }
@@ -1944,26 +1729,18 @@ provide let decode = (bytes: Bytes, encoding: Encoding, keepBom=false) => {
1944
1729
  */
1945
1730
  @unsafe
1946
1731
  provide let forEachCodePoint = (fn: Number => Void, str: String) => {
1947
- use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
1732
+ use WasmI32.{ (+), ltU as (<) }
1948
1733
 
1949
1734
  let strPtr = WasmI32.fromGrain(str)
1950
1735
 
1951
- let byteSize = WasmI32.load(strPtr, 4n)
1736
+ let byteSize = rawByteLength(strPtr)
1952
1737
 
1953
- let mut ptr = strPtr + 8n
1738
+ let mut ptr = strPtr + _STR_DATA_OFFSET
1954
1739
  let end = ptr + byteSize
1955
1740
 
1956
1741
  while (ptr < end) {
1957
1742
  let byte = WasmI32.load8U(ptr, 0n)
1958
- let codePointByteCount = if ((byte & 0x80n) == 0x00n) {
1959
- 1n
1960
- } else if ((byte & 0xF0n) == 0xF0n) {
1961
- 4n
1962
- } else if ((byte & 0xE0n) == 0xE0n) {
1963
- 3n
1964
- } else {
1965
- 2n
1966
- }
1743
+ let codePointByteCount = utf8ByteCount(byte)
1967
1744
 
1968
1745
  // Note that even if up to 4 bytes are needed to represent Unicode
1969
1746
  // codepoints, this doesn't mean 32 bits. The highest allowed code point is
@@ -1997,27 +1774,19 @@ provide let forEachCodePoint = (fn: Number => Void, str: String) => {
1997
1774
  */
1998
1775
  @unsafe
1999
1776
  provide let forEachCodePointi = (fn: (Number, Number) => Void, str: String) => {
2000
- use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
1777
+ use WasmI32.{ (+), ltU as (<), (==) }
2001
1778
 
2002
1779
  let strPtr = WasmI32.fromGrain(str)
2003
1780
 
2004
- let byteSize = WasmI32.load(strPtr, 4n)
1781
+ let byteSize = rawByteLength(strPtr)
2005
1782
 
2006
- let mut ptr = strPtr + 8n
1783
+ let mut ptr = strPtr + _STR_DATA_OFFSET
2007
1784
  let end = ptr + byteSize
2008
1785
 
2009
1786
  let mut idx = 0n
2010
1787
  while (ptr < end) {
2011
1788
  let byte = WasmI32.load8U(ptr, 0n)
2012
- let codePointByteCount = if ((byte & 0x80n) == 0x00n) {
2013
- 1n
2014
- } else if ((byte & 0xF0n) == 0xF0n) {
2015
- 4n
2016
- } else if ((byte & 0xE0n) == 0xE0n) {
2017
- 3n
2018
- } else {
2019
- 2n
2020
- }
1789
+ let codePointByteCount = utf8ByteCount(byte)
2021
1790
 
2022
1791
  // Note that even if up to 4 bytes are needed to represent Unicode
2023
1792
  // codepoints, this doesn't mean 32 bits. The highest allowed code point is
@@ -2050,26 +1819,18 @@ provide let forEachCodePointi = (fn: (Number, Number) => Void, str: String) => {
2050
1819
  */
2051
1820
  @unsafe
2052
1821
  provide let forEachChar = (fn: Char => Void, str: String) => {
2053
- use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
1822
+ use WasmI32.{ (+), ltU as (<), (==) }
2054
1823
 
2055
1824
  let strPtr = WasmI32.fromGrain(str)
2056
1825
 
2057
- let byteSize = WasmI32.load(strPtr, 4n)
1826
+ let byteSize = rawByteLength(strPtr)
2058
1827
 
2059
- let mut ptr = strPtr + 8n
1828
+ let mut ptr = strPtr + _STR_DATA_OFFSET
2060
1829
  let end = ptr + byteSize
2061
1830
 
2062
1831
  while (ptr < end) {
2063
1832
  let byte = WasmI32.load8U(ptr, 0n)
2064
- let codePointByteCount = if ((byte & 0x80n) == 0x00n) {
2065
- 1n
2066
- } else if ((byte & 0xF0n) == 0xF0n) {
2067
- 4n
2068
- } else if ((byte & 0xE0n) == 0xE0n) {
2069
- 3n
2070
- } else {
2071
- 2n
2072
- }
1833
+ let codePointByteCount = utf8ByteCount(byte)
2073
1834
 
2074
1835
  // Note that even if up to 4 bytes are needed to represent Unicode
2075
1836
  // codepoints, this doesn't mean 32 bits. The highest allowed code point is
@@ -2100,27 +1861,19 @@ provide let forEachChar = (fn: Char => Void, str: String) => {
2100
1861
  */
2101
1862
  @unsafe
2102
1863
  provide let forEachChari = (fn: (Char, Number) => Void, str: String) => {
2103
- use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
1864
+ use WasmI32.{ (+), ltU as (<), (==) }
2104
1865
 
2105
1866
  let strPtr = WasmI32.fromGrain(str)
2106
1867
 
2107
- let byteSize = WasmI32.load(strPtr, 4n)
1868
+ let byteSize = rawByteLength(strPtr)
2108
1869
 
2109
- let mut ptr = strPtr + 8n
1870
+ let mut ptr = strPtr + _STR_DATA_OFFSET
2110
1871
  let end = ptr + byteSize
2111
1872
 
2112
1873
  let mut idx = 0n
2113
1874
  while (ptr < end) {
2114
1875
  let byte = WasmI32.load8U(ptr, 0n)
2115
- let codePointByteCount = if ((byte & 0x80n) == 0x00n) {
2116
- 1n
2117
- } else if ((byte & 0xF0n) == 0xF0n) {
2118
- 4n
2119
- } else if ((byte & 0xE0n) == 0xE0n) {
2120
- 3n
2121
- } else {
2122
- 2n
2123
- }
1876
+ let codePointByteCount = utf8ByteCount(byte)
2124
1877
 
2125
1878
  // Note that even if up to 4 bytes are needed to represent Unicode
2126
1879
  // codepoints, this doesn't mean 32 bits. The highest allowed code point is
@@ -2194,7 +1947,8 @@ let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
2194
1947
  // TODO(#661): Use unicode whitespace property and unicode line terminator
2195
1948
  if (!fromEnd) {
2196
1949
  if (
2197
- byte == 0xEFn && // Check for the first BOM byte
1950
+ byte == 0xEFn
1951
+ && // Check for the first BOM byte
2198
1952
  // Check for the full BOM codepoint, 0xEFBBBF. Bytes are reversed because wasm is little-endian
2199
1953
  WasmI32.load(stringPtr, i - 1n) >>> 8n == 0xBFBBEFn
2200
1954
  ) {
@@ -2204,7 +1958,8 @@ let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
2204
1958
  }
2205
1959
  } else {
2206
1960
  if (
2207
- byte == 0xBFn && // Check for the last BOM byte
1961
+ byte == 0xBFn
1962
+ && // Check for the last BOM byte
2208
1963
  // Check for the full BOM codepoint, 0xEFBBBF. Bytes are reversed because wasm is little-endian
2209
1964
  WasmI32.load(stringPtr, i - 3n) >>> 8n == 0xBFBBEFn
2210
1965
  ) {
@@ -2214,12 +1969,18 @@ let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
2214
1969
  }
2215
1970
  }
2216
1971
  if (
2217
- byte != 0x20n && // Space
2218
- byte != 0x0Dn && // LF
2219
- byte != 0x0An && // CR
2220
- byte != 0x09n && // Tab
2221
- byte != 0x0Bn && // LINE TABULATION
2222
- byte != 0x0Cn && // FORM FEED (FF)
1972
+ byte != 0x20n
1973
+ && // Space
1974
+ byte != 0x0Dn
1975
+ && // LF
1976
+ byte != 0x0An
1977
+ && // CR
1978
+ byte != 0x09n
1979
+ && // Tab
1980
+ byte != 0x0Bn
1981
+ && // LINE TABULATION
1982
+ byte != 0x0Cn
1983
+ && // FORM FEED (FF)
2223
1984
  byte != 0xA0n // No Break Space
2224
1985
  ) {
2225
1986
  break
@@ -2242,11 +2003,11 @@ let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
2242
2003
  provide let trimStart = (string: String) => {
2243
2004
  use WasmI32.{ (-), (+) }
2244
2005
  let mut stringPtr = WasmI32.fromGrain(string)
2245
- let byteLength = WasmI32.load(stringPtr, 4n)
2246
- stringPtr += 8n
2006
+ let byteLength = rawByteLength(stringPtr)
2007
+ stringPtr += _STR_DATA_OFFSET
2247
2008
  let count = trimString(stringPtr, byteLength, false)
2248
2009
  let str = allocateString(byteLength - count)
2249
- Memory.copy(str + 8n, stringPtr + count, byteLength - count)
2010
+ Memory.copy(str + _STR_DATA_OFFSET, stringPtr + count, byteLength - count)
2250
2011
  ignore(string)
2251
2012
  WasmI32.toGrain(str): String
2252
2013
  }
@@ -2264,11 +2025,11 @@ provide let trimStart = (string: String) => {
2264
2025
  provide let trimEnd = (string: String) => {
2265
2026
  use WasmI32.{ (-), (+) }
2266
2027
  let mut stringPtr = WasmI32.fromGrain(string)
2267
- let byteLength = WasmI32.load(stringPtr, 4n)
2268
- stringPtr += 8n
2028
+ let byteLength = rawByteLength(stringPtr)
2029
+ stringPtr += _STR_DATA_OFFSET
2269
2030
  let count = trimString(stringPtr, byteLength, true)
2270
2031
  let str = allocateString(byteLength - count)
2271
- Memory.copy(str + 8n, stringPtr, byteLength - count)
2032
+ Memory.copy(str + _STR_DATA_OFFSET, stringPtr, byteLength - count)
2272
2033
  ignore(string)
2273
2034
  WasmI32.toGrain(str): String
2274
2035
  }
@@ -2286,14 +2047,14 @@ provide let trimEnd = (string: String) => {
2286
2047
  provide let trim = (string: String) => {
2287
2048
  use WasmI32.{ (-), (+), (==) }
2288
2049
  let mut stringPtr = WasmI32.fromGrain(string)
2289
- let byteLength = WasmI32.load(stringPtr, 4n)
2290
- stringPtr += 8n
2050
+ let byteLength = rawByteLength(stringPtr)
2051
+ stringPtr += _STR_DATA_OFFSET
2291
2052
  let startCount = trimString(stringPtr, byteLength, false)
2292
2053
  if (startCount == byteLength) return ""
2293
2054
  let endCount = trimString(stringPtr, byteLength, true)
2294
2055
  let str = allocateString(byteLength - startCount - endCount)
2295
2056
  Memory.copy(
2296
- str + 8n,
2057
+ str + _STR_DATA_OFFSET,
2297
2058
  stringPtr + startCount,
2298
2059
  byteLength - startCount - endCount
2299
2060
  )
@@ -2315,7 +2076,7 @@ provide let toAsciiLowercase = string => {
2315
2076
  let chars = explode(string)
2316
2077
  let len = arrayLength(chars)
2317
2078
  for (let mut i = 0; i < len; i += 1) {
2318
- chars[i] = Char.toAsciiLowercase(chars[i])
2079
+ chars[i] = Char.Ascii.toLowercase(chars[i])
2319
2080
  }
2320
2081
  implode(chars)
2321
2082
  }
@@ -2334,7 +2095,41 @@ provide let toAsciiUppercase = string => {
2334
2095
  let chars = explode(string)
2335
2096
  let len = arrayLength(chars)
2336
2097
  for (let mut i = 0; i < len; i += 1) {
2337
- chars[i] = Char.toAsciiUppercase(chars[i])
2098
+ chars[i] = Char.Ascii.toUppercase(chars[i])
2338
2099
  }
2339
2100
  implode(chars)
2340
2101
  }
2102
+
2103
+ /**
2104
+ * Produces a new string by repeating a substring a given number of times.
2105
+ *
2106
+ * @param count: The number of times to repeat the string
2107
+ * @param string: The string to repeat
2108
+ * @returns A string containing the repeated input string
2109
+ *
2110
+ * @throws InvalidArgument(String): When the `count` is not an integer
2111
+ * @throws InvalidArgument(String): When the `count` is negative
2112
+ *
2113
+ * @example assert String.repeat(5, "=") == "====="
2114
+ * @example assert String.repeat(0, ".") == ""
2115
+ *
2116
+ * @since v0.6.7
2117
+ */
2118
+ @unsafe
2119
+ provide let repeat = (count, string: String) => {
2120
+ use WasmI32.{ (+), (*), (<), (&), (!=) }
2121
+ let rawCount = convSimpleNumber(count, "Invalid count value")
2122
+ if (rawCount < 0n) {
2123
+ throw InvalidArgument("Invalid count must be a positive integer or zero")
2124
+ }
2125
+ let stringPtr = WasmI32.fromGrain(string)
2126
+ let byteLength = rawByteLength(stringPtr)
2127
+ let stringPtr = stringPtr + _STR_DATA_OFFSET
2128
+ let newStringPtr = allocateString(byteLength * rawCount)
2129
+ let strContentPtr = newStringPtr + _STR_DATA_OFFSET
2130
+ for (let mut i = 0n; i < rawCount; i += 1n) {
2131
+ Memory.copy(strContentPtr + byteLength * i, stringPtr, byteLength)
2132
+ }
2133
+ ignore(string)
2134
+ return WasmI32.toGrain(newStringPtr): String
2135
+ }