Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add safe boolean transmutation module #11

Merged
merged 2 commits into from
Dec 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions src/bool.rs
Original file line number Diff line number Diff line change
@@ -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::<bool>();
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::<bool>(),
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<u8>) -> Result<Vec<bool>, 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<u8>) -> Result<Vec<bool>, Error> {
check_bool(&bytes)?;
unsafe { guarded_transmute_vec_pedantic(bytes) }
}
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}


Expand All @@ -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",
}
}
}
Expand Down
12 changes: 8 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -64,6 +66,7 @@
//! ```


mod bool;
mod pod;
mod error;

Expand All @@ -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.
///
Expand Down
41 changes: 41 additions & 0 deletions tests/guarded_transmute_bool_pedantic/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}));
}
35 changes: 35 additions & 0 deletions tests/guarded_transmute_bool_permissive/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}));
}
42 changes: 42 additions & 0 deletions tests/guarded_transmute_bool_vec_pedantic/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}));
}
36 changes: 36 additions & 0 deletions tests/guarded_transmute_bool_vec_permissive/mod.rs
Original file line number Diff line number Diff line change
@@ -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,
}));
}
5 changes: 4 additions & 1 deletion tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");