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

vbe*_*nar 3 serialization rust

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

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

kmc*_*ire 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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我希望这有助于任何来到这里寻找将整数转换为字节的人!


Vla*_*eev 7

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

  • 谢谢你的回答。尽管对于系统语言来说这是一个奇怪的决定。解析网络协议或文件格式是一项常见任务,标准库应为常见任务提供必要的方法。我希望这个决定将来会被修改。 (2认同)