diff options
author | Genny <gen@nyble.dev> | 2022-11-25 01:28:19 -0600 |
---|---|---|
committer | Genny <gen@nyble.dev> | 2022-11-25 01:28:19 -0600 |
commit | 3068091f9bf05a48e2f20806ca22368188b56a7a (patch) | |
tree | b8fc116d6ee28e70324545fa79f15cbfb22fd2d1 /gifed/src | |
parent | ac8578823b9ef467dc58a6afd5f7f4adfdb4c8bd (diff) | |
download | gifed-3068091f9bf05a48e2f20806ca22368188b56a7a.tar.gz gifed-3068091f9bf05a48e2f20806ca22368188b56a7a.zip |
Read now gives offsets
Diffstat (limited to 'gifed/src')
-rw-r--r-- | gifed/src/reader/mod.rs | 368 |
1 files changed, 207 insertions, 161 deletions
diff --git a/gifed/src/reader/mod.rs b/gifed/src/reader/mod.rs index 2620811..b4d892a 100644 --- a/gifed/src/reader/mod.rs +++ b/gifed/src/reader/mod.rs @@ -1,230 +1,262 @@ use std::{ - borrow::Cow, - convert::{TryFrom, TryInto}, + convert::TryFrom, error::Error, fmt, fs::File, - io::Read, + io::{BufReader, ErrorKind, Read}, + ops::Range, path::Path, }; -use crate::{ - block::{ - extension::{Application, GraphicControl}, - Block, CompressedImage, ImageDescriptor, Palette, ScreenDescriptor, Version, - }, - Gif, +use crate::block::{ + extension::{Application, GraphicControl}, + Block, CompressedImage, ImageDescriptor, Palette, ScreenDescriptor, Version, }; -pub struct GifReader {} - -impl GifReader { - pub fn file<P: AsRef<Path>>(path: P) -> Result<Gif, DecodeError> { - let mut file = File::open(path)?; - let mut reader = SmartReader { - inner: vec![], - position: 0, - }; - file.read_to_end(&mut reader.inner)?; +pub struct Decoder<R: Read> { + reader: SmartReader<R>, +} - let mut gif = Self::read_required(&mut reader)?; +impl Decoder<BufReader<File>> { + pub fn file<P: AsRef<Path>>(path: P) -> Result<Self, DecodeError> { + let file = File::open(path).map_err(|e| DecodeError::IoError(e))?; + let buffreader = BufReader::new(file); + Ok(Decoder::new(buffreader)) + } +} - if gif.screen_descriptor.has_color_table() { - let gct_size = gif.screen_descriptor.color_table_len() * 3; - gif.global_color_table = Some(Self::read_color_table(&mut reader, gct_size)?); - } - - loop { - match Self::read_block(&mut reader)? { - Some(block) => gif.blocks.push(block), - None => return Ok(gif), - } +impl<R: Read> Decoder<R> { + pub fn new(reader: R) -> Self { + Self { + reader: SmartReader::new(reader), } } - fn read_required(reader: &mut SmartReader) -> Result<Gif, DecodeError> { - let version = match reader.take_lossy_utf8(6).as_deref() { - Some("GIF87a") => Version::Gif87a, - Some("GIF89a") => Version::Gif89a, - _ => return Err(DecodeError::UnknownVersionString), - }; - - let mut lsd_buffer: [u8; 7] = [0; 7]; - reader - .read_exact(&mut lsd_buffer) - .ok_or(DecodeError::UnexpectedEof)?; + pub fn read(mut self) -> Result<Reader<R>, DecodeError> { + let version = self.read_version()?; + let screen_descriptor = self.read_screen_descriptor()?; - let lsd = ScreenDescriptor::from(lsd_buffer); + let palette = if screen_descriptor.has_color_table() { + Some( + self.reader + .read_palette(screen_descriptor.color_table_len())?, + ) + } else { + None + }; - Ok(Gif { - header: version, - screen_descriptor: lsd, - global_color_table: None, - blocks: vec![], + Ok(Reader { + version, + screen_descriptor, + palette, + reader: self.reader, + saw_trailer: false, }) } - fn read_color_table(reader: &mut SmartReader, size: usize) -> Result<Palette, DecodeError> { - let buffer = reader - .take(size as usize) - .ok_or(DecodeError::UnexpectedEof)?; + fn read_version(&mut self) -> Result<Version, DecodeError> { + let mut buf = [0; 6]; + self.reader.read_exact(&mut buf)?; - // We get the size from the screen descriptor. This should never return Err - Ok(Palette::try_from(&buffer[..]).unwrap()) + match buf.as_slice() { + b"GIF87a" => Ok(Version::Gif87a), + b"GIF89a" => Ok(Version::Gif89a), + _ => Err(DecodeError::InvalidVersion), + } } - fn read_block(reader: &mut SmartReader) -> Result<Option<Block>, DecodeError> { - let block_id = reader.u8().ok_or(DecodeError::UnexpectedEof)?; - - //TODO: remove panic - match block_id { - 0x21 => Self::read_extension(reader).map(|block| Some(block)), - 0x2C => Self::read_image(reader).map(|block| Some(block)), - 0x3B => Ok(None), - _ => panic!( - "Unknown block identifier {:X} {:X}", - block_id, reader.position - ), - } + fn read_screen_descriptor(&mut self) -> Result<ScreenDescriptor, DecodeError> { + let mut buf = [0; 7]; + self.reader.read_exact(&mut buf)?; + Ok(buf.into()) } +} - fn read_extension(reader: &mut SmartReader) -> Result<Block, DecodeError> { - let extension_id = reader.u8().expect("File ended early"); +pub struct ReadBlock { + pub offset: Range<usize>, + pub block: Block, +} - match extension_id { - 0xF9 => { - reader.skip(1); // Skip block length, we know it - let mut data = [0u8; 4]; - reader - .read_exact(&mut data) - .ok_or(DecodeError::UnexpectedEof)?; - reader.skip(1); // Skip block terminator - - Ok(Block::GraphicControlExtension(GraphicControl::from(data))) +pub struct Reader<R: Read> { + pub version: Version, + pub screen_descriptor: ScreenDescriptor, + pub palette: Option<Palette>, + + reader: SmartReader<R>, + saw_trailer: bool, +} + +impl<R: Read> Reader<R> { + pub fn block(&mut self) -> Result<Option<ReadBlock>, DecodeError> { + if self.saw_trailer { + return Ok(None); + } + + let before = self.reader.bytes_read; + let introducer = self.reader.u8()?; + + match introducer { + 0x2C => { + let mut buf = [0; 9]; + self.reader.read_exact(&mut buf)?; + let descriptor: ImageDescriptor = buf.into(); + + let palette = if descriptor.has_color_table() { + Some(self.reader.read_palette(descriptor.color_table_size())?) + } else { + None + }; + + let lzw_code_size = self.reader.u8()?; + let data = self.reader.take_data_subblocks()?; + let after = self.reader.bytes_read; + + Ok(Some(ReadBlock { + offset: before..after, + block: Block::CompressedImage(CompressedImage { + image_descriptor: descriptor, + local_color_table: palette, + lzw_code_size, + blocks: data, + }), + })) } - 0xFE => Ok(Block::CommentExtension( - reader.take_and_collapse_subblocks(), - )), - 0x01 => todo!(), //TODO: do; plain text extension - 0xFF => { - //TODO: error instead of unwraps - assert_eq!(Some(11), reader.u8()); - let identifier = TryInto::try_into(reader.take(8).unwrap()).unwrap(); - let authentication_code: [u8; 3] = - TryInto::try_into(reader.take(3).unwrap()).unwrap(); - let data = reader.take_and_collapse_subblocks(); - - Ok(Block::ApplicationExtension(Application { - identifier, - authentication_code, - data, + 0x21 => { + let block = self.read_extension()?; + + Ok(Some(ReadBlock { + offset: before..self.reader.bytes_read, + block, })) } - _ => panic!("Unknown Extension Identifier!"), + 0x3B => { + self.saw_trailer = true; + + Ok(None) + } + _ => Err(DecodeError::UnknownBlock { byte: introducer }), } } - fn read_image(mut reader: &mut SmartReader) -> Result<Block, DecodeError> { - let mut buffer = [0u8; 9]; - reader - .read_exact(&mut buffer) - .ok_or(DecodeError::UnexpectedEof)?; - let descriptor = ImageDescriptor::from(buffer); + fn read_extension(&mut self) -> Result<Block, DecodeError> { + let label = self.reader.u8()?; - let color_table = if descriptor.has_color_table() { - let size = descriptor.color_table_size() * 3; - Some(Self::read_color_table(&mut reader, size)?) - } else { - None - }; - - let lzw_csize = reader.u8().ok_or(DecodeError::UnexpectedEof)?; - let compressed_data = reader.take_data_subblocks(); + match label { + 0xF9 => { + // Graphics Control Extension + let _len = self.reader.u8()?; + let mut buf = [0; 4]; + self.reader.read_exact(&mut buf)?; + let _ = self.reader.u8()?; + let gce = GraphicControl::from(buf); + + Ok(Block::GraphicControlExtension(gce)) + } + 0xFE => { + // Comment Extension + let data = self.reader.take_and_collapse_subblocks()?; + Ok(Block::CommentExtension(data)) + } + 0xFF => { + //TODO: Should we check this is 11? + let _len = self.reader.u8()?; + let mut app_id = [0; 8]; + let mut auth = [0; 3]; + self.reader.read_exact(&mut app_id)?; + self.reader.read_exact(&mut auth)?; + let data = self.reader.take_and_collapse_subblocks()?; + let app = Application { + identifier: app_id, + authentication_code: auth, + data, + }; - Ok(Block::CompressedImage(CompressedImage { - image_descriptor: descriptor, - local_color_table: color_table, - lzw_code_size: lzw_csize, - blocks: compressed_data, - })) + Ok(Block::ApplicationExtension(app)) + } + _ => Err(DecodeError::UnknownExtension), + } } } -struct SmartReader { - inner: Vec<u8>, - position: usize, +struct SmartReader<R: Read> { + inner: R, + bytes_read: usize, } -impl SmartReader { - pub fn u8(&mut self) -> Option<u8> { - self.position += 1; - self.inner.get(self.position - 1).map(|b| *b) +impl<R: Read> SmartReader<R> { + pub fn new(reader: R) -> Self { + Self { + inner: reader, + bytes_read: 0, + } } - pub fn u16(&mut self) -> Option<u16> { - self.position += 2; - self.inner - .get(self.position - 2..self.position) - .map(|bytes| u16::from_le_bytes(bytes.try_into().unwrap())) - } + pub fn u8(&mut self) -> Result<u8, DecodeError> { + let mut buffer = [0]; - pub fn skip(&mut self, size: usize) { - self.position += size; + match self.inner.read(&mut buffer) { + Ok(read) => { + self.bytes_read += read; + Ok(buffer[0]) + } + Err(e) => Err(DecodeError::IoError(e)), + } } - pub fn take(&mut self, size: usize) -> Option<&[u8]> { - self.position += size; - self.inner.get(self.position - size..self.position) + #[allow(dead_code)] + pub fn u16(&mut self) -> Result<u16, DecodeError> { + let mut buffer = [0, 0]; + + self.read_exact(&mut buffer).map(|_| { + self.bytes_read += 2; + u16::from_le_bytes(buffer) + }) } //TODO: Result not Option when buffer len - pub fn read_exact(&mut self, buf: &mut [u8]) -> Option<()> { - if self.position + buf.len() > self.inner.len() { - None - } else { - self.position += buf.len(); - buf.copy_from_slice(&self.inner[self.position - buf.len()..self.position]); - Some(()) + pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), DecodeError> { + match self.inner.read_exact(buf) { + Ok(_) => { + self.bytes_read += buf.len(); + Ok(()) + } + Err(e) if e.kind() == ErrorKind::UnexpectedEof => Err(DecodeError::UnexpectedEof), + Err(e) => Err(DecodeError::IoError(e)), } } - pub fn take_vec(&mut self, size: usize) -> Option<Vec<u8>> { - self.position += size; - self.inner - .get(self.position - size..self.position) - .map(|bytes| bytes.to_vec()) - } - - pub fn take_lossy_utf8(&mut self, size: usize) -> Option<Cow<'_, str>> { - self.take(size).map(|bytes| String::from_utf8_lossy(bytes)) - } - - pub fn take_data_subblocks(&mut self) -> Vec<Vec<u8>> { + pub fn take_data_subblocks(&mut self) -> Result<Vec<Vec<u8>>, DecodeError> { let mut blocks = vec![]; loop { - let block_size = self.u8().expect("Failed to read length of sublock"); + let block_size = self.u8()?; if block_size == 0 { - return blocks; + return Ok(blocks); } - let block = self - .take_vec(block_size as usize) - .expect("Failed to read sublock"); + let mut block = vec![0; block_size as usize]; + self.read_exact(&mut block)?; blocks.push(block); } } - pub fn take_and_collapse_subblocks(&mut self) -> Vec<u8> { - let blocks = self.take_data_subblocks(); + pub fn take_and_collapse_subblocks(&mut self) -> Result<Vec<u8>, DecodeError> { + let blocks = self.take_data_subblocks()?; let mut ret = vec![]; for block in blocks { ret.extend_from_slice(&block) } - ret + Ok(ret) + } + + pub fn read_palette(&mut self, count: usize) -> Result<Palette, DecodeError> { + let mut buf = vec![0; count as usize * 3]; + self.read_exact(&mut buf)?; + + Ok(Palette::try_from(buf.as_slice()).unwrap()) } } @@ -234,6 +266,9 @@ pub enum DecodeError { UnknownVersionString, UnexpectedEof, ColorIndexOutOfBounds, + InvalidVersion, + UnknownBlock { byte: u8 }, + UnknownExtension, } impl Error for DecodeError {} @@ -253,6 +288,17 @@ impl fmt::Display for DecodeError { "The image contained an index not found in the color table" ) } + DecodeError::InvalidVersion => { + write!(f, "GIF header was incorrect") + } + DecodeError::UnknownBlock { byte } => { + //TODO: gen- Better error message + write!(f, "No block with introducer {byte:02X}") + } + DecodeError::UnknownExtension => { + //TODO: gen- Better error message + write!(f, "Unknown extension") + } } } } |