use crate::{Result, RpcError};
#[derive(Default)]
pub struct NdrEncoder {
buf: Vec<u8>,
next_referent: u32,
}
impl NdrEncoder {
pub fn new() -> Self {
NdrEncoder {
buf: Vec::new(),
next_referent: 0x0002_0000,
}
}
pub fn align(&mut self, a: usize) {
while self.buf.len() % a != 0 {
self.buf.push(0);
}
}
pub fn u8(&mut self, v: u8) {
self.buf.push(v);
}
pub fn u16(&mut self, v: u16) {
self.align(2);
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn u32(&mut self, v: u32) {
self.align(4);
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn u64(&mut self, v: u64) {
self.align(8);
self.buf.extend_from_slice(&v.to_le_bytes());
}
pub fn bytes(&mut self, b: &[u8]) {
self.buf.extend_from_slice(b);
}
pub fn uuid(&mut self, b: &[u8; 16]) {
self.align(4);
self.buf.extend_from_slice(b);
}
pub fn referent(&mut self) -> u32 {
let id = self.next_referent;
self.next_referent += 4;
self.u32(id);
id
}
pub fn null_ptr(&mut self) {
self.u32(0);
}
pub fn conformant_varying_wstr(&mut self, s: &str) {
let mut units: Vec<u16> = s.encode_utf16().collect();
units.push(0);
let n = units.len() as u32;
self.u32(n); self.u32(0); self.u32(n); for u in units {
self.u16(u);
}
}
pub fn into_bytes(self) -> Vec<u8> {
self.buf
}
pub fn len(&self) -> usize {
self.buf.len()
}
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
}
pub struct NdrDecoder<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> NdrDecoder<'a> {
pub fn new(buf: &'a [u8]) -> Self {
NdrDecoder { buf, pos: 0 }
}
pub fn align(&mut self, a: usize) {
while self.pos % a != 0 {
self.pos += 1;
}
}
fn take(&mut self, n: usize) -> Result<&'a [u8]> {
let end = self.pos + n;
if end > self.buf.len() {
return Err(RpcError::Underrun {
need: n,
pos: self.pos,
});
}
let s = &self.buf[self.pos..end];
self.pos = end;
Ok(s)
}
pub fn u8(&mut self) -> Result<u8> {
Ok(self.take(1)?[0])
}
pub fn u16(&mut self) -> Result<u16> {
self.align(2);
Ok(u16::from_le_bytes(self.take(2)?.try_into().unwrap()))
}
pub fn u32(&mut self) -> Result<u32> {
self.align(4);
Ok(u32::from_le_bytes(self.take(4)?.try_into().unwrap()))
}
pub fn u64(&mut self) -> Result<u64> {
self.align(8);
Ok(u64::from_le_bytes(self.take(8)?.try_into().unwrap()))
}
pub fn uuid(&mut self) -> Result<[u8; 16]> {
self.align(4);
Ok(self.take(16)?.try_into().unwrap())
}
pub fn conformant_varying_wstr(&mut self) -> Result<String> {
let _max = self.u32()?;
let _offset = self.u32()?;
let actual = self.u32()? as usize;
let mut units = Vec::with_capacity(actual);
for _ in 0..actual {
units.push(self.u16()?);
}
while units.last() == Some(&0) {
units.pop();
}
Ok(String::from_utf16_lossy(&units))
}
pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
self.take(n)
}
pub fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
pub fn position(&self) -> usize {
self.pos
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn primitive_alignment() {
let mut e = NdrEncoder::new();
e.u8(0x01);
e.u32(0xAABB_CCDD); assert_eq!(e.into_bytes(), vec![0x01, 0, 0, 0, 0xDD, 0xCC, 0xBB, 0xAA]);
}
#[test]
fn wstr_roundtrip() {
let mut e = NdrEncoder::new();
e.conformant_varying_wstr("ADHAMMER");
let bytes = e.into_bytes();
assert_eq!(bytes.len(), 30);
let mut d = NdrDecoder::new(&bytes);
assert_eq!(d.conformant_varying_wstr().unwrap(), "ADHAMMER");
assert_eq!(d.remaining(), 0);
}
#[test]
fn referent_ids_are_nonzero_and_advance() {
let mut e = NdrEncoder::new();
let a = e.referent();
let b = e.referent();
assert_ne!(a, 0);
assert_eq!(b, a + 4);
}
}