1use core::fmt;
2
3#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16pub struct Position {
17 pub offset: usize,
19 pub line: usize,
21 pub column: usize,
23}
24
25impl Position {
26 pub const fn start() -> Position {
28 Position {
29 offset: 0,
30 line: 1,
31 column: 1,
32 }
33 }
34
35 pub fn of(source: &[u8], offset: usize) -> Position {
41 let mut rv = Position::start();
42 rv.advance(&source[..offset.min(source.len())]);
43 rv
44 }
45
46 pub fn advance(&mut self, bytes: &[u8]) {
58 let mut line_start = None;
59 find_newlines(bytes, |idx| {
60 self.line += 1;
61 line_start = Some(idx + 1);
62 });
63 let rest = match line_start {
64 Some(line_start) => {
65 self.column = 1;
66 &bytes[line_start..]
67 }
68 None => bytes,
69 };
70 self.column += count_chars(rest);
71 self.offset += bytes.len();
72 }
73}
74
75impl Default for Position {
76 fn default() -> Position {
77 Position::start()
78 }
79}
80
81impl fmt::Debug for Position {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "{}:{}", self.line, self.column)
84 }
85}
86
87impl fmt::Display for Position {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{}:{}", self.line, self.column)
90 }
91}
92
93const LO7: u64 = 0x7f7f_7f7f_7f7f_7f7f;
97const HI: u64 = 0x8080_8080_8080_8080;
98const NEWLINES: u64 = 0x0a0a_0a0a_0a0a_0a0a;
99
100fn zero_bytes(x: u64) -> u64 {
102 !(((x & LO7).wrapping_add(LO7)) | x | LO7)
103}
104
105fn find_newlines<F: FnMut(usize)>(bytes: &[u8], mut f: F) {
107 let (chunks, rest) = bytes.as_chunks::<8>();
108 for (idx, &chunk) in chunks.iter().enumerate() {
109 let mut mask = zero_bytes(u64::from_le_bytes(chunk) ^ NEWLINES);
110 while mask != 0 {
111 f(idx * 8 + mask.trailing_zeros() as usize / 8);
112 mask &= mask - 1;
113 }
114 }
115 let offset = bytes.len() - rest.len();
116 for (idx, &byte) in rest.iter().enumerate() {
117 if byte == b'\n' {
118 f(offset + idx);
119 }
120 }
121}
122
123fn count_chars(bytes: &[u8]) -> usize {
125 let (chunks, rest) = bytes.as_chunks::<8>();
126 let mut continuation = 0;
127 for &chunk in chunks {
128 let w = u64::from_le_bytes(chunk);
129 continuation += (w & !(w << 1) & HI).count_ones() as usize;
131 }
132 continuation += rest.iter().filter(|&&b| b & 0xc0 == 0x80).count();
133 bytes.len() - continuation
134}
135
136#[test]
137fn test_helpers() {
138 let mut state = 0x2545f4914f6cdd1du64;
139 let alphabet = "ab\n\u{e4}\u{1f600}x\n".as_bytes();
140 let rounds = if cfg!(miri) { 1 } else { 50 };
141 for len in 0..64 {
142 for _ in 0..rounds {
143 let input: Vec<u8> = (0..len)
144 .map(|_| {
145 state ^= state << 13;
146 state ^= state >> 7;
147 state ^= state << 17;
148 alphabet[(state % alphabet.len() as u64) as usize]
149 })
150 .collect();
151 let mut newlines = Vec::new();
152 find_newlines(&input, |idx| newlines.push(idx));
153 let expected: Vec<usize> = input
154 .iter()
155 .enumerate()
156 .filter(|&(_, &b)| b == b'\n')
157 .map(|(idx, _)| idx)
158 .collect();
159 assert_eq!(newlines, expected);
160 assert_eq!(
161 count_chars(&input),
162 input.iter().filter(|&&b| b & 0xc0 != 0x80).count()
163 );
164
165 let split = input.len() / 3;
167 let mut pos = Position::start();
168 pos.advance(&input[..split]);
169 pos.advance(&input[split..]);
170 let line_start = input.iter().rposition(|&b| b == b'\n').map_or(0, |x| x + 1);
171 let expected = Position {
172 offset: input.len(),
173 line: newlines.len() + 1,
174 column: input[line_start..]
175 .iter()
176 .filter(|&&b| b & 0xc0 != 0x80)
177 .count()
178 + 1,
179 };
180 assert_eq!(pos, expected);
181 assert_eq!(Position::of(&input, input.len()), expected);
182 }
183 }
184}
185
186#[test]
187fn test_of() {
188 let source = "ab\ncäd\n\nx".as_bytes();
189 let pos = |offset| Position::of(source, offset).to_string();
190 assert_eq!(pos(0), "1:1");
191 assert_eq!(pos(2), "1:3");
192 assert_eq!(pos(3), "2:1");
193 assert_eq!(pos(6), "2:3");
195 assert_eq!(pos(7), "2:4");
196 assert_eq!(pos(8), "3:1");
197 assert_eq!(pos(9), "4:1");
198 assert_eq!(pos(100), "4:2");
200}