1use crate::cell::*;
16use crate::machine::Machine;
17use std::collections::HashMap;
18
19#[derive(Clone, Default)]
20pub struct TermBuf {
21 pub cells: Vec<u64>,
22 pub root: Word,
23}
24
25pub fn copy_to_buf(m: &Machine, root: Word) -> TermBuf {
27 let mut buf = TermBuf::default();
28 let mut memo: HashMap<usize, usize> = HashMap::new();
29 let mut work: Vec<(usize, usize)> = Vec::new();
31 buf.root = xlate_out(m, root, &mut buf.cells, &mut memo, &mut work);
32 while let Some((src, dst)) = work.pop() {
33 let w = m.heap[src];
34 buf.cells[dst] = xlate_out(m, w, &mut buf.cells, &mut memo, &mut work);
35 }
36 buf
37}
38
39fn xlate_out(
40 m: &Machine,
41 w: Word,
42 cells: &mut Vec<u64>,
43 memo: &mut HashMap<usize, usize>,
44 work: &mut Vec<(usize, usize)>,
45) -> Word {
46 let w = m.deref(w);
47 let idx = payload(w) as usize;
48 match tag_of(w) {
49 TAG_ATOM | TAG_INT => w,
50 TAG_REF => {
51 if let Some(&b) = memo.get(&idx) {
53 return make(TAG_REF, b as u64);
54 }
55 let b = cells.len();
56 cells.push(make(TAG_REF, b as u64)); memo.insert(idx, b);
58 make(TAG_REF, b as u64)
59 }
60 TAG_STR => {
61 if let Some(&b) = memo.get(&idx) {
62 return make(TAG_STR, b as u64);
63 }
64 let (_, arity) = unpack_functor(m.heap[idx]);
65 let b = cells.len();
66 cells.push(m.heap[idx]); memo.insert(idx, b);
68 for k in 0..arity as usize {
69 cells.push(0);
70 work.push((idx + 1 + k, b + 1 + k));
71 }
72 make(TAG_STR, b as u64)
73 }
74 TAG_LST => {
75 if let Some(&b) = memo.get(&idx) {
76 return make(TAG_LST, b as u64);
77 }
78 let b = cells.len();
79 cells.push(0);
80 cells.push(0);
81 memo.insert(idx, b);
82 work.push((idx, b));
83 work.push((idx + 1, b + 1));
84 make(TAG_LST, b as u64)
85 }
86 TAG_FLT | TAG_BIG => {
87 if let Some(&b) = memo.get(&idx) {
88 return make(tag_of(w), b as u64);
89 }
90 let b = cells.len();
91 cells.push(m.heap[idx]); memo.insert(idx, b);
93 make(tag_of(w), b as u64)
94 }
95 _ => unreachable!("bad tag"),
96 }
97}
98
99pub fn restore_from_buf(m: &mut Machine, buf: &TermBuf) -> Word {
101 restore_cells(m, &buf.cells, buf.root)
102}
103
104pub fn restore_cells(m: &mut Machine, cells: &[Word], root: Word) -> Word {
110 let mut memo: HashMap<usize, usize> = HashMap::new();
111 let mut work: Vec<(usize, usize)> = Vec::new();
112 let root = xlate_in(m, cells, root, &mut memo, &mut work);
113 while let Some((src, dst)) = work.pop() {
114 let w = cells[src];
115 m.heap[dst] = xlate_in(m, cells, w, &mut memo, &mut work);
116 }
117 root
118}
119
120fn xlate_in(
121 m: &mut Machine,
122 cells: &[Word],
123 w: Word,
124 memo: &mut HashMap<usize, usize>,
125 work: &mut Vec<(usize, usize)>,
126) -> Word {
127 let idx = payload(w) as usize;
128 match tag_of(w) {
129 TAG_ATOM | TAG_INT => w,
130 TAG_REF => {
131 if let Some(&h) = memo.get(&idx) {
132 return make(TAG_REF, h as u64);
133 }
134 let v = m.new_var();
135 memo.insert(idx, payload(v) as usize);
136 v
137 }
138 TAG_STR => {
139 if let Some(&h) = memo.get(&idx) {
140 return make(TAG_STR, h as u64);
141 }
142 let (_, arity) = unpack_functor(cells[idx]);
143 let h = m.heap.len();
144 m.heap.push(cells[idx]);
145 memo.insert(idx, h);
146 for k in 0..arity as usize {
147 m.heap.push(0);
148 work.push((idx + 1 + k, h + 1 + k));
149 }
150 make(TAG_STR, h as u64)
151 }
152 TAG_LST => {
153 if let Some(&h) = memo.get(&idx) {
154 return make(TAG_LST, h as u64);
155 }
156 let h = m.heap.len();
157 m.heap.push(0);
158 m.heap.push(0);
159 memo.insert(idx, h);
160 work.push((idx, h));
161 work.push((idx + 1, h + 1));
162 make(TAG_LST, h as u64)
163 }
164 TAG_FLT | TAG_BIG => {
165 if let Some(&h) = memo.get(&idx) {
166 return make(tag_of(w), h as u64);
167 }
168 let h = m.heap.len();
169 m.heap.push(cells[idx]);
170 memo.insert(idx, h);
171 make(tag_of(w), h as u64)
172 }
173 _ => unreachable!("bad tag"),
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180 use plg_shared::StringInterner;
181
182 fn machine() -> Box<Machine> {
183 Machine::new(StringInterner::new(), Vec::new())
184 }
185
186 #[test]
187 fn copy_restore_roundtrips_structures() {
188 let mut m = machine();
189 let x = m.new_var();
190 let nil = make_atom(plg_shared::atom::ATOM_NIL);
192 let l1 = {
193 let i = m.heap.len();
194 m.heap.push(x);
195 m.heap.push(nil);
196 make(TAG_LST, i as u64)
197 };
198 let l0 = {
199 let i = m.heap.len();
200 m.heap.push(make_int(1));
201 m.heap.push(l1);
202 make(TAG_LST, i as u64)
203 };
204 let s = {
205 let i = m.heap.len();
206 m.heap.push(pack_functor(9, 3));
207 m.heap.push(make_atom(7));
208 m.heap.push(x);
209 m.heap.push(l0);
210 make(TAG_STR, i as u64)
211 };
212 let buf = copy_to_buf(&m, s);
213 let xi = payload(x) as usize;
215 m.bind(xi, make_int(42));
216
217 let r = restore_from_buf(&mut m, &buf);
218 let ri = payload(r) as usize;
219 let (f, n) = unpack_functor(m.heap[ri]);
220 assert_eq!((f, n), (9, 3));
221 let copied_x = m.deref(m.heap[ri + 2]);
223 assert_eq!(tag_of(copied_x), TAG_REF);
224 assert_ne!(payload(copied_x) as usize, xi);
225 let l = m.deref(m.heap[ri + 3]);
227 let li = payload(l) as usize;
228 let l2 = m.deref(m.heap[li + 1]);
229 let l2i = payload(l2) as usize;
230 assert_eq!(m.deref(m.heap[l2i]), copied_x, "var sharing preserved");
231 }
232
233 #[test]
234 fn copy_snapshots_bindings_at_copy_time() {
235 let mut m = machine();
236 let x = m.new_var();
237 m.bind(payload(x) as usize, make_atom(5));
238 let buf = copy_to_buf(&m, x);
239 assert_eq!(buf.root, make_atom(5), "bound var copies its value");
240 }
241
242 #[test]
243 fn long_list_copy_is_iterative() {
244 let mut m = machine();
246 let nil = make_atom(plg_shared::atom::ATOM_NIL);
247 let mut w = nil;
248 for i in 0..100_000 {
249 let idx = m.heap.len();
250 m.heap.push(make_int(i));
251 m.heap.push(w);
252 w = make(TAG_LST, idx as u64);
253 }
254 let buf = copy_to_buf(&m, w);
255 let r = restore_from_buf(&mut m, &buf);
256 assert_eq!(tag_of(r), TAG_LST);
257 }
258
259 #[test]
260 fn cyclic_terms_terminate() {
261 let mut m = machine();
262 let x = m.new_var();
263 let s = {
265 let i = m.heap.len();
266 m.heap.push(pack_functor(3, 1));
267 m.heap.push(x);
268 make(TAG_STR, i as u64)
269 };
270 m.bind(payload(x) as usize, s);
271 let buf = copy_to_buf(&m, s);
272 let r = restore_from_buf(&mut m, &buf);
273 assert_eq!(tag_of(r), TAG_STR, "cycle copied without divergence");
274 }
275}