How to convert hex string to a float in Rust? - rust

What's the most straightforward way to convert a hex string into a float? (without using 3rd party crates).
Does Rust provide some equivalent to Python's struct.unpack('!f', bytes.fromhex('41973333'))
See this question for Python & Java, mentioning for reference.

This is quite easy without external crates:
fn main() {
// Hex string to 4-bytes, aka. u32
let bytes = u32::from_str_radix("41973333", 16).unwrap();
// Reinterpret 4-bytes as f32:
let float = unsafe { std::mem::transmute::<u32, f32>(bytes) };
// Print 18.9
println!("{}", float);
}
Playground link.

There's f32::from_bits which performs the transmute in safe code. Note that transmuting is not the same as struct.unpack, since struct.unpack lets you specify endianness and has a well-defined IEEE representation.

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

How do you create a CString from a String in rust?

How does one create a std::ffi::CString from a String in Rust?
Assume the String is already stored in a variable that can be moved if necessary, NOT a literal like it is in many of the examples for constructing a CString.
I have studied the docs for both CString:
https://doc.rust-lang.org/std/ffi/struct.CString.html
and String:
https://doc.rust-lang.org/std/string/struct.String.html
and I still don't see the path. You must have to go through one of the many pointer types; Into and From aren't implemented for these types, so .into() doesn't work.
String implements Into<Vec<u8>> already:
use std::ffi::CString;
fn main() {
let ss = "Hello world".to_string();
let s = CString::new(ss).unwrap();
println!("{:?}", s);
}
Playground

Is there a better way to test if a char is whitespace than converting it to a string?

I have a function that checks if a char is whitespace:
fn is_any_whitespace(ch: &char) -> bool {
let re = Regex::new(r"\s").unwrap();
let slice = &ch.to_string()[..];
re.is_match(slice)
}
What bothers me is the part where I convert char to &str (&str is the type of the parameter that is_match accepts):
&ch.to_string()[..]
Can it be done more elegantly and efficiently?
Instead of using regex for this problem, you can use char::is_whitespace. It should have the same behavior and will likely be more efficient, depending on compiler optimizations. In any case, not depending on the regex crate is a gain.
The only real difference is that char::is_whitespace takes self rather than &self. If you're worried about that, don't be. Using &char over char has no gain since char implements Copy. In fact, since chars are 4 bytes, it'll typically be more efficient to pass the characters directly, rather than an 8 byte pointer. And that's not even taking into account the extra layer of indirection.
If you insist on using the approach you have, it can be simplified to
use regex::Regex;
fn is_any_whitespace(ch: &char) -> bool {
let re = Regex::new(r"\s").unwrap();
re.is_match(&ch.to_string())
}
(playground)

How to convert a very large decimal string to hexadecimal?

let hex = "100000000000000000".as_bytes().to_hex();
// hex == "313030303030303030303030303030303030"
println!("{:x}", 100000000000000000000000u64);
// literal out of range for u64
How can I got that value?
In Python, I would just call hex(100000000000000000000000) and I get '0x152d02c7e14af6800000'.
to_hex() comes from the hex crate.
One needs to be aware of the range of representable values for different numeric types in Rust. In this particular case, the value exceeds the limits of an u64, but the u128 type accommodates the value. The following code outputs the same value as the example in Python:
fn main() {
let my_string = "100000000000000000000000".to_string(); // `parse()` works with `&str` and `String`!
let my_int = my_string.parse::<u128>().unwrap();
let my_hex = format!("{:X}", my_int);
println!("{}", my_hex);
}
Checked with the Rust Playground:
152D02C7E14AF6800000
An explicit usage of arbitrary precision arithmetic is required in the general case. A few suggestions from What's the best crate for arbitrary precision arithmetic in Rust? on Reddit:
num_bigint works on stable and does not have unsafe code.
ramp uses unsafe and does not work on stable Rust, but it is faster.
rust-gmp and rug bind to the state-of-the-art bigint implementation in C (GMP). They are the fastest and have the most features. You probably want to use one of those.

Convert a Vec<u16> or Vec<WCHAR> to a &str

I'm getting into Rust programming to realize a small program and I'm a little bit lost in string conversions.
In my program, I have a vector as follows:
let mut name: Vec<winnt::WCHAR> = Vec::new();
WCHAR is the same as a u16 on my Windows machine.
I hand over the Vec<u16> to a C function (as a pointer) which fills it with data. I then need to convert the string contained in the vector into a &str. However, no matter, what I try, I can not manage to get this conversion working.
The only thing I managed to get working is to convert it to a WideString:
widestr = unsafe { WideCString::from_ptr_str(name.as_ptr()) };
But this seems to be a step into the wrong direction.
What is the best way to convert the Vec<u16> to an &str under the assumption that the vector holds a valid and null-terminated string.
I then need to convert the string contained in the vector into a &str. However, no matter, what I try, I can not manage to get this conversion working.
There's no way of making this a "free" conversion.
A &str is a Unicode string encoded with UTF-8. This is a byte-oriented encoding. If you have UTF-16 (or the different but common UCS-2 encoding), there's no way to read one as the other. That's equivalent to trying to read a JPEG image as a PDF. Both chunks of data might be a string, but the encoding is important.
The first question is "do you really need to do that?". Many times, you can take data from one function and shovel it back into another function, never looking at it. If you can get away with that, that might be be best answer.
If you do need to transform it, then you have to deal with the errors that can occur. An arbitrary array of 16-bit integers may not be valid UTF-16 or UCS-2. These encodings have edge cases that can easily produce invalid strings. Null-termination is another aspect - Unicode actually allows for embedded NUL characters, so a null-terminated string can't hold all possible Unicode characters!
Once you've ensured that the encoding is valid 1 and figured out how many entries in the input vector comprise the string, then you have to decode the input format and re-encode to the output format. This is likely to require some kind of new allocation, so you are most likely to end up with a String, which can then be used most anywhere a &str can be used.
There is a built-in method to convert UTF-16 data to a String: String::from_utf16. Note that it returns a Result to allow for these error cases. There's also String::from_utf16_lossy, which replaces invalid encoded parts with the Unicode replacement character.
let name = [0x68, 0x65, 0x6c, 0x6c, 0x6f];
let a = String::from_utf16(&name);
let b = String::from_utf16_lossy(&name);
println!("{:?}", a);
println!("{:?}", b);
If you are starting from a pointer to a u16 or WCHAR, you will need to convert to a slice first by using slice::from_raw_parts. If you have a null-terminated string, you need to find the NUL yourself and slice the input appropriately.
1: This is actually a great way of using types; a &str is guaranteed to be UTF-8 encoded, so no further check needs to be made. Similarly, the WideCString is likely to perform a check once upon construction and then can skip the check on later uses.
This is my simple hack for this case. There must be a bug; fix for your own case:
let mut v = vec![0u16; MAX_PATH as usize];
// imaginary win32 function
win32_function(v.as_mut_ptr());
let mut path = String::new();
for val in v.iter() {
let c: u8 = (*val & 0xFF) as u8;
if c == 0 {
break;
} else {
path.push(c as char);
}
}

Resources