How to convert a &str or &[u8] like "0x124" to its numerical representation in no_std rust? - string

I have this function created in rust that will take an input like "0x152" and return its integer value. For example, "123" would be 123 as u32. This works in a std environment, but after testing on a MCU, I realized there are functional differences between chars in std and no_std rust, and the comparisions and casting I am doing do not work. How can I perform the same function??
pub fn bytes_to_number(s: &str) -> Result<u32, &'static str> {
let mut result: u32 = 0;
// Check if the input is hex or decimal
let mut chars = s.chars();
if let Some(c) = chars.next() {
if c != '0' || chars.next() != Some('x') {
if '0' <= c && c <= '9' {
result += c as u32 - '0' as u32;
for c in chars {
let digit= match c {
'0'..='9' => c as u32 - '0' as u32,
_ => return Err("Invalid decimal character"),
};
if result >= 429_496_720 {
return Err("Integer number too large!")
}
result = result * 10 + digit;
}
return Ok(result)
}
return Err("Not a hex or decimal string")
}
}
if chars.clone().count() > 8 {
return Err("Integer number too large!")
}
for c in chars {
let digit = match c {
'0'..='9' => c as u32 - '0' as u32,
'a'..='f' => c as u32 - 'a' as u32 + 10,
'A'..='F' => c as u32 - 'A' as u32 + 10,
_ => return Err("Invalid hex character"),
};
result = result * 16 + digit;
}
Ok(result)
}
Input: 45, returns: Err(Invalid decimal character)
Input: 3, returns: Err(Not a hex or decimal string)

Your function could best be written like so:
use core::num::ParseIntError;
use core::str::FromStr;
pub fn bytes_to_number(s: &str) -> Result<u32, ParseIntError> {
if let Some(s) = s.strip_prefix("0x") {
u32::from_str_radix(s, 16)
} else {
u32::from_str(s)
}
}
This only uses functions available in a #![no_std] environment. To use it with &[u8] as well, you would first use core::str::from_utf8 to convert it to a &str.

Related

Convert a vector of u8 bytes into a rust_decimal

I am loading data from another language. Numbers can be very large and they are serialized as a byte array of u8s.
These are loaded into rust as a vec of u8s:
vec![1, 0, 0]
This represents 100. I also have a u32 to represent the cale.
I'm trying to load this into a rust_decimal, but am stuck.
measure_value.value -> a vec of u8
measure_value.scale -> a u32
let r_dec = rust_Decimal::????
This is the implementation I have so far, but it feels inelegant!
pub fn proto_to_decimal(input: &DecimalValueProto) -> Result<Decimal, String> {
let mut num = 0;
let mut power: i32 = (input.value.len() - 1)
.try_into()
.map_err(|_| "Failed to convert proto to decimal")?; //casting down from usize to i32 is failable
for digit in input.value.iter() {
let expansion: i128 = if power == 0 { expansion = *digit as i128 } else { expansion = (*digit as i128) * 10_i128.pow(power as u32) as i128 }
num += expansion;
power -= 1;
}
Ok(Decimal::from_i128_with_scale(num as i128, input.scale))
}

How to format to other number bases besides decimal, hexadecimal? [duplicate]

