use alloc::vec::Vec; use platform::{Pal, Sys}; use platform::types::*; use header::unistd::{lseek, SEEK_SET}; /// Implements an `Iterator` which returns on either newline or EOF. #[derive(Clone)] pub struct RawLineBuffer { pub fd: c_int, buf: Vec, newline: Option, read: usize } #[derive(PartialEq)] pub enum Line<'a> { Error, EOF, Some(&'a [u8]) } impl RawLineBuffer { pub const fn new(fd: c_int) -> Self { Self { fd: fd, buf: Vec::new(), newline: None, read: 0 } } // Can't use iterators because we want to return a reference. // See https://stackoverflow.com/a/30422716/5069285 pub fn next(&mut self) -> Line { // Remove last line if let Some(newline) = self.newline { self.buf.drain(..newline + 1); } loop { // Exit if newline was read already self.newline = self.buf.iter().position(|b| *b == b'\n'); if self.newline.is_some() { break; } let len = self.buf.len(); if len >= self.buf.capacity() { self.buf.reserve(1024); } // Create buffer of what's left in the vector, uninitialized memory unsafe { let capacity = self.buf.capacity(); self.buf.set_len(capacity); } let read = Sys::read(self.fd, &mut self.buf[len..]); let read_usize = read.max(0) as usize; // Remove all uninitialized memory that wasn't read unsafe { self.buf.set_len(len + read_usize); } self.read += read_usize; if read == 0 { return Line::EOF; } if read < 0 { return Line::Error; } } let newline = self.newline.unwrap(); // safe because it doesn't break the loop otherwise Line::Some(&self.buf[..newline]) } /// Return the byte position of the start of the line pub fn line_pos(&self) -> usize { self.read - self.buf.len() } /// Seek to a byte position in the file pub fn seek(&mut self, pos: usize) -> off_t { let ret = lseek(self.fd, pos as i64, SEEK_SET); if ret != !0 { self.read = pos; } ret } }