Compare commits
1 commit
main
...
querycache
Author | SHA1 | Date | |
---|---|---|---|
|
684cd7a46c |
6 changed files with 143 additions and 22 deletions
|
@ -160,6 +160,30 @@ impl OpSetInternal {
|
||||||
self.length
|
self.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hint_clear(&mut self, obj: &ObjId) {
|
||||||
|
if let Some(tree) = self.trees.get_mut(obj) {
|
||||||
|
tree.internal.cache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hint_delete(&mut self, pos: usize, obj: &ObjId) {
|
||||||
|
if let Some(tree) = self.trees.get_mut(obj) {
|
||||||
|
tree.internal.cache.delete(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hint_shift(&mut self, index: Option<usize>, pos: usize, obj: &ObjId) {
|
||||||
|
if let Some(tree) = self.trees.get_mut(obj) {
|
||||||
|
tree.internal.cache.shift(index, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn hint_insert(&mut self, index: usize, pos: usize, obj: &ObjId, element: &Op) {
|
||||||
|
if let Some(tree) = self.trees.get_mut(obj) {
|
||||||
|
tree.internal.cache.insert(index, pos, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, index: usize, obj: &ObjId, element: Op) {
|
pub(crate) fn insert(&mut self, index: usize, obj: &ObjId, element: Op) {
|
||||||
if let OpType::Make(typ) = element.action {
|
if let OpType::Make(typ) = element.action {
|
||||||
self.trees.insert(
|
self.trees.insert(
|
||||||
|
@ -180,6 +204,8 @@ impl OpSetInternal {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert_op(&mut self, obj: &ObjId, op: Op) -> Op {
|
pub(crate) fn insert_op(&mut self, obj: &ObjId, op: Op) -> Op {
|
||||||
|
self.hint_clear(obj);
|
||||||
|
|
||||||
let q = self.search(obj, query::SeekOp::new(&op));
|
let q = self.search(obj, query::SeekOp::new(&op));
|
||||||
|
|
||||||
let succ = q.succ;
|
let succ = q.succ;
|
||||||
|
@ -201,6 +227,9 @@ impl OpSetInternal {
|
||||||
op: Op,
|
op: Op,
|
||||||
observer: &mut Obs,
|
observer: &mut Obs,
|
||||||
) -> Op {
|
) -> Op {
|
||||||
|
// FIXME
|
||||||
|
self.hint_clear(obj);
|
||||||
|
|
||||||
let q = self.search(obj, query::SeekOpWithPatch::new(&op));
|
let q = self.search(obj, query::SeekOpWithPatch::new(&op));
|
||||||
|
|
||||||
let query::SeekOpWithPatch {
|
let query::SeekOpWithPatch {
|
||||||
|
|
|
@ -11,16 +11,77 @@ use crate::{
|
||||||
query::{self, Index, QueryResult, ReplaceArgs, TreeQuery},
|
query::{self, Index, QueryResult, ReplaceArgs, TreeQuery},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{ObjId, Op, OpId},
|
types::{Key, ObjId, Op, OpId},
|
||||||
ObjType,
|
ObjType,
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashSet, VecDeque};
|
||||||
|
|
||||||
pub(crate) const B: usize = 16;
|
pub(crate) const B: usize = 16;
|
||||||
|
|
||||||
mod iter;
|
mod iter;
|
||||||
pub(crate) use iter::OpTreeIter;
|
pub(crate) use iter::OpTreeIter;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub(crate) struct ListCachePoint {
|
||||||
|
pub(crate) index: usize, // list index
|
||||||
|
pub(crate) pos: usize, // op tree position
|
||||||
|
pub(crate) key: Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
|
pub(crate) struct QueryCache {
|
||||||
|
index: VecDeque<ListCachePoint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const CACHE_MAX: usize = 4;
|
||||||
|
|
||||||
|
impl QueryCache {
|
||||||
|
pub(crate) fn find(&self, index: usize) -> Option<&ListCachePoint> {
|
||||||
|
self.index.iter().find(|c| c.index == index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn insert(&mut self, index: usize, pos: usize, op: &Op) {
|
||||||
|
for c in &mut self.index {
|
||||||
|
if c.pos >= pos {
|
||||||
|
c.pos += 1;
|
||||||
|
c.index += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if self.index.len() >= CACHE_MAX {
|
||||||
|
self.index.pop_front();
|
||||||
|
}
|
||||||
|
self.index.push_back(ListCachePoint {
|
||||||
|
index,
|
||||||
|
pos,
|
||||||
|
key: op.elemid_or_key(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear(&mut self) {
|
||||||
|
self.index.truncate(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn delete(&mut self, pos: usize) {
|
||||||
|
for c in &mut self.index {
|
||||||
|
if c.pos >= pos {
|
||||||
|
c.index -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.index.retain(|c| c.pos + 1 != pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn shift(&mut self, index: Option<usize>, pos: usize) {
|
||||||
|
for c in &mut self.index {
|
||||||
|
if c.pos >= pos {
|
||||||
|
c.pos += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(index) = index {
|
||||||
|
self.index.retain(|c| c.index != index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub(crate) struct OpTree {
|
pub(crate) struct OpTree {
|
||||||
pub(crate) internal: OpTreeInternal,
|
pub(crate) internal: OpTreeInternal,
|
||||||
|
@ -46,6 +107,7 @@ impl OpTree {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct OpTreeInternal {
|
pub(crate) struct OpTreeInternal {
|
||||||
pub(crate) root_node: Option<OpTreeNode>,
|
pub(crate) root_node: Option<OpTreeNode>,
|
||||||
|
pub(crate) cache: QueryCache,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -59,7 +121,10 @@ pub(crate) struct OpTreeNode {
|
||||||
impl OpTreeInternal {
|
impl OpTreeInternal {
|
||||||
/// Construct a new, empty, sequence.
|
/// Construct a new, empty, sequence.
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
Self { root_node: None }
|
Self {
|
||||||
|
root_node: None,
|
||||||
|
cache: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the length of the sequence.
|
/// Get the length of the sequence.
|
||||||
|
@ -121,13 +186,15 @@ impl OpTreeInternal {
|
||||||
where
|
where
|
||||||
Q: TreeQuery<'a>,
|
Q: TreeQuery<'a>,
|
||||||
{
|
{
|
||||||
self.root_node
|
if !query.read_cache(&self.cache) {
|
||||||
.as_ref()
|
self.root_node
|
||||||
.map(|root| match query.query_node_with_metadata(root, m) {
|
.as_ref()
|
||||||
QueryResult::Descend => root.search(&mut query, m, None),
|
.map(|root| match query.query_node_with_metadata(root, m) {
|
||||||
QueryResult::Skip(skip) => root.search(&mut query, m, Some(skip)),
|
QueryResult::Descend => root.search(&mut query, m, None),
|
||||||
_ => true,
|
QueryResult::Skip(skip) => root.search(&mut query, m, Some(skip)),
|
||||||
});
|
_ => true,
|
||||||
|
});
|
||||||
|
}
|
||||||
query
|
query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::op_tree::{OpSetMetadata, OpTreeNode};
|
use crate::op_tree::{OpSetMetadata, OpTreeNode, QueryCache};
|
||||||
use crate::types::{Clock, Counter, Key, Op, OpId, OpType, ScalarValue};
|
use crate::types::{Clock, Counter, Key, Op, OpId, OpType, ScalarValue};
|
||||||
use fxhash::FxBuildHasher;
|
use fxhash::FxBuildHasher;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
@ -85,6 +85,12 @@ pub(crate) trait TreeQuery<'a> {
|
||||||
fn query_element(&mut self, _element: &'a Op) -> QueryResult {
|
fn query_element(&mut self, _element: &'a Op) -> QueryResult {
|
||||||
panic!("invalid element query")
|
panic!("invalid element query")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_cache(&mut self, _cache: &QueryCache) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_cache(&mut self, _cache: &mut QueryCache) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::error::AutomergeError;
|
use crate::error::AutomergeError;
|
||||||
use crate::op_tree::OpTreeNode;
|
use crate::op_tree::OpTreeNode;
|
||||||
use crate::query::{QueryResult, TreeQuery};
|
use crate::query::{QueryCache, QueryResult, TreeQuery};
|
||||||
use crate::types::{ElemId, Key, Op, HEAD};
|
use crate::types::{ElemId, Key, Op, HEAD};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
@ -46,16 +46,6 @@ impl InsertNth {
|
||||||
pub(crate) fn key(&self) -> Result<Key, AutomergeError> {
|
pub(crate) fn key(&self) -> Result<Key, AutomergeError> {
|
||||||
self.last_valid_insert
|
self.last_valid_insert
|
||||||
.ok_or(AutomergeError::InvalidIndex(self.target))
|
.ok_or(AutomergeError::InvalidIndex(self.target))
|
||||||
//if self.target == 0 {
|
|
||||||
/*
|
|
||||||
if self.last_insert.is_none() {
|
|
||||||
Ok(HEAD.into())
|
|
||||||
} else if self.seen == self.target && self.last_insert.is_some() {
|
|
||||||
Ok(Key::Seq(self.last_insert.unwrap()))
|
|
||||||
} else {
|
|
||||||
Err(AutomergeError::InvalidIndex(self.target))
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,4 +100,18 @@ impl<'a> TreeQuery<'a> for InsertNth {
|
||||||
self.n += 1;
|
self.n += 1;
|
||||||
QueryResult::Next
|
QueryResult::Next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AXIOM: ListCachePoint is only for single item inserts
|
||||||
|
// remove cache points on update
|
||||||
|
|
||||||
|
fn read_cache(&mut self, cache: &QueryCache) -> bool {
|
||||||
|
if self.target > 0 {
|
||||||
|
if let Some(c) = cache.find(self.target - 1) {
|
||||||
|
self.last_valid_insert = Some(c.key);
|
||||||
|
self.valid = Some(c.pos + 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,7 @@ impl TransactionInner {
|
||||||
let num = self.pending_ops();
|
let num = self.pending_ops();
|
||||||
// remove in reverse order so sets are removed before makes etc...
|
// remove in reverse order so sets are removed before makes etc...
|
||||||
for (obj, _prop, op) in self.operations.into_iter().rev() {
|
for (obj, _prop, op) in self.operations.into_iter().rev() {
|
||||||
|
doc.ops.hint_clear(&obj);
|
||||||
for pred_id in &op.pred {
|
for pred_id in &op.pred {
|
||||||
if let Some(p) = doc.ops.search(&obj, OpIdSearch::new(*pred_id)).index() {
|
if let Some(p) = doc.ops.search(&obj, OpIdSearch::new(*pred_id)).index() {
|
||||||
doc.ops.replace(&obj, p, |o| o.remove_succ(&op));
|
doc.ops.replace(&obj, p, |o| o.remove_succ(&op));
|
||||||
|
@ -169,7 +170,10 @@ impl TransactionInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !op.is_delete() {
|
if !op.is_delete() {
|
||||||
|
doc.ops.hint_shift((&prop).into(), pos, &obj);
|
||||||
doc.ops.insert(pos, &obj, op.clone());
|
doc.ops.insert(pos, &obj, op.clone());
|
||||||
|
} else {
|
||||||
|
doc.ops.hint_delete(pos, &obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.operations.push((obj, prop, op));
|
self.operations.push((obj, prop, op));
|
||||||
|
@ -223,6 +227,8 @@ impl TransactionInner {
|
||||||
insert: true,
|
insert: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let pos = query.pos();
|
||||||
|
doc.ops.hint_insert(index, pos, &obj, &op);
|
||||||
doc.ops.insert(query.pos(), &obj, op.clone());
|
doc.ops.insert(query.pos(), &obj, op.clone());
|
||||||
self.operations.push((obj, Prop::Seq(index), op));
|
self.operations.push((obj, Prop::Seq(index), op));
|
||||||
|
|
||||||
|
|
|
@ -575,3 +575,12 @@ impl From<Prop> for wasm_bindgen::JsValue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&Prop> for Option<usize> {
|
||||||
|
fn from(prop: &Prop) -> Self {
|
||||||
|
match prop {
|
||||||
|
Prop::Map(_) => None,
|
||||||
|
Prop::Seq(index) => Some(*index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue