use std::collections::HashMap;
use std::sync::Arc;
use dashmap::DashMap;
use dashmap::DashSet;
use pagable::PagableSerialize;
use pagable::PagableSerializer;
use crate::pagable::heap_ref_id::HeapRefId;
use crate::pagable::serialized_frozen_value::SerializedFrozenValue;
use crate::pagable::starlark_serialize::StarlarkSerializeContext;
use crate::pagable::static_value::get_static_value_id;
use crate::values::FrozenValue;
use crate::values::layout::heap::arena::ArenaOffset;
use crate::values::layout::heap::heap_type::FrozenHeapRef;
use crate::values::layout::pointer::PointerTags;
pub(crate) struct StarlarkSerState {
ptr_to_location: DashMap<usize, (HeapRefId, ArenaOffset)>,
registered_heaps: DashSet<HeapRefId>,
}
impl StarlarkSerState {
pub(crate) fn new() -> Self {
Self {
ptr_to_location: DashMap::new(),
registered_heaps: DashSet::new(),
}
}
fn register_heap(&self, heap_id: HeapRefId, offset_map: HashMap<usize, ArenaOffset>) {
for (ptr, offset) in offset_map {
self.ptr_to_location.insert(ptr, (heap_id, offset));
}
self.registered_heaps.insert(heap_id);
}
fn has_heap(&self, heap_id: HeapRefId) -> bool {
self.registered_heaps.contains(&heap_id)
}
pub(crate) fn ensure_offset_maps_registered_inner(
&self,
heap_id: HeapRefId,
refs: &[FrozenHeapRef],
build_map: impl FnOnce() -> HashMap<usize, ArenaOffset>,
) {
if self.has_heap(heap_id) {
return;
}
for dep in refs {
self.ensure_offset_maps_registered(dep);
}
self.register_heap(heap_id, build_map());
}
pub(crate) fn ensure_offset_maps_registered(&self, heap_ref: &FrozenHeapRef) {
let Some(name) = heap_ref.name() else {
return;
};
let heap_id = HeapRefId::from_heap_name(name);
self.ensure_offset_maps_registered_inner(heap_id, heap_ref.refs_slice(), || {
heap_ref.build_ptr_to_offset_map()
});
}
}
pub struct StarlarkSerializerImpl<'a> {
pagable: &'a mut dyn PagableSerializer,
state: Arc<StarlarkSerState>,
}
impl<'a> StarlarkSerializerImpl<'a> {
pub fn recover_from_pagable(serializer: &'a mut dyn PagableSerializer) -> crate::Result<Self> {
let state = Self::get_or_create_state(serializer);
Ok(Self::new(serializer, state))
}
pub(crate) fn new(
pagable: &'a mut dyn PagableSerializer,
state: Arc<StarlarkSerState>,
) -> Self {
Self { pagable, state }
}
pub(crate) fn get_or_create_state(
serializer: &mut dyn PagableSerializer,
) -> Arc<StarlarkSerState> {
serializer
.session_context()
.get_or_insert_with(|| Arc::new(StarlarkSerState::new()))
}
}
impl StarlarkSerializeContext for StarlarkSerializerImpl<'_> {
fn pagable(&mut self) -> &mut dyn PagableSerializer {
self.pagable
}
fn serialize_frozen_value(&mut self, fv: FrozenValue) -> crate::Result<()> {
match fv.ptr_value().tags() {
PointerTags::OtherFrozen | PointerTags::StrFrozen => {
if let Some(static_id) = get_static_value_id(fv) {
let serialized = SerializedFrozenValue::Static(static_id);
serialized.pagable_serialize(self.pagable)?;
return Ok(());
}
let is_str = fv.ptr_value().tags() == PointerTags::StrFrozen;
let raw_ptr = fv.ptr_value().ptr_value_untagged();
let (heap_id, arena_offset) = self
.state
.ptr_to_location
.get(&raw_ptr)
.map(|loc| *loc)
.unwrap_or_else(|| {
panic!(
"FrozenValue pointer {:#x} not found in any heap's offset map",
raw_ptr
)
});
let serialized = SerializedFrozenValue::HeapPtr {
heap_id,
offset: arena_offset,
is_str,
};
serialized.pagable_serialize(self.pagable)?;
return Ok(());
}
PointerTags::Int => {
let int_val = fv.unpack_inline_int().expect("Int tag implies inline int");
let serialized = SerializedFrozenValue::InlineInt(int_val.to_i32());
serialized.pagable_serialize(self.pagable)?;
}
PointerTags::OtherUnfrozen | PointerTags::StrUnfrozen => {
unreachable!("FrozenValue cannot have unfrozen tag")
}
}
Ok(())
}
}