Строки base64 float массив ↔


Мне нужно конвертировать f32 массивы с фиксированной длины в base64 представление и обратно.

Мой текущий код выглядит так. Он работает, но он чувствует себя слишком сложным.

Как я могу улучшить его?

main.rs:

extern crate base64;

fn read_float_vec_from_byte_vec(v: Vec<u8>) -> Result<Vec<f32>, String> {
    match v.len() % 4 {
        0 => {
            use std::ptr;
            let p = v.as_ptr();
            let dest_len = v.len() / 4;
            let mut result = Vec::<f32>::with_capacity(dest_len);
            for i in 0..dest_len as isize {
                unsafe {
                    let f_ptr = p.offset(4 * i) as *const f32;
                    result.push(ptr::read::<f32>(f_ptr));
                }
            }
            Ok(result)
        }
        _ => Err("Number of bytes not divisible by 4.".to_string())
    }
}

fn float_vec_as_byte_vec(values: &Vec<f32>) -> Vec<u8> {
    let dest_len = values.len() * 4;
    let mut result = Vec::<u8>::with_capacity(dest_len);
    result.resize(dest_len, 0);
    for (i, x) in values.iter().enumerate() {
        let bytes: [u8; 4] = unsafe { std::mem::transmute(*x) };
        result[i*4..i*4+4].copy_from_slice(&bytes[..]);
    }
    result
}

fn show_base64_decode_error(e: base64::DecodeError) -> String {
    use std::error::Error;
    format!("{:?}", e.description())
}

fn base64_to_f32_16_array(base64_str: &str) -> Result<[f32; 16], String>
{
    base64::decode(base64_str
        ).map_err(show_base64_decode_error
        ).and_then(read_float_vec_from_byte_vec
        ).map(
        |float_vec| {
            let mut result: [f32; 16] = [0.0; 16];
            result.copy_from_slice(&float_vec[..]);
            result
        }
    )
}

fn f32_16_array_to_base64(vals: &[f32; 16]) -> String
{
    base64::encode(&float_vec_as_byte_vec(&vals.to_vec()))
}

#[cfg(test)]
mod tests {
    static VALUES: [f32; 16] = [-0.35, -0.97, 0.05, 0.49, 1.35, 1.08, -0.71, 0.27, -0.44, 0.22, -1.34, -1.17, 0.68, 0.02, -0.52, 0.83];
    static BASE64_STR: &str = "MzOzvuxReL/NzEw9SOH6Ps3MrD9xPYo/j8I1v3E9ij6uR+G+rkdhPh+Fq7+PwpW/exQuPwrXozy4HgW/4XpUPw==";
    #[test]
    fn base64_to_f32_16_array_test() {
        assert_eq!(super::base64_to_f32_16_array(&BASE64_STR), Ok(VALUES));
        assert_eq!(
            super::base64_to_f32_16_array("foo"),
            Err("Number of bytes not divisible by 4.".to_string()));
    }
    #[test]
    fn f32_16_array_to_base64_test() {
        assert_eq!(super::f32_16_array_to_base64(&VALUES), BASE64_STR);
    }
}

cargo.toml (для полноты):

[package]
name = "base64_f32_test"
version = "0.1.0"

[dependencies]
base64 = "0.9.0"


Комментарии
1 ответ

Вы должны использовать итераторы: когда вы используете строку for i in 0..dest_len as isizeкомпилятор не может оптимизировать при прохождении среза.

Лучше пример:

fn read_float_vec_from_byte_vec(v: Vec<u8>) -> Result<Vec<f32>, &'static str> {
#[inline(always)]
fn float_from_u8_slice(s: &[u8]) -> f32 {
let array = [s[0], s[1], s[2], s[3]];
unsafe { std::mem::transmute(array) }
}

match v.len() % 4 {
0 => Ok(v.chunks(4).map(float_from_u8_slice).collect()),
_ => Err("Number not divisible by 4"),
}
}

С итерации v.chunks(4).map(float_from_u8_slice).collect()код меньше и более читабельным. Вообще говоря, итераторы всегда лучше, чем индексация, потому что операция индексирования выполняет проверку границ.

К сожалению, вы не можете сделать то же самое с float_vec_as_byte_vec потому что (сейчас) массивы в Русте не первого сорта. Из-за того, что генерики на целые числа не существует, вы не можете потреблять массив в итератор. Если это так, мы могли бы написать что-то вроде:

// This code does not work currently
fn float_vec_as_byte_vec(s: &[f32]) -> Vec<u8> {
s.iter().flat_map(|&f| unsafe { transmute::<f32, [u8; 4]>(f)).collect()
}

В этом коде, я принимаю тип &[f32]вместо &Vec<f32>. Слайсы-это вид памяти, и это то, что мы хотим. &Vec<f32> на самом деле не имеет смысла, семантически.

В первой функции я переписал, я использовал &'static str и не String потому что нам не нужны выделенную строку, поскольку она является буквальным.

2
ответ дан 8 февраля 2018 в 09:02 Источник Поделиться