@grain/stdlib 0.6.3 → 0.6.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/string.gr CHANGED
@@ -64,11 +64,11 @@ provide let concat = (++)
64
64
  @unsafe
65
65
  provide let length = (string: String) => {
66
66
  use WasmI32.{ (+), (&), (!=) }
67
- let string = WasmI32.fromGrain(string)
68
- let size = WasmI32.load(string, 4n)
67
+ let stringPtr = WasmI32.fromGrain(string)
68
+ let size = WasmI32.load(stringPtr, 4n)
69
69
 
70
70
  let mut len = 0n
71
- let mut ptr = string + 8n
71
+ let mut ptr = stringPtr + 8n
72
72
  let end = ptr + size
73
73
 
74
74
  while (WasmI32.ltU(ptr, end)) {
@@ -79,6 +79,8 @@ provide let length = (string: String) => {
79
79
  ptr += 1n
80
80
  }
81
81
 
82
+ ignore(string)
83
+
82
84
  Conv.wasmI32ToNumber(len)
83
85
  }
84
86
 
@@ -94,8 +96,10 @@ provide let length = (string: String) => {
94
96
  */
95
97
  @unsafe
96
98
  provide let byteLength = (string: String) => {
97
- let string = WasmI32.fromGrain(string)
98
- Conv.wasmI32ToNumber(WasmI32.load(string, 4n))
99
+ let stringPtr = WasmI32.fromGrain(string)
100
+ let result = Conv.wasmI32ToNumber(WasmI32.load(stringPtr, 4n))
101
+ ignore(string)
102
+ result
99
103
  }
100
104
 
101
105
  /**
@@ -110,7 +114,9 @@ provide let byteLength = (string: String) => {
110
114
  provide let isEmpty = (string: String) => {
111
115
  use WasmI32.{ (==) }
112
116
  let strPtr = WasmI32.fromGrain(string)
113
- WasmI32.load(strPtr, 4n) == 0n
117
+ let result = WasmI32.load(strPtr, 4n) == 0n
118
+ ignore(string)
119
+ result
114
120
  }
115
121
 
116
122
  /**
@@ -126,19 +132,19 @@ provide let isEmpty = (string: String) => {
126
132
  */
127
133
  @unsafe
128
134
  provide let indexOf = (search: String, string: String) => {
129
- let search = WasmI32.fromGrain(search)
130
- let string = WasmI32.fromGrain(string)
135
+ let searchPtr = WasmI32.fromGrain(search)
136
+ let stringPtr = WasmI32.fromGrain(string)
131
137
 
132
- let size = WasmI32.load(string, 4n)
133
- let psize = WasmI32.load(search, 4n)
138
+ let size = WasmI32.load(stringPtr, 4n)
139
+ let psize = WasmI32.load(searchPtr, 4n)
134
140
  use WasmI32.{ (+), (-), (&), ltU as (<), gtU as (>), (==) }
135
141
 
136
142
  if (psize > size) {
137
143
  return None
138
144
  }
139
145
  let mut idx = 0n
140
- let mut ptr = string + 8n
141
- let pptr = search + 8n
146
+ let mut ptr = stringPtr + 8n
147
+ let pptr = searchPtr + 8n
142
148
  let end = ptr + size - psize + 1n
143
149
 
144
150
  while (ptr < end) {
@@ -158,6 +164,9 @@ provide let indexOf = (search: String, string: String) => {
158
164
  }
159
165
  }
160
166
 
167
+ ignore(search)
168
+ ignore(string)
169
+
161
170
  return None
162
171
  }
163
172
 
@@ -179,16 +188,16 @@ provide let lastIndexOf = (search: String, string: String) => {
179
188
 
180
189
  use WasmI32.{ (+), (-), (&), gtU as (>), geU as (>=), (==), (!=) }
181
190
 
182
- let search = WasmI32.fromGrain(search)
183
- let string = WasmI32.fromGrain(string)
184
- let searchSize = WasmI32.load(search, 4n)
185
- let stringSize = WasmI32.load(string, 4n)
191
+ let searchPtr = WasmI32.fromGrain(search)
192
+ let stringPtr = WasmI32.fromGrain(string)
193
+ let searchSize = WasmI32.load(searchPtr, 4n)
194
+ let stringSize = WasmI32.load(stringPtr, 4n)
186
195
  if (searchSize > stringSize) {
187
196
  return None
188
197
  }
189
198
  let mut stringIndex = untagSimpleNumber(lastIndex)
190
- let searchPtr = search + 8n
191
- let stringStartPtr = string + 8n
199
+ let searchPtr = searchPtr + 8n
200
+ let stringStartPtr = stringPtr + 8n
192
201
  for (
193
202
  let mut stringPtr = stringStartPtr + stringSize - searchSize;
194
203
  stringPtr >= stringStartPtr;
@@ -205,6 +214,9 @@ provide let lastIndexOf = (search: String, string: String) => {
205
214
  stringIndex -= 1n
206
215
  }
207
216
 
217
+ ignore(search)
218
+ ignore(string)
219
+
208
220
  return None
209
221
  }
210
222
 
@@ -268,8 +280,8 @@ let charAtHelp = (position, string: String) => {
268
280
  use WasmI32.{ (+), (&), (>>>), ltU as (<), (==) }
269
281
  let size = WasmI32.fromGrain(byteLength(string)) >>> 1n
270
282
  let position = WasmI32.fromGrain(position) >>> 1n
271
- let string = WasmI32.fromGrain(string)
272
- let mut ptr = string + 8n
283
+ let stringPre = WasmI32.fromGrain(string)
284
+ let mut ptr = stringPre + 8n
273
285
  let end = ptr + size
274
286
  let mut counter = 0n
275
287
  while (ptr < end) {
@@ -290,6 +302,8 @@ let charAtHelp = (position, string: String) => {
290
302
  ptr += n
291
303
  }
292
304
 
305
+ ignore(string)
306
+
293
307
  fail "charAt: should be impossible (please report)"
294
308
  }
295
309
 
@@ -338,9 +352,9 @@ let explodeHelp = (s: String, chars) => {
338
352
  let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
339
353
  let len = WasmI32.fromGrain(length(s)) >>> 1n
340
354
 
341
- let s = WasmI32.fromGrain(s)
355
+ let sPtr = WasmI32.fromGrain(s)
342
356
 
343
- let mut ptr = s + 8n
357
+ let mut ptr = sPtr + 8n
344
358
  let end = ptr + size
345
359
 
346
360
  let arr = allocateArray(len)
@@ -361,9 +375,9 @@ let explodeHelp = (s: String, chars) => {
361
375
  let c = if (chars) {
362
376
  WasmI32.fromGrain(tagChar(getCodePoint(ptr)))
363
377
  } else {
364
- let s = allocateString(n)
365
- Memory.copy(s + 8n, ptr, n)
366
- s
378
+ let sPtr = allocateString(n)
379
+ Memory.copy(sPtr + 8n, ptr, n)
380
+ sPtr
367
381
  }
368
382
 
369
383
  WasmI32.store(arr + arrIdx, c, 8n)
@@ -371,6 +385,8 @@ let explodeHelp = (s: String, chars) => {
371
385
  ptr += n
372
386
  }
373
387
 
388
+ ignore(s)
389
+
374
390
  arr
375
391
  }
376
392
 
@@ -523,11 +539,11 @@ provide let split = (separator: String, string: String) => {
523
539
  } else if (psize > size) {
524
540
  [> string]
525
541
  } else {
526
- let string = WasmI32.fromGrain(string)
527
- let separator = WasmI32.fromGrain(separator)
542
+ let stringPtr = WasmI32.fromGrain(string)
543
+ let separatorPtr = WasmI32.fromGrain(separator)
528
544
 
529
- let mut ptr = string + 8n
530
- let mut pptr = separator + 8n
545
+ let mut ptr = stringPtr + 8n
546
+ let mut pptr = separatorPtr + 8n
531
547
  let end = ptr + size - psize + 1n
532
548
 
533
549
  let mut numStrings = 1n
@@ -548,7 +564,7 @@ provide let split = (separator: String, string: String) => {
548
564
  }
549
565
  }
550
566
 
551
- ptr = string + 8n
567
+ ptr = stringPtr + 8n
552
568
  let mut last = ptr
553
569
  let arr = allocateArray(numStrings)
554
570
  let mut arrIdx = 0n
@@ -577,11 +593,14 @@ provide let split = (separator: String, string: String) => {
577
593
  }
578
594
 
579
595
  // Grab last string
580
- let strSize = string + 8n + size - last
596
+ let strSize = stringPtr + 8n + size - last
581
597
  let lastStr = allocateString(strSize)
582
598
  Memory.copy(lastStr + 8n, last, strSize)
583
599
  WasmI32.store(arr + arrIdx, lastStr, 8n)
584
600
 
601
+ ignore(separator)
602
+ ignore(string)
603
+
585
604
  WasmI32.toGrain(arr): Array<String>
586
605
  }
587
606
  }
@@ -613,7 +632,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
613
632
  let len = WasmI32.fromGrain(length(string)) >> 1n
614
633
  let size = WasmI32.fromGrain(byteLength(string)) >> 1n
615
634
 
616
- let string = WasmI32.fromGrain(string)
635
+ let stringPtr = WasmI32.fromGrain(string)
617
636
 
618
637
  let mut start = WasmI32.fromGrain(start)
619
638
  if ((start & 1n) != 1n) {
@@ -642,7 +661,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
642
661
  throw InvalidArgument("Start index exceeds end index")
643
662
  }
644
663
 
645
- let mut ptr = string + 8n
664
+ let mut ptr = stringPtr + 8n
646
665
  let mut begin = ptr
647
666
  let mut end = ptr
648
667
  let stop = ptr + size
@@ -663,7 +682,7 @@ provide let slice = (start: Number, end=length(string), string: String) => {
663
682
  ptr += 1n
664
683
  }
665
684
  if (to == len) {
666
- end = string + 8n + size
685
+ end = stringPtr + 8n + size
667
686
  }
668
687
  if (start == to) {
669
688
  begin = end
@@ -674,6 +693,8 @@ provide let slice = (start: Number, end=length(string), string: String) => {
674
693
 
675
694
  Memory.copy(newString + 8n, begin, newSize)
676
695
 
696
+ ignore(string)
697
+
677
698
  WasmI32.toGrain(newString): String
678
699
  }
679
700
 
@@ -710,11 +731,11 @@ provide let contains = (search: String, string: String) => {
710
731
  let n = WasmI32.fromGrain(byteLength(string)) >> 1n
711
732
  let m = WasmI32.fromGrain(byteLength(search)) >> 1n
712
733
 
713
- let mut string = WasmI32.fromGrain(string)
714
- let mut search = WasmI32.fromGrain(search)
734
+ let mut stringPtr = WasmI32.fromGrain(string)
735
+ let mut searchPtr = WasmI32.fromGrain(search)
715
736
 
716
- string += 8n
717
- search += 8n
737
+ stringPtr += 8n
738
+ searchPtr += 8n
718
739
 
719
740
  let mut j = 0n
720
741
  and k = 0n
@@ -731,9 +752,9 @@ provide let contains = (search: String, string: String) => {
731
752
  return true
732
753
  }
733
754
 
734
- let pat = WasmI32.load8U(search, 0n)
755
+ let pat = WasmI32.load8U(searchPtr, 0n)
735
756
  while (j < n) {
736
- if (pat == WasmI32.load8U(string + j, 0n)) {
757
+ if (pat == WasmI32.load8U(stringPtr + j, 0n)) {
737
758
  return true
738
759
  } else {
739
760
  j += 1n
@@ -744,7 +765,7 @@ provide let contains = (search: String, string: String) => {
744
765
  }
745
766
 
746
767
  // NSM preprocessing
747
- if (WasmI32.load8U(search, 0n) == WasmI32.load8U(search, 1n)) {
768
+ if (WasmI32.load8U(searchPtr, 0n) == WasmI32.load8U(searchPtr, 1n)) {
748
769
  k = 2n
749
770
  ell = 1n
750
771
  } else {
@@ -754,12 +775,12 @@ provide let contains = (search: String, string: String) => {
754
775
 
755
776
  // NSM searching
756
777
  while (j <= n - m) {
757
- if (WasmI32.load8U(search, 1n) != WasmI32.load8U(string + j, 1n)) {
778
+ if (WasmI32.load8U(searchPtr, 1n) != WasmI32.load8U(stringPtr + j, 1n)) {
758
779
  j += k
759
780
  } else {
760
781
  if (
761
- Memory.compare(search + 2n, string + j + 2n, m - 2n) == 0n &&
762
- WasmI32.load8U(search, 0n) == WasmI32.load8U(string + j, 0n)
782
+ Memory.compare(searchPtr + 2n, stringPtr + j + 2n, m - 2n) == 0n &&
783
+ WasmI32.load8U(searchPtr, 0n) == WasmI32.load8U(stringPtr + j, 0n)
763
784
  ) {
764
785
  return true
765
786
  }
@@ -767,6 +788,9 @@ provide let contains = (search: String, string: String) => {
767
788
  }
768
789
  }
769
790
 
791
+ ignore(search)
792
+ ignore(string)
793
+
770
794
  return false
771
795
  }
772
796
 
@@ -787,21 +811,26 @@ provide let startsWith = (search: String, string: String) => {
787
811
  let pOrig = search
788
812
  let sOrig = string
789
813
 
790
- let mut search = WasmI32.fromGrain(search)
791
- let mut string = WasmI32.fromGrain(string)
814
+ let mut searchPtr = WasmI32.fromGrain(search)
815
+ let mut stringPtr = WasmI32.fromGrain(string)
792
816
 
793
- let n = WasmI32.load(string, 4n)
794
- let m = WasmI32.load(search, 4n)
817
+ let n = WasmI32.load(stringPtr, 4n)
818
+ let m = WasmI32.load(searchPtr, 4n)
795
819
 
796
- string += 8n
797
- search += 8n
820
+ stringPtr += 8n
821
+ searchPtr += 8n
798
822
 
799
823
  // Bail if pattern length is longer than input length
800
- if (m > n) {
824
+ let result = if (m > n) {
801
825
  false
802
826
  } else {
803
- Memory.compare(search, string, m) == 0n
827
+ Memory.compare(searchPtr, stringPtr, m) == 0n
804
828
  }
829
+
830
+ ignore(search)
831
+ ignore(string)
832
+
833
+ result
805
834
  }
806
835
 
807
836
  /**
@@ -821,21 +850,26 @@ provide let endsWith = (search: String, string: String) => {
821
850
  let pOrig = search
822
851
  let sOrig = string
823
852
 
824
- let mut search = WasmI32.fromGrain(search)
825
- let mut string = WasmI32.fromGrain(string)
853
+ let mut searchPtr = WasmI32.fromGrain(search)
854
+ let mut stringPtr = WasmI32.fromGrain(string)
826
855
 
827
- let n = WasmI32.load(string, 4n)
828
- let m = WasmI32.load(search, 4n)
856
+ let n = WasmI32.load(stringPtr, 4n)
857
+ let m = WasmI32.load(searchPtr, 4n)
829
858
 
830
- string += 8n
831
- search += 8n
859
+ stringPtr += 8n
860
+ searchPtr += 8n
832
861
 
833
862
  // Bail if pattern length is longer than input length
834
- if (m > n) {
863
+ let result = if (m > n) {
835
864
  false
836
865
  } else {
837
- Memory.compare(search, string + n - m, m) == 0n
866
+ Memory.compare(searchPtr, stringPtr + n - m, m) == 0n
838
867
  }
868
+
869
+ ignore(search)
870
+ ignore(string)
871
+
872
+ result
839
873
  }
840
874
 
841
875
  /**
@@ -895,6 +929,10 @@ provide let replaceFirst = (
895
929
  }
896
930
  }
897
931
 
932
+ ignore(searchPattern)
933
+ ignore(string)
934
+ ignore(replacement)
935
+
898
936
  return string
899
937
  }
900
938
 
@@ -957,6 +995,10 @@ provide let replaceLast = (
957
995
  }
958
996
  }
959
997
 
998
+ ignore(searchPattern)
999
+ ignore(string)
1000
+ ignore(replacement)
1001
+
960
1002
  return string
961
1003
  }
962
1004
 
@@ -989,7 +1031,7 @@ provide let replaceAll = (
989
1031
  let replacementLen = WasmI32.load(replacementPtr, 4n)
990
1032
 
991
1033
  // Bail if search str is longer than the string
992
- if (stringLen < patternLen) {
1034
+ let result = if (stringLen < patternLen) {
993
1035
  string
994
1036
  } else {
995
1037
  patternPtr += 8n
@@ -1043,6 +1085,12 @@ provide let replaceAll = (
1043
1085
  string
1044
1086
  }
1045
1087
  }
1088
+
1089
+ ignore(searchPattern)
1090
+ ignore(string)
1091
+ ignore(replacement)
1092
+
1093
+ result
1046
1094
  }
1047
1095
 
1048
1096
  // String->Byte encoding and helper functions:
@@ -1075,9 +1123,9 @@ let utf16Length = (s: String) => {
1075
1123
  let size = WasmI32.fromGrain(byteLength(s)) >>> 1n
1076
1124
  let len = WasmI32.fromGrain(length(s)) >>> 1n
1077
1125
 
1078
- let s = WasmI32.fromGrain(s)
1126
+ let sPtr = WasmI32.fromGrain(s)
1079
1127
 
1080
- let mut ptr = s + 8n
1128
+ let mut ptr = sPtr + 8n
1081
1129
  let end = ptr + size
1082
1130
  let mut size = 0n // <- number of UTF-16 code words
1083
1131
 
@@ -1099,11 +1147,13 @@ let utf16Length = (s: String) => {
1099
1147
  }
1100
1148
  ptr += n
1101
1149
  }
1150
+
1151
+ ignore(s)
1152
+
1102
1153
  // multiply by two to get number of bytes
1103
1154
  tagSimpleNumber(size << 1n)
1104
1155
  }
1105
1156
 
1106
- @unsafe
1107
1157
  let encodedLength = (s: String, encoding) => {
1108
1158
  match (encoding) {
1109
1159
  UTF32_BE => length(s) * 4,
@@ -1131,9 +1181,9 @@ let encodeAtHelp = (
1131
1181
  let byteSize = WasmI32.fromGrain(byteLength(string)) >>> 1n
1132
1182
  let len = WasmI32.fromGrain(length(string)) >>> 1n
1133
1183
 
1134
- let string = WasmI32.fromGrain(string)
1184
+ let stringPtr = WasmI32.fromGrain(string)
1135
1185
 
1136
- let mut ptr = string + 8n
1186
+ let mut ptr = stringPtr + 8n
1137
1187
  let end = ptr + byteSize
1138
1188
 
1139
1189
  let bytes = WasmI32.fromGrain(dest)
@@ -1358,6 +1408,8 @@ let encodeAtHelp = (
1358
1408
  },
1359
1409
  }
1360
1410
 
1411
+ ignore(string)
1412
+
1361
1413
  dest
1362
1414
  }
1363
1415
 
@@ -1472,7 +1524,7 @@ let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1472
1524
  let ptr = WasmI32.fromGrain(bytes)
1473
1525
  let bytesSize = WasmI32.load(ptr, 4n)
1474
1526
  let ptr = ptr + start
1475
- match (encoding) {
1527
+ let result = match (encoding) {
1476
1528
  UTF8 => {
1477
1529
  bytesSize >= 3n &&
1478
1530
  WasmI32.load8U(ptr, _BYTES_OFFSET) == 0xEFn &&
@@ -1504,6 +1556,10 @@ let bytesHaveBom = (bytes: Bytes, encoding: Encoding, start: WasmI32) => {
1504
1556
  WasmI32.load8U(ptr + 3n, _BYTES_OFFSET) == 0x00n
1505
1557
  },
1506
1558
  }
1559
+
1560
+ ignore(bytes)
1561
+
1562
+ result
1507
1563
  }
1508
1564
 
1509
1565
  @unsafe
@@ -1537,7 +1593,7 @@ let decodedLength = (
1537
1593
  }
1538
1594
  }
1539
1595
  let start = ptr + _BYTES_OFFSET + start
1540
- match (encoding) {
1596
+ let result = match (encoding) {
1541
1597
  UTF8 => bytesSize,
1542
1598
  UTF16_BE => {
1543
1599
  let end = start + bytesSize
@@ -1674,6 +1730,10 @@ let decodedLength = (
1674
1730
  count
1675
1731
  },
1676
1732
  }
1733
+
1734
+ ignore(bytes)
1735
+
1736
+ result
1677
1737
  }
1678
1738
 
1679
1739
  @unsafe
@@ -1723,7 +1783,7 @@ let decodeRangeHelp = (
1723
1783
  UTF32_BE => 4n,
1724
1784
  }
1725
1785
  }
1726
- if (stringSize == 0n) {
1786
+ let result = if (stringSize == 0n) {
1727
1787
  WasmI32.toGrain(str): String
1728
1788
  } else {
1729
1789
  match (encoding) {
@@ -1808,6 +1868,10 @@ let decodeRangeHelp = (
1808
1868
  }
1809
1869
  WasmI32.toGrain(str): String
1810
1870
  }
1871
+
1872
+ ignore(bytes)
1873
+
1874
+ result
1811
1875
  }
1812
1876
 
1813
1877
  /**
@@ -1842,7 +1906,15 @@ provide let decodeRange = (
1842
1906
  let decodeHelp = (bytes: Bytes, encoding: Encoding, skipBom: Bool) => {
1843
1907
  let bytesPtr = WasmI32.fromGrain(bytes)
1844
1908
  let bytesSize = WasmI32.load(bytesPtr, 4n)
1845
- decodeRangeHelp(bytes, encoding, skipBom, 0, tagSimpleNumber(bytesSize))
1909
+ let result = decodeRangeHelp(
1910
+ bytes,
1911
+ encoding,
1912
+ skipBom,
1913
+ 0,
1914
+ tagSimpleNumber(bytesSize)
1915
+ )
1916
+ ignore(bytes)
1917
+ result
1846
1918
  }
1847
1919
 
1848
1920
  /**
@@ -1881,7 +1953,6 @@ provide let forEachCodePoint = (fn: Number => Void, str: String) => {
1881
1953
  let mut ptr = strPtr + 8n
1882
1954
  let end = ptr + byteSize
1883
1955
 
1884
- let mut idx = 0n
1885
1956
  while (ptr < end) {
1886
1957
  let byte = WasmI32.load8U(ptr, 0n)
1887
1958
  let codePointByteCount = if ((byte & 0x80n) == 0x00n) {
@@ -1905,8 +1976,10 @@ provide let forEachCodePoint = (fn: Number => Void, str: String) => {
1905
1976
  fn(tagSimpleNumber(codePoint))
1906
1977
 
1907
1978
  ptr += codePointByteCount
1908
- idx += 1n
1909
1979
  }
1980
+
1981
+ ignore(str)
1982
+
1910
1983
  void
1911
1984
  }
1912
1985
 
@@ -1959,9 +2032,152 @@ provide let forEachCodePointi = (fn: (Number, Number) => Void, str: String) => {
1959
2032
  ptr += codePointByteCount
1960
2033
  idx += 1n
1961
2034
  }
2035
+
2036
+ ignore(str)
2037
+
1962
2038
  void
1963
2039
  }
1964
2040
 
2041
+ /**
2042
+ * Iterates over Unicode characters in a string.
2043
+ *
2044
+ * @param fn: The iterator function
2045
+ * @param str: The string to iterate
2046
+ *
2047
+ * @example String.forEachChar(print, "Hello world")
2048
+ *
2049
+ * @since v0.6.5
2050
+ */
2051
+ @unsafe
2052
+ provide let forEachChar = (fn: Char => Void, str: String) => {
2053
+ use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
2054
+
2055
+ let strPtr = WasmI32.fromGrain(str)
2056
+
2057
+ let byteSize = WasmI32.load(strPtr, 4n)
2058
+
2059
+ let mut ptr = strPtr + 8n
2060
+ let end = ptr + byteSize
2061
+
2062
+ while (ptr < end) {
2063
+ 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
+ }
2073
+
2074
+ // Note that even if up to 4 bytes are needed to represent Unicode
2075
+ // codepoints, this doesn't mean 32 bits. The highest allowed code point is
2076
+ // 0x10FFFF and it should not change in future versions of Unicode. This
2077
+ // means no more than 21 bits are necessary to represent a code point and
2078
+ // thus we can use Grain's "simple" numbers that hold up to 31 bits and
2079
+ // avoid heap allocations. `getCodePoint` will throw
2080
+ // MalformedUnicode exception for values exceeding this limit.
2081
+ let codePoint = getCodePoint(ptr)
2082
+ fn(tagChar(codePoint))
2083
+
2084
+ ptr += codePointByteCount
2085
+ }
2086
+ void
2087
+ }
2088
+
2089
+ /**
2090
+ * Iterates over Unicode characters in a string. This is the same as
2091
+ * `forEachChar`, but provides the characters's index in the string
2092
+ * as the second argument to the iterator function.
2093
+ *
2094
+ * @param fn: The iterator function
2095
+ * @param str: The string to iterate
2096
+ *
2097
+ * @example String.forEachChari((char, index) => print((char, index)), "Hello world")
2098
+ *
2099
+ * @since v0.6.5
2100
+ */
2101
+ @unsafe
2102
+ provide let forEachChari = (fn: (Char, Number) => Void, str: String) => {
2103
+ use WasmI32.{ (+), (-), (&), (>>>), ltU as (<), leU as (<=), (==) }
2104
+
2105
+ let strPtr = WasmI32.fromGrain(str)
2106
+
2107
+ let byteSize = WasmI32.load(strPtr, 4n)
2108
+
2109
+ let mut ptr = strPtr + 8n
2110
+ let end = ptr + byteSize
2111
+
2112
+ let mut idx = 0n
2113
+ while (ptr < end) {
2114
+ 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
+ }
2124
+
2125
+ // Note that even if up to 4 bytes are needed to represent Unicode
2126
+ // codepoints, this doesn't mean 32 bits. The highest allowed code point is
2127
+ // 0x10FFFF and it should not change in future versions of Unicode. This
2128
+ // means no more than 21 bits are necessary to represent a code point and
2129
+ // thus we can use Grain's "simple" numbers that hold up to 31 bits and
2130
+ // avoid heap allocations. `getCodePoint` will throw
2131
+ // MalformedUnicode exception for values exceeding this limit.
2132
+ let codePoint = getCodePoint(ptr)
2133
+ fn(tagChar(codePoint), tagSimpleNumber(idx))
2134
+
2135
+ ptr += codePointByteCount
2136
+ idx += 1n
2137
+ }
2138
+ void
2139
+ }
2140
+
2141
+ /**
2142
+ * Builds a new string by mapping Unicode characters.
2143
+ *
2144
+ * @param fn: The mapping function
2145
+ * @param str: The string to map
2146
+ *
2147
+ * @example assert String.map((c) => 'a', "Hello world") == "aaaaaaaaaaa"
2148
+ *
2149
+ * @since v0.6.5
2150
+ */
2151
+ provide let map = (fn: Char => Char, str: String) => {
2152
+ let chars = explode(str)
2153
+ let arrLen = arrayLength(chars)
2154
+ for (let mut i = 0; i < arrLen; i += 1) {
2155
+ chars[i] = fn(chars[i])
2156
+ }
2157
+ implode(chars)
2158
+ }
2159
+
2160
+ /**
2161
+ * Builds a new string by mapping Unicode characters. This is the same as
2162
+ * `mapChar`, but provides the characters's index in the string
2163
+ * as the second argument to the mapping function.
2164
+ *
2165
+ * @param fn: The mapping function
2166
+ * @param str: The string to map
2167
+ *
2168
+ * @example assert String.mapi((char, index) => String.charAt(0, toString(index)), "Hello world") == "01234567891"
2169
+ *
2170
+ * @since v0.6.5
2171
+ */
2172
+ provide let mapi = (fn: (Char, Number) => Char, str: String) => {
2173
+ let chars = explode(str)
2174
+ let arrLen = arrayLength(chars)
2175
+ for (let mut i = 0; i < arrLen; i += 1) {
2176
+ chars[i] = fn(chars[i], i)
2177
+ }
2178
+ implode(chars)
2179
+ }
2180
+
1965
2181
  @unsafe
1966
2182
  let trimString = (stringPtr: WasmI32, byteLength: WasmI32, fromEnd: Bool) => {
1967
2183
  use WasmI32.{ (+), (-), (*), (>>>), ltU as (<), (==), (!=) }
@@ -2031,6 +2247,7 @@ provide let trimStart = (string: String) => {
2031
2247
  let count = trimString(stringPtr, byteLength, false)
2032
2248
  let str = allocateString(byteLength - count)
2033
2249
  Memory.copy(str + 8n, stringPtr + count, byteLength - count)
2250
+ ignore(string)
2034
2251
  WasmI32.toGrain(str): String
2035
2252
  }
2036
2253
  /**
@@ -2052,6 +2269,7 @@ provide let trimEnd = (string: String) => {
2052
2269
  let count = trimString(stringPtr, byteLength, true)
2053
2270
  let str = allocateString(byteLength - count)
2054
2271
  Memory.copy(str + 8n, stringPtr, byteLength - count)
2272
+ ignore(string)
2055
2273
  WasmI32.toGrain(str): String
2056
2274
  }
2057
2275
  /**
@@ -2079,6 +2297,7 @@ provide let trim = (string: String) => {
2079
2297
  stringPtr + startCount,
2080
2298
  byteLength - startCount - endCount
2081
2299
  )
2300
+ ignore(string)
2082
2301
  return WasmI32.toGrain(str): String
2083
2302
  }
2084
2303