use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use rustc_hash::{FxHashMap, FxHashSet};
use super::DumpError;
use super::mapped_heap::MappedHeapView;
use super::types::*;
use super::value_fixups::RawValueFixup;
use crate::buffer::buffer::{Buffer, BufferId, BufferManager, InsertionType};
use crate::buffer::buffer_text::BufferText;
use crate::buffer::overlay::{Overlay, OverlayList};
use crate::buffer::shared::SharedUndoState;
use crate::buffer::text_props::{PropertyInterval, TextPropertyTable};
use crate::emacs_core::abbrev::{Abbrev, AbbrevManager, AbbrevTable};
use crate::emacs_core::advice::{VariableWatcher, VariableWatcherList};
use crate::emacs_core::autoload::{AutoloadEntry, AutoloadManager, AutoloadType};
use crate::emacs_core::bookmark::{Bookmark, BookmarkManager};
use crate::emacs_core::bytecode::chunk::ByteCodeFunction;
use crate::emacs_core::bytecode::opcode::Op;
use crate::emacs_core::charset::{
CharsetInfoSnapshot, CharsetMethodSnapshot, CharsetRegistrySnapshot, restore_charset_registry,
snapshot_charset_registry,
};
use crate::emacs_core::coding::{CodingSystemInfo, CodingSystemManager, EolType};
use crate::emacs_core::custom::CustomManager;
use crate::emacs_core::eval::Context;
use crate::emacs_core::fontset::{
FontRepertory, FontSpecEntry, FontsetDataSnapshot, FontsetRangeEntrySnapshot,
FontsetRegistrySnapshot, StoredFontSpec, restore_fontset_registry, snapshot_fontset_registry,
};
use crate::emacs_core::interactive::{InteractiveRegistry, InteractiveSpec};
use crate::emacs_core::intern::{self, NameId, SymId};
use crate::emacs_core::kmacro::KmacroManager;
use crate::emacs_core::mode::{
self, CustomGroup as ModeCustomGroup, CustomType as ModeCustomType,
CustomVariable as ModeCustomVariable, FontLockDefaults, FontLockKeyword, MajorMode, MinorMode,
ModeRegistry,
};
use crate::emacs_core::rect::RectangleState;
use crate::emacs_core::register::{RegisterContent, RegisterManager};
use crate::emacs_core::symbol::{LispSymbol, Obarray, SymbolTrappedWrite};
use crate::emacs_core::syntax::{SyntaxClass, SyntaxEntry, SyntaxFlags, SyntaxTable};
use crate::emacs_core::value::{
HashKey, HashTableTest, HashTableWeakness, LambdaData, LambdaParams, LispHashTable,
OrderedRuntimeBindingMap, OrderedSymMap, RuntimeBindingValue, StringTextPropertyRun, Value,
};
use crate::emacs_core::value::{ValueKind, VecLikeType};
use crate::emacs_core::value::{
get_string_text_properties_for_value, set_string_text_properties_for_value,
};
use crate::face::{
BoxBorder, BoxStyle, Color, Face, FaceHeight, FaceTable, FontSlant, FontWeight, FontWidth,
Underline, UnderlineStyle,
};
use crate::heap_types::LispString;
use crate::tagged::gc::with_tagged_heap;
use crate::tagged::header::{
BufferObj, ByteCodeObj, CLOSURE_MIN_SLOTS, ConsCell, FloatObj, FrameObj, GcHeader,
HashTableObj, HeapObjectKind, LambdaObj, LispValueVec, MacroObj, MarkerObj, OverlayObj,
RecordObj, StringObj, SubrObj, TimerObj, VecLikeHeader, VectorObj, WindowObj,
};
use crate::tagged::value::TaggedValue;
thread_local! {
static PDUMP_LOAD_NAME_REMAP: RefCell<Option<Vec<NameId>>> = const { RefCell::new(None) };
static PDUMP_LOAD_SYM_REMAP: RefCell<Option<Vec<SymId>>> = const { RefCell::new(None) };
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
struct TaggedHeapRef {
index: u32,
}
struct TaggedDumpState {
objects: Vec<Option<DumpHeapObject>>,
object_ids: HashMap<usize, TaggedHeapRef>,
}
impl TaggedDumpState {
fn new() -> Self {
Self {
objects: Vec::new(),
object_ids: HashMap::new(),
}
}
fn finalize(self) -> DumpTaggedHeap {
DumpTaggedHeap {
objects: self
.objects
.into_iter()
.map(|obj| obj.unwrap_or(DumpHeapObject::Free))
.collect(),
mapped_cons: Vec::new(),
mapped_floats: Vec::new(),
mapped_strings: Vec::new(),
mapped_veclikes: Vec::new(),
mapped_slots: Vec::new(),
}
}
}
pub(crate) struct DumpEncoder {
state: TaggedDumpState,
}
impl DumpEncoder {
fn new() -> Self {
Self {
state: TaggedDumpState::new(),
}
}
fn finalize(self) -> DumpTaggedHeap {
self.state.finalize()
}
fn value_to_heap_ref(&mut self, v: &Value) -> TaggedHeapRef {
debug_assert!(v.is_heap_object());
let bits = v.bits();
if let Some(id) = self.state.object_ids.get(&bits).copied() {
return id;
}
let id = TaggedHeapRef {
index: self.state.objects.len() as u32,
};
self.state.object_ids.insert(bits, id);
self.state.objects.push(None);
let dumped = dump_heap_object_from_value(self, *v);
self.state.objects[id.index as usize] = Some(dumped);
id
}
fn dump_value(&mut self, v: &Value) -> DumpValue {
match v.kind() {
ValueKind::Nil => DumpValue::Nil,
ValueKind::T => DumpValue::True,
ValueKind::Fixnum(n) => DumpValue::Int(n),
ValueKind::Float => DumpValue::Float(dump_heap_ref(self.value_to_heap_ref(v))),
ValueKind::Symbol(s) => DumpValue::Symbol(dump_sym_id(s)),
ValueKind::String => DumpValue::Str(dump_heap_ref(self.value_to_heap_ref(v))),
ValueKind::Cons => DumpValue::Cons(dump_heap_ref(self.value_to_heap_ref(v))),
ValueKind::Veclike(VecLikeType::Vector) => {
DumpValue::Vector(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Record) => {
DumpValue::Record(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::HashTable) => {
DumpValue::HashTable(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Lambda) => {
DumpValue::Lambda(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Macro) => {
DumpValue::Macro(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Subr(s) => DumpValue::Subr(dump_name_id(intern::symbol_name_id(s))),
ValueKind::Veclike(VecLikeType::Subr) => {
let s = v.as_subr_id().unwrap();
DumpValue::Subr(dump_name_id(intern::symbol_name_id(s)))
}
ValueKind::Veclike(VecLikeType::ByteCode) => {
DumpValue::ByteCode(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Marker) => {
DumpValue::Marker(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Overlay) => {
DumpValue::Overlay(dump_heap_ref(self.value_to_heap_ref(v)))
}
ValueKind::Veclike(VecLikeType::Buffer) => {
DumpValue::Buffer(DumpBufferId(v.as_buffer_id().unwrap().0))
}
ValueKind::Veclike(VecLikeType::Window) => DumpValue::Window(v.as_window_id().unwrap()),
ValueKind::Veclike(VecLikeType::Frame) => DumpValue::Frame(v.as_frame_id().unwrap()),
ValueKind::Veclike(VecLikeType::Timer) => DumpValue::Timer(v.as_timer_id().unwrap()),
ValueKind::Veclike(VecLikeType::Bignum) => {
DumpValue::Bignum(v.as_bignum().unwrap().to_string())
}
ValueKind::Veclike(VecLikeType::SymbolWithPos) => {
panic!("pdump: symbol-with-pos is not yet supported in portable dumps")
}
ValueKind::Unbound => DumpValue::Unbound,
ValueKind::Unknown => DumpValue::Nil,
}
}
fn dump_opt_value(&mut self, v: &Option<Value>) -> Option<DumpValue> {
v.as_ref().map(|value| self.dump_value(value))
}
}
pub(crate) struct TaggedLoadState {
objects: Vec<DumpHeapObject>,
mapped_cons: Vec<Option<DumpConsSpan>>,
mapped_floats: Vec<Option<DumpFloatSpan>>,
mapped_strings: Vec<Option<DumpStringSpan>>,
mapped_veclikes: Vec<Option<DumpVecLikeSpan>>,
mapped_slots: Vec<Option<DumpSlotSpan>>,
value_fixups: Vec<RawValueFixup>,
values: Vec<Option<Value>>,
populated: Vec<bool>,
mapped_heap: Option<MappedHeapView>,
buffers: HashMap<u64, Value>,
windows: HashMap<u64, Value>,
frames: HashMap<u64, Value>,
timers: HashMap<u64, Value>,
pub(crate) markers_by_id: FxHashMap<u64, *mut crate::tagged::header::MarkerObj>,
}
impl TaggedLoadState {
fn new(
heap: &DumpTaggedHeap,
mapped_heap: Option<MappedHeapView>,
value_fixups: Vec<RawValueFixup>,
) -> Self {
let len = heap.objects.len();
Self {
objects: heap.objects.clone(),
mapped_cons: heap.mapped_cons.clone(),
mapped_floats: heap.mapped_floats.clone(),
mapped_strings: heap.mapped_strings.clone(),
mapped_veclikes: heap.mapped_veclikes.clone(),
mapped_slots: heap.mapped_slots.clone(),
value_fixups,
values: vec![None; len],
populated: vec![false; len],
mapped_heap,
buffers: HashMap::new(),
windows: HashMap::new(),
frames: HashMap::new(),
timers: HashMap::new(),
markers_by_id: FxHashMap::default(),
}
}
fn from_tagged_heap(
heap: DumpTaggedHeap,
mapped_heap: Option<MappedHeapView>,
value_fixups: Vec<RawValueFixup>,
) -> Self {
let len = heap.objects.len();
Self {
objects: heap.objects,
mapped_cons: heap.mapped_cons,
mapped_floats: heap.mapped_floats,
mapped_strings: heap.mapped_strings,
mapped_veclikes: heap.mapped_veclikes,
mapped_slots: heap.mapped_slots,
value_fixups,
values: vec![None; len],
populated: vec![false; len],
mapped_heap,
buffers: HashMap::new(),
windows: HashMap::new(),
frames: HashMap::new(),
timers: HashMap::new(),
markers_by_id: FxHashMap::default(),
}
}
}
pub(crate) struct LoadDecoder {
state: TaggedLoadState,
}
impl LoadDecoder {
pub(crate) fn new(heap: &DumpTaggedHeap) -> Self {
Self::new_with_mapped_heap(heap, None)
}
pub(crate) fn new_with_mapped_heap(
heap: &DumpTaggedHeap,
mapped_heap: Option<MappedHeapView>,
) -> Self {
Self::new_with_mapped_heap_and_fixups(heap, mapped_heap, Vec::new())
}
pub(crate) fn new_with_mapped_heap_and_fixups(
heap: &DumpTaggedHeap,
mapped_heap: Option<MappedHeapView>,
value_fixups: Vec<RawValueFixup>,
) -> Self {
Self {
state: TaggedLoadState::new(heap, mapped_heap, value_fixups),
}
}
pub(crate) fn from_tagged_heap_with_mapped_heap_and_fixups(
heap: DumpTaggedHeap,
mapped_heap: Option<MappedHeapView>,
value_fixups: Vec<RawValueFixup>,
) -> Self {
Self {
state: TaggedLoadState::from_tagged_heap(heap, mapped_heap, value_fixups),
}
}
pub(crate) fn preload_tagged_heap(&mut self) -> Result<(), DumpError> {
self.register_mapped_cons_ranges()?;
self.register_mapped_float_ranges()?;
self.register_mapped_string_objects()?;
self.register_mapped_veclike_objects()?;
for index in 0..self.state.objects.len() {
self.allocate_tagged_placeholder(TaggedHeapRef {
index: index as u32,
})?;
}
self.apply_mapped_value_fixups()?;
for index in 0..self.state.objects.len() {
self.populate_tagged_object(TaggedHeapRef {
index: index as u32,
})?;
}
Ok(())
}
fn apply_mapped_value_fixups(&mut self) -> Result<(), DumpError> {
if self.state.value_fixups.is_empty() {
return Ok(());
}
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError("value fixups require a writable mapped heap image".into())
})?;
let fixups = self.state.value_fixups.clone();
for fixup in fixups {
let value = self.load_value(&fixup.value);
mapped_heap.write_value_word(fixup.location_offset, value)?;
}
Ok(())
}
fn register_mapped_string_objects(&self) -> Result<(), DumpError> {
let mapped_heap = self.state.mapped_heap;
for (index, span) in self.state.mapped_strings.iter().enumerate() {
let Some(span) = *span else {
continue;
};
if !matches!(&self.state.objects[index], DumpHeapObject::Str { .. }) {
return Err(DumpError::ImageFormatError(format!(
"mapped string span attached to non-string object: {:?}",
self.state.objects[index]
)));
}
let mapped_heap = mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped string objects but image has no heap section".into(),
)
})?;
let ptr = mapped_heap.string_obj_mut(span)?;
with_tagged_heap(|heap| unsafe {
heap.register_mapped_string_object(ptr, std::mem::size_of::<StringObj>())
});
}
Ok(())
}
fn register_mapped_veclike_objects(&self) -> Result<(), DumpError> {
let mapped_heap = self.state.mapped_heap;
for (index, span) in self.state.mapped_veclikes.iter().enumerate() {
let Some(span) = *span else {
continue;
};
let mapped_heap = mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped vectorlike objects but image has no heap section".into(),
)
})?;
let (ptr, byte_len) = match &self.state.objects[index] {
DumpHeapObject::Vector(_) => (
mapped_heap
.typed_object_mut::<VectorObj>(span, "vector")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<VectorObj>(),
),
DumpHeapObject::Lambda(_) => (
mapped_heap
.typed_object_mut::<LambdaObj>(span, "lambda")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<LambdaObj>(),
),
DumpHeapObject::Macro(_) => (
mapped_heap
.typed_object_mut::<MacroObj>(span, "macro")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<MacroObj>(),
),
DumpHeapObject::Record(_) => (
mapped_heap
.typed_object_mut::<RecordObj>(span, "record")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<RecordObj>(),
),
DumpHeapObject::Marker(_) => (
mapped_heap
.typed_object_mut::<MarkerObj>(span, "marker")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<MarkerObj>(),
),
DumpHeapObject::Overlay(_) => (
mapped_heap
.typed_object_mut::<OverlayObj>(span, "overlay")?
.cast::<VecLikeHeader>(),
std::mem::size_of::<OverlayObj>(),
),
other => {
return Err(DumpError::ImageFormatError(format!(
"mapped vectorlike span attached to non-vectorlike object: {other:?}"
)));
}
};
with_tagged_heap(|heap| unsafe { heap.register_mapped_veclike_object(ptr, byte_len) });
}
Ok(())
}
fn register_mapped_float_ranges(&self) -> Result<(), DumpError> {
let mut offsets: Vec<_> = self
.state
.mapped_floats
.iter()
.flatten()
.map(|span| span.offset)
.collect();
if offsets.is_empty() {
return Ok(());
}
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped float objects but image has no heap section".into(),
)
})?;
offsets.sort_unstable();
let object_size = std::mem::size_of::<FloatObj>() as u64;
let mut run_start = offsets[0];
let mut run_len = 1usize;
let mut prev = offsets[0];
for offset in offsets.into_iter().skip(1) {
if offset == prev + object_size {
run_len += 1;
} else {
let ptr = mapped_heap.float_obj_mut(DumpFloatSpan { offset: run_start })?;
with_tagged_heap(|heap| unsafe { heap.register_mapped_float_range(ptr, run_len) });
run_start = offset;
run_len = 1;
}
prev = offset;
}
let ptr = mapped_heap.float_obj_mut(DumpFloatSpan { offset: run_start })?;
with_tagged_heap(|heap| unsafe { heap.register_mapped_float_range(ptr, run_len) });
Ok(())
}
fn register_mapped_cons_ranges(&self) -> Result<(), DumpError> {
let mut offsets: Vec<_> = self
.state
.mapped_cons
.iter()
.flatten()
.map(|span| span.offset)
.collect();
if offsets.is_empty() {
return Ok(());
}
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped cons cells but image has no heap section".into(),
)
})?;
offsets.sort_unstable();
let cell_size = std::mem::size_of::<ConsCell>() as u64;
let mut run_start = offsets[0];
let mut run_len = 1usize;
let mut prev = offsets[0];
for offset in offsets.into_iter().skip(1) {
if offset == prev + cell_size {
run_len += 1;
} else {
let ptr = mapped_heap.cons_cell_mut(DumpConsSpan { offset: run_start })?;
with_tagged_heap(|heap| unsafe { heap.register_mapped_cons_range(ptr, run_len) });
run_start = offset;
run_len = 1;
}
prev = offset;
}
let ptr = mapped_heap.cons_cell_mut(DumpConsSpan { offset: run_start })?;
with_tagged_heap(|heap| unsafe { heap.register_mapped_cons_range(ptr, run_len) });
Ok(())
}
fn heap_ref_to_value(&mut self, id: TaggedHeapRef) -> Value {
self.allocate_tagged_placeholder(id)
.expect("pdump placeholder allocation should succeed")
}
fn load_cached_buffer(&mut self, id: u64) -> Value {
*self
.state
.buffers
.entry(id)
.or_insert_with(|| Value::make_buffer(BufferId(id)))
}
fn load_cached_window(&mut self, id: u64) -> Value {
*self
.state
.windows
.entry(id)
.or_insert_with(|| Value::make_window(id))
}
fn load_cached_frame(&mut self, id: u64) -> Value {
*self
.state
.frames
.entry(id)
.or_insert_with(|| Value::make_frame(id))
}
fn load_cached_timer(&mut self, id: u64) -> Value {
*self
.state
.timers
.entry(id)
.or_insert_with(|| Value::make_timer(id))
}
fn load_dump_string(
&self,
data: &DumpByteData,
size: usize,
size_byte: i64,
) -> Result<LispString, DumpError> {
match data {
DumpByteData::Owned(bytes) => Ok(LispString::from_dump(bytes.clone(), size, size_byte)),
DumpByteData::Mapped(_) => {
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump references mapped heap bytes but image has no heap section".into(),
)
})?;
let bytes = mapped_heap.bytes(data)?;
Ok(unsafe { LispString::from_mapped_bytes(bytes.ptr, bytes.len, size, size_byte) })
}
}
}
fn mapped_slots_for_object(
&self,
id: TaggedHeapRef,
slots: &[Value],
) -> Result<Option<LispValueVec>, DumpError> {
let Some(ptr) = self.mapped_slots_ptr_for_object(id, slots.len())? else {
return Ok(None);
};
if !slots.is_empty() {
unsafe {
std::ptr::copy_nonoverlapping(slots.as_ptr(), ptr, slots.len());
}
}
Ok(Some(unsafe {
LispValueVec::mapped(ptr.cast_const(), slots.len())
}))
}
fn mapped_slots_for_object_without_copy(
&self,
id: TaggedHeapRef,
expected_len: usize,
) -> Result<Option<LispValueVec>, DumpError> {
let Some(ptr) = self.mapped_slots_ptr_for_object(id, expected_len)? else {
return Ok(None);
};
Ok(Some(unsafe {
LispValueVec::mapped(ptr.cast_const(), expected_len)
}))
}
fn mapped_slots_ptr_for_object(
&self,
id: TaggedHeapRef,
expected_len: usize,
) -> Result<Option<*mut TaggedValue>, DumpError> {
let Some(span) = self
.state
.mapped_slots
.get(id.index as usize)
.copied()
.flatten()
else {
return Ok(None);
};
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped vector slots but image has no heap section".into(),
)
})?;
mapped_heap.slots_mut(span, expected_len).map(Some)
}
fn mapped_cons_cell_for_object(
&self,
id: TaggedHeapRef,
) -> Result<Option<*mut ConsCell>, DumpError> {
let Some(span) = self
.state
.mapped_cons
.get(id.index as usize)
.copied()
.flatten()
else {
return Ok(None);
};
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped cons cells but image has no heap section".into(),
)
})?;
mapped_heap.cons_cell_mut(span).map(Some)
}
fn mapped_float_obj_for_object(
&self,
id: TaggedHeapRef,
) -> Result<Option<*mut FloatObj>, DumpError> {
let Some(span) = self
.state
.mapped_floats
.get(id.index as usize)
.copied()
.flatten()
else {
return Ok(None);
};
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped float objects but image has no heap section".into(),
)
})?;
mapped_heap.float_obj_mut(span).map(Some)
}
fn mapped_string_obj_for_object(
&self,
id: TaggedHeapRef,
) -> Result<Option<*mut StringObj>, DumpError> {
let Some(span) = self
.state
.mapped_strings
.get(id.index as usize)
.copied()
.flatten()
else {
return Ok(None);
};
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped string objects but image has no heap section".into(),
)
})?;
mapped_heap.string_obj_mut(span).map(Some)
}
fn mapped_typed_object_for_object<T>(
&self,
id: TaggedHeapRef,
label: &'static str,
) -> Result<Option<*mut T>, DumpError> {
let Some(span) = self
.state
.mapped_veclikes
.get(id.index as usize)
.copied()
.flatten()
else {
return Ok(None);
};
let mapped_heap = self.state.mapped_heap.ok_or_else(|| {
DumpError::ImageFormatError(
"dump reserves mapped vectorlike objects but image has no heap section".into(),
)
})?;
mapped_heap.typed_object_mut::<T>(span, label).map(Some)
}
fn install_mapped_vector_slots(value: Value, storage: LispValueVec) -> bool {
if value.veclike_type() != Some(VecLikeType::Vector) {
return false;
}
let ptr = value.as_veclike_ptr().unwrap() as *mut VectorObj;
unsafe {
(*ptr).data = storage;
}
true
}
fn install_mapped_record_slots(value: Value, storage: LispValueVec) -> bool {
if value.veclike_type() != Some(VecLikeType::Record) {
return false;
}
let ptr = value.as_veclike_ptr().unwrap() as *mut RecordObj;
unsafe {
(*ptr).data = storage;
}
true
}
fn install_mapped_closure_slots(value: Value, storage: LispValueVec) -> bool {
match value.veclike_type() {
Some(VecLikeType::Lambda) => {
let ptr = value.as_veclike_ptr().unwrap() as *mut LambdaObj;
unsafe {
let obj = &mut *ptr;
let _ = obj.parsed_params.take();
obj.data = storage;
}
true
}
Some(VecLikeType::Macro) => {
let ptr = value.as_veclike_ptr().unwrap() as *mut MacroObj;
unsafe {
let obj = &mut *ptr;
let _ = obj.parsed_params.take();
obj.data = storage;
}
true
}
_ => false,
}
}
fn mapped_raw_word_available(&self, value: &DumpValue) -> bool {
match value {
DumpValue::Nil | DumpValue::True | DumpValue::Int(_) | DumpValue::Unbound => true,
DumpValue::Cons(id) => self
.state
.mapped_cons
.get(id.index as usize)
.is_some_and(|span| span.is_some()),
DumpValue::Float(id) => self
.state
.mapped_floats
.get(id.index as usize)
.is_some_and(|span| span.is_some()),
DumpValue::Str(id) => self
.state
.mapped_strings
.get(id.index as usize)
.is_some_and(|span| span.is_some()),
DumpValue::Vector(id)
| DumpValue::Record(id)
| DumpValue::Lambda(id)
| DumpValue::Macro(id)
| DumpValue::Marker(id)
| DumpValue::Overlay(id) => self
.state
.mapped_veclikes
.get(id.index as usize)
.is_some_and(|span| span.is_some()),
DumpValue::Symbol(_)
| DumpValue::Subr(_)
| DumpValue::HashTable(_)
| DumpValue::ByteCode(_)
| DumpValue::Buffer(_)
| DumpValue::Window(_)
| DumpValue::Frame(_)
| DumpValue::Timer(_)
| DumpValue::Bignum(_) => false,
}
}
fn mapped_raw_words_available(&self, values: &[DumpValue]) -> bool {
values
.iter()
.all(|value| self.mapped_raw_word_available(value))
}
fn mapped_cons_has_raw_words(
&self,
id: TaggedHeapRef,
_car: &DumpValue,
_cdr: &DumpValue,
) -> bool {
self.state
.mapped_cons
.get(id.index as usize)
.is_some_and(|span| span.is_some())
}
fn allocate_tagged_placeholder(&mut self, id: TaggedHeapRef) -> Result<Value, DumpError> {
if let Some(value) = self.state.values[id.index as usize] {
return Ok(value);
}
let value = match &self.state.objects[id.index as usize] {
DumpHeapObject::Cons { .. } => {
if let Some(cell) = self.mapped_cons_cell_for_object(id)? {
unsafe { Value::from_cons_ptr(cell) }
} else {
Value::cons(Value::NIL, Value::NIL)
}
}
DumpHeapObject::Vector(items) => {
if let Some(ptr) = self.mapped_typed_object_for_object::<VectorObj>(id, "vector")? {
unsafe {
std::ptr::write(
ptr,
VectorObj {
header: VecLikeHeader::new(VecLikeType::Vector),
data: LispValueVec::owned(vec![Value::NIL; items.len()]),
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
Value::make_vector(vec![Value::NIL; items.len()])
}
}
DumpHeapObject::HashTable(ht) => with_tagged_heap(|heap| {
heap.alloc_hash_table(LispHashTable::new_with_options(
load_hash_table_test(&ht.test),
ht.size,
ht.weakness.as_ref().map(load_hash_table_weakness),
ht.rehash_size,
ht.rehash_threshold,
))
}),
DumpHeapObject::Str {
data,
size,
size_byte,
..
} => {
let string = self.load_dump_string(data, *size, *size_byte)?;
if let Some(ptr) = self.mapped_string_obj_for_object(id)? {
unsafe {
std::ptr::write(
ptr,
StringObj {
header: GcHeader::new(HeapObjectKind::String),
data: string,
text_props: TextPropertyTable::new(),
},
);
Value::from_string_ptr(ptr)
}
} else {
Value::heap_string(string)
}
}
DumpHeapObject::Float(value) => {
if let Some(ptr) = self.mapped_float_obj_for_object(id)? {
unsafe {
debug_assert!(matches!((*ptr).header.kind, HeapObjectKind::Float));
Value::from_float_ptr(ptr)
}
} else {
Value::make_float(*value)
}
}
DumpHeapObject::Lambda(slots) => {
let len = slots.len().max(CLOSURE_MIN_SLOTS);
if let Some(ptr) = self.mapped_typed_object_for_object::<LambdaObj>(id, "lambda")? {
unsafe {
std::ptr::write(
ptr,
LambdaObj {
header: VecLikeHeader::new(VecLikeType::Lambda),
data: LispValueVec::owned(vec![Value::NIL; len]),
parsed_params: std::sync::OnceLock::new(),
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
with_tagged_heap(|heap| heap.alloc_lambda(vec![Value::NIL; len]))
}
}
DumpHeapObject::Macro(slots) => {
let len = slots.len().max(CLOSURE_MIN_SLOTS);
if let Some(ptr) = self.mapped_typed_object_for_object::<MacroObj>(id, "macro")? {
unsafe {
std::ptr::write(
ptr,
MacroObj {
header: VecLikeHeader::new(VecLikeType::Macro),
data: LispValueVec::owned(vec![Value::NIL; len]),
parsed_params: std::sync::OnceLock::new(),
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
with_tagged_heap(|heap| heap.alloc_macro(vec![Value::NIL; len]))
}
}
DumpHeapObject::ByteCode(_) => Value::make_bytecode(ByteCodeFunction {
ops: Vec::new(),
constants: Vec::new(),
max_stack: 0,
params: LambdaParams::simple(Vec::new()),
arglist: Value::NIL,
lexical: false,
env: None,
gnu_byte_offset_map: None,
gnu_bytecode_bytes: None,
docstring: None,
doc_form: None,
interactive: None,
closure_slot_count: 4,
extra_slots: Vec::new(),
}),
DumpHeapObject::Record(items) => {
if let Some(ptr) = self.mapped_typed_object_for_object::<RecordObj>(id, "record")? {
unsafe {
std::ptr::write(
ptr,
RecordObj {
header: VecLikeHeader::new(VecLikeType::Record),
data: LispValueVec::owned(vec![Value::NIL; items.len()]),
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
Value::make_record(vec![Value::NIL; items.len()])
}
}
DumpHeapObject::Marker(marker) => {
let data = crate::heap_types::MarkerData {
buffer: marker.buffer.map(|id| BufferId(id.0)),
insertion_type: marker.insertion_type,
marker_id: marker.marker_id,
bytepos: marker.bytepos,
charpos: marker.charpos,
next_marker: std::ptr::null_mut(),
};
let value = if let Some(ptr) =
self.mapped_typed_object_for_object::<MarkerObj>(id, "marker")?
{
unsafe {
std::ptr::write(
ptr,
MarkerObj {
header: VecLikeHeader::new(VecLikeType::Marker),
data,
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
Value::make_marker(data)
};
if let Some(id) = marker.marker_id {
if let Some(ptr) = value.as_veclike_ptr() {
self.state
.markers_by_id
.insert(id, ptr as *mut crate::tagged::header::MarkerObj);
}
}
value
}
DumpHeapObject::Overlay(overlay) => {
let data = crate::heap_types::OverlayData {
plist: Value::NIL,
buffer: overlay.buffer.map(|id| BufferId(id.0)),
start: overlay.start,
end: overlay.end,
front_advance: overlay.front_advance,
rear_advance: overlay.rear_advance,
};
if let Some(ptr) =
self.mapped_typed_object_for_object::<OverlayObj>(id, "overlay")?
{
unsafe {
std::ptr::write(
ptr,
OverlayObj {
header: VecLikeHeader::new(VecLikeType::Overlay),
data,
},
);
Value::from_veclike_ptr(ptr.cast::<VecLikeHeader>())
}
} else {
Value::make_overlay(data)
}
}
DumpHeapObject::Buffer(id) => self.load_cached_buffer(id.0),
DumpHeapObject::Window(id) => self.load_cached_window(*id),
DumpHeapObject::Frame(id) => self.load_cached_frame(*id),
DumpHeapObject::Timer(id) => self.load_cached_timer(*id),
DumpHeapObject::Subr { name, .. } => {
let name_id = load_name_id(name);
if let Some(sym_id) = intern::canonical_symbol_for_name(name_id) {
Value::subr_from_sym_id(sym_id)
} else {
let n = intern::resolve_name(name_id);
Value::subr_from_sym_id(intern::intern(n))
}
}
DumpHeapObject::Free => Value::NIL,
};
self.state.values[id.index as usize] = Some(value);
Ok(value)
}
fn populate_tagged_object(&mut self, id: TaggedHeapRef) -> Result<(), DumpError> {
if self.state.populated[id.index as usize] {
return Ok(());
}
let value = self.allocate_tagged_placeholder(id)?;
self.state.populated[id.index as usize] = true;
match self.state.objects[id.index as usize].clone() {
DumpHeapObject::Cons { car, cdr } => {
if !self.mapped_cons_has_raw_words(id, &car, &cdr) {
value.set_car(self.load_value(&car));
value.set_cdr(self.load_value(&cdr));
}
}
DumpHeapObject::Vector(items) => {
if let Some(storage) = self.mapped_slots_for_object_without_copy(id, items.len())? {
let _ = Self::install_mapped_vector_slots(value, storage);
} else {
let slots: Vec<_> = items.iter().map(|item| self.load_value(item)).collect();
if let Some(storage) = self.mapped_slots_for_object(id, &slots)? {
let _ = Self::install_mapped_vector_slots(value, storage);
} else {
let _ = value.replace_vector_data(slots);
}
}
}
DumpHeapObject::HashTable(ht) => {
let _ = value.with_hash_table_mut(|table| {
table.test = load_hash_table_test(&ht.test);
table.test_name = ht.test_name.map(|s| load_sym_id(&s));
table.size = ht.size;
table.weakness = ht.weakness.as_ref().map(load_hash_table_weakness);
table.rehash_size = ht.rehash_size;
table.rehash_threshold = ht.rehash_threshold;
table.data = ht
.entries
.iter()
.map(|(k, v)| (load_hash_key(self, k), self.load_value(v)))
.collect();
table.key_snapshots = ht
.key_snapshots
.iter()
.map(|(k, v)| (load_hash_key(self, k), self.load_value(v)))
.collect();
table.insertion_order = ht
.insertion_order
.iter()
.map(|key| load_hash_key(self, key))
.collect();
});
}
DumpHeapObject::Str { text_props, .. } => {
if !text_props.is_empty() {
for run in &text_props {
self.populate_value_graph(&run.plist)?;
}
let runs = text_props
.iter()
.map(|run| StringTextPropertyRun {
start: run.start,
end: run.end,
plist: self.load_value(&run.plist),
})
.collect();
set_string_text_properties_for_value(value, runs);
}
}
DumpHeapObject::Float(_) => {}
DumpHeapObject::Lambda(slots) | DumpHeapObject::Macro(slots) => {
if let Some(storage) = self.mapped_slots_for_object_without_copy(id, slots.len())? {
let _ = Self::install_mapped_closure_slots(value, storage);
} else {
let slots: Vec<_> = slots.iter().map(|slot| self.load_value(slot)).collect();
if let Some(storage) = self.mapped_slots_for_object(id, &slots)? {
let _ = Self::install_mapped_closure_slots(value, storage);
} else {
let _ = value.replace_closure_slots(slots);
}
}
}
DumpHeapObject::ByteCode(bc) => {
let _ = value
.with_bytecode_data_mut(|data| {
*data = load_bytecode(self, &bc)?;
Ok::<(), DumpError>(())
})
.transpose()?;
}
DumpHeapObject::Record(items) => {
if let Some(storage) = self.mapped_slots_for_object_without_copy(id, items.len())? {
let _ = Self::install_mapped_record_slots(value, storage);
} else {
let slots: Vec<_> = items.iter().map(|item| self.load_value(item)).collect();
if let Some(storage) = self.mapped_slots_for_object(id, &slots)? {
let _ = Self::install_mapped_record_slots(value, storage);
} else {
let _ = value.replace_record_data(slots);
}
}
}
DumpHeapObject::Marker(marker) => {
let _ = value.with_marker_data_mut(|data| {
data.buffer = marker.buffer.map(|id| BufferId(id.0));
data.bytepos = marker.bytepos;
data.charpos = marker.charpos;
data.insertion_type = marker.insertion_type;
data.marker_id = marker.marker_id;
});
}
DumpHeapObject::Overlay(overlay) => {
let _ = value.with_overlay_data_mut(|data| {
data.plist = self.load_value(&overlay.plist);
data.buffer = overlay.buffer.map(|id| BufferId(id.0));
data.start = overlay.start;
data.end = overlay.end;
data.front_advance = overlay.front_advance;
data.rear_advance = overlay.rear_advance;
});
}
DumpHeapObject::Buffer(_)
| DumpHeapObject::Window(_)
| DumpHeapObject::Frame(_)
| DumpHeapObject::Timer(_)
| DumpHeapObject::Subr { .. }
| DumpHeapObject::Free => {}
}
Ok(())
}
fn populate_value_graph(&mut self, root: &DumpValue) -> Result<(), DumpError> {
let mut stack = vec![root.clone()];
let mut seen = FxHashSet::default();
while let Some(value) = stack.pop() {
let Some(id) = dump_value_heap_ref(&value) else {
continue;
};
if !seen.insert(id.index) {
continue;
}
self.populate_tagged_object(id)?;
match self.state.objects[id.index as usize].clone() {
DumpHeapObject::Cons { car, cdr } => {
stack.push(car);
stack.push(cdr);
}
DumpHeapObject::Vector(items)
| DumpHeapObject::Lambda(items)
| DumpHeapObject::Macro(items)
| DumpHeapObject::Record(items) => {
stack.extend(items);
}
DumpHeapObject::HashTable(ht) => {
for (_, value) in ht.entries {
stack.push(value);
}
for (_, value) in ht.key_snapshots {
stack.push(value);
}
}
DumpHeapObject::Str { text_props, .. } => {
for run in text_props {
stack.push(run.plist);
}
}
DumpHeapObject::ByteCode(bc) => {
stack.extend(bc.constants);
if let Some(arglist) = bc.arglist {
stack.push(arglist);
}
if let Some(env) = bc.env {
stack.push(env);
}
if let Some(doc_form) = bc.doc_form {
stack.push(doc_form);
}
if let Some(interactive) = bc.interactive {
stack.push(interactive);
}
stack.extend(bc.extra_slots);
}
DumpHeapObject::Overlay(overlay) => {
stack.push(overlay.plist);
}
DumpHeapObject::Float(_)
| DumpHeapObject::Marker(_)
| DumpHeapObject::Buffer(_)
| DumpHeapObject::Window(_)
| DumpHeapObject::Frame(_)
| DumpHeapObject::Timer(_)
| DumpHeapObject::Subr { .. }
| DumpHeapObject::Free => {}
}
}
Ok(())
}
pub(crate) fn load_value(&mut self, v: &DumpValue) -> Value {
match v {
DumpValue::Nil => Value::NIL,
DumpValue::True => Value::T,
DumpValue::Int(n) => Value::fixnum(*n),
DumpValue::Float(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Symbol(s) => Value::symbol(load_sym_id(s)),
DumpValue::Str(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Cons(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Vector(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Record(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::HashTable(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Lambda(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Macro(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Subr(s) => {
let name_id = load_name_id(s);
if let Some(sym_id) = intern::canonical_symbol_for_name(name_id) {
Value::subr_from_sym_id(sym_id)
} else {
let name = intern::resolve_name(name_id);
Value::subr_from_sym_id(intern::intern(name))
}
}
DumpValue::ByteCode(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Marker(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Overlay(id) => self.heap_ref_to_value(tagged_heap_ref(id)),
DumpValue::Buffer(bid) => self.load_cached_buffer(bid.0),
DumpValue::Window(w) => self.load_cached_window(*w),
DumpValue::Frame(f) => self.load_cached_frame(*f),
DumpValue::Timer(t) => self.load_cached_timer(*t),
DumpValue::Bignum(text) => Value::make_integer_from_str_or_zero(text),
DumpValue::Unbound => Value::UNBOUND,
}
}
pub(crate) fn load_opt_value(&mut self, v: &Option<DumpValue>) -> Option<Value> {
v.as_ref().map(|value| self.load_value(value))
}
}
fn dump_value_heap_ref(value: &DumpValue) -> Option<TaggedHeapRef> {
match value {
DumpValue::Float(id)
| DumpValue::Str(id)
| DumpValue::Cons(id)
| DumpValue::Vector(id)
| DumpValue::Record(id)
| DumpValue::HashTable(id)
| DumpValue::Lambda(id)
| DumpValue::Macro(id)
| DumpValue::ByteCode(id)
| DumpValue::Marker(id)
| DumpValue::Overlay(id) => Some(tagged_heap_ref(id)),
DumpValue::Nil
| DumpValue::True
| DumpValue::Int(_)
| DumpValue::Symbol(_)
| DumpValue::Subr(_)
| DumpValue::Buffer(_)
| DumpValue::Window(_)
| DumpValue::Frame(_)
| DumpValue::Timer(_)
| DumpValue::Bignum(_)
| DumpValue::Unbound => None,
}
}
fn dump_heap_ref(id: TaggedHeapRef) -> DumpHeapRef {
DumpHeapRef { index: id.index }
}
fn tagged_heap_ref(id: &DumpHeapRef) -> TaggedHeapRef {
TaggedHeapRef { index: id.index }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn preload_tagged_heap_handles_deep_cons_chains_without_recursive_population() {
crate::test_utils::init_test_tracing();
let chain_len = 4096usize;
let objects = (0..chain_len)
.map(|index| DumpHeapObject::Cons {
car: DumpValue::Int(index as i64),
cdr: if index + 1 == chain_len {
DumpValue::Nil
} else {
DumpValue::Cons(DumpHeapRef {
index: (index + 1) as u32,
})
},
})
.collect();
let heap = DumpTaggedHeap {
objects,
mapped_cons: Vec::new(),
mapped_floats: Vec::new(),
mapped_strings: Vec::new(),
mapped_veclikes: Vec::new(),
mapped_slots: Vec::new(),
};
let mut decoder = LoadDecoder::new(&heap);
decoder
.preload_tagged_heap()
.expect("deep cons chain should preload without recursive overflow");
let mut cursor = decoder.load_value(&DumpValue::Cons(DumpHeapRef { index: 0 }));
for index in 0..chain_len {
assert_eq!(cursor.cons_car(), Value::fixnum(index as i64));
cursor = cursor.cons_cdr();
}
assert!(cursor.is_nil());
}
#[test]
fn mapped_cons_raw_words_are_loader_source_of_truth_when_no_remap_needed() {
crate::test_utils::init_test_tracing();
let mut runtime_heap = Box::new(crate::tagged::gc::TaggedHeap::new());
crate::tagged::gc::set_tagged_heap(&mut runtime_heap);
let heap = DumpTaggedHeap {
objects: vec![DumpHeapObject::Cons {
car: DumpValue::Int(1),
cdr: DumpValue::Int(2),
}],
mapped_cons: vec![Some(DumpConsSpan { offset: 0 })],
mapped_floats: Vec::new(),
mapped_strings: Vec::new(),
mapped_veclikes: Vec::new(),
mapped_slots: Vec::new(),
};
let mut bytes = vec![0u8; std::mem::size_of::<ConsCell>()];
write_raw_word(&mut bytes, 0, Value::fixnum(99).bits());
write_raw_word(
&mut bytes,
std::mem::size_of::<TaggedValue>(),
Value::fixnum(100).bits(),
);
let mapped = MappedHeapView::from_mut_slice(&mut bytes);
let mut decoder = LoadDecoder::new_with_mapped_heap(&heap, Some(mapped));
decoder.preload_tagged_heap().unwrap();
let value = decoder.load_value(&DumpValue::Cons(DumpHeapRef { index: 0 }));
assert_eq!(value.cons_car(), Value::fixnum(99));
assert_eq!(value.cons_cdr(), Value::fixnum(100));
}
#[test]
fn mapped_vector_raw_slots_are_loader_source_of_truth_when_no_remap_needed() {
crate::test_utils::init_test_tracing();
let mut runtime_heap = Box::new(crate::tagged::gc::TaggedHeap::new());
crate::tagged::gc::set_tagged_heap(&mut runtime_heap);
let slot_offset = std::mem::size_of::<VectorObj>();
let heap = DumpTaggedHeap {
objects: vec![DumpHeapObject::Vector(vec![
DumpValue::Int(1),
DumpValue::Int(2),
])],
mapped_cons: Vec::new(),
mapped_floats: Vec::new(),
mapped_strings: Vec::new(),
mapped_veclikes: vec![Some(DumpVecLikeSpan {
offset: 0,
len: std::mem::size_of::<VectorObj>() as u64,
})],
mapped_slots: vec![Some(DumpSlotSpan {
offset: slot_offset as u64,
len: 2,
})],
};
let mut bytes = vec![0u8; slot_offset + 2 * std::mem::size_of::<TaggedValue>()];
write_raw_word(&mut bytes, slot_offset, Value::fixnum(77).bits());
write_raw_word(
&mut bytes,
slot_offset + std::mem::size_of::<TaggedValue>(),
Value::fixnum(88).bits(),
);
let mapped = MappedHeapView::from_mut_slice(&mut bytes);
let mut decoder = LoadDecoder::new_with_mapped_heap(&heap, Some(mapped));
decoder.preload_tagged_heap().unwrap();
let value = decoder.load_value(&DumpValue::Vector(DumpHeapRef { index: 0 }));
let slots = value.as_vector_data().unwrap();
assert_eq!(slots.as_slice(), &[Value::fixnum(77), Value::fixnum(88)]);
}
fn write_raw_word(bytes: &mut [u8], offset: usize, word: usize) {
bytes[offset..offset + std::mem::size_of::<usize>()].copy_from_slice(&word.to_ne_bytes());
}
}
pub(crate) fn dump_sym_id(id: SymId) -> DumpSymId {
DumpSymId(id.0)
}
pub(crate) fn dump_name_id(id: NameId) -> DumpNameId {
DumpNameId(id.0)
}
fn dump_lisp_string(string: &LispString) -> DumpLispString {
DumpLispString {
data: string.as_bytes().to_vec(),
size: string.schars(),
size_byte: if string.is_multibyte() {
string.sbytes() as i64
} else {
-1
},
}
}
pub(super) fn load_lisp_string(dump: &DumpLispString) -> LispString {
LispString::from_dump(dump.data.clone(), dump.size, dump.size_byte)
}
pub(crate) fn dump_op(op: &Op) -> DumpOp {
match *op {
Op::Constant(n) => DumpOp::Constant(n),
Op::Nil => DumpOp::Nil,
Op::True => DumpOp::True,
Op::Pop => DumpOp::Pop,
Op::Dup => DumpOp::Dup,
Op::StackRef(n) => DumpOp::StackRef(n),
Op::StackSet(n) => DumpOp::StackSet(n),
Op::DiscardN(n) => DumpOp::DiscardN(n),
Op::VarRef(n) => DumpOp::VarRef(n),
Op::VarSet(n) => DumpOp::VarSet(n),
Op::VarBind(n) => DumpOp::VarBind(n),
Op::Unbind(n) => DumpOp::Unbind(n),
Op::Call(n) => DumpOp::Call(n),
Op::Apply(n) => DumpOp::Apply(n),
Op::Goto(n) => DumpOp::Goto(n),
Op::GotoIfNil(n) => DumpOp::GotoIfNil(n),
Op::GotoIfNotNil(n) => DumpOp::GotoIfNotNil(n),
Op::GotoIfNilElsePop(n) => DumpOp::GotoIfNilElsePop(n),
Op::GotoIfNotNilElsePop(n) => DumpOp::GotoIfNotNilElsePop(n),
Op::Switch => DumpOp::Switch,
Op::Return => DumpOp::Return,
Op::Add => DumpOp::Add,
Op::Sub => DumpOp::Sub,
Op::Mul => DumpOp::Mul,
Op::Div => DumpOp::Div,
Op::Rem => DumpOp::Rem,
Op::Add1 => DumpOp::Add1,
Op::Sub1 => DumpOp::Sub1,
Op::Negate => DumpOp::Negate,
Op::Eqlsign => DumpOp::Eqlsign,
Op::Gtr => DumpOp::Gtr,
Op::Lss => DumpOp::Lss,
Op::Leq => DumpOp::Leq,
Op::Geq => DumpOp::Geq,
Op::Max => DumpOp::Max,
Op::Min => DumpOp::Min,
Op::Car => DumpOp::Car,
Op::Cdr => DumpOp::Cdr,
Op::Cons => DumpOp::Cons,
Op::List(n) => DumpOp::List(n),
Op::Length => DumpOp::Length,
Op::Nth => DumpOp::Nth,
Op::Nthcdr => DumpOp::Nthcdr,
Op::Setcar => DumpOp::Setcar,
Op::Setcdr => DumpOp::Setcdr,
Op::CarSafe => DumpOp::CarSafe,
Op::CdrSafe => DumpOp::CdrSafe,
Op::Elt => DumpOp::Elt,
Op::Nconc => DumpOp::Nconc,
Op::Nreverse => DumpOp::Nreverse,
Op::Member => DumpOp::Member,
Op::Memq => DumpOp::Memq,
Op::Assq => DumpOp::Assq,
Op::Symbolp => DumpOp::Symbolp,
Op::Consp => DumpOp::Consp,
Op::Stringp => DumpOp::Stringp,
Op::Listp => DumpOp::Listp,
Op::Integerp => DumpOp::Integerp,
Op::Numberp => DumpOp::Numberp,
Op::Null => DumpOp::Null,
Op::Not => DumpOp::Not,
Op::Eq => DumpOp::Eq,
Op::Equal => DumpOp::Equal,
Op::Concat(n) => DumpOp::Concat(n),
Op::Substring => DumpOp::Substring,
Op::StringEqual => DumpOp::StringEqual,
Op::StringLessp => DumpOp::StringLessp,
Op::Aref => DumpOp::Aref,
Op::Aset => DumpOp::Aset,
Op::SymbolValue => DumpOp::SymbolValue,
Op::SymbolFunction => DumpOp::SymbolFunction,
Op::Set => DumpOp::Set,
Op::Fset => DumpOp::Fset,
Op::Get => DumpOp::Get,
Op::Put => DumpOp::Put,
Op::PushConditionCase(n) => DumpOp::PushConditionCase(n),
Op::PushConditionCaseRaw(n) => DumpOp::PushConditionCaseRaw(n),
Op::PushCatch(n) => DumpOp::PushCatch(n),
Op::PopHandler => DumpOp::PopHandler,
Op::UnwindProtectPop => DumpOp::UnwindProtectPop,
Op::Throw => DumpOp::Throw,
Op::SaveCurrentBuffer => DumpOp::SaveCurrentBuffer,
Op::SaveExcursion => DumpOp::SaveExcursion,
Op::SaveRestriction => DumpOp::SaveRestriction,
Op::SaveWindowExcursion => DumpOp::SaveWindowExcursion,
Op::MakeClosure(n) => DumpOp::MakeClosure(n),
Op::CallBuiltin(a, b) => DumpOp::CallBuiltin(a, b),
Op::CallBuiltinSym(sym, b) => DumpOp::CallBuiltinSym(dump_sym_id(sym), b),
}
}
pub(crate) fn dump_lambda_params(p: &LambdaParams) -> DumpLambdaParams {
DumpLambdaParams {
required: p.required.iter().map(|s| dump_sym_id(*s)).collect(),
optional: p.optional.iter().map(|s| dump_sym_id(*s)).collect(),
rest: p.rest.map(|s| dump_sym_id(s)),
}
}
pub(crate) fn dump_bytecode(
encoder: &mut DumpEncoder,
bc: &ByteCodeFunction,
) -> DumpByteCodeFunction {
DumpByteCodeFunction {
ops: bc.ops.iter().map(dump_op).collect(),
constants: bc
.constants
.iter()
.map(|value| encoder.dump_value(value))
.collect(),
max_stack: bc.max_stack,
params: dump_lambda_params(&bc.params),
arglist: Some(encoder.dump_value(&bc.arglist)),
lexical: bc.lexical,
env: encoder.dump_opt_value(&bc.env),
gnu_byte_offset_map: bc.gnu_byte_offset_map.as_ref().map(|map| {
map.iter()
.map(|(byte_off, instr_idx)| (*byte_off as u32, *instr_idx as u32))
.collect()
}),
gnu_bytecode_bytes: bc.gnu_bytecode_bytes.clone(),
docstring: bc.docstring.as_ref().map(dump_lisp_string),
doc_form: encoder.dump_opt_value(&bc.doc_form),
interactive: encoder.dump_opt_value(&bc.interactive),
closure_slot_count: bc.observable_closure_slot_count(),
extra_slots: bc
.extra_slots
.iter()
.map(|value| encoder.dump_value(value))
.collect(),
}
}
pub(crate) fn dump_hash_key(encoder: &mut DumpEncoder, k: &HashKey) -> DumpHashKey {
match k {
HashKey::Nil => DumpHashKey::Nil,
HashKey::True => DumpHashKey::True,
HashKey::Int(n) => DumpHashKey::Int(*n),
HashKey::Float(bits) => DumpHashKey::Float(*bits),
HashKey::FloatEq(bits, id) => DumpHashKey::FloatEq(*bits, *id),
HashKey::Symbol(s) => DumpHashKey::Symbol(dump_sym_id(*s)),
HashKey::Keyword(s) => DumpHashKey::Keyword(dump_sym_id(*s)),
HashKey::Char(c) => DumpHashKey::Char(*c),
HashKey::Window(w) => DumpHashKey::Window(*w),
HashKey::Frame(f) => DumpHashKey::Frame(*f),
HashKey::Ptr(p) => {
let value = TaggedValue(*p);
if value.is_heap_object() {
let id = encoder.value_to_heap_ref(&value);
DumpHashKey::HeapRef(id.index)
} else {
DumpHashKey::Ptr(*p as u64)
}
}
HashKey::EqualCons(a, b) => DumpHashKey::EqualCons(
Box::new(dump_hash_key(encoder, a)),
Box::new(dump_hash_key(encoder, b)),
),
HashKey::EqualVec(v) => {
DumpHashKey::EqualVec(v.iter().map(|key| dump_hash_key(encoder, key)).collect())
}
HashKey::SymbolWithPos(sym, pos) => DumpHashKey::SymbolWithPos(
Box::new(dump_hash_key(encoder, sym)),
Box::new(dump_hash_key(encoder, pos)),
),
HashKey::Cycle(index) => DumpHashKey::Cycle(*index),
HashKey::Text(text) => DumpHashKey::Text(text.clone()),
}
}
pub(crate) fn dump_hash_table_test(t: &HashTableTest) -> DumpHashTableTest {
match t {
HashTableTest::Eq => DumpHashTableTest::Eq,
HashTableTest::Eql => DumpHashTableTest::Eql,
HashTableTest::Equal => DumpHashTableTest::Equal,
}
}
pub(crate) fn dump_hash_table_weakness(w: &HashTableWeakness) -> DumpHashTableWeakness {
match w {
HashTableWeakness::Key => DumpHashTableWeakness::Key,
HashTableWeakness::Value => DumpHashTableWeakness::Value,
HashTableWeakness::KeyOrValue => DumpHashTableWeakness::KeyOrValue,
HashTableWeakness::KeyAndValue => DumpHashTableWeakness::KeyAndValue,
}
}
pub(crate) fn dump_hash_table(encoder: &mut DumpEncoder, ht: &LispHashTable) -> DumpLispHashTable {
DumpLispHashTable {
test: dump_hash_table_test(&ht.test),
test_name: ht.test_name.map(|s| dump_sym_id(s)),
size: ht.size,
weakness: ht.weakness.as_ref().map(dump_hash_table_weakness),
rehash_size: ht.rehash_size,
rehash_threshold: ht.rehash_threshold,
entries: ht
.data
.iter()
.map(|(k, v)| (dump_hash_key(encoder, k), encoder.dump_value(v)))
.collect(),
key_snapshots: ht
.key_snapshots
.iter()
.map(|(k, v)| (dump_hash_key(encoder, k), encoder.dump_value(v)))
.collect(),
insertion_order: ht
.insertion_order
.iter()
.map(|key| dump_hash_key(encoder, key))
.collect(),
}
}
fn dump_closure_slots(encoder: &mut DumpEncoder, value: Value) -> Vec<DumpValue> {
value
.closure_slots()
.map(|slots| slots.iter().map(|slot| encoder.dump_value(slot)).collect())
.unwrap_or_default()
}
fn dump_heap_object_from_value(encoder: &mut DumpEncoder, value: Value) -> DumpHeapObject {
match value.kind() {
ValueKind::Cons => DumpHeapObject::Cons {
car: encoder.dump_value(&value.cons_car()),
cdr: encoder.dump_value(&value.cons_cdr()),
},
ValueKind::String => {
let string = value.as_lisp_string().expect("string");
DumpHeapObject::Str {
data: DumpByteData::owned(string.as_bytes().to_vec()),
size: string.schars(),
size_byte: if string.is_multibyte() {
string.sbytes() as i64
} else {
-1
},
text_props: get_string_text_properties_for_value(value)
.unwrap_or_default()
.into_iter()
.map(|run| DumpStringTextPropertyRun {
start: run.start,
end: run.end,
plist: encoder.dump_value(&run.plist),
})
.collect(),
}
}
ValueKind::Float => DumpHeapObject::Float(value.xfloat()),
ValueKind::Veclike(VecLikeType::Vector) => DumpHeapObject::Vector(
value
.as_vector_data()
.expect("vector")
.iter()
.map(|item| encoder.dump_value(item))
.collect(),
),
ValueKind::Veclike(VecLikeType::HashTable) => DumpHeapObject::HashTable(dump_hash_table(
encoder,
value.as_hash_table().expect("hash-table"),
)),
ValueKind::Veclike(VecLikeType::Lambda) => {
DumpHeapObject::Lambda(dump_closure_slots(encoder, value))
}
ValueKind::Veclike(VecLikeType::Macro) => {
DumpHeapObject::Macro(dump_closure_slots(encoder, value))
}
ValueKind::Veclike(VecLikeType::ByteCode) => DumpHeapObject::ByteCode(dump_bytecode(
encoder,
value.get_bytecode_data().expect("bytecode"),
)),
ValueKind::Veclike(VecLikeType::Record) => DumpHeapObject::Record(
value
.as_record_data()
.expect("record")
.iter()
.map(|item| encoder.dump_value(item))
.collect(),
),
ValueKind::Veclike(VecLikeType::Overlay) => DumpHeapObject::Overlay(dump_overlay(
encoder,
value.as_overlay_data().expect("overlay"),
)),
ValueKind::Veclike(VecLikeType::Marker) => {
DumpHeapObject::Marker(dump_marker_object(value.as_marker_data().expect("marker")))
}
ValueKind::Veclike(VecLikeType::Buffer) => {
DumpHeapObject::Buffer(DumpBufferId(value.as_buffer_id().expect("buffer").0))
}
ValueKind::Veclike(VecLikeType::Window) => {
DumpHeapObject::Window(value.as_window_id().expect("window"))
}
ValueKind::Veclike(VecLikeType::Frame) => {
DumpHeapObject::Frame(value.as_frame_id().expect("frame"))
}
ValueKind::Veclike(VecLikeType::Timer) => {
DumpHeapObject::Timer(value.as_timer_id().expect("timer"))
}
ValueKind::Veclike(VecLikeType::Subr) => {
let ptr = value.as_veclike_ptr().expect("subr") as *const SubrObj;
let subr = unsafe { &*ptr };
DumpHeapObject::Subr {
name: dump_name_id(subr.name),
min_args: subr.min_args,
max_args: subr.max_args,
}
}
_ => DumpHeapObject::Free,
}
}
pub(crate) fn dump_symbol_table() -> DumpSymbolTable {
let dumped = intern::dump_runtime_interner();
DumpSymbolTable {
names: dumped.names,
symbols: dumped
.symbol_names
.into_iter()
.zip(dumped.canonical)
.map(|(name, canonical)| DumpSymbolEntry {
name: DumpNameId(name),
canonical,
})
.collect(),
}
}
pub(crate) fn dump_symbol_data(
encoder: &mut DumpEncoder,
sd: &LispSymbol,
dynamic_default: Option<Option<Value>>,
) -> DumpSymbolData {
use crate::emacs_core::symbol::{SymbolInterned, SymbolRedirect};
let redirect = sd.flags.redirect();
let val = match redirect {
SymbolRedirect::Plainval => {
let v = match dynamic_default {
Some(Some(value)) => value,
Some(None) => Value::UNBOUND,
None => unsafe { sd.val.plain },
};
DumpSymbolVal::Plain(encoder.dump_value(&v))
}
SymbolRedirect::Varalias => {
let target = unsafe { sd.val.alias };
DumpSymbolVal::Alias(dump_sym_id(target))
}
SymbolRedirect::Localized => {
let (default_val, local_if_set) = unsafe {
let blv = &*sd.val.blv;
let default_val = blv.defcell.cons_cdr();
(default_val, blv.local_if_set)
};
let default_val = match dynamic_default {
Some(Some(value)) => value,
Some(None) => Value::UNBOUND,
None => default_val,
};
DumpSymbolVal::Localized {
default: encoder.dump_value(&default_val),
local_if_set,
}
}
SymbolRedirect::Forwarded => {
DumpSymbolVal::Forwarded
}
};
DumpSymbolData {
redirect: redirect as u8,
trapped_write: sd.flags.trapped_write() as u8,
interned: sd.flags.interned() as u8,
declared_special: sd.flags.declared_special(),
val,
function: encoder.dump_value(&sd.function),
plist: encoder.dump_value(&sd.plist),
}
}
fn dynamic_default_for_dump(eval: &Context, sym_id: SymId) -> Option<Option<Value>> {
eval.specpdl.iter().find_map(|binding| match binding {
crate::emacs_core::eval::SpecBinding::Let {
sym_id: binding_sym,
old_value,
}
| crate::emacs_core::eval::SpecBinding::LetDefault {
sym_id: binding_sym,
old_value,
} if *binding_sym == sym_id => Some(*old_value),
_ => None,
})
}
pub(crate) fn dump_obarray(encoder: &mut DumpEncoder, eval: &Context) -> DumpObarray {
DumpObarray {
symbols: eval
.obarray
.iter_symbols()
.map(|(id, sd)| {
(
dump_sym_id(id),
dump_symbol_data(encoder, sd, dynamic_default_for_dump(eval, id)),
)
})
.collect(),
global_members: eval.obarray.global_member_ids().map(dump_sym_id).collect(),
function_unbound: eval
.obarray
.function_unbound_ids()
.map(dump_sym_id)
.collect(),
function_epoch: eval.obarray.function_epoch(),
}
}
fn dump_runtime_binding_value(
encoder: &mut DumpEncoder,
value: &RuntimeBindingValue,
) -> DumpRuntimeBindingValue {
match value {
RuntimeBindingValue::Bound(value) => {
DumpRuntimeBindingValue::Bound(encoder.dump_value(value))
}
RuntimeBindingValue::Void => DumpRuntimeBindingValue::Void,
}
}
fn load_runtime_binding_value(
decoder: &mut LoadDecoder,
value: &DumpRuntimeBindingValue,
) -> RuntimeBindingValue {
match value {
DumpRuntimeBindingValue::Bound(value) => {
RuntimeBindingValue::Bound(decoder.load_value(value))
}
DumpRuntimeBindingValue::Void => RuntimeBindingValue::Void,
}
}
pub(crate) fn dump_ordered_sym_map(
encoder: &mut DumpEncoder,
m: &OrderedRuntimeBindingMap,
) -> DumpOrderedSymMap {
DumpOrderedSymMap {
entries: m
.iter()
.map(|(k, v)| (dump_sym_id(*k), dump_runtime_binding_value(encoder, v)))
.collect(),
}
}
fn dump_property_interval(
encoder: &mut DumpEncoder,
pi: &PropertyInterval,
) -> DumpPropertyInterval {
DumpPropertyInterval {
start: pi.start,
end: pi.end,
properties: pi
.properties
.iter()
.map(|(k, v)| (encoder.dump_value(k), encoder.dump_value(v)))
.collect(),
}
}
fn dump_text_property_table(
encoder: &mut DumpEncoder,
tpt: &TextPropertyTable,
) -> DumpTextPropertyTable {
DumpTextPropertyTable {
intervals: tpt
.dump_intervals()
.into_iter()
.map(|iv| dump_property_interval(encoder, &iv))
.collect(),
}
}
fn dump_overlay(encoder: &mut DumpEncoder, o: &Overlay) -> DumpOverlay {
DumpOverlay {
plist: encoder.dump_value(&o.plist),
buffer: o.buffer.map(|id| DumpBufferId(id.0)),
start: o.start,
end: o.end,
front_advance: o.front_advance,
rear_advance: o.rear_advance,
}
}
fn dump_marker_object(marker: &crate::heap_types::MarkerData) -> DumpMarker {
DumpMarker {
buffer: marker.buffer.map(|id| DumpBufferId(id.0)),
insertion_type: marker.insertion_type,
marker_id: marker.marker_id,
bytepos: marker.bytepos,
charpos: marker.charpos,
}
}
fn dump_overlay_list(encoder: &mut DumpEncoder, ol: &OverlayList) -> DumpOverlayList {
DumpOverlayList {
overlays: ol
.dump_overlays()
.iter()
.filter_map(|v| v.as_overlay_data())
.map(|data| dump_overlay(encoder, data))
.collect(),
}
}
fn dump_buffer(encoder: &mut DumpEncoder, buf: &Buffer) -> DumpBuffer {
let is_shared_text_owner = buf.base_buffer.is_none();
DumpBuffer {
id: DumpBufferId(buf.id.0),
name_lisp: buf.name_value().as_lisp_string().map(dump_lisp_string),
name: None,
base_buffer: buf.base_buffer.map(|id| DumpBufferId(id.0)),
text: DumpGapBuffer {
text: buf.text.dump_text(),
},
pt: buf.pt_byte,
pt_char: Some(buf.pt),
mark: buf.mark_byte,
mark_char: buf.mark_char(),
begv: buf.begv_byte,
begv_char: Some(buf.begv),
zv: buf.zv_byte,
zv_char: Some(buf.zv),
modified: buf.is_modified(),
modified_tick: buf.modified_tick(),
chars_modified_tick: buf.chars_modified_tick(),
save_modified_tick: Some(buf.save_modified_tick()),
autosave_modified_tick: Some(buf.autosave_modified_tick),
last_window_start: Some(buf.last_window_start),
read_only: buf.get_read_only(),
multibyte: buf.get_multibyte(),
file_name_lisp: buf.file_name_lisp_string().map(dump_lisp_string),
file_name: None,
auto_save_file_name_lisp: buf.auto_save_file_name_lisp_string().map(dump_lisp_string),
auto_save_file_name: None,
markers: if is_shared_text_owner {
let mut out = Vec::new();
buf.text.chain_walk_data(|data| {
out.push(DumpMarker {
buffer: data.buffer.map(|id| DumpBufferId(id.0)),
insertion_type: data.insertion_type,
marker_id: data.marker_id,
bytepos: data.bytepos,
charpos: data.charpos,
});
});
out
} else {
Vec::new()
},
state_pt_marker: buf.state_markers.map(|markers| markers.pt_marker),
state_begv_marker: buf.state_markers.map(|markers| markers.begv_marker),
state_zv_marker: buf.state_markers.map(|markers| markers.zv_marker),
properties_syms: buf
.ordered_buffer_local_bindings()
.into_iter()
.map(|(sym_id, value)| {
(
dump_sym_id(sym_id),
dump_runtime_binding_value(encoder, &value),
)
})
.collect(),
properties: Vec::new(),
local_binding_syms: buf
.ordered_buffer_local_names()
.into_iter()
.map(dump_sym_id)
.collect(),
local_binding_names: Vec::new(),
local_map: encoder.dump_value(&buf.local_map()),
text_props: if is_shared_text_owner {
dump_text_property_table(encoder, &buf.text.text_props_snapshot())
} else {
dump_text_property_table(encoder, &TextPropertyTable::new())
},
overlays: dump_overlay_list(encoder, &buf.overlays),
undo_list: None,
slots: buf
.slots
.iter()
.map(|slot| encoder.dump_value(slot))
.collect(),
local_flags: buf.local_flags,
local_var_alist: encoder.dump_value(&buf.local_var_alist),
}
}
pub(crate) fn dump_buffer_manager(
encoder: &mut DumpEncoder,
bm: &BufferManager,
) -> DumpBufferManager {
DumpBufferManager {
buffers: bm
.dump_buffers()
.iter()
.map(|(id, buf)| (DumpBufferId(id.0), dump_buffer(encoder, buf)))
.collect(),
current: bm.dump_current().map(|id| DumpBufferId(id.0)),
next_id: bm.dump_next_id(),
next_marker_id: bm.dump_next_marker_id(),
buffer_defaults: bm
.buffer_defaults
.iter()
.map(|value| encoder.dump_value(value))
.collect(),
}
}
pub(crate) fn dump_autoload_manager(
encoder: &mut DumpEncoder,
am: &AutoloadManager,
) -> DumpAutoloadManager {
DumpAutoloadManager {
entries_syms: am
.dump_entries()
.iter()
.map(|(k, v)| {
(
dump_sym_id(*k),
DumpAutoloadEntry {
file: dump_lisp_string(&v.file),
docstring: v.docstring.as_ref().map(dump_lisp_string),
interactive: v.interactive,
autoload_type: match v.autoload_type {
AutoloadType::Function => DumpAutoloadType::Function,
AutoloadType::Macro => DumpAutoloadType::Macro,
AutoloadType::Keymap => DumpAutoloadType::Keymap,
},
},
)
})
.collect(),
entries: Vec::new(),
after_load_lisp: am
.dump_after_load()
.iter()
.map(|(k, v)| {
(
dump_lisp_string(k.as_lisp_string()),
v.iter().map(|value| encoder.dump_value(value)).collect(),
)
})
.collect(),
after_load: Vec::new(),
loaded_files: am
.dump_loaded_files()
.iter()
.map(dump_lisp_string)
.collect(),
obsolete_functions_syms: am
.dump_obsolete_functions()
.iter()
.map(|(name, (new_name, when))| {
(
dump_sym_id(*name),
(dump_lisp_string(new_name), dump_lisp_string(when)),
)
})
.collect(),
obsolete_functions: Vec::new(),
obsolete_variables_syms: am
.dump_obsolete_variables()
.iter()
.map(|(name, (new_name, when))| {
(
dump_sym_id(*name),
(dump_lisp_string(new_name), dump_lisp_string(when)),
)
})
.collect(),
obsolete_variables: Vec::new(),
}
}
pub(crate) fn dump_custom_manager(_cm: &CustomManager) -> DumpCustomManager {
DumpCustomManager {
auto_buffer_local_syms: Vec::new(),
auto_buffer_local: Vec::new(),
}
}
fn dump_font_lock_keyword(kw: &FontLockKeyword) -> DumpFontLockKeyword {
DumpFontLockKeyword {
pattern_lisp: Some(dump_lisp_string(&kw.pattern)),
pattern: None,
face_sym: Some(dump_sym_id(kw.face)),
face: None,
group: kw.group,
override_: kw.override_,
laxmatch: kw.laxmatch,
}
}
fn dump_font_lock_defaults(fld: &FontLockDefaults) -> DumpFontLockDefaults {
DumpFontLockDefaults {
keywords: fld.keywords.iter().map(dump_font_lock_keyword).collect(),
case_fold: fld.case_fold,
syntax_table_lisp: fld.syntax_table.as_ref().map(dump_lisp_string),
syntax_table: None,
}
}
fn dump_mode_custom_type(encoder: &mut DumpEncoder, ct: &ModeCustomType) -> DumpModeCustomType {
match ct {
ModeCustomType::Boolean => DumpModeCustomType::Boolean,
ModeCustomType::Integer => DumpModeCustomType::Integer,
ModeCustomType::Float => DumpModeCustomType::Float,
ModeCustomType::String => DumpModeCustomType::String,
ModeCustomType::Symbol => DumpModeCustomType::Symbol,
ModeCustomType::Sexp => DumpModeCustomType::Sexp,
ModeCustomType::Choice(choices) => DumpModeCustomType::Choice(
choices
.iter()
.map(|(s, v)| (s.clone(), encoder.dump_value(v)))
.collect(),
),
ModeCustomType::List(inner) => {
DumpModeCustomType::List(Box::new(dump_mode_custom_type(encoder, inner)))
}
ModeCustomType::Alist(k, v) => DumpModeCustomType::Alist(
Box::new(dump_mode_custom_type(encoder, k)),
Box::new(dump_mode_custom_type(encoder, v)),
),
ModeCustomType::Plist(k, v) => DumpModeCustomType::Plist(
Box::new(dump_mode_custom_type(encoder, k)),
Box::new(dump_mode_custom_type(encoder, v)),
),
ModeCustomType::Color => DumpModeCustomType::Color,
ModeCustomType::Face => DumpModeCustomType::Face,
ModeCustomType::File => DumpModeCustomType::File,
ModeCustomType::Directory => DumpModeCustomType::Directory,
ModeCustomType::Function => DumpModeCustomType::Function,
ModeCustomType::Variable => DumpModeCustomType::Variable,
ModeCustomType::Hook => DumpModeCustomType::Hook,
ModeCustomType::Coding => DumpModeCustomType::Coding,
}
}
pub(crate) fn dump_mode_registry(encoder: &mut DumpEncoder, mr: &ModeRegistry) -> DumpModeRegistry {
DumpModeRegistry {
major_modes: mr
.dump_major_modes()
.iter()
.map(|(k, m)| {
(
dump_sym_id(*k),
DumpMajorMode {
pretty_name: dump_lisp_string(&m.pretty_name),
parent: encoder.dump_opt_value(&m.parent),
mode_hook: encoder.dump_value(&m.mode_hook),
keymap_name: encoder.dump_opt_value(&m.keymap_name),
syntax_table_name: encoder.dump_opt_value(&m.syntax_table_name),
abbrev_table_name: encoder.dump_opt_value(&m.abbrev_table_name),
font_lock: m.font_lock.as_ref().map(dump_font_lock_defaults),
body: encoder.dump_opt_value(&m.body),
},
)
})
.collect(),
minor_modes: mr
.dump_minor_modes()
.iter()
.map(|(k, m)| {
(
dump_sym_id(*k),
DumpMinorMode {
lighter: m.lighter.as_ref().map(dump_lisp_string),
keymap_name: encoder.dump_opt_value(&m.keymap_name),
global: m.global,
body: encoder.dump_opt_value(&m.body),
},
)
})
.collect(),
buffer_major_modes: mr
.dump_buffer_major_modes()
.iter()
.map(|(k, v)| (*k, encoder.dump_value(v)))
.collect(),
buffer_minor_modes: mr
.dump_buffer_minor_modes()
.iter()
.map(|(k, v)| {
(
*k,
v.iter().map(|value| encoder.dump_value(value)).collect(),
)
})
.collect(),
global_minor_modes: mr
.dump_global_minor_modes()
.iter()
.map(|value| encoder.dump_value(value))
.collect(),
auto_mode_alist: Vec::new(),
auto_mode_alist_lisp: mr
.dump_auto_mode_alist()
.iter()
.map(|(pattern, value)| (dump_lisp_string(pattern), encoder.dump_value(value)))
.collect(),
custom_variables: mr
.dump_custom_variables()
.iter()
.map(|(k, cv)| {
(
dump_sym_id(*k),
DumpModeCustomVariable {
default_value: encoder.dump_value(&cv.default_value),
doc: cv.doc.as_ref().map(dump_lisp_string),
custom_type: dump_mode_custom_type(encoder, &cv.type_),
group: encoder.dump_opt_value(&cv.group),
set_function: encoder.dump_opt_value(&cv.set_function),
get_function: encoder.dump_opt_value(&cv.get_function),
tag: cv.tag.as_ref().map(dump_lisp_string),
},
)
})
.collect(),
custom_groups: mr
.dump_custom_groups()
.iter()
.map(|(k, g)| {
(
dump_sym_id(*k),
DumpModeCustomGroup {
doc: g.doc.as_ref().map(dump_lisp_string),
parent: encoder.dump_opt_value(&g.parent),
members: g
.members
.iter()
.map(|value| encoder.dump_value(value))
.collect(),
},
)
})
.collect(),
fundamental_mode: encoder.dump_value(&mr.dump_fundamental_mode()),
}
}
fn dump_eol_type(e: &EolType) -> DumpEolType {
match e {
EolType::Unix => DumpEolType::Unix,
EolType::Dos => DumpEolType::Dos,
EolType::Mac => DumpEolType::Mac,
EolType::Undecided => DumpEolType::Undecided,
}
}
pub(crate) fn dump_coding_system_manager(
encoder: &mut DumpEncoder,
csm: &CodingSystemManager,
) -> DumpCodingSystemManager {
DumpCodingSystemManager {
systems_syms: csm
.systems
.iter()
.map(|(k, v)| {
(
dump_sym_id(*k),
DumpCodingSystemInfo {
name_sym: Some(dump_sym_id(v.name)),
name: None,
coding_type_sym: Some(dump_sym_id(v.coding_type)),
coding_type: None,
mnemonic: v.mnemonic,
eol_type: dump_eol_type(&v.eol_type),
ascii_compatible_p: v.ascii_compatible_p,
charset_list_syms: v
.charset_list
.iter()
.map(|id| dump_sym_id(*id))
.collect(),
charset_list: Vec::new(),
post_read_conversion_sym: v.post_read_conversion.map(dump_sym_id),
post_read_conversion: None,
pre_write_conversion_sym: v.pre_write_conversion.map(dump_sym_id),
pre_write_conversion: None,
default_char: v.default_char,
for_unibyte: v.for_unibyte,
properties_syms: v
.properties
.iter()
.map(|(k, v)| (dump_sym_id(*k), encoder.dump_value(v)))
.collect(),
properties: Vec::new(),
int_properties: v
.int_properties
.iter()
.map(|(k, v)| (*k, encoder.dump_value(v)))
.collect(),
},
)
})
.collect(),
systems: Vec::new(),
aliases_syms: csm
.aliases
.iter()
.map(|(k, v)| (dump_sym_id(*k), dump_sym_id(*v)))
.collect(),
aliases: Vec::new(),
priority_syms: csm.priority.iter().map(|id| dump_sym_id(*id)).collect(),
priority: Vec::new(),
keyboard_coding_sym: Some(dump_sym_id(csm.dump_keyboard_coding_sym())),
keyboard_coding: None,
terminal_coding_sym: Some(dump_sym_id(csm.dump_terminal_coding_sym())),
terminal_coding: None,
}
}
pub(crate) fn dump_charset_registry(encoder: &mut DumpEncoder) -> DumpCharsetRegistry {
let snapshot = snapshot_charset_registry();
DumpCharsetRegistry {
charsets: snapshot
.charsets
.into_iter()
.map(|info| DumpCharsetInfo {
id: info.id,
name_sym: Some(dump_sym_id(info.name)),
name: None,
dimension: info.dimension,
code_space: info.code_space,
min_code: info.min_code,
max_code: info.max_code,
iso_final_char: info.iso_final_char,
iso_revision: info.iso_revision,
emacs_mule_id: info.emacs_mule_id,
ascii_compatible_p: info.ascii_compatible_p,
supplementary_p: info.supplementary_p,
invalid_code: info.invalid_code,
unify_map: encoder.dump_value(&info.unify_map),
method: match info.method {
CharsetMethodSnapshot::Offset(offset) => DumpCharsetMethod::Offset(offset),
CharsetMethodSnapshot::Map(map_name) => DumpCharsetMethod::Map(map_name),
CharsetMethodSnapshot::Subset(subset) => {
DumpCharsetMethod::Subset(DumpCharsetSubsetSpec {
parent_sym: Some(dump_sym_id(subset.parent)),
parent: None,
parent_min_code: subset.parent_min_code,
parent_max_code: subset.parent_max_code,
offset: subset.offset,
})
}
CharsetMethodSnapshot::Superset(members) => DumpCharsetMethod::SupersetSyms(
members
.into_iter()
.map(|(name, offset)| (dump_sym_id(name), offset))
.collect(),
),
},
plist_syms: info
.plist
.into_iter()
.map(|(key, value)| (dump_sym_id(key), encoder.dump_value(&value)))
.collect(),
plist: Vec::new(),
})
.collect(),
priority_syms: snapshot.priority.into_iter().map(dump_sym_id).collect(),
priority: Vec::new(),
next_id: snapshot.next_id,
}
}
fn dump_font_width(width: &FontWidth) -> DumpFontWidth {
match width {
FontWidth::UltraCondensed => DumpFontWidth::UltraCondensed,
FontWidth::ExtraCondensed => DumpFontWidth::ExtraCondensed,
FontWidth::Condensed => DumpFontWidth::Condensed,
FontWidth::SemiCondensed => DumpFontWidth::SemiCondensed,
FontWidth::Normal => DumpFontWidth::Normal,
FontWidth::SemiExpanded => DumpFontWidth::SemiExpanded,
FontWidth::Expanded => DumpFontWidth::Expanded,
FontWidth::ExtraExpanded => DumpFontWidth::ExtraExpanded,
FontWidth::UltraExpanded => DumpFontWidth::UltraExpanded,
}
}
fn dump_font_repertory(repertory: FontRepertory) -> DumpFontRepertory {
match repertory {
FontRepertory::Charset(name) => DumpFontRepertory::CharsetSym(dump_sym_id(name)),
FontRepertory::CharTableRanges(ranges) => DumpFontRepertory::CharTableRanges(ranges),
}
}
fn dump_stored_font_spec(spec: StoredFontSpec) -> DumpStoredFontSpec {
DumpStoredFontSpec {
family_sym: spec.family.map(dump_sym_id),
family: None,
registry_sym: spec.registry.map(dump_sym_id),
registry: None,
lang_sym: spec.lang.map(dump_sym_id),
lang: None,
weight: spec.weight.map(|weight| weight.0),
slant: spec.slant.map(|slant| dump_font_slant(&slant)),
width: spec.width.map(|width| dump_font_width(&width)),
repertory: spec.repertory.map(dump_font_repertory),
}
}
fn dump_font_spec_entry(entry: FontSpecEntry) -> DumpFontSpecEntry {
match entry {
FontSpecEntry::Font(spec) => DumpFontSpecEntry::Font(dump_stored_font_spec(spec)),
FontSpecEntry::ExplicitNone => DumpFontSpecEntry::ExplicitNone,
}
}
pub(crate) fn dump_fontset_registry() -> DumpFontsetRegistry {
let snapshot = snapshot_fontset_registry();
DumpFontsetRegistry {
ordered_names_lisp: snapshot
.ordered_names
.iter()
.map(dump_lisp_string)
.collect(),
alias_to_name_lisp: snapshot
.alias_to_name
.iter()
.map(|(alias, name)| (dump_lisp_string(alias), dump_lisp_string(name)))
.collect(),
fontsets_lisp: snapshot
.fontsets
.iter()
.map(|(name, data)| {
(
dump_lisp_string(name),
DumpFontsetData {
ranges: data
.ranges
.iter()
.map(|range| DumpFontsetRangeEntry {
from: range.from,
to: range.to,
entries: range
.entries
.iter()
.cloned()
.map(dump_font_spec_entry)
.collect(),
})
.collect(),
fallback: data.fallback.as_ref().map(|entries| {
entries.iter().cloned().map(dump_font_spec_entry).collect()
}),
},
)
})
.collect(),
ordered_names: Vec::new(),
alias_to_name: Vec::new(),
fontsets: Vec::new(),
generation: snapshot.generation,
}
}
fn dump_color(c: &Color) -> DumpColor {
DumpColor {
r: c.r,
g: c.g,
b: c.b,
a: c.a,
}
}
fn dump_font_slant(s: &FontSlant) -> DumpFontSlant {
match s {
FontSlant::Normal => DumpFontSlant::Normal,
FontSlant::Italic => DumpFontSlant::Italic,
FontSlant::Oblique => DumpFontSlant::Oblique,
FontSlant::ReverseItalic => DumpFontSlant::ReverseItalic,
FontSlant::ReverseOblique => DumpFontSlant::ReverseOblique,
}
}
fn dump_underline_style(s: &UnderlineStyle) -> DumpUnderlineStyle {
match s {
UnderlineStyle::Line => DumpUnderlineStyle::Line,
UnderlineStyle::Wave => DumpUnderlineStyle::Wave,
UnderlineStyle::Dot => DumpUnderlineStyle::Dot,
UnderlineStyle::Dash => DumpUnderlineStyle::Dash,
UnderlineStyle::DoubleLine => DumpUnderlineStyle::DoubleLine,
}
}
fn dump_box_style(s: &BoxStyle) -> DumpBoxStyle {
match s {
BoxStyle::Flat => DumpBoxStyle::Flat,
BoxStyle::Raised => DumpBoxStyle::Raised,
BoxStyle::Pressed => DumpBoxStyle::Pressed,
}
}
fn dump_face_height(h: &FaceHeight) -> DumpFaceHeight {
match h {
FaceHeight::Absolute(n) => DumpFaceHeight::Absolute(*n),
FaceHeight::Relative(f) => DumpFaceHeight::Relative(*f),
}
}
fn dump_face(encoder: &mut DumpEncoder, f: &Face) -> DumpFace {
DumpFace {
foreground: f.foreground.map(|c| dump_color(&c)),
background: f.background.map(|c| dump_color(&c)),
family_value: f.family.as_ref().map(|value| encoder.dump_value(value)),
family: None,
foundry_value: f.foundry.as_ref().map(|value| encoder.dump_value(value)),
foundry: None,
height: f.height.as_ref().map(dump_face_height),
weight: f.weight.map(|w| w.0),
slant: f.slant.as_ref().map(dump_font_slant),
underline: f.underline.as_ref().map(|u| DumpUnderline {
style: dump_underline_style(&u.style),
color: u.color.map(|c| dump_color(&c)),
position: u.position,
}),
overline: f.overline,
strike_through: f.strike_through,
box_border: f.box_border.as_ref().map(|b| DumpBoxBorder {
color: b.color.map(|c| dump_color(&c)),
width: b.width,
style: dump_box_style(&b.style),
}),
inverse_video: f.inverse_video,
stipple_value: f.stipple.as_ref().map(|value| encoder.dump_value(value)),
stipple: None,
extend: f.extend,
inherit_syms: match f.inherit {
None => Vec::new(),
Some(v) => {
if let Some(id) = v.as_symbol_id() {
vec![dump_sym_id(id)]
} else {
crate::emacs_core::value::list_to_vec(&v)
.map(|items| {
items
.iter()
.filter_map(|entry| entry.as_symbol_id().map(dump_sym_id))
.collect()
})
.unwrap_or_default()
}
}
},
inherit: Vec::new(),
overstrike: f.overstrike,
doc_value: f.doc.as_ref().map(|value| encoder.dump_value(value)),
doc: None,
}
}
pub(crate) fn dump_face_table(encoder: &mut DumpEncoder, ft: &FaceTable) -> DumpFaceTable {
DumpFaceTable {
face_ids: ft
.dump_faces_by_sym_id()
.into_iter()
.map(|(id, f)| (dump_sym_id(id), dump_face(encoder, &f)))
.collect(),
faces: Vec::new(),
}
}
pub(crate) fn dump_rectangle(r: &RectangleState) -> DumpRectangleState {
DumpRectangleState {
killed: r.killed.iter().map(dump_lisp_string).collect(),
}
}
pub(crate) fn dump_kmacro(encoder: &mut DumpEncoder, km: &KmacroManager) -> DumpKmacroManager {
DumpKmacroManager {
current_macro: Vec::new(),
last_macro: None,
macro_ring: km
.macro_ring
.iter()
.map(|m| m.iter().map(|value| encoder.dump_value(value)).collect())
.collect(),
counter: km.counter,
counter_format_lisp: Some(dump_lisp_string(&km.counter_format)),
counter_format: None,
}
}
pub(crate) fn dump_register_manager(
encoder: &mut DumpEncoder,
rm: &RegisterManager,
) -> DumpRegisterManager {
DumpRegisterManager {
registers: rm
.dump_registers()
.iter()
.map(|(c, r)| {
(
*c,
match r {
RegisterContent::Text(s) => DumpRegisterContent::Text {
data: s.as_bytes().to_vec(),
size: s.schars(),
size_byte: if s.is_multibyte() {
s.sbytes() as i64
} else {
-1
},
},
RegisterContent::Number(n) => DumpRegisterContent::Number(*n),
RegisterContent::Marker(v) => {
DumpRegisterContent::Marker(encoder.dump_value(v))
}
RegisterContent::Rectangle(lines) => DumpRegisterContent::Rectangle(
lines.iter().map(dump_lisp_string).collect(),
),
RegisterContent::FrameConfig(v) => {
DumpRegisterContent::FrameConfig(encoder.dump_value(v))
}
RegisterContent::File(s) => DumpRegisterContent::File(dump_lisp_string(s)),
RegisterContent::KbdMacro(keys) => DumpRegisterContent::KbdMacro(
keys.iter().map(|value| encoder.dump_value(value)).collect(),
),
},
)
})
.collect(),
}
}
pub(crate) fn dump_bookmark_manager(bm: &BookmarkManager) -> DumpBookmarkManager {
DumpBookmarkManager {
bookmarks_lisp: bm
.dump_bookmarks()
.iter()
.map(|(k, b)| {
(
dump_lisp_string(k.as_lisp_string()),
DumpBookmark {
name: dump_lisp_string(&b.name),
filename: b
.filename
.as_ref()
.map(crate::emacs_core::builtins::runtime_string_from_lisp_string),
position: b.position,
front_context: b
.front_context
.as_ref()
.map(crate::emacs_core::builtins::runtime_string_from_lisp_string),
rear_context: b
.rear_context
.as_ref()
.map(crate::emacs_core::builtins::runtime_string_from_lisp_string),
annotation: b
.annotation
.as_ref()
.map(crate::emacs_core::builtins::runtime_string_from_lisp_string),
handler: b
.handler
.as_ref()
.map(crate::emacs_core::builtins::runtime_string_from_lisp_string),
},
)
})
.collect(),
bookmarks: Vec::new(),
recent: bm.dump_recent().iter().map(dump_lisp_string).collect(),
}
}
pub(crate) fn dump_abbrev_manager(am: &AbbrevManager) -> DumpAbbrevManager {
DumpAbbrevManager {
tables_syms: am
.dump_tables()
.iter()
.map(|(sym, t)| {
(
dump_sym_id(*sym),
DumpAbbrevTable {
name: dump_lisp_string(&t.name),
abbrevs: t
.abbrevs
.iter()
.map(|(k, a)| {
(
dump_lisp_string(k),
DumpAbbrev {
expansion: dump_lisp_string(&a.expansion),
hook: a.hook.as_ref().map(dump_lisp_string),
count: a.count,
system: a.system,
},
)
})
.collect(),
parent: t.parent.as_ref().map(dump_lisp_string),
case_fixed: t.case_fixed,
enable_quoting: t.enable_quoting,
},
)
})
.collect(),
tables: Vec::new(),
global_table_sym: Some(dump_sym_id(am.dump_global_table_sym())),
global_table_name: dump_lisp_string(&am.global_table_name()),
abbrev_mode: am.dump_abbrev_mode(),
}
}
pub(crate) fn dump_interactive_registry(
encoder: &mut DumpEncoder,
ir: &InteractiveRegistry,
) -> DumpInteractiveRegistry {
DumpInteractiveRegistry {
specs: ir
.dump_specs()
.iter()
.map(|(k, s)| {
(
dump_sym_id(*k),
DumpInteractiveSpec {
spec: encoder.dump_value(&s.spec),
},
)
})
.collect(),
}
}
pub(crate) fn dump_watcher_list(
encoder: &mut DumpEncoder,
wl: &VariableWatcherList,
) -> DumpVariableWatcherList {
DumpVariableWatcherList {
watchers: wl
.dump_watchers()
.iter()
.map(|(k, watchers)| {
(
dump_sym_id(*k),
watchers
.iter()
.map(|w| encoder.dump_value(&w.callback))
.collect(),
)
})
.collect(),
}
}
pub(crate) fn dump_string_text_prop_run(
encoder: &mut DumpEncoder,
r: &StringTextPropertyRun,
) -> DumpStringTextPropertyRun {
DumpStringTextPropertyRun {
start: r.start,
end: r.end,
plist: encoder.dump_value(&r.plist),
}
}
fn dump_string_text_property_table(
encoder: &mut DumpEncoder,
table: &TextPropertyTable,
) -> Vec<DumpPropertyInterval> {
let mut intervals = Vec::new();
for iv in table.dump_intervals() {
if iv.properties.is_empty() {
continue;
}
let properties: Vec<(DumpValue, DumpValue)> = iv
.properties
.iter()
.map(|(key, val)| (encoder.dump_value(key), encoder.dump_value(val)))
.collect();
intervals.push(DumpPropertyInterval {
start: iv.start,
end: iv.end,
properties,
});
}
intervals
}
pub(crate) fn dump_evaluator(eval: &Context) -> DumpContextState {
let mut encoder = DumpEncoder::new();
let dump = DumpContextState {
symbol_table: dump_symbol_table(),
tagged_heap: DumpTaggedHeap {
objects: Vec::new(),
mapped_cons: Vec::new(),
mapped_floats: Vec::new(),
mapped_strings: Vec::new(),
mapped_veclikes: Vec::new(),
mapped_slots: Vec::new(),
},
obarray: dump_obarray(&mut encoder, eval),
dynamic: Vec::new(),
lexenv: encoder.dump_value(&eval.lexenv),
features: eval.features.iter().copied().map(dump_sym_id).collect(),
require_stack: eval
.require_stack
.iter()
.copied()
.map(dump_sym_id)
.collect(),
loads_in_progress: eval
.loads_in_progress
.iter()
.map(dump_lisp_string)
.collect(),
buffers: dump_buffer_manager(&mut encoder, &eval.buffers),
autoloads: dump_autoload_manager(&mut encoder, &eval.autoloads),
custom: dump_custom_manager(&eval.custom),
modes: dump_mode_registry(&mut encoder, &eval.modes),
coding_systems: dump_coding_system_manager(&mut encoder, &eval.coding_systems),
charset_registry: dump_charset_registry(&mut encoder),
fontset_registry: dump_fontset_registry(),
face_table: dump_face_table(&mut encoder, &eval.face_table),
abbrevs: dump_abbrev_manager(&eval.abbrevs),
interactive: dump_interactive_registry(&mut encoder, &eval.interactive),
rectangle: dump_rectangle(&eval.rectangle),
standard_syntax_table: encoder.dump_value(&eval.standard_syntax_table),
standard_category_table: encoder.dump_value(&eval.standard_category_table),
current_local_map: encoder.dump_value(&eval.current_local_map),
kmacro: dump_kmacro(&mut encoder, &eval.kmacro),
registers: dump_register_manager(&mut encoder, &eval.registers),
bookmarks: dump_bookmark_manager(&eval.bookmarks),
watchers: dump_watcher_list(&mut encoder, &eval.watchers),
};
let tagged_heap = encoder.finalize();
DumpContextState {
tagged_heap,
..dump
}
}
pub(crate) fn load_sym_id(id: &DumpSymId) -> SymId {
PDUMP_LOAD_SYM_REMAP.with(|slot| {
slot.borrow()
.as_ref()
.and_then(|remap| remap.get(id.0 as usize))
.copied()
.unwrap_or_else(|| panic!("pdump symbol slot {} should have a runtime remap", id.0))
})
}
pub(crate) fn load_name_id(id: &DumpNameId) -> NameId {
PDUMP_LOAD_NAME_REMAP.with(|slot| {
slot.borrow()
.as_ref()
.and_then(|remap| remap.get(id.0 as usize))
.copied()
.unwrap_or_else(|| panic!("pdump name slot {} should have a runtime remap", id.0))
})
}
pub(crate) fn load_op(op: &DumpOp) -> Result<Op, DumpError> {
let op = match *op {
DumpOp::Constant(n) => Op::Constant(n),
DumpOp::Nil => Op::Nil,
DumpOp::True => Op::True,
DumpOp::Pop => Op::Pop,
DumpOp::Dup => Op::Dup,
DumpOp::StackRef(n) => Op::StackRef(n),
DumpOp::StackSet(n) => Op::StackSet(n),
DumpOp::DiscardN(n) => Op::DiscardN(n),
DumpOp::VarRef(n) => Op::VarRef(n),
DumpOp::VarSet(n) => Op::VarSet(n),
DumpOp::VarBind(n) => Op::VarBind(n),
DumpOp::Unbind(n) => Op::Unbind(n),
DumpOp::Call(n) => Op::Call(n),
DumpOp::Apply(n) => Op::Apply(n),
DumpOp::Goto(n) => Op::Goto(n),
DumpOp::GotoIfNil(n) => Op::GotoIfNil(n),
DumpOp::GotoIfNotNil(n) => Op::GotoIfNotNil(n),
DumpOp::GotoIfNilElsePop(n) => Op::GotoIfNilElsePop(n),
DumpOp::GotoIfNotNilElsePop(n) => Op::GotoIfNotNilElsePop(n),
DumpOp::Switch => Op::Switch,
DumpOp::Return => Op::Return,
DumpOp::Add => Op::Add,
DumpOp::Sub => Op::Sub,
DumpOp::Mul => Op::Mul,
DumpOp::Div => Op::Div,
DumpOp::Rem => Op::Rem,
DumpOp::Add1 => Op::Add1,
DumpOp::Sub1 => Op::Sub1,
DumpOp::Negate => Op::Negate,
DumpOp::Eqlsign => Op::Eqlsign,
DumpOp::Gtr => Op::Gtr,
DumpOp::Lss => Op::Lss,
DumpOp::Leq => Op::Leq,
DumpOp::Geq => Op::Geq,
DumpOp::Max => Op::Max,
DumpOp::Min => Op::Min,
DumpOp::Car => Op::Car,
DumpOp::Cdr => Op::Cdr,
DumpOp::Cons => Op::Cons,
DumpOp::List(n) => Op::List(n),
DumpOp::Length => Op::Length,
DumpOp::Nth => Op::Nth,
DumpOp::Nthcdr => Op::Nthcdr,
DumpOp::Setcar => Op::Setcar,
DumpOp::Setcdr => Op::Setcdr,
DumpOp::CarSafe => Op::CarSafe,
DumpOp::CdrSafe => Op::CdrSafe,
DumpOp::Elt => Op::Elt,
DumpOp::Nconc => Op::Nconc,
DumpOp::Nreverse => Op::Nreverse,
DumpOp::Member => Op::Member,
DumpOp::Memq => Op::Memq,
DumpOp::Assq => Op::Assq,
DumpOp::Symbolp => Op::Symbolp,
DumpOp::Consp => Op::Consp,
DumpOp::Stringp => Op::Stringp,
DumpOp::Listp => Op::Listp,
DumpOp::Integerp => Op::Integerp,
DumpOp::Numberp => Op::Numberp,
DumpOp::Null => Op::Null,
DumpOp::Not => Op::Not,
DumpOp::Eq => Op::Eq,
DumpOp::Equal => Op::Equal,
DumpOp::Concat(n) => Op::Concat(n),
DumpOp::Substring => Op::Substring,
DumpOp::StringEqual => Op::StringEqual,
DumpOp::StringLessp => Op::StringLessp,
DumpOp::Aref => Op::Aref,
DumpOp::Aset => Op::Aset,
DumpOp::SymbolValue => Op::SymbolValue,
DumpOp::SymbolFunction => Op::SymbolFunction,
DumpOp::Set => Op::Set,
DumpOp::Fset => Op::Fset,
DumpOp::Get => Op::Get,
DumpOp::Put => Op::Put,
DumpOp::PushConditionCase(n) => Op::PushConditionCase(n),
DumpOp::PushConditionCaseRaw(n) => Op::PushConditionCaseRaw(n),
DumpOp::PushCatch(n) => Op::PushCatch(n),
DumpOp::PopHandler => Op::PopHandler,
DumpOp::UnwindProtect(n) => {
return Err(DumpError::DeserializationError(format!(
"legacy neomacs unwind-protect opcode is unsupported in pdump snapshots; rebuild the dump or recompile this bytecode (target {n})"
)));
}
DumpOp::UnwindProtectPop => Op::UnwindProtectPop,
DumpOp::Throw => Op::Throw,
DumpOp::SaveCurrentBuffer => Op::SaveCurrentBuffer,
DumpOp::SaveExcursion => Op::SaveExcursion,
DumpOp::SaveRestriction => Op::SaveRestriction,
DumpOp::SaveWindowExcursion => Op::SaveWindowExcursion,
DumpOp::MakeClosure(n) => Op::MakeClosure(n),
DumpOp::CallBuiltin(a, b) => Op::CallBuiltin(a, b),
DumpOp::CallBuiltinSym(sym, b) => Op::CallBuiltinSym(load_sym_id(&sym), b),
};
Ok(op)
}
pub(crate) fn load_lambda_params(p: &DumpLambdaParams) -> LambdaParams {
LambdaParams {
required: p.required.iter().map(|s| load_sym_id(s)).collect(),
optional: p.optional.iter().map(|s| load_sym_id(s)).collect(),
rest: p.rest.map(|s| load_sym_id(&s)),
}
}
pub(crate) fn load_bytecode(
decoder: &mut LoadDecoder,
bc: &DumpByteCodeFunction,
) -> Result<ByteCodeFunction, DumpError> {
let params = load_lambda_params(&bc.params);
let arglist = bc
.arglist
.as_ref()
.map(|value| decoder.load_value(value))
.unwrap_or_else(|| crate::emacs_core::builtins::lambda_params_to_value(¶ms));
Ok(ByteCodeFunction {
ops: bc.ops.iter().map(load_op).collect::<Result<Vec<_>, _>>()?,
constants: bc
.constants
.iter()
.map(|value| decoder.load_value(value))
.collect(),
max_stack: bc.max_stack,
params,
arglist,
lexical: bc.lexical,
env: decoder.load_opt_value(&bc.env),
gnu_byte_offset_map: bc.gnu_byte_offset_map.as_ref().map(|pairs| {
pairs
.iter()
.map(|(byte_off, instr_idx)| (*byte_off as usize, *instr_idx as usize))
.collect()
}),
gnu_bytecode_bytes: bc.gnu_bytecode_bytes.clone(),
docstring: bc.docstring.as_ref().map(load_lisp_string),
doc_form: decoder.load_opt_value(&bc.doc_form),
interactive: decoder.load_opt_value(&bc.interactive),
closure_slot_count: bc.closure_slot_count,
extra_slots: bc
.extra_slots
.iter()
.map(|value| decoder.load_value(value))
.collect(),
})
}
pub(crate) fn load_hash_key(decoder: &mut LoadDecoder, k: &DumpHashKey) -> HashKey {
match k {
DumpHashKey::Nil => HashKey::Nil,
DumpHashKey::True => HashKey::True,
DumpHashKey::Int(n) => HashKey::Int(*n),
DumpHashKey::Float(bits) => HashKey::Float(*bits),
DumpHashKey::FloatEq(bits, id) => HashKey::FloatEq(*bits, *id),
DumpHashKey::Symbol(s) => HashKey::Symbol(load_sym_id(s)),
DumpHashKey::Keyword(s) => HashKey::Keyword(load_sym_id(s)),
DumpHashKey::Str(id) => HashKey::Ptr(decoder.heap_ref_to_value(tagged_heap_ref(id)).bits()),
DumpHashKey::Char(c) => HashKey::Char(*c),
DumpHashKey::Window(w) => HashKey::Window(*w),
DumpHashKey::Frame(f) => HashKey::Frame(*f),
DumpHashKey::Ptr(p) => HashKey::Ptr(*p as usize),
DumpHashKey::HeapRef(a) => HashKey::Ptr(
decoder
.heap_ref_to_value(TaggedHeapRef { index: *a })
.bits(),
),
DumpHashKey::EqualCons(a, b) => HashKey::EqualCons(
Box::new(load_hash_key(decoder, a)),
Box::new(load_hash_key(decoder, b)),
),
DumpHashKey::EqualVec(v) => {
HashKey::EqualVec(v.iter().map(|item| load_hash_key(decoder, item)).collect())
}
DumpHashKey::SymbolWithPos(sym, pos) => HashKey::SymbolWithPos(
Box::new(load_hash_key(decoder, sym)),
Box::new(load_hash_key(decoder, pos)),
),
DumpHashKey::Cycle(index) => HashKey::Cycle(*index),
DumpHashKey::Text(text) => HashKey::Text(text.clone()),
}
}
pub(crate) fn load_hash_table_test(t: &DumpHashTableTest) -> HashTableTest {
match t {
DumpHashTableTest::Eq => HashTableTest::Eq,
DumpHashTableTest::Eql => HashTableTest::Eql,
DumpHashTableTest::Equal => HashTableTest::Equal,
}
}
pub(crate) fn load_hash_table_weakness(w: &DumpHashTableWeakness) -> HashTableWeakness {
match w {
DumpHashTableWeakness::Key => HashTableWeakness::Key,
DumpHashTableWeakness::Value => HashTableWeakness::Value,
DumpHashTableWeakness::KeyOrValue => HashTableWeakness::KeyOrValue,
DumpHashTableWeakness::KeyAndValue => HashTableWeakness::KeyAndValue,
}
}
pub(crate) fn load_hash_table(decoder: &mut LoadDecoder, ht: &DumpLispHashTable) -> LispHashTable {
let data: HashMap<HashKey, Value> = ht
.entries
.iter()
.map(|(k, v)| (load_hash_key(decoder, k), decoder.load_value(v)))
.collect();
let key_snapshots: HashMap<HashKey, Value> = ht
.key_snapshots
.iter()
.map(|(k, v)| (load_hash_key(decoder, k), decoder.load_value(v)))
.collect();
let insertion_order: Vec<HashKey> = ht
.insertion_order
.iter()
.map(|key| load_hash_key(decoder, key))
.collect();
LispHashTable {
test: load_hash_table_test(&ht.test),
test_name: ht.test_name.map(|s| load_sym_id(&s)),
size: ht.size,
weakness: ht.weakness.as_ref().map(load_hash_table_weakness),
rehash_size: ht.rehash_size,
rehash_threshold: ht.rehash_threshold,
data,
key_snapshots,
insertion_order,
}
}
pub(crate) fn load_symbol_table(table: &DumpSymbolTable) -> Result<(), DumpError> {
let symbol_names: Vec<u32> = table.symbols.iter().map(|entry| entry.name.0).collect();
let canonical: Vec<bool> = table.symbols.iter().map(|entry| entry.canonical).collect();
load_symbol_table_parts(&table.names, &symbol_names, &canonical)
}
pub(crate) fn load_symbol_table_parts(
names: &[LispString],
symbol_names: &[u32],
canonical: &[bool],
) -> Result<(), DumpError> {
let remap = intern::restore_runtime_interner(names, symbol_names, Some(canonical))
.map_err(DumpError::DeserializationError)?;
let intern::RestoredDumpSymbolTable { names, symbols } = remap;
PDUMP_LOAD_NAME_REMAP.with(|slot| {
let mut slot = slot.borrow_mut();
assert!(
slot.is_none(),
"pdump name remap should not already be initialized"
);
*slot = Some(names);
});
PDUMP_LOAD_SYM_REMAP.with(|slot| {
let mut slot = slot.borrow_mut();
assert!(
slot.is_none(),
"pdump symbol remap should not already be initialized"
);
*slot = Some(symbols);
});
Ok(())
}
pub(crate) fn finish_load_interner() {
PDUMP_LOAD_NAME_REMAP.with(|slot| {
slot.borrow_mut().take();
});
PDUMP_LOAD_SYM_REMAP.with(|slot| {
slot.borrow_mut().take();
});
}
pub(crate) fn load_symbol_data(
decoder: &mut LoadDecoder,
sym_id: SymId,
sd: &DumpSymbolData,
) -> LispSymbol {
use crate::emacs_core::symbol::{SymbolInterned, SymbolRedirect, SymbolVal};
let mut symbol = LispSymbol::new(sym_id);
let trapped_write: SymbolTrappedWrite = unsafe { std::mem::transmute(sd.trapped_write & 0b11) };
let interned: SymbolInterned = unsafe { std::mem::transmute(sd.interned & 0b11) };
symbol.flags.set_trapped_write(trapped_write);
symbol.flags.set_interned(interned);
symbol.flags.set_declared_special(sd.declared_special);
match &sd.val {
DumpSymbolVal::Plain(v) => {
symbol.flags.set_redirect(SymbolRedirect::Plainval);
symbol.val = SymbolVal {
plain: decoder.load_value(v),
};
}
DumpSymbolVal::Alias(target) => {
symbol.set_alias_target(load_sym_id(target));
}
DumpSymbolVal::Localized { default, .. } => {
symbol.flags.set_redirect(SymbolRedirect::Plainval);
symbol.val = SymbolVal {
plain: decoder.load_value(default),
};
}
DumpSymbolVal::Forwarded => {
symbol.flags.set_redirect(SymbolRedirect::Plainval);
symbol.val = SymbolVal {
plain: crate::emacs_core::value::Value::UNBOUND,
};
}
}
symbol.function = decoder.load_value(&sd.function);
symbol.plist = decoder.load_value(&sd.plist);
symbol
}
pub(crate) fn load_obarray(
decoder: &mut LoadDecoder,
dob: &DumpObarray,
) -> Result<Obarray, DumpError> {
let mut seen_symbol_ids = FxHashSet::default();
let mut localized_entries: Vec<(SymId, &DumpSymbolData)> = Vec::new();
let mut symbols = Vec::with_capacity(dob.symbols.len());
for (id, sd) in &dob.symbols {
let sym_id = load_sym_id(id);
if !seen_symbol_ids.insert(sym_id) {
return Err(DumpError::DeserializationError(format!(
"pdump obarray is inconsistent: duplicate symbol slot {}",
sym_id.0
)));
}
if matches!(sd.val, DumpSymbolVal::Localized { .. }) {
localized_entries.push((sym_id, sd));
}
symbols.push((sym_id, load_symbol_data(decoder, sym_id, sd)));
}
let load_member_set = |label: &str, ids: &[DumpSymId]| -> Result<Vec<SymId>, DumpError> {
let mut seen = FxHashSet::default();
let mut loaded = Vec::with_capacity(ids.len());
for id in ids {
let sym_id = load_sym_id(id);
if !seen.insert(sym_id) {
return Err(DumpError::DeserializationError(format!(
"pdump obarray is inconsistent: duplicate {label} entry for symbol slot {}",
sym_id.0
)));
}
if !seen_symbol_ids.contains(&sym_id) {
return Err(DumpError::DeserializationError(format!(
"pdump obarray is inconsistent: {label} entry references missing symbol slot {}",
sym_id.0
)));
}
loaded.push(sym_id);
}
Ok(loaded)
};
let global_members = load_member_set("global_members", &dob.global_members)?;
let function_unbound = load_member_set("function_unbound", &dob.function_unbound)?;
let mut obarray = Obarray::from_dump(
symbols,
global_members,
function_unbound,
dob.function_epoch,
);
for (sym_id, sd) in &localized_entries {
if let DumpSymbolVal::Localized {
default,
local_if_set,
} = &sd.val
{
let default_val = decoder.load_value(default);
obarray.make_symbol_localized(*sym_id, default_val);
if *local_if_set {
obarray.set_blv_local_if_set(*sym_id, true);
}
use crate::emacs_core::symbol::SymbolInterned;
if let Some(sym) = obarray.get_mut_by_id(*sym_id) {
let trapped_write: SymbolTrappedWrite =
unsafe { std::mem::transmute(sd.trapped_write & 0b11) };
let interned: SymbolInterned = unsafe { std::mem::transmute(sd.interned & 0b11) };
sym.flags.set_trapped_write(trapped_write);
sym.flags.set_interned(interned);
sym.flags.set_declared_special(sd.declared_special);
}
}
}
Ok(obarray)
}
fn load_property_interval(
decoder: &mut LoadDecoder,
pi: &DumpPropertyInterval,
) -> PropertyInterval {
let properties: std::collections::HashMap<
crate::emacs_core::value::Value,
crate::emacs_core::value::Value,
> = pi
.properties
.iter()
.map(|(k, v)| (decoder.load_value(k), decoder.load_value(v)))
.collect();
let key_order: Vec<crate::emacs_core::value::Value> = pi
.properties
.iter()
.map(|(k, _)| decoder.load_value(k))
.collect();
PropertyInterval {
start: pi.start,
end: pi.end,
properties,
key_order,
}
}
fn load_buffer(decoder: &mut LoadDecoder, db: &DumpBuffer) -> Buffer {
let text = BufferText::from_dump(db.text.text.clone(), db.multibyte);
let total_chars = text.char_count();
let begv_char = db.begv_char.unwrap_or_else(|| text.byte_to_char(db.begv));
let zv_char = db.zv_char.unwrap_or_else(|| {
if db.zv == text.len() {
total_chars
} else {
text.byte_to_char(db.zv)
}
});
let pt_char = db.pt_char.unwrap_or_else(|| {
if db.pt == db.begv {
begv_char
} else if db.pt == db.zv {
zv_char
} else {
text.byte_to_char(db.pt)
}
});
let mark_char = match db.mark {
Some(mark) => Some(db.mark_char.unwrap_or_else(|| {
if mark == db.begv {
begv_char
} else if mark == db.zv {
zv_char
} else {
text.byte_to_char(mark)
}
})),
None => None,
};
for dump_marker in db.markers.iter().rev() {
let Some(marker_id) = dump_marker.marker_id else {
continue;
};
let buffer_id = match dump_marker.buffer {
Some(id) => BufferId(id.0),
None => BufferId(db.id.0),
};
let insertion_type = if dump_marker.insertion_type {
crate::buffer::InsertionType::After
} else {
crate::buffer::InsertionType::Before
};
let marker_ptr = decoder
.state
.markers_by_id
.get(&marker_id)
.copied()
.unwrap_or_else(|| {
let scratch =
crate::emacs_core::value::Value::make_marker(crate::heap_types::MarkerData {
buffer: Some(buffer_id),
insertion_type: dump_marker.insertion_type,
marker_id: Some(marker_id),
bytepos: dump_marker.bytepos,
charpos: dump_marker.charpos,
next_marker: std::ptr::null_mut(),
});
scratch
.as_veclike_ptr()
.expect("freshly allocated marker should have a veclike ptr")
as *mut crate::tagged::header::MarkerObj
});
text.chain_unlink(marker_ptr);
text.register_marker(
marker_ptr,
buffer_id,
marker_id,
dump_marker.bytepos,
dump_marker.charpos,
insertion_type,
);
}
let loaded_keymap = decoder.load_value(&db.local_map);
let mut loaded_properties: std::collections::HashMap<SymId, RuntimeBindingValue> =
if db.properties_syms.is_empty() {
db.properties
.iter()
.map(|(name, value)| {
(
intern::intern(name),
load_runtime_binding_value(decoder, value),
)
})
.collect()
} else {
db.properties_syms
.iter()
.map(|(sym_id, value)| {
(
load_sym_id(sym_id),
load_runtime_binding_value(decoder, value),
)
})
.collect()
};
let mut loaded_undo_list = Value::NIL;
if let Some(RuntimeBindingValue::Bound(value)) =
loaded_properties.remove(&intern::intern("buffer-undo-list"))
{
loaded_undo_list = value;
}
let mut loaded_local_var_alist = Value::NIL;
let prepend_alist_entry = |alist: &mut Value, sym_id: SymId, binding: RuntimeBindingValue| {
if crate::buffer::buffer::lookup_buffer_slot(intern::resolve_sym(sym_id)).is_some() {
return;
}
let RuntimeBindingValue::Bound(value) = binding else {
return;
};
let key = Value::from_sym_id(sym_id);
let cell = Value::cons(key, value);
*alist = Value::cons(cell, *alist);
};
let ordered_local_bindings: Vec<SymId> = if db.local_binding_syms.is_empty() {
db.local_binding_names
.iter()
.map(|name| intern::intern(name))
.collect()
} else {
db.local_binding_syms.iter().map(load_sym_id).collect()
};
for sym_id in ordered_local_bindings.into_iter().rev() {
if sym_id == intern::intern("buffer-undo-list") {
continue;
}
if let Some(binding) = loaded_properties.remove(&sym_id) {
prepend_alist_entry(&mut loaded_local_var_alist, sym_id, binding);
}
}
let mut remaining: Vec<_> = loaded_properties.into_iter().collect();
remaining.sort_by(|left, right| intern::resolve_sym(left.0).cmp(intern::resolve_sym(right.0)));
for (sym_id, binding) in remaining.into_iter().rev() {
if sym_id == intern::intern("buffer-undo-list") {
continue;
}
prepend_alist_entry(&mut loaded_local_var_alist, sym_id, binding);
}
let undo_list = loaded_undo_list;
let save_modified_tick = db.save_modified_tick.unwrap_or_else(|| {
if db.modified {
db.modified_tick.saturating_sub(1)
} else {
db.modified_tick
}
});
let autosave_modified_tick = db.autosave_modified_tick.unwrap_or(save_modified_tick);
let last_window_start = db.last_window_start.unwrap_or(1).max(1);
let text_props = TextPropertyTable::from_dump(
db.text_props
.intervals
.iter()
.map(|interval| load_property_interval(decoder, interval))
.collect(),
);
text.text_props_replace(text_props);
text.set_modification_state(db.modified_tick, db.chars_modified_tick, save_modified_tick);
let markers_by_id = &decoder.state.markers_by_id;
let state_markers = match (db.state_pt_marker, db.state_begv_marker, db.state_zv_marker) {
(Some(pt_marker), Some(begv_marker), Some(zv_marker)) => {
let resolve = |mid: u64| -> *mut crate::tagged::header::MarkerObj {
let chain_hit = text.chain_find_by_id(mid);
if !chain_hit.is_null() {
return chain_hit;
}
if let Some(p) = markers_by_id.get(&mid).copied() {
return p;
}
let scratch =
crate::emacs_core::value::Value::make_marker(crate::heap_types::MarkerData {
buffer: Some(BufferId(db.id.0)),
insertion_type: false,
marker_id: Some(mid),
bytepos: 0,
charpos: 0,
next_marker: std::ptr::null_mut(),
});
scratch
.as_veclike_ptr()
.expect("freshly allocated marker should have a veclike ptr")
as *mut crate::tagged::header::MarkerObj
};
Some(crate::buffer::buffer::BufferStateMarkers {
pt_marker,
begv_marker,
zv_marker,
pt_marker_ptr: resolve(pt_marker),
begv_marker_ptr: resolve(begv_marker),
zv_marker_ptr: resolve(zv_marker),
})
}
_ => None,
};
Buffer {
id: BufferId(db.id.0),
name: if let Some(ref name) = db.name_lisp {
Value::heap_string(load_lisp_string(name))
} else {
Value::string(db.name.clone().unwrap_or_default())
},
base_buffer: db.base_buffer.map(|id| BufferId(id.0)),
text,
pt: pt_char,
pt_byte: db.pt,
mark: mark_char,
mark_byte: db.mark,
begv: begv_char,
begv_byte: db.begv,
zv: zv_char,
zv_byte: db.zv,
autosave_modified_tick,
last_window_start,
last_selected_window: None,
inhibit_buffer_hooks: false,
state_markers,
local_var_alist: {
let dumped = decoder.load_value(&db.local_var_alist);
if dumped.is_nil() && !loaded_local_var_alist.is_nil() {
loaded_local_var_alist
} else {
dumped
}
},
keymap: loaded_keymap,
slots: {
let mut s =
[crate::emacs_core::value::Value::NIL; crate::buffer::buffer::BUFFER_SLOT_COUNT];
for info in crate::buffer::buffer::BUFFER_SLOT_INFO {
s[info.offset] = info.default.to_value();
}
for (idx, dumped) in db.slots.iter().enumerate() {
if idx >= crate::buffer::buffer::BUFFER_SLOT_COUNT {
break;
}
s[idx] = decoder.load_value(dumped);
}
if let Some(ref fname) = db.file_name_lisp {
s[crate::buffer::buffer::BUFFER_SLOT_FILE_NAME] =
crate::emacs_core::value::Value::heap_string(load_lisp_string(fname));
} else if let Some(ref fname) = db.file_name {
s[crate::buffer::buffer::BUFFER_SLOT_FILE_NAME] =
crate::emacs_core::value::Value::string(fname);
}
if let Some(ref asname) = db.auto_save_file_name_lisp {
s[crate::buffer::buffer::BUFFER_SLOT_AUTO_SAVE_FILE_NAME] =
crate::emacs_core::value::Value::heap_string(load_lisp_string(asname));
} else if let Some(ref asname) = db.auto_save_file_name {
s[crate::buffer::buffer::BUFFER_SLOT_AUTO_SAVE_FILE_NAME] =
crate::emacs_core::value::Value::string(asname);
}
if db.read_only {
s[crate::buffer::buffer::BUFFER_SLOT_READ_ONLY] =
crate::emacs_core::value::Value::T;
}
if db.multibyte {
s[crate::buffer::buffer::BUFFER_SLOT_ENABLE_MULTIBYTE_CHARACTERS] =
crate::emacs_core::value::Value::T;
}
s
},
local_flags: db.local_flags,
overlays: OverlayList::from_dump(
db.overlays
.overlays
.iter()
.map(|d| {
Value::make_overlay(crate::heap_types::OverlayData {
plist: decoder.load_value(&d.plist),
buffer: d.buffer.map(|id| BufferId(id.0)),
start: d.start,
end: d.end,
front_advance: d.front_advance,
rear_advance: d.rear_advance,
})
})
.collect(),
),
undo_state: SharedUndoState::from_parts(undo_list, false, false),
}
}
pub(crate) fn load_buffer_manager(
decoder: &mut LoadDecoder,
dbm: &DumpBufferManager,
) -> BufferManager {
let buffers: HashMap<BufferId, Buffer> = dbm
.buffers
.iter()
.map(|(id, buf)| (BufferId(id.0), load_buffer(decoder, buf)))
.collect();
let defaults_values: Vec<crate::emacs_core::value::Value> = dbm
.buffer_defaults
.iter()
.map(|value| decoder.load_value(value))
.collect();
let dumped_defaults = if defaults_values.is_empty() {
None
} else {
Some(defaults_values.as_slice())
};
BufferManager::from_dump(
buffers,
dbm.current.map(|id| BufferId(id.0)),
dbm.next_id,
dbm.next_marker_id,
dumped_defaults,
)
}
pub(crate) fn load_autoload_manager(
decoder: &mut LoadDecoder,
dam: &DumpAutoloadManager,
) -> AutoloadManager {
let entries: HashMap<SymId, AutoloadEntry> = if dam.entries_syms.is_empty() {
dam.entries
.iter()
.map(|(k, e)| {
(
crate::emacs_core::intern::intern(k),
AutoloadEntry {
file: load_lisp_string(&e.file),
docstring: e.docstring.as_ref().map(load_lisp_string),
interactive: e.interactive,
autoload_type: match e.autoload_type {
DumpAutoloadType::Function => AutoloadType::Function,
DumpAutoloadType::Macro => AutoloadType::Macro,
DumpAutoloadType::Keymap => AutoloadType::Keymap,
},
},
)
})
.collect()
} else {
dam.entries_syms
.iter()
.map(|(k, e)| {
(
load_sym_id(k),
AutoloadEntry {
file: load_lisp_string(&e.file),
docstring: e.docstring.as_ref().map(load_lisp_string),
interactive: e.interactive,
autoload_type: match e.autoload_type {
DumpAutoloadType::Function => AutoloadType::Function,
DumpAutoloadType::Macro => AutoloadType::Macro,
DumpAutoloadType::Keymap => AutoloadType::Keymap,
},
},
)
})
.collect()
};
let after_load: HashMap<crate::emacs_core::autoload::AfterLoadKey, Vec<Value>> =
if !dam.after_load_lisp.is_empty() {
dam.after_load_lisp
.iter()
.map(|(k, v)| {
(
crate::emacs_core::autoload::AfterLoadKey::from_lisp_string(
&load_lisp_string(k),
),
v.iter().map(|value| decoder.load_value(value)).collect(),
)
})
.collect()
} else {
dam.after_load
.iter()
.map(|(k, v)| {
(
crate::emacs_core::autoload::AfterLoadKey::from_runtime(k),
v.iter().map(|value| decoder.load_value(value)).collect(),
)
})
.collect()
};
AutoloadManager::from_dump(
entries,
after_load,
dam.loaded_files.iter().map(load_lisp_string).collect(),
if dam.obsolete_functions_syms.is_empty() {
dam.obsolete_functions
.iter()
.map(|(k, (new_name, when))| {
(
crate::emacs_core::intern::intern(k),
(
crate::emacs_core::builtins::runtime_string_to_lisp_string(
new_name, true,
),
crate::emacs_core::builtins::runtime_string_to_lisp_string(when, true),
),
)
})
.collect()
} else {
dam.obsolete_functions_syms
.iter()
.map(|(k, (new_name, when))| {
(
load_sym_id(k),
(load_lisp_string(new_name), load_lisp_string(when)),
)
})
.collect()
},
if dam.obsolete_variables_syms.is_empty() {
dam.obsolete_variables
.iter()
.map(|(k, (new_name, when))| {
(
crate::emacs_core::intern::intern(k),
(
crate::emacs_core::builtins::runtime_string_to_lisp_string(
new_name, true,
),
crate::emacs_core::builtins::runtime_string_to_lisp_string(when, true),
),
)
})
.collect()
} else {
dam.obsolete_variables_syms
.iter()
.map(|(k, (new_name, when))| {
(
load_sym_id(k),
(load_lisp_string(new_name), load_lisp_string(when)),
)
})
.collect()
},
)
}
pub(crate) fn load_custom_manager(_dcm: &DumpCustomManager) -> CustomManager {
CustomManager {}
}
fn load_mode_custom_type(decoder: &mut LoadDecoder, ct: &DumpModeCustomType) -> ModeCustomType {
match ct {
DumpModeCustomType::Boolean => ModeCustomType::Boolean,
DumpModeCustomType::Integer => ModeCustomType::Integer,
DumpModeCustomType::Float => ModeCustomType::Float,
DumpModeCustomType::String => ModeCustomType::String,
DumpModeCustomType::Symbol => ModeCustomType::Symbol,
DumpModeCustomType::Sexp => ModeCustomType::Sexp,
DumpModeCustomType::Choice(choices) => ModeCustomType::Choice(
choices
.iter()
.map(|(s, v)| (s.clone(), decoder.load_value(v)))
.collect(),
),
DumpModeCustomType::List(inner) => {
ModeCustomType::List(Box::new(load_mode_custom_type(decoder, inner)))
}
DumpModeCustomType::Alist(k, v) => ModeCustomType::Alist(
Box::new(load_mode_custom_type(decoder, k)),
Box::new(load_mode_custom_type(decoder, v)),
),
DumpModeCustomType::Plist(k, v) => ModeCustomType::Plist(
Box::new(load_mode_custom_type(decoder, k)),
Box::new(load_mode_custom_type(decoder, v)),
),
DumpModeCustomType::Color => ModeCustomType::Color,
DumpModeCustomType::Face => ModeCustomType::Face,
DumpModeCustomType::File => ModeCustomType::File,
DumpModeCustomType::Directory => ModeCustomType::Directory,
DumpModeCustomType::Function => ModeCustomType::Function,
DumpModeCustomType::Variable => ModeCustomType::Variable,
DumpModeCustomType::Hook => ModeCustomType::Hook,
DumpModeCustomType::Coding => ModeCustomType::Coding,
}
}
pub(crate) fn load_mode_registry(
decoder: &mut LoadDecoder,
dmr: &DumpModeRegistry,
) -> ModeRegistry {
let major_modes: HashMap<SymId, MajorMode> = dmr
.major_modes
.iter()
.map(|(k, m)| {
(
load_sym_id(k),
MajorMode {
pretty_name: load_lisp_string(&m.pretty_name),
parent: decoder.load_opt_value(&m.parent),
mode_hook: decoder.load_value(&m.mode_hook),
keymap_name: decoder.load_opt_value(&m.keymap_name),
syntax_table_name: decoder.load_opt_value(&m.syntax_table_name),
abbrev_table_name: decoder.load_opt_value(&m.abbrev_table_name),
font_lock: m.font_lock.as_ref().map(|fl| FontLockDefaults {
keywords: fl
.keywords
.iter()
.map(|kw| FontLockKeyword {
pattern: kw
.pattern_lisp
.as_ref()
.map(load_lisp_string)
.unwrap_or_else(|| {
LispString::from_utf8(
kw.pattern.as_deref().unwrap_or_default(),
)
}),
face: kw.face_sym.as_ref().map(load_sym_id).unwrap_or_else(|| {
crate::emacs_core::intern::intern(
kw.face.as_deref().unwrap_or_default(),
)
}),
group: kw.group,
override_: kw.override_,
laxmatch: kw.laxmatch,
})
.collect(),
case_fold: fl.case_fold,
syntax_table: fl
.syntax_table_lisp
.as_ref()
.map(load_lisp_string)
.or_else(|| fl.syntax_table.as_deref().map(LispString::from_utf8)),
}),
body: decoder.load_opt_value(&m.body),
},
)
})
.collect();
let minor_modes: HashMap<SymId, MinorMode> = dmr
.minor_modes
.iter()
.map(|(k, m)| {
(
load_sym_id(k),
MinorMode {
lighter: m.lighter.as_ref().map(load_lisp_string),
keymap_name: decoder.load_opt_value(&m.keymap_name),
global: m.global,
body: decoder.load_opt_value(&m.body),
},
)
})
.collect();
let custom_variables: HashMap<SymId, ModeCustomVariable> = dmr
.custom_variables
.iter()
.map(|(k, cv)| {
(
load_sym_id(k),
ModeCustomVariable {
default_value: decoder.load_value(&cv.default_value),
doc: cv.doc.as_ref().map(load_lisp_string),
type_: load_mode_custom_type(decoder, &cv.custom_type),
group: decoder.load_opt_value(&cv.group),
set_function: decoder.load_opt_value(&cv.set_function),
get_function: decoder.load_opt_value(&cv.get_function),
tag: cv.tag.as_ref().map(load_lisp_string),
},
)
})
.collect();
let custom_groups: HashMap<SymId, ModeCustomGroup> = dmr
.custom_groups
.iter()
.map(|(k, g)| {
(
load_sym_id(k),
ModeCustomGroup {
doc: g.doc.as_ref().map(load_lisp_string),
parent: decoder.load_opt_value(&g.parent),
members: g
.members
.iter()
.map(|value| decoder.load_value(value))
.collect(),
},
)
})
.collect();
ModeRegistry::from_dump(
major_modes,
minor_modes,
dmr.buffer_major_modes
.iter()
.map(|(k, v)| (*k, decoder.load_value(v)))
.collect(),
dmr.buffer_minor_modes
.iter()
.map(|(k, v)| {
(
*k,
v.iter().map(|value| decoder.load_value(value)).collect(),
)
})
.collect(),
dmr.global_minor_modes
.iter()
.map(|value| decoder.load_value(value))
.collect(),
if !dmr.auto_mode_alist_lisp.is_empty() {
dmr.auto_mode_alist_lisp
.iter()
.map(|(pattern, value)| (load_lisp_string(pattern), decoder.load_value(value)))
.collect()
} else {
dmr.auto_mode_alist
.iter()
.map(|(pattern, value)| (LispString::from_utf8(pattern), decoder.load_value(value)))
.collect()
},
custom_variables,
custom_groups,
decoder.load_value(&dmr.fundamental_mode),
)
}
pub(crate) fn load_coding_system_manager(
decoder: &mut LoadDecoder,
dcsm: &DumpCodingSystemManager,
) -> CodingSystemManager {
let systems: HashMap<SymId, CodingSystemInfo> = if dcsm.systems_syms.is_empty() {
dcsm.systems
.iter()
.map(|(k, v)| {
(
crate::emacs_core::intern::intern(k),
CodingSystemInfo {
name: crate::emacs_core::intern::intern(
v.name
.as_deref()
.expect("legacy coding dump entry missing name"),
),
coding_type: crate::emacs_core::intern::intern(
v.coding_type
.as_deref()
.expect("legacy coding dump entry missing coding type"),
),
mnemonic: v.mnemonic,
eol_type: match v.eol_type {
DumpEolType::Unix => EolType::Unix,
DumpEolType::Dos => EolType::Dos,
DumpEolType::Mac => EolType::Mac,
DumpEolType::Undecided => EolType::Undecided,
},
ascii_compatible_p: v.ascii_compatible_p,
charset_list: v
.charset_list
.iter()
.map(|name| crate::emacs_core::intern::intern(name))
.collect(),
post_read_conversion: v
.post_read_conversion
.as_ref()
.map(|name| crate::emacs_core::intern::intern(name)),
pre_write_conversion: v
.pre_write_conversion
.as_ref()
.map(|name| crate::emacs_core::intern::intern(name)),
default_char: v.default_char,
for_unibyte: v.for_unibyte,
properties: v
.properties
.iter()
.map(|(k, v)| {
(crate::emacs_core::intern::intern(k), decoder.load_value(v))
})
.collect(),
int_properties: v
.int_properties
.iter()
.map(|(k, v)| (*k, decoder.load_value(v)))
.collect(),
},
)
})
.collect()
} else {
dcsm.systems_syms
.iter()
.map(|(k, v)| {
(
load_sym_id(k),
CodingSystemInfo {
name: v.name_sym.as_ref().map(load_sym_id).unwrap_or_else(|| {
crate::emacs_core::intern::intern(
v.name.as_deref().expect("coding dump entry missing name"),
)
}),
coding_type: v.coding_type_sym.as_ref().map(load_sym_id).unwrap_or_else(
|| {
crate::emacs_core::intern::intern(
v.coding_type
.as_deref()
.expect("coding dump entry missing coding type"),
)
},
),
mnemonic: v.mnemonic,
eol_type: match v.eol_type {
DumpEolType::Unix => EolType::Unix,
DumpEolType::Dos => EolType::Dos,
DumpEolType::Mac => EolType::Mac,
DumpEolType::Undecided => EolType::Undecided,
},
ascii_compatible_p: v.ascii_compatible_p,
charset_list: if v.charset_list_syms.is_empty() {
v.charset_list
.iter()
.map(|name| crate::emacs_core::intern::intern(name))
.collect()
} else {
v.charset_list_syms.iter().map(load_sym_id).collect()
},
post_read_conversion: v
.post_read_conversion_sym
.as_ref()
.map(load_sym_id)
.or_else(|| {
v.post_read_conversion
.as_ref()
.map(|name| crate::emacs_core::intern::intern(name))
}),
pre_write_conversion: v
.pre_write_conversion_sym
.as_ref()
.map(load_sym_id)
.or_else(|| {
v.pre_write_conversion
.as_ref()
.map(|name| crate::emacs_core::intern::intern(name))
}),
default_char: v.default_char,
for_unibyte: v.for_unibyte,
properties: if v.properties_syms.is_empty() {
v.properties
.iter()
.map(|(k, v)| {
(crate::emacs_core::intern::intern(k), decoder.load_value(v))
})
.collect()
} else {
v.properties_syms
.iter()
.map(|(k, v)| (load_sym_id(k), decoder.load_value(v)))
.collect()
},
int_properties: v
.int_properties
.iter()
.map(|(k, v)| (*k, decoder.load_value(v)))
.collect(),
},
)
})
.collect()
};
CodingSystemManager::from_dump(
systems,
if dcsm.aliases_syms.is_empty() {
dcsm.aliases
.iter()
.map(|(k, v)| {
(
crate::emacs_core::intern::intern(k),
crate::emacs_core::intern::intern(v),
)
})
.collect()
} else {
dcsm.aliases_syms
.iter()
.map(|(k, v)| (load_sym_id(k), load_sym_id(v)))
.collect()
},
if dcsm.priority_syms.is_empty() {
dcsm.priority
.iter()
.map(|name| crate::emacs_core::intern::intern(name))
.collect()
} else {
dcsm.priority_syms.iter().map(load_sym_id).collect()
},
dcsm.keyboard_coding_sym
.as_ref()
.map(load_sym_id)
.unwrap_or_else(|| {
crate::emacs_core::intern::intern(
dcsm.keyboard_coding
.as_deref()
.expect("legacy coding dump missing keyboard coding"),
)
}),
dcsm.terminal_coding_sym
.as_ref()
.map(load_sym_id)
.unwrap_or_else(|| {
crate::emacs_core::intern::intern(
dcsm.terminal_coding
.as_deref()
.expect("legacy coding dump missing terminal coding"),
)
}),
)
}
pub(crate) fn load_charset_registry(decoder: &mut LoadDecoder, dcr: &DumpCharsetRegistry) {
let snapshot = CharsetRegistrySnapshot {
charsets: dcr
.charsets
.iter()
.map(|info| CharsetInfoSnapshot {
id: info.id,
name: info.name_sym.as_ref().map(load_sym_id).unwrap_or_else(|| {
crate::emacs_core::intern::intern(
info.name
.as_deref()
.expect("legacy charset dump entry missing name"),
)
}),
dimension: info.dimension,
code_space: info.code_space,
min_code: info.min_code,
max_code: info.max_code,
iso_final_char: info.iso_final_char,
iso_revision: info.iso_revision,
emacs_mule_id: info.emacs_mule_id,
ascii_compatible_p: info.ascii_compatible_p,
supplementary_p: info.supplementary_p,
invalid_code: info.invalid_code,
unify_map: decoder.load_value(&info.unify_map),
method: match &info.method {
DumpCharsetMethod::Offset(offset) => CharsetMethodSnapshot::Offset(*offset),
DumpCharsetMethod::Map(map_name) => {
CharsetMethodSnapshot::Map(map_name.clone())
}
DumpCharsetMethod::Subset(subset) => CharsetMethodSnapshot::Subset(
crate::emacs_core::charset::CharsetSubsetSpecSnapshot {
parent: subset.parent_sym.as_ref().map(load_sym_id).unwrap_or_else(
|| {
crate::emacs_core::intern::intern(
subset
.parent
.as_deref()
.expect("legacy charset subset missing parent"),
)
},
),
parent_min_code: subset.parent_min_code,
parent_max_code: subset.parent_max_code,
offset: subset.offset,
},
),
DumpCharsetMethod::SupersetSyms(members) => CharsetMethodSnapshot::Superset(
members
.iter()
.map(|(name, offset)| (load_sym_id(name), *offset))
.collect(),
),
DumpCharsetMethod::Superset(members) => CharsetMethodSnapshot::Superset(
members
.iter()
.map(|(name, offset)| {
(crate::emacs_core::intern::intern(name), *offset)
})
.collect(),
),
},
plist: if info.plist_syms.is_empty() {
info.plist
.iter()
.map(|(key, value)| {
(
crate::emacs_core::intern::intern(key),
decoder.load_value(value),
)
})
.collect()
} else {
info.plist_syms
.iter()
.map(|(key, value)| (load_sym_id(key), decoder.load_value(value)))
.collect()
},
})
.collect(),
priority: if dcr.priority_syms.is_empty() {
dcr.priority
.iter()
.map(|name| crate::emacs_core::intern::intern(name))
.collect()
} else {
dcr.priority_syms.iter().map(load_sym_id).collect()
},
next_id: dcr.next_id,
};
restore_charset_registry(snapshot);
}
fn load_font_width(width: &DumpFontWidth) -> FontWidth {
match width {
DumpFontWidth::UltraCondensed => FontWidth::UltraCondensed,
DumpFontWidth::ExtraCondensed => FontWidth::ExtraCondensed,
DumpFontWidth::Condensed => FontWidth::Condensed,
DumpFontWidth::SemiCondensed => FontWidth::SemiCondensed,
DumpFontWidth::Normal => FontWidth::Normal,
DumpFontWidth::SemiExpanded => FontWidth::SemiExpanded,
DumpFontWidth::Expanded => FontWidth::Expanded,
DumpFontWidth::ExtraExpanded => FontWidth::ExtraExpanded,
DumpFontWidth::UltraExpanded => FontWidth::UltraExpanded,
}
}
fn load_font_repertory(repertory: &DumpFontRepertory) -> FontRepertory {
match repertory {
DumpFontRepertory::Charset(name) => {
FontRepertory::Charset(crate::emacs_core::intern::intern(name))
}
DumpFontRepertory::CharsetSym(name) => FontRepertory::Charset(load_sym_id(name)),
DumpFontRepertory::CharTableRanges(ranges) => {
FontRepertory::CharTableRanges(ranges.clone())
}
}
}
fn load_font_spec_entry(entry: &DumpFontSpecEntry) -> FontSpecEntry {
match entry {
DumpFontSpecEntry::Font(spec) => FontSpecEntry::Font(StoredFontSpec {
family: spec.family_sym.as_ref().map(load_sym_id).or_else(|| {
spec.family
.as_deref()
.map(crate::emacs_core::intern::intern)
}),
registry: spec.registry_sym.as_ref().map(load_sym_id).or_else(|| {
spec.registry
.as_deref()
.map(crate::emacs_core::intern::intern)
}),
lang: spec
.lang_sym
.as_ref()
.map(load_sym_id)
.or_else(|| spec.lang.as_deref().map(crate::emacs_core::intern::intern)),
weight: spec.weight.map(FontWeight),
slant: spec.slant.as_ref().map(load_font_slant),
width: spec.width.as_ref().map(load_font_width),
repertory: spec.repertory.as_ref().map(load_font_repertory),
}),
DumpFontSpecEntry::ExplicitNone => FontSpecEntry::ExplicitNone,
}
}
pub(crate) fn load_fontset_registry(dfr: &DumpFontsetRegistry) {
let snapshot = FontsetRegistrySnapshot {
ordered_names: if dfr.ordered_names_lisp.is_empty() {
dfr.ordered_names
.iter()
.map(|name| LispString::from_utf8(name))
.collect()
} else {
dfr.ordered_names_lisp
.iter()
.map(load_lisp_string)
.collect()
},
alias_to_name: if dfr.alias_to_name_lisp.is_empty() {
dfr.alias_to_name
.iter()
.map(|(alias, name)| (LispString::from_utf8(alias), LispString::from_utf8(name)))
.collect()
} else {
dfr.alias_to_name_lisp
.iter()
.map(|(alias, name)| (load_lisp_string(alias), load_lisp_string(name)))
.collect()
},
fontsets: if dfr.fontsets_lisp.is_empty() {
dfr.fontsets
.iter()
.map(|(name, data)| {
(
LispString::from_utf8(name),
FontsetDataSnapshot {
ranges: data
.ranges
.iter()
.map(|range| FontsetRangeEntrySnapshot {
from: range.from,
to: range.to,
entries: range
.entries
.iter()
.map(load_font_spec_entry)
.collect(),
})
.collect(),
fallback: data
.fallback
.as_ref()
.map(|entries| entries.iter().map(load_font_spec_entry).collect()),
},
)
})
.collect()
} else {
dfr.fontsets_lisp
.iter()
.map(|(name, data)| {
(
load_lisp_string(name),
FontsetDataSnapshot {
ranges: data
.ranges
.iter()
.map(|range| FontsetRangeEntrySnapshot {
from: range.from,
to: range.to,
entries: range
.entries
.iter()
.map(load_font_spec_entry)
.collect(),
})
.collect(),
fallback: data
.fallback
.as_ref()
.map(|entries| entries.iter().map(load_font_spec_entry).collect()),
},
)
})
.collect()
},
generation: dfr.generation,
};
restore_fontset_registry(snapshot);
}
fn load_color(c: &DumpColor) -> Color {
Color {
r: c.r,
g: c.g,
b: c.b,
a: c.a,
}
}
fn load_font_slant(s: &DumpFontSlant) -> FontSlant {
match s {
DumpFontSlant::Normal => FontSlant::Normal,
DumpFontSlant::Italic => FontSlant::Italic,
DumpFontSlant::Oblique => FontSlant::Oblique,
DumpFontSlant::ReverseItalic => FontSlant::ReverseItalic,
DumpFontSlant::ReverseOblique => FontSlant::ReverseOblique,
}
}
fn load_face(decoder: &mut LoadDecoder, df: &DumpFace) -> Face {
Face {
foreground: df.foreground.map(|c| load_color(&c)),
background: df.background.map(|c| load_color(&c)),
family: df
.family_value
.as_ref()
.map(|value| decoder.load_value(value))
.or_else(|| df.family.as_ref().map(Value::string)),
height: df.height.as_ref().map(|h| match h {
DumpFaceHeight::Absolute(n) => FaceHeight::Absolute(*n),
DumpFaceHeight::Relative(f) => FaceHeight::Relative(*f),
}),
weight: df.weight.map(FontWeight),
slant: df.slant.as_ref().map(load_font_slant),
underline: df.underline.as_ref().map(|u| Underline {
style: match u.style {
DumpUnderlineStyle::Line => UnderlineStyle::Line,
DumpUnderlineStyle::Wave => UnderlineStyle::Wave,
DumpUnderlineStyle::Dot => UnderlineStyle::Dot,
DumpUnderlineStyle::Dash => UnderlineStyle::Dash,
DumpUnderlineStyle::DoubleLine => UnderlineStyle::DoubleLine,
},
color: u.color.map(|c| load_color(&c)),
position: u.position,
}),
overline: df.overline,
strike_through: df.strike_through,
box_border: df.box_border.as_ref().map(|b| BoxBorder {
color: b.color.map(|c| load_color(&c)),
width: b.width,
style: match b.style {
DumpBoxStyle::Flat => BoxStyle::Flat,
DumpBoxStyle::Raised => BoxStyle::Raised,
DumpBoxStyle::Pressed => BoxStyle::Pressed,
},
}),
inverse_video: df.inverse_video,
stipple: df
.stipple_value
.as_ref()
.map(|value| decoder.load_value(value))
.or_else(|| df.stipple.as_ref().map(Value::string)),
extend: df.extend,
inherit: {
let syms: Vec<Value> = if !df.inherit_syms.is_empty() {
df.inherit_syms
.iter()
.map(|name| Value::from_sym_id(load_sym_id(name)))
.collect()
} else {
df.inherit
.iter()
.map(|name| Value::symbol(name.as_str()))
.collect()
};
match syms.len() {
0 => None,
1 => Some(syms[0]),
_ => Some(Value::list(syms)),
}
},
overstrike: df.overstrike,
doc: df
.doc_value
.as_ref()
.map(|value| decoder.load_value(value))
.or_else(|| df.doc.as_ref().map(Value::string)),
overline_color: None,
strike_through_color: None,
distant_foreground: None,
foundry: df
.foundry_value
.as_ref()
.map(|value| decoder.load_value(value))
.or_else(|| df.foundry.as_ref().map(Value::string)),
width: None,
}
}
pub(crate) fn load_face_table(decoder: &mut LoadDecoder, dft: &DumpFaceTable) -> FaceTable {
if !dft.face_ids.is_empty() {
FaceTable::from_dump_sym_ids(
dft.face_ids
.iter()
.map(|(k, f)| (load_sym_id(k), load_face(decoder, f)))
.collect(),
)
} else {
FaceTable::from_dump(
dft.faces
.iter()
.map(|(k, f)| (k.clone(), load_face(decoder, f)))
.collect(),
)
}
}
pub(crate) fn load_rectangle(dr: &DumpRectangleState) -> RectangleState {
RectangleState {
killed: dr.killed.iter().map(load_lisp_string).collect(),
}
}
pub(crate) fn load_kmacro(decoder: &mut LoadDecoder, dkm: &DumpKmacroManager) -> KmacroManager {
KmacroManager {
macro_ring: dkm
.macro_ring
.iter()
.map(|m| m.iter().map(|value| decoder.load_value(value)).collect())
.collect(),
counter: dkm.counter,
counter_format: dkm
.counter_format_lisp
.as_ref()
.map(load_lisp_string)
.or_else(|| {
dkm.counter_format.as_ref().map(|text| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(text, true)
})
})
.unwrap_or_else(|| crate::heap_types::LispString::from_utf8("%d")),
}
}
pub(crate) fn load_register_manager(
decoder: &mut LoadDecoder,
drm: &DumpRegisterManager,
) -> RegisterManager {
let registers: HashMap<char, RegisterContent> = drm
.registers
.iter()
.map(|(c, r)| {
(
*c,
match r {
DumpRegisterContent::Text {
data,
size,
size_byte,
} => RegisterContent::Text(LispString::from_dump(
data.clone(),
*size,
*size_byte,
)),
DumpRegisterContent::Number(n) => RegisterContent::Number(*n),
DumpRegisterContent::Marker(v) => {
RegisterContent::Marker(decoder.load_value(v))
}
DumpRegisterContent::Rectangle(lines) => {
RegisterContent::Rectangle(lines.iter().map(load_lisp_string).collect())
}
DumpRegisterContent::FrameConfig(v) => {
RegisterContent::FrameConfig(decoder.load_value(v))
}
DumpRegisterContent::File(s) => RegisterContent::File(load_lisp_string(s)),
DumpRegisterContent::KbdMacro(keys) => RegisterContent::KbdMacro(
keys.iter().map(|value| decoder.load_value(value)).collect(),
),
},
)
})
.collect();
RegisterManager::from_dump(registers)
}
pub(crate) fn load_bookmark_manager(dbm: &DumpBookmarkManager) -> BookmarkManager {
let bookmarks: HashMap<crate::emacs_core::bookmark::BookmarkKey, Bookmark> =
if !dbm.bookmarks_lisp.is_empty() {
dbm.bookmarks_lisp
.iter()
.map(|(k, b)| {
(
crate::emacs_core::bookmark::BookmarkKey::from_lisp_string(
&load_lisp_string(k),
),
Bookmark {
name: load_lisp_string(&b.name),
filename: b.filename.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
position: b.position,
front_context: b.front_context.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
rear_context: b.rear_context.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
annotation: b.annotation.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
handler: b.handler.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
},
)
})
.collect()
} else {
dbm.bookmarks
.iter()
.map(|(k, b)| {
(
crate::emacs_core::bookmark::BookmarkKey::from_lisp_string(
&crate::emacs_core::builtins::runtime_string_to_lisp_string(k, true),
),
Bookmark {
name: load_lisp_string(&b.name),
filename: b.filename.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
position: b.position,
front_context: b.front_context.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
rear_context: b.rear_context.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
annotation: b.annotation.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
handler: b.handler.as_deref().map(|s| {
crate::emacs_core::builtins::runtime_string_to_lisp_string(s, true)
}),
},
)
})
.collect()
};
BookmarkManager::from_dump(bookmarks, dbm.recent.iter().map(load_lisp_string).collect())
}
pub(crate) fn load_abbrev_manager(dam: &DumpAbbrevManager) -> AbbrevManager {
let tables: HashMap<SymId, AbbrevTable> = if !dam.tables_syms.is_empty() {
dam.tables_syms
.iter()
.map(|(sym, t)| {
(
load_sym_id(sym),
AbbrevTable {
name: load_lisp_string(&t.name),
abbrevs: t
.abbrevs
.iter()
.map(|(k, a)| {
(
load_lisp_string(k),
Abbrev {
expansion: load_lisp_string(&a.expansion),
hook: a.hook.as_ref().map(load_lisp_string),
count: a.count,
system: a.system,
},
)
})
.collect(),
parent: t.parent.as_ref().map(load_lisp_string),
case_fixed: t.case_fixed,
enable_quoting: t.enable_quoting,
},
)
})
.collect()
} else {
dam.tables
.iter()
.map(|(k, t)| {
(
intern::intern(k),
AbbrevTable {
name: load_lisp_string(&t.name),
abbrevs: t
.abbrevs
.iter()
.map(|(k, a)| {
(
load_lisp_string(k),
Abbrev {
expansion: load_lisp_string(&a.expansion),
hook: a.hook.as_ref().map(load_lisp_string),
count: a.count,
system: a.system,
},
)
})
.collect(),
parent: t.parent.as_ref().map(load_lisp_string),
case_fixed: t.case_fixed,
enable_quoting: t.enable_quoting,
},
)
})
.collect()
};
let global_table_sym = dam
.global_table_sym
.map(|sym| load_sym_id(&sym))
.unwrap_or_else(|| {
intern::intern(
&crate::emacs_core::builtins::runtime_string_from_lisp_string(&load_lisp_string(
&dam.global_table_name,
)),
)
});
AbbrevManager::from_dump(tables, global_table_sym, dam.abbrev_mode)
}
pub(crate) fn load_interactive_registry(
decoder: &mut LoadDecoder,
dir: &DumpInteractiveRegistry,
) -> InteractiveRegistry {
let specs: HashMap<SymId, InteractiveSpec> = dir
.specs
.iter()
.map(|(k, s)| {
(
load_sym_id(k),
InteractiveSpec {
spec: decoder.load_value(&s.spec),
},
)
})
.collect();
InteractiveRegistry::from_dump(specs)
}
pub(crate) fn load_watcher_list(
decoder: &mut LoadDecoder,
dwl: &DumpVariableWatcherList,
) -> VariableWatcherList {
let watchers: HashMap<SymId, Vec<VariableWatcher>> = dwl
.watchers
.iter()
.map(|(k, callbacks)| {
(
load_sym_id(k),
callbacks
.iter()
.map(|v| VariableWatcher {
callback: decoder.load_value(v),
})
.collect(),
)
})
.collect();
VariableWatcherList::from_dump(watchers)
}
pub(crate) fn load_string_text_prop_run(
decoder: &mut LoadDecoder,
r: &DumpStringTextPropertyRun,
) -> StringTextPropertyRun {
StringTextPropertyRun {
start: r.start,
end: r.end,
plist: decoder.load_value(&r.plist),
}
}
pub(crate) fn load_text_property_table(
decoder: &mut LoadDecoder,
intervals: &[DumpPropertyInterval],
) -> TextPropertyTable {
let mut table = TextPropertyTable::new();
for iv in intervals {
for (name, dump_val) in &iv.properties {
let val = decoder.load_value(dump_val);
table.put_property(iv.start, iv.end, decoder.load_value(name), val);
}
}
table
}