Extract 7 bits signed integer from u8 byte - rust

I am using the Human Interface Device protocol to get data from an external device. The library I'm using returns an array of bytes ([u8; 64]) which I want to extract an i7 (which will be i8 in Rust) from one byte.
The byte I want to manipulate has two different pieces of information in it:
1 bit for something
the 7 other bits (which I have to decode as a signed integer) for another thing.
Do you know what can I do to achieve this?

Using the crate bitreader I have been able to properly decode the signed integer of 7 bits.
let mut bit_reader = BitReader::new(buffer);
let first_useless_bit: u8 = bit_reader.read_u8(1).unwrap();
let extracted_value: i8 = bit_reader.read_i8(7).unwrap();

Your question is pretty unclear, but I think you are just asking about normal bit manipulation. Mask the 7 bits (assuming the lower 7 bits, although you did not say) and convert the remaining bits to a signed number:
fn main() {
let byte = 0xFFu8;
let byte2 = (byte & 0b0111_1111) as i8;
println!("{}", byte2);
}

If you want to turn an array of u8 into a vector of i8 while ignoring the most significant bit, you can do it in the following manner:
fn main() {
let array_unsigned = [1u8, 2, 3]; // this will work for 64 values too
let vec_signed: Vec<i8> = array_unsigned.into_iter()
.map(|&e| if e <= 127 { e as i8 } else { (e - 128) as i8 }).collect();
println!("{:?}", vec_signed);
}
This way consumes the input array. It could probably be done in a nicer way with some bit-fiddling.

Related

Convert float64 to hexadecimal in Rust

I would like to know how to convert a float64 (or float32) to a corresponding binary/hexadecimal format. It would be great to be able to specify endianness as well (prefer to print it in little-endian format).
Linked post: How to convert hex string to a float in Rust?
Thanks!
Use f32::to_be_bytes, f32::to_le_bytes, or f32::to_ne_bytes (depending on the desired endianness) and then format the resulting elements of the array:
let float: f32 = 123.45;
let bytes = float.to_le_bytes();
let hex = format!("{:02X}{:02X}{:02X}{:02X}", bytes[0], bytes[1], bytes[2], bytes[3]);
assert_eq!(hex, "66E6F642");
No need for the unsafe and dangerous transmute.
Rust Playground Link
It's just the inverse of the operations from the answer to the question you linked:
fn main() {
// Hex string to 4-bytes, aka. u32
let float: f32 = 18.9;
let bytes = unsafe { std::mem::transmute::<f32, u32>(float) };
let hex = format!("{:x}", bytes);
// Print 41973333
println!("{}", hex);
}
Rust Playground link
Call .from_be(), .from_le() or .swap_bytes() on the u32 value (bytes) before formatting to alter the byte order. Change from f32 and u32 to f64 and u64 for larger data types.
Similarly, the other answer to that question (using f32.from_bits) has a direct inverse in f32.to_bits (though those functions are still marked as unstable).

Copy a slice of i32 pixels into an [u8] slice

How to copy a row of pixels in an i32 slice into an existing slice of pixels in an [u8] slice ?
Both slices are in the same memory layout (i.e. RGBA) but I don't know the unsafe syntax to copy one efficiently into the other. In C it would just be a memcpy().
You can flat_map the byte representation of each i32 into a Vec<u8>:
fn main() {
let pixels: &[i32] = &[-16776961, 16711935, 65535, -1];
let bytes: Vec<u8> = pixels
.iter()
.flat_map(|e| e.to_ne_bytes())
.collect();
println!("{bytes:?}");
}
There are different ways to handle the endianess of the system, I left to_ne_bytes to preserve the native order, but there are also to_le_bytes and to_be_bytes if that is something that needs to be controlled.
Alternatively, if you know the size of your pixel buffer ahead of time, you can use an unsafe transmute:
const BUF_LEN: usize = 4; // this is your buffer length
fn main() {
let pixels: [i32; BUF_LEN] = [-16776961, 16711935, 65535, -1];
let bytes = unsafe {
std::mem::transmute::<[i32; BUF_LEN], [u8; BUF_LEN * 4]>(pixels)
};
println!("{bytes:?}");
}
Assuming that you in fact do not need any byte reordering, the bytemuck library is the tool to use here, as it allows you to write the i32 to u8 reinterpretation without needing to consider safety (because bytemuck has checked it for you).
Specifically, bytemuck::cast_slice() will allow converting &[i32] to &[u8].
(In general, the function may panic if there is an alignment or size problem, but there never can be such a problem when converting to u8 or any other one-byte type.)

Convert array (or vector) of u16 (or u32, u64) to array of u8

