use crate::expr::Seg;
use crate::expr::path;
use crate::expr::path::MAX_PATH_SEGMENTS;
use crate::expr::rules;
use datavalue::{DataValue, OwnedDataValue};
use std::collections::HashMap;
#[derive(Clone, Copy, Debug)]
pub(crate) struct At<'a> {
segs: &'a [Seg],
kinds: u64,
}
fn mask(n: usize) -> u64 {
if n >= MAX_PATH_SEGMENTS {
u64::MAX
} else {
(1 << n) - 1
}
}
impl<'a> At<'a> {
pub(crate) fn new(segs: &'a [Seg], kinds: u64) -> Self {
debug_assert!(
segs.len() <= MAX_PATH_SEGMENTS,
"a path is at most {MAX_PATH_SEGMENTS} segments deep"
);
Self {
segs,
kinds: kinds & mask(segs.len()),
}
}
pub(crate) fn len(&self) -> usize {
self.segs.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.segs.is_empty()
}
pub(crate) fn segs(&self) -> &'a [Seg] {
self.segs
}
pub(crate) fn is_index(&self, i: usize) -> bool {
self.kinds >> i & 1 == 1
}
pub(crate) fn index(&self, i: usize) -> Option<usize> {
if self.is_index(i) {
self.segs[i].index
} else {
None
}
}
pub(crate) fn prefix(&self, n: usize) -> Self {
Self {
segs: &self.segs[..n],
kinds: self.kinds & mask(n),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum Mark {
Set(Option<OwnedDataValue>),
Removed,
Appended {
from: usize,
owned: Option<Vec<OwnedDataValue>>,
},
}
#[derive(Debug, PartialEq)]
pub(crate) enum Lookup<'l> {
Clean,
Dirty,
Owned(Option<&'l OwnedDataValue>),
}
pub(super) fn absorb(above: &mut Mark, at: At<'_>, i: usize, new: Mark) {
match above {
Mark::Set(Some(value)) => {
let rest = &at.segs()[i..];
let applied = match new {
Mark::Set(Some(v)) => path::set(value, rest, v),
Mark::Removed => {
path::remove(value, rest);
true
}
Mark::Appended {
owned: Some(items), ..
} => path::append(value, rest, items).is_ok(),
Mark::Set(None) | Mark::Appended { owned: None, .. } => false,
};
if !applied {
*above = Mark::Set(None);
}
}
Mark::Set(None) => {}
Mark::Removed => *above = Mark::Set(None),
Mark::Appended { from, owned } => {
if matches!(at.index(i), Some(j) if j >= *from) {
*owned = None;
} else {
*above = Mark::Set(None);
}
}
}
}
pub(super) fn combine(old: Option<Mark>, new: Mark, has_children: bool) -> Mark {
match (old, new) {
(_, new @ (Mark::Set(_) | Mark::Removed)) => new,
(Some(Mark::Appended { from, owned: a }), Mark::Appended { owned: b, .. }) => {
let owned = match (a, b) {
(Some(mut a), Some(b)) => {
a.extend(b);
Some(a)
}
_ => None,
};
Mark::Appended { from, owned }
}
(
Some(Mark::Set(Some(mut v))),
Mark::Appended {
owned: Some(items), ..
},
) => match path::append(&mut v, &[], items) {
Ok(_) => Mark::Set(Some(v)),
Err(_) => Mark::Set(None),
},
(Some(_), Mark::Appended { .. }) => Mark::Set(None),
(None, new @ Mark::Appended { .. }) if !has_children => new,
(None, Mark::Appended { .. }) => Mark::Set(None),
}
}
pub(super) fn read_under<'l>(mark: &'l Mark, rest: &[Seg]) -> Lookup<'l> {
match mark {
Mark::Set(Some(v)) => Lookup::Owned(path::get(v, rest)),
Mark::Removed => Lookup::Owned(None),
Mark::Set(None) | Mark::Appended { .. } => Lookup::Dirty,
}
}
pub(super) fn removes_an_element(at: At<'_>, new: &Mark) -> bool {
matches!(new, Mark::Removed) && at.is_index(at.len() - 1)
}
fn settle_mark(mark: &mut Mark, here: Option<DataValue<'_>>) -> u32 {
match mark {
Mark::Set(slot @ None) => {
*slot = Some(here.expect("a marked path is in the view").to_owned());
1
}
Mark::Appended {
from,
owned: slot @ None,
} => match here {
Some(DataValue::Array(items)) => {
*slot = Some(items[*from..].iter().map(DataValue::to_owned).collect());
1
}
other => unreachable!("an appended path holds an array, not {other:?}"),
},
_ => 0,
}
}
fn apply_mark(parent: &mut OwnedDataValue, key: rules::Hop<'_>, mark: Mark) {
match mark {
Mark::Set(Some(v)) => {
rules::put(parent, key, v);
}
Mark::Removed => {
rules::take(parent, key);
}
Mark::Appended {
owned: Some(items), ..
} => match rules::owned_child_mut(parent, key) {
Some(OwnedDataValue::Array(existing)) => existing.extend(items),
_ => {
rules::put(parent, key, OwnedDataValue::Array(items));
}
},
Mark::Set(None) | Mark::Appended { owned: None, .. } => {
unreachable!("applied before settled")
}
}
}
fn reorder_keys(v: &mut OwnedDataValue, order: &[Box<str>]) {
let OwnedDataValue::Object(pairs) = v else {
return;
};
let mut first = HashMap::with_capacity(order.len());
for (i, key) in order.iter().enumerate() {
first.entry(&**key).or_insert(i);
}
let ranks: Vec<usize> = pairs
.iter()
.map(|(k, _)| first.get(k.as_str()).copied().unwrap_or(order.len()))
.collect();
let mut next = vec![0; order.len() + 2];
for &r in &ranks {
next[r + 1] += 1;
}
for r in 1..next.len() {
next[r] += next[r - 1];
}
let mut placed: Vec<Option<(String, OwnedDataValue)>> = Vec::new();
placed.resize_with(pairs.len(), || None);
for (pair, r) in pairs.drain(..).zip(ranks) {
placed[next[r]] = Some(pair);
next[r] += 1;
}
pairs.extend(placed.into_iter().flatten());
}
fn key_order(v: Option<DataValue<'_>>) -> Vec<Box<str>> {
match v {
Some(DataValue::Object(pairs)) => pairs.iter().map(|(k, _)| (*k).into()).collect(),
_ => Vec::new(),
}
}
pub(super) const NONE: u32 = u32::MAX;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum Key {
Root,
Name {
start: u32,
len: u32,
hash: u32,
},
Index(usize),
}
#[derive(Debug)]
pub(super) struct Node {
pub(super) key: Key,
pub(super) mark: Option<Mark>,
pub(super) reorder: bool,
order: Vec<Box<str>>,
pub(super) first: u32,
pub(super) last: u32,
pub(super) next: u32,
}
impl Node {
pub(super) fn new(key: Key) -> Self {
Self {
key,
mark: None,
reorder: false,
order: Vec::new(),
first: NONE,
last: NONE,
next: NONE,
}
}
}
#[derive(Debug)]
pub(crate) struct Writes {
pub(super) nodes: Vec<Node>,
names: String,
}
const WRITES_POOLED: usize = 16;
const WRITES_NODES_CAP: usize = 4096;
const WRITES_NAMES_CAP: usize = 64 << 10;
thread_local! {
pub(super) static WRITES_POOL: std::cell::RefCell<Vec<(Vec<Node>, String)>> = const { std::cell::RefCell::new(Vec::new()) };
}
impl Default for Writes {
fn default() -> Self {
let (nodes, names) = WRITES_POOL
.try_with(|p| p.borrow_mut().pop())
.ok()
.flatten()
.unwrap_or_default();
let mut log = Self { nodes, names };
log.reset();
log
}
}
impl Drop for Writes {
fn drop(&mut self) {
if self.nodes.capacity() > WRITES_NODES_CAP || self.names.capacity() > WRITES_NAMES_CAP {
return;
}
let mut nodes = std::mem::take(&mut self.nodes);
let mut names = std::mem::take(&mut self.names);
nodes.clear();
names.clear();
let _ = WRITES_POOL.try_with(|p| {
let mut p = p.borrow_mut();
if p.len() < WRITES_POOLED {
p.push((nodes, names));
}
});
}
}
impl Writes {
pub(super) fn reset(&mut self) {
self.nodes.clear();
self.nodes.push(Node::new(Key::Root));
self.names.clear();
}
pub(super) fn name(&self, start: u32, len: u32) -> &str {
&self.names[start as usize..(start + len) as usize]
}
fn key_ref(names: &str, key: Key) -> rules::Hop<'_> {
match key {
Key::Name { start, len, .. } => {
rules::Hop::Key(&names[start as usize..(start + len) as usize])
}
Key::Index(i) => rules::Hop::Index(i),
Key::Root => unreachable!("the root has no key"),
}
}
fn matches(&self, key: Key, at: At<'_>, i: usize) -> bool {
match key {
Key::Index(j) => at.index(i) == Some(j),
Key::Name { start, len, hash } => {
let seg = &at.segs()[i];
!at.is_index(i) && hash == seg.hash && self.name(start, len) == &*seg.key
}
Key::Root => false,
}
}
fn find(&self, parent: u32, at: At<'_>, i: usize) -> Option<u32> {
let mut c = self.nodes[parent as usize].first;
while c != NONE {
if self.matches(self.nodes[c as usize].key, at, i) {
return Some(c);
}
c = self.nodes[c as usize].next;
}
None
}
fn push_child(&mut self, parent: u32, key: Key) -> u32 {
let id = self.nodes.len() as u32;
self.nodes.push(Node::new(key));
let last = self.nodes[parent as usize].last;
if last == NONE {
self.nodes[parent as usize].first = id;
} else {
self.nodes[last as usize].next = id;
}
self.nodes[parent as usize].last = id;
id
}
fn child(&mut self, parent: u32, at: At<'_>, i: usize) -> u32 {
match at.index(i) {
Some(j) => {
if let Some(c) = self.find(parent, at, i) {
return c;
}
self.push_child(parent, Key::Index(j))
}
None => {
let seg = &at.segs()[i];
self.child_name(parent, &seg.key, seg.hash)
}
}
}
fn matches_name(&self, key: Key, name: &str, hash: u32) -> bool {
matches!(key, Key::Name { start, len, hash: h } if h == hash && self.name(start, len) == name)
}
fn child_name(&mut self, parent: u32, name: &str, hash: u32) -> u32 {
let mut c = self.nodes[parent as usize].first;
while c != NONE {
if self.matches_name(self.nodes[c as usize].key, name, hash) {
return c;
}
c = self.nodes[c as usize].next;
}
let start = self.names.len() as u32;
self.names.push_str(name);
let key = Key::Name {
start,
len: self.names.len() as u32 - start,
hash,
};
self.push_child(parent, key)
}
fn unlink_children(&mut self, n: u32) {
let node = &mut self.nodes[n as usize];
node.first = NONE;
node.last = NONE;
}
fn finish_mark(&mut self, n: u32, new: Mark) {
let has_children = self.nodes[n as usize].first != NONE;
let node = &mut self.nodes[n as usize];
let mark = combine(node.mark.take(), new, has_children);
let keep = matches!(mark, Mark::Appended { .. }) && !has_children;
node.reorder = false;
node.mark = Some(mark);
if !keep {
self.unlink_children(n);
}
}
}
impl Writes {
pub(crate) fn mark(&mut self, at: At<'_>, new: Mark) {
debug_assert!(!at.is_empty(), "a write has a path");
let element = removes_an_element(at, &new);
let mut new = new;
let mut n = 0u32;
for i in 0..at.len() {
if let Some(above) = self.nodes[n as usize].mark.as_mut() {
return absorb(above, at, i, new);
}
if element && i + 1 == at.len() {
new = Mark::Set(None);
break;
}
n = self.child(n, at, i);
}
self.finish_mark(n, new);
}
pub(crate) fn mark_root_key(&mut self, root: &str, field: Option<&str>, new: Mark) {
use crate::expr::path::key_hash;
let root_hash = key_hash(root);
let mut n = self.child_name(0, root, root_hash);
if let Some(field) = field {
let field_hash = key_hash(field);
if let Some(above) = self.nodes[n as usize].mark.as_mut() {
match above {
Mark::Set(Some(value)) => {
let applied = match new {
Mark::Set(Some(v)) => {
rules::put(value, rules::Hop::Key(field), v).is_some()
}
_ => false,
};
if !applied {
*above = Mark::Set(None);
}
}
Mark::Set(None) => {}
Mark::Removed | Mark::Appended { .. } => *above = Mark::Set(None),
}
return;
}
n = self.child_name(n, field, field_hash);
}
self.finish_mark(n, new);
}
pub(crate) fn reorder(&mut self, at: At<'_>) {
let mut n = 0u32;
for i in 0..at.len() {
if self.nodes[n as usize].mark.is_some() {
return;
}
n = self.child(n, at, i);
}
let node = &mut self.nodes[n as usize];
if node.mark.is_none() {
node.reorder = true;
}
}
pub(crate) fn lookup(&self, at: At<'_>) -> Lookup<'_> {
let mut n = 0u32;
for i in 0..at.len() {
if let Some(mark) = &self.nodes[n as usize].mark {
return read_under(mark, &at.segs()[i..]);
}
match self.find(n, at, i) {
Some(c) => n = c,
None => return Lookup::Clean,
}
}
let node = &self.nodes[n as usize];
match &node.mark {
Some(mark) => read_under(mark, &[]),
None if node.first == NONE && !node.reorder => Lookup::Clean,
None => Lookup::Dirty,
}
}
pub(crate) fn is_empty(&self) -> bool {
let root = &self.nodes[0];
root.first == NONE && !root.reorder
}
pub(crate) fn settle(&mut self, root: DataValue<'_>) -> u32 {
fn reads_view(node: &Node) -> bool {
match &node.mark {
Some(Mark::Set(None) | Mark::Appended { owned: None, .. }) => true,
Some(Mark::Set(Some(_)) | Mark::Removed | Mark::Appended { .. }) => false,
None => node.reorder || node.first != NONE,
}
}
fn walk(nodes: &mut [Node], names: &str, n: u32, here: Option<DataValue<'_>>) -> u32 {
if let Some(mark) = &mut nodes[n as usize].mark {
return settle_mark(mark, here);
}
if nodes[n as usize].reorder {
nodes[n as usize].order = key_order(here);
}
let mut converted = 0;
let mut c = nodes[n as usize].first;
while c != NONE {
let child = &nodes[c as usize];
let below = if reads_view(child) {
here.and_then(|v| rules::view_child(v, Writes::key_ref(names, child.key)))
} else {
None
};
converted += walk(nodes, names, c, below);
c = nodes[c as usize].next;
}
converted
}
let Self { nodes, names } = self;
walk(nodes, names, 0, Some(root))
}
pub(crate) fn apply(&mut self, owned: &mut OwnedDataValue) {
fn walk(nodes: &mut [Node], names: &str, n: u32, here: &mut OwnedDataValue) {
let mut c = nodes[n as usize].first;
while c != NONE {
let key = Writes::key_ref(names, nodes[c as usize].key);
match nodes[c as usize].mark.take() {
Some(mark) => apply_mark(here, key, mark),
None => {
if let Some(below) = rules::owned_child_mut(here, key) {
walk(nodes, names, c, below);
}
}
}
c = nodes[c as usize].next;
}
if nodes[n as usize].reorder {
reorder_keys(here, &nodes[n as usize].order);
}
}
let Self { nodes, names } = self;
walk(nodes, names, 0, owned);
self.reset();
}
}