use std::io::{Cursor, Read};
use super::parse_atom::{parse_atom, parse_path};
use crate::allocator::{Allocator, NodePtr, SExp};
use crate::error::{EvalErr, Result};
use crate::traverse_path::{first_non_zero, msb_mask, traverse_path};
const BACK_REFERENCE: u8 = 0xfe;
const CONS_BOX_MARKER: u8 = 0xff;
#[repr(u8)]
enum ParseOp {
SExp,
Cons,
}
pub fn node_from_stream_backrefs(
allocator: &mut Allocator,
f: &mut Cursor<&[u8]>,
mut backref_callback: impl FnMut(NodePtr),
) -> Result<NodePtr> {
let mut values = Vec::<(NodePtr, Option<NodePtr>)>::new();
let mut ops = vec![ParseOp::SExp];
let mut b = [0; 1];
while let Some(op) = ops.pop() {
match op {
ParseOp::SExp => {
f.read_exact(&mut b)?;
if b[0] == CONS_BOX_MARKER {
ops.push(ParseOp::Cons);
ops.push(ParseOp::SExp);
ops.push(ParseOp::SExp);
} else if b[0] == BACK_REFERENCE {
let path = parse_path(f)?;
let back_reference = traverse_path_with_vec(allocator, path, &mut values)?;
backref_callback(back_reference);
allocator.add_ghost_pair(1)?;
values.push((back_reference, None));
} else {
let new_atom = parse_atom(allocator, b[0], f)?;
allocator.add_ghost_pair(1)?; values.push((new_atom, None));
}
}
ParseOp::Cons => {
let right = values.pop().expect("No cons without two vals.");
let left = values.pop().expect("No cons without two vals.");
let root_node = allocator.new_pair(left.0, right.0)?;
allocator.add_ghost_pair(1)?;
values.push((root_node, None));
}
}
}
Ok(values.pop().expect("Top of the stack").0)
}
fn node_from_stream_backrefs_old(
allocator: &mut Allocator,
f: &mut Cursor<&[u8]>,
mut backref_callback: impl FnMut(NodePtr),
) -> Result<NodePtr> {
let mut values = allocator.nil();
let mut ops = vec![ParseOp::SExp];
let mut b = [0; 1];
while let Some(op) = ops.pop() {
match op {
ParseOp::SExp => {
f.read_exact(&mut b)?;
if b[0] == CONS_BOX_MARKER {
ops.push(ParseOp::Cons);
ops.push(ParseOp::SExp);
ops.push(ParseOp::SExp);
} else if b[0] == BACK_REFERENCE {
let path = parse_path(f)?;
let reduction = traverse_path(allocator, path, values)?;
let back_reference = reduction.1;
backref_callback(back_reference);
values = allocator.new_pair(back_reference, values)?;
} else {
let new_atom = parse_atom(allocator, b[0], f)?;
values = allocator.new_pair(new_atom, values)?;
}
}
ParseOp::Cons => {
let SExp::Pair(right, rest) = allocator.sexp(values) else {
panic!("internal error");
};
let SExp::Pair(left, rest) = allocator.sexp(rest) else {
panic!("internal error");
};
let new_root = allocator.new_pair(left, right)?;
values = allocator.new_pair(new_root, rest)?;
}
}
}
match allocator.sexp(values) {
SExp::Pair(v1, _v2) => Ok(v1),
_ => panic!("unexpected atom"),
}
}
pub fn node_from_bytes_backrefs(allocator: &mut Allocator, b: &[u8]) -> Result<NodePtr> {
let mut buffer = Cursor::new(b);
node_from_stream_backrefs(allocator, &mut buffer, |_node| {})
}
pub fn node_from_bytes_backrefs_old(allocator: &mut Allocator, b: &[u8]) -> Result<NodePtr> {
let mut buffer = Cursor::new(b);
node_from_stream_backrefs_old(allocator, &mut buffer, |_node| {})
}
pub fn traverse_path_with_vec(
allocator: &mut Allocator,
node_index: &[u8],
args: &mut [(NodePtr, Option<NodePtr>)],
) -> Result<NodePtr> {
let mut parsing_sexp = args.is_empty();
let mut arg_index: usize = if parsing_sexp { 0 } else { args.len() - 1 };
let first_bit_byte_index = first_non_zero(node_index);
if first_bit_byte_index >= node_index.len() {
return Ok(NodePtr::NIL);
}
let last_bitmask = msb_mask(node_index[first_bit_byte_index]);
let mut byte_idx = node_index.len() - 1;
let mut bitmask = 0x01;
let mut sexp_to_parse = NodePtr::NIL;
while byte_idx > first_bit_byte_index || bitmask < last_bitmask {
let is_bit_set: bool = (node_index[byte_idx] & bitmask) != 0;
if parsing_sexp {
match allocator.sexp(sexp_to_parse) {
SExp::Atom => {
return Err(EvalErr::SerializationBackreferenceError);
}
SExp::Pair(left, right) => {
sexp_to_parse = if is_bit_set { right } else { left };
}
}
} else if is_bit_set {
if arg_index == 0 {
parsing_sexp = true;
} else {
arg_index -= 1;
}
} else {
parsing_sexp = true;
sexp_to_parse = args[arg_index].0;
}
if bitmask == 0x80 {
bitmask = 0x01;
byte_idx -= 1;
} else {
bitmask <<= 1;
}
}
if parsing_sexp {
return Ok(sexp_to_parse);
}
let mut backref_node = NodePtr::NIL;
for x in args.iter_mut().take(arg_index + 1) {
if let Some(pair) = x.1 {
backref_node = pair;
continue;
}
allocator.remove_ghost_pair(1)?;
backref_node = allocator.new_pair(x.0, backref_node)?;
x.1 = Some(backref_node);
}
Ok(backref_node)
}
#[cfg(test)]
mod tests {
use crate::test_ops::node_eq;
use super::*;
use rstest::rstest;
use hex::FromHex;
#[rstest]
#[case(
"ff86666f6f626172ff86666f6f62617280",
"9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba"
)]
#[case(
"fffe0100",
"8b7bb45fbbdd29c84edcab98274c6e084f19af64d24c65a475562dbbfee67735"
)]
#[case(
"ff86666f6f626172fe01",
"9148834131750904c023598bed28db269bdb29012514579e723d63e27829bcba"
)]
#[case(
"ffff01ff02ff03ff0480ff01ff02ff03ff0480",
"028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8"
)]
#[case(
"ffff01ff02ff03ff0480fe02",
"028c16eb4fec600e6153d8dde60eb3916d13d0dc446b5cd7936a1248f8963bf8"
)]
#[case(
"ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff04\
05ff0607ff0809ff0aff9b615f766572795f6c6f6e675f72657065617465645f737472696e6780",
"e23c73777f814e8a4e2785487b272b8b22ddaded1f7cfb808b43f1148602882f"
)]
#[case(
"ffffffffff9b615f766572795f6c6f6e675f72657065617465645f737472696e6701ff0203ffff0405ff0607ff0809ff0afffe4180",
"e23c73777f814e8a4e2785487b272b8b22ddaded1f7cfb808b43f1148602882f"
)]
fn test_deserialize_with_backrefs(
#[case] serialization_as_hex: &str,
#[case] expected_hash_as_hex: &str,
) {
use crate::serde::object_cache::{ObjectCache, treehash};
let buf = Vec::from_hex(serialization_as_hex).unwrap();
let mut allocator = Allocator::new();
let node = node_from_bytes_backrefs(&mut allocator, &buf).unwrap();
let old_node = node_from_bytes_backrefs_old(&mut allocator, &buf).unwrap();
let mut oc = ObjectCache::new(treehash);
let calculated_hash = oc.get_or_calculate(&allocator, &node, None).unwrap();
let ch: &[u8] = calculated_hash;
let expected_hash: Vec<u8> = Vec::from_hex(expected_hash_as_hex).unwrap();
assert_eq!(expected_hash, ch);
let calculated_hash = oc.get_or_calculate(&allocator, &old_node, None).unwrap();
let ch: &[u8] = calculated_hash;
assert_eq!(expected_hash, ch);
}
#[test]
fn test_counters() {
use crate::allocator::Allocator;
let mut a = Allocator::new();
let cp = a.checkpoint();
a.add_ghost_pair(1).unwrap();
assert_eq!(a.pair_count(), 1);
a.restore_checkpoint(&cp);
let buf = Vec::from_hex("0a").unwrap();
let _node = node_from_bytes_backrefs(&mut a, &buf).unwrap();
let pair_count = a.pair_count();
let allocated_pair_count = a.allocated_pair_count();
a.restore_checkpoint(&cp);
let _old_node = node_from_bytes_backrefs_old(&mut a, &buf).unwrap();
let old_pair_count = a.pair_count();
let old_allocated_pair_count = a.allocated_pair_count();
assert_eq!(allocated_pair_count, 0);
assert_eq!(old_allocated_pair_count, 1);
assert_eq!(pair_count, old_pair_count);
}
#[test]
fn test_traverse_path_with_vec() {
use crate::allocator::Allocator;
let mut a = Allocator::new();
let nul = a.nil();
let n1 = a.new_atom(&[0, 1, 2]).unwrap();
let n2 = a.new_atom(&[4, 5, 6]).unwrap();
let mut list: Vec<(NodePtr, Option<NodePtr>)> = vec![(n1, None)];
assert_eq!(traverse_path_with_vec(&mut a, &[], &mut list).unwrap(), nul);
assert_eq!(
traverse_path_with_vec(&mut a, &[0], &mut list).unwrap(),
nul
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0, 0], &mut list).unwrap(),
nul
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0, 0, 0], &mut list).unwrap(),
nul
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0, 0, 0, 0], &mut list).unwrap(),
nul
);
let mut list: Vec<(NodePtr, Option<NodePtr>)> = vec![(n1, None), (n2, None)];
let list_node_part = a.new_pair(n1, NodePtr::NIL).unwrap();
let list_node = a.new_pair(n2, list_node_part).unwrap();
assert_eq!(
traverse_path_with_vec(&mut a, &[0b10], &mut list).unwrap(),
n2
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b101], &mut list).unwrap(),
n1
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b111], &mut list).unwrap(),
nul
);
assert!(list[0].1.is_none());
assert!(list[1].1.is_none());
a.add_ghost_pair(2).unwrap();
let test_val = traverse_path_with_vec(&mut a, &[0b1], &mut list).unwrap();
assert!(node_eq(&a, test_val, list_node));
assert!(node_eq(&a, list[0].1.unwrap(), list_node_part));
assert!(node_eq(&a, list[1].1.unwrap(), list_node));
let test_val = traverse_path_with_vec(&mut a, &[0b1], &mut list).unwrap();
assert!(node_eq(&a, test_val, list_node));
let mut list: Vec<(NodePtr, Option<NodePtr>)> =
vec![(n1, Some(list_node_part)), (n2, Some(list_node))];
assert_eq!(
traverse_path_with_vec(&mut a, &[0b1], &mut list).unwrap(),
list_node
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b11], &mut list).unwrap(),
list_node_part
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b10], &mut list).unwrap(),
n2
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b101], &mut list).unwrap(),
n1
);
assert_eq!(
traverse_path_with_vec(&mut a, &[0b111], &mut list).unwrap(),
nul
);
assert!(traverse_path_with_vec(&mut a, &[0b1011], &mut list).is_err());
assert!(traverse_path_with_vec(&mut a, &[0b1111], &mut list).is_err());
assert!(traverse_path_with_vec(&mut a, &[0b1101], &mut list).is_err());
assert!(traverse_path_with_vec(&mut a, &[0b1001], &mut list).is_err());
assert!(traverse_path_with_vec(&mut a, &[0b1010], &mut list).is_err());
assert!(traverse_path_with_vec(&mut a, &[0b1110], &mut list).is_err());
}
}