use bytes::{BufMut, Bytes, BytesMut};
#[derive(Debug, Default)]
pub struct Writer {
buf: BytesMut,
}
impl Writer {
#[must_use]
pub fn new() -> Self {
Self {
buf: BytesMut::new(),
}
}
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
buf: BytesMut::with_capacity(capacity),
}
}
#[must_use]
pub fn len(&self) -> usize {
self.buf.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
pub fn write_u8(&mut self, val: u8) {
self.buf.put_u8(val);
}
pub fn write_u16(&mut self, val: u16) {
self.buf.put_u16(val);
}
pub fn write_u32(&mut self, val: u32) {
self.buf.put_u32(val);
}
pub fn write_slice(&mut self, data: &[u8]) {
self.buf.put_slice(data);
}
pub fn patch_u16(&mut self, offset: usize, val: u16) {
let bytes = val.to_be_bytes();
self.buf[offset] = bytes[0];
self.buf[offset + 1] = bytes[1];
}
#[must_use]
pub fn finish(self) -> Bytes {
self.buf.freeze()
}
#[must_use]
pub fn finish_mut(self) -> BytesMut {
self.buf
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn write_u8_single_byte() {
let mut w = Writer::new();
w.write_u8(0xAB);
assert_eq!(&w.finish()[..], &[0xAB]);
}
#[test]
fn write_u16_big_endian() {
let mut w = Writer::new();
w.write_u16(0x0102);
let b = w.finish();
assert_eq!(&b[..], &[0x01, 0x02]);
}
#[test]
fn write_u16_byte_order_explicit() {
let mut w = Writer::new();
w.write_u16(0xFF00);
let b = w.finish();
assert_eq!(b[0], 0xFF);
assert_eq!(b[1], 0x00);
}
#[test]
fn write_u32_big_endian() {
let mut w = Writer::new();
w.write_u32(0x01020304);
let b = w.finish();
assert_eq!(&b[..], &[0x01, 0x02, 0x03, 0x04]);
}
#[test]
fn write_slice_appends_verbatim() {
let mut w = Writer::new();
w.write_slice(b"hello");
assert_eq!(&w.finish()[..], b"hello");
}
#[test]
fn round_trip_u8() {
use crate::codec::reader::Reader;
let mut w = Writer::new();
w.write_u8(0x42);
let mut r = Reader::new(w.finish());
assert_eq!(r.read_u8().unwrap(), 0x42);
}
#[test]
fn round_trip_u16() {
use crate::codec::reader::Reader;
let mut w = Writer::new();
w.write_u16(0xDEAD);
let mut r = Reader::new(w.finish());
assert_eq!(r.read_u16().unwrap(), 0xDEAD);
}
#[test]
fn round_trip_u32() {
use crate::codec::reader::Reader;
let mut w = Writer::new();
w.write_u32(0xDEADBEEF);
let mut r = Reader::new(w.finish());
assert_eq!(r.read_u32().unwrap(), 0xDEADBEEF);
}
#[test]
fn round_trip_slice() {
use crate::codec::reader::Reader;
let payload = b"dns-payload";
let mut w = Writer::new();
w.write_slice(payload);
let mut r = Reader::new(w.finish());
let out = r.read_slice(payload.len()).unwrap();
assert_eq!(&out[..], payload);
}
#[test]
fn multi_field_wire_layout() {
let mut w = Writer::new();
w.write_u8(0x01);
w.write_u16(0x0203);
w.write_u32(0x04050607);
let b = w.finish();
assert_eq!(&b[..], &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07]);
}
#[test]
fn len_tracks_writes() {
let mut w = Writer::new();
assert!(w.is_empty());
assert_eq!(w.len(), 0);
w.write_u8(0);
assert_eq!(w.len(), 1);
w.write_u16(0);
assert_eq!(w.len(), 3);
w.write_u32(0);
assert_eq!(w.len(), 7);
w.write_slice(b"abc");
assert_eq!(w.len(), 10);
}
#[test]
fn patch_u16_backfills_length() {
let mut w = Writer::new();
w.write_u8(0xAA);
let len_offset = w.len(); w.write_u16(0x0000);
w.write_slice(b"payload");
let payload_len = (w.len() - len_offset - 2) as u16;
w.patch_u16(len_offset, payload_len);
let b = w.finish();
assert_eq!(b[0], 0xAA);
assert_eq!(b[1], 0x00);
assert_eq!(b[2], 0x07);
assert_eq!(&b[3..], b"payload");
}
#[test]
fn finish_mut_returns_bytes_mut() {
let mut w = Writer::new();
w.write_u16(0x1234);
let bm = w.finish_mut();
assert_eq!(&bm[..], &[0x12, 0x34]);
}
#[test]
fn with_capacity_works() {
let mut w = Writer::with_capacity(512);
w.write_u8(0xFF);
assert_eq!(w.len(), 1);
}
}