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}