use crate::graphics_state::{GraphicsState, GstateEntry};
use crate::object::EntityId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StoreType {
String,
Array,
Dict,
}
#[derive(Debug, Clone)]
pub struct SaveRecord {
pub src: EntityId,
pub copy: EntityId,
pub store_type: StoreType,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct VmMarks {
pub string_data: usize,
pub string_entities: usize,
pub array_data: usize,
pub array_entities: usize,
pub dict_slots: usize,
pub dict_entities: usize,
}
pub struct SaveSnapshot {
pub d_stack_depth: usize,
pub packing_mode: bool,
pub vm_alloc_mode: bool,
pub object_format: i32,
pub gstate: GraphicsState,
pub gstate_stack: Vec<GstateEntry>,
pub gstate_store_len: usize,
pub marks: VmMarks,
}
pub struct SaveLevel {
pub level: u16,
pub save_id: u32,
pub records: Vec<SaveRecord>,
pub valid: bool,
pub d_stack_depth: usize,
pub packing_mode: bool,
pub vm_alloc_mode: bool,
pub object_format: i32,
pub gstate: GraphicsState,
pub gstate_stack: Vec<GstateEntry>,
pub gstate_store_len: usize,
pub marks: VmMarks,
}
pub struct SaveStack {
levels: Vec<SaveLevel>,
next_save_id: u32,
}
impl SaveStack {
pub fn new() -> Self {
Self {
levels: Vec::new(),
next_save_id: 1,
}
}
pub fn save(&mut self, snapshot: SaveSnapshot) -> (u16, u32) {
let SaveSnapshot {
d_stack_depth,
packing_mode,
vm_alloc_mode,
object_format,
gstate,
gstate_stack,
gstate_store_len,
marks,
} = snapshot;
let level = (self.levels.len() + 1) as u16;
let save_id = self.next_save_id;
self.next_save_id += 1;
self.levels.push(SaveLevel {
level,
save_id,
records: Vec::new(),
valid: true,
d_stack_depth,
packing_mode,
vm_alloc_mode,
object_format,
gstate,
gstate_stack,
gstate_store_len,
marks,
});
(level, save_id)
}
pub fn add_record(&mut self, record: SaveRecord) {
if let Some(level) = self.levels.last_mut() {
level.records.push(record);
}
}
pub fn restore(&mut self) -> Option<SaveLevel> {
self.levels.pop()
}
pub fn restore_to(&mut self, save_id: u32) -> Option<Vec<SaveLevel>> {
let idx = self.levels.iter().position(|l| l.save_id == save_id)?;
let popped: Vec<SaveLevel> = self.levels.drain(idx..).collect();
Some(popped)
}
pub fn current_level(&self) -> u16 {
self.levels.last().map(|l| l.level).unwrap_or(0)
}
pub fn last_save_id(&self) -> u32 {
self.levels.last().map(|l| l.save_id).unwrap_or(0)
}
pub fn is_valid(&self, save_id: u32) -> bool {
self.levels.iter().any(|l| l.save_id == save_id && l.valid)
}
pub fn depth(&self) -> usize {
self.levels.len()
}
pub fn levels_ref(&self) -> &[SaveLevel] {
&self.levels
}
pub fn invalidate_newer(&mut self, save_id: u32) {
let mut found = false;
for level in &mut self.levels {
if found {
level.valid = false;
}
if level.save_id == save_id {
found = true;
}
}
}
}
impl Default for SaveStack {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_save_and_depth() {
let mut ss = SaveStack::new();
assert_eq!(ss.depth(), 0);
assert_eq!(ss.current_level(), 0);
let (level, id) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
assert_eq!(level, 1);
assert_eq!(id, 1);
assert_eq!(ss.depth(), 1);
assert_eq!(ss.current_level(), 1);
}
#[test]
fn test_nested_save() {
let mut ss = SaveStack::new();
let (l1, _) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let (l2, _) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
assert_eq!(l1, 1);
assert_eq!(l2, 2);
assert_eq!(ss.depth(), 2);
assert_eq!(ss.current_level(), 2);
}
#[test]
fn test_restore() {
let mut ss = SaveStack::new();
let (_, id1) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
ss.add_record(SaveRecord {
src: EntityId(0),
copy: EntityId(1),
store_type: StoreType::String,
});
let level = ss.restore().unwrap();
assert_eq!(level.save_id, id1);
assert_eq!(level.records.len(), 1);
assert_eq!(ss.depth(), 0);
}
#[test]
fn test_is_valid() {
let mut ss = SaveStack::new();
let (_, id1) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
assert!(ss.is_valid(id1));
ss.restore();
assert!(!ss.is_valid(id1));
}
#[test]
fn test_invalidate_newer() {
let mut ss = SaveStack::new();
let (_, id1) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let (_, id2) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let (_, id3) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
ss.invalidate_newer(id1);
assert!(ss.is_valid(id1));
assert!(!ss.is_valid(id2));
assert!(!ss.is_valid(id3));
}
#[test]
fn test_add_record_to_current() {
let mut ss = SaveStack::new();
ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
ss.add_record(SaveRecord {
src: EntityId(0),
copy: EntityId(1),
store_type: StoreType::Array,
});
ss.add_record(SaveRecord {
src: EntityId(2),
copy: EntityId(3),
store_type: StoreType::Dict,
});
let level = ss.restore().unwrap();
assert_eq!(level.records.len(), 2);
}
#[test]
fn test_restore_empty() {
let mut ss = SaveStack::new();
assert!(ss.restore().is_none());
}
#[test]
fn test_d_stack_depth_snapshot() {
let mut ss = SaveStack::new();
ss.save(SaveSnapshot {
d_stack_depth: 5,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let level = ss.restore().unwrap();
assert_eq!(level.d_stack_depth, 5);
}
#[test]
fn test_unique_save_ids() {
let mut ss = SaveStack::new();
let (_, id1) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let (_, id2) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
ss.restore();
let (_, id3) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_ne!(id1, id3);
}
#[test]
fn test_save_level_numbers() {
let mut ss = SaveStack::new();
let (l1, _) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
let (l2, _) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
ss.restore();
let (l3, _) = ss.save(SaveSnapshot {
d_stack_depth: 3,
packing_mode: false,
vm_alloc_mode: false,
object_format: 0,
gstate: GraphicsState::new(),
gstate_stack: Vec::new(),
gstate_store_len: 0,
marks: VmMarks::default(),
});
assert_eq!(l1, 1);
assert_eq!(l2, 2);
assert_eq!(l3, 2);
}
}