OXYGENHUB

Coverage / crates/oxy-core/src/lines.rs 94.9%

148 of 156 lines run, 60 of 71 functions · run #339 on proposal/124 · run missed

1
//! The lines of an insert (spec §7.4) held as one buffer and where each
2
//! line ends, shared by the change and the file graph it builds: a tree of
3
//! forty million lines is then a few allocations per insert, not one per
4
//! line, and a graph's atoms point into their insert's buffer rather than
5
//! copying it.
6
7
use std::sync::Arc;
8
9
#[derive(Default, PartialEq, Eq, Hash)]
10
struct Inner {
11
    /// Shared with the file it was read from, when there was one.
12
    buf: Arc<Vec<u8>>,
13
    /// The end offset of each line in `buf`.
14
    ends: Ends,
15
}
16
17
/// Line end offsets, four bytes each unless the buffer is over 4 GiB: a
18
/// first commit of the Linux tree holds 36 million of them at once. Which
19
/// form follows from the buffer's length alone, so equal lines compare
20
/// equal.
21
#[derive(PartialEq, Eq, Hash)]
22
enum Ends {
23
    Small(Vec<u32>),
24
    Large(Vec<u64>),
25
}
26
27
impl Default for Ends {
2851524×
    fn default() -> Self {
2951524×
        Ends::Small(Vec::new())
3051524×
    }
31
}
32
33
impl Ends {
3444261×
    fn from_u64(ends: Vec<u64>, buf_len: usize) -> Ends {
3544261×
        if u32::try_from(buf_len).is_ok() {
3690200×
            Ends::Small(ends.into_iter().map(|e| e as u32).collect())
37
        } else {
380×
            Ends::Large(ends)
39
        }
4044261×
    }
41
42363438×
    fn len(&self) -> usize {
43363438×
        match self {
44363438×
            Ends::Small(v) => v.len(),
450×
            Ends::Large(v) => v.len(),
46
        }
47363438×
    }
48
49136380×
    fn is_empty(&self) -> bool {
50136380×
        self.len() == 0
51136380×
    }
52
53938785×
    fn get(&self, i: usize) -> Option<usize> {
54938785×
        match self {
55938785×
            Ends::Small(v) => v.get(i).map(|&e| e as usize),
560×
            Ends::Large(v) => v.get(i).map(|&e| e as usize),
57
        }
58938785×
    }
59
}
60
61
/// An insert's lines. Cloning is cheap: the buffer is shared.
62
#[derive(Clone, Default, PartialEq, Eq, Hash)]
63
pub struct Lines(Arc<Inner>);
64
65
impl Lines {
66
    /// Lines from their bytes, each keeping its terminator; a final
67
    /// unterminated line is its own line (the rule of
68
    /// [`crate::diff::split_lines`]).
69141×
    pub fn split(content: &[u8]) -> Lines {
70141×
        Lines::split_shared(Arc::new(content.to_vec()))
71141×
    }
72
73
    /// [`Lines::split`] of a buffer held elsewhere too (a file's content),
74
    /// shared rather than copied.
756044×
    pub fn split_shared(buf: Arc<Vec<u8>>) -> Lines {
766044×
        let content = &buf[..];
77
        // Counted first, so the offsets take exactly their room.
786044×
        let newlines = memchr::memchr_iter(b'\n', content).count();
796044×
        let unterminated = content.last().is_some_and(|&c| c != b'\n');
806044×
        let n = newlines + usize::from(unterminated);
816044×
        let ends_at = memchr::memchr_iter(b'\n', content)
82198348×
            .map(|i| i + 1)
836044×
            .chain(unterminated.then_some(content.len()));
846044×
        let ends = if u32::try_from(content.len()).is_ok() {
856044×
            let mut v = Vec::with_capacity(n);
86198482×
            v.extend(ends_at.map(|e| e as u32));
876044×
            Ends::Small(v)
88
        } else {
890×
            let mut v = Vec::with_capacity(n);
900×
            v.extend(ends_at.map(|e| e as u64));
910×
            Ends::Large(v)
92
        };
936044×
        Lines(Arc::new(Inner { buf, ends }))
946044×
    }
95
96192418×
    pub fn len(&self) -> usize {
97192418×
        self.0.ends.len()
98192418×
    }
99
100136380×
    pub fn is_empty(&self) -> bool {
101136380×
        self.0.ends.is_empty()
102136380×
    }
103
104
    /// Line `i`, with its terminator.
105487416×
    pub fn get(&self, i: usize) -> Option<&[u8]> {
106487416×
        let end = self.0.ends.get(i)?;
107487416×
        let start = if i == 0 { 0 } else { self.0.ends.get(i - 1)? };
108487416×
        Some(&self.0.buf[start..end])
109487416×
    }
110
11139158×
    pub fn iter(&self) -> impl ExactSizeIterator<Item = &[u8]> + Clone + '_ {
112311611×
        (0..self.len()).map(move |i| self.get(i).unwrap())
11339158×
    }
114
115
    /// Every line, joined: the bytes they were split from.
1165703×
    pub fn bytes(&self) -> &[u8] {
1175703×
        &self.0.buf
1185703×
    }
119
120
    /// Whether splitting [`Lines::bytes`] by the line rule gives these lines
121
    /// back: every line but the last ends in a newline and none holds
122
    /// another. Every insert oxy makes is so, and such lines are stored as
123
    /// their bytes alone (spec §7.4).
