1use std::cmp::Ordering;
21
22use crate::flat::{FlatChange, FlatOp, PathSeg, WireNode, WireRow};
23use crate::value::{compare_rows, owned_row, OwnedRow, Schema, Sort};
24
25#[derive(Clone, Debug)]
31pub struct RecvNode {
32 pub row: OwnedRow,
33 pub rc: u32,
34 pub rels: Vec<Vec<RecvNode>>,
35}
36
37#[derive(Debug)]
40pub struct Receiver {
41 schema: Schema,
42 top: Vec<RecvNode>,
43}
44
45impl Receiver {
46 pub fn new(schema: Schema) -> Receiver {
50 Receiver {
51 schema,
52 top: Vec::new(),
53 }
54 }
55
56 pub fn apply(&mut self, change: &FlatChange) {
60 apply_at(&mut self.top, &self.schema, &change.path, &change.op);
61 }
62
63 pub fn apply_all(&mut self, changes: &[FlatChange]) {
66 for c in changes {
67 self.apply(c);
68 }
69 }
70
71 pub fn top(&self) -> &[RecvNode] {
73 &self.top
74 }
75
76 pub fn schema(&self) -> &Schema {
78 &self.schema
79 }
80}
81
82#[inline]
83fn binary_search(list: &[RecvNode], row: &OwnedRow, sort: &Sort) -> Result<usize, usize> {
84 list.binary_search_by(|n| compare_rows(sort, &n.row, row))
85}
86
87fn apply_at(list: &mut Vec<RecvNode>, schema: &Schema, path: &[PathSeg], op: &FlatOp) {
91 let Some((seg, rest)) = path.split_first() else {
92 return apply_op(list, schema, op);
93 };
94 let Some(child_schema) = schema.rel_child(seg.rel) else {
97 return;
98 };
99 let pr = owned_row(seg.parent_row.clone());
101 let pos = match binary_search(list, &pr, &schema.sort) {
102 Ok(p) => p,
103 Err(_) => panic!("flat receiver: parent not found at path hop (inconsistent stream)"),
104 };
105 apply_at(&mut list[pos].rels[seg.rel.ix()], child_schema, rest, op);
106}
107
108fn apply_op(list: &mut Vec<RecvNode>, schema: &Schema, op: &FlatOp) {
109 match op {
110 FlatOp::Add(node) => apply_add(list, schema, node),
111 FlatOp::Remove { row } => apply_remove(list, schema, row),
112 FlatOp::Edit { old, new } => apply_edit(list, schema, old, new),
113 }
114}
115
116fn apply_add(list: &mut Vec<RecvNode>, schema: &Schema, node: &WireNode) {
118 let row = owned_row(node.row.clone());
119 match binary_search(list, &row, &schema.sort) {
120 Ok(p) => list[p].rc += 1,
121 Err(ins) => list.insert(ins, build_node(node, schema)),
122 }
123}
124
125fn build_node(node: &WireNode, schema: &Schema) -> RecvNode {
130 let mut rels: Vec<Vec<RecvNode>> = (0..schema.relationships.len())
131 .map(|_| Vec::new())
132 .collect();
133 for (slot, children) in &node.rels {
134 let Some(child_schema) = schema.rel_child(*slot) else {
135 continue; };
137 let mut built: Vec<RecvNode> = Vec::with_capacity(children.len());
138 for child in children {
139 let crow = owned_row(child.row.clone());
140 match binary_search(&built, &crow, &child_schema.sort) {
141 Ok(p) => built[p].rc += 1,
142 Err(ins) => built.insert(ins, build_node(child, child_schema)),
143 }
144 }
145 rels[slot.ix()] = built;
146 }
147 RecvNode {
148 row: owned_row(node.row.clone()),
149 rc: 1,
150 rels,
151 }
152}
153
154fn apply_remove(list: &mut Vec<RecvNode>, schema: &Schema, row: &WireRow) {
156 let r = owned_row(row.clone());
157 let pos = match binary_search(list, &r, &schema.sort) {
158 Ok(p) => p,
159 Err(_) => panic!("flat receiver: remove of non-existent node"),
160 };
161 if list[pos].rc == 1 {
162 list.remove(pos);
163 } else {
164 list[pos].rc -= 1;
165 }
166}
167
168fn apply_edit(list: &mut Vec<RecvNode>, schema: &Schema, old: &WireRow, new: &WireRow) {
171 let sort = &schema.sort;
172 let old_r = owned_row(old.clone());
173 let new_r = owned_row(new.clone());
174
175 if compare_rows(sort, &old_r, &new_r) == Ordering::Equal {
176 let pos = match binary_search(list, &old_r, sort) {
178 Ok(p) => p,
179 Err(_) => panic!("flat receiver: edit of non-existent node"),
180 };
181 list[pos].row = new_r;
182 return;
183 }
184
185 let old_pos = match binary_search(list, &old_r, sort) {
187 Ok(p) => p,
188 Err(_) => panic!("flat receiver: edit old node does not exist"),
189 };
190 let raw = binary_search(list, &new_r, sort);
191 let old_rc = list[old_pos].rc;
192 let found = raw.is_ok();
193 let pos = raw.unwrap_or_else(|e| e);
194
195 if old_rc == 1 && (pos == old_pos || pos.checked_sub(1) == Some(old_pos)) {
197 list[old_pos].row = new_r;
198 return;
199 }
200
201 let old_entry = list[old_pos].clone(); let new_rc = old_rc - 1;
204 let adjusted_pos;
205 if new_rc == 0 {
206 list.remove(old_pos);
207 adjusted_pos = if old_pos < pos { pos - 1 } else { pos };
208 } else {
209 list[old_pos].rc = new_rc; adjusted_pos = pos;
211 }
212 if found {
213 let existing_rc = list[adjusted_pos].rc;
215 list[adjusted_pos].row = new_r;
216 list[adjusted_pos].rc = existing_rc + 1;
217 } else {
218 let mut moved = old_entry;
220 moved.row = new_r;
221 moved.rc = 1;
222 list.insert(adjusted_pos, moved);
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use crate::flat::{FlatChange, FlatOp, PathSeg, WireNode};
230 use crate::value::{OwnedValue as V, RelDef, RelId, Schema};
231
232 fn kid_schema() -> Schema {
237 Schema::new(vec!["id", "parent"], vec![0], vec![(0, true)])
238 }
239 fn root_schema() -> Schema {
240 Schema::new(vec!["id", "val"], vec![0], vec![(1, true), (0, true)]).with_relationships(
241 vec![RelDef::related("kids", kid_schema()), RelDef::new("gate")],
242 )
243 }
244
245 fn wnode(cells: Vec<V>, rels: Vec<(RelId, Vec<WireNode>)>) -> WireNode {
246 WireNode { row: cells, rels }
247 }
248 fn add(cells: Vec<V>) -> FlatChange {
249 FlatChange {
250 path: vec![],
251 op: FlatOp::Add(wnode(cells, vec![])),
252 }
253 }
254 fn add_node(node: WireNode) -> FlatChange {
255 FlatChange {
256 path: vec![],
257 op: FlatOp::Add(node),
258 }
259 }
260 fn remove(cells: Vec<V>) -> FlatChange {
261 FlatChange {
262 path: vec![],
263 op: FlatOp::Remove { row: cells },
264 }
265 }
266 fn edit(old: Vec<V>, new: Vec<V>) -> FlatChange {
267 FlatChange {
268 path: vec![],
269 op: FlatOp::Edit { old, new },
270 }
271 }
272
273 fn ids(list: &[RecvNode]) -> Vec<i64> {
274 list.iter()
275 .map(|n| match n.row.col(0) {
276 crate::value::Value::Int(i) => i,
277 o => panic!("{o:?}"),
278 })
279 .collect()
280 }
281 fn rcs(list: &[RecvNode]) -> Vec<u32> {
282 list.iter().map(|n| n.rc).collect()
283 }
284
285 #[test]
286 fn add_remove_keep_sort_order_and_rc() {
287 let mut r = Receiver::new(root_schema());
288 r.apply(&add(vec![V::Int(1), V::Int(30)]));
290 r.apply(&add(vec![V::Int(2), V::Int(10)]));
291 r.apply(&add(vec![V::Int(3), V::Int(20)]));
292 assert_eq!(ids(r.top()), vec![2, 3, 1]); assert_eq!(rcs(r.top()), vec![1, 1, 1]);
294
295 r.apply(&add(vec![V::Int(3), V::Int(20)]));
297 assert_eq!(ids(r.top()), vec![2, 3, 1]);
298 assert_eq!(rcs(r.top()), vec![1, 2, 1]);
299
300 r.apply(&remove(vec![V::Int(3), V::Int(20)]));
302 assert_eq!(rcs(r.top()), vec![1, 1, 1]);
303 r.apply(&remove(vec![V::Int(3), V::Int(20)]));
305 assert_eq!(ids(r.top()), vec![2, 1]);
306 }
307
308 #[test]
309 fn edit_in_place_when_sort_key_unchanged() {
310 let mut r = Receiver::new(root_schema());
311 r.apply(&add(vec![V::Int(1), V::Int(10)]));
312 r.apply(&add(vec![V::Int(2), V::Int(20)]));
313 r.apply(&edit(
317 vec![V::Int(1), V::Int(10)],
318 vec![V::Int(1), V::Int(10)],
319 ));
320 assert_eq!(ids(r.top()), vec![1, 2]);
321 assert_eq!(rcs(r.top()), vec![1, 1]);
322 }
323
324 #[test]
325 fn edit_move_rc1_relocates() {
326 let mut r = Receiver::new(root_schema());
327 r.apply(&add(vec![V::Int(1), V::Int(10)]));
328 r.apply(&add(vec![V::Int(2), V::Int(20)]));
329 r.apply(&add(vec![V::Int(3), V::Int(30)]));
330 assert_eq!(ids(r.top()), vec![1, 2, 3]);
331 r.apply(&edit(
333 vec![V::Int(1), V::Int(10)],
334 vec![V::Int(1), V::Int(25)],
335 ));
336 assert_eq!(ids(r.top()), vec![2, 1, 3]); assert_eq!(rcs(r.top()), vec![1, 1, 1]);
338 }
339
340 #[test]
341 fn edit_move_with_ghost_when_rc_gt_1() {
342 let mut r = Receiver::new(root_schema());
343 r.apply(&add(vec![V::Int(1), V::Int(10)]));
344 r.apply(&add(vec![V::Int(1), V::Int(10)])); r.apply(&add(vec![V::Int(2), V::Int(30)]));
346 assert_eq!(ids(r.top()), vec![1, 2]);
347 assert_eq!(rcs(r.top()), vec![2, 1]);
348 r.apply(&edit(
351 vec![V::Int(1), V::Int(10)],
352 vec![V::Int(1), V::Int(40)],
353 ));
354 assert_eq!(ids(r.top()), vec![1, 2, 1]); assert_eq!(rcs(r.top()), vec![1, 1, 1]);
356 }
357
358 #[test]
359 fn edit_move_merges_into_existing_destination() {
360 let mut r = Receiver::new(root_schema());
361 r.apply(&add(vec![V::Int(1), V::Int(10)])); r.apply(&add(vec![V::Int(1), V::Int(10)])); r.apply(&add(vec![V::Int(1), V::Int(20)])); assert_eq!(rcs(r.top()), vec![2, 1]); r.apply(&edit(
367 vec![V::Int(1), V::Int(10)],
368 vec![V::Int(1), V::Int(20)],
369 ));
370 assert_eq!(ids(r.top()), vec![1, 1]);
372 assert_eq!(rcs(r.top()), vec![1, 2]);
373 }
374
375 #[test]
376 fn child_path_descends_and_in_view_gate_drops_gating_slot() {
377 let mut r = Receiver::new(root_schema());
378 let parent = wnode(
380 vec![V::Int(1), V::Int(10)],
381 vec![(RelId(0), vec![wnode(vec![V::Int(100), V::Int(1)], vec![])])],
382 );
383 r.apply(&add_node(parent));
384 assert_eq!(ids(r.top()), vec![1]);
385 assert_eq!(ids(&r.top()[0].rels[0]), vec![100]); r.apply(&FlatChange {
389 path: vec![PathSeg {
390 rel: RelId(0),
391 parent_row: vec![V::Int(1), V::Int(10)],
392 }],
393 op: FlatOp::Add(wnode(vec![V::Int(50), V::Int(1)], vec![])),
394 });
395 assert_eq!(ids(&r.top()[0].rels[0]), vec![50, 100]); r.apply(&FlatChange {
399 path: vec![PathSeg {
400 rel: RelId(1),
401 parent_row: vec![V::Int(1), V::Int(10)],
402 }],
403 op: FlatOp::Add(wnode(vec![V::Int(999), V::Int(1)], vec![])),
404 });
405 assert!(r.top()[0].rels[1].is_empty());
407 assert_eq!(ids(&r.top()[0].rels[0]), vec![50, 100]);
408 }
409}