diff --git a/src/reading.rs b/src/reading.rs index 53ddd7e..afe29be 100644 --- a/src/reading.rs +++ b/src/reading.rs @@ -12,7 +12,7 @@ Reading logic use std::error; use std::io; -use std::io::{Cursor, Read, Write, SeekFrom, Error, ErrorKind}; +use std::io::{Cursor, Write, SeekFrom, Error, ErrorKind}; use byteorder::{ReadBytesExt, LittleEndian}; use std::collections::HashMap; use std::collections::hash_map::Entry; @@ -20,7 +20,7 @@ use std::fmt::{Display, Formatter, Error as FmtError}; use std::mem::replace; use crc::vorbis_crc32_update; use Packet; -use std::io::Seek; +use std::io::Read; /// Error that can be raised when decoding an Ogg transport. #[derive(Debug)] @@ -59,7 +59,7 @@ impl error::Error for OggReadError { fn cause(&self) -> Option<&dyn error::Error> { match *self { - OggReadError::ReadError(ref err) => Some(err as &error::Error), + OggReadError::ReadError(ref err) => Some(err as &dyn error::Error), _ => None } } @@ -528,177 +528,20 @@ impl BasePacketReader { } } -#[derive(Clone, Copy)] -enum UntilPageHeaderReaderMode { - Searching, - FoundWithNeeded(u8), - SeekNeeded(i32), - Found, +#[derive(Default)] +struct MagicFinder { + len :usize, } - -enum UntilPageHeaderResult { - Eof, - Found, - ReadNeeded, - SeekNeeded, -} - -struct UntilPageHeaderReader { - mode :UntilPageHeaderReaderMode, - /// Capture pattern offset. Needed so that if we only partially - /// recognized the capture pattern, we later on only check the - /// remaining part. - cpt_of :u8, - /// The return buffer. - ret_buf :[u8; 27], - read_amount :usize, -} - -impl UntilPageHeaderReader { - pub fn new() -> Self { - UntilPageHeaderReader { - mode : UntilPageHeaderReaderMode::Searching, - cpt_of : 0, - ret_buf : [0; 27], - read_amount : 0, +impl MagicFinder { + fn feed(&mut self, b :u8) { + match (b, self.len) { + (b'O', _) => self.len = 1, + (b'g', 1..=2) | (b'S', 3) => self.len += 1, + _ => self.len = 0, } } - /// Returns Some(off), where off is the offset of the last byte - /// of the capture pattern if it's found, None if the capture pattern - /// is not inside the passed slice. - /// - /// Changes the capture pattern offset accordingly - fn check_arr(&mut self, arr :&[u8]) -> Option { - for (i, ch) in arr.iter().enumerate() { - match *ch { - b'O' => self.cpt_of = 1, - b'g' if self.cpt_of == 1 || self.cpt_of == 2 => self.cpt_of += 1, - b'S' if self.cpt_of == 3 => return Some(i), - _ => self.cpt_of = 0, - } - } - return None; - } - /// Do one read exactly, and if it was successful, - /// return Ok(true) if the full header has been read and can be extracted with - /// - /// or return Ok(false) if the - pub fn do_read(&mut self, mut rdr :R) - -> Result { - use self::UntilPageHeaderReaderMode::*; - use self::UntilPageHeaderResult as Res; - // The array's size is freely choseable, but must be > 27, - // and must well fit into an i32 (needs to be stored in SeekNeeded) - let mut buf :[u8; 1024] = [0; 1024]; - - let rd_len = tri!(rdr.read(if self.read_amount < 27 { - // This is an optimisation for the most likely case: - // the next page directly follows the current read position. - // Then it would be a waste to read more than the needed amount. - &mut buf[0 .. 27 - self.read_amount] - } else { - match self.mode { - Searching => &mut buf, - FoundWithNeeded(amount) => &mut buf[0 .. amount as usize], - SeekNeeded(_) => return Ok(Res::SeekNeeded), - Found => return Ok(Res::Found), - } - })); - - if rd_len == 0 { - // Reached EOF. - if self.read_amount == 0 { - // If we have read nothing yet, there is no data - // but ogg data, meaning the stream ends legally - // and without corruption. - return Ok(Res::Eof); - } else { - // There is most likely a corruption here. - // I'm not sure, but the ogg spec doesn't say that - // random data past the last ogg page is allowed, - // so we just assume it's not allowed. - tri!(Err(OggReadError::NoCapturePatternFound)); - } - } - self.read_amount += rd_len; - - // 150 kb gives us a bit of safety: we can survive - // up to one page with a corrupted capture pattern - // after having seeked right after a capture pattern - // of an earlier page. - let read_amount_max = 150 * 1024; - if self.read_amount > read_amount_max { - // Exhaustive searching for the capture pattern - // has returned no ogg capture pattern. - tri!(Err(OggReadError::NoCapturePatternFound)); - } - - let rd_buf = &buf[0 .. rd_len]; - - use std::cmp::min; - let (off, needed) = match self.mode { - Searching => match self.check_arr(rd_buf) { - // Capture pattern found - Some(off) => { - self.ret_buf[0] = b'O'; - self.ret_buf[1] = b'g'; - self.ret_buf[2] = b'g'; - self.ret_buf[3] = b'S'; // (Not actually needed) - (off, 24) - }, - // Nothing found - None => return Ok(Res::ReadNeeded), - }, - FoundWithNeeded(needed) => { - (0, needed as usize) - }, - _ => unimplemented!(), - }; - - let fnd_buf = &rd_buf[off..]; - - let copy_amount = min(needed, fnd_buf.len()); - let start_fill = 27 - needed; - (&mut self.ret_buf[start_fill .. copy_amount + start_fill]) - .copy_from_slice(&fnd_buf[0 .. copy_amount]); - if fnd_buf.len() == needed { - // Capture pattern found! - self.mode = Found; - return Ok(Res::Found); - } else if fnd_buf.len() < needed { - // We still have to read some content. - let needed_new = needed - copy_amount; - self.mode = FoundWithNeeded(needed_new as u8); - return Ok(Res::ReadNeeded); - } else { - // We have read too much content (exceeding the header). - // Seek back so that we are at the position - // right after the header. - - self.mode = SeekNeeded(needed as i32 - fnd_buf.len() as i32); - return Ok(Res::SeekNeeded); - } - } - pub fn do_seek(&mut self, mut skr :S) - -> Result { - use self::UntilPageHeaderReaderMode::*; - use self::UntilPageHeaderResult as Res; - match self.mode { - Searching | FoundWithNeeded(_) => Ok(Res::ReadNeeded), - SeekNeeded(offs) => { - tri!(skr.seek(SeekFrom::Current(offs as i64))); - self.mode = Found; - Ok(Res::Found) - }, - Found => Ok(Res::Found), - } - } - pub fn into_header(self) -> [u8; 27] { - use self::UntilPageHeaderReaderMode::*; - match self.mode { - Found => self.ret_buf, - _ => panic!("wrong mode"), - } + fn found(&self) -> bool { + self.len == 4 } } @@ -711,14 +554,17 @@ This reader is not async ready. It does not keep its internal state consistent when it encounters the `WouldBlock` error kind. If you desire async functionality, consider enabling the `async` feature and look into the async module. + +The reader passed to this packet reader should be buffered properly, +since `Read::read` will be called many times. */ -pub struct PacketReader { +pub struct PacketReader { rdr :T, base_pck_rdr :BasePacketReader, } -impl PacketReader { +impl PacketReader { /// Constructs a new `PacketReader` with a given `Read`. pub fn new(rdr :T) -> PacketReader { PacketReader { rdr, base_pck_rdr : BasePacketReader::new() } @@ -767,18 +613,29 @@ impl PacketReader { /// Ok(None) is returned if the stream has ended without an uncompleted page /// or non page data after the last page (if any) present. fn read_until_pg_header(&mut self) -> Result, OggReadError> { - let mut r = UntilPageHeaderReader::new(); - use self::UntilPageHeaderResult::*; - let mut res = tri!(r.do_read(&mut self.rdr)); + let mut ret = [0u8; 27]; + ret[..4].copy_from_slice(b"OggS"); + let mut finder = MagicFinder::default(); + while !finder.found() { + let next = match self.rdr.by_ref().bytes().next() { + Some(b) => tri!(b), + None => return Ok(None), + }; + finder.feed(next); + } + let mut pos = 4; loop { - res = match res { - Eof => return Ok(None), - Found => break, - ReadNeeded => tri!(r.do_read(&mut self.rdr)), - SeekNeeded => tri!(r.do_seek(&mut self.rdr)) + let read = tri!(self.rdr.read(&mut ret[pos..])); + if read == 0 { + break; } + pos += read; + } + if pos == ret.len() { + Ok(Some(ret)) + } else { + Ok(None) } - Ok(Some(r.into_header())) } /// Parses and reads a new OGG page @@ -803,7 +660,9 @@ impl PacketReader { Ok(Some(tri!(pg_prs.parse_packet_data(packet_data)))) } +} +impl PacketReader { /// Seeks the underlying reader /// /// Seeks the reader that this PacketReader bases on by the specified