Currently I'm using the following code to return a number as a binary (base 2), octal (base 8), or hexadecimal (base 16) string.
fn convert(inp: u32, out: u32, numb: &String) -> Result<String, String> {
match isize::from_str_radix(numb, inp) {
Ok(a) => match out {
2 => Ok(format!("{:b}", a)),
8 => Ok(format!("{:o}", a)),
16 => Ok(format!("{:x}", a)),
10 => Ok(format!("{}", a)),
0 | 1 => Err(format!("No base lower than 2!")),
_ => Err(format!("printing in this base is not supported")),
},
Err(e) => Err(format!(
"Could not convert {} to a number in base {}.\n{:?}\n",
numb, inp, e
)),
}
}
Now I want to replace the inner match statement so I can return the number as an arbitrarily based string (e.g. base 3.) Are there any built-in functions to convert a number into any given radix, similar to JavaScript's Number.toString() method?
For now, you cannot do it using the standard library, but you can:
use my crate radix_fmt
or roll your own implementation:
fn format_radix(mut x: u32, radix: u32) -> String {
let mut result = vec![];
loop {
let m = x % radix;
x = x / radix;
// will panic if you use a bad radix (< 2 or > 36).
result.push(std::char::from_digit(m, radix).unwrap());
if x == 0 {
break;
}
}
result.into_iter().rev().collect()
}
fn main() {
assert_eq!(format_radix(1234, 10), "1234");
assert_eq!(format_radix(1000, 10), "1000");
assert_eq!(format_radix(0, 10), "0");
}
If you wanted to eke out a little more performance, you can create a struct and implement Display or Debug for it. This avoids allocating a String. For maximum over-engineering, you can also have a stack-allocated array instead of the Vec.
Here is Boiethios' answer with these changes applied:
struct Radix {
x: i32,
radix: u32,
}
impl Radix {
fn new(x: i32, radix: u32) -> Result<Self, &'static str> {
if radix < 2 || radix > 36 {
Err("Unnsupported radix")
} else {
Ok(Self { x, radix })
}
}
}
use std::fmt;
impl fmt::Display for Radix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut x = self.x;
// Good for binary formatting of `u128`s
let mut result = ['\0'; 128];
let mut used = 0;
let negative = x < 0;
if negative {
x*=-1;
}
let mut x = x as u32;
loop {
let m = x % self.radix;
x /= self.radix;
result[used] = std::char::from_digit(m, self.radix).unwrap();
used += 1;
if x == 0 {
break;
}
}
if negative {
write!(f, "-")?;
}
for c in result[..used].iter().rev() {
write!(f, "{}", c)?;
}
Ok(())
}
}
fn main() {
assert_eq!(Radix::new(1234, 10).to_string(), "1234");
assert_eq!(Radix::new(1000, 10).to_string(), "1000");
assert_eq!(Radix::new(0, 10).to_string(), "0");
}
This could still be optimized by:
creating an ASCII array instead of a char array
not zero-initializing the array
Since these avenues require unsafe or an external crate like arraybuf, I have not included them. You can see sample code in internal implementation details of the standard library.
Here is an extended solution based on the first comment which does not bind the parameter x to be a u32:
fn format_radix(mut x: u128, radix: u32) -> String {
let mut result = vec![];
loop {
let m = x % radix as u128;
x = x / radix as u128;
// will panic if you use a bad radix (< 2 or > 36).
result.push(std::char::from_digit(m as u32, radix).unwrap());
if x == 0 {
break;
}
}
result.into_iter().rev().collect()
}
This is faster than the other answer:
use std::char::from_digit;
fn encode(mut n: u32, r: u32) -> Option<String> {
let mut s = String::new();
loop {
if let Some(c) = from_digit(n % r, r) {
s.insert(0, c)
} else {
return None
}
n /= r;
if n == 0 {
break
}
}
Some(s)
}
Note I also tried these, but they were slower:
https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.push_front
https://doc.rust-lang.org/std/string/struct.String.html#method.push
https://doc.rust-lang.org/std/vec/struct.Vec.html#method.insert

How can I convert from Vec<char> to u32 in Rust without going through String?

My rust code runs in an environment where I have no access to std::string and std::* (but I have access to core::str). How can I convert a Vec<char> to u32 without going through String, such as:
let num_in_chars: Vec<char> = vec!['1', '2'];
// some process here
// let num = ...
// This is how I could do it if I have access to `String`
// let num = num_in_chars.iter().collect::<String>().parse::<u32>().unwrap();
assert_eq!(12, num);
Thanks
You must convert each char to a digit (in the map) and then you multiply each previous result by 10 and you add the new digit:
/// Returns `None` in case of invalid digit.
pub fn vec_to_int(digits: impl IntoIterator<Item = char>) -> Option<u32> {
const RADIX: u32 = 10;
digits
.into_iter()
.map(|c| c.to_digit(RADIX))
.try_fold(0, |ans, i| i.map(|i| ans * RADIX + i))
}
#[test]
fn it_works() {
let nums = vec!['1', '2'];
let num = vec_to_int(nums);
assert_eq!(Some(12), num);
}
#[test]
fn invalid_digit() {
let nums = vec!['1', 'a'];
let num = vec_to_int(nums);
assert_eq!(None, num);
}

How can I write a Rust function to find different characters between two strings?

The order of the characters is not important but the count is. I mean aaabaaa equals to 6a + b and the function is like math subtraction. For example:
fn diff(a: String, b: String) -> String {}
diff("aabbac", "accba") => "ab"
---------------------------------
"aabbac" = (3a+2b+c)
"accba" = (2a+b+2c)
(3a+2b+c) - (2a+b+2c) = a+b // -c is ignored
The usual technique is to create a function that counts the number of occurrences of each char, like collections.Counter in Python, and to compare these numbers for strings a and b.
The Rust standard library documentation contains a snippet that does the job. This is an adaptation that accepts any iterator:
use std::collections::HashMap;
use std::hash::Hash;
use std::iter::Iterator;
fn counter<T, I>(it: I) -> HashMap<T, usize>
where
T: Eq + Hash,
I: Iterator<Item = T>,
{
let mut count_by_element = HashMap::new();
for e in it {
*count_by_element.entry(e).or_insert(0) += 1;
}
count_by_element
}
Now that we know how to build a map char -> count, we just have to compare the counts of the string a and b:
use std::iter;
fn diff(a: &str, b: &str) -> String {
let mut v: Vec<char> = vec![];
let counter_a = counter(a.chars());
let counter_b = counter(b.chars());
for (c, n_a) in &counter_a {
let n_b = counter_b.get(c).unwrap_or(&0); // how many `c` in `b`?
if n_a > n_b {
v.extend(iter::repeat(c).take(n_a - n_b)); // add `n_a - n_b` `c`s
}
}
v.into_iter().collect::<String>() // build the String
}
If you want a "one shot" function, you can forget the counter function and use a more direct approach:
fn diff_one_shot(a: &str, b: &str) -> String {
let mut counter = HashMap::new();
for c in a.chars() {
*counter.entry(c).or_insert(0) += 1; // one more
}
for c in b.chars() {
*counter.entry(c).or_insert(0) -= 1; // one less
}
counter
.iter()
.filter(|(_c, &n)| n > 0) // only if more `c` in `a` than in `b`
.flat_map(|(c, &n)| iter::repeat(c).take(n)) // `n` times `c`
.collect::<String>()
}
Examples:
fn main() {
println!("{:?}", counter("aaabbc".chars()));
// {'b': 2, 'c': 1, 'a': 3}
println!("{}", diff("aaabbc", "ab"));
//aabc
println!("{}", diff_one_shot("aaabbc", "ab"));
//aacb
}