I have no problem to do it for u16 to u8 using bit shifts and cast but how could I do it with an array of u16? Even ideally I would prefer to convert directly from vec to [u8]. What would be the most elegant way to do it?
&my_vector[..] // my vector converted to [u16] but I need [u8]
Was able to make it work thanks to #Aplet123 insight:
From Vector to bytes array
From Vec to [u8]
let mut my_u16_vec : Vec<u16> = Vec::new();
let my_u8_array = my_u16_vec.align_to::<u8>().1;
From bytes array back to Vector
From [u8] to Vec
let n = my_u16_vec.len() * 2;
let my_u16_vec_bis:Vec<u16> = (my_u8_array[..n].align_to::<u16>().1).to_vec();
Getting the bytes right
And then reverse bytes as values are written reversely in memory due to endianness:
for e in my_u16_vec_bis:Vec.iter() {
let value = e >> 8 | (e & 0xff) << 8;
}

How to fill a [u8] array with a repeated u16 value? [duplicate]

This question already has answers here:
How to slice a large Vec<i32> as &[u8]?
(2 answers)
What is the correct way to write `Vec<u16>` content to a file?
(2 answers)
How to convert vector of integers to and from bytes? [duplicate]
(2 answers)
Closed 1 year ago.
I am looking to build an array that will be copied into RAM and sent to an LCD driver. I would like to be able to pass in a color value (color: u16) and use it to populate the array. Note that I am using #![no_std] as this is intended for an embedded application.
The obvious simple approach would be as follows:
let mut block: [u8;256] = [0; 256];
for i in (0..block.len()).step_by(2) {
block[i] = ((color && 0xFF00) >> 8) as u8;
block[i+1] = (color && 0xFF) as u8;
}
As more of my personal experience comes from C, I wondered if there were any better solutions using type casting and conversion. For example, using Rust to initialize a u16 array and cast as a u8 array:
let block_u16: [u16; 128] = [color; 128]
let block_u8 = block_u16 as [u8; 256];
For reference, the target function prototype is:
spi::setup_block(block: &[u8]);
You could go the unsafe route via transmute, but I would not recommend it. I would not vouch for a C-style version to do the same on a little and big-endian machine.
Personally I would take the chunk iterator over the slice.
let color_bytes = color.to_le_bytes();
for word in block.chunks_exact_mut(2) {
word[0] = color_bytes[0];
word[1] = color_bytes[1];
}
You could use something like your second approach (casting the u16 array to a u8 pointer).
let color: u16 = 0x0102;
let mut block: [ u16; 128 ] = [ color; 128 ];
let mut block = unsafe {
core::slice::from_raw_parts_mut( block.as_mut_ptr( ) as *mut u8, block.len( ) * 2 )
};
Beware of endianness with this solution, it might not do what you want on your target architecture.
for chunk in block.chunks_exact_mut(2) {
chunk.copy_from_slice(&color.to_be_bytes());
}

Bitwise operations, comparing a u32 with a byte array

Lets say I have the value 1025 as a byte array and the value 1030 as usize. How would I go about comparing if the byte array is bigger, lesser or equal without deserializing it?
I'm completely stuck, I assume the easisest way is to get the biggest bytes of the byte array, its position, then bitshift the u32 and see if any bits in the byte is set, if not the byte array is bigger.
In short I want to write some functions to be able to decide if a > b, a < b and a == b.
To use a code example
fn is_greater(a: &[u8], b: usize) -> bool {
// a is LE, so reverse and get the largest bytes
let c = a.iter()
.enumerate()
.rev()
.filter_map(|(i, b)| ( if *b != 0 { return Some((i, *b)); } else { None }))
.collect::<Vec<(usize, u8)>>();
for (i, be) in c {
let k = (b >> (i * 8)) & 255;
println!("{}, {}", be, k);
return be as usize > k
}
false
}
EDIT: Should have clarified, the byte array can be any integer, unsigned integer or float. Simply any integer bincode::serialize can serialize.
I also had in mind to avoid converting the byte array, comparison is supposed to be used on 100000 of byte arrays, so I assume bit operations is the preferred way.
No need for all those extra steps. The basic problem is to know if the integer encoded in the byte-array is little endian, big endian or native endian. Knowing that, you can use usize::from_??_bytes to convert a fixed-size array to an integer; use the TryFrom-trait to get the fixed-size array from the slice.
fn is_greater(b: &[u8], v: usize) -> Result<bool, std::array::TryFromSliceError> {
use std::convert::TryFrom;
Ok(usize::from_le_bytes(<[u8; 8]>::try_from(b)?) > v)
}
This function will return an error if the byte-slice is smaller than 8 bytes, in which case there is no way to construct a usize; you can also convert to u32 or even u16, upcast that to usize and then do the comparison. Also notice that this example uses from_le_bytes, assuming the bytes-slice contains an integer encoded as little endian.

Resources