use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use dashmap::DashMap;
use dupe::Dupe;
use pagable::PagableCursor;
use pagable::PagableDeserialize;
use pagable::PagableDeserializer;
use starlark_syntax::internal_error;
use crate::pagable::error::PagableError;
use crate::pagable::heap_ref_id::HeapRefId;
use crate::pagable::serialized_frozen_value::SerializedFrozenValue;
use crate::pagable::starlark_deserialize::StarlarkDeserializeContext;
use crate::pagable::static_value::get_frozen_value_by_static_id;
use crate::values::FrozenValue;
use crate::values::layout::heap::arena::ArenaOffset;
use crate::values::layout::heap::arena::BumpKind;
use crate::values::layout::heap::repr::AValueHeader;
use crate::values::types::int::inline_int::InlineInt;
struct HeapBumpBases {
drop_base: usize,
non_drop_base: usize,
}
impl HeapBumpBases {
fn resolve(&self, offset: &ArenaOffset) -> usize {
let base = match offset.bump {
BumpKind::Drop => self.drop_base,
BumpKind::NonDrop => self.non_drop_base,
};
base + offset.offset as usize
}
}
use crate::values::layout::pointer::PointerTags;
use crate::values::layout::vtable::AValueVTable;
use crate::values::layout::vtable::StarlarkValueRawPtr;
pub(crate) struct ValueDeserSlot {
stream_offset: u32,
arc_offset: u32,
vtable: &'static AValueVTable,
raw_ptr: StarlarkValueRawPtr,
header_ptr: *mut AValueHeader,
initialized: bool,
}
unsafe impl Send for ValueDeserSlot {}
impl ValueDeserSlot {
pub(crate) fn new(
stream_offset: u32,
arc_offset: u32,
vtable: &'static AValueVTable,
raw_ptr: StarlarkValueRawPtr,
header_ptr: *mut AValueHeader,
) -> Self {
Self {
stream_offset,
arc_offset,
header_ptr,
vtable,
raw_ptr,
initialized: false,
}
}
}
pub(crate) struct DeserializeRecipe {
pub(crate) abs_pos: PagableCursor,
pub(crate) vtable: &'static AValueVTable,
pub(crate) raw_ptr: StarlarkValueRawPtr,
pub(crate) header_ptr: *mut AValueHeader,
}
impl DeserializeRecipe {
pub(crate) unsafe fn write_vtable_to_header(&self) {
unsafe {
std::ptr::write(self.header_ptr, AValueHeader(self.vtable));
}
}
}
pub(crate) struct HeapDeserializationState {
slots: Vec<ValueDeserSlot>,
ptr_to_index: HashMap<usize, usize>,
base_pos: PagableCursor,
end_pos: PagableCursor,
}
impl HeapDeserializationState {
pub(crate) fn new(
slots: Vec<ValueDeserSlot>,
ptr_to_index: HashMap<usize, usize>,
base_pos: PagableCursor,
end_pos: PagableCursor,
) -> Self {
Self {
slots,
ptr_to_index,
base_pos,
end_pos,
}
}
pub(crate) fn empty() -> Self {
Self {
slots: Vec::new(),
ptr_to_index: HashMap::new(),
base_pos: PagableCursor::default(),
end_pos: PagableCursor::default(),
}
}
pub(crate) fn value_count(&self) -> usize {
self.slots.len()
}
pub(crate) fn find_by_frozen_value(&self, fv: FrozenValue) -> Option<usize> {
match fv.ptr_value().tags() {
PointerTags::OtherFrozen | PointerTags::StrFrozen => {
let ptr_addr = fv.ptr_value().ptr_value_untagged();
self.ptr_to_index.get(&ptr_addr).copied()
}
_ => None,
}
}
pub(crate) fn try_claim(&mut self, index: usize) -> Option<DeserializeRecipe> {
let slot = &mut self.slots[index];
if slot.initialized {
return None;
}
slot.initialized = true;
Some(DeserializeRecipe {
abs_pos: PagableCursor {
byte_pos: self.base_pos.byte_pos + slot.stream_offset as usize,
arc_index: self.base_pos.arc_index + slot.arc_offset as usize,
},
vtable: slot.vtable,
raw_ptr: slot.raw_ptr,
header_ptr: slot.header_ptr,
})
}
pub(crate) fn end_position(&self) -> PagableCursor {
self.end_pos
}
}
pub(crate) struct StarlarkDeserState {
heap_bases: DashMap<HeapRefId, HeapBumpBases>,
}
impl StarlarkDeserState {
pub(crate) fn new() -> Self {
Self {
heap_bases: DashMap::new(),
}
}
pub(crate) fn register_bases(
&self,
heap_id: HeapRefId,
drop_base: usize,
non_drop_base: usize,
) {
self.heap_bases.insert(
heap_id,
HeapBumpBases {
drop_base,
non_drop_base,
},
);
}
}
pub struct StarlarkDeserializerImpl<'a, 'de> {
pagable: &'a mut dyn PagableDeserializer<'de>,
state: Arc<StarlarkDeserState>,
current_heap_deser_state: Arc<Mutex<HeapDeserializationState>>,
}
#[derive(Clone, Dupe)]
pub(crate) struct CurrentHeapDeserState {
pub(crate) deser_state: Arc<Mutex<HeapDeserializationState>>,
}
impl<'a, 'de> StarlarkDeserializerImpl<'a, 'de> {
pub fn recover_from_pagable(
deserializer: &'a mut dyn PagableDeserializer<'de>,
) -> crate::Result<Self> {
let current =
Self::current_heap_deser_state_from_context(deserializer).ok_or_else(|| {
internal_error!(
"recover_from_pagable called outside of starlark heap deserialization: \
no current heap deser state in session context"
)
})?;
let state = Self::get_or_create_state(deserializer);
Ok(Self::new(deserializer, state, current.deser_state))
}
pub(crate) fn new(
pagable: &'a mut dyn PagableDeserializer<'de>,
state: Arc<StarlarkDeserState>,
current_heap_deser_state: Arc<Mutex<HeapDeserializationState>>,
) -> Self {
pagable.session_context().set(CurrentHeapDeserState {
deser_state: current_heap_deser_state.dupe(),
});
Self {
pagable,
state,
current_heap_deser_state,
}
}
pub(crate) fn get_or_create_state(
deserializer: &mut dyn PagableDeserializer<'_>,
) -> Arc<StarlarkDeserState> {
deserializer
.session_context()
.get_or_insert_with(|| Arc::new(StarlarkDeserState::new()))
}
pub(crate) fn current_heap_deser_state_from_context(
deserializer: &mut dyn PagableDeserializer<'_>,
) -> Option<CurrentHeapDeserState> {
deserializer
.session_context()
.get::<CurrentHeapDeserState>()
}
pub(crate) fn current_heap_deser_state(&self) -> MutexGuard<'_, HeapDeserializationState> {
self.current_heap_deser_state
.lock()
.expect("current heap deser state lock poisoned")
}
}
impl<'de> StarlarkDeserializeContext<'de> for StarlarkDeserializerImpl<'_, 'de> {
fn pagable(&mut self) -> &mut dyn PagableDeserializer<'de> {
self.pagable
}
fn deserialize_frozen_value(&mut self) -> crate::Result<FrozenValue> {
let serialized = SerializedFrozenValue::pagable_deserialize(self.pagable)?;
match serialized {
SerializedFrozenValue::HeapPtr {
heap_id,
offset,
is_str,
} => {
let bases = self
.state
.heap_bases
.get(&heap_id)
.ok_or(PagableError::HeapBasesNotRegistered { heap_id })?;
let ptr = bases.resolve(&offset);
let header = unsafe { &*(ptr as *const AValueHeader) };
let fv = FrozenValue::new_ptr(header, is_str);
drop(bases);
self.ensure_initialized(fv)?;
Ok(fv)
}
SerializedFrozenValue::InlineInt(v) => {
let inline = InlineInt::try_from(v)
.map_err(|_| anyhow::anyhow!("Integer {} does not fit in InlineInt", v))?;
Ok(FrozenValue::new_int(inline))
}
SerializedFrozenValue::Static(id) => {
let fv = get_frozen_value_by_static_id(id).ok_or_else(|| {
anyhow::anyhow!("Static value ID {:?} not found in inventory registry", id)
})?;
Ok(fv)
}
}
}
fn ensure_initialized(&mut self, fv: FrozenValue) -> crate::Result<()> {
let idx = match self.current_heap_deser_state().find_by_frozen_value(fv) {
Some(idx) => idx,
None => return Ok(()),
};
let target = match self.current_heap_deser_state().try_claim(idx) {
Some(target) => target,
None => return Ok(()), };
let saved_pos = self.pagable.position();
unsafe { self.pagable.seek(target.abs_pos) };
(target.vtable.starlark_deserialize)(target.raw_ptr, self)?;
unsafe { target.write_vtable_to_header() };
unsafe { self.pagable.seek(saved_pos) };
Ok(())
}
}