Compile time verified byte writer for slice and vector.
[dependencies]
const-writer = "0.1.0"
use const_writer::ConstWrite;
fn main() {
let mut vec = vec![];
let writer = vec.const_writer::<10>() // reserve 10 bytes in vec
.write_u32_le(12) // no runtime checks
.write_u32_le(34); // no runtime checks
assert_eq!(writer.remaining(), 2);
assert_eq!(vec.len(), 8);
assert_eq!(&vec[0..8], &[12, 0, 0, 0, 34, 0, 0, 0]);
}
use const_writer::ConstWrite;
fn main() {
let mut buff = [0u8; 1024];
buff.as_mut().const_writer::<10>()
.write_slice(&[1, 2, 3, 4, 5, 6])
.write_u64_le(111); // compile error.
}
use const_writer::{ConstWriterAdapter, ConstWriter, ConstWrite};
fn main() {
// write 10 bytes
fn write_struct<T: ConstWriterAdapter>(writer: ConstWriter<T, 10>) {
writer
.write_u16_le(34)
.write_u16_le(2)
.write_u16_le(3)
.write_u16_le(4)
.write_u16_le(5);
}
let mut buff = [0u8; 16];
write_struct(buff.as_mut().const_writer());
assert_eq!(buff, [34, 0, 2, 0, 3, 0, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0]);
}
It generates super optimal assembly with pretty rust syntax
// we return modified reference so compiler won't optimise away reference manipulation
pub fn write(mut ref_buff: &mut [u8]) -> &mut [u8] {
use crate::ConstWrite;
ref_buff.const_writer::<31>()
.write_u8_le(0x01)
.write_u16_le(0x0203)
.write_u32_le(0x04050607)
.write_u64_le(0x08090A0B0C0D0E0F)
.write_u128_le(0x101112131415161718191A1B1C1D1E1F);
ref_buff
}
Assembly consists only one comparison
write:
subq $88, %rsp
movq %rsi, %rdx
cmpq $30, %rsi
jbe .LBB2_1 # panic
movq %rdi, %rax
movb $1, (%rdi)
movw $515, 1(%rdi) # imm = 0x203
movl $67438087, 3(%rdi) # imm = 0x4050607
movabsq $579005069656919567, %rcx # imm = 0x8090A0B0C0D0E0F
movq %rcx, 7(%rdi)
movabsq $1157726452361532951, %rcx # imm = 0x1011121314151617
movq %rcx, 23(%rdi)
movabsq $1736447835066146335, %rcx # imm = 0x18191A1B1C1D1E1F
movq %rcx, 15(%rdi)
addq $31, %rax
addq $-31, %rdx
addq $88, %rsp
retq
And sometimes rustc can generate really pretty assembly 😍
pub fn write_struct<T: ConstWriterAdapter>(writer: ConstWriter<T, 12>) -> ConstWriter<T, 0> {
writer.write_u32_le(34).write_u16_le(2).write_u16_le(3).write_u16_le(4).write_u16_le(5)
}
write_struct:
movq %rdi, %rax
movl $34, (%rsi)
movabsq $1407392063619074, %rcx # imm = 0x5000400030002 <- OMG
movq %rcx, 4(%rsi)
leaq 12(%rsi), %rdx
retq
- Support
no_std