How do I convert two u8 primitives into a u16 primitive? - rust

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

Related

u32 to ASCII Bytes without String Rust

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,
]

Error trying to slice bytes expected `usize`, found `u32`

Im trying to slice into a buffer but am getting an error.
let mut buf = Vec::new();
let mut dst = [0u8; 4];
let read_index = 0;
dst.clone_from_slice(&buf[read_index..(read_index+3)]);
let length = u32::from_be_bytes(dst);
&buf[(read_index+4)..(read_index+length)]
error[E0308]: mismatched types
--> src/main.rs:28:79
|
28 | let trade = root_as_fb_iq_feed_trade(&buf[(read_index+4)..(read_index+length)]);
| ^^^^^^ expected `usize`, found `u32`
error[E0277]: cannot add `u32` to `usize`
--> src/main.rs:28:78
|
28 | let trade = root_as_fb_iq_feed_trade(&buf[(read_index+4)..(read_index+length)]);
| ^ no implementation for `usize + u32`
|
= help: the trait `Add<u32>` is not implemented for `usize`
let mut buf = Vec::new();
let read_index = 0;
dst.clone_from_slice(&buf[read_index..(read_index+3)]);
let length = usize::from_be_bytes(dst); // u32 -> usize
&buf[(read_index+4)..(read_index+length)]
usize supports the from_be_bytes method: The Rust doc of size::from_be_bytes
The error info from the compiler is clear enough to make you write the code that works. There is also specification on the doc I mentioned above, the function from_be_bytes only accepts the param as [u8; 8], so there is one more modification:
let mut buf = Vec::new();
let mut dst = [0u8; 8]; // Expand your buffer to the size of 8
let read_index = 0;
dst.clone_from_slice(&buf[read_index..(read_index+3)]);
let length = usize::from_be_bytes(dst);
&buf[(read_index+4)..(read_index+length)]
let length = u32::from_be_bytes(dst);
let length_us = usize::try_from(length).unwrap();

How to write a slice into a file?

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

How do I concatenate two slices in Rust?

I want to take the x first and last elements from a vector and concatenate them. I have the following code:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
vec![v.iter().take(3), v.iter().skip(l-3)];
}
This gives me the error
error[E0308]: mismatched types
--> <anon>:4:28
|
4 | vec![v.iter().take(3), v.iter().skip(l-3)];
| ^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Take`, found struct `std::iter::Skip`
<anon>:4:5: 4:48 note: in this expansion of vec! (defined in <std macros>)
|
= note: expected type `std::iter::Take<std::slice::Iter<'_, u64>>`
= note: found type `std::iter::Skip<std::slice::Iter<'_, u64>>`
How do I get my vec of 1, 2, 3, 8, 9, 10? I am using Rust 1.12.
Just use .concat() on a slice of slices:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
let first_and_last = [&v[..3], &v[l - 3..]].concat();
println!("{:?}", first_and_last);
// The output is `[0, 1, 2, 7, 8, 9]`
}
This creates a new vector, and it works with arbitrary number of slices.
(Playground link)
Ok, first of all, your initial sequence definition is wrong. You say you want 1, 2, 3, 8, 9, 10 as output, so it should look like:
let v = (1u64 .. 11).collect::<Vec<_>>();
Next, you say you want to concatenate slices, so let's actually use slices:
let head = &v[..3];
let tail = &v[l-3..];
At this point, it's really down to which approach you like the most. You can turn those slices into iterators, chain, then collect...
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
...or make a vec and extend it with the slices directly...
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
...or extend using more general iterators (which will become equivalent in the future with specialisation, but I don't believe it's as efficient just yet)...
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
...or you could use Vec::with_capacity and push in a loop, or do the chained iterator thing, but using extend... but I have to stop at some point.
Full example code:
fn main() {
let v = (1u64 .. 11).collect::<Vec<_>>();
let l = v.len();
let head = &v[..3];
let tail = &v[l-3..];
println!("head: {:?}", head);
println!("tail: {:?}", tail);
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
println!("v2: {:?}", v2);
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
println!("v3: {:?}", v3);
// Explicit type to help inference.
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
println!("v4: {:?}", v4);
}
You should collect() the results of the take() and extend() them with the collect()ed results of skip():
let mut p1 = v.iter().take(3).collect::<Vec<_>>();
let p2 = v.iter().skip(l-3);
p1.extend(p2);
println!("{:?}", p1);
Edit: as Neikos said, you don't even need to collect the result of skip(), since extend() accepts arguments implementing IntoIterator (which Skip does, as it is an Iterator).
Edit 2: your numbers are a bit off, though; in order to get 1, 2, 3, 8, 9, 10 you should declare v as follows:
let v = (1u64 .. 11).collect::<Vec<_>>();
Since the Range is left-closed and right-open.

Convert int to a vector of strings

