use std::marker::PhantomData;
use std::mem;
use std::mem::MaybeUninit;
use std::ptr;
use allocative::Key;
use allocative::Visitor;
use pagable::PagableDeserialize;
use pagable::PagableSerialize;
use crate::cast;
use crate::collections::maybe_uninit_backport::maybe_uninit_write_slice;
use crate::collections::maybe_uninit_backport::maybe_uninit_write_slice_cloned;
use crate::pagable::StarlarkPagable;
use crate::values::FreezeResult;
use crate::values::Freezer;
use crate::values::FrozenHeap;
use crate::values::FrozenValue;
use crate::values::Heap;
use crate::values::Trace;
use crate::values::Tracer;
use crate::values::Value;
use crate::values::ValueTyped;
use crate::values::array::VALUE_EMPTY_ARRAY;
use crate::values::layout::avalue::AValue;
use crate::values::layout::avalue::AValueImpl;
use crate::values::layout::heap::repr::AValueForward;
use crate::values::layout::heap::repr::AValueHeader;
use crate::values::layout::heap::repr::AValueRepr;
use crate::values::layout::heap::repr::ForwardPtr;
use crate::values::types::any_array::AnyArray;
use crate::values::types::any_array::AnyArrayRegistered;
use crate::values::types::any_array::FrozenAnyArray;
use crate::values::types::array::Array;
fn array_avalue<'v>(
cap: u32,
) -> AValueImpl<'v, impl AValue<'v, StarlarkValue = Array<'v>, ExtraElem = Value<'v>>> {
AValueImpl::<AValueArray>::new(unsafe { Array::new(0, cap) })
}
fn any_array_avalue<T: AnyArrayRegistered + StarlarkPagable>(
cap: usize,
) -> AValueImpl<'static, impl AValue<'static, StarlarkValue = AnyArray<T>, ExtraElem = T>> {
AValueImpl::<AValueAnyArray<T>>::new(unsafe { AnyArray::new(cap) })
}
struct AValueArray;
impl<'v> AValue<'v> for AValueArray {
type StarlarkValue = Array<'v>;
type ExtraElem = Value<'v>;
fn extra_len(value: &Array<'v>) -> usize {
value.capacity()
}
fn offset_of_extra() -> usize {
Array::offset_of_content()
}
fn visit_extra_allocative<'a, 'b: 'a>(
value: &Self::StarlarkValue,
visitor: &'a mut Visitor<'b>,
) {
visitor.visit_simple(Key::new("content"), mem::size_of::<Value>() * value.len());
visitor.visit_simple(
Key::new("unused_capacity"),
mem::size_of::<Value>() * (value.capacity() - value.len()),
);
}
unsafe fn heap_freeze(
_me: *mut AValueRepr<Self::StarlarkValue>,
_freezer: &Freezer,
) -> FreezeResult<FrozenValue> {
panic!("arrays should not be frozen")
}
unsafe fn heap_copy(
me: *mut AValueRepr<Self::StarlarkValue>,
tracer: &Tracer<'v>,
) -> Value<'v> {
unsafe {
debug_assert!(
(*me).payload.capacity() != 0,
"empty array is allocated statically"
);
if (*me).payload.len() == 0 {
return VALUE_EMPTY_ARRAY.unpack().to_value();
}
AValueForward::assert_does_not_overwrite_extra::<Self>();
let content = (*me).payload.content_mut();
let (v, r, extra) = tracer.reserve_with_extra::<Self>(content.len());
let x = AValueHeader::overwrite_with_forward::<Self::StarlarkValue>(
me,
ForwardPtr::new_unfrozen(v),
);
debug_assert_eq!(content.len(), x.len());
content.trace(tracer);
r.fill(Array::new(content.len() as u32, content.len() as u32));
let extra = &mut *extra;
maybe_uninit_write_slice(extra, content);
v
}
}
}
pub struct AValueAnyArray<T>(PhantomData<T>);
impl<'v, T: AnyArrayRegistered + StarlarkPagable> AValue<'v> for AValueAnyArray<T> {
type StarlarkValue = AnyArray<T>;
type ExtraElem = T;
fn extra_len(value: &AnyArray<T>) -> usize {
value.len
}
fn offset_of_extra() -> usize {
AnyArray::<T>::offset_of_content()
}
fn visit_extra_allocative<'a, 'b: 'a>(
value: &Self::StarlarkValue,
visitor: &'a mut Visitor<'b>,
) {
visitor.visit_simple(Key::new("content"), mem::size_of::<T>() * value.len);
}
unsafe fn heap_freeze(
_me: *mut AValueRepr<Self::StarlarkValue>,
_freezer: &Freezer,
) -> FreezeResult<FrozenValue> {
panic!("AnyArray for now can only be allocated in FrozenHeap");
}
unsafe fn heap_copy(
_me: *mut AValueRepr<Self::StarlarkValue>,
_tracer: &Tracer<'v>,
) -> Value<'v> {
panic!("AnyArray for now can only be allocated in FrozenHeap");
}
fn starlark_serialize(
me: *const AValueRepr<Self::StarlarkValue>,
ctx: &mut dyn crate::pagable::StarlarkSerializeContext,
) -> crate::Result<()> {
let any_array = unsafe { &(*me).payload };
any_array.len.pagable_serialize(ctx.pagable())?;
for elem in any_array.as_slice() {
elem.starlark_serialize(ctx)?;
}
Ok(())
}
fn starlark_deserialize(
me: *mut AValueRepr<Self::StarlarkValue>,
ctx: &mut dyn crate::pagable::StarlarkDeserializeContext<'_>,
) -> crate::Result<()> {
let len = usize::pagable_deserialize(ctx.pagable())?;
unsafe {
ptr::write(&mut (*me).payload, AnyArray::new(len));
let extra_offset = AValueRepr::<Self::StarlarkValue>::offset_of_payload()
+ <Self as AValue>::offset_of_extra();
let extra_ptr = (me as *mut u8).add(extra_offset) as *mut MaybeUninit<T>;
for i in 0..len {
let elem = T::starlark_deserialize(ctx)?;
(*extra_ptr.add(i)).write(elem);
}
}
Ok(())
}
}
impl FrozenHeap {
pub(crate) fn alloc_any_array_value<
T: AnyArrayRegistered + StarlarkPagable + Send + Sync + Clone,
>(
&self,
values: &[T],
) -> FrozenAnyArray<T> {
let this: &'static FrozenHeap = unsafe { cast::ptr_lifetime(self) };
let (any_array, content) = this.alloc_raw_extra(any_array_avalue(values.len()));
let content = unsafe { &mut *content };
maybe_uninit_write_slice_cloned(content, values);
any_array
}
}
impl<'v> Heap<'v> {
pub(crate) fn alloc_array(self, cap: usize) -> ValueTyped<'v, Array<'v>> {
if cap == 0 {
return VALUE_EMPTY_ARRAY.unpack().to_value_typed();
}
let cap: u32 = cap.try_into().expect("capacity overflows u32::MAX");
self.alloc_raw_extra(array_avalue(cap)).0
}
}