use std::hash::Hash;
use std::mem;
use std::mem::ManuallyDrop;
use std::ptr;
use dupe::Dupe;
use crate::any::AnyLifetime;
use crate::cast;
use crate::values::layout::avalue::AValue;
use crate::values::layout::heap::heap_type::HeapKind;
use crate::values::layout::value_alloc_size::ValueAllocSize;
use crate::values::layout::vtable::AValueDyn;
use crate::values::layout::vtable::AValueVTable;
use crate::values::layout::vtable::StarlarkValueRawPtr;
use crate::values::FrozenValue;
use crate::values::StarlarkValue;
use crate::values::Value;
#[derive(Clone)]
#[repr(C)]
pub(crate) struct AValueHeader(pub(crate) &'static AValueVTable);
impl Hash for AValueHeader {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
ptr::hash(self.0, state);
}
}
impl PartialEq for AValueHeader {
fn eq(&self, other: &Self) -> bool {
ptr::eq(self.0, other.0)
}
}
impl Eq for AValueHeader {}
impl Dupe for AValueHeader {}
#[repr(C, align(8))]
pub(crate) struct AValueRepr<T> {
pub(crate) header: AValueHeader,
pub(crate) payload: T,
}
#[derive(Copy, Clone, Dupe)]
pub(crate) struct ForwardPtr(usize);
impl ForwardPtr {
fn new(ptr: usize) -> ForwardPtr {
debug_assert!(ptr & 1 == 0);
ForwardPtr(ptr)
}
pub(crate) fn new_frozen(value: FrozenValue) -> ForwardPtr {
ForwardPtr::new(value.0.raw().ptr_value())
}
pub(crate) fn new_unfrozen(value: Value) -> ForwardPtr {
debug_assert!(value.unpack_frozen().is_none());
ForwardPtr::new(value.0.raw().ptr_value() & !1)
}
pub(crate) unsafe fn unpack_frozen_value(self) -> FrozenValue {
FrozenValue::new_ptr_usize_with_str_tag(self.0)
}
pub(crate) unsafe fn unpack_unfrozen_value<'v>(self) -> Value<'v> {
Value::new_ptr_usize_with_str_tag(self.0)
}
pub(crate) unsafe fn unpack_value<'v>(self, heap_kind: HeapKind) -> Value<'v> {
match heap_kind {
HeapKind::Unfrozen => self.unpack_unfrozen_value(),
HeapKind::Frozen => self.unpack_frozen_value().to_value(),
}
}
}
#[repr(C)]
#[derive(Debug)]
pub(crate) struct AValueForward {
forward_ptr: usize,
object_size: ValueAllocSize,
}
impl AValueForward {
pub(crate) fn new(forward_ptr: ForwardPtr, object_size: ValueAllocSize) -> AValueForward {
AValueForward {
forward_ptr: forward_ptr.0 | 1,
object_size,
}
}
pub(crate) fn forward_ptr(&self) -> ForwardPtr {
debug_assert!((self.forward_ptr & 1) != 0);
ForwardPtr(self.forward_ptr & !1)
}
}
#[repr(C)]
pub(crate) union AValueOrForward {
header: ManuallyDrop<AValueHeader>,
forward: ManuallyDrop<AValueForward>,
flags: usize,
}
impl AValueOrForward {
#[inline]
fn is_forward(&self) -> bool {
unsafe { (self.flags & 1) != 0 }
}
pub(crate) fn unpack(&self) -> AValueOrForwardUnpack {
if self.is_forward() {
AValueOrForwardUnpack::Forward(unsafe { &self.forward })
} else {
AValueOrForwardUnpack::Header(unsafe { &self.header })
}
}
#[inline]
pub(crate) unsafe fn unpack_header_unchecked(&self) -> &AValueHeader {
debug_assert!(!self.is_forward());
&self.header
}
pub(crate) fn unpack_header(&self) -> Option<&AValueHeader> {
match self.unpack() {
AValueOrForwardUnpack::Header(header) => Some(header),
AValueOrForwardUnpack::Forward(_) => None,
}
}
pub(crate) fn unpack_forward(&self) -> Option<&AValueForward> {
match self.unpack() {
AValueOrForwardUnpack::Header(_) => None,
AValueOrForwardUnpack::Forward(forward) => Some(forward),
}
}
pub(crate) fn alloc_size(&self) -> ValueAllocSize {
match self.unpack() {
AValueOrForwardUnpack::Header(ptr) => ptr.unpack().memory_size(),
AValueOrForwardUnpack::Forward(forward) => {
forward.object_size
}
}
}
}
pub(crate) enum AValueOrForwardUnpack<'a> {
Header(&'a AValueHeader),
Forward(&'a AValueForward),
}
impl AValueForward {
pub(crate) fn assert_does_not_overwrite_extra<'v, T: AValue<'v>>() {
assert!(mem::size_of::<AValueForward>() <= AValueRepr::<T>::offset_of_extra());
}
}
impl AValueHeader {
pub(crate) const ALIGN: usize = 8;
pub(crate) fn new<'v, T: AValue<'v>>() -> AValueHeader {
let header = AValueHeader::new_const::<T>();
let vtable_ptr = header.0 as *const AValueVTable as usize;
debug_assert!(vtable_ptr & 1 == 0);
header
}
pub(crate) const fn new_const<'v, T: AValue<'v>>() -> AValueHeader {
AValueHeader(AValueVTable::new::<T>())
}
#[inline]
pub(crate) fn payload_ptr(&self) -> StarlarkValueRawPtr {
StarlarkValueRawPtr::new_header(self)
}
pub(crate) unsafe fn payload<'v, T: StarlarkValue<'v>>(&self) -> &T {
debug_assert_eq!(self.0.static_type_of_value.get(), T::static_type_id());
&*self.payload_ptr().value_ptr::<T>()
}
pub(crate) unsafe fn unpack_value<'v>(&'v self, heap_kind: HeapKind) -> Value<'v> {
match heap_kind {
HeapKind::Unfrozen => Value::new_ptr_query_is_str(self),
HeapKind::Frozen => {
FrozenValue::new_ptr_query_is_str(cast::ptr_lifetime(self)).to_value()
}
}
}
pub(crate) fn unpack<'v>(&'v self) -> AValueDyn<'v> {
unsafe {
debug_assert!(
!(*(self as *const AValueHeader as *const AValueOrForward)).is_forward(),
"value is a forward pointer; value cannot be unpacked during GC or freeze"
);
}
unsafe { AValueDyn::new(self.payload_ptr(), self.0) }
}
pub unsafe fn overwrite_with_forward<'v, T: StarlarkValue<'v>>(
me: *mut AValueRepr<T>,
forward_ptr: ForwardPtr,
) -> T {
let sz = (*me).header.unpack().memory_size();
let p = me as *const AValueRepr<T>;
let res = ptr::read(p).payload;
let p = me as *mut AValueForward;
*p = AValueForward::new(forward_ptr, sz);
res
}
#[inline]
pub(crate) unsafe fn as_repr<'v, T: StarlarkValue<'v>>(&self) -> &AValueRepr<T> {
debug_assert_eq!(T::static_type_id(), self.0.static_type_of_value.get());
&*(self as *const AValueHeader as *const AValueRepr<T>)
}
fn as_avalue_or_header(&self) -> &AValueOrForward {
unsafe { &*(self as *const AValueHeader as *const AValueOrForward) }
}
pub(crate) fn alloc_size(&self) -> ValueAllocSize {
self.as_avalue_or_header().alloc_size()
}
}
impl<T> AValueRepr<T> {
const _ASSERTIONS: () = {
assert!(mem::align_of::<Self>() == AValueHeader::ALIGN);
};
pub(crate) const fn with_metadata(
metadata: &'static AValueVTable,
payload: T,
) -> AValueRepr<T> {
AValueRepr {
header: AValueHeader(metadata),
payload,
}
}
pub(crate) fn offset_of_payload() -> usize {
memoffset::offset_of!(Self, payload)
}
pub(crate) fn padding_after_header() -> usize {
Self::offset_of_payload() - mem::size_of::<AValueHeader>()
}
pub(crate) fn offset_of_extra<'v>() -> usize
where
T: AValue<'v>,
{
Self::offset_of_payload() + T::offset_of_extra()
}
pub(crate) fn from_payload_ptr_mut(payload_ptr: *mut T) -> *mut AValueRepr<T> {
let payload_ptr = payload_ptr as usize;
let header_ptr = payload_ptr - Self::offset_of_payload();
header_ptr as *mut AValueRepr<T>
}
}