如何从没有 old_io 的字节中 read/write 整数值?
How to read/write integer values from bytes without old_io?
std::old_io
模块中有 Reader
和 Writer
方便的特性 read/write 使用各种字节序的整数值。但是那个模块被声明为过时的,所以我试图找出其他方法来做到这一点。
一种方法是读取字节并用位运算构造结果值。标准库中还有其他方法吗?例如。从 &[u8]
中读取 u64
,它以大端编码进行编码。我会在 C 中做的是将 uint8_t
数组中的 8 个字节 memcpy 到 uint64_t
值,然后执行类似 htons
的操作以在必要时交换字节。
不,现在标准库中无法read/write特定字节顺序的数字。据推测,Rust 用户将为此使用社区库。据我所知,目前开发和使用最多的字节序处理库是byteorder。它提供扩展特性,扩展 std::io::{Read, Write}
的方法与 std::old_io::{Reader, Writer}
.
上定义的方法非常相似
很容易将一个整数值转换成一个array/slice,它可以用来写入文件流,就像你上面说的使用位运算一样。但是,我想在这里 post 以便人们理解使用位方法(就像我在下面做的和原来的 poster 已经提到的)实际上优化了 X86_64 上的单个指令至少。这和原poster说的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]
数组,即使大端版本 u32tou8abe
也是创建 [u8; 4]
的一条指令。这是可能的,因为优化器。你可能会说这是因为它是一个常量编译时间值,但如果你试验你会发现,当给定一个编译器无法提前知道的 u32 值时,它仍然会在堆栈中一次性生成数组本质上是通过执行内存复制操作来执行指令。例如:
fn main() {
unsafe {
let p: *const u32 = std::mem::transmute(main);
println!("{:?}", u32tou8ale(*p));
}
}
这从符号 main
引用的内存位置读取一个值,这是我们的函数。编译器不知道这个值,因此它发出一条移动指令,将该值读入堆栈,然后它认为该值是 [u8; 4]
.
至于可移植性,只需始终明确说明您读取和写入值的字节顺序,一切都会正常进行。例如,如果你使用 u32tou8ale
那么无论你的目标是什么架构,你都会得到小字节顺序,并且如果你编写了等效的读取函数并且你明确地读取为大字节顺序那么你可以确定你会读取那个订购。
我希望这能帮助任何来这里寻找整数和字节之间转换的人!
std::old_io
模块中有 Reader
和 Writer
方便的特性 read/write 使用各种字节序的整数值。但是那个模块被声明为过时的,所以我试图找出其他方法来做到这一点。
一种方法是读取字节并用位运算构造结果值。标准库中还有其他方法吗?例如。从 &[u8]
中读取 u64
,它以大端编码进行编码。我会在 C 中做的是将 uint8_t
数组中的 8 个字节 memcpy 到 uint64_t
值,然后执行类似 htons
的操作以在必要时交换字节。
不,现在标准库中无法read/write特定字节顺序的数字。据推测,Rust 用户将为此使用社区库。据我所知,目前开发和使用最多的字节序处理库是byteorder。它提供扩展特性,扩展 std::io::{Read, Write}
的方法与 std::old_io::{Reader, Writer}
.
很容易将一个整数值转换成一个array/slice,它可以用来写入文件流,就像你上面说的使用位运算一样。但是,我想在这里 post 以便人们理解使用位方法(就像我在下面做的和原来的 poster 已经提到的)实际上优化了 X86_64 上的单个指令至少。这和原poster说的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]
数组,即使大端版本 u32tou8abe
也是创建 [u8; 4]
的一条指令。这是可能的,因为优化器。你可能会说这是因为它是一个常量编译时间值,但如果你试验你会发现,当给定一个编译器无法提前知道的 u32 值时,它仍然会在堆栈中一次性生成数组本质上是通过执行内存复制操作来执行指令。例如:
fn main() {
unsafe {
let p: *const u32 = std::mem::transmute(main);
println!("{:?}", u32tou8ale(*p));
}
}
这从符号 main
引用的内存位置读取一个值,这是我们的函数。编译器不知道这个值,因此它发出一条移动指令,将该值读入堆栈,然后它认为该值是 [u8; 4]
.
至于可移植性,只需始终明确说明您读取和写入值的字节顺序,一切都会正常进行。例如,如果你使用 u32tou8ale
那么无论你的目标是什么架构,你都会得到小字节顺序,并且如果你编写了等效的读取函数并且你明确地读取为大字节顺序那么你可以确定你会读取那个订购。
我希望这能帮助任何来这里寻找整数和字节之间转换的人!