From b9c97a4dc10e59334bb9697069f3c22be2e55864 Mon Sep 17 00:00:00 2001 From: Dheatly23 <71598333+Dheatly23@users.noreply.github.com> Date: Thu, 1 Aug 2024 15:29:22 +0700 Subject: [PATCH] Add From for Packed Array and Optimize From --- .../src/builtin/collections/packed_array.rs | 66 ++++++++++++++++--- .../containers/packed_array_test.rs | 18 +++++ 2 files changed, 75 insertions(+), 9 deletions(-) diff --git a/godot-core/src/builtin/collections/packed_array.rs b/godot-core/src/builtin/collections/packed_array.rs index 7f30a0829..e2689b7d8 100644 --- a/godot-core/src/builtin/collections/packed_array.rs +++ b/godot-core/src/builtin/collections/packed_array.rs @@ -9,7 +9,7 @@ use godot_ffi as sys; use crate::builtin::*; use crate::meta::ToGodot; -use std::{fmt, ops}; +use std::{fmt, ops, ptr}; use sys::types::*; use sys::{ffi_methods, interface_fn, GodotFfi}; @@ -378,6 +378,28 @@ macro_rules! impl_packed_array { pub fn as_inner(&self) -> inner::$Inner<'_> { inner::$Inner::from_outer(self) } + + /// Create array filled with default elements. + fn default_with_size(n: usize) -> Self { + let mut ret = Self::new(); + ret.resize(n); + ret + } + + /// Moves elements from slice. + /// + /// # Safety + /// + /// * Pointer must be valid and not point to `self` data. + /// * Length must be equal. + /// * Source data must not be dropped later. + unsafe fn move_from_slice(&mut self, src: *const $Element, len: usize) { + let ptr = self.ptr_mut(0); + // Drop all values in place first. + ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, len)); + // All values are dropped, so we can safely overwrite them. + ptr.copy_from_nonoverlapping(src, len); + } } impl_builtin_traits! { @@ -414,12 +436,10 @@ macro_rules! impl_packed_array { #[doc = concat!("Creates a `", stringify!($PackedArray), "` from the given slice.")] impl From<&[$Element]> for $PackedArray { fn from(slice: &[$Element]) -> Self { - let mut array = Self::new(); - let len = slice.len(); - if len == 0 { - return array; - } - array.resize(len); + let mut array = match slice.len() { + 0 => return Self::new(), + len => Self::default_with_size(len), + }; let ptr = array.ptr_mut(0); for (i, element) in slice.iter().enumerate() { // SAFETY: The array contains exactly `len` elements, stored contiguously in memory. @@ -433,10 +453,38 @@ macro_rules! impl_packed_array { } } + #[doc = concat!("Creates a `", stringify!($PackedArray), "` from the given Rust array.")] + impl From<[$Element; N]> for $PackedArray { + fn from(arr: [$Element; N]) -> Self { + let mut ret = match N { + 0 => return Self::new(), + len => Self::default_with_size(len), + }; + + // SAFETY: The packed array contains exactly N elements and the source array will be forgotten. + unsafe { + let arr = std::mem::ManuallyDrop::new(arr); + ret.move_from_slice(arr.as_ptr(), N); + } + ret + } + } + #[doc = concat!("Creates a `", stringify!($PackedArray), "` from the given Rust vec.")] impl From> for $PackedArray { - fn from(vec: Vec<$Element>) -> Self{ - vec.into_iter().collect() + fn from(mut vec: Vec<$Element>) -> Self { + let (len, mut ret) = match vec.len() { + 0 => return Self::new(), + len => (len, Self::default_with_size(len)), + }; + + // SAFETY: The packed array andvector contains exactly `len` elements. + // The vector is forcibly set to empty, so it's contents are forgotten. + unsafe { + vec.set_len(0); + ret.move_from_slice(vec.as_ptr(), len); + } + ret } } diff --git a/itest/rust/src/builtin_tests/containers/packed_array_test.rs b/itest/rust/src/builtin_tests/containers/packed_array_test.rs index ea084f839..a35642a6d 100644 --- a/itest/rust/src/builtin_tests/containers/packed_array_test.rs +++ b/itest/rust/src/builtin_tests/containers/packed_array_test.rs @@ -45,6 +45,24 @@ fn packed_array_from_vec_i32() { assert_eq!(int32_array[1], 2); } +#[itest] +fn packed_array_from_array_str() { + let string_array = PackedStringArray::from(["hello".into(), "world".into()]); + + assert_eq!(string_array.len(), 2); + assert_eq!(string_array[0], "hello".into()); + assert_eq!(string_array[1], "world".into()); +} + +#[itest] +fn packed_array_from_array_i32() { + let int32_array = PackedInt32Array::from([1, 2]); + + assert_eq!(int32_array.len(), 2); + assert_eq!(int32_array[0], 1); + assert_eq!(int32_array[1], 2); +} + #[itest] fn packed_array_to_vec() { let array = PackedByteArray::new();