#![allow(clippy::result_unit_err)]
use std::iter::FromIterator;
use std::{fmt, ops, ptr};
use godot_ffi as sys;
use sys::{ExtVariantType, GodotFfi, SysPtr, ffi_methods};
use crate::builtin::collections::extend_buffer::ExtendBufferTrait;
use crate::builtin::*;
use crate::classes::file_access::CompressionMode;
use crate::meta;
use crate::meta::shape::GodotShape;
use crate::meta::signed_range::SignedRange;
use crate::meta::{AsArg, FromGodot, GodotConvert, PackedElement, ToGodot};
use crate::obj::EngineEnum;
use crate::registry::property::{Export, SimpleVar};
pub type PackedByteArray = PackedArray<u8>;
pub type PackedInt32Array = PackedArray<i32>;
pub type PackedInt64Array = PackedArray<i64>;
pub type PackedFloat32Array = PackedArray<f32>;
pub type PackedFloat64Array = PackedArray<f64>;
pub type PackedStringArray = PackedArray<GString>;
pub type PackedVector2Array = PackedArray<Vector2>;
pub type PackedVector3Array = PackedArray<Vector3>;
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
pub type PackedVector4Array = PackedArray<Vector4>;
pub type PackedColorArray = PackedArray<Color>;
pub struct PackedArray<T: PackedElement> {
opaque: sys::types::OpaquePackedByteArray,
_phantom: std::marker::PhantomData<T>,
}
impl<T: PackedElement> PackedArray<T> {
fn from_opaque(opaque: sys::types::OpaquePackedByteArray) -> Self {
Self {
opaque,
_phantom: std::marker::PhantomData,
}
}
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, index: usize) -> Option<T> {
let ptr = self.ptr_or_none(index)?;
unsafe { Some((*ptr).clone()) }
}
#[doc(alias = "has")]
pub fn contains(&self, value: impl AsArg<T>) -> bool {
T::op_has(self.as_inner(), value.into_arg())
}
pub fn count(&self, value: impl AsArg<T>) -> usize {
let count_i64 = T::op_count(self.as_inner(), value.into_arg());
to_usize(count_i64)
}
#[doc(alias = "size")]
pub fn len(&self) -> usize {
to_usize(T::op_size(self.as_inner()))
}
pub fn is_empty(&self) -> bool {
T::op_is_empty(self.as_inner())
}
pub fn clear(&mut self) {
T::op_clear(self.as_inner());
}
#[doc(alias = "append")]
#[doc(alias = "push_back")]
pub fn push(&mut self, value: impl AsArg<T>) {
T::op_push_back(self.as_inner(), value.into_arg());
}
pub fn insert(&mut self, index: usize, value: impl AsArg<T>) {
if index > self.len() {
self.panic_out_of_bounds(index);
}
T::op_insert(self.as_inner(), to_i64(index), value.into_arg());
}
#[doc(alias = "remove_at")]
pub fn remove(&mut self, index: usize) -> T {
let element = self.get(index).expect("index out of bounds"); T::op_remove_at(self.as_inner(), to_i64(index));
element
}
pub fn fill(&mut self, value: impl AsArg<T>) {
T::op_fill(self.as_inner(), value.into_arg());
}
pub fn resize(&mut self, size: usize) {
T::op_resize(self.as_inner(), to_i64(size));
}
pub fn extend_array(&mut self, other: &PackedArray<T>) {
T::op_append_array(self.as_inner(), other);
}
pub fn to_vec(&self) -> Vec<T> {
self.as_slice().to_vec()
}
#[doc(alias = "slice")]
pub fn subarray(&self, range: impl SignedRange) -> Self {
T::op_slice(self.as_inner(), range)
}
pub fn as_slice(&self) -> &[T] {
if self.is_empty() {
&[]
} else {
let data = self.ptr(0);
unsafe { std::slice::from_raw_parts(data, self.len()) }
}
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
if self.is_empty() {
&mut []
} else {
let data = self.ptr_mut(0);
unsafe { std::slice::from_raw_parts_mut(data, self.len()) }
}
}
pub fn find(&self, value: impl AsArg<T>, from: Option<usize>) -> Option<usize> {
let from = to_i64(from.unwrap_or(0));
let index = T::op_find(self.as_inner(), value.into_arg(), from);
if index >= 0 {
Some(index.try_into().unwrap())
} else {
None
}
}
pub fn rfind(&self, value: impl AsArg<T>, from: Option<usize>) -> Option<usize> {
let from = from.map(to_i64).unwrap_or(-1);
let index = T::op_rfind(self.as_inner(), value.into_arg(), from);
if index >= 0 {
Some(to_usize(index))
} else {
None
}
}
pub fn bsearch(&self, value: impl AsArg<T>) -> usize {
let inner = self.as_inner();
to_usize(T::op_bsearch(inner, value.into_arg(), true))
}
pub fn reverse(&mut self) {
T::op_reverse(self.as_inner());
}
pub fn sort(&mut self) {
T::op_sort(self.as_inner());
}
pub(crate) fn from_typed_array(array: &Array<T>) -> Self
where
T: meta::Element,
{
unsafe {
Self::new_with_uninit(|self_ptr| {
T::ffi_from_array(array.sys(), self_ptr);
})
}
}
pub fn to_typed_array(&self) -> Array<T>
where
T: meta::Element, {
self.as_slice().iter().cloned().collect()
}
#[inline]
pub fn to_var_array(&self) -> VarArray {
unsafe {
VarArray::new_with_uninit(|ptr| {
T::ffi_to_array(self.sys(), ptr);
})
}
}
fn panic_out_of_bounds(&self, index: usize) -> ! {
panic!(
"Array index {index} is out of bounds: length is {}",
self.len()
);
}
fn ptr(&self, index: usize) -> *const T {
self.ptr_or_none(index)
.unwrap_or_else(|| self.panic_out_of_bounds(index))
}
fn ptr_or_none(&self, index: usize) -> Option<*const T> {
let item_ptr: *const T::Indexed = unsafe { T::ffi_index_const(self.sys(), to_i64(index)) };
if item_ptr.is_null() {
None
} else {
Some(item_ptr as *const T)
}
}
fn ptr_mut(&mut self, index: usize) -> *mut T {
let item_ptr: *mut T::Indexed = unsafe { T::ffi_index_mut(self.sys_mut(), to_i64(index)) };
if item_ptr.is_null() {
self.panic_out_of_bounds(index)
} else {
item_ptr as *mut T
}
}
fn as_inner(&self) -> T::Inner<'_> {
T::inner(self)
}
fn default_with_size(n: usize) -> Self {
let mut array = Self::new();
array.resize(n);
array
}
unsafe fn move_from_slice(&mut self, src: *const T, dst: usize, len: usize) {
let ptr = self.ptr_mut(dst);
sys::strict_assert_eq!(len, self.len() - dst, "length precondition violated");
unsafe {
ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, len));
}
unsafe {
ptr.copy_from_nonoverlapping(src, len);
}
}
}
impl<T: PackedElement> Default for PackedArray<T> {
fn default() -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
T::ffi_default(SysPtr::force_init(self_ptr));
})
}
}
}
impl<T: PackedElement> Clone for PackedArray<T> {
fn clone(&self) -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
T::ffi_copy(self.sys(), SysPtr::force_init(self_ptr));
})
}
}
}
impl<T: PackedElement> Drop for PackedArray<T> {
fn drop(&mut self) {
unsafe { T::ffi_destroy(self.sys_mut()) };
}
}
impl<T: PackedElement> PartialEq for PackedArray<T> {
fn eq(&self, other: &Self) -> bool {
unsafe { T::ffi_equals(self.sys(), other.sys()) }
}
}
impl<T: PackedElement> Eq for PackedArray<T> {}
unsafe impl<T: PackedElement> GodotFfi for PackedArray<T> {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(T::VARIANT_TYPE);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
}
impl<T: PackedElement> GodotConvert for PackedArray<T> {
type Via = Self;
fn godot_shape() -> GodotShape {
GodotShape::of_builtin::<Self>()
}
}
impl<T: PackedElement> ToGodot for PackedArray<T> {
type Pass = meta::ByRef;
fn to_godot(&self) -> &Self::Via {
self
}
}
impl<T: PackedElement> FromGodot for PackedArray<T> {
fn try_from_godot(via: Self::Via) -> Result<Self, meta::error::ConvertError> {
Ok(via)
}
}
impl<T: PackedElement> meta::Element for PackedArray<T> {}
impl<T: PackedElement> meta::GodotType for PackedArray<T> {
type Ffi = Self;
type ToFfi<'f>
= meta::RefArg<'f, PackedArray<T>>
where
Self: 'f;
fn to_ffi(&self) -> Self::ToFfi<'_> {
meta::RefArg::new(self)
}
fn into_ffi(self) -> Self::Ffi {
self
}
fn try_from_ffi(ffi: Self::Ffi) -> Result<Self, meta::error::ConvertError> {
Ok(ffi)
}
}
impl<T: PackedElement> meta::GodotFfiVariant for PackedArray<T> {
fn ffi_to_variant(&self) -> Variant {
unsafe {
Variant::new_with_var_uninit(|variant_ptr| {
T::ffi_to_variant(self.sys(), SysPtr::force_init(variant_ptr));
})
}
}
fn ffi_from_variant(variant: &Variant) -> Result<Self, meta::error::ConvertError> {
let array = unsafe {
Self::new_with_uninit(|ptr| {
T::ffi_from_variant(variant.var_sys(), SysPtr::force_init(ptr));
})
};
Ok(array)
}
}
impl<T: PackedElement> ops::Index<usize> for PackedArray<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
let ptr = self.ptr(index);
unsafe { &*ptr }
}
}
impl<T: PackedElement> ops::IndexMut<usize> for PackedArray<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
let ptr = self.ptr_mut(index);
unsafe { &mut *ptr }
}
}
impl<T: PackedElement> SimpleVar for PackedArray<T> {}
impl<T: PackedElement> Export for PackedArray<T> {}
impl<T: PackedElement> From<&[T]> for PackedArray<T> {
fn from(slice: &[T]) -> Self {
if slice.is_empty() {
return Self::new();
}
let mut array = Self::default_with_size(slice.len());
let dst = unsafe { std::slice::from_raw_parts_mut(array.ptr_mut(0), slice.len()) };
dst.clone_from_slice(slice);
array
}
}
impl<T: PackedElement, const N: usize> From<&[T; N]> for PackedArray<T> {
fn from(arr: &[T; N]) -> Self {
Self::from(&arr[..])
}
}
impl<T: PackedElement, const N: usize> From<[T; N]> for PackedArray<T> {
fn from(arr: [T; N]) -> Self {
if N == 0 {
return Self::new();
}
let mut packed_array = Self::default_with_size(N);
let arr = std::mem::ManuallyDrop::new(arr);
unsafe {
packed_array.move_from_slice(arr.as_ptr(), 0, N);
}
packed_array
}
}
impl<T: PackedElement> From<Vec<T>> for PackedArray<T> {
fn from(mut vec: Vec<T>) -> Self {
if vec.is_empty() {
return Self::new();
}
let len = vec.len();
let mut array = Self::default_with_size(len);
unsafe {
vec.set_len(0);
array.move_from_slice(vec.as_ptr(), 0, len);
}
array
}
}
impl<T: PackedElement> FromIterator<T> for PackedArray<T> {
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut array = PackedArray::<T>::default();
array.extend(iter);
array
}
}
impl<T: PackedElement> Extend<T> for PackedArray<T> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
let mut iter = iter.into_iter();
let mut len = self.len();
let (size_hint_min, _size_hint_max) = iter.size_hint();
if size_hint_min > 0 {
let capacity = len + size_hint_min;
self.resize(capacity);
for out_ref in &mut self.as_mut_slice()[len..] {
*out_ref = iter
.next()
.expect("iterator returned fewer than size_hint().0 elements");
}
len = capacity;
}
let mut buf = T::ExtendBuffer::default();
while let Some(item) = iter.next() {
buf.push(item);
while !buf.is_full() {
if let Some(item) = iter.next() {
buf.push(item);
} else {
break;
}
}
let buf_slice = buf.drain_as_mut_slice();
let capacity = len + buf_slice.len();
self.resize(capacity);
unsafe {
self.move_from_slice(buf_slice.as_ptr(), len, buf_slice.len());
}
len = capacity;
}
}
}
impl<T: PackedElement> fmt::Debug for PackedArray<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.to_variant().stringify())
}
}
impl<T: PackedElement + fmt::Display> fmt::Display for PackedArray<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, elem) in self.as_slice().iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{elem}")?;
}
write!(f, "]")
}
}
macro_rules! declare_encode_decode {
($Ty:ty, $bytes:literal, $encode_fn:ident, $decode_fn:ident, $Via:ty) => {
#[doc = concat!("Encodes `", stringify!($Ty), "` as ", stringify!($bytes), " byte(s) at position `byte_offset`.")]
#[doc = concat!("[`", stringify!($Ty), "::to_be_bytes()`].")]
pub fn $encode_fn(&mut self, byte_offset: usize, value: $Ty) -> Result<(), ()> {
if byte_offset + $bytes > self.len() {
return Err(());
}
self.as_inner()
.$encode_fn(byte_offset as i64, value as $Via);
Ok(())
}
#[doc = concat!("Decodes `", stringify!($Ty), "` from ", stringify!($bytes), " byte(s) at position `byte_offset`.")]
#[doc = concat!("[`", stringify!($Ty), "::from_be_bytes()`].")]
pub fn $decode_fn(&self, byte_offset: usize) -> Result<$Ty, ()> {
if byte_offset + $bytes > self.len() {
return Err(());
}
let decoded: $Via = self.as_inner().$decode_fn(byte_offset as i64);
Ok(decoded as $Ty)
}
};
}
impl PackedByteArray {
pub fn to_float32_array(&self) -> PackedFloat32Array {
self.as_inner().to_float32_array()
}
pub fn to_float64_array(&self) -> PackedFloat64Array {
self.as_inner().to_float64_array()
}
pub fn to_int32_array(&self) -> PackedInt32Array {
self.as_inner().to_int32_array()
}
pub fn to_int64_array(&self) -> PackedInt64Array {
self.as_inner().to_int64_array()
}
}
macro_rules! impl_to_byte_array {
($ArrayType:ident) => {
impl $ArrayType {
pub fn to_byte_array(&self) -> PackedByteArray {
self.as_inner().to_byte_array()
}
}
};
}
impl_to_byte_array!(PackedInt32Array);
impl_to_byte_array!(PackedInt64Array);
impl_to_byte_array!(PackedFloat32Array);
impl_to_byte_array!(PackedFloat64Array);
impl_to_byte_array!(PackedStringArray);
impl_to_byte_array!(PackedVector2Array);
impl_to_byte_array!(PackedVector3Array);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
impl_to_byte_array!(PackedVector4Array);
impl_to_byte_array!(PackedColorArray);
impl PackedByteArray {
declare_encode_decode!(u8, 1, encode_u8, decode_u8, i64);
declare_encode_decode!(i8, 1, encode_s8, decode_s8, i64);
declare_encode_decode!(u16, 2, encode_u16, decode_u16, i64);
declare_encode_decode!(i16, 2, encode_s16, decode_s16, i64);
declare_encode_decode!(u32, 4, encode_u32, decode_u32, i64);
declare_encode_decode!(i32, 4, encode_s32, decode_s32, i64);
declare_encode_decode!(u64, 8, encode_u64, decode_u64, i64);
declare_encode_decode!(i64, 8, encode_s64, decode_s64, i64);
declare_encode_decode!(f32, 2, encode_half, decode_half, f64);
declare_encode_decode!(f32, 4, encode_float, decode_float, f64);
declare_encode_decode!(f64, 8, encode_double, decode_double, f64);
pub fn encode_var(
&mut self,
byte_offset: usize,
value: impl AsArg<Variant>,
allow_objects: bool,
) -> Result<usize, ()> {
meta::arg_into_ref!(value);
let bytes_written: i64 =
self.as_inner()
.encode_var(byte_offset as i64, value, allow_objects);
if bytes_written == -1 {
Err(())
} else {
Ok(bytes_written as usize)
}
}
#[doc(alias = "has_encoded_var", alias = "decode_var_size")]
#[inline]
pub fn decode_var(
&self,
byte_offset: usize,
allow_objects: bool,
) -> Result<(Variant, usize), ()> {
let variant = self
.as_inner()
.decode_var(byte_offset as i64, allow_objects);
if variant.is_nil() {
return Err(());
}
let size: i64 = self
.as_inner()
.decode_var_size(byte_offset as i64, allow_objects);
sys::strict_assert_ne!(size, -1);
Ok((variant, size as usize))
}
#[inline]
pub fn decode_var_allow_nil(
&self,
byte_offset: usize,
allow_objects: bool,
) -> (Variant, usize) {
let byte_offset = byte_offset as i64;
let variant = self.as_inner().decode_var(byte_offset, allow_objects);
let decoded_size = self.as_inner().decode_var_size(byte_offset, allow_objects);
let decoded_size = decoded_size.try_into().unwrap_or_else(|_| {
panic!("unexpected value {decoded_size} returned from decode_var_size()")
});
(variant, decoded_size)
}
pub fn compress(&self, compression_mode: CompressionMode) -> Result<PackedByteArray, ()> {
let compressed: PackedByteArray = self.as_inner().compress(compression_mode.ord() as i64);
populated_or_err(compressed)
}
pub fn decompress(
&self,
buffer_size: usize,
compression_mode: CompressionMode,
) -> Result<PackedByteArray, ()> {
let decompressed: PackedByteArray = self
.as_inner()
.decompress(buffer_size as i64, compression_mode.ord() as i64);
populated_or_err(decompressed)
}
pub fn decompress_dynamic(
&self,
max_output_size: Option<usize>,
compression_mode: CompressionMode,
) -> Result<PackedByteArray, ()> {
let max_output_size = max_output_size.map(|i| i as i64).unwrap_or(-1);
let decompressed: PackedByteArray = self
.as_inner()
.decompress_dynamic(max_output_size, compression_mode.ord() as i64);
populated_or_err(decompressed)
}
}
fn populated_or_err(array: PackedByteArray) -> Result<PackedByteArray, ()> {
if array.is_empty() { Err(()) } else { Ok(array) }
}