Skip to content
This repository was archived by the owner on Jul 23, 2019. It is now read-only.

Commit 2d6c1b7

Browse files
author
Antonio Scandurra
committed
Cache mapping between offsets and points
When moving cursors, we heavily rely on `Buffer::len_for_row` to understand when such cursors should wrap. This method is implemented by traversing the tree twice: the first traversal gets the offset of the beginning of the requested line, whereas the second traversal gets the offset of the line next to the requested one. Then, the two offsets are subtracted to obtain the length of the line. With this commit we start caching the mapping between offsets and points. This allows to skip the two traversals and retrieve the length of a line in constant time if the line length had been computed before and no edit has occurred since then.
1 parent 29b886e commit 2d6c1b7

File tree

1 file changed

+46
-32
lines changed

1 file changed

+46
-32
lines changed

xray_core/src/buffer.rs

+46-32
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ pub struct Buffer {
2626
lamport_clock: LamportTimestamp,
2727
fragments: Tree<Fragment>,
2828
insertions: HashMap<ChangeId, Tree<FragmentMapping>>,
29-
position_cache: RefCell<HashMap<Anchor, (usize, Point)>>,
29+
anchor_cache: RefCell<HashMap<Anchor, (usize, Point)>>,
30+
offset_cache: RefCell<HashMap<Point, usize>>,
3031
pub version: NotifyCell<Version>
3132
}
3233

3334
#[derive(Clone, Copy, Debug)]
3435
pub struct Version(LocalTimestamp);
3536

36-
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
37+
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
3738
pub struct Point {
3839
pub row: u32,
3940
pub column: u32
@@ -146,7 +147,8 @@ impl Buffer {
146147
lamport_clock: 0,
147148
fragments,
148149
insertions: HashMap::new(),
149-
position_cache: RefCell::new(HashMap::new()),
150+
anchor_cache: RefCell::new(HashMap::new()),
151+
offset_cache: RefCell::new(HashMap::new()),
150152
version: NotifyCell::new(Version(0))
151153
}
152154
}
@@ -195,7 +197,8 @@ impl Buffer {
195197
local_timestamp: self.local_clock
196198
};
197199
self.splice_fragments(change_id, old_range, new_text);
198-
self.position_cache.borrow_mut().clear();
200+
self.anchor_cache.borrow_mut().clear();
201+
self.offset_cache.borrow_mut().clear();
199202
self.version.set(Version(self.local_clock));
200203
}
201204
}
@@ -392,17 +395,13 @@ impl Buffer {
392395
let fragment = cursor.item().unwrap();
393396
let offset_in_fragment = offset - cursor.start::<CharacterCount>().0;
394397
let offset_in_insertion = fragment.start_offset + offset_in_fragment;
398+
let point = cursor.start::<Point>() + &fragment.point_for_offset(offset_in_fragment)?;
395399
let anchor = Anchor(AnchorInner::Middle {
396400
insertion_id: fragment.insertion.id,
397401
offset: offset_in_insertion,
398402
bias
399403
});
400-
401-
if let Ok(mut position_cache) = self.position_cache.try_borrow_mut() {
402-
let point = cursor.start::<Point>() + &fragment.point_for_offset(offset_in_fragment)?;
403-
position_cache.insert(anchor.clone(), (offset, point.clone()));
404-
}
405-
404+
self.cache_position(Some(anchor.clone()), offset, point);
406405
Ok(anchor)
407406
}
408407

@@ -448,12 +447,8 @@ impl Buffer {
448447
offset: offset_in_insertion,
449448
bias
450449
});
451-
452-
if let Ok(mut position_cache) = self.position_cache.try_borrow_mut() {
453-
let offset = cursor.start::<CharacterCount>().0 + offset_in_fragment;
454-
position_cache.insert(anchor.clone(), (offset, point.clone()));
455-
}
456-
450+
let offset = cursor.start::<CharacterCount>().0 + offset_in_fragment;
451+
self.cache_position(Some(anchor.clone()), offset, point);
457452
Ok(anchor)
458453
}
459454

