#![allow(missing_docs)]
use crate::error::{Error, Result};
#[inline]
pub const fn pad4(n: usize) -> usize {
(n + 3) & !3
}
#[derive(Debug, Default, Clone)]
pub struct XdrWriter {
buf: Vec<u8>,
}
impl XdrWriter {
#[inline]
pub fn new() -> Self {
Self {
buf: Vec::with_capacity(64),
}
}
#[inline]
pub fn with_capacity(cap: usize) -> Self {
Self {
buf: Vec::with_capacity(cap),
}
}
#[inline]
pub fn len(&self) -> usize {
self.buf.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buf.is_empty()
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.buf
}
#[inline]
pub fn into_vec(self) -> Vec<u8> {
self.buf
}
#[inline]
pub fn put_i32(&mut self, v: i32) -> &mut Self {
self.buf.extend_from_slice(&v.to_be_bytes());
self
}
#[inline]
pub fn put_u32(&mut self, v: u32) -> &mut Self {
self.buf.extend_from_slice(&v.to_be_bytes());
self
}
#[inline]
pub fn put_i64(&mut self, v: i64) -> &mut Self {
self.buf.extend_from_slice(&v.to_be_bytes());
self
}
#[inline]
pub fn put_f64(&mut self, v: f64) -> &mut Self {
self.buf.extend_from_slice(&v.to_be_bytes());
self
}
#[inline]
pub fn put_raw(&mut self, bytes: &[u8]) -> &mut Self {
self.buf.extend_from_slice(bytes);
self
}
#[inline]
pub fn align(&mut self) -> &mut Self {
while !self.buf.len().is_multiple_of(4) {
self.buf.push(0);
}
self
}
pub fn put_bytes(&mut self, data: &[u8]) -> &mut Self {
self.put_i32(data.len() as i32);
self.buf.extend_from_slice(data);
self.align();
self
}
#[inline]
pub fn put_str(&mut self, s: &str) -> &mut Self {
self.put_bytes(s.as_bytes())
}
}
#[derive(Debug, Clone)]
pub struct XdrReader<'a> {
buf: &'a [u8],
pos: usize,
}
impl<'a> XdrReader<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> Self {
Self { buf, pos: 0 }
}
#[inline]
pub fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
#[inline]
pub fn is_empty(&self) -> bool {
self.remaining() == 0
}
fn need(&self, n: usize) -> Result<()> {
if self.remaining() < n {
return Err(Error::protocol(format!(
"short XDR read: need {n} bytes, have {}",
self.remaining()
)));
}
Ok(())
}
#[inline]
pub fn get_i32(&mut self) -> Result<i32> {
self.need(4)?;
let v = i32::from_be_bytes(self.buf[self.pos..self.pos + 4].try_into().unwrap());
self.pos += 4;
Ok(v)
}
#[inline]
pub fn get_u32(&mut self) -> Result<u32> {
Ok(self.get_i32()? as u32)
}
#[inline]
pub fn get_i64(&mut self) -> Result<i64> {
self.need(8)?;
let v = i64::from_be_bytes(self.buf[self.pos..self.pos + 8].try_into().unwrap());
self.pos += 8;
Ok(v)
}
#[inline]
pub fn get_f64(&mut self) -> Result<f64> {
Ok(f64::from_bits(self.get_i64()? as u64))
}
pub fn get_raw(&mut self, n: usize) -> Result<&'a [u8]> {
self.need(n)?;
let s = &self.buf[self.pos..self.pos + n];
self.pos += n;
Ok(s)
}
#[inline]
pub fn align(&mut self) -> Result<()> {
let pad = pad4(self.pos) - self.pos;
if pad > 0 {
self.need(pad)?;
self.pos += pad;
}
Ok(())
}
pub fn get_bytes(&mut self) -> Result<&'a [u8]> {
let len = self.get_i32()? as usize;
let data = self.get_raw(len)?;
self.align()?;
Ok(data)
}
pub fn get_bytes_owned(&mut self) -> Result<Vec<u8>> {
Ok(self.get_bytes()?.to_vec())
}
}
#[derive(Debug, Clone)]
pub struct ParameterBuffer {
buf: Vec<u8>,
}
impl ParameterBuffer {
pub fn new(version: u8) -> Self {
Self { buf: vec![version] }
}
pub fn raw() -> Self {
Self { buf: Vec::new() }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.buf.len() <= 1
}
pub fn tag(&mut self, tag: u8) -> &mut Self {
self.buf.push(tag);
self
}
pub fn bytes(&mut self, tag: u8, value: &[u8]) -> &mut Self {
debug_assert!(value.len() <= u8::MAX as usize, "clumplet value too long");
self.buf.push(tag);
self.buf.push(value.len() as u8);
self.buf.extend_from_slice(value);
self
}
#[inline]
pub fn string(&mut self, tag: u8, value: &str) -> &mut Self {
self.bytes(tag, value.as_bytes())
}
pub fn int(&mut self, tag: u8, value: i32) -> &mut Self {
let le = value.to_le_bytes();
let mut n = 4;
while n > 1 && le[n - 1] == 0 {
n -= 1;
}
self.bytes(tag, &le[..n])
}
pub fn int_u32(&mut self, tag: u8, value: u32) -> &mut Self {
self.bytes(tag, &value.to_le_bytes())
}
pub fn int_u64(&mut self, tag: u8, value: u64) -> &mut Self {
self.bytes(tag, &value.to_le_bytes())
}
pub fn bytes_be_len4(&mut self, tag: u8, value: &[u8]) -> &mut Self {
self.buf.push(tag);
self.buf
.extend_from_slice(&(value.len() as u32).to_le_bytes());
self.buf.extend_from_slice(value);
self
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
&self.buf
}
#[inline]
pub fn into_vec(self) -> Vec<u8> {
self.buf
}
}
pub fn read_le_int(bytes: &[u8]) -> i64 {
let mut v: i64 = 0;
for (i, &b) in bytes.iter().enumerate().take(8) {
v |= (b as i64) << (8 * i);
}
v
}
pub fn read_le_int_signed(bytes: &[u8]) -> i64 {
let v = read_le_int(bytes);
let width = bytes.len().min(8);
if width == 0 || width == 8 {
return v;
}
let bits = width * 8;
let sign = 1i64 << (bits - 1);
if v & sign != 0 {
v | !((1i64 << bits) - 1) } else {
v
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_ints_and_bytes() {
let mut w = XdrWriter::new();
w.put_i32(-7)
.put_i64(1 << 40)
.put_bytes(b"hello")
.put_str("hi");
let bytes = w.into_vec();
let mut r = XdrReader::new(&bytes);
assert_eq!(r.get_i32().unwrap(), -7);
assert_eq!(r.get_i64().unwrap(), 1 << 40);
assert_eq!(r.get_bytes().unwrap(), b"hello");
assert_eq!(r.get_bytes().unwrap(), b"hi");
assert!(r.is_empty());
}
#[test]
fn put_bytes_is_padded() {
let mut w = XdrWriter::new();
w.put_bytes(b"abc"); assert_eq!(w.len(), 8);
assert_eq!(&w.as_slice()[4..7], b"abc");
assert_eq!(w.as_slice()[7], 0);
}
#[test]
fn clumplet_minimal_int_width() {
let mut pb = ParameterBuffer::new(crate::wire::consts::DPB_VERSION1);
pb.int(crate::wire::consts::dpb::SQL_DIALECT, 3);
assert_eq!(pb.as_slice(), &[1, 63, 1, 3]);
}
#[test]
fn le_int_decode() {
assert_eq!(read_le_int(&[0x10, 0x27]), 10000);
assert_eq!(read_le_int(&[0xff]), 255);
}
}