9

I have a [u8; 16384] and a u16. How would I "temporarily transmute" the array so I can set the two u8s at once, the first to the least significant byte and the second to the most significant byte?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Shien
  • 495
  • 5
  • 13

3 Answers3

13

The obvious, safe and portable way is to just use math.

fn set_u16_le(a: &mut [u8], v: u16) {
    a[0] = v as u8;
    a[1] = (v >> 8) as u8;
}

If you want a higher-level interface, there's the byteorder crate which is designed to do this.

You should definitely not use transmute to turn a [u8] into a [u16], because that doesn't guarantee anything about the byte order.

DK.
  • 55,277
  • 5
  • 189
  • 162
  • Well, yeah, that's what I have right now, but I was curious about micro-optimisations, and in any case, can't you use `#[cfg(target_endian = "...")]` to check for byte order? – Shien Nov 28 '15 at 07:50
  • @Shien Normally you can get the bitshifting to be near-optimal. I wouldn't worry about it for most use-cases. – Veedrac Nov 28 '15 at 09:45
  • I don't see how your answer does care for byte order either. – Neikos Nov 28 '15 at 11:49
  • 8
    @Neikos The function above doesn't *have* to account for byte order because it's not written in a way that depends on it. – DK. Nov 28 '15 at 12:08
  • But you called it 'portable' yourself. So that seems to be case though. – Neikos Nov 28 '15 at 12:53
  • 5
    @Neikos The goal is to store an u16 number in little endian format into a byte array (note the function's name with the `le` suffix). This is exactly what's happening here regardless of whether you are on a little or big endian machine. So, it's portable. – sellibitze Nov 28 '15 at 13:52
  • This does not answer the question. –  Apr 14 '17 at 23:09
11

slice::align_to and slice::align_to_mut are stable as of Rust 1.30. These functions handle the alignment concerns that sellibitze brings up.

The big- and little- endian problems are still yours to worry about. You may be able to use methods like u16::to_le to help with that. I don't have access to a big-endian computer to test with, however.

fn example(blob: &mut [u8; 16], value: u16) {
   // I copied this example from Stack Overflow without providing 
   // rationale why my specific case is safe.
   let (head, body, tail) = unsafe { blob.align_to_mut::<u16>() };

   // This example simply does not handle the case where the input data
   // is misaligned such that there are bytes that cannot be correctly
   // reinterpreted as u16.
   assert!(head.is_empty());
   assert!(tail.is_empty());

   body[0] = value
}

fn main() {
   let mut data = [0; 16];
   example(&mut data, 500);
   println!("{:?}", data);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
7

As DK suggests, you probably shouldn't really use unsafe code to reinterpret the memory... but you can if you want to.

If you really want to go that route, you should be aware of a couple of gotchas:

  • You could have an alignment problem. If you just take a &mut [u8] from somewhere and convert it to a &mut [u16], it could refer to some memory region that is not properly aligned to be accessed as a u16. Depending on what computer you run this code on, such an unaligned memory access might be illegal. In this case, the program would probably abort somehow. For example, the CPU could generate some kind of signal which the operating system responds to in order to kill the process.
  • It'll be non-portable. Even without the alignment issue, you'll get different results on different machines (little- versus big-endian machines).

If you can switch it around (creating a u16 array and temporarily dealing with it on a byte level), you would solve the potential memory alignment problem:

/// warning: The resulting byte view is system-specific
unsafe fn raw_byte_access(s16: &mut [u16]) -> &mut [u8] {
    use std::slice;
    slice::from_raw_parts_mut(s16.as_mut_ptr() as *mut u8, s16.len() * 2)
}

On a big-endian machine, this function will not do what you want; you want a little-endian byte order. You can only use this as an optimization for little-endian machines and need to stick with a solution like DK's for big- or mixed-endian machines.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
sellibitze
  • 27,611
  • 3
  • 75
  • 95