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
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}");
}
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
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;
}
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.
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.