This commit is contained in:
Bojan Serafimov
2023-01-10 15:32:18 -05:00
parent d0095d4457
commit c3f5e00ad1

View File

@@ -5,12 +5,21 @@ use std::ops::Range;
// results are not the same on some tests.
use rpds::RedBlackTreeMapSync;
/// Data structure that can efficiently:
/// - find the latest layer by lsn.end at a given key
/// - iterate the latest layers in a key range
/// - insert layers in non-decreasing lsn.start order
///
/// The struct is parameterized over Value for easier
/// testing, but in practice it's some sort of layer.
pub struct LayerCoverage<Value> {
/// Mapping key to the latest layer (if any) until the next key.
/// For every change in coverage (as we sweep the key space)
/// we store (lsn.end, value).
///
/// We use the Sync version of the map because we want Self to
/// be Sync. Using nonsync might be faster, if we can work with
/// that.
head: RedBlackTreeMapSync<i128, Option<(u64, Value)>>,
nodes: RedBlackTreeMapSync<i128, Option<(u64, Value)>>,
}
impl<T: Clone> Default for LayerCoverage<T> {
@@ -22,20 +31,25 @@ impl<T: Clone> Default for LayerCoverage<T> {
impl<Value: Clone> LayerCoverage<Value> {
pub fn new() -> Self {
Self {
head: RedBlackTreeMapSync::default(),
nodes: RedBlackTreeMapSync::default(),
}
}
/// Helper function to subdivide the key range without changing any values
///
/// Complexity: O(log N)
fn add_node(self: &mut Self, key: i128) {
let value = match self.head.range(..=key).last() {
let value = match self.nodes.range(..=key).last() {
Some((_, Some(v))) => Some(v.clone()),
Some((_, None)) => None,
None => None,
};
self.head.insert_mut(key, value);
self.nodes.insert_mut(key, value);
}
/// Insert a layer.
///
/// Complexity: worst case O(N), in practice O(log N). See not in implementation.
pub fn insert(self: &mut Self, key: Range<i128>, lsn: Range<u64>, value: Value) {
// NOTE The order of the following lines is important!!
@@ -47,12 +61,13 @@ impl<Value: Clone> LayerCoverage<Value> {
//
// NOTE This loop is worst case O(N), but amortized O(log N) in the special
// case when rectangles have no height. In practice I don't think we'll see
// the kind of layer intersections needed to trigger O(N) behavior. If we
// do it can be fixed using lazy propagation.
// the kind of layer intersections needed to trigger O(N) behavior. The worst
// case is N/2 horizontal layers overlapped with N/2 vertical layers in a
// grid pattern.
let mut to_update = Vec::new();
let mut to_remove = Vec::new();
let mut prev_covered = false;
for (k, node) in self.head.range(key.clone()) {
for (k, node) in self.nodes.range(key.clone()) {
let needs_cover = match node {
None => true,
Some((h, _)) => h < &lsn.end,
@@ -69,16 +84,19 @@ impl<Value: Clone> LayerCoverage<Value> {
to_remove.push(key.end);
}
for k in to_update {
self.head
self.nodes
.insert_mut(k.clone(), Some((lsn.end.clone(), value.clone())));
}
for k in to_remove {
self.head.remove_mut(&k);
self.nodes.remove_mut(&k);
}
}
/// Get the latest (by lsn.end) layer at a given key
///
/// Complexity: O(log N)
pub fn query(self: &Self, key: i128) -> Option<Value> {
self.head
self.nodes
.range(..=key)
.rev()
.next()?
@@ -87,24 +105,30 @@ impl<Value: Clone> LayerCoverage<Value> {
.map(|(_, v)| v.clone())
}
/// Iterate the changes in layer coverage in a given range. You will likely
/// want to start with self.query(key.start), and then follow up with self.range
///
/// Complexity: O(log N + result_size)
pub fn range(
self: &Self,
key: Range<i128>,
) -> impl '_ + Iterator<Item = (i128, Option<Value>)> {
self.head
self.nodes
.range(key)
.map(|(k, v)| (k.clone(), v.as_ref().map(|x| x.1.clone())))
}
/// Like range, but covers the entire key space
pub fn iter(self: &Self) -> impl '_ + Iterator<Item = (i128, Option<Value>)> {
self.head
self.nodes
.iter()
.map(|(k, v)| (k.clone(), v.as_ref().map(|x| x.1.clone())))
}
/// O(1) clone
pub fn clone(self: &Self) -> Self {
Self {
head: self.head.clone(),
nodes: self.nodes.clone(),
}
}
}