1use alloc::{boxed::Box, string::String, vec::Vec};
2use core::{
3 fmt::{self, Write},
4 num::Wrapping,
5 ops::{Bound, RangeBounds},
6};
7
8#[derive(Debug)]
10pub struct LineBuf {
11 lines: Box<[Line]>,
12 line_len: usize,
14 start: Wrapping<usize>,
15 end: Wrapping<usize>,
16}
17
18#[derive(Copy, Clone, Debug)]
20#[non_exhaustive]
21pub struct BufConfig {
22 pub line_len: usize,
23 pub lines: usize,
24}
25
26#[derive(Copy, Clone, Debug)]
27#[must_use = "iterators do nothing if not iterated over"]
28pub struct Iter<'buf> {
29 buf: &'buf LineBuf,
30 idx: Wrapping<usize>,
31 end: Wrapping<usize>,
32}
33
34struct Line {
35 line: String,
36 stamp: Wrapping<usize>,
37}
38
39#[cfg(test)]
40macro_rules! test_dbg {
41 ($x:expr) => {
42 dbg!($x)
43 };
44}
45
46#[cfg(not(test))]
47macro_rules! test_dbg {
48 ($x:expr) => {
49 $x
50 };
51}
52
53impl LineBuf {
54 #[must_use]
55 pub fn new(config: BufConfig) -> Self {
56 let line_len = config.line_len;
57 Self {
58 lines: (0..config.lines)
59 .map(|stamp| Line {
60 stamp: Wrapping(stamp),
61 line: String::with_capacity(line_len),
62 })
63 .collect::<Vec<_>>()
64 .into(),
65 line_len,
66 start: Wrapping(0),
67 end: Wrapping(0),
68 }
69 }
70
71 pub fn iter(&self) -> Iter<'_> {
72 Iter {
73 idx: self.start,
74 end: self.end,
75 buf: self,
76 }
77 }
78
79 pub fn lines(&self, range: impl RangeBounds<usize>) -> Iter<'_> {
80 let idx = match range.start_bound() {
81 Bound::Excluded(&offset) => self.wrap_offset(offset + 1),
82 Bound::Included(&offset) => self.wrap_offset(offset),
83 Bound::Unbounded => self.start,
84 };
85 let end = match range.end_bound() {
86 Bound::Excluded(&offset) => self.wrap_offset(offset),
87 Bound::Included(&offset) => self.wrap_offset(offset + 1),
88 Bound::Unbounded => self.end,
89 };
90 Iter {
91 idx,
92 end,
93 buf: self,
94 }
95 }
96
97 fn wrap_offset(&self, offset: usize) -> Wrapping<usize> {
98 self.start + Wrapping(offset)
99 }
100
101 fn advance(&mut self) {
102 self.end += 1;
103 }
104
105 fn wrap_idx(&self, Wrapping(idx): Wrapping<usize>) -> usize {
106 idx % self.lines.len()
107 }
108
109 fn line_mut(&mut self) -> &mut String {
110 let idx = self.wrap_idx(self.end);
111 let Line { stamp, line } = &mut self.lines[idx];
112 if *stamp != self.end {
113 *stamp = self.end;
114 line.clear();
115
116 if idx == self.start.0 {
117 self.start += 1;
120 }
121 }
122 line
123 }
124
125 fn write_chunk<'s>(&mut self, s: &'s str) -> Option<&'s str> {
126 let rem = self.line_len - self.line_mut().len();
127 let (line, next) = if s.len() > rem {
128 let (this, next) = s.split_at(rem);
129 (this, Some(next))
130 } else {
131 (s, None)
132 };
133 self.line_mut().push_str(line);
134 next
135 }
136
137 fn write_line(&mut self, mut line: &str) {
138 while let Some(next) = test_dbg!(self.write_chunk(test_dbg!(line))) {
139 line = next;
140 test_dbg!(self.advance());
141 }
142 }
143}
144
145impl Write for &mut LineBuf {
146 fn write_str(&mut self, mut s: &str) -> fmt::Result {
147 let ends_with_newline = if let Some(stripped) = s.strip_suffix('\n') {
148 s = stripped;
149 true
150 } else {
151 false
152 };
153
154 let mut lines = s.split('\n');
155 if let Some(line) = lines.next() {
156 self.write_line(line);
157 for line in lines {
158 self.advance();
159 self.write_line(line);
160 }
161 }
162
163 if ends_with_newline {
164 self.advance();
165 }
166
167 Ok(())
168 }
169}
170
171impl<'buf> Iterator for Iter<'buf> {
172 type Item = &'buf str;
173
174 fn next(&mut self) -> Option<Self::Item> {
175 let idx = self.idx;
176 if idx >= self.end {
177 return None;
178 }
179 self.idx += 1;
180 let Line { line, stamp } = self.buf.lines.get(self.buf.wrap_idx(idx))?;
181 if *stamp != idx {
182 return None;
183 }
184 Some(line.as_str())
185 }
186}
187
188impl fmt::Debug for Line {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 let Line { line, stamp } = self;
191 write!(f, "{line:?}:{stamp}")
192 }
193}
194
195impl Default for BufConfig {
198 fn default() -> Self {
199 Self {
200 line_len: 80,
201 lines: 120,
203 }
204 }
205}
206
207#[cfg(test)]
208mod tests {
209 use core::slice::SliceIndex;
210
211 use super::*;
212
213 #[test]
214 fn basic() {
215 let mut buf = LineBuf::new(BufConfig {
216 line_len: 6,
217 lines: 6,
218 });
219 writeln!(&mut buf, "hello").unwrap();
220 writeln!(&mut buf, "world").unwrap();
221 writeln!(&mut buf, "have\nlots").unwrap();
222 writeln!(&mut buf, "of").unwrap();
223 writeln!(&mut buf, "fun").unwrap();
224
225 let mut iter = buf.iter();
226 assert_eq!(
227 test_dbg!(iter.next()),
228 Some("hello"),
229 "\n buf: {buf:?}\n iter: {iter:?}"
230 );
231 assert_eq!(
232 test_dbg!(iter.next()),
233 Some("world"),
234 "\n buf: {buf:?}\n iter: {iter:?}"
235 );
236 assert_eq!(
237 test_dbg!(iter.next()),
238 Some("have"),
239 "\n buf: {buf:?}\n iter: {iter:?}"
240 );
241 assert_eq!(
242 test_dbg!(iter.next()),
243 Some("lots"),
244 "\n buf: {buf:?}\n iter: {iter:?}"
245 );
246 assert_eq!(
247 test_dbg!(iter.next()),
248 Some("of"),
249 "\n buf: {buf:?}\n iter: {iter:?}"
250 );
251 assert_eq!(
252 test_dbg!(iter.next()),
253 Some("fun"),
254 "\n buf: {buf:?}\n iter: {iter:?}"
255 );
256 assert_eq!(test_dbg!(iter.next()), None);
257 }
258
259 #[test]
260 fn buffer_wraparound() {
261 let mut buf = LineBuf::new(BufConfig {
262 line_len: 7,
263 lines: 6,
264 });
265 writeln!(&mut buf, "hello").unwrap();
266 writeln!(&mut buf, "world").unwrap();
267 writeln!(&mut buf, "have\nlots").unwrap();
268 writeln!(&mut buf, "of").unwrap();
269 writeln!(&mut buf, "fun").unwrap();
270 writeln!(&mut buf, "goodbye").unwrap();
271
272 assert_slicelike(
273 "buffer wraparound",
274 &buf,
275 &["world", "have", "lots", "of", "fun", "goodbye"],
276 ..,
277 )
278 }
279
280 #[test]
281 fn line_wrapping() {
282 let mut buf = LineBuf::new(BufConfig {
283 line_len: 4,
284 lines: 6,
285 });
286 writeln!(&mut buf, "this is a very long line").unwrap();
287
288 dbg!(&buf);
289 let mut iter = buf.iter();
290 assert_eq!(
291 test_dbg!(iter.next()),
292 Some("this"),
293 "\n buf: {buf:?}\n iter: {iter:?}"
294 );
295 assert_eq!(
296 test_dbg!(iter.next()),
297 Some(" is "),
298 "\n buf: {buf:?}\n iter: {iter:?}"
299 );
300 assert_eq!(
301 test_dbg!(iter.next()),
302 Some("a ve"),
303 "\n buf: {buf:?}\n iter: {iter:?}"
304 );
305 assert_eq!(
306 test_dbg!(iter.next()),
307 Some("ry l"),
308 "\n buf: {buf:?}\n iter: {iter:?}"
309 );
310 assert_eq!(
311 test_dbg!(iter.next()),
312 Some("ong "),
313 "\n buf: {buf:?}\n iter: {iter:?}"
314 );
315 assert_eq!(
316 test_dbg!(iter.next()),
317 Some("line"),
318 "\n buf: {buf:?}\n iter: {iter:?}"
319 );
320 assert_eq!(
321 test_dbg!(iter.next()),
322 None,
323 "\n buf: {buf:?}\n iter: {iter:?}"
324 );
325 }
326
327 #[test]
328 fn range_iter_unbounded() {
329 let mut buf = LineBuf::new(BufConfig {
330 line_len: 6,
331 lines: 6,
332 });
333 let expected = ["hello", "world", "have", "lots", "of", "fun"];
334 fill(&mut buf, &expected);
335 assert_slicelike("unbounded", &buf, &expected, ..)
336 }
337
338 #[test]
339 fn range_iter_basic() {
340 let mut buf = LineBuf::new(BufConfig {
341 line_len: 6,
342 lines: 6,
343 });
344
345 let expected = ["hello", "world", "have", "lots", "of", "fun"];
346 fill(&mut buf, &expected);
347 test_range_iters(&buf, &expected)
348 }
349
350 #[test]
351 fn range_iter_buf_wrapped() {
352 let mut buf = LineBuf::new(BufConfig {
353 line_len: 7,
354 lines: 6,
355 });
356 writeln!(&mut buf, "hello").unwrap();
357 writeln!(&mut buf, "world").unwrap();
358 writeln!(&mut buf, "have\nlots").unwrap();
359 writeln!(&mut buf, "of").unwrap();
360 writeln!(&mut buf, "fun").unwrap();
361 writeln!(&mut buf, "goodbye").unwrap();
362
363 let expected = ["world", "have", "lots", "of", "fun", "goodbye"];
364 test_range_iters(&buf, &expected);
365 }
366
367 fn test_range_iters(buf: &LineBuf, expected: &[&str]) {
368 assert_slicelike("unbounded", buf, expected, ..);
369
370 assert_slicelike("start inclusive", buf, expected, 2..);
371 assert_slicelike("start inclusive", buf, expected, 3..);
372 assert_slicelike("start inclusive", buf, expected, 4..);
373 assert_slicelike("start inclusive", buf, expected, 5..);
374
375 assert_slicelike("end inclusive", buf, expected, ..=2);
376 assert_slicelike("end inclusive", buf, expected, ..=5);
377
378 assert_slicelike("end exclusive", buf, expected, ..2);
379 assert_slicelike("end exclusive", buf, expected, ..5);
380 assert_slicelike("end exclusive", buf, expected, ..6)
381 }
382
383 fn fill(mut buf: &mut LineBuf, strs: &[&str]) {
384 for item in strs {
385 writeln!(buf, "{item}").unwrap();
386 }
387 }
388
389 fn assert_slicelike<'ex>(
390 kind: &str,
391 buf: &LineBuf,
392 expected: &'ex [&'ex str],
393 range: impl RangeBounds<usize>
394 + SliceIndex<[&'ex str], Output = [&'ex str]>
395 + Clone
396 + fmt::Debug,
397 ) {
398 let slice = &expected[range.clone()];
399 let mut iter = buf.lines(range.clone());
400 for &expected in slice {
401 assert_eq!(
402 Some(expected),
403 iter.next(),
404 "\n range: {range:?}\n buf: {buf:?}\n iter: {iter:?}\n exp: {slice:?}\n kind: {kind}"
405 )
406 }
407 assert_eq!(
408 None,
409 iter.next(),
410 "\n range: {range:?}\n buf: {buf:?}\n iter: {iter:?}\n exp: {slice:?}\n kind: {kind}"
411 )
412 }
413}