Rust bitwise operations - rust

I am currently trying to convert a hexadecimal value in rust (as a u16) to a RGB value stored as separate variables, all of type u8. The way I decided to do this was to use bitwise operators to get the separate values from the hex with this code:
Color::RGB(((hex >> (16u8)) & 0xFF) as u8, ((hex >> (8u8)) & 0xFF) as u8, ((hex) & 0xFF) as u8)
which resulted in an error:
error: this arithmetic operation will overflow
--> src/style.rs:50:21
|
50 | Color::RGB(((hex >> (16u8)) & 0xFF) as u8, ((hex >> (8u8)) & 0xFF) as u8, ((hex) & 0xFF) as u8)
| ^^^^^^^^^^^^^^^ attempt to shift right by `16_u8`, which would overflow
|
= note: `#[deny(arithmetic_overflow)]` on by default
error: aborting due to previous error; 3 warnings emitted
I have solved the issue by using u16's checked_shr(u16), however this makes my code more complex than I thought it would need to be, with:
let mut r: u8 = 0;
let mut g: u8 = 0;
let mut b: u8 = (hex as u8) & 0xFF;
if let Some(h16) = hex.checked_shr(16) {
r = (h16 as u8) & 0xFF;
}
if let Some(h8) = hex.checked_shr(8) {
g = (h8 as u8) & 0xFF;
}
Color::RGB(r, g, b)
I was wondering if there was a better way to solve the error, if I was just making a mistake in my original code, and/or if there is a better way of doing this? I was also trying to avoid turning off the #[deny(arithmetic_overflow)], but that could be a mistake. Thank you for your time!

The problem was solved by changing the hexadecimal value from u16 to u32, Thank you to matt and Mihir who pointed that out!

Related

How to split H256 into u32, u112, u112 in Rust

0x638d0490000000004b7cdeca2fe41a1b6411000000158fb5610df6aa553bfedb of type H256
https://docs.rs/ethers/0.17.0/ethers/types/struct.H256.html#
It is a storage slot on EVM. A single slot is uint256, but there, three different values were packed into one storage slot (thats how EVM works). So uint112 + uint112 + uint32 were packed into uint256 that I need to reverse engineer.
Looking to get:
0x638d049
0x4b7cdeca2fe41a1b6411
0x158fb5610df6aa553bfedb
Then (1, 2, 3) into (u32, u128, u128) - u128 as the closet one to uint112.
Tried a few things with padding but does not seem to be optimal (looping thru).
You could extract them manually using bitshifts like this:
use num::BigUint;
fn main() {
let a = BigUint::parse_bytes(b"638d0490000000004b7cdeca2fe41a1b6411000000158fb5610df6aa553bfedb", 16).unwrap();
println!("0x{a:x}");
let x = a.clone() >> (2*112);
let y = (a.clone() >> 112) & ((BigUint::from(1u8) << 112) - 1u8);
let z = a.clone() & ((BigUint::from(1u8) << 112) - 1u8);
println!("0x{x:x}");
println!("0x{y:x}");
println!("0x{z:x}");
}

How do I split a 16-bit value into two 8-bit values?

I have u16 value and want to convert it into two u8 values to put it into u8 array, the same as described here.
0000000000000011 -> 00000000 and 00000011
How would I do that in a convenient way?
Putting the answers provided by #apilat and #Anton into code:
Bit Shifting - verbose style
This can be accomplished in a couple of different ways. I actually think that bit shifting is a little clearer, as no thought needs to be given to whether I need to worry about endianness.
Spelling out each:
fn shift_verbose_split_u16(short_16: u16) -> [u8; 2] {
let high_byte: u8 = (short_16 >> 8) as u8;
let low_byte: u8 = (short_16 & 0xff) as u8;
return [high_byte, low_byte];
}
Bit Shifting - idiomatic style
The above code can be reduced to a one-line function:
fn shift_idiomatic_split_u16(short_16: u16) -> [u8; 2] {
[(short_16 >> 8) as u8, (short_16 & 0xff) as u8]
}
Using to_be_bytes()
Using to_be_bytes() certainly is the simplest solution. Using this solution, you have to realize that you really want to use the big-endian call, regardless of the underlying cpu's architecture:
let [high, low] = short_16.to_be_bytes();
Grouping the code into one file:
fn main() {
let short_16: u16 = 0x3f23;
// verbose bit shifting
let [high, low] = shift_verbose_split_u16(short_16);
assert_eq!(high, 0x3f);
assert_eq!(low, 0x23);
// idiomatic bit shifting
let [high, low] = shift_idiomatic_split_u16(short_16);
assert_eq!(high, 0x3f);
assert_eq!(low, 0x23);
let [high, low] = short_16.to_be_bytes();
assert_eq!(high, 0x3f);
assert_eq!(low, 0x23);
println!("High: {:#0x?}, Low: {:#0x?}", high, low);
}
fn shift_verbose_split_u16(short_16: u16) -> [u8; 2] {
let high_byte: u8 = (short_16 >> 8) as u8;
let low_byte: u8 = (short_16 & 0xff) as u8;
return [high_byte, low_byte];
}
fn shift_idiomatic_split_u16(short_16: u16) -> [u8; 2] {
[(short_16 >> 8) as u8, (short_16 & 0xff) as u8]
}
Output:
High: 0x3f, Low: 0x23

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 can I convert the lower/upper 8 bits of a u16 to a u8 in Rust?