I am trying to convert long numbers to a string vector. For example, 17562 would become ["1", "7", "5", "6", "2"]. I have seen a lot of examples of converting ints to strings, but no ints to string vectors. I want to iterate over each digit individually.
Here is what I have so far, but it isn't working.
fn main() {
let x = 42;
let values: Vec<&str> = x.to_string().split(|c: char| c.is_alphabetic()).collect();
println!("{:?}", values);
}
Gives me the compiler error of :
<anon>:3:29: 3:42 error: borrowed value does not live long enough
<anon>:3 let values: Vec<&str> = x.to_string().split(|c: char| c.is_alphabetic()).collect();
<anon>:3:88: 6:2 note: reference must be valid for the block suffix following statement 1 at 3:87...
<anon>:3 let values: Vec<&str> = x.to_string().split(|c: char| c.is_alphabetic()).collect();
<anon>:4 println!("{:?}", values);
<anon>:5
<anon>:6 }
<anon>:3:5: 3:88 note: ...but borrowed value is only valid for the statement at 3:4
<anon>:3 let values: Vec<&str> = x.to_string().split(|c: char| c.is_alphabetic()).collect();
<anon>:3:5: 3:88 help: consider using a `let` binding to increase its lifetime
<anon>:3 let values: Vec<&str> = x.to_string().split(|c: char| c.is_alphabetic()).collect();
The equivalent of what I am trying to do in python would be x = 42; x = list(str(x)); print(x)
Ok, the first problem is that you don't store the result of x.to_string() anywhere. As such, it will cease to exist at the end of the expression, meaning that values will be trying to reference a value that no longer exists. Hence the error. The simplest solution is to just store the temporary string somewhere so that it continues to exist:
fn main() {
let x = 42;
let x_str = x.to_string();
let values: Vec<&str> = x_str.split(|c: char| c.is_alphabetic()).collect();
println!("{:?}", values);
}
Second problem: this outputs ["42"] because you told it to split on letters. You probably meant to use is_numeric:
fn main() {
let x = 42;
let x_str = x.to_string();
let values: Vec<&str> = x_str.split(|c: char| c.is_numeric()).collect();
println!("{:?}", values);
}
Third problem: this outputs ["", "", ""], because those are the three strings between numeric characters. Split's argument is the separator. Thus, the third problem is that you're using entirely the wrong method to begin with.
The closest direct equivalent to the Python code you listed would be:
fn main() {
let x = 42;
let values: Vec<String> = x.to_string().chars().map(|c| c.to_string()).collect();
println!("{:?}", values);
}
At last, it outputs: ["4", "2"].
But, this is horribly inefficient: this takes the integer, allocates an intermediate buffer, prints the integer to it, turns it into a string. It takes each code point in that string, allocates an intermediate buffer, prints the code point to it, turns it into a string. Then it collects all these strings into a Vec, possibly reallocating more than once.
It works, but is a bit wasteful. If you don't care about waste, you can stop reading now.
You can make things a bit less wasteful by collecting code points instead of strings:
fn main() {
let x = 42;
let values: Vec<char> = x.to_string().chars().collect();
println!("{:?}", values);
}
This outputs: ['4', '2']. Note the different quotes because we're using char instead of String.
We can remove the intermediate allocations from Vec resizing by pre-allocating its storage, which gives us this version:
fn main() {
let x = 42u32; // no negatives!
let values = {
if x == 0 {
vec!['0']
} else {
// pre-allocate Vec so there's no resizing
let digits = 1 + (x as f64).log10() as u32;
let mut cs = Vec::with_capacity(digits as usize);
let mut div = 10u32.pow(digits - 1);
while div > 0 {
cs.push((b'0' + ((x / div) % 10) as u8) as char);
div /= 10;
}
cs
}
};
println!("{:?}", values);
}
Unless you're doing this in a loop, I'd just stick to the correct, wasteful version.
If you are looking for a performant version, I'd just use this
fn digits(mut val: u64) -> Vec<u8> {
// An unsigned 64-bit number can have 20 digits
let mut result = Vec::with_capacity(20);
loop {
let digit = val % 10;
val = val / 10;
result.push(digit as u8);
if val == 0 { break }
}
result.reverse();
result
}
fn main() {
println!("{:?}", digits(0));
println!("{:?}", digits(1));
println!("{:?}", digits(9));
println!("{:?}", digits(10));
println!("{:?}", digits(11));
println!("{:?}", digits(1234567890));
println!("{:?}", digits(0xFFFFFFFFFFFFFFFF));
}
This may over allocate by a few bytes, but 20 bytes total is small unless you are doing this a whole bunch. It also leaves each value as a number, which you can convert to a string as needed.
What about:
let ss = value.to_string()
.chars()
.map(|c| c.to_string())
.collect::<Vec<_>>();
Demo
Not the greatest perf but reads well.

Resources