use bumpalo::Bump;
use datavalue::OwnedDataValue;
use crate::Facts;
use crate::arena::DataValue;
const MAX_PATH_SEGMENTS: usize = crate::node::MAX_COMPILE_DEPTH;
#[derive(Debug, Clone, Default)]
pub(crate) struct Projection {
whole: bool,
children: Vec<(Box<str>, Projection)>,
}
impl Projection {
pub(crate) fn of(facts: &Facts) -> Option<Self> {
if !facts.reads_complete() {
return None;
}
let mut root = Projection::default();
for path in facts.reads() {
if path.is_root() || path.segments().len() > MAX_PATH_SEGMENTS {
return None;
}
root.insert(path.segments());
}
Some(root)
}
#[inline]
pub(crate) fn under(&self, key: &str) -> Option<&Projection> {
const SCAN_MAX: usize = 8;
if self.children.len() <= SCAN_MAX {
return self
.children
.iter()
.find(|(k, _)| **k == *key)
.map(|(_, n)| n);
}
self.children
.binary_search_by(|(k, _)| (**k).cmp(key))
.ok()
.map(|at| &self.children[at].1)
}
fn insert(&mut self, segments: &[String]) {
if self.whole {
return;
}
let Some((first, rest)) = segments.split_first() else {
self.whole = true;
self.children = Vec::new();
return;
};
let at = match self
.children
.binary_search_by(|(k, _)| (**k).cmp(first.as_str()))
{
Ok(at) => at,
Err(at) => {
self.children
.insert(at, (first.as_str().into(), Projection::default()));
at
}
};
self.children[at].1.insert(rest);
}
pub(crate) fn owned<'a>(&self, input: &'a OwnedDataValue, arena: &'a Bump) -> DataValue<'a> {
if self.whole {
return input.view_in(arena);
}
match input {
OwnedDataValue::Object(pairs) => {
let mut kept =
bumpalo::collections::Vec::with_capacity_in(self.children.len(), arena);
for (key, value) in pairs {
if let Some(child) = self.under(key) {
kept.push((key.as_str(), child.owned(value, arena)));
}
}
if pairs.len() >= crate::arena::value::ORDERED_PROBE_MIN_PAIRS
&& kept.len() > self.children.len()
{
return input.view_in(arena);
}
DataValue::Object(kept.into_bump_slice())
}
other => other.view_in(arena),
}
}
#[cfg(feature = "serde_json")]
pub(crate) fn serde<'a>(&self, input: &'a serde_json::Value, arena: &'a Bump) -> DataValue<'a> {
if self.whole {
return crate::arena::value_to_data(input, arena);
}
match input {
serde_json::Value::Object(map) => {
let mut kept =
bumpalo::collections::Vec::with_capacity_in(self.children.len(), arena);
for (key, child) in &self.children {
if let Some((key, value)) = map.get_key_value(&**key) {
kept.push((key.as_str(), child.serde(value, arena)));
}
}
DataValue::Object(kept.into_bump_slice())
}
other => crate::arena::value_to_data(other, arena),
}
}
}