diff --git a/src/bytecast.rs b/src/bytecast.rs new file mode 100644 index 0000000..c6a0f50 --- /dev/null +++ b/src/bytecast.rs @@ -0,0 +1,31 @@ +//! Trivial, internal byte transmutation. +//! +//! A dependency like bytemuck would give us extra assurance of the safety but overall would not +//! reduce the amount of total unsafety. We don't use it in the interface where the traits would +//! really become useful. +//! +//! SAFETY: These are benign casts as we apply them to fixed size integer types only. All of them +//! are naturally aligned, valid for all bit patterns and their alignment is surely at most their +//! size (we assert the latter fact since it is 'implementation defined' if following the letter of +//! the unsafe code guidelines). +//! +//! TODO: Would like to use std-lib here. +use std::{mem, slice}; + +macro_rules! integral_slice_as_bytes{($int:ty, $const:ident $(,$mut:ident)*) => { + pub(crate) fn $const(slice: &[$int]) -> &[u8] { + assert!(mem::align_of::<$int>() <= mem::size_of::<$int>()); + unsafe { slice::from_raw_parts(slice.as_ptr() as *const u8, mem::size_of_val(slice)) } + } + $(pub(crate) fn $mut(slice: &mut [$int]) -> &mut [u8] { + assert!(mem::align_of::<$int>() <= mem::size_of::<$int>()); + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, mem::size_of_val(slice)) } + })* +}} + +integral_slice_as_bytes!(i8, i8_as_ne_bytes); +integral_slice_as_bytes!(u16, u16_as_ne_bytes, u16_as_ne_mut_bytes); +integral_slice_as_bytes!(i16, i16_as_ne_bytes); +integral_slice_as_bytes!(u32, u32_as_ne_bytes, u32_as_ne_mut_bytes); +integral_slice_as_bytes!(i32, i32_as_ne_bytes); +integral_slice_as_bytes!(u64, u64_as_ne_bytes, u64_as_ne_mut_bytes); diff --git a/src/decoder/stream.rs b/src/decoder/stream.rs index 53595f1..db9d36c 100644 --- a/src/decoder/stream.rs +++ b/src/decoder/stream.rs @@ -1,10 +1,10 @@ //! All IO functionality needed for TIFF decoding +use crate::bytecast; use crate::error::{TiffError, TiffResult}; use lzw; use miniz_oxide::inflate; use std::io::{self, Read, Seek}; -use std::slice; /// Byte order of the TIFF file. #[derive(Clone, Copy, Debug)] @@ -33,7 +33,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u16_into(&mut self, buffer: &mut [u16]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 2) })?; + self.read_exact(bytecast::u16_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { @@ -71,7 +71,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u32_into(&mut self, buffer: &mut [u32]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 4) })?; + self.read_exact(bytecast::u32_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { @@ -109,7 +109,7 @@ pub trait EndianReader: Read { #[inline(always)] fn read_u64_into(&mut self, buffer: &mut [u64]) -> Result<(), io::Error> { - self.read_exact(unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr() as *mut u8, buffer.len() * 8) })?; + self.read_exact(bytecast::u64_as_ne_mut_bytes(buffer))?; match self.byte_order() { ByteOrder::LittleEndian => for n in buffer { diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 35cb4b8..eb62d33 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -3,6 +3,7 @@ use std::convert::TryFrom; use std::io::{Seek, Write}; use std::mem; +use bytecast; use tags::{self, ResolutionUnit, Tag, Type}; use error::{TiffError, TiffFormatError, TiffResult}; @@ -59,9 +60,7 @@ impl TiffValue for [i8] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len()) }; + let slice = bytecast::i8_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -76,9 +75,7 @@ impl TiffValue for [u16] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 2) }; + let slice = bytecast::u16_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -93,9 +90,7 @@ impl TiffValue for [i16] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; + let slice = bytecast::i16_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -110,9 +105,7 @@ impl TiffValue for [u32] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 4) }; + let slice = bytecast::u32_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -127,9 +120,7 @@ impl TiffValue for [i32] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this should be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * Self::BYTE_LEN as usize) }; + let slice = bytecast::i32_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } @@ -144,9 +135,7 @@ impl TiffValue for [u64] { } fn write(&self, writer: &mut TiffWriter) -> TiffResult<()> { - // We write using nativeedian so this sould be safe - let slice = - unsafe { ::std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * 8) }; + let slice = bytecast::u64_as_ne_bytes(self); writer.write_bytes(slice)?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index 9f0f1a2..7bb874b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ extern crate lzw; extern crate miniz_oxide; +mod bytecast; pub mod decoder; pub mod encoder; mod error;