如何从没有old_io的字节读取/写入整数值?

时间:2015-03-23 09:13:12

标签: serialization rust

Reader模块中有方便的特征Writerstd::old_io来使用各种endiannes读取/写入整数值。但该模块被宣布为过时,所以我试图找出其他方法来做到这一点。

一种方法是读取字节并使用位运算构造结果值。标准库中还有其他方法吗?例如。从u64中读取&[u8],以大端编码进行编码。我在C中做的是将uint8_t数组中的8个字节记忆到uint64_t值,然后执行htons之类的操作,以便在必要时交换字节。

2 个答案:

答案 0 :(得分:13)

将整数值转换为数组/切片非常容易,可以用来写入文件流,就像上面提到的使用位运算一样。但是,我想在这里发帖,以便人们了解使用位方法(如下所述和已经提到的原始海报)实际上至少优化了X86_64上的单个指令。这与原始海报谈论的memcpy操作完全相同。

例如,看看这段代码:

#[inline]
fn u16tou8ale(v: u16) -> [u8; 2] {
    [
        v as u8,
        (v >> 8) as u8,
    ]
}

// little endian
#[inline]
fn u32tou8ale(v: u32) -> [u8; 4] {
    [
        v as u8,
        (v >> 8) as u8,
        (v >> 16) as u8,
        (v >> 24) as u8,
    ]
}

// big endian
#[inline]
fn u32tou8abe(v: u32) -> [u8; 4] {
    [
        (v >> 24) as u8,
        (v >> 16) as u8,
        (v >> 8) as u8,
        v as u8,
    ]
}

fn main() {
    println!("{:?}", u32tou8ale(0x12345678));
    println!("{:?}", u32tou8abe(0x12345678));
}

例如,函数u32tou8ale实际上变为CPU执行的单个指令。该单个指令在堆栈上创建[u8; 4]数组,即使是big-endian版本u32tou8abe也是创建[u8; 4]的单个指令。由于优化器,这是可能的。你可能会说这是因为它是一个恒定的编译时间值,但是如果你进行实验,你会发现当给出一个编译器无法提前知道的u32值时,它仍会在一个堆栈中生成一个数组指令主要是通过执行内存复制操作。例如:

fn main() {
    unsafe {
        let p: *const u32 = std::mem::transmute(main);
        println!("{:?}", u32tou8ale(*p));
    }
}

这将从符号main引用的内存位置读取值,这是我们的函数。编译器无法知道此值,因此它会发出一个将值读入堆栈的移动指令,然后它将该值视为[u8; 4]

至于可移植性,只需简单地总是明确你读取的字节顺序并写入值,一切都会好起来的。例如,如果你使用u32tou8ale,那么无论你的目标是什么架构,你都会获得很少的字节顺序,如果你编写了等效的读取函数并且你明确地读作大字节顺序,那么你可以确定你会读到排序

我希望这可以帮助任何来这里寻找将整数转换成字节的人来自!

答案 1 :(得分:7)

不,现在无法在标准库中读取/写入特定字节序的数字。假设Rust用户将使用社区库。据我所知,目前用于字节序处理的最发达和最常用的库是byteorder。它提供了扩展特征,使用与std::io::{Read, Write}上定义的方法非常相似的方法扩展std::old_io::{Reader, Writer}