use alloc::string::String;
use alloc::vec::Vec;
use super::error::{NfsError, NfsResult, NfsStatus};
#[derive(Debug)]
pub struct XdrReader<'a> {
data: &'a [u8],
pos: usize,
}
impl<'a> XdrReader<'a> {
pub fn new(data: &'a [u8]) -> Self {
Self { data, pos: 0 }
}
pub fn remaining(&self) -> usize {
self.data.len().saturating_sub(self.pos)
}
pub fn check_remaining(&self, n: usize) -> NfsResult<()> {
if self.remaining() >= n {
Ok(())
} else {
Err(NfsError::badxdr())
}
}
pub fn read_raw(&mut self, len: usize) -> NfsResult<&'a [u8]> {
self.check_remaining(len)?;
let slice = &self.data[self.pos..self.pos + len];
self.pos += len;
Ok(slice)
}
fn skip_padding(&mut self, len: usize) -> NfsResult<()> {
let pad = (4 - (len % 4)) % 4;
if pad > 0 {
self.check_remaining(pad)?;
self.pos += pad;
}
Ok(())
}
pub fn read_u32(&mut self) -> NfsResult<u32> {
self.check_remaining(4)?;
let value = u32::from_be_bytes([
self.data[self.pos],
self.data[self.pos + 1],
self.data[self.pos + 2],
self.data[self.pos + 3],
]);
self.pos += 4;
Ok(value)
}
pub fn read_i32(&mut self) -> NfsResult<i32> {
Ok(self.read_u32()? as i32)
}
pub fn read_u64(&mut self) -> NfsResult<u64> {
let high = self.read_u32()? as u64;
let low = self.read_u32()? as u64;
Ok((high << 32) | low)
}
pub fn read_i64(&mut self) -> NfsResult<i64> {
Ok(self.read_u64()? as i64)
}
pub fn read_bool(&mut self) -> NfsResult<bool> {
Ok(self.read_u32()? != 0)
}
pub fn read_opaque_fixed(&mut self, len: usize) -> NfsResult<Vec<u8>> {
let data = self.read_raw(len)?.to_vec();
self.skip_padding(len)?;
Ok(data)
}
pub fn read_opaque(&mut self) -> NfsResult<Vec<u8>> {
let len = self.read_u32()? as usize;
self.read_opaque_fixed(len)
}
pub fn read_opaque_max(&mut self, max_len: usize) -> NfsResult<Vec<u8>> {
let len = self.read_u32()? as usize;
if len > max_len {
return Err(NfsError::badxdr());
}
self.read_opaque_fixed(len)
}
pub fn read_string(&mut self) -> NfsResult<String> {
let data = self.read_opaque()?;
String::from_utf8(data).map_err(|_| NfsError::badxdr())
}
pub fn read_string_max(&mut self, max_len: usize) -> NfsResult<String> {
let data = self.read_opaque_max(max_len)?;
String::from_utf8(data).map_err(|_| NfsError::badxdr())
}
pub fn read_bytes<const N: usize>(&mut self) -> NfsResult<[u8; N]> {
let data = self.read_raw(N)?;
let mut arr = [0u8; N];
arr.copy_from_slice(data);
self.skip_padding(N)?;
Ok(arr)
}
pub fn read_optional<T, F>(&mut self, f: F) -> NfsResult<Option<T>>
where
F: FnOnce(&mut Self) -> NfsResult<T>,
{
if self.read_bool()? {
Ok(Some(f(self)?))
} else {
Ok(None)
}
}
pub fn read_array<T, F>(&mut self, f: F) -> NfsResult<Vec<T>>
where
F: Fn(&mut Self) -> NfsResult<T>,
{
let count = self.read_u32()? as usize;
let mut result = Vec::with_capacity(count);
for _ in 0..count {
result.push(f(self)?);
}
Ok(result)
}
pub fn read_array_max<T, F>(&mut self, max_len: usize, f: F) -> NfsResult<Vec<T>>
where
F: Fn(&mut Self) -> NfsResult<T>,
{
let count = self.read_u32()? as usize;
if count > max_len {
return Err(NfsError::badxdr());
}
let mut result = Vec::with_capacity(count);
for _ in 0..count {
result.push(f(self)?);
}
Ok(result)
}
pub fn position(&self) -> usize {
self.pos
}
pub fn skip(&mut self, n: usize) -> NfsResult<()> {
self.check_remaining(n)?;
self.pos += n;
Ok(())
}
}
#[derive(Debug, Default)]
pub struct XdrWriter {
data: Vec<u8>,
}
impl XdrWriter {
pub fn new() -> Self {
Self { data: Vec::new() }
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
data: Vec::with_capacity(capacity),
}
}
pub fn into_inner(self) -> Vec<u8> {
self.data
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn as_slice(&self) -> &[u8] {
&self.data
}
pub fn write_raw(&mut self, data: &[u8]) {
self.data.extend_from_slice(data);
}
fn write_padding(&mut self, len: usize) {
let pad = (4 - (len % 4)) % 4;
for _ in 0..pad {
self.data.push(0);
}
}
pub fn write_u32(&mut self, value: u32) {
self.data.extend_from_slice(&value.to_be_bytes());
}
pub fn write_i32(&mut self, value: i32) {
self.write_u32(value as u32);
}
pub fn write_u64(&mut self, value: u64) {
self.write_u32((value >> 32) as u32);
self.write_u32(value as u32);
}
pub fn write_i64(&mut self, value: i64) {
self.write_u64(value as u64);
}
pub fn write_bool(&mut self, value: bool) {
self.write_u32(if value { 1 } else { 0 });
}
pub fn write_opaque_fixed(&mut self, data: &[u8]) {
self.write_raw(data);
self.write_padding(data.len());
}
pub fn write_opaque(&mut self, data: &[u8]) {
self.write_u32(data.len() as u32);
self.write_opaque_fixed(data);
}
pub fn write_string(&mut self, s: &str) {
self.write_opaque(s.as_bytes());
}
pub fn write_bytes<const N: usize>(&mut self, data: &[u8; N]) {
self.write_raw(data);
self.write_padding(N);
}
pub fn write_optional<T, F>(&mut self, value: &Option<T>, f: F)
where
F: FnOnce(&mut Self, &T),
{
if let Some(v) = value {
self.write_bool(true);
f(self, v);
} else {
self.write_bool(false);
}
}
pub fn write_array<T, F>(&mut self, values: &[T], f: F)
where
F: Fn(&mut Self, &T),
{
self.write_u32(values.len() as u32);
for v in values {
f(self, v);
}
}
pub fn clear(&mut self) {
self.data.clear();
}
}
pub trait XdrRead: Sized {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self>;
}
pub trait XdrWrite {
fn xdr_write(&self, writer: &mut XdrWriter);
}
impl XdrRead for u32 {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self> {
reader.read_u32()
}
}
impl XdrWrite for u32 {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_u32(*self);
}
}
impl XdrRead for u64 {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self> {
reader.read_u64()
}
}
impl XdrWrite for u64 {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_u64(*self);
}
}
impl XdrRead for bool {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self> {
reader.read_bool()
}
}
impl XdrWrite for bool {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_bool(*self);
}
}
impl XdrRead for String {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self> {
reader.read_string()
}
}
impl XdrWrite for String {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_string(self);
}
}
impl XdrWrite for str {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_string(self);
}
}
impl XdrRead for Vec<u8> {
fn xdr_read(reader: &mut XdrReader) -> NfsResult<Self> {
reader.read_opaque()
}
}
impl XdrWrite for Vec<u8> {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_opaque(self);
}
}
impl XdrWrite for [u8] {
fn xdr_write(&self, writer: &mut XdrWriter) {
writer.write_opaque(self);
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::vec;
#[test]
fn test_u32_roundtrip() {
let mut writer = XdrWriter::new();
writer.write_u32(0x12345678);
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(reader.read_u32().unwrap(), 0x12345678);
}
#[test]
fn test_u64_roundtrip() {
let mut writer = XdrWriter::new();
writer.write_u64(0x123456789ABCDEF0);
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(reader.read_u64().unwrap(), 0x123456789ABCDEF0);
}
#[test]
fn test_bool_roundtrip() {
let mut writer = XdrWriter::new();
writer.write_bool(true);
writer.write_bool(false);
let mut reader = XdrReader::new(writer.as_slice());
assert!(reader.read_bool().unwrap());
assert!(!reader.read_bool().unwrap());
}
#[test]
fn test_opaque_roundtrip() {
let mut writer = XdrWriter::new();
writer.write_opaque(b"hello world");
let mut reader = XdrReader::new(writer.as_slice());
let data = reader.read_opaque().unwrap();
assert_eq!(data, b"hello world");
}
#[test]
fn test_string_roundtrip() {
let mut writer = XdrWriter::new();
writer.write_string("test string");
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(reader.read_string().unwrap(), "test string");
}
#[test]
fn test_padding() {
let mut writer = XdrWriter::new();
writer.write_opaque(b"x"); writer.write_opaque(b"xx"); writer.write_opaque(b"xxx"); writer.write_opaque(b"xxxx");
assert_eq!(writer.len(), 32);
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(reader.read_opaque().unwrap().len(), 1);
assert_eq!(reader.read_opaque().unwrap().len(), 2);
assert_eq!(reader.read_opaque().unwrap().len(), 3);
assert_eq!(reader.read_opaque().unwrap().len(), 4);
}
#[test]
fn test_optional() {
let mut writer = XdrWriter::new();
writer.write_optional(&Some(42u32), |w, v| w.write_u32(*v));
writer.write_optional::<u32, _>(&None, |w, v| w.write_u32(*v));
let mut reader = XdrReader::new(writer.as_slice());
let opt1: Option<u32> = reader.read_optional(|r| r.read_u32()).unwrap();
let opt2: Option<u32> = reader.read_optional(|r| r.read_u32()).unwrap();
assert_eq!(opt1, Some(42));
assert_eq!(opt2, None);
}
#[test]
fn test_array() {
let values = vec![1u32, 2, 3, 4, 5];
let mut writer = XdrWriter::new();
writer.write_array(&values, |w, v| w.write_u32(*v));
let mut reader = XdrReader::new(writer.as_slice());
let result: Vec<u32> = reader.read_array(|r| r.read_u32()).unwrap();
assert_eq!(result, values);
}
#[test]
fn test_fixed_bytes() {
let mut writer = XdrWriter::new();
writer.write_bytes(&[1u8, 2, 3, 4, 5, 6, 7, 8]);
let mut reader = XdrReader::new(writer.as_slice());
let bytes: [u8; 8] = reader.read_bytes().unwrap();
assert_eq!(bytes, [1, 2, 3, 4, 5, 6, 7, 8]);
}
#[test]
fn test_reader_remaining() {
let data = [0u8; 20];
let mut reader = XdrReader::new(&data);
assert_eq!(reader.remaining(), 20);
reader.read_u32().unwrap();
assert_eq!(reader.remaining(), 16);
}
#[test]
fn test_reader_eof() {
let data = [0u8; 2];
let mut reader = XdrReader::new(&data);
let result = reader.read_u32();
assert!(result.is_err());
}
#[test]
fn test_max_length_checks() {
let mut writer = XdrWriter::new();
writer.write_string("hello");
let mut reader = XdrReader::new(writer.as_slice());
let result = reader.read_string_max(3); assert!(result.is_err());
let mut reader = XdrReader::new(writer.as_slice());
let result = reader.read_string_max(10); assert!(result.is_ok());
}
#[test]
fn test_xdr_traits() {
let mut writer = XdrWriter::new();
42u32.xdr_write(&mut writer);
"test".xdr_write(&mut writer);
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(u32::xdr_read(&mut reader).unwrap(), 42);
assert_eq!(String::xdr_read(&mut reader).unwrap(), "test");
}
#[test]
fn test_negative_i32() {
let mut writer = XdrWriter::new();
writer.write_i32(-1);
writer.write_i32(-12345);
let mut reader = XdrReader::new(writer.as_slice());
assert_eq!(reader.read_i32().unwrap(), -1);
assert_eq!(reader.read_i32().unwrap(), -12345);
}
#[test]
fn test_skip() {
let data = [1, 2, 3, 4, 5, 6, 7, 8];
let mut reader = XdrReader::new(&data);
reader.skip(4).unwrap();
assert_eq!(reader.position(), 4);
let result = reader.skip(10);
assert!(result.is_err());
}
#[test]
fn test_writer_clear() {
let mut writer = XdrWriter::new();
writer.write_u32(123);
assert!(!writer.is_empty());
writer.clear();
assert!(writer.is_empty());
}
}