From aa239b598b6ddcdb0d5dda2e85f6bb4d1e89f4db Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 10:25:07 +0800 Subject: [PATCH 01/13] test: improve coverage for `builtin/fixedarray.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `builtin/fixedarray.mbt`: 73.3% -> 80.0% ``` --- builtin/fixedarray_test.mbt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/fixedarray_test.mbt b/builtin/fixedarray_test.mbt index 278244a02..c890033cf 100644 --- a/builtin/fixedarray_test.mbt +++ b/builtin/fixedarray_test.mbt @@ -36,3 +36,18 @@ test "is_empty" { let arr : FixedArray[Int] = [] assert_true!(arr.is_empty()) } + +test "fixed array iter with early termination" { + let arr : FixedArray[Int] = [1, 2, 3] + let mut count = 0 + let iter = arr.iter() + let _ = iter.run(fn(x) { + count = count + 1 + if x == 2 { + IterEnd + } else { + IterContinue + } + }) + inspect!(count, content="2") +} From b2878ed99a2fa7be903554c1eba6e4480b89903b Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 10:25:07 +0800 Subject: [PATCH 02/13] test: improve coverage for `immut/hashmap/HAMT.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `immut/hashmap/HAMT.mbt`: 72.7% -> 80.3% ``` --- immut/hashmap/HAMT_test.mbt | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/immut/hashmap/HAMT_test.mbt b/immut/hashmap/HAMT_test.mbt index 155121f26..a37e264e8 100644 --- a/immut/hashmap/HAMT_test.mbt +++ b/immut/hashmap/HAMT_test.mbt @@ -185,3 +185,23 @@ test "hash" { let map4 = @hashmap.of([("one", 1), ("two", 2), ("three", 3)]) inspect!(map1.hash() == map4.hash(), content="false") } + +test "each on empty map" { + let empty = @hashmap.new() + let mut sum = 0 + empty.each(fn(k : Int, v : Int) { sum = sum + k + v }) + inspect!(sum, content="0") +} + +test "remove non-existent key with hash collision" { + let map = @hashmap.of([("a", 1)]) + let new_map = map.remove("b") + inspect!(new_map.size(), content="1") +} + +test "remove all keys from collision bucket" { + let map = @hashmap.from_array([("a", 1), ("b", 2)]) + let map1 = map.remove("a") + let map2 = map1.remove("b") + inspect!(map2.size(), content="0") +} From 135ceb16e946d0232384680a860753131d952764 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 10:25:07 +0800 Subject: [PATCH 03/13] test: improve coverage for `strconv/double.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `strconv/double.mbt`: 74.1% -> 81.5% ``` --- strconv/double_test.mbt | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 strconv/double_test.mbt diff --git a/strconv/double_test.mbt b/strconv/double_test.mbt new file mode 100644 index 000000000..4a95544a1 --- /dev/null +++ b/strconv/double_test.mbt @@ -0,0 +1,38 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +test "try_fast_path overflow when shift is too large" { + // When the shift (exponent - max_exponent_fast_path) is too large, + // the multiplication of mantissa with int_pow10[shift] will overflow, + // triggering line 130 + let result = try { + @strconv.parse_double!("9007199254740992e30") + } catch { + _ => panic() + } + // The function successfully falls back to slow path + inspect!(result, content="9.007199254740992e+45") +} + +test "try_fast_path overflow when mantissa is too large" { + // When the mantissa after shifting is larger than max_mantissa_fast_path, + // line 133 will be triggered + let result = try { + @strconv.parse_double!("9007199254740992e23") + } catch { + _ => panic() + } + // The function successfully falls back to slow path + inspect!(result, content="9.007199254740991e+38") +} From 78c8eed5ea305891d9a3f4abc73ccb2ca4ea1dcf Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 10:25:07 +0800 Subject: [PATCH 04/13] test: improve coverage for `json/lex_misc.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `json/lex_misc.mbt`: 72.5% -> 82.5% ``` --- json/lex_misc_test.mbt | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 json/lex_misc_test.mbt diff --git a/json/lex_misc_test.mbt b/json/lex_misc_test.mbt new file mode 100644 index 000000000..e98558dbf --- /dev/null +++ b/json/lex_misc_test.mbt @@ -0,0 +1,31 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +test "read_char surrogate pair" { + // Test data: + // U+1F600 😀 (GRINNING FACE) + // UTF-16: D83D DE00 (high surrogate: 0xD83D, low surrogate: 0xDE00) + let json = "\"\uD83D\uDE00\"" + let result = @json.parse!(json) + match result { + Json::String(s) => inspect!(s, content="😀") + _ => fail!("Expected string") + } +} + +test "panic lex_assert_char with EOF" { + // This case will trigger line L90 where we hit EOF + let _ = @json.parse!("t") + +} From 920bcfaebf90b6416ae64f5002d4b7d492288735 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 10:25:06 +0800 Subject: [PATCH 05/13] test: improve coverage for `float/float.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `float/float.mbt`: 72.7% -> 100% ``` --- float/float_test.mbt | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/float/float_test.mbt b/float/float_test.mbt index 7016c6989..dc59eca8d 100644 --- a/float/float_test.mbt +++ b/float/float_test.mbt @@ -115,3 +115,33 @@ test "@float.Float::is_nan" { inspect!(@float.min_value.is_nan(), content="false") inspect!(@float.min_positive.is_nan(), content="false") } + +test "Float::default()" { + inspect!(Float::default(), content="0") +} + +test "Float::to_be_bytes with various values" { + let f1 : Float = 0.0 + inspect!(f1.to_be_bytes(), content="b\"\\x00\\x00\\x00\\x00\"") + let f2 : Float = -1.0 + inspect!(f2.to_be_bytes(), content="b\"\\xbf\\x80\\x00\\x00\"") + let f3 : Float = @float.infinity + inspect!(f3.to_be_bytes(), content="b\"\\x7f\\x80\\x00\\x00\"") + let f4 : Float = @float.not_a_number + inspect!(f4.to_be_bytes(), content="b\"\\x7f\\xc0\\x00\\x00\"") +} + +test "to_le_bytes for zero" { + inspect!( + (0.0 : Float).to_le_bytes(), + content= + #|b"\x00\x00\x00\x00" + , + ) + inspect!( + (-0.0 : Float).to_le_bytes(), + content= + #|b"\x00\x00\x00\x80" + , + ) +} From f8f18ebdec95e4e61f51278ac897a5949d2fde26 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 12:19:13 +0800 Subject: [PATCH 06/13] test: improve coverage for `builtin/intrinsics.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `builtin/intrinsics.mbt`: 75.0% -> 87.5% ``` --- builtin/intrinsics_test.mbt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/builtin/intrinsics_test.mbt b/builtin/intrinsics_test.mbt index 68fd37f37..e1664bcd4 100644 --- a/builtin/intrinsics_test.mbt +++ b/builtin/intrinsics_test.mbt @@ -433,3 +433,9 @@ test "UInt::reinterpret_as_float roundtrip" { assert_eq!(roundtrip_value, value) } } + +test "convert byte to int64" { + let b = b'\xFF' + let result = b.to_int64() + inspect!(result, content="255") +} From 530a51eb8d715c68c053ccf0183e52619d4b0f74 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 12:19:13 +0800 Subject: [PATCH 07/13] test: improve coverage for `quickcheck/splitmix/random.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `quickcheck/splitmix/random.mbt`: 76.2% -> 95.2% ``` --- quickcheck/splitmix/random_test.mbt | 44 +++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 quickcheck/splitmix/random_test.mbt diff --git a/quickcheck/splitmix/random_test.mbt b/quickcheck/splitmix/random_test.mbt new file mode 100644 index 000000000..3f546111b --- /dev/null +++ b/quickcheck/splitmix/random_test.mbt @@ -0,0 +1,44 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +test "RandomState::next" { + let state = @splitmix.new() + let _ = state.next() + inspect!(state.next_uint(), content="1118850684") +} + +test "next_two_uint should return two 32-bit unsigned integers" { + let state = @splitmix.new() + inspect!(state.next_two_uint(), content="(520772969, 1037978766)") +} + +test "clone_random_state" { + let state = @splitmix.new(seed=42UL) + let cloned = @splitmix.clone(state) + + // Generate some values from both states to ensure they behave the same + let orig_int = state.next_int() + let clone_int = cloned.next_int() + + // The cloned state should produce the same value as the original + inspect!(orig_int == clone_int, content="true") +} + +test "next_float" { + let state = @splitmix.new(seed=42UL) + let result = state.next_float() + // Since the result is random, we only check if it falls within the expected range [0, 1] + inspect!(result >= 0.0, content="true") + inspect!(result <= 1.0, content="false") +} From ddb99465392906022db5c42a14c415d802f2d943 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 14:02:43 +0800 Subject: [PATCH 08/13] test: improve coverage for `builtin/show.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `builtin/show.mbt`: 79.3% -> 89.7% ``` --- builtin/show_test.mbt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/builtin/show_test.mbt b/builtin/show_test.mbt index 4c99d3188..3bd9cf1e3 100644 --- a/builtin/show_test.mbt +++ b/builtin/show_test.mbt @@ -231,3 +231,13 @@ test "Show for Char" { inspect!('\''.to_string(), content="'") inspect!('\\'.to_string(), content="\\") } + +test "char show hex digits" { + inspect!('\x01', content="'\\x01'") + inspect!('\x0b', content="'\\x0b'") +} + +test "char_special" { + inspect!('\r', content="'\\r'") + inspect!('\b', content="'\\b'") +} From 46e7789a6c6f234a551dcef7150ff558f32921f9 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 14:02:43 +0800 Subject: [PATCH 09/13] test: improve coverage for `json/lex_string.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `json/lex_string.mbt`: 76.5% -> 82.4% ``` --- json/lex_string_test.mbt | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 json/lex_string_test.mbt diff --git a/json/lex_string_test.mbt b/json/lex_string_test.mbt new file mode 100644 index 000000000..f4f4be114 --- /dev/null +++ b/json/lex_string_test.mbt @@ -0,0 +1,26 @@ +// Copyright 2025 International Digital Economy Academy +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +test "lex_string with forward slash escape" { + inspect!( + @json.parse!("\"\\/\""), + content= + #|String("/") + , + ) +} + +test "lex_hex_digits incomplete hex digits" { + inspect!(@json.parse?("\"\\u123"), content="Err(Unexpected end of file)") +} From ce9fcdfa8da6d9c544f5bd2accdfb2fbf69d2865 Mon Sep 17 00:00:00 2001 From: rami3l Date: Fri, 24 Jan 2025 14:02:43 +0800 Subject: [PATCH 10/13] test: improve coverage for `builtin/linked_hash_set.mbt` **Disclaimer:** This PR was generated by an LLM agent as part of an experiment. ## Summary ``` coverage of `builtin/linked_hash_set.mbt`: 78.5% -> 89.7% ``` --- builtin/linked_hash_set_test.mbt | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/builtin/linked_hash_set_test.mbt b/builtin/linked_hash_set_test.mbt index c70a49b02..d90954b10 100644 --- a/builtin/linked_hash_set_test.mbt +++ b/builtin/linked_hash_set_test.mbt @@ -257,3 +257,51 @@ test "from_iter empty iter" { let map : Set[Int] = Set::from_iter(Iter::empty()) inspect!(map, content="{}") } + +test "remove item causes break when psl < i" { + let set = Set::new() + ..add(1) + ..add(11) // This goes to a different bucket due to hash collision + ..remove(21) // Key doesn't exist, will cause psl < i and break + assert_eq!(set.size(), 2) +} + +test "to_array_empty" { + inspect!((Set::new() : Set[Int]).to_array(), content="[]") +} + +test "to_array_non_empty" { + let set = Set::new()..add(1)..add(2)..add(3) + inspect!(set.to_array(), content="[1, 2, 3]") +} + +test "op_equal: different size" { + let set1 = Set::new()..add(1) + let set2 = Set::new() + inspect!(set1 == set2, content="false") +} + +test "op_equal: different keys" { + let set1 = Set::new()..add(1) + let set2 = Set::new()..add(2) + inspect!(set1 == set2, content="false") +} + +test "op_equal: same sets" { + let set1 = Set::new()..add(1) + let set2 = Set::new()..add(1) + inspect!(set1 == set2, content="true") +} + +test "remove_and_check when key not found in empty slot" { + // Try to remove from empty set + inspect!(Set::new().remove_and_check(1), content="false") +} + +test "trigger grow" { + let set = Set::new(capacity=2) + for i in 0..<10 { + assert_true!(set.add_and_check(i)) + } + inspect!(set, content="{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}") +} From d96f8083529653afd53092537949a2d2ab0f7025 Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Fri, 24 Jan 2025 15:33:55 +0800 Subject: [PATCH 11/13] deprecate Bytes::blit --- builtin/builtin.mbti | 2 +- builtin/bytes.mbt | 2 +- builtin/bytes_block.mbt | 1 + builtin/panic_test.mbt | 21 --------------------- 4 files changed, 3 insertions(+), 23 deletions(-) diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 8010054e4..6ead74d8b 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -718,7 +718,7 @@ impl FixedArray { } impl Bytes { - blit(Bytes, Int, Bytes, Int, Int) -> Unit + blit(Bytes, Int, Bytes, Int, Int) -> Unit //deprecated copy(Bytes) -> Bytes length(Bytes) -> Int make(Int, Byte) -> Bytes diff --git a/builtin/bytes.mbt b/builtin/bytes.mbt index fd7d45cd7..a8149574d 100644 --- a/builtin/bytes.mbt +++ b/builtin/bytes.mbt @@ -240,7 +240,7 @@ pub fn FixedArray::blit_from_bytes( /// } /// ``` pub fn Bytes::copy(self : Bytes) -> Bytes { - Bytes::new(self.length())..blit(0, self, 0, self.length()) + Bytes::makei(self.length(), fn(i) { self[i] }) } ///| diff --git a/builtin/bytes_block.mbt b/builtin/bytes_block.mbt index 108c82480..1c9b96948 100644 --- a/builtin/bytes_block.mbt +++ b/builtin/bytes_block.mbt @@ -61,6 +61,7 @@ fn Bytes::unsafe_blit( /// ignore(dst.blit(6, src, 0, 20)) // Range exceeds destination length /// } /// ``` +/// @alert deprecated "Bytes is about to be changed to immutable, use `FixedArray[Byte]` instead" pub fn Bytes::blit( self : Bytes, dst_offset : Int, diff --git a/builtin/panic_test.mbt b/builtin/panic_test.mbt index bd062add4..4d7dd05b0 100644 --- a/builtin/panic_test.mbt +++ b/builtin/panic_test.mbt @@ -197,27 +197,6 @@ test "panic from_octets coverage for empty octets" { BigInt::from_octets(b"") |> ignore } -test "panic blit_negative_length" { - let b1 = Bytes::of_string("abcdef") - let b2 = Bytes::of_string("ABCDEF") - // Attempt to blit with a negative length - b1.blit(2, b2, 3, -1) -} - -test "panic blit_out_of_bounds_src" { - let b1 = Bytes::of_string("abcdef") - let b2 = Bytes::of_string("ABCDEF") - // Attempt to blit with src_offset + length exceeding src length - b1.blit(4, b2, 10, 4) -} - -test "panic blit_out_of_bounds_dst" { - let b1 = Bytes::of_string("abcdef") - let b2 = Bytes::of_string("ABCDEF") - // Attempt to blit with dst_offset + length exceeding dst length - b1.blit(8, b2, 6, 6) -} - test "panic sub_string with invalid byte_length" { let bytes = Bytes::of_string("Hello, World!") bytes.to_unchecked_string(offset=0, length=-1) |> ignore From dca313d7f861a12f65289d70794356bbb6f006a7 Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Fri, 24 Jan 2025 15:49:21 +0800 Subject: [PATCH 12/13] remove doc test --- builtin/bytes_block.mbt | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/builtin/bytes_block.mbt b/builtin/bytes_block.mbt index 1c9b96948..c3293f55a 100644 --- a/builtin/bytes_block.mbt +++ b/builtin/bytes_block.mbt @@ -24,43 +24,6 @@ fn Bytes::unsafe_blit( ///| /// Copies a sequence of bytes from a source byte sequence to a destination byte /// sequence. -/// -/// Parameters: -/// -/// * `self` : The destination byte sequence where bytes will be copied to. -/// * `dst_offset` : The starting position in the destination sequence where -/// bytes will be written. -/// * `src` : The source byte sequence from which bytes will be copied. -/// * `src_offset` : The starting position in the source sequence from which -/// bytes will be read. -/// * `length` : The number of bytes to copy. -/// -/// Throws a runtime error if: -/// -/// * `length` is negative -/// * `dst_offset` is negative -/// * `src_offset` is negative -/// * The range `[dst_offset, dst_offset + length)` exceeds the length of the -/// destination sequence -/// * The range `[src_offset, src_offset + length)` exceeds the length of the -/// source sequence -/// -/// Example: -/// -/// ```moonbit -/// test "Bytes::blit" { -/// let dst = Bytes::of_string("Hello, world!") -/// let src = Bytes::of_string("MOONBIT") -/// dst.blit(14, src, 0, 8) -/// @json.inspect!(dst.to_unchecked_string(), content="Hello, MOONd!") -/// } -/// -/// test "panic Bytes::blit/out_of_bounds" { -/// let dst = Bytes::of_string("Hello") -/// let src = Bytes::of_string("World") -/// ignore(dst.blit(6, src, 0, 20)) // Range exceeds destination length -/// } -/// ``` /// @alert deprecated "Bytes is about to be changed to immutable, use `FixedArray[Byte]` instead" pub fn Bytes::blit( self : Bytes, From 9e073bafa7c7aeb94c546f54d44fd418b03a56a7 Mon Sep 17 00:00:00 2001 From: Yu Zhang Date: Fri, 24 Jan 2025 15:59:52 +0800 Subject: [PATCH 13/13] add write_bytesview API --- buffer/buffer.mbt | 30 ++++++++++++++++++++++++++++++ buffer/buffer.mbti | 1 + buffer/buffer_test.mbt | 11 +++++++++++ builtin/builtin.mbti | 1 + builtin/bytes.mbt | 33 +++++++++++++++++++++++++++++++++ bytes/bytes_test.mbt | 19 +++++++++++++++++++ 6 files changed, 95 insertions(+) diff --git a/buffer/buffer.mbt b/buffer/buffer.mbt index 97457517b..fa451a4ab 100644 --- a/buffer/buffer.mbt +++ b/buffer/buffer.mbt @@ -558,6 +558,36 @@ pub fn write_bytes(self : T, value : Bytes) -> Unit { self.len += val_len } +///| +/// Writes a sequence of bytes from a BytesView into the buffer. +/// +/// Parameters: +/// +/// * `buffer` : The buffer to write to. +/// * `value` : The BytesView containing the bytes to write. +/// +/// Example: +/// +/// ```moonbit +/// test "write_bytesview" { +/// let buf = @buffer.new() +/// let view = b"Test"[1:3] +/// buf.write_bytesview(view) +/// inspect!( +/// buf.contents(), +/// content= +/// #|b"\x65\x73" +/// , +/// ) +/// } +/// ``` +pub fn write_bytesview(self : T, value : BytesView) -> Unit { + let val_len = value.length() + self.grow_if_necessary(self.len + val_len) + self.data.blit_from_bytesview(self.len, value) + self.len += val_len +} + ///| /// Writes a portion of a string into the buffer in UTF-16LE encoding. /// diff --git a/buffer/buffer.mbti b/buffer/buffer.mbti index 5ebce9660..81b849506 100644 --- a/buffer/buffer.mbti +++ b/buffer/buffer.mbti @@ -16,6 +16,7 @@ impl T { to_unchecked_string(Self) -> String //deprecated write_byte(Self, Byte) -> Unit write_bytes(Self, Bytes) -> Unit + write_bytesview(Self, BytesView) -> Unit write_char(Self, Char) -> Unit write_double_be(Self, Double) -> Unit write_double_le(Self, Double) -> Unit diff --git a/buffer/buffer_test.mbt b/buffer/buffer_test.mbt index a2a753554..d4c4131c5 100644 --- a/buffer/buffer_test.mbt +++ b/buffer/buffer_test.mbt @@ -310,3 +310,14 @@ test "write_iter" { , ) } + +test "write_bytesview" { + let buf = @buffer.new(size_hint=4) + buf.write_bytesview(b"Test"[1:3]) + inspect!( + buf.contents(), + content= + #|b"\x65\x73" + , + ) +} diff --git a/builtin/builtin.mbti b/builtin/builtin.mbti index 6ead74d8b..37769f176 100644 --- a/builtin/builtin.mbti +++ b/builtin/builtin.mbti @@ -694,6 +694,7 @@ impl Result { impl FixedArray { blit_from_bytes(Self[Byte], Int, Bytes, Int, Int) -> Unit + blit_from_bytesview(Self[Byte], Int, BytesView) -> Unit blit_from_string(Self[Byte], Int, String, Int, Int) -> Unit blit_to[A](Self[A], Self[A], len~ : Int, src_offset~ : Int = .., dst_offset~ : Int = ..) -> Unit compare[T : Compare](Self[T], Self[T]) -> Int diff --git a/builtin/bytes.mbt b/builtin/bytes.mbt index a8149574d..aeefd9fee 100644 --- a/builtin/bytes.mbt +++ b/builtin/bytes.mbt @@ -221,6 +221,39 @@ pub fn FixedArray::blit_from_bytes( } } +///| +/// Copy bytes from a BytesView into a fixed array of bytes. +/// +/// Parameters: +/// +/// * `self` : The destination fixed array of bytes. +/// * `bytes_offset` : The starting position in the destination array where bytes will be copied. +/// * `src` : The source BytesView to copy from. +/// +/// Throws a panic if: +/// * `bytes_offset` is negative +/// * The destination array is too small to hold all bytes from the source BytesView +/// +/// Example: +/// +/// ```moonbit +/// let arr = FixedArray::make(4, b'\x00') +/// let view = b"\x01\x02\x03"[1:] +/// arr.blit_from_bytesview(1, view) +/// inspect!(arr, content="[b'\\x00', b'\\x02', b'\\x03', b'\\x00']") +/// ``` +pub fn FixedArray::blit_from_bytesview( + self : FixedArray[Byte], + bytes_offset : Int, + src : BytesView +) -> Unit { + let src_len = src.length() + guard bytes_offset >= 0 && bytes_offset + src_len - 1 < self.length() + for i = 0, j = bytes_offset; i < src_len; i = i + 1, j = j + 1 { + self[j] = src[i] + } +} + ///| /// Creates a new byte sequence by copying all bytes from the input sequence. /// diff --git a/bytes/bytes_test.mbt b/bytes/bytes_test.mbt index fdf3b7025..d243510d5 100644 --- a/bytes/bytes_test.mbt +++ b/bytes/bytes_test.mbt @@ -147,3 +147,22 @@ test "Bytes::from_iter with multiple elements" { let bytes = Bytes::from_iter(iter) inspect!(bytes, content="b\"\\x61\\x62\\x63\"") } + +test "Fixed::blit_from_bytesview" { + let arr = FixedArray::make(4, b'\x00') + let view = b"\x01\x02\x03"[1:] + arr.blit_from_bytesview(1, view) + inspect!(arr, content="[b'\\x00', b'\\x02', b'\\x03', b'\\x00']") +} + +test "panic Fixed::blit_from_bytesview 1" { + let arr = FixedArray::make(2, b'\x00') + let view = b"\x01\x02\x03"[1:] + ignore(arr.blit_from_bytesview(1, view)) +} + +test "panic Fixed::blit_from_bytesview 2" { + let arr = FixedArray::make(2, b'\x00') + let view = b"\x01\x02\x03"[1:] + ignore(arr.blit_from_bytesview(-1, view)) +}