dav*_*mac 5 metaprogramming rust
我有一个 C++ 程序,我在其中使用模板元编程来生成要“通过网络”发送的小型二进制格式数据包,与分配固定大小缓冲区和复制各种数据的更幼稚的方法相比,它提供了更好的安全性和清晰度使用手工计算的偏移量将项目放入其中。
int foo(int fd, long long data1, float f1)
{
auto m = membuf()
.append<char>(0xAA).append<char>(0xBB) // header
.append(data1)
.append(f1)
.append(555); // in the pipe (arbitary extra data)
return write(fd, m.data(), m.size());
}
Run Code Online (Sandbox Code Playgroud)
这将发送一个由两个字节0xAA和组成的数据包0xBB,(例如)来自 的 8 个字节、来自 的data14 个字节f1和形成 的 4 个字节555。(等的实际大小int当然取决于编译器/架构细节,但我也可以使用例如uint64_t类型进行精确控制)。
(注意: 的完整实现membuf与问题无关,但如果感兴趣,您可以在此处查看:https : //godbolt.org/z/sr0Cuu)
本案的重要特征是:
碰巧的是,这被编译成一个非常有效的指令序列,它只是在堆栈上分配缓冲区并将每个值写入其中的正确位置:
foo(int, long long, float):
subq $40, %rsp
movl $-17494, %eax
movl $18, %edx
movq %rsi, 2(%rsp)
movq %rsp, %rsi
movw %ax, (%rsp)
movl $555, 14(%rsp)
movd %xmm0, 10(%rsp)
call write
addq $40, %rsp
ret
Run Code Online (Sandbox Code Playgroud)
我正在寻找的是一个 Rust 解决方案来实现同样的目标。我不介意 Rust 编译器当前是否不能生成与上述一样高效的代码,但满足上述要求很重要:没有堆分配,没有包大小或数据偏移的动态计算,没有使用实验性/“不稳定”语言功能。
我一直在阅读Rust 书并试图了解我是否以及如何在 Rust 中做到这一点,但到目前为止我一无所获:
membuf示例所做的那种事情。本质上:我想要一个由缓冲区大小参数化的泛型类型,它可以接受一个值并返回一个更大的、固定大小的缓冲区,并在末尾附加数据。但也许该规范过于以 C++ 为中心,而且 Rust 还可以采用另一种方法——我只需要弄清楚它是什么!
用实际代码稍微放大我的评论:restruct-crate 可以做你要求的事情;然而,到目前为止,它确实需要每晚一次,因为打包和解包是const功能,但还不稳定。
给定您的示例,在添加restruct和restruct_derive到依赖项后:
#![feature(const_int_conversion)]
#![feature(const_fn)]
#![feature(const_slice_len)]
#![feature(const_transmute)]
/// A packer/unpacker for two unsigned bytes, a group of eight unsigned bytes, a group of
/// four unsigned bytes and four padding bytes; all in little endian.
#[derive(restruct_derive::Struct)]
#[fmt="< 2B 8s 4s 4x"]
struct Foo;
fn main() {
let data = (0xAA, 0xBB, [1,2,3,4,5,6,7,8], [4,3,2,1]);
println!("The buffer has a size of {}", Foo::SIZE);
let buf: [u8; Foo::SIZE] = Foo::pack(data);
println!("Packed as {:?}", buf);
let unpacked: <Foo as restruct::Struct>::Unpacked = Foo::unpack(buf);
println!("Unpacked as {:?}", unpacked);
}
Run Code Online (Sandbox Code Playgroud)