@@ -470,13 +465,13 @@ impl Buffer {
470465
&AnchorInner::Start => Ok((0, Point {row: 0, column: 0})),
471466
&AnchorInner::End => Ok((self.len(), self.fragments.len::<Point>())),
472467
&AnchorInner::Middle { ref insertion_id, offset, ref bias } => {
473-
let position = {
474-
let position_cache = self.position_cache.try_borrow();
475-
position_cache.ok().and_then(|position_cache| position_cache.get(&anchor).cloned())
468+
let cached_position = {
469+
let anchor_cache = self.anchor_cache.try_borrow().ok();
470+
anchor_cache.as_ref().and_then(|cache| cache.get(anchor).cloned())
476471
};
477472

478-
if position.is_some() {
479-
Ok(position.unwrap())
473+
if let Some(cached_position) = cached_position {
474+
Ok(cached_position)
480475
} else {
481476
let seek_bias = match bias {
482477
&AnchorBias::Left => SeekBias::Left,
@@ -497,11 +492,7 @@ impl Buffer {
497492
};
498493
let offset = fragments_cursor.start::<CharacterCount>().0 + overshoot;
499494
let point = fragments_cursor.start::<Point>() + &fragment.point_for_offset(overshoot)?;
500-
501-
if let Ok(mut position_cache) = self.position_cache.try_borrow_mut() {
502-
position_cache.insert(anchor.clone(), (offset, point));
503-
}
504-
495+
self.cache_position(Some(anchor.clone()), offset, point);
505496
Ok((offset, point))
506497
})
507498
})
@@ -511,19 +502,42 @@ impl Buffer {
511502
}
512503

513504
fn offset_for_point(&self, point: Point) -> Result<usize> {
514-
let mut fragments_cursor = self.fragments.cursor();
515-
fragments_cursor.seek(&point, SeekBias::Left);
516-
fragments_cursor.item().ok_or(Error::OffsetOutOfRange).map(|fragment| {
517-
let overshoot = fragment.offset_for_point(point - &fragments_cursor.start::<Point>()).unwrap();
518-
&fragments_cursor.start::<CharacterCount>().0 + &overshoot
519-
})
505+
let cached_offset = {
506+
let offset_cache = self.offset_cache.try_borrow().ok();
507+
offset_cache.as_ref().and_then(|cache| cache.get(&point).cloned())
508+
};
509+
510+
if let Some(cached_offset) = cached_offset {
511+
Ok(cached_offset)
512+
} else {
513+
let mut fragments_cursor = self.fragments.cursor();
514+
fragments_cursor.seek(&point, SeekBias::Left);
515+
fragments_cursor.item().ok_or(Error::OffsetOutOfRange).map(|fragment| {
516+
let overshoot = fragment.offset_for_point(point - &fragments_cursor.start::<Point>()).unwrap();
517+
let offset = &fragments_cursor.start::<CharacterCount>().0 + &overshoot;
518+
self.cache_position(None, offset, point);
519+
offset
520+
})
521+
}
520522
}
521523

522524
pub fn cmp_anchors(&self, a: &Anchor, b: &Anchor) -> Result<cmp::Ordering> {
523525
let a_offset = self.offset_for_anchor(a)?;
524526
let b_offset = self.offset_for_anchor(b)?;
525527
Ok(a_offset.cmp(&b_offset))
526528
}
529+
530+
fn cache_position(&self, anchor: Option<Anchor>, offset: usize, point: Point) {
531+
anchor.map(|anchor| {
532+
if let Ok(mut anchor_cache) = self.anchor_cache.try_borrow_mut() {
533+
anchor_cache.insert(anchor, (offset, point));
534+
}
535+
});
536+
537+
if let Ok(mut offset_cache) = self.offset_cache.try_borrow_mut() {
538+
offset_cache.insert(point, offset);
539+
}
540+
}
527541
}
528542

529543
impl Point {

0 commit comments

Comments
 (0)