Format/convert a number to a string in any base (including bases other than decimal or hexadecimal)

Currently I'm using the following code to return a number as a binary (base 2), octal (base 8), or hexadecimal (base 16) string.
fn convert(inp: u32, out: u32, numb: &String) -> Result<String, String> {
match isize::from_str_radix(numb, inp) {
Ok(a) => match out {
2 => Ok(format!("{:b}", a)),
8 => Ok(format!("{:o}", a)),
16 => Ok(format!("{:x}", a)),
10 => Ok(format!("{}", a)),
0 | 1 => Err(format!("No base lower than 2!")),
_ => Err(format!("printing in this base is not supported")),
},
Err(e) => Err(format!(
"Could not convert {} to a number in base {}.\n{:?}\n",
numb, inp, e
)),
}
}
Now I want to replace the inner match statement so I can return the number as an arbitrarily based string (e.g. base 3.) Are there any built-in functions to convert a number into any given radix, similar to JavaScript's Number.toString() method?
For now, you cannot do it using the standard library, but you can:
use my crate radix_fmt
or roll your own implementation:
fn format_radix(mut x: u32, radix: u32) -> String {
let mut result = vec![];
loop {
let m = x % radix;
x = x / radix;
// will panic if you use a bad radix (< 2 or > 36).
result.push(std::char::from_digit(m, radix).unwrap());
if x == 0 {
break;
}
}
result.into_iter().rev().collect()
}
fn main() {
assert_eq!(format_radix(1234, 10), "1234");
assert_eq!(format_radix(1000, 10), "1000");
assert_eq!(format_radix(0, 10), "0");
}
If you wanted to eke out a little more performance, you can create a struct and implement Display or Debug for it. This avoids allocating a String. For maximum over-engineering, you can also have a stack-allocated array instead of the Vec.
Here is Boiethios' answer with these changes applied:
struct Radix {
x: i32,
radix: u32,
}
impl Radix {
fn new(x: i32, radix: u32) -> Result<Self, &'static str> {
if radix < 2 || radix > 36 {
Err("Unnsupported radix")
} else {
Ok(Self { x, radix })
}
}
}
use std::fmt;
impl fmt::Display for Radix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut x = self.x;
// Good for binary formatting of `u128`s
let mut result = ['\0'; 128];
let mut used = 0;
let negative = x < 0;
if negative {
x*=-1;
}
let mut x = x as u32;
loop {
let m = x % self.radix;
x /= self.radix;
result[used] = std::char::from_digit(m, self.radix).unwrap();
used += 1;
if x == 0 {
break;
}
}
if negative {
write!(f, "-")?;
}
for c in result[..used].iter().rev() {
write!(f, "{}", c)?;
}
Ok(())
}
}
fn main() {
assert_eq!(Radix::new(1234, 10).to_string(), "1234");
assert_eq!(Radix::new(1000, 10).to_string(), "1000");
assert_eq!(Radix::new(0, 10).to_string(), "0");
}
This could still be optimized by:
creating an ASCII array instead of a char array
not zero-initializing the array
Since these avenues require unsafe or an external crate like arraybuf, I have not included them. You can see sample code in internal implementation details of the standard library.
Here is an extended solution based on the first comment which does not bind the parameter x to be a u32:
fn format_radix(mut x: u128, radix: u32) -> String {
let mut result = vec![];
loop {
let m = x % radix as u128;
x = x / radix as u128;
// will panic if you use a bad radix (< 2 or > 36).
result.push(std::char::from_digit(m as u32, radix).unwrap());
if x == 0 {
break;
}
}
result.into_iter().rev().collect()
}
This is faster than the other answer:
use std::char::from_digit;
fn encode(mut n: u32, r: u32) -> Option<String> {
let mut s = String::new();
loop {
if let Some(c) = from_digit(n % r, r) {
s.insert(0, c)
} else {
return None
}
n /= r;
if n == 0 {
break
}
}
Some(s)
}
Note I also tried these, but they were slower:
https://doc.rust-lang.org/std/collections/struct.VecDeque.html#method.push_front
https://doc.rust-lang.org/std/string/struct.String.html#method.push
https://doc.rust-lang.org/std/vec/struct.Vec.html#method.insert

Resources