How I can convert vector of hex digits into a u64? - rust

I have byte string like this ["80", "1c", "07", "53", "1b", "fc", "c7", "01"] and I would like to convert it to long little endian, result should be something like this 128348308690640000
How would I best go about doing that?

You'll need to combine from_str_radix and from_le_bytes. Quick example (with anyhow for error handling, you'll need to think about what you want to do if your input slice doesn't have 8 strings, or if one of them is not a hex digit.):
use anyhow::Context;
fn hex_bytes_to_u64(input: &[&str]) -> anyhow::Result<u64> {
anyhow::ensure!(input.len() == 8, "expected 8 hex bytes");
let bytes = input
.iter()
.map(|s| u8::from_str_radix(s, 16))
.collect::<Result<Vec<_>, _>>()
.context("Hex byte parse failure")?;
Ok(u64::from_le_bytes(bytes.try_into().expect("u64 doesn't have 8 bytes?")))
}
Playground

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).

Slicing string with Nordic letters in rust

What I am trying to do is to slice a string that has Nordic letters but it throws this error:
'byte index 1 is not a char boundary; it is inside 'å' (bytes 0..2) of å'
fn main() {
let str = "äåö".to_string();
println!("{}", &str[1..]);
}
fn main() {
let str = "äåö".to_string();
let slice_position = str.char_indices().nth(1).unwrap().0;
println!("{}", &str[slice_position..]);
}
åö
The problem here is that str's indexing is in bytes, but it is UTF-8 encoded and ä takes more than one byte in UTF-8. So slicing at 1 actually cuts off half a character, which is a runtime error in Rust.
The reason str behaves this way is because you can't actually determine the position of the n-th character without iterating over the entire string. UTF-8 has variable-length characters, meaning, the position of a character depends on the previous characters.

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.

Extract 7 bits signed integer from u8 byte

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.

Parsing a char to u32

As the questions states, how do I achieve this?
If I have a code like this:
let a = "29";
for c in a.chars() {
println!("{}", c as u32);
}
What I obtain is the unicode codepoints for 2 and 9:
50
57
What I want is to parse those characters into the actual numbers.
char::to_digit(radix) does that. radix denotes the "base", i.e. 10 for the decimal system, 16 for hex, etc.:
let a = "29";
for c in a.chars() {
println!("{:?}", c.to_digit(10));
}
It returns an Option, so you need to unwrap() it, or better: expect("that's no number!"). You can read more about proper error handling in the appropriate chapter of the Rust book.
Well, you can always use the following hacky solution:
fn main() {
let a = "29";
for c in a.chars() {
println!("{}", c as u32 - 48);
}
}
ASCII digits are encoded with values from 48 to 57, so when you have a string containing characters 2 and 9 and attempt to interpret them as integers, you get 50 and 57. To get their expected values you just need to subtract 48 from them.

Resources