1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
//! Parser data source. //! //! FBX parsers requires cursor position tracking and skipping. //! These features are used through `ParserSource` trait. //! //! For reader types which does not provide these features, `reader::*Source` //! wrappers are provided. //! Using those wrappers, any types implementing `std::io::Read` can be used as //! source reader for parsers. //! //! Usually users don't need to deal with these types and traits, because parser //! types or modules will provide simple functions to automatically wrap readers //! if necessary. use std::io; pub use self::{ position_cache::PositionCacheReader, source::{PlainSource, SeekableSource}, }; mod position_cache; mod source; /// A trait for types which can be data sources. /// /// Users can implement this manually, but usually it is enough to use wrappers /// in the [`reader`] module. /// /// [`reader`]: index.html pub trait ParserSource: Sized + io::Read { /// Returns the offset of a byte which would be read next. /// /// This is called many times during parsing, so it is desirable to be fast /// as possible. /// /// Reader types with `std::io::Seek` can implement this as /// `self.seek(SeekFrom::Current(0)).unwrap()`, but this is fallible and /// can be inefficient. /// Use of [`PositionCacheReader`] is reccomended. /// /// [`PositionCacheReader`]: struct.PositionCacheReader.html fn position(&self) -> u64; /// Skips (seeks formward) the given size. /// /// Reader types can make this more efficient using `io::Seek::seek` if /// possible. /// /// # Examples /// /// ``` /// use fbxcel::pull_parser::{ParserSource, reader::PlainSource}; /// /// let msg = "Hello, world!"; /// let len = msg.len() as u64; /// let mut reader = std::io::Cursor::new(msg); /// let mut reader = PlainSource::new(&mut reader); /// /// assert_eq!(reader.position(), 0); /// reader.skip_distance(7).expect("Failed to skip"); /// assert_eq!(reader.position(), 7); /// ``` fn skip_distance(&mut self, distance: u64) -> io::Result<()> { // NOTE: `let mut limited = self.by_ref().take(distance);` is E0507. let mut limited = io::Read::take(self.by_ref(), distance); io::copy(&mut limited, &mut io::sink())?; Ok(()) } /// Skips (seeks forward) to the given position. /// /// Reader types can make this more efficient using `io::Seek::seek` if /// possible. /// /// # Panics /// /// Panics if the given position is behind the current position. /// /// ``` /// use fbxcel::pull_parser::{ParserSource, reader::PlainSource}; /// /// let msg = "Hello, world!"; /// let len = msg.len() as u64; /// let mut reader = std::io::Cursor::new(msg); /// let mut reader = PlainSource::new(&mut reader); /// /// assert_eq!(reader.position(), 0); /// reader.skip_to(2).expect("Failed to skip"); /// assert_eq!(reader.position(), 2); /// reader.skip_to(7).expect("Failed to skip"); /// assert_eq!(reader.position(), 7); /// ``` fn skip_to(&mut self, pos: u64) -> io::Result<()> { let distance = pos .checked_sub(self.position()) .expect("Attempt to skip backward"); self.skip_distance(distance) } } impl<R: ParserSource> ParserSource for &mut R { fn position(&self) -> u64 { (**self).position() } fn skip_distance(&mut self, distance: u64) -> io::Result<()> { (**self).skip_distance(distance) } fn skip_to(&mut self, pos: u64) -> io::Result<()> { (**self).skip_to(pos) } }