Skip to main content

stet_core/
dual_array_store.rs

1// stet - A PostScript Interpreter
2// Copyright (c) 2026 Scott Bowman
3// SPDX-License-Identifier: Apache-2.0 OR MIT
4
5//! Dual-arena array storage: routes to global or local `ArrayStore`
6//! based on the tag bit in `EntityId`.
7
8use crate::array_store::ArrayStore;
9use crate::entity_table::EntityMeta;
10use crate::object::{EntityId, PsObject};
11
12/// Dual-arena array store with separate global and local backing stores.
13pub struct DualArrayStore {
14    pub global: ArrayStore,
15    pub local: ArrayStore,
16}
17
18impl DualArrayStore {
19    pub fn new() -> Self {
20        Self {
21            global: ArrayStore::new(),
22            local: ArrayStore::new(),
23        }
24    }
25
26    #[inline]
27    fn store(&self, entity: EntityId) -> &ArrayStore {
28        if entity.is_global() {
29            &self.global
30        } else {
31            &self.local
32        }
33    }
34
35    #[inline]
36    fn store_mut(&mut self, entity: EntityId) -> &mut ArrayStore {
37        if entity.is_global() {
38            &mut self.global
39        } else {
40            &mut self.local
41        }
42    }
43
44    // --- Allocation ---
45
46    /// Allocate `len` null-filled slots in local VM.
47    pub fn allocate(&mut self, len: usize) -> EntityId {
48        self.local.allocate(len)
49    }
50
51    /// Allocate and copy `items` into local VM.
52    pub fn allocate_from(&mut self, items: &[PsObject]) -> EntityId {
53        self.local.allocate_from(items)
54    }
55
56    /// Allocate and copy `items` with a specific save level and global flag.
57    pub fn allocate_from_with(
58        &mut self,
59        items: &[PsObject],
60        save_level: u16,
61        global: bool,
62        created_after_save: u32,
63    ) -> EntityId {
64        if global {
65            self.global
66                .allocate_from_with(items, save_level, global, created_after_save)
67        } else {
68            self.local
69                .allocate_from_with(items, save_level, global, created_after_save)
70        }
71    }
72
73    /// Allocate with a specific save level and global flag.
74    pub fn allocate_with(
75        &mut self,
76        len: usize,
77        save_level: u16,
78        global: bool,
79        created_after_save: u32,
80    ) -> EntityId {
81        if global {
82            self.global
83                .allocate_with(len, save_level, global, created_after_save)
84        } else {
85            self.local
86                .allocate_with(len, save_level, global, created_after_save)
87        }
88    }
89
90    // --- Access ---
91
92    /// Get a slice of array elements.
93    pub fn get(&self, entity: EntityId, start: u32, len: u32) -> &[PsObject] {
94        self.store(entity).get(entity, start, len)
95    }
96
97    /// Get a mutable slice of array elements.
98    pub fn get_mut(&mut self, entity: EntityId, start: u32, len: u32) -> &mut [PsObject] {
99        self.store_mut(entity).get_mut(entity, start, len)
100    }
101
102    /// Get a single element.
103    #[inline]
104    pub fn get_element(&self, entity: EntityId, index: u32) -> PsObject {
105        self.store(entity).get_element(entity, index)
106    }
107
108    /// Set a single element.
109    pub fn set_element(&mut self, entity: EntityId, index: u32, obj: PsObject) {
110        self.store_mut(entity).set_element(entity, index, obj);
111    }
112
113    // --- COW ---
114
115    /// COW copy (always local — global entities skip COW).
116    pub fn cow_copy(&mut self, entity: EntityId) -> EntityId {
117        debug_assert!(!entity.is_global(), "COW copy on global entity");
118        self.local.cow_copy(entity)
119    }
120
121    /// Swap offsets between two entities (used by restore, always local).
122    pub fn swap_offsets(&mut self, a: EntityId, b: EntityId) {
123        debug_assert!(
124            !a.is_global() && !b.is_global(),
125            "swap_offsets on global entity"
126        );
127        self.local.swap_offsets(a, b);
128    }
129
130    // --- Metadata access ---
131
132    /// Get entity metadata (read-only).
133    pub fn entity_meta(&self, entity: EntityId) -> &EntityMeta {
134        self.store(entity).entities.get(entity)
135    }
136
137    /// Get mutable entity metadata.
138    pub fn entity_meta_mut(&mut self, entity: EntityId) -> &mut EntityMeta {
139        self.store_mut(entity).entities.get_mut(entity)
140    }
141
142    // --- Stats ---
143
144    /// Total entity count across both stores.
145    pub fn entity_count(&self) -> usize {
146        self.local.entities.len() + self.global.entities.len()
147    }
148
149    /// Reset local VM (for job boundary cleanup).
150    pub fn reset_local(&mut self) {
151        self.local = ArrayStore::new();
152    }
153}
154
155impl Default for DualArrayStore {
156    fn default() -> Self {
157        Self::new()
158    }
159}