Skip to content

Commit

Permalink
Add From<Array> for Packed Array and Optimize From<Vec>
Browse files Browse the repository at this point in the history
  • Loading branch information
Dheatly23 committed Aug 2, 2024
1 parent 15bc053 commit b9c97a4
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 9 deletions.
66 changes: 57 additions & 9 deletions godot-core/src/builtin/collections/packed_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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! {
Expand Down Expand Up @@ -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.
Expand All @@ -433,10 +453,38 @@ macro_rules! impl_packed_array {
}
}

#[doc = concat!("Creates a `", stringify!($PackedArray), "` from the given Rust array.")]
impl<const N: usize> 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<Vec<$Element>> 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
}
}

Expand Down
18 changes: 18 additions & 0 deletions itest/rust/src/builtin_tests/containers/packed_array_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down

0 comments on commit b9c97a4

Please sign in to comment.