I have code which interracts with C via bindings. In it I have this:
let a1: *const i8 = get_data();
let size1 = get_size_of_data();
let a2 = ::std::slice::from_raw_parts(a2, size1);
I need to write data to which a1 points. Or a2 as a slice. The size or length of the data is known.
I tried:
let mut f1 = File::create("my_file.dat").unwrap();
// ??? f1.write_all(a2).expect("unable to write binary data to file");
// ??? f1.write_all(a2.as_bytes).expect("unable to write binary data to file");
and nothing has compiled.
How to do it?
You need pass a &[u8] to write_all, but you have a &[i8]. So, the only change you need is to cast your *const i8 to a *const u8 in slice::from_raw_parts
let a1: *const i8 = std::ptr::null(); // replace with `get_data()`
let size1 = 42; // replace `get_size_of_data()`
let a2: &[u8] = unsafe { std::slice::from_raw_parts(a1 as *const u8, size1) };
use std::io::Write;
let mut f1 = std::fs::File::create("my_file.dat").unwrap();
f1.write_all(a2).expect("write must succeed");
Related
i want to convert u32 into ASCII bytes.
input: 1u32
output [49]
This was my try, but its empty with 0u32 and also using Vec, i would prefer ArrayVec but how do i know the size of the number. Is there any simple way to do this , without using any dynamic allocations?
let mut num = 1u32;
let base = 10u32;
let mut a: Vec<char> = Vec::new();
while num != 0 {
let chars = char::from_digit(num % base,10u32).unwrap();
a.push(chars);
num /= base;
}
let mut vec_of_u8s: Vec<u8> = a.iter().map(|c| *c as u8).collect();
vec_of_u8s.reverse();
println!("{:?}",vec_of_u8s)
Use the write! macro and ArrayVec with the capacity set to 10 (the maximum digits of a u32):
use std::io::Write;
use arrayvec::ArrayVec; // 0.7.2
fn main() {
let input = 1u32;
let mut buffer = ArrayVec::<u8, 10>::new();
write!(buffer, "{}", input).unwrap();
dbg!(buffer);
}
[src/main.rs:10] buffer = [
49,
]
I'm trying to format an u64 to a &str, but without dynamically allocating any memory on heap. I want to manually declare a space on stack (e.g., let mut buffer = [0u8; 20] and print the u64 to buffer and get a &str from it with some unsafe.
I tryied write!(&mut buffer[..], "{}", i), but it returns a Result<()> and I couldn't get the length of the formatted string so as to unsafely convert it to &str.
I'm currently straightly coping the implementation of Display for u64 from std library, is there a better way of doing so?
You could use a Cursor:
use std::io::{Write, Cursor};
fn main() {
let mut cursor = Cursor::new([0u8; 20]);
let i = 42u64;
write!(cursor, "{i}").unwrap();
let pos = cursor.position();
let buffer = cursor.into_inner();
let text = std::str::from_utf8(&buffer[..pos as usize]).unwrap();
println!("{text}");
}
How to add or multiply two i16 numbers in Rust into a larger i32 without overflowing?
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i32 = a + b; // throws EO308 "Expected `u32`, found `u16`
There are no integral promotions or implicit casts in Rust, so you must do all type conversions manually.
For casts, you can use <value> as <type>. However, if the destination type is a superset of the original one and converting it cannot lose information (such as your case), you can self-document that by using <type>::from:
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i32 = i32::from(a) + i32::from(b);
assert_eq!(c, 65534);
This particular case cannot overflow, but for other cases you can prevent overflows using {integer}::checked_*() functions:
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: Option<i16> = a.checked_add(b);
assert_eq!(c, None); //overflow!
Note that overflowing an integer operation panics by default in debug builds.
If what you want is wrapping arithmetic, just like in old C, you can use {integer}::wraping_*(), with the additional bonus that it works for signed as well as unsigned values.
let a: i16 = i16::max_value();
let b: i16 = i16::max_value();
let c: i16 = a.wrapping_add(b);
assert_eq!(c, -2);
You can cast them both to i32 before addition.
let c: i32 = (a as i32) + (b as i32);
I'm reading a binary file into a Rust program using a Vec<u8> as a buffer. Two bytes in the stream represent a big-endian u16.
So far, the only way I've figured out how to convert to a primitive u16 involves converting the two elements to Strings first, and it looks terrible.
Code:
let vector: Vec<u8> = [1, 16].to_vec();
let vector0: String = format!("{:02x}", vector[0]);
let vector1: String = format!("{:02x}", vector[1]);
let mut vector_combined = String::new();
vector_combined = vector_combined + &vector0.clone();
vector_combined = vector_combined + &vector1.clone();
let number: u16 = u16::from_str_radix(&vector_combined.to_string(), 16).unwrap();
println!("vector[0]: 0x{:02x}", vector[0]);
println!("vector[1]: 0x{:02x}", vector[1]);
println!("number: 0x{:04x}", number);
Output:
vector[0]: 0x01
vector[1]: 0x10
number: 0x0110
If you actually had two distinct u8s, the conventional solution involves bitwise manipulation, specifically shifting and bitwise OR. This requires zero heap allocation and is very efficient:
let number = ((vector[0] as u16) << 8) | vector[1] as u16;
And a graphical explanation:
A0 B0
+--------+ +--------+
|XXXXXXXX| |YYYYYYYY|
+-------++ +-------++
| |
A1 = A0 as u16 | B1 = B0 as u16 |
+---------------v+ +---------------v+
|00000000XXXXXXXX| |00000000YYYYYYYY|
+---------------++ +---------------++
| |
A2 = A1 << 8 | |
+---------------v+ |
|XXXXXXXX00000000| |
+---------------++ |
| +--+ |
+-------------->OR<--+
+-++
|
V = A2 | B1 |
+----------+----v+
|XXXXXXXXYYYYYYYY|
+----------------+
However, you are really looking at your problem too narrowly. You don't have two u8, you have a &[u8].
In this case, use the byteorder crate:
use byteorder::{ByteOrder, LittleEndian}; // 1.3.4
fn main() {
let data = [1, 16];
let v = LittleEndian::read_u16(&data);
println!("{}", v);
}
This shows its power when you want to handle reading through the buffer:
use byteorder::{BigEndian, LittleEndian, ReadBytesExt}; // 1.3.4
fn main() {
let data = [1, 16, 1, 2];
let mut current = &data[..];
let v1 = current.read_u16::<LittleEndian>();
let v2 = current.read_u16::<BigEndian>();
println!("{:?}, {:?}", v1, v2); // Ok(4097), Ok(258)
}
As you can see, you need to be conscious of the endianness of your input data.
You could also get a fixed-size array from your slice and then use u16::from_le_bytes. If you had a &[u8] and wanted to get a Vec<u16>, you can iterate over appropriately-sized slices using chunks_exact (or array_chunks).
See also:
How do you set, clear and toggle a single bit in Rust?
Temporarily transmute [u8] to [u16]
How do I convert a Vec<T> to a Vec<U> without copying the vector?
Free code review on your original post:
There's no need to use to_vec here, use vec! instead.
There's no need to specify the vast majority of the types.
let vector = [1u8, 16].to_vec();
let vector0 = format!("{:02x}", vector[0]);
let vector1 = format!("{:02x}", vector[1]);
let mut vector_combined = String::new();
vector_combined = vector_combined + &vector0.clone();
vector_combined = vector_combined + &vector1.clone();
let number = u16::from_str_radix(&vector_combined.to_string(), 16).unwrap();
There's no need to clone the strings before taking a reference to them when adding.
There's no need to convert the String to... another String in from_str_radix.
let vector0 = format!("{:02x}", vector[0]);
let vector1 = format!("{:02x}", vector[1]);
let mut vector_combined = String::new();
vector_combined = vector_combined + &vector0;
vector_combined = vector_combined + &vector1;
let number = u16::from_str_radix(&vector_combined, 16).unwrap();
There's no need to create an empty String to append to, just use vector0
let vector0 = format!("{:02x}", vector[0]);
let vector1 = format!("{:02x}", vector[1]);
let vector_combined = vector0 + &vector1;
let number = u16::from_str_radix(&vector_combined, 16).unwrap();
There's no need to create two strings at all, one will do:
let vector_combined = format!("{:02x}{:02x}", vector[0], vector[1]);
let number = u16::from_str_radix(&vector_combined, 16).unwrap();
Of course, this still isn't the right solution, but it's better.
You can multiply the first element to move it to the higher byte, then add the second element. It just needs extra casting:
let a: u8 = 1;
let b: u8 = 2;
let c: u16 = (a as u16 * 256) + b as u16;
println!("c: {}", c); // c: 258
To be able to use C library, I need to give a *mut c_char parameter to a function. But I don't find a way to have it from a str.
I converted my str to a CString, that's ok, but there's no more way from CString to get a *mut c_char in the nightly build. I found that in 0.12.0 there was a method, but now, what is the process to get that *mut c_char?
let bytes = String::from_str("Test").into_bytes() + b"\0";
let cchars = bytes.map_in_place(|b| b as c_char);
let name: *mut c_char = cchars.as_mut_ptr();
The basic idea is the same as yours but there is no need to slice the Vec explicitly; also a zero byte is appended to the buffer. See also my comment to the question.
Five years later and none of these solutions are working (for me, at least).
A slightly modified version of this answer:
let string: &str = "Hello, world!";
let bytes: Vec<u8> = String::from(string).into_bytes();
let mut c_chars: Vec<i8> = bytes.iter().map(| c | *c as i8).collect::<Vec<i8>>();
c_chars.push(0); // null terminator
let ptr: *mut c_char = c_chars.as_mut_ptr();
Look at this piece of documentation, the fn as_mut_ptr() has been moved to the slice part of the API. So you need a mutable slice of type &mut [c_char]. And AFAIK you cannot get that from a CString, those would be read-only.
Instead you can use a mut Vec<c_char>:
let mut x : Vec<c_char> = ...;
let slice = x.as_mut_slice();
let ptr = slice.as_mut_ptr();
Now (2022), into_raw() works well for me, for example:
use std::ffi::CString;
use std::os::raw::c_char;
fn main() {
let c_string = CString::new("Hello!").expect("CString::new failed");
let raw: *mut c_char = c_string.into_raw();
}
Check https://doc.rust-lang.org/std/ffi/struct.CString.html#method.into_raw for more information.
Thanks to rodrigo's post, I found a way (very dirty, but it works) to solve my problem. Here is the code :
let mut string = std::string::String::from_str("Test");
let bytes = string.into_bytes();
let mut cchar : Vec<c_char> = bytes.map_in_place(|w| w as c_char);
let slice = cchar.as_mut_slice();
let name: *mut c_char = slice.as_mut_ptr();
A bit complex in my opinion
If you want to
let mut hello = b"Hello World\0".to_vec().as_mut_ptr() as *mut i8;