locator.rs

  1use lazy_static::lazy_static;
  2use smallvec::{smallvec, SmallVec};
  3use std::iter;
  4
  5lazy_static! {
  6    static ref MIN: Locator = Locator::min();
  7    static ref MAX: Locator = Locator::max();
  8}
  9
 10/// An identifier for a position in a ordered collection.
 11///
 12/// Allows prepending and appending without needing to renumber existing locators
 13/// using `Locator::between(lhs, rhs)`.
 14///
 15/// The initial location for a collection should be `Locator::between(Locator::min(), Locator::max())`,
 16/// leaving room for items to be inserted before and after it.
 17#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 18pub struct Locator(SmallVec<[u64; 4]>);
 19
 20impl Locator {
 21    pub fn min() -> Self {
 22        Self(smallvec![u64::MIN])
 23    }
 24
 25    pub fn max() -> Self {
 26        Self(smallvec![u64::MAX])
 27    }
 28
 29    pub fn min_ref() -> &'static Self {
 30        &MIN
 31    }
 32
 33    pub fn max_ref() -> &'static Self {
 34        &MAX
 35    }
 36
 37    pub fn assign(&mut self, other: &Self) {
 38        self.0.resize(other.0.len(), 0);
 39        self.0.copy_from_slice(&other.0);
 40    }
 41
 42    pub fn between(lhs: &Self, rhs: &Self) -> Self {
 43        let lhs = lhs.0.iter().copied().chain(iter::repeat(u64::MIN));
 44        let rhs = rhs.0.iter().copied().chain(iter::repeat(u64::MAX));
 45        let mut location = SmallVec::new();
 46        for (lhs, rhs) in lhs.zip(rhs) {
 47            let mid = lhs + ((rhs.saturating_sub(lhs)) >> 48);
 48            location.push(mid);
 49            if mid > lhs {
 50                break;
 51            }
 52        }
 53        Self(location)
 54    }
 55
 56    pub fn len(&self) -> usize {
 57        self.0.len()
 58    }
 59
 60    pub fn is_empty(&self) -> bool {
 61        self.len() == 0
 62    }
 63}
 64
 65impl Default for Locator {
 66    fn default() -> Self {
 67        Self::min()
 68    }
 69}
 70
 71impl sum_tree::Item for Locator {
 72    type Summary = Locator;
 73
 74    fn summary(&self) -> Self::Summary {
 75        self.clone()
 76    }
 77}
 78
 79impl sum_tree::KeyedItem for Locator {
 80    type Key = Locator;
 81
 82    fn key(&self) -> Self::Key {
 83        self.clone()
 84    }
 85}
 86
 87impl sum_tree::Summary for Locator {
 88    type Context = ();
 89
 90    fn add_summary(&mut self, summary: &Self, _: &()) {
 91        self.assign(summary);
 92    }
 93}
 94
 95#[cfg(test)]
 96mod tests {
 97    use super::*;
 98    use rand::prelude::*;
 99    use std::mem;
100
101    #[gpui::test(iterations = 100)]
102    fn test_locators(mut rng: StdRng) {
103        let mut lhs = Default::default();
104        let mut rhs = Default::default();
105        while lhs == rhs {
106            lhs = Locator(
107                (0..rng.gen_range(1..=5))
108                    .map(|_| rng.gen_range(0..=100))
109                    .collect(),
110            );
111            rhs = Locator(
112                (0..rng.gen_range(1..=5))
113                    .map(|_| rng.gen_range(0..=100))
114                    .collect(),
115            );
116        }
117
118        if lhs > rhs {
119            mem::swap(&mut lhs, &mut rhs);
120        }
121
122        let middle = Locator::between(&lhs, &rhs);
123        assert!(middle > lhs);
124        assert!(middle < rhs);
125        for ix in 0..middle.0.len() - 1 {
126            assert!(
127                middle.0[ix] == *lhs.0.get(ix).unwrap_or(&0)
128                    || middle.0[ix] == *rhs.0.get(ix).unwrap_or(&0)
129            );
130        }
131    }
132}