diff --git a/src/bool.rs b/src/bool.rs new file mode 100644 index 0000000..c0f88e0 --- /dev/null +++ b/src/bool.rs @@ -0,0 +1,120 @@ +//! Functions for safe transmutation to `bool`. +//! Transmuting to `bool' is not undefined behavior if the transmuted value is +//! either 0 or 1. These functions will return an error if the integer value +//! behind the `bool` value is neither one. + +use self::super::{guarded_transmute_many_pedantic, guarded_transmute_many_permissive, guarded_transmute_vec_pedantic, guarded_transmute_vec_permissive, Error, + ErrorReason}; + +use std::mem::align_of; + +/// Makes sure that the bytes represent a sequence of valid boolean values. It is done +/// this way because the language does not guarantee that `bool` is 1 byte sized. +#[inline] +pub fn bytes_are_bool(v: &[u8]) -> bool { + let sizeof_bool: usize = ::std::mem::align_of::(); + v.chunks(sizeof_bool) + .filter(|c| c.len() == sizeof_bool) + .all(|c| { + let (rest, lsb) = if cfg!(target_endian = "little") { + (&c[1..], c) + } else { + c.split_at(sizeof_bool - 1) + }; + lsb[0] <= 1 && rest.iter().all(|&x| x == 0) + }) +} + +fn check_bool(bytes: &[u8]) -> Result<(), Error> { + if bytes_are_bool(bytes) { + Ok(()) + } else { + Err(Error { + required: align_of::(), + actual: bytes.len(), + reason: ErrorReason::InvalidValue, + }) + } +} + +/// View a byte slice as a slice of boolean values. +/// +/// The resulting slice will have as many instances of `bool` as will fit, can be empty. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_permissive; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_permissive(&[0x00, 0x01, 0x00, 0x01]).unwrap(), +/// &[false, true, false, true]); +/// assert_eq!(guarded_transmute_bool_permissive(&[]).unwrap(), &[]); +/// # } + +/// ``` +pub fn guarded_transmute_bool_permissive(bytes: &[u8]) -> Result<&[bool], Error> { + check_bool(bytes)?; + unsafe { Ok(guarded_transmute_many_permissive(bytes)) } +} + +/// View a byte slice as a slice of boolean values. +/// +/// The byte slice must have at least enough bytes to fill a single `bool`. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_pedantic; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_pedantic(&[0x01, 0x01, 0x01, 0x01]).unwrap(), +/// &[true, true, true, true]); +/// assert!(guarded_transmute_bool_pedantic(&[]).is_err()); +/// # } +/// ``` +pub fn guarded_transmute_bool_pedantic(bytes: &[u8]) -> Result<&[bool], Error> { + check_bool(bytes)?; + unsafe { guarded_transmute_many_pedantic(bytes) } +} + +/// Trasform a byte vector into a vector of bool. +/// +/// The vector's allocated byte buffer will be reused when possible, and +/// have as many instances of a type as will fit, rounded down. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_vec_permissive; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x00, 0x01]).unwrap(), +/// vec![false, true, false, true]); +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x01, 0x00, 0x00, 0x00, 0x01]).unwrap(), +/// vec![true, false, false, false, true]); +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![]), Ok(vec![])); +/// # } +/// ``` +pub fn guarded_transmute_bool_vec_permissive(bytes: Vec) -> Result, Error> { + check_bool(&bytes)?; + unsafe { Ok(guarded_transmute_vec_permissive(bytes)) } +} + +/// Trasform a byte vector into a vector of bool. +/// +/// The vector's allocated byte buffer will be reused when possible, and +/// should not have extraneous data. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_vec_pedantic; +/// assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x00, 0x01]).unwrap(), +/// vec![false, true, false, true]); +/// +/// assert!(guarded_transmute_bool_vec_pedantic(vec![]).is_err()); +/// +/// assert!(guarded_transmute_bool_vec_pedantic(vec![0x04, 0x00, 0xED]).is_err()); +/// ``` +pub fn guarded_transmute_bool_vec_pedantic(bytes: Vec) -> Result, Error> { + check_bool(&bytes)?; + unsafe { guarded_transmute_vec_pedantic(bytes) } +} diff --git a/src/error.rs b/src/error.rs index c1fda3e..fe91eaf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,8 @@ pub enum ErrorReason { TooManyBytes, /// The byte amount received is not the same as the type's size. InexactByteCount, + /// The byte count is fine, but the data contains an invalid value for the target type. + InvalidValue, } @@ -47,6 +49,7 @@ impl StdError for Error { ErrorReason::NotEnoughBytes => "Not enough bytes to fill type", ErrorReason::TooManyBytes => "Too many bytes for type", ErrorReason::InexactByteCount => "Not exactly the amount of bytes for type", + ErrorReason::InvalidValue => "Invalid target value detected", } } } diff --git a/src/lib.rs b/src/lib.rs index 72ccd29..7f77e26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ //! This crate contains checked implementations of `transmute()`. //! -//! The functions in this crate are not inherently safe, but just guarded against common simple mistakes +//! The base functions in this crate are not inherently safe, but just guarded against common simple mistakes //! (like trying to create an 8-byte type from 7 bytes). -//! -//! Those functions are exactly as safe as the data passed to them - creating a null pointer, +//! These functions are exactly as safe as the data passed to them - creating a null pointer, //! for example, is not unsafe in and of itself, but dereferencing it certainly *is*, //! but they don't do that (see [here](https://github.com/nabijaczleweli/safe-transmute-rs/issues/1) //! for extended discussion). //! +//! Other functions in this crate, on the other hand, provide enough safety measures to ensure safety on +//! all circumstances. This is the case for those found in the `pod` and `bool` modules. +//! //! # Examples //! //! View bytes as a series of `u16`s: @@ -64,6 +66,7 @@ //! ``` +mod bool; mod pod; mod error; @@ -76,7 +79,8 @@ pub use self::error::{ErrorReason, Error}; pub use self::pod::{PodTransmutable, guarded_transmute_pod_many_permissive, guarded_transmute_pod_vec_permissive, guarded_transmute_pod_many_pedantic, guarded_transmute_pod_pedantic, guarded_transmute_pod_vec_pedantic, guarded_transmute_pod_many, guarded_transmute_pod, guarded_transmute_pod_vec}; - +pub use self::bool::{guarded_transmute_bool_pedantic, guarded_transmute_bool_permissive, guarded_transmute_bool_vec_pedantic, + guarded_transmute_bool_vec_permissive}; /// Transmute a byte slice into a single instance of a `Copy`able type. /// diff --git a/tests/guarded_transmute_bool_pedantic/mod.rs b/tests/guarded_transmute_bool_pedantic/mod.rs new file mode 100644 index 0000000..ab09452 --- /dev/null +++ b/tests/guarded_transmute_bool_pedantic/mod.rs @@ -0,0 +1,41 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_pedantic}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_pedantic([].as_ref()), + Err(Error { + required: 1, + actual: 0, + reason: ErrorReason::NotEnoughBytes, + })); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_pedantic([0x00, 0x01].as_ref()), + Ok([false, true].into_iter().as_slice())); + assert_eq!(guarded_transmute_bool_pedantic([0x01, 0x01, 0x00, 0x01].as_ref()), + Ok([true, true, false, true].into_iter().as_slice())); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_pedantic([0x00, 0x01, 0x02].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_pedantic([0x05, 0x01, 0x00].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_pedantic([0xFF].as_ref()), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_permissive/mod.rs b/tests/guarded_transmute_bool_permissive/mod.rs new file mode 100644 index 0000000..ea4030e --- /dev/null +++ b/tests/guarded_transmute_bool_permissive/mod.rs @@ -0,0 +1,35 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_permissive}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_permissive([].as_ref()), Ok([].as_ref())); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01].as_ref()), Ok([false, true].as_ref())); + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01, 0x00, 0x01].as_ref()), + Ok([false, true, false, true].as_ref())); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01, 0x02].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_permissive([0x05, 0x01, 0x00].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_permissive([0xFF].as_ref()), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_vec_pedantic/mod.rs b/tests/guarded_transmute_bool_vec_pedantic/mod.rs new file mode 100644 index 0000000..b0c38b5 --- /dev/null +++ b/tests/guarded_transmute_bool_vec_pedantic/mod.rs @@ -0,0 +1,42 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_vec_pedantic}; + + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![]), + Err(Error { + required: 1, + actual: 0, + reason: ErrorReason::NotEnoughBytes, + })); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01]), + Ok(vec![false, true])); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x00, 0x01]), + Ok(vec![false, true, false, true])); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x02]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x05, 0x01, 0x00]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0xFF]), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_vec_permissive/mod.rs b/tests/guarded_transmute_bool_vec_permissive/mod.rs new file mode 100644 index 0000000..3c76251 --- /dev/null +++ b/tests/guarded_transmute_bool_vec_permissive/mod.rs @@ -0,0 +1,36 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_vec_permissive}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![]), Ok(vec![])); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01]), + Ok(vec![false, true])); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x00, 0x01]), + Ok(vec![false, true, false, true])); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x02]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x05, 0x01, 0x00]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0xFF]), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/lib.rs b/tests/lib.rs index 18f9c19..4cda620 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -18,6 +18,9 @@ mod guarded_transmute_pod_many_permissive; mod guarded_transmute_pod_vec; mod guarded_transmute_pod_vec_pedantic; mod guarded_transmute_pod_vec_permissive; - +mod guarded_transmute_bool_pedantic; +mod guarded_transmute_bool_permissive; +mod guarded_transmute_bool_vec_pedantic; +mod guarded_transmute_bool_vec_permissive; include!("test_util/le_to_native.rs");