hermes_support/
line_index.rs1pub struct LineIndex {
7 line_starts: Vec<u32>,
10}
11
12impl LineIndex {
13 pub fn build(bytes: &[u8]) -> LineIndex {
16 let mut line_starts = Vec::with_capacity(64);
17 line_starts.push(0u32);
18 for (i, &b) in bytes.iter().enumerate() {
19 if b == b'\n' {
20 line_starts.push((i + 1) as u32);
21 }
22 }
23 LineIndex { line_starts }
24 }
25
26 pub fn line_col(&self, offset: u32) -> (u32, u32) {
29 let line0 = match self.line_starts.binary_search(&offset) {
31 Ok(i) => i,
32 Err(i) => i - 1,
33 };
34 let col = offset - self.line_starts[line0] + 1;
35 ((line0 + 1) as u32, col)
36 }
37
38 pub fn line_ref<'a>(&self, bytes: &'a [u8], line: u32) -> &'a [u8] {
41 let start = self.line_starts[(line - 1) as usize] as usize;
42 let end = self
43 .line_starts
44 .get(line as usize)
45 .map(|&e| e as usize)
46 .unwrap_or(bytes.len());
47 &bytes[start..end]
48 }
49
50 #[allow(dead_code)]
52 pub fn line_count(&self) -> u32 {
53 self.line_starts.len() as u32
54 }
55
56 pub fn line_start(&self, line: u32) -> u32 {
59 self.line_starts[(line - 1) as usize]
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
68 fn lf_line_and_col() {
69 let idx = LineIndex::build(b"ab\ncde\nf");
71 assert_eq!(idx.line_col(0), (1, 1)); assert_eq!(idx.line_col(1), (1, 2)); assert_eq!(idx.line_col(3), (2, 1)); assert_eq!(idx.line_col(5), (2, 3)); assert_eq!(idx.line_col(7), (3, 1)); }
77
78 #[test]
79 fn crlf_column_counts_bytes() {
80 let idx = LineIndex::build(b"a\r\nb");
82 assert_eq!(idx.line_col(0), (1, 1));
83 assert_eq!(idx.line_col(3), (2, 1));
84 }
85
86 #[test]
87 fn line_ref_excludes_nothing_before_eol() {
88 let bytes = b"ab\ncde\nf";
89 let idx = LineIndex::build(bytes);
90 assert_eq!(idx.line_ref(bytes, 1), b"ab\n");
92 assert_eq!(idx.line_ref(bytes, 3), b"f");
93 }
94
95 #[test]
96 fn empty_buffer_has_one_line() {
97 let idx = LineIndex::build(b"");
98 assert_eq!(idx.line_count(), 1);
99 assert_eq!(idx.line_col(0), (1, 1));
100 assert_eq!(idx.line_ref(b"", 1), b"");
101 }
102
103 #[test]
104 fn trailing_newline_starts_an_empty_last_line() {
105 let bytes = b"a\n";
108 let idx = LineIndex::build(bytes);
109 assert_eq!(idx.line_count(), 2);
110 assert_eq!(idx.line_ref(bytes, 1), b"a\n");
111 assert_eq!(idx.line_ref(bytes, 2), b"");
112 assert_eq!(idx.line_col(2), (2, 1));
113 }
114}