Crash while trying to do bit shift - rust

I am trying to run this rust code
use std::net::Ipv4Addr;
fn ip_to_int(addr: Ipv4Addr) -> u32 {
let ip = addr.octets();
(ip[0] as u32) << 24 + (ip[1] as u32) << 16 + (ip[2] as u32) << 8 + (ip[3] as u32)
}
fn main() {
let ip = Ipv4Addr::new(74, 125, 227, 0);
println!("{}", ip_to_int(ip));
}
This crashes with
thread '<main>' panicked at 'shift operation overflowed', test.rs:5
I did typecast everything to 32 bit ints and no shift is larger than 32 bits. Why does it crash? Also, isn't the compiler supposed to catch this and prevent compilation?
Abhishek#Abhisheks-MacBook-Pro-2:~$ rustc --version
rustc 1.1.0-nightly (21f278a68 2015-04-23) (built 2015-04-24)

According to the the Rust reference, operator + as a stronger precedence than operator <<, meaning that your expression is actually parsed like this:
(ip[0] as u32) << (24 + (ip[1] as u32)) << (16 + (ip[2] as u32)) << (8 + (ip[3] as u32))
Which can pretty easily overflow.
You need to add the appropriates brackets:
((ip[0] as u32) << 24) + ((ip[1] as u32) << 16) + ((ip[2] as u32) << 8) + (ip[3] as u32)

Related

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

Insert a bit in an 8-bit integer

I have a 5-bit u8, let's say 0b10101. I want to insert three bits, all ones, into positions 1, 2, and 4, to get: ii1i0101, i.e., 11110101. I want to accomplish this in three function calls, meaning that the function should take the index as one of the parameters and insert a single bit in that position.
I've come across this question, however, the answers on that page did not work for me. For example, the answer with the least upvotes panics when implemented, while others do not give the correct result.
fn insert_at(x: u8, index: u8, bit: u8) -> u8 {
let mask = (1 << (8 - index + 1)) - 1;
(x & !mask) | (bit << (8 - index)) | ((x & mask) >> 1)
}
#[cfg(test)]
mod tests {
use super::*;
use rstest::*;
#[rstest(
input, index, expected,
case(0b10101, 1, 0b110101),
)]
fn test_bla(input: u8, index: u8, expected: u8) {
let result = insert_at(input, index, 1);
assert_eq!(result, expected);
}
}
thread 'tests::test_bla::case_1' panicked at 'attempt to shift left with overflow'
I've made a few assumptions (and a modification) to make the semantics of your question a bit more concrete:
The result of the operation must fit in 8 bits; if it does not, no value is returned.
index 0 (rather than index 1) refers to the position to the left of the most significant set bit.
A bit inserted in any index > 0 shifts all more significant set bits to the left by 1.
A working implementation (playground link):
fn insert_at(x: u8, index: u8, bit: u8) -> Option<u8> {
if bit != 0 && bit != 1 {
return None;
}
// most significant set bit, from right
// 0b00010101
// 87654321
let msb = 8 - x.leading_zeros() as u8;
if index >= msb {
// the new bit is past the LSB
return None;
}
// an index of 0 means insert as the new MSB (if it fits).
// insert is the absolute index of the inserted bit.
let insert = msb - index;
if insert == 8 {
// the new bit is out of u8 range.
// 0b_11111111
// ^ trying to insert here
return None;
}
let mask = (1 << insert) - 1;
Some((x & !mask) << 1 | (bit << insert) | (x & mask))
}
The reason your implementation panics is that Rust's left-shift operation is checked: if any bits would be shifted off the left side of the integer, the check fails. The main reasoning for this is that different platforms have different behavior in this case.
Rust also provides arithmetic operations with specified behavior in these cases, such as overflowing_shl() and wrapping_shl().

Rust bitwise operations

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!

Rust overflowing shift left

I'm trying to combine 4 bytes into a u32, and the compiler telling me that the shift has overflown.
This is my code:
pub fn get_instruction(file: &[u8], counter: usize) {
let ins = u32::from(file[counter] << 24)
| u32::from(file[counter + 1] << 16)
| u32::from(file[counter + 2] << 8)
| u32::from(file[counter + 3]);
println!("{:x}", ins);
}
You got your operator priority and cast wrong:
pub fn get_instruction(file: &[u8], counter: usize) {
let ins = u32::from(file[counter]) << 24
| u32::from(file[counter + 1]) << 16
| u32::from(file[counter + 2]) << 8
| u32::from(file[counter + 3]);
println!("{:x}", ins);
}
You were casting after trying to shift a u8 24 bits, which was your problem.
It's not necessary to twiddle the bits yourself – you can use the function u32::from_be_bytes() instead:
pub fn get_instruction(file: &[u8], counter: usize) {
let ins_bytes = <[u8; 4]>::try_from(&file[counter..counter + 4]).unwrap();
let ins = u32::from_be_bytes(ins_bytes);
println!("{:x}", ins);
}

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