I want to convert a u16 to two separate u8s. I tried to use some bit masks:
use std::convert::From;
fn main() {
let n1: u8 = 0x41;
let n2: u16 = 0x4157;
println!("Number:{}", char::from(n1));
let b1: u8 = n2 & 0xFF;
let b2: u8 = n2 >> 8;
println!("b1: {}", b1);
println!("b2: {}", b2);
}
error[E0308]: mismatched types
--> src/main.rs:9:18
|
9 | let b1: u8 = n2 & 0xFF;
| ^^^^^^^^^ expected u8, found u16
error[E0308]: mismatched types
--> src/main.rs:10:18
|
10 | let b2: u8 = n2 >> 8;
| ^^^^^^^ expected u8, found u16
This question is not why does the compiler raise a mismatched type error?, rather, it is How can I convert the lower/upper 8 bits of a u16 to a u8 in Rust?. Potentially, there are other ways to do this and this question does not constrain the answer to the as keyword.
Update: As of Rust 1.32.0 there is u16::to_be_bytes, which can be used in favor a custom function.
fn main() {
let bytes = 28923u16.to_be_bytes();
assert_eq!([0x70, 0xFB], bytes);
}
You can use the as keyword to convert a u16 to u8 in a safe way.
fn convert_u16_to_two_u8s_be(integer: u16) -> [u8; 2] {
[(integer >> 8) as u8, integer as u8]
}
If you need more types or different endianness use the byteorder crate.
extern crate byteorder;
use byteorder::{WriteBytesExt, BigEndian};
fn convert_u16_to_two_u8s_be(integer: u16) -> Vec<u8> {
let mut res = vec![];
res.write_u16::<BigEndian>(integer).unwrap();
res
}
You can cast between integer types with as.
let b1 = n2 as u8;
let b2 = (n2 >> 8) as u8;
Note that the masking is unnecessary, because the cast will truncate the upper bits.

In rust how can i do this type conversion in one line

I am trying to use a bit shift but I need the result as an f64. I can't seem to figure how how to shift and let the result as an f64 without making an ugly tmp varible.
let num_bits: uint = 32; // just for reference
// how can these two lines be 1 line
let muli: int = 1<<(num_bits-2);
let mul: f64 = muli as f64;
How can I write the last two line as one line so I don't need muli?
I have tried made various attempts in the theme of:
let m: f64 = 1<<(num_bits-2) as f64;
which gives playpen
<anon>:8:21: 8:40 error: mismatched types: expected `uint`, found `f64` (expected uint, found f64)
<anon>:8 let m: f64 = 1<<(num_bits-2) as f64;
You can do it by annotating the type of the 1 literal. I'm assuming you want it the shift result to be an int (before converting to f64) since you said multi: int. Otherwise, you want 1u.
let m: f64 = (1i << (num_bits - 2)) as f64;
Check the playpen.
If you look at rust reference you can see that as operator has higher precedence than << so you have to do:
fn main () {
let num_bits: uint = 32; // just for reference
let m: f64 = (1u << num_bits - 2) as f64;
println!("mul {}", mul);
}
You also must specify the bype of 1 as uint (1u) because the compiler cannot deretmine the type for it from the context when written that way.

Resources