use core::ops::Range;
#[cfg(not(feature = "std"))]
use crate::nostd_prelude::*;
use crate::{Entry, Value};
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
pub struct RowWatch {
rules: Vec<(Vec<u8>, Vec<Vec<u8>>)>,
}
impl RowWatch {
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with_prefix(mut self, prefix: impl Into<Vec<u8>>, fields: Vec<Vec<u8>>) -> Self {
self.rules.push((prefix.into(), fields));
self
}
pub fn is_empty(&self) -> bool {
self.rules.is_empty()
}
pub fn rules(&self) -> usize {
self.rules.len()
}
#[inline]
fn mask_of(&self, key: &[u8]) -> u64 {
let mut m = 0u64;
for (i, (p, _)) in self.rules.iter().enumerate().take(64) {
if key.starts_with(p) {
m |= 1 << i;
}
}
m
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Was {
Absent,
NotHash,
Hash,
Cold,
}
#[derive(Debug, Clone)]
struct Item {
key: Range<u32>,
was: Was,
mask: u64,
first: u32,
#[cfg_attr(not(all(feature = "std", not(target_arch = "wasm32"))), allow(dead_code))]
cold: Option<crate::value_cold::ColdRef>,
}
#[derive(Debug, Clone, Default)]
pub struct RowChanges {
reset: bool,
bytes: Vec<u8>,
items: Vec<Item>,
vals: Vec<Option<Range<u32>>>,
widths: Vec<u32>,
}
impl RowChanges {
pub fn is_reset(&self) -> bool {
self.reset
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn iter(&self) -> impl Iterator<Item = RowChange<'_>> {
(0..self.items.len()).map(move |i| RowChange { c: self, i })
}
fn clear(&mut self) {
self.reset = false;
self.bytes.clear();
self.items.clear();
self.vals.clear();
}
}
#[derive(Debug, Clone, Copy)]
pub struct RowChange<'a> {
c: &'a RowChanges,
i: usize,
}
impl<'a> RowChange<'a> {
fn item(&self) -> &'a Item {
&self.c.items[self.i]
}
pub fn key(&self) -> &'a [u8] {
let r = &self.item().key;
&self.c.bytes[r.start as usize..r.end as usize]
}
pub fn was_hash(&self) -> bool {
self.item().was == Was::Hash
}
pub fn field(&self, rule: usize, field: usize) -> Option<&'a [u8]> {
let it = self.item();
if it.was != Was::Hash
|| rule >= 64
|| it.mask & (1 << rule) == 0
|| field >= self.c.widths[rule] as usize
{
return None;
}
let before: u32 =
(0..rule).filter(|r| it.mask & (1 << r) != 0).map(|r| self.c.widths[r]).sum();
let r = self.c.vals.get((it.first + before) as usize + field)?.clone()?;
Some(&self.c.bytes[r.start as usize..r.end as usize])
}
}
fn field_of<'v>(v: &'v Value, f: &[u8]) -> Option<&'v [u8]> {
match v {
Value::Hash(h) => h.get(f).map(|b| b.as_slice()),
Value::SegHash(h) => h.get(f).map(|b| b.as_slice()),
Value::SmallHashInline(h) => h.get(f),
Value::PackedRow(r) => r.get_named(f),
_ => None,
}
}
fn is_hash(v: &Value) -> bool {
matches!(
v,
Value::Hash(_) | Value::SegHash(_) | Value::SmallHashInline(_) | Value::PackedRow(_)
)
}
const INDEXED_PAST: usize = 16;
#[derive(Debug, Default)]
pub(crate) struct Journal {
watch: RowWatch,
changes: RowChanges,
index: kevy_map::KevySet<crate::SmallBytes>,
}
impl Journal {
pub(crate) fn new(watch: RowWatch) -> Journal {
let widths = watch.rules.iter().map(|(_, f)| f.len() as u32).collect();
Journal {
watch,
changes: RowChanges { widths, ..RowChanges::default() },
index: Default::default(),
}
}
pub(crate) fn watch(&self) -> &RowWatch {
&self.watch
}
#[inline]
pub(crate) fn note(&mut self, key: &[u8], cur: Option<&Entry>) {
let mask = self.watch.mask_of(key);
if mask == 0 || self.holds(key) {
return;
}
self.record(key, mask, cur.map(|e| &e.value));
}
fn holds(&mut self, key: &[u8]) -> bool {
let c = &self.changes;
if c.items.len() <= INDEXED_PAST {
return c
.items
.iter()
.any(|it| &c.bytes[it.key.start as usize..it.key.end as usize] == key);
}
if self.index.is_empty() {
for it in &c.items {
self.index.insert(crate::SmallBytes::from_slice(
&c.bytes[it.key.start as usize..it.key.end as usize],
));
}
}
!self.index.insert(crate::SmallBytes::from_slice(key))
}
fn record(&mut self, key: &[u8], mask: u64, v: Option<&Value>) {
let c = &mut self.changes;
let start = c.bytes.len() as u32;
c.bytes.extend_from_slice(key);
let key_range = start..c.bytes.len() as u32;
let (was, cold) = match v {
None => (Was::Absent, None),
Some(Value::Cold(r)) if r.type_tag == crate::value_cold::COLD_TAG_HASH => {
(Was::Cold, Some(*r))
}
Some(v) if is_hash(v) => (Was::Hash, None),
Some(_) => (Was::NotHash, None),
};
let first = c.vals.len() as u32;
c.items.push(Item { key: key_range, was, mask, first, cold });
if was == Was::Hash {
let v = v.expect("a hash is a value");
self.project(mask, v);
}
}
fn project(&mut self, mask: u64, v: &Value) {
let c = &mut self.changes;
for (r, (_, fields)) in self.watch.rules.iter().enumerate().take(64) {
if mask & (1 << r) == 0 {
continue;
}
for f in fields {
let range = field_of(v, f).map(|b| {
let s = c.bytes.len() as u32;
c.bytes.extend_from_slice(b);
s..c.bytes.len() as u32
});
c.vals.push(range);
}
}
}
pub(crate) fn reset(&mut self) {
self.index.clear();
self.changes.clear();
self.changes.reset = true;
}
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
pub(crate) fn pending_cold(&self) -> Vec<(usize, Vec<u8>, crate::value_cold::ColdRef)> {
let c = &self.changes;
c.items
.iter()
.enumerate()
.filter(|(_, it)| it.was == Was::Cold)
.map(|(i, it)| {
(
i,
c.bytes[it.key.start as usize..it.key.end as usize].to_vec(),
it.cold.expect("a cold row"),
)
})
.collect()
}
#[cfg(all(feature = "std", not(target_arch = "wasm32")))]
pub(crate) fn resolve(&mut self, i: usize, v: &Value) {
let mask = self.changes.items[i].mask;
let first = self.changes.vals.len() as u32;
let it = &mut self.changes.items[i];
(it.was, it.first) = (Was::Hash, first);
self.project(mask, v);
}
pub(crate) fn take(&mut self, mut spare: RowChanges) -> RowChanges {
self.index.clear();
spare.clear();
spare.widths.clone_from(&self.changes.widths);
core::mem::replace(&mut self.changes, spare)
}
pub(crate) fn is_empty(&self) -> bool {
!self.changes.reset && self.changes.items.is_empty()
}
}