16 releases (10 breaking)

Uses old Rust 2015

0.11.2 May 31, 2021
0.11.1 Feb 3, 2021
0.11.0 Jun 20, 2020
0.11.0-rc.2 Jul 8, 2019
0.3.0 Nov 11, 2016

#95 in Rust patterns

Download history 3540/week @ 2022-06-03 3318/week @ 2022-06-10 3512/week @ 2022-06-17 3110/week @ 2022-06-24 3766/week @ 2022-07-01 3587/week @ 2022-07-08 3437/week @ 2022-07-15 4043/week @ 2022-07-22 3775/week @ 2022-07-29 4622/week @ 2022-08-05 3809/week @ 2022-08-12 2803/week @ 2022-08-19 2814/week @ 2022-08-26 3016/week @ 2022-09-02 3925/week @ 2022-09-09 2932/week @ 2022-09-16

13,114 downloads per month
Used in 96 crates (21 directly)

MIT license

94KB
690 lines

safe-transmute-rs TravisCI build status AppVeyorCI build status Licence Crates.io version

A safeguarded transmute() for Rust.

Documentation


lib.rs:

This crate contains checked implementations of transmutation procedures, some of which ensure memory safety.

Crate outline

The following modules are available:

  • The functions in the base module are not inherently safe, but just protected against out of boundary access (like trying to create an 8-byte type from 7 bytes). These functions are as safe as the data passed to them: any attempt of transmuting data to an invalid memory representation is still undefined behavior. Moreover, unaligned memory access is not prevented, and must be previously ensured by the caller.
  • The guard module contains the Guard API, which imposes slice boundary restrictions in a conversion.
  • The trivial module introduces the TriviallyTransmutable trait, which statically ensures that any bit combination makes a valid value for a given type. The functions in this module are safer than base, but still do not prevent unaligned memory access.
  • to_bytes enables the opposite operation of reintepreting values as bytes.
  • The bool module ensures safe transmutation of bytes to boolean values.
  • At the root of this crate, there are transmutation functions with enough checks to be considered safe to use in any circumstance. The operation may still arbitrarily return (recoverable) errors due to unaligned data or incompatible vector transmutation targets, but it will not eat your laundry, and helper functions are available to assist the programmer in making some use cases work.

This crate can be used in a no-std environment by disabling the std feature through specifying default-features = false on import. However, std is only used for integration with std::error::Error.

Note, though, that functions operating on items from alloc will also be disabled by this. If your no-std environment has an alloc implementation, you will have to reenable them by using features = ["alloc"].

Migrating

If you've used safe-transmute before v0.11, we recommend the v0.11 migration guide to help get you going quickly.

Examples

View bytes as a series of u16s, with a single-many boundary guard (at least one value, extraneous bytes are allowed):

# use safe_transmute::{SingleManyGuard, Error, transmute_many};
# #[cfg(feature = "std")]
# fn main() -> Result<(), Box<std::error::Error>> {
let bytes = &[0x00, 0x01, 0x12, 0x24,
              0x00]; // 1 spare byte
match transmute_many::<u16, SingleManyGuard>(bytes) {
    Ok(words) => {
        assert_eq!(words,
                   [u16::from_be(0x0001), u16::from_be(0x1224)]);
    },
    Err(Error::Unaligned(e)) => {
        // Copy needed, would otherwise trap on some archs
        let words = e.copy();
        assert_eq!(*words,
                   [u16::from_be(0x0001), u16::from_be(0x1224)]);
    },
    Err(e) => panic!("Unexpected error: {}", e),
}
# Ok(())
# }
# #[cfg(not(feature = "std"))]
# fn main() {}

Since one may not always be able to ensure that a slice of bytes is well aligned for reading data of different constraints, such as from u8 to u16, the operation may fail without a trivial way of preventing it.

As a remedy, the data can instead be copied byte-for-byte to a new vector, with the help of the try_copy!() macro.

# #[macro_use]
# extern crate safe_transmute;
# use safe_transmute::{SingleManyGuard, Error, transmute_many};
# #[cfg(feature = "std")]
# fn main() -> Result<(), Box<std::error::Error>> {
let bytes = &[0x00, 0x01, 0x12, 0x24, 0x00];
let words = try_copy!(transmute_many::<u16, SingleManyGuard>(bytes));

assert_eq!(*words,
           [u16::from_be(0x0001), u16::from_be(0x1224)]);
# Ok(())
# }
# #[cfg(not(feature = "std"))]
# fn main() {}

View all bytes as a series of u16s:

# #[macro_use]
# extern crate safe_transmute;
# use safe_transmute::{Error, transmute_many_pedantic};
# include!("../tests/test_util/le_to_native.rs");
# #[cfg(feature = "std")]
# fn main() -> Result<(), Box<std::error::Error>> {
# let bytes = &[0x00, 0x01, 0x12, 0x34].le_to_native::<u16>();
# let words = try_copy!(transmute_many_pedantic::<u16>(bytes).map_err(Error::without_src));
# /*
// Assuming little-endian
let bytes = &[0x00, 0x01, 0x12, 0x34];
let words = try_copy!(transmute_many_pedantic::<u16>(bytes));
# */

assert_eq!(*words, [0x0100, 0x3412]);
# Ok(())
# }
# #[cfg(not(feature = "std"))]
# fn main() {}

View a byte slice as a single f64:

# use safe_transmute::transmute_one;
# include!("../tests/test_util/le_to_native.rs");
# fn main() {
assert_eq!(transmute_one::<f64>(
# /*
   &[0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x40])?,
# */
#   &Le2NAl8([0x00, 0x00, 0x00, 0x00,
#             0x00, 0x00, 0x00, 0x40]).0.le_to_native::<f64>()).unwrap(),
   2.0);
# }

View a series of u16s as bytes:

# use safe_transmute::transmute_to_bytes;
# include!("../tests/test_util/le_to_native.rs");
# fn main() {
assert_eq!(transmute_to_bytes(&[0x0001u16,
                                0x1234u16]),
# /*
           &[0x01, 0x00, 0x34, 0x12]);
# */
#          &[0x01, 0x00, 0x34, 0x12].le_to_native::<u16>());
# }

No runtime deps

Features

  • alloc
  • const_generics
  • std