Skip to content

Add functions cast::transmute_slice, cast::transmute_mut_slice #9972

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

Closed
wants to merge 3 commits into from
Closed
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
54 changes: 54 additions & 0 deletions src/libstd/cast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use ptr::RawPtr;
use mem;
use unstable::intrinsics;
use unstable::raw::Slice;

/// Casts the value at `src` to U. The two types must have the same length.
#[cfg(target_word_size = "32")]
Expand Down Expand Up @@ -111,6 +112,30 @@ pub unsafe fn transmute_mut_region<'a,'b,T>(ptr: &'a mut T) -> &'b mut T {
transmute(ptr)
}

/// Coerce a slice of one type into a slice of another.
/// This function modifies the length of the resulting slice appropriately.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you mention the semantics of the division: i.e. that trailing bytes get ignored, and that &[A] -> &[B] -> &[A] isn't necessarily a noop.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added some expository comments.

///
/// Note that if the coercion leaves some bytes from the original slice un-addressable
/// with the new slice, then this function will not be able to round-trip back to the
/// original slice. For example, if a &[u8] of length 16 is transmuted to a &[[u8,..3]],
/// which will have length 5, transmuting back to a &[u8] will produce a slice of length 15.
#[inline]
pub unsafe fn transmute_slice<'a,L,G>(thing: &'a [L]) -> &'a [G] {
let mut slice: Slice<G> = transmute(thing);
let bytes = slice.len * mem::nonzero_size_of::<L>();
// NB: the previous operation cannot overflow. A slice cannot possibly hold more memory than
// is possible to allocate, and uint can represent all valid allocation sizes.
slice.len = bytes / mem::nonzero_size_of::<G>();
transmute(slice)
}

/// Coerce a mutable slice of one type into a mutable slice of another.
/// This function modifies the length of the resulting slice appropriately.
#[inline]
pub unsafe fn transmute_mut_slice<'a,L,G>(thing: &'a mut [L]) -> &'a mut [G] {
transmute(transmute_slice::<L,G>(thing))
}

/// Transforms lifetime of the second pointer to match the first.
#[inline]
pub unsafe fn copy_lifetime<'a,S,T>(_ptr: &'a S, ptr: &T) -> &'a T {
Expand Down Expand Up @@ -138,6 +163,7 @@ pub unsafe fn copy_lifetime_vec<'a,S,T>(_ptr: &'a [S], ptr: &T) -> &'a T {
mod tests {
use cast::{bump_box_refcount, transmute};
use unstable::raw;
use container::Container;

#[test]
fn test_transmute_copy() {
Expand Down Expand Up @@ -175,4 +201,32 @@ mod tests {
assert_eq!(~[76u8], transmute(~"L"));
}
}

#[test]
fn test_transmute_slice() {
use cast::transmute_slice;
use unstable::raw::Vec;

unsafe {
let u8ary = [0u8, ..16];
let u32ary = [0u32, ..4];
assert_eq!(4, transmute_slice::<u8,u32>(u8ary).len());
assert_eq!(16, transmute_slice::<u32,u8>(u32ary).len());

let threeary = [[0u8, ..3], ..5];
assert_eq!(5, transmute_slice::<u8, [u8,..3]>(u8ary).len());
assert_eq!(15, transmute_slice::<[u8,..3],u8>(threeary).len());

// ensure the repr for a Vec uses nonzero sizes, since we rely on that
let vec: *Vec<()> = transmute(~[()]);
assert_eq!(1, (*vec).fill);
transmute::<*Vec<()>,~[()]>(vec); // don't leak it

let unitary = [(), ..16];
assert_eq!(16, transmute_slice::<u32,()>(u32ary).len());
assert_eq!(16, transmute_slice::<u8,()>(u8ary).len());
assert_eq!(4, transmute_slice::<(),u32>(unitary).len());
assert_eq!(16, transmute_slice::<(),u8>(unitary).len());
}
}
}
4 changes: 2 additions & 2 deletions src/libstd/rand/isaac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ impl IsaacRng {
let mut rng = EMPTY;

{
let bytes = unsafe {cast::transmute::<&mut [u32], &mut [u8]>(rng.rsl)};
let bytes = unsafe { cast::transmute_mut_slice::<u32,u8>(rng.rsl) };
OSRng::new().fill_bytes(bytes);
}

Expand Down Expand Up @@ -239,7 +239,7 @@ impl Isaac64Rng {
pub fn new() -> Isaac64Rng {
let mut rng = EMPTY_64;
{
let bytes = unsafe {cast::transmute::<&mut [u64], &mut [u8]>(rng.rsl)};
let bytes = unsafe { cast::transmute_mut_slice::<u64,u8>(rng.rsl) };
OSRng::new().fill_bytes(bytes);
}
rng.init(true);
Expand Down
16 changes: 6 additions & 10 deletions src/libstd/rand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@ fn main () {
```
*/

use mem::size_of;
use unstable::raw::Slice;
use cast;
use container::Container;
use iter::{Iterator, range};
Expand Down Expand Up @@ -136,11 +134,11 @@ pub trait Rng {
/// }
/// ```
fn fill_bytes(&mut self, dest: &mut [u8]) {
let mut slice: Slice<u64> = unsafe { cast::transmute_copy(&dest) };
slice.len /= size_of::<u64>();
let as_u64: &mut [u64] = unsafe { cast::transmute(slice) };
for dest in as_u64.mut_iter() {
*dest = self.next_u64();
{
let as_u64: &mut [u64] = unsafe { cast::transmute_mut_slice(dest) };
for dest in as_u64.mut_iter() {
*dest = self.next_u64();
}
}

// the above will have filled up the vector as much as
Expand All @@ -149,9 +147,7 @@ pub trait Rng {

// space for a u32
if remaining >= 4 {
let mut slice: Slice<u32> = unsafe { cast::transmute_copy(&dest) };
slice.len /= size_of::<u32>();
let as_u32: &mut [u32] = unsafe { cast::transmute(slice) };
let as_u32: &mut [u32] = unsafe { cast::transmute_mut_slice(dest) };
as_u32[as_u32.len() - 1] = self.next_u32();
remaining -= 4;
}
Expand Down
1 change: 1 addition & 0 deletions src/libstd/std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,4 +225,5 @@ mod std {
pub use fmt;
pub use to_bytes;
pub use logging;
pub use io;
}