From a169a55925d3efc187a8affd45e2a508c9f8bc3c Mon Sep 17 00:00:00 2001 From: Yorick Peterse Date: Sun, 16 Oct 2022 04:28:27 +0200 Subject: [PATCH] Add ByteArray.resize and apply in ByteArray.filled This adds ByteArray.resize, used to efficiently resize a ByteArray and fill it with new values (or truncate it). This method is then used in ByteArray.filled to reduce the overhead of creating a ByteArray filled with certain values. Changelog: added --- bytecode/src/lib.rs | 3 +++ compiler/src/type_check/methods.rs | 1 + libstd/src/std/byte_array.inko | 33 ++++++++++++++++++++------ libstd/test/std/test_byte_array.inko | 19 ++++++++++++++- vm/src/builtin_functions.rs | 1 + vm/src/builtin_functions/byte_array.rs | 14 +++++++++++ 6 files changed, 63 insertions(+), 8 deletions(-) diff --git a/bytecode/src/lib.rs b/bytecode/src/lib.rs index d916b4171..8f4809b89 100644 --- a/bytecode/src/lib.rs +++ b/bytecode/src/lib.rs @@ -909,6 +909,7 @@ pub enum BuiltinFunction { ByteArraySlice, ByteArrayAppend, ByteArrayCopyFrom, + ByteArrayResize, } impl BuiltinFunction { @@ -1064,6 +1065,7 @@ impl BuiltinFunction { BuiltinFunction::ByteArraySlice => 147, BuiltinFunction::ByteArrayAppend => 148, BuiltinFunction::ByteArrayCopyFrom => 149, + BuiltinFunction::ByteArrayResize => 150, } } @@ -1261,6 +1263,7 @@ impl BuiltinFunction { BuiltinFunction::ByteArraySlice => "byte_array_slice", BuiltinFunction::ByteArrayAppend => "byte_array_append", BuiltinFunction::ByteArrayCopyFrom => "byte_array_copy_from", + BuiltinFunction::ByteArrayResize => "byte_array_resize", } } } diff --git a/compiler/src/type_check/methods.rs b/compiler/src/type_check/methods.rs index c414674be..acb886543 100644 --- a/compiler/src/type_check/methods.rs +++ b/compiler/src/type_check/methods.rs @@ -1446,6 +1446,7 @@ pub(crate) fn define_builtin_functions(state: &mut State) -> bool { (BIF::ByteArraySlice, byte_array, never), (BIF::ByteArrayAppend, nil, never), (BIF::ByteArrayCopyFrom, int, never), + (BIF::ByteArrayResize, nil, never), ]; // Regular VM instructions exposed directly to the standard library. These diff --git a/libstd/src/std/byte_array.inko b/libstd/src/std/byte_array.inko index c53cf88e6..128ba7ad2 100644 --- a/libstd/src/std/byte_array.inko +++ b/libstd/src/std/byte_array.inko @@ -56,14 +56,8 @@ class builtin ByteArray { # bytes[1] # => 0 fn pub static filled(with: Int, times: Int) -> ByteArray { let bytes = new - let mut index = 0 - - while index < times { - bytes.push(with) - - index += 1 - } + bytes.resize(times, with) bytes } @@ -295,6 +289,31 @@ class builtin ByteArray { fn pub mut copy_from(bytes: ref ByteArray, at: Int, length: Int) -> Int { _INKO.byte_array_copy_from(self, bytes, at, length) } + + # Resizes `self` to the new length. + # + # If the given length is greater than the current length, the `value` argument + # is used to fill in the additional slots. If the given length is less than + # the current length, `self` is simply truncated. + # + # # Panics + # + # This method panics if the given length is less than zero. + # + # # Examples + # + # let bytes = ByteArray.new + # + # bytes.resize(length: 2, value: 1) + # bytes # => ByteArray.from_array([1, 1]) + # + # bytes.resize(length: 0, value: 0) + # bytes # => ByteArray.new + fn pub mut resize(length: Int, value: Int) { + if length < 0 { panic('The new length must be greater than zero') } + + _INKO.byte_array_resize(self, length, value) + } } impl Drop for ByteArray { diff --git a/libstd/test/std/test_byte_array.inko b/libstd/test/std/test_byte_array.inko index 43594b7d1..b49d7f1f0 100644 --- a/libstd/test/std/test_byte_array.inko +++ b/libstd/test/std/test_byte_array.inko @@ -1,4 +1,4 @@ -import helpers::(fmt, hash) +import helpers::(fmt, hash, Script) import std::iter::EOF import std::test::Tests @@ -246,4 +246,21 @@ fn pub tests(t: mut Tests) { t.equal(b.copy_from(b, at: 0, length: 2), 2) t.equal(b, ByteArray.from_array([1, 2, 1, 2])) } + + t.test('ByteArray.resize') fn (t) { + let bytes = ByteArray.new + + bytes.resize(length: 2, value: 1) + t.equal(bytes, ByteArray.from_array([1, 1])) + + bytes.resize(length: 0, value: 0) + t.equal(bytes, ByteArray.new) + + t.true( + Script + .new(id: t.id, code: 'ByteArray.new.resize(length: -5, value: 0)') + .run + .contains?('greater than zero') + ) + } } diff --git a/vm/src/builtin_functions.rs b/vm/src/builtin_functions.rs index 2655ae321..a7d03e3c9 100644 --- a/vm/src/builtin_functions.rs +++ b/vm/src/builtin_functions.rs @@ -205,6 +205,7 @@ impl BuiltinFunctions { byte_array::byte_array_slice, byte_array::byte_array_append, byte_array::byte_array_copy_from, + byte_array::byte_array_resize, ], } } diff --git a/vm/src/builtin_functions/byte_array.rs b/vm/src/builtin_functions/byte_array.rs index e5313b57e..a3fd6138c 100644 --- a/vm/src/builtin_functions/byte_array.rs +++ b/vm/src/builtin_functions/byte_array.rs @@ -85,3 +85,17 @@ pub(crate) fn byte_array_copy_from( target.value_mut().extend_from_slice(slice); Ok(Int::alloc(state.permanent_space.int_class(), amount)) } + +pub(crate) fn byte_array_resize( + _: &State, + _: &mut Thread, + _: ProcessPointer, + args: &[Pointer], +) -> Result { + let bytes = unsafe { args[0].get_mut::() }; + let size = unsafe { Int::read(args[1]) as usize }; + let filler = unsafe { Int::read(args[2]) as u8 }; + + bytes.value_mut().resize(size, filler); + Ok(Pointer::nil_singleton()) +}