@grain/stdlib 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ### [0.4.4](https://www.github.com/grain-lang/grain/compare/stdlib-v0.4.3...stdlib-v0.4.4) (2021-12-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * **stdlib:** Add reverse function to String module ([#1027](https://www.github.com/grain-lang/grain/issues/1027)) ([df761db](https://www.github.com/grain-lang/grain/commit/df761db55b3e14e31190090ae008ce5047135c09))
9
+ * **stdlib:** Add rotate function in Array module ([#838](https://www.github.com/grain-lang/grain/issues/838)) ([98fc577](https://www.github.com/grain-lang/grain/commit/98fc577ee754317cd2421bfaa8e3c1e049488949))
10
+ * **stdlib:** Add sort function to Array module ([#1012](https://www.github.com/grain-lang/grain/issues/1012)) ([9091930](https://www.github.com/grain-lang/grain/commit/9091930344224925bb7b2e1ef6f879c79a5c2f62))
11
+ * **stdlib:** Implement List.sort via mergesort ([#1014](https://www.github.com/grain-lang/grain/issues/1014)) ([a076e20](https://www.github.com/grain-lang/grain/commit/a076e200013114ccf16c2e6cbe814af1ec09c1ce))
12
+ * **stdlib:** Number.parseInt ([#1051](https://www.github.com/grain-lang/grain/issues/1051)) ([abafb58](https://www.github.com/grain-lang/grain/commit/abafb587e54219a32ed77ba09863bb2d6a80bac8))
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **graindoc:** Ensure value_description is resolved to outcome before printing ([#1070](https://www.github.com/grain-lang/grain/issues/1070)) ([5eb05cc](https://www.github.com/grain-lang/grain/commit/5eb05cc2dedc3b933e194be86dd5d3c3656d6490))
18
+ * **stdlib:** Add bounds checking to Buffer addStringSlice & addBytesSlice ([#1065](https://www.github.com/grain-lang/grain/issues/1065)) ([06fe512](https://www.github.com/grain-lang/grain/commit/06fe512e863aeeb855ccf3e3b83bcd3bc8854723))
19
+ * **stdlib:** Fix String.encode GC ([#1067](https://www.github.com/grain-lang/grain/issues/1067)) ([0ab38c9](https://www.github.com/grain-lang/grain/commit/0ab38c9f4aa0ee84688ba5c6bec1521b380d38b1))
20
+ * **stdlib:** Removed memory leak in Hash module ([#1045](https://www.github.com/grain-lang/grain/issues/1045)) ([01a81c6](https://www.github.com/grain-lang/grain/commit/01a81c6a2573cca94b2d57d0fc70693d39f810a1))
21
+ * **stdlib:** Removed memory leaks in Buffer module ([#1047](https://www.github.com/grain-lang/grain/issues/1047)) ([d33017b](https://www.github.com/grain-lang/grain/commit/d33017b37e988d3facbca2e30e3de4fb8c7b5b8a))
22
+ * **stdlib:** Support arrays of any type in Array rotate ([#1048](https://www.github.com/grain-lang/grain/issues/1048)) ([3ceb1cf](https://www.github.com/grain-lang/grain/commit/3ceb1cf04c1604f49077e8733dcccb6cdaaf9f3a))
23
+ * **stdlib:** Support empty arrays in Array rotate ([3ceb1cf](https://www.github.com/grain-lang/grain/commit/3ceb1cf04c1604f49077e8733dcccb6cdaaf9f3a))
24
+
3
25
  ### [0.4.3](https://www.github.com/grain-lang/grain/compare/stdlib-v0.4.2...stdlib-v0.4.3) (2021-10-27)
4
26
 
5
27
 
package/array.gr CHANGED
@@ -243,15 +243,10 @@ export let copy = (array) => {
243
243
  */
244
244
  export let cycle = (fn, n, array) => {
245
245
  let length = length(array)
246
- let mut iteration = 0
247
- let mut count = 0
248
- while (iteration < n) {
249
- while (count < length) {
250
- fn(array[count]): Void
251
- count = incr(count)
246
+ for (let mut iteration = 0; iteration < n; iteration += 1) {
247
+ for (let mut count = 0; count < length; count += 1) {
248
+ fn(array[count]): (Void)
252
249
  }
253
- iteration = incr(iteration)
254
- count = 0
255
250
  }
256
251
  }
257
252
 
@@ -266,10 +261,8 @@ export let cycle = (fn, n, array) => {
266
261
  */
267
262
  export let forEach = (fn, array) => {
268
263
  let length = length(array)
269
- let mut count = 0
270
- while (count < length) {
271
- fn(array[count]): Void
272
- count = incr(count)
264
+ for (let mut count = 0; count < length; count += 1) {
265
+ fn(array[count]): (Void)
273
266
  }
274
267
  }
275
268
 
@@ -285,10 +278,8 @@ export let forEach = (fn, array) => {
285
278
  */
286
279
  export let forEachi = (fn, array) => {
287
280
  let length = length(array)
288
- let mut count = 0
289
- while (count < length) {
281
+ for (let mut count = 0; count < length; count += 1) {
290
282
  fn(array[count], count): (Void)
291
- count = incr(count)
292
283
  }
293
284
  }
294
285
 
@@ -458,9 +449,12 @@ export let fillRange = (value, start, stop, array) => {
458
449
  }
459
450
 
460
451
  let mut index = startIndex
461
- while (index < stopIndex && index < length) {
462
- array[index] = value
452
+ for (
453
+ let mut index = startIndex;
454
+ index < stopIndex && index < length;
463
455
  index += 1
456
+ ) {
457
+ array[index] = value
464
458
  }
465
459
  void
466
460
  }
@@ -543,11 +537,9 @@ export let fromList = (list) => {
543
537
  export let contains = (search, array) => {
544
538
  // TODO: This should use recursion when we can pattern match arrays
545
539
  let len = length(array)
546
- let mut index = 0
547
540
  let mut found = false
548
- while ((!found) && (index < len)) {
549
- found = (array[index] == search)
550
- index += 1
541
+ for (let mut index = 0; !found && index < len; index += 1) {
542
+ found = array[index] == search
551
543
  }
552
544
  found
553
545
  }
@@ -575,7 +567,7 @@ export let find = (fn, array) => {
575
567
  matchedItem = array[count]
576
568
  count = length
577
569
  } else {
578
- count = incr(count)
570
+ count += 1
579
571
  }
580
572
  }
581
573
  if(!matching) {
@@ -609,7 +601,7 @@ export let findIndex = (fn, array) => {
609
601
  matchedIndex = count
610
602
  count = length
611
603
  } else {
612
- count = incr(count)
604
+ count += 1
613
605
  }
614
606
  }
615
607
  if(!matching) {
@@ -637,7 +629,7 @@ export let product = (array1: Array<a>, array2: Array<b>) => {
637
629
 
638
630
  init(lenA * lenB, n => {
639
631
  if(n % lenB == 0){
640
- indexA = incr(indexA)
632
+ indexA += 1
641
633
  } else {
642
634
  indexA = indexA
643
635
  }
@@ -658,12 +650,9 @@ export let count = (fn, array) => {
658
650
  let length = length(array)
659
651
  let mut position = 0
660
652
  let mut count = 0
661
- while (position < length) {
653
+ for (let mut position = 0; position < length; position += 1) {
662
654
  if(fn(array[position])) {
663
- count = incr(count)
664
- position = incr(position)
665
- } else {
666
- position = incr(position)
655
+ count += 1
667
656
  }
668
657
  }
669
658
  count
@@ -681,14 +670,10 @@ export let count = (fn, array) => {
681
670
  */
682
671
  export let counti = (fn, array) => {
683
672
  let length = length(array)
684
- let mut position = 0
685
673
  let mut count = 0
686
- while (position < length) {
687
- if(fn(array[position], position)) {
688
- count = incr(count)
689
- position = incr(position)
690
- } else {
691
- position = incr(position)
674
+ for (let mut position = 0; position < length; position += 1) {
675
+ if (fn(array[position], position)) {
676
+ count += 1
692
677
  }
693
678
  }
694
679
  count
@@ -711,7 +696,7 @@ export let filter = (fn, array) => {
711
696
  forEach((el) => {
712
697
  if (fn(el)) {
713
698
  filtered[position] = el
714
- position = incr(position)
699
+ position += 1
715
700
  }
716
701
  }, array)
717
702
  init(position, (index) => {
@@ -736,7 +721,7 @@ export let filteri = (fn, array) => {
736
721
  forEachi((el, index) => {
737
722
  if (fn(el, index)) {
738
723
  filtered[position] = el
739
- position = incr(position)
724
+ position += 1
740
725
  }
741
726
  }, array)
742
727
  init(position, (index) => {
@@ -828,6 +813,18 @@ export let join = (separator: String, items: Array<String>) => {
828
813
  }
829
814
  }
830
815
 
816
+ /**
817
+ * A simple helper function to convert a negative array index
818
+ * number to its positive positional equivalent.
819
+ */
820
+ let wrapNegativeIndex = (arrLen, idx) => {
821
+ if (idx >= 0) {
822
+ idx
823
+ } else {
824
+ arrLen + idx
825
+ }
826
+ }
827
+
831
828
  /**
832
829
  * Slices an array given zero-based start and end indexes. The value
833
830
  * at the end index will not be included in the result.
@@ -844,17 +841,8 @@ export let join = (separator: String, items: Array<String>) => {
844
841
  */
845
842
  export let slice = (startIndex, endIndex, array) => {
846
843
  let arrayLength = length(array)
847
-
848
- let wrapNegativeIndex = idx => {
849
- if (idx >= 0) {
850
- idx
851
- } else {
852
- arrayLength + idx
853
- }
854
- }
855
-
856
- let startIndex = wrapNegativeIndex(startIndex)
857
- let endIndex = wrapNegativeIndex(endIndex)
844
+ let startIndex = wrapNegativeIndex(arrayLength, startIndex)
845
+ let endIndex = wrapNegativeIndex(arrayLength, endIndex)
858
846
  // Ensure we aren't working with an `end` value that is too big
859
847
  let endIndex = if (endIndex > arrayLength) {
860
848
  arrayLength
@@ -871,3 +859,84 @@ export let slice = (startIndex, endIndex, array) => {
871
859
  init(newLength, n => array[startIndex + n])
872
860
  }
873
861
  }
862
+
863
+ /**
864
+ * Sorts an array in-place.
865
+ *
866
+ * Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
867
+ * @param comp: The comparator function used to indicate sort order
868
+ * @param array: The array to be sorted
869
+ * @since v0.4.5
870
+ */
871
+ export let sort = (comp, array) => {
872
+ let partition = (low, high) => {
873
+ let pivot = array[high]
874
+ let mut i = low - 1
875
+ for(let mut j = low; j < high; j += 1) {
876
+ if(comp(array[j], pivot) < 0){
877
+ i += 1
878
+ let temp = array[i]
879
+ array[i] = array[j]
880
+ array[j] = temp
881
+ }
882
+ }
883
+ let temp = array[i + 1]
884
+ array[i + 1] = array[high]
885
+ array[high] = temp
886
+ i + 1
887
+ }
888
+ let rec quicksort = (low, high) => {
889
+ if(low < high){
890
+ let partitionIndex = partition(low, high)
891
+ quicksort(partitionIndex + 1, high)
892
+ quicksort(low, partitionIndex - 1)
893
+ }
894
+ }
895
+ let len = length(array)
896
+ quicksort(0, len - 1)
897
+ }
898
+
899
+ /**
900
+ * Rotates an array by n elements to the right, in place.
901
+ *
902
+ * If n is negative, the array will be rotated by n elements
903
+ * to the left. See examples.
904
+ *
905
+ * @param n: The number of elements to rotate by
906
+ * @param arr: The array to be rotated
907
+ *
908
+ * @example let array = [> 1, 2, 3, 4, 5]; rotate(2, arr); arr == [> 4, 5, 1, 2, 3]
909
+ * @example let array = [> 1, 2, 3, 4, 5]; rotate(-1, arr); arr == [> 2, 3, 4, 5, 1]
910
+ * @since v0.4.5
911
+ */
912
+ export let rotate = (n, arr) => {
913
+ let rec gcd = (a, b) => {
914
+ if (b == 0) {
915
+ a
916
+ }
917
+ else{
918
+ gcd(b, a % b)
919
+ }
920
+ }
921
+
922
+ let arrLen = length(arr)
923
+ if (arrLen > 0) {
924
+ let k = n % arrLen
925
+ let mut d = -1
926
+ let mut j = 0
927
+ for (let mut i = 0; i < gcd(arrLen, k); i += 1) {
928
+ j = i
929
+ let temp = arr[i]
930
+ while (true) {
931
+ d = (j - k) % arrLen
932
+ if (d == i) {
933
+ break
934
+ }
935
+ let newVal = arr[d]
936
+ arr[j] = newVal
937
+ j = d
938
+ }
939
+ arr[j] = temp
940
+ }
941
+ }
942
+ }
package/array.md CHANGED
@@ -371,7 +371,7 @@ Returns:
371
371
 
372
372
  |type|description|
373
373
  |----|-----------|
374
- |`Array<a>`|The new array with mapped values|
374
+ |`Array<b>`|The new array with mapped values|
375
375
 
376
376
  ### Array.**mapi**
377
377
 
@@ -398,7 +398,7 @@ Returns:
398
398
 
399
399
  |type|description|
400
400
  |----|-----------|
401
- |`Array<a>`|The new array with mapped values|
401
+ |`Array<b>`|The new array with mapped values|
402
402
 
403
403
  ### Array.**reduce**
404
404
 
@@ -425,7 +425,7 @@ Parameters:
425
425
  |-----|----|-----------|
426
426
  |`fn`|`(a, b) -> a`|The reducer function to call on each element, where the value returned will be the next accumulator value|
427
427
  |`initial`|`a`|The initial value to use for the accumulator on the first iteration|
428
- |`array`|`Array<a>`|The array to iterate|
428
+ |`array`|`Array<b>`|The array to iterate|
429
429
 
430
430
  Returns:
431
431
 
@@ -465,7 +465,7 @@ Parameters:
465
465
  |-----|----|-----------|
466
466
  |`fn`|`(a, b, Number) -> a`|The reducer function to call on each element, where the value returned will be the next accumulator value|
467
467
  |`initial`|`a`|The initial value to use for the accumulator on the first iteration|
468
- |`array`|`Array<a>`|The array to iterate|
468
+ |`array`|`Array<b>`|The array to iterate|
469
469
 
470
470
  Returns:
471
471
 
@@ -500,7 +500,7 @@ Returns:
500
500
 
501
501
  |type|description|
502
502
  |----|-----------|
503
- |`Array<a>`|The new array|
503
+ |`Array<b>`|The new array|
504
504
 
505
505
  ### Array.**every**
506
506
 
@@ -1027,3 +1027,58 @@ Returns:
1027
1027
  |----|-----------|
1028
1028
  |`Array<a>`|The subset of the array that was sliced|
1029
1029
 
1030
+ ### Array.**sort**
1031
+
1032
+ <details disabled>
1033
+ <summary tabindex="-1">Added in <code>next</code></summary>
1034
+ No other changes yet.
1035
+ </details>
1036
+
1037
+ ```grain
1038
+ sort : (((a, a) -> Number), Array<a>) -> Void
1039
+ ```
1040
+
1041
+ Sorts an array in-place.
1042
+
1043
+ Ordering is calculated using a comparator function which takes two array elements and must return 0 if both are equal, a positive number if the first is greater, and a negative number if the first is smaller.
1044
+
1045
+ Parameters:
1046
+
1047
+ |param|type|description|
1048
+ |-----|----|-----------|
1049
+ |`comp`|`(a, a) -> Number`|The comparator function used to indicate sort order|
1050
+ |`array`|`Array<a>`|The array to be sorted|
1051
+
1052
+ ### Array.**rotate**
1053
+
1054
+ <details disabled>
1055
+ <summary tabindex="-1">Added in <code>next</code></summary>
1056
+ No other changes yet.
1057
+ </details>
1058
+
1059
+ ```grain
1060
+ rotate : (Number, Array<a>) -> Void
1061
+ ```
1062
+
1063
+ Rotates an array by n elements to the right, in place.
1064
+
1065
+ If n is negative, the array will be rotated by n elements
1066
+ to the left. See examples.
1067
+
1068
+ Parameters:
1069
+
1070
+ |param|type|description|
1071
+ |-----|----|-----------|
1072
+ |`n`|`Number`|The number of elements to rotate by|
1073
+ |`arr`|`Array<a>`|The array to be rotated|
1074
+
1075
+ Examples:
1076
+
1077
+ ```grain
1078
+ let array = [> 1, 2, 3, 4, 5]; rotate(2, arr); arr == [> 4, 5, 1, 2, 3]
1079
+ ```
1080
+
1081
+ ```grain
1082
+ let array = [> 1, 2, 3, 4, 5]; rotate(-1, arr); arr == [> 2, 3, 4, 5, 1]
1083
+ ```
1084
+
package/buffer.gr CHANGED
@@ -16,24 +16,20 @@ import Bytes from "bytes"
16
16
  import String from "string"
17
17
  import { coerceNumberToWasmI32 } from "runtime/numbers"
18
18
 
19
- record Buffer {
20
- mut len: Number,
21
- initialSize: Number,
22
- mut data: Bytes,
23
- }
19
+ record Buffer { mut len: Number, initialSize: Number, mut data: Bytes }
24
20
 
25
21
  @disableGC
26
- let mut _SIZE_OFFSET = 1n;
22
+ let mut _SIZE_OFFSET = 1n
27
23
 
28
24
  @disableGC
29
- let mut _VALUE_OFFSET = 1n;
25
+ let mut _VALUE_OFFSET = 1n
30
26
 
31
27
  @disableGC
32
28
  let initOffsets = () => {
33
- _SIZE_OFFSET = 4n;
34
- _VALUE_OFFSET = 8n;
29
+ _SIZE_OFFSET = 4n
30
+ _VALUE_OFFSET = 8n
35
31
  }
36
- initOffsets();
32
+ initOffsets()
37
33
 
38
34
  let _8BIT_LEN = 1
39
35
 
@@ -45,27 +41,28 @@ let _64BIT_LEN = 8
45
41
 
46
42
  /* Gets the size of a Bytes via its ptr */
47
43
  @disableGC
48
- let getSize = (ptr) => WasmI32.load(ptr, _SIZE_OFFSET)
44
+ let getSize = ptr => WasmI32.load(ptr, _SIZE_OFFSET)
49
45
 
50
46
  /* Doubles the size of buffer's underlying byte sequence, if the given size is larger than the size of a buffer's underlying byte sequence */
51
47
  let autogrow = (len, buf) => {
52
48
  while (buf.len + len > Bytes.length(buf.data)) {
53
49
  let mut n = Bytes.length(buf.data)
54
- if (n == 0) n = 4 // Make sure bytes of 0 length grow too
50
+ if (n == 0) n = 4
51
+ // Make sure bytes of 0 length grow too
55
52
  buf.data = Bytes.resize(0, n, buf.data)
56
53
  }
57
54
  }
58
55
 
59
56
  /* Gets the pointer for a char's bytes following the char's tag */
60
57
  @disableGC
61
- let rec getCharAsWasmI32 = (char) => {
58
+ let rec getCharAsWasmI32 = char => {
62
59
  let c = WasmI32.fromGrain(char)
63
60
  WasmI32.load(c, 4n)
64
61
  }
65
62
 
66
63
  /* Gets the UTF-8 byte length of a char */
67
64
  @disableGC
68
- let rec getCharByteLength = (byte) => {
65
+ let rec getCharByteLength = byte => {
69
66
  let (+) = WasmI32.add
70
67
  let (&) = WasmI32.and
71
68
  let (==) = WasmI32.eq
@@ -132,13 +129,11 @@ let addInt32help = (value, buffer) => {
132
129
  *
133
130
  * @since v0.4.0
134
131
  */
135
- export let make = (initialSize) => {
136
- if (initialSize < 0) throw Exception.InvalidArgument("Buffers size must be >= 0")
137
- {
138
- len: 0,
139
- initialSize,
140
- data: Bytes.make(initialSize),
141
- }
132
+ export let make = initialSize => {
133
+ if (initialSize < 0) throw Exception.InvalidArgument(
134
+ "Buffers size must be >= 0",
135
+ )
136
+ { len: 0, initialSize, data: Bytes.make(initialSize) }
142
137
  }
143
138
 
144
139
  /**
@@ -149,7 +144,7 @@ export let make = (initialSize) => {
149
144
  *
150
145
  * @since v0.4.0
151
146
  */
152
- export let length = (buffer) => buffer.len
147
+ export let length = buffer => buffer.len
153
148
 
154
149
  /**
155
150
  * Clears data in the buffer and sets its length to zero.
@@ -160,7 +155,7 @@ export let length = (buffer) => buffer.len
160
155
  *
161
156
  * @since v0.4.0
162
157
  */
163
- export let clear = (buffer) => {
158
+ export let clear = buffer => {
164
159
  Bytes.fill(0x0l, buffer.data)
165
160
  buffer.len = 0
166
161
  }
@@ -174,7 +169,7 @@ export let clear = (buffer) => {
174
169
  *
175
170
  * @since v0.4.0
176
171
  */
177
- export let reset = (buffer) => {
172
+ export let reset = buffer => {
178
173
  buffer.data = Bytes.make(buffer.initialSize)
179
174
  buffer.len = 0
180
175
  }
@@ -192,7 +187,6 @@ export let reset = (buffer) => {
192
187
  @disableGC
193
188
  export let rec truncate = (length, buffer) => {
194
189
  Memory.incRef(WasmI32.fromGrain((<)))
195
- Memory.incRef(WasmI32.fromGrain((||)))
196
190
  Memory.incRef(WasmI32.fromGrain((>)))
197
191
  if (length < 0 || length > buffer.len) throw Exception.IndexOutOfBounds
198
192
 
@@ -217,7 +211,7 @@ export let rec truncate = (length, buffer) => {
217
211
  *
218
212
  * @since v0.4.0
219
213
  */
220
- export let toBytes = (buffer) => {
214
+ export let toBytes = buffer => {
221
215
  let len = Bytes.length(buffer.data)
222
216
  if (buffer.len == len) {
223
217
  buffer.data
@@ -249,7 +243,7 @@ export let toBytesSlice = (start, length, buffer) => {
249
243
  *
250
244
  * @since v0.4.0
251
245
  */
252
- export let toString = (buffer) => {
246
+ export let toString = buffer => {
253
247
  Bytes.toString(toBytes(buffer))
254
248
  }
255
249
 
@@ -286,6 +280,8 @@ export let rec addChar = (char: Char, buffer: Buffer) => {
286
280
  Memory.incRef(WasmI32.fromGrain(c))
287
281
  Memory.incRef(WasmI32.fromGrain(buffer))
288
282
  addInt8help(c, buffer)
283
+ Memory.decRef(WasmI32.fromGrain(c))
284
+ void
289
285
  },
290
286
  2n => {
291
287
  let c = Conv.toInt32(n)
@@ -293,6 +289,8 @@ export let rec addChar = (char: Char, buffer: Buffer) => {
293
289
  Memory.incRef(WasmI32.fromGrain(c))
294
290
  Memory.incRef(WasmI32.fromGrain(buffer))
295
291
  addInt16help(c, buffer)
292
+ Memory.decRef(WasmI32.fromGrain(c))
293
+ void
296
294
  },
297
295
  3n => {
298
296
  let (<) = WasmI32.ltU
@@ -300,12 +298,14 @@ export let rec addChar = (char: Char, buffer: Buffer) => {
300
298
  let (*) = WasmI32.mul
301
299
  let (&) = WasmI32.and
302
300
  let (>>) = WasmI32.shrU
303
- for (let mut i = 0n; i < 3n; i = i + 1n) {
304
- let c = Conv.toInt32(n >> (i * 8n) & 0xffn)
301
+ for (let mut i = 0n; i < 3n; i += 1n) {
302
+ let c = Conv.toInt32(n >> i * 8n & 0xffn)
305
303
  Memory.incRef(WasmI32.fromGrain(addInt8help))
306
304
  Memory.incRef(WasmI32.fromGrain(c))
307
305
  Memory.incRef(WasmI32.fromGrain(buffer))
308
306
  addInt8help(c, buffer)
307
+ Memory.decRef(WasmI32.fromGrain(c))
308
+ void
309
309
  }
310
310
  },
311
311
  _ => {
@@ -314,6 +314,8 @@ export let rec addChar = (char: Char, buffer: Buffer) => {
314
314
  Memory.incRef(WasmI32.fromGrain(c))
315
315
  Memory.incRef(WasmI32.fromGrain(buffer))
316
316
  addInt32help(c, buffer)
317
+ Memory.decRef(WasmI32.fromGrain(c))
318
+ void
317
319
  },
318
320
  }
319
321
 
@@ -406,28 +408,58 @@ export let rec addString = (string, buffer) => {
406
408
  * @since v0.4.0
407
409
  */
408
410
  @disableGC
409
- export let rec addStringSlice = (start, length, string, buffer) => {
410
- Memory.incRef(WasmI32.fromGrain(String.byteLength))
411
- Memory.incRef(WasmI32.fromGrain(start))
412
- Memory.incRef(WasmI32.fromGrain(length))
411
+ export let rec addStringSlice = (start: Number, length, string, buffer) => {
412
+ // Handle negative start index (needed before #1071 is fixed)
413
+ let start = {
414
+ // this is a block to avoid making all of these operators
415
+ // overriden in the whole function
416
+ let (<<) = WasmI32.shl
417
+ let (>>) = WasmI32.shrS
418
+ let (+) = WasmI32.add
419
+ let (!=) = WasmI32.ne
420
+ let (&) = WasmI32.and
421
+ Memory.incRef(WasmI32.fromGrain(String.length))
422
+ Memory.incRef(WasmI32.fromGrain(string))
423
+ let strlen = WasmI32.fromGrain(String.length(string)) >> 1n
424
+ let mut startW = WasmI32.fromGrain(start)
425
+ if ((startW & 1n) != 1n) {
426
+ throw InvalidArgument("Invalid start index")
427
+ }
428
+ startW = startW >> 1n
429
+ if (WasmI32.ltS(startW, 0n)) {
430
+ WasmI32.toGrain(WasmI32.shl(startW + strlen, 1n) + 1n)
431
+ } else {
432
+ start
433
+ }
434
+ }
435
+ let end = start + length
436
+ Memory.incRef(WasmI32.fromGrain(String.slice))
437
+ // no incref for start since we know it's a simple num. Add back for good measure after #1071 is fixed!
438
+ Memory.incRef(WasmI32.fromGrain(end))
413
439
  Memory.incRef(WasmI32.fromGrain(string))
414
- let bytelen = String.byteLength(String.slice(start, length, string))
440
+ let slice = String.slice(start, end, string)
441
+
442
+ Memory.incRef(WasmI32.fromGrain(String.byteLength))
443
+ Memory.incRef(WasmI32.fromGrain(slice))
444
+ let bytelen = String.byteLength(slice)
415
445
 
416
446
  Memory.incRef(WasmI32.fromGrain(autogrow))
417
447
  Memory.incRef(WasmI32.fromGrain(bytelen))
418
448
  Memory.incRef(WasmI32.fromGrain(buffer))
419
449
  autogrow(bytelen, buffer)
420
450
 
421
- let srcOff = coerceNumberToWasmI32(start)
451
+ let srcOff = 0n
422
452
  let dstOff = coerceNumberToWasmI32(buffer.len)
423
- let src = WasmI32.fromGrain(string)
453
+ let src = WasmI32.fromGrain(slice)
424
454
  let dst = WasmI32.fromGrain(buffer.data)
425
455
  appendBytes(srcOff, dstOff, coerceNumberToWasmI32(bytelen), src, dst)
456
+ Memory.decRef(WasmI32.fromGrain(slice))
426
457
 
427
458
  Memory.incRef(WasmI32.fromGrain((+)))
428
459
  Memory.incRef(WasmI32.fromGrain(buffer.len))
429
460
  Memory.incRef(WasmI32.fromGrain(bytelen))
430
461
  buffer.len = buffer.len + bytelen
462
+ Memory.decRef(WasmI32.fromGrain(bytelen))
431
463
 
432
464
  Memory.decRef(WasmI32.fromGrain(start))
433
465
  Memory.decRef(WasmI32.fromGrain(length))
@@ -448,17 +480,40 @@ export let rec addStringSlice = (start, length, string, buffer) => {
448
480
  * @since v0.4.0
449
481
  */
450
482
  @disableGC
451
- export let rec addBytesSlice = (start: Number, length: Number, bytes: Bytes, buffer: Buffer) => {
483
+ export let rec addBytesSlice =
484
+ (
485
+ start: Number,
486
+ length: Number,
487
+ bytes: Bytes,
488
+ buffer: Buffer,
489
+ ) => {
490
+ let (-) = WasmI32.sub
491
+ let (<) = WasmI32.ltS
492
+ let (>) = WasmI32.gtS
493
+ let (>=) = WasmI32.geS
494
+
495
+ // bounds check start
496
+ let bytelen = WasmI32.load(WasmI32.fromGrain(bytes), 4n)
497
+ let srcOff = coerceNumberToWasmI32(start)
498
+ if (srcOff < 0n || srcOff >= bytelen) {
499
+ throw Exception.IndexOutOfBounds
500
+ }
501
+
502
+ // bounds check length
503
+ let len = coerceNumberToWasmI32(length)
504
+ if (len < 0n || len > bytelen - srcOff) {
505
+ throw Exception.IndexOutOfBounds
506
+ }
507
+
452
508
  Memory.incRef(WasmI32.fromGrain(autogrow))
453
509
  Memory.incRef(WasmI32.fromGrain(length))
454
510
  Memory.incRef(WasmI32.fromGrain(buffer))
455
511
  autogrow(length, buffer)
456
512
 
457
- let srcOff = coerceNumberToWasmI32(start)
458
513
  let dstOff = coerceNumberToWasmI32(buffer.len)
459
514
  let src = WasmI32.fromGrain(bytes)
460
515
  let dst = WasmI32.fromGrain(buffer.data)
461
- appendBytes(srcOff, dstOff, coerceNumberToWasmI32(length), src, dst)
516
+ appendBytes(srcOff, dstOff, len, src, dst)
462
517
 
463
518
  Memory.incRef(WasmI32.fromGrain((+)))
464
519
  Memory.incRef(WasmI32.fromGrain(buffer.len))
@@ -507,7 +562,6 @@ export let addBufferSlice = (start, length, srcBuffer, dstBuffer) => {
507
562
  * @section Binary operations on integers: Functions for encoding and decoding integers stored in a buffer.
508
563
  */
509
564
 
510
-
511
565
  /**
512
566
  * Gets a signed 8-bit integer starting at the given byte index.
513
567
  *
@@ -783,4 +837,4 @@ export let addFloat64 = (value, buffer) => {
783
837
  let index = buffer.len
784
838
  buffer.len = buffer.len + _64BIT_LEN
785
839
  setFloat64(index, value, buffer)
786
- }
840
+ }