Skip to main content

relibc/platform/
rlb.rs

1use alloc::vec::Vec;
2
3use crate::platform::{Pal, Sys, types::*};
4
5use crate::{
6    error::ResultExt,
7    header::unistd::{SEEK_SET, lseek},
8};
9/// Implements an `Iterator` which returns on either newline or EOF.
10#[derive(Clone)]
11pub struct RawLineBuffer {
12    pub fd: c_int,
13    buf: Vec<u8>,
14    newline: Option<usize>,
15    read: usize,
16}
17
18#[derive(PartialEq)]
19pub enum Line<'a> {
20    Error,
21    EOF,
22    Some(&'a [u8]),
23}
24
25impl RawLineBuffer {
26    pub const fn new(fd: c_int) -> Self {
27        Self {
28            fd,
29            buf: Vec::new(),
30            newline: None,
31            read: 0,
32        }
33    }
34
35    // Can't use iterators because we want to return a reference.
36    // See https://stackoverflow.com/a/30422716/5069285
37    #[allow(clippy::should_implement_trait)]
38    pub fn next(&mut self) -> Line<'_> {
39        // Remove last line
40        if let Some(newline) = self.newline {
41            self.buf.drain(..=newline);
42        }
43
44        loop {
45            // Exit if newline was read already
46            self.newline = self.buf.iter().position(|b| *b == b'\n');
47
48            if self.newline.is_some() {
49                break;
50            }
51
52            let len = self.buf.len();
53
54            if len >= self.buf.capacity() {
55                self.buf.reserve(1024);
56            }
57
58            // Create buffer of what's left in the vector, uninitialized memory
59            unsafe {
60                let capacity = self.buf.capacity();
61                self.buf.set_len(capacity);
62            }
63
64            let read = Sys::read(self.fd, &mut self.buf[len..])
65                .map(|u| u as isize)
66                .or_minus_one_errno();
67
68            let read_usize = read.max(0) as usize;
69
70            // Remove all uninitialized memory that wasn't read
71            unsafe {
72                self.buf.set_len(len + read_usize);
73            }
74
75            self.read += read_usize;
76
77            if read == 0 {
78                return if self.buf.is_empty() {
79                    Line::EOF
80                } else {
81                    Line::Some(&self.buf)
82                };
83            }
84            if read < 0 {
85                return Line::Error;
86            }
87        }
88
89        let newline = self.newline.unwrap(); // safe because it doesn't break the loop otherwise
90        Line::Some(&self.buf[..newline])
91    }
92
93    /// Return the byte position of the start of the line
94    pub fn line_pos(&self) -> usize {
95        self.read - self.buf.len()
96    }
97
98    /// Seek to a byte position in the file
99    pub fn seek(&mut self, pos: usize) -> off_t {
100        let ret = lseek(self.fd, pos as off_t, SEEK_SET);
101        if ret != !0 {
102            self.read = pos;
103        }
104        ret
105    }
106}