12479×
    pub fn is_split_form(&self) -> bool {
12579×
        let buf = &self.0.buf;
12679×
        let mut start = 0usize;
12734562×
        for i in 0..self.0.ends.len() {
12834562×
            let end = self.0.ends.get(i).unwrap_or(start);
12934562×
            let line = &buf[start..end];
13034562×
            if line.is_empty() {
1311×
                return false;
13234561×
            }
13334561×
            let last = i + 1 == self.0.ends.len();
13434561×
            let body = if last && !line.ends_with(b"\n") {
13553×
                line
13634508×
            } else if line.ends_with(b"\n") {
13734505×
                &line[..line.len() - 1]
138
            } else {
1393×
                return false;
140
            };
14134558×
            if body.contains(&b'\n') {
1421×
                return false;
14334557×
            }
14434557×
            start = end;
145
        }
14674×
        true
14779×
    }
148
149
    /// Lines `range` as lines of their own.
1501×
    pub fn slice(&self, range: std::ops::Range<usize>) -> Lines {
1511×
        self.iter().skip(range.start).take(range.len()).collect()
1521×
    }
153
154
    /// The lines as owned vectors, for code that edits them one by one.
15517×
    pub fn to_vec(&self) -> Vec<Vec<u8>> {
15617×
        self.iter().map(<[u8]>::to_vec).collect()
15717×
    }
158
}
159
160
impl std::ops::Index<usize> for Lines {
161
    type Output = [u8];
162175805×
    fn index(&self, i: usize) -> &[u8] {
163175805×
        self.get(i).expect("line index out of range")
164175805×
    }
165
}
166
167
impl<'a> FromIterator<&'a [u8]> for Lines {
16844197×
    fn from_iter<I: IntoIterator<Item = &'a [u8]>>(iter: I) -> Lines {
16944197×
        let (mut buf, mut ends) = (Vec::new(), Vec::new());
17081920×
        for l in iter {
17181920×
            buf.extend_from_slice(l);
17281920×
            ends.push(buf.len() as u64);
17381920×
        }
17444197×
        let ends = Ends::from_u64(ends, buf.len());
17544197×
        Lines(Arc::new(Inner {
17644197×
            buf: Arc::new(buf),
17744197×
            ends,
17844197×
        }))
17944197×
    }
180
}
181
182
impl FromIterator<Vec<u8>> for Lines {
18364×
    fn from_iter<I: IntoIterator<Item = Vec<u8>>>(iter: I) -> Lines {
18464×
        let (mut buf, mut ends) = (Vec::new(), Vec::new());
1858280×
        for l in iter {
1868280×
            buf.extend_from_slice(&l);
1878280×
            ends.push(buf.len() as u64);
1888280×
        }
18964×
        let ends = Ends::from_u64(ends, buf.len());
19064×
        Lines(Arc::new(Inner {
19164×
            buf: Arc::new(buf),
19264×
            ends,
19364×
        }))
19464×
    }
195
}
196
197
impl From<Vec<Vec<u8>>> for Lines {
19853×
    fn from(v: Vec<Vec<u8>>) -> Lines {
19953×
        v.into_iter().collect()
20053×
    }
201
}
202
203
impl std::fmt::Debug for Lines {
2046654×
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2056654×
        f.debug_list()
2066654×
            .entries(self.iter().map(String::from_utf8_lossy))
2076654×
            .finish()
2086654×
    }
209
}
210
211
/// One line of a [`Lines`], held by a file graph's atom.
212
#[derive(Clone, Debug, PartialEq, Eq)]
213
pub struct LineRef {
214
    lines: Lines,
215
    index: u32,
216
}
217
218
impl LineRef {
219181665×
    pub fn new(lines: Lines, index: u32) -> LineRef {
220181665×
        LineRef { lines, index }
221181665×
    }
222
223175804×
    pub fn bytes(&self) -> &[u8] {
224175804×
        &self.lines[self.index as usize]
225175804×
    }
226
}
227
228
#[cfg(test)]
229
mod tests {
230
    use super::*;
231
232
    #[test]
2331×
    fn lines_split_index_and_join_as_the_line_rule_says() {
2346×
        for text in [&b""[..], b"a\n", b"a\nb", b"a\n\nb\n", b"\n", b"no end"] {
2356×
            let l = Lines::split(text);
2366×
            let want = crate::diff::split_lines(text);
2376×
            assert_eq!(l.len(), want.len(), "{text:?}");
2386×
            assert_eq!(l.iter().collect::<Vec<_>>(), want);
2396×
            assert_eq!(l.bytes(), text);
2406×
            let v: Lines = want.iter().copied().collect();
2416×
            assert_eq!(v, l);
2426×
            assert_eq!(Lines::from(l.to_vec()), l);
243
        }
2441×
        let l = Lines::split(b"a\nb\nc\n");
2451×
        assert!(l.is_split_form());
2461×
        assert!(!Lines::from(vec![b"a".to_vec(), b"b\n".to_vec()]).is_split_form());
2471×
        assert!(!Lines::from(vec![b"a\nb\n".to_vec()]).is_split_form());
2481×
        assert!(!Lines::from(vec![Vec::new()]).is_split_form());
2491×
        assert!(Lines::from(vec![b"a\n".to_vec(), b"end".to_vec()]).is_split_form());
2501×
        assert_eq!(&l[1], b"b\n");
2511×
        assert_eq!(
2521×
            l.slice(1..3).to_vec(),
2531×
            vec![b"b\n".to_vec(), b"c\n".to_vec()]
254
        );
2551×
        assert_eq!(LineRef::new(l.clone(), 2).bytes(), b"c\n");
2561×
    }
257
}