use std::ops::{Deref, DerefMut, Range};
use std::collections::TryReserveError;
use std::fmt::{self, Debug};
use std::{mem, slice};
use std::borrow::Cow;
use std::sync::Arc;
use widestring::{Utf16Str, Utf16String};
use crate::value::{DevEnum, Scalar, Spectrum};
use crate::error::{Error, ErrorKind, Result};
use crate::buf::{Buf, BufMut, PackedElement};
use super::codeset::{AnyCodeset, Codeset, CodesetDef, WideCodeset, WideCodesetDef, Codesets};
use super::endian::Endian;
#[derive(Clone)]
pub struct CdrReader<'a> {
buf: &'a [u8],
owner: &'a Buf<u8, 1>,
pos: usize,
endian: Endian,
codesets: Option<Codesets>,
}
impl<'a> CdrReader<'a> {
pub fn new(buf: &'a Buf<u8, 1>) -> Self {
Self {
buf: buf.as_slice(),
owner: buf,
pos: 0,
endian: Endian::NATIVE,
codesets: None,
}
}
pub fn empty() -> CdrReader<'static> {
static EMPTY: Buf<u8, 1> = Buf::new();
CdrReader::new(&EMPTY)
}
pub fn substream(&self, bytes: &'a [u8]) -> Self {
let range = self.buf.as_ptr_range();
assert!(range.start <= bytes.as_ptr() && bytes.as_ptr_range().end <= range.end,
"the octets of a CDR substream must be within its reader");
Self { buf: bytes, ..Self::new(self.owner) }
}
#[inline]
pub const fn endian(&self) -> Endian {
self.endian
}
#[inline]
pub const fn set_endian(&mut self, endian: Endian) {
self.endian = endian;
}
#[inline]
pub const fn with_endian(mut self, endian: Endian) -> Self {
self.endian = endian;
self
}
#[inline]
pub const fn codesets(&self) -> Option<Codesets> {
self.codesets
}
#[inline]
pub const fn set_codesets(&mut self, codesets: Option<Codesets>) {
self.codesets = codesets;
}
#[inline]
pub const fn with_codesets(mut self, codesets: Option<Codesets>) -> Self {
self.codesets = codesets;
self
}
#[inline]
pub fn codeset(&self) -> Codeset {
self.codesets.map(|codesets| codesets.codeset).unwrap_or(Codeset::Iso8859_1)
}
#[inline]
pub fn wide_codeset(&self) -> Option<WideCodeset> {
self.codesets.map(|codesets| codesets.wide_codeset)
}
#[inline]
pub const fn buffer(&self) -> &'a [u8] {
self.buf
}
#[inline]
pub const fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn set_position(&mut self, pos: usize) -> usize {
assert!(pos <= self.buf.len(), "cannot move the CDR position past the {} octets of the buffer", self.buf.len());
mem::replace(&mut self.pos, pos)
}
#[inline]
pub const fn remaining(&self) -> usize {
self.buf.len() - self.pos
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.remaining() == 0
}
#[inline]
pub const fn remaining_slice(&self) -> &'a [u8] {
self.buf.split_at(self.pos).1
}
#[track_caller]
pub fn skip(&mut self, len: usize) -> Result<()> {
let remaining_len = self.remaining();
if remaining_len < len {
return Err(invalid_data_err(format!("Failed to skip {len} octets, only {remaining_len} remain")));
}
self.pos += len;
Ok(())
}
#[track_caller]
fn read_raw_slice(&mut self, len: usize, align: usize) -> Result<&'a [u8]> {
debug_assert!(align.is_power_of_two());
let offset = align_offset(self.pos, align);
let remaining_len = self.remaining();
if offset > remaining_len || remaining_len - offset < len {
return Err(invalid_data_err(format!("Failed to read {len} octets aligned to {align}, only {remaining_len} remain")));
}
self.pos += offset;
let ret = &self.buf[self.pos..][..len];
self.pos += len;
Ok(ret)
}
#[track_caller]
fn read_num_slice<T: CdrNum>(&mut self, len: usize) -> Result<Cow<'a, [T]>> {
let size_and_align = mem::size_of::<T>();
let byte_len = len.checked_mul(size_and_align)
.ok_or_else(|| invalid_data_err(format!("Failed to read {len} elements of {size_and_align} octets, their size overflows")))?;
let slice = self.read_raw_slice(byte_len, size_and_align)?;
if slice.as_ptr().addr() & (size_and_align - 1) == 0 && (size_and_align == 1 || self.endian.is_native()) {
Ok(Cow::Borrowed(unsafe { slice::from_raw_parts(slice.as_ptr().cast::<T>(), len) }))
} else {
let mut owned = Vec::<T>::with_capacity(len);
unsafe {
owned.spare_capacity_mut().as_mut_ptr().cast::<u8>().copy_from_nonoverlapping(slice.as_ptr(), byte_len);
owned.set_len(len);
}
if !self.endian.is_native() {
for value in &mut owned {
*value = T::swap_bytes(*value);
}
}
Ok(Cow::Owned(owned))
}
}
#[track_caller]
fn read_num<T: CdrNum>(&mut self) -> Result<T> {
let size_and_align = mem::size_of::<T>();
let slice = self.read_raw_slice(size_and_align, size_and_align)?;
let num = unsafe {
let ptr = slice.as_ptr().cast::<T>();
if ptr.is_aligned() {
ptr.read()
} else {
ptr.read_unaligned()
}
};
if self.endian.is_native() {
Ok(num)
} else {
Ok(T::swap_bytes(num))
}
}
#[track_caller]
pub fn read_octet_slice(&mut self, len: usize) -> Result<Cow<'a, [u8]>> {
self.read_raw_slice(len, 1).map(Cow::Borrowed)
}
#[track_caller]
pub fn read_octet(&mut self) -> Result<u8> {
self.read_num()
}
#[track_caller]
pub fn read_bool_slice(&mut self, len: usize) -> Result<Cow<'a, [bool]>> {
let ret = self.read_raw_slice(len, 1)?;
if ret.iter().all(|&b| b <= 1) {
Ok(Cow::Borrowed(unsafe { slice::from_raw_parts(ret.as_ptr().cast::<bool>(), len) }))
} else {
Err(invalid_data_err("Failed to read booleans, one of the octets is neither 0 nor 1"))
}
}
#[track_caller]
pub fn read_bool(&mut self) -> Result<bool> {
let ret = self.read_octet()?;
match ret {
0 => Ok(false),
1 => Ok(true),
_ => Err(invalid_data_err("Failed to read a boolean, its octet is neither 0 nor 1"))
}
}
#[track_caller]
pub fn read_ushort_slice(&mut self, len: usize) -> Result<Cow<'a, [u16]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_ushort(&mut self) -> Result<u16> {
self.read_num()
}
#[track_caller]
pub fn read_short_slice(&mut self, len: usize) -> Result<Cow<'a, [i16]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_short(&mut self) -> Result<i16> {
self.read_num()
}
#[track_caller]
pub fn read_ulong_slice(&mut self, len: usize) -> Result<Cow<'a, [u32]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_ulong(&mut self) -> Result<u32> {
self.read_num()
}
#[track_caller]
pub fn read_long_slice(&mut self, len: usize) -> Result<Cow<'a, [i32]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_long(&mut self) -> Result<i32> {
self.read_num()
}
#[track_caller]
pub fn read_ulonglong_slice(&mut self, len: usize) -> Result<Cow<'a, [u64]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_ulonglong(&mut self) -> Result<u64> {
self.read_num()
}
#[track_caller]
pub fn read_longlong_slice(&mut self, len: usize) -> Result<Cow<'a, [i64]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_longlong(&mut self) -> Result<i64> {
self.read_num()
}
#[track_caller]
pub fn read_float_slice(&mut self, len: usize) -> Result<Cow<'a, [f32]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_float(&mut self) -> Result<f32> {
self.read_num()
}
#[track_caller]
pub fn read_double_slice(&mut self, len: usize) -> Result<Cow<'a, [f64]>> {
self.read_num_slice(len)
}
#[track_caller]
pub fn read_double(&mut self) -> Result<f64> {
self.read_num()
}
#[track_caller]
pub fn read_sequence_len(&mut self) -> Result<usize> {
usize::try_from(self.read_ulong()?)
.map_err(|e| invalid_data_err("Failed to read a sequence length, it does not fit into a usize")
.with_source(Box::new(e)))
}
#[track_caller]
pub fn read_raw_string(&mut self) -> Result<&'a [u8]> {
let len = self.read_sequence_len()?;
if len == 0 {
return Err(invalid_data_err("Failed to read a string, its length is zero"));
}
let raw = self.read_raw_slice(len, 1)?;
if raw[len - 1] != 0 {
return Err(invalid_data_err("Failed to read a string, it has no terminating nul octet"));
}
Ok(&raw[..len - 1])
}
#[track_caller]
pub fn read_string(&mut self) -> Result<Cow<'a, str>> {
let codeset = self.codeset();
let raw = self.read_raw_string()?;
codeset.decode_to_cow(raw)
.ok_or_else(|| invalid_data_err(format!("Failed to read a string, decoding it with {codeset:?} failed")))
}
#[track_caller]
pub fn read_wstring(&mut self) -> Result<Cow<'a, Utf16Str>> {
let codeset = self.wide_codeset()
.ok_or_else(|| invalid_data_err("Failed to read a wide string, no wide codeset was negotiated"))?;
let len = self.read_sequence_len()?;
if len % 2 != 0 {
return Err(invalid_data_err(format!("Failed to read a wide string, its length of {len} octets is invalid")));
}
let mut raw = self.read_ushort_slice(len / 2)?;
if raw.last() == Some(&0) {
match &mut raw {
Cow::Borrowed(raw) => { *raw = &raw[..raw.len() - 1] }
Cow::Owned(raw) => { raw.pop(); }
}
}
let decoded = match raw {
Cow::Borrowed(raw) => codeset.decode_to_cow(raw),
Cow::Owned(raw) => codeset.decode_to_cow(&raw).map(|s| Cow::Owned(s.into_owned())),
};
decoded.ok_or_else(|| invalid_data_err(format!("Failed to read a wide string, decoding it with {codeset:?} failed")))
}
fn share(&self, raw: &'a [u8]) -> Buf<u8, 1> {
self.owner.slice_ref(raw)
}
#[track_caller]
pub fn read_buf<T: CdrElement>(&mut self, len: usize) -> Result<Spectrum<T>> {
let size = mem::size_of::<T>();
let byte_len = len.checked_mul(size)
.ok_or_else(|| invalid_data_err(format!("Failed to read {len} elements of {size} octets, their size overflows")))?;
let raw = self.read_raw_slice(byte_len, size)?;
let mut buf = self.share(raw);
if size > 1 && !self.endian.is_native() {
buf.make_mut().as_mut_slice().chunks_exact_mut(size).for_each(<[u8]>::reverse);
}
buf.cast([len])
.map_err(|_| invalid_data_err(format!("Failed to read an array of {len} values, one of them is invalid")))
}
#[track_caller]
pub fn read_string_buf(&mut self, len: usize) -> Result<Spectrum<str>> {
if len > self.remaining() / 5 {
return Err(invalid_data_err(format!("Failed to read {len} strings, only {} octets remain", self.remaining())));
}
let codeset = self.codeset();
let decode_failed = || invalid_data_err(format!("Failed to read a string, decoding it with {codeset:?} failed"));
let start = self.pos;
let window = &self.buf[start..];
let mut buf = None::<BufMut<u8, 1>>;
let mut spans = Vec::with_capacity(len);
for _ in 0..len {
let raw = self.read_raw_string()?;
let buf = match &mut buf {
Some(buf) => buf,
buf @ None => match codeset.decode_is_native(raw) {
Some(true) => {
let at = raw.as_ptr().addr() - window.as_ptr().addr();
spans.push(span(at, at + raw.len())?);
continue;
}
Some(false) => {
let buf = buf.insert(BufMut::with_capacity(self.pos - start));
for span in &mut spans {
let at = buf.len();
buf.extend_from_slice(&window[span.start as usize..span.end as usize]);
*span = at as u32..buf.len() as u32;
}
buf
}
None => return Err(decode_failed()),
}
};
let at = buf.len();
codeset.decode_to_buf(raw, buf).ok_or_else(decode_failed)?;
spans.push(span(at, buf.len())?);
}
let buf = match buf {
Some(buf) => buf.freeze(),
None => self.share(&self.buf[start..self.pos]),
};
Ok(unsafe { Buf::from_spans_unchecked(buf, spans, [len]) })
}
#[track_caller]
pub fn read_endian(&mut self) -> Result<Endian> {
let endian = if self.read_bool()? { Endian::Little } else { Endian::Big };
self.set_endian(endian);
Ok(endian)
}
#[track_caller]
pub fn read_encapsulation(&mut self) -> Result<CdrReader<'a>> {
let len = self.read_sequence_len()?;
if len == 0 {
return Err(invalid_data_err("Failed to read an encapsulation, it does not even hold a byte order flag"));
}
let raw = self.read_raw_slice(len, 1)?;
let mut reader = self.substream(raw);
reader.read_endian()?;
Ok(reader)
}
}
impl Debug for CdrReader<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CdrReader")
.field("position", &self.pos)
.field("remaining", &self.owner.slice_ref(self.remaining_slice()))
.field("endian", &self.endian)
.field("codesets", &self.codesets)
.finish()
}
}
pub struct CdrWriter {
buf: BufMut<u8, 1>,
pos: usize,
origin: usize,
codesets: Option<Codesets>,
}
impl CdrWriter {
#[inline]
pub fn new() -> Self {
Self::from_buf(BufMut::new())
}
#[inline]
pub fn with_capacity(len: usize) -> Self {
Self::from_buf(BufMut::with_capacity(len))
}
#[inline]
pub fn from_buf(buf: BufMut<u8, 1>) -> Self {
Self {
pos: buf.len(),
buf,
origin: 0,
codesets: None,
}
}
#[inline]
pub const fn codesets(&self) -> Option<Codesets> {
self.codesets
}
#[inline]
pub const fn set_codesets(&mut self, codesets: Option<Codesets>) {
self.codesets = codesets;
}
#[inline]
pub const fn with_codesets(mut self, codesets: Option<Codesets>) -> Self {
self.codesets = codesets;
self
}
#[inline]
pub fn codeset(&self) -> Codeset {
self.codesets.map(|codesets| codesets.codeset).unwrap_or(Codeset::Iso8859_1)
}
#[inline]
pub fn wide_codeset(&self) -> Option<WideCodeset> {
self.codesets.map(|codesets| codesets.wide_codeset)
}
#[inline]
pub fn buffer(&self) -> &[u8] {
&self.buf.as_slice()[self.origin..self.pos]
}
#[inline]
pub fn into_buf(mut self) -> BufMut<u8, 1> {
self.buf.truncate(self.pos);
if self.origin != 0 {
self.buf.as_mut_slice().copy_within(self.origin.., 0);
self.buf.truncate(self.pos - self.origin);
}
self.buf
}
#[inline]
pub fn into_sender(self) -> CdrSender {
CdrSender::from_writer(self)
}
#[inline]
pub fn clear(&mut self) {
self.buf.clear();
self.pos = 0;
self.origin = 0;
}
#[inline]
pub const fn origin(&self) -> usize {
self.origin
}
#[inline]
pub fn set_origin(&mut self, origin: usize) {
assert!(origin <= self.pos, "cannot move the CDR origin past the position of {}", self.pos);
self.origin = origin;
}
#[inline]
pub const fn position(&self) -> usize {
self.pos
}
#[inline]
pub fn set_position(&mut self, pos: usize) {
assert!(pos <= self.buf.len(), "cannot move the CDR position past the {} octets of the buffer", self.buf.len());
assert!(pos >= self.origin, "cannot move the CDR position before the origin of the stream being written");
self.pos = pos;
}
#[track_caller]
pub fn align(&mut self, align: usize) -> Result<()> {
assert!(align.is_power_of_two(), "cannot align a CDR stream on {align}, which is not a power of two");
let len = align_offset(self.pos - self.origin, align);
if len == 0 {
return Ok(());
}
self.next_slice(1, len)?.fill(0);
Ok(())
}
fn reserve_slice(&mut self, align: usize, len: usize) -> Result<usize> {
debug_assert!(align.is_power_of_two());
if len == 0 {
return Ok(self.pos);
}
let start = self.pos + align_offset(self.pos - self.origin, align);
let end = start + len;
if end - self.origin > u32::MAX as usize {
return Err(invalid_data_err(format!("Failed to write the CDR stream, its {} octets do not fit into an ulong length", end - self.origin)));
}
if end > self.buf.len() {
self.buf.try_reserve(end - self.buf.len())?;
self.buf.set_len(end);
}
self.buf.as_mut_slice()[self.pos..start].fill(0);
self.pos = end;
Ok(start)
}
#[inline]
fn next_slice(&mut self, align: usize, len: usize) -> Result<&mut [u8]> {
let start = self.reserve_slice(align, len)?;
Ok(&mut self.buf.as_mut_slice()[start..][..len])
}
fn write_num_at<T: CdrNum>(&mut self, offset: usize, value: T) {
let size_and_align = mem::size_of::<T>();
let dst = &mut self.buf.as_mut_slice()[offset..][..size_and_align];
unsafe {
dst.as_mut_ptr().cast::<T>().write_unaligned(value);
}
}
fn write_num<T: CdrNum>(&mut self, value: T) -> Result<()> {
let size_and_align = mem::size_of::<T>();
let offset = self.reserve_slice(size_and_align, size_and_align)?;
self.write_num_at(offset, value);
Ok(())
}
fn write_num_slice<T: CdrNum>(&mut self, values: &[T]) -> Result<()> {
let size_and_align = mem::size_of::<T>();
let len = values.len();
let byte_len = size_and_align.checked_mul(len)
.ok_or_else(|| invalid_data_err(format!("Failed to write {len} elements of {size_and_align} octets, their size overflows")))?;
let dst = self.next_slice(size_and_align, byte_len)?;
unsafe {
dst.as_mut_ptr().copy_from_nonoverlapping(values.as_ptr().cast::<u8>(), byte_len);
}
Ok(())
}
#[inline]
pub fn write_octet_slice(&mut self, values: &[u8]) -> Result<()> {
self.next_slice(1, values.len())?.copy_from_slice(values);
Ok(())
}
#[inline]
pub fn write_octet(&mut self, value: u8) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_bool_slice(&mut self, values: &[bool]) -> Result<()> {
self.write_octet_slice(unsafe { slice::from_raw_parts(values.as_ptr().cast::<u8>(), values.len()) })
}
#[inline]
pub fn write_bool(&mut self, value: bool) -> Result<()> {
self.write_octet(value as u8)
}
#[inline]
pub fn write_ushort_slice(&mut self, values: &[u16]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_ushort(&mut self, value: u16) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_short_slice(&mut self, values: &[i16]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_short(&mut self, value: i16) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_ulong_slice(&mut self, values: &[u32]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_ulong(&mut self, value: u32) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_long_slice(&mut self, values: &[i32]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_long(&mut self, value: i32) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_ulonglong_slice(&mut self, values: &[u64]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_ulonglong(&mut self, value: u64) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_longlong_slice(&mut self, values: &[i64]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_longlong(&mut self, value: i64) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_float_slice(&mut self, values: &[f32]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_float(&mut self, value: f32) -> Result<()> {
self.write_num(value)
}
#[inline]
pub fn write_double_slice(&mut self, values: &[f64]) -> Result<()> {
self.write_num_slice(values)
}
#[inline]
pub fn write_double(&mut self, value: f64) -> Result<()> {
self.write_num(value)
}
pub fn write_data<T: CdrElement, const DIM: usize>(&mut self, data: &Buf<T, DIM>) -> Result<()> {
let bytes = data.as_bytes();
self.next_slice(mem::size_of::<T>(), bytes.len())?.copy_from_slice(bytes);
Ok(())
}
pub fn write_string_data<const DIM: usize>(&mut self, data: &Buf<str, DIM>) -> Result<()> {
data.iter().try_for_each(|s| self.write_string(s))
}
pub fn write_sequence_len(&mut self, len: usize) -> Result<()> {
let len = u32::try_from(len)
.map_err(|_| invalid_data_err(format!("Failed to write a sequence length, {len} does not fit into an ulong")))?;
self.write_ulong(len)
}
#[track_caller]
pub fn write_raw_string(&mut self, raw: &[u8]) -> Result<()> {
if raw.contains(&0) {
return Err(invalid_data_err("Failed to write a raw string, it holds a nul octet"));
}
self.write_sequence_len(raw.len() + 1)?;
self.write_octet_slice(raw)?;
self.write_octet(0)
}
#[track_caller]
pub fn write_string(&mut self, s: &str) -> Result<()> {
let codeset = self.codeset();
let encode_failed = || invalid_data_err(format!("Failed to write a string, encoding it with {codeset:?} failed"));
let len_at = self.reserve_slice(4, 4)?;
let start = self.pos;
if start == self.buf.len() {
let len = codeset.encode_to_buf(s, &mut self.buf).ok_or_else(encode_failed)?;
self.pos += len;
if self.pos - self.origin > u32::MAX as usize {
self.buf.truncate(start);
self.pos = start;
return Err(invalid_data_err("Failed to write a string, it is too long for an ulong length"));
}
} else {
let mut buf = BufMut::new();
codeset.encode_to_buf(s, &mut buf).ok_or_else(encode_failed)?;
self.write_octet_slice(buf.as_slice())?;
}
self.write_octet(0)?;
self.write_num_at(len_at, (self.pos - start) as u32);
Ok(())
}
#[track_caller]
pub fn write_wstring(&mut self, s: &Utf16Str) -> Result<()> {
let codeset = self.wide_codeset()
.ok_or_else(|| invalid_data_err("Failed to write a wide string, no wide codeset was negotiated"))?;
let mut units = BufMut::new();
let len = codeset.encode_to_buf(s, &mut units)
.ok_or_else(|| invalid_data_err(format!("Failed to write a wide string, encoding it with {codeset:?} failed")))?;
let len = len.checked_mul(2)
.and_then(|len| u32::try_from(len).ok())
.ok_or_else(|| invalid_data_err("Failed to write a wide string, it is too long for an ulong length"))?;
self.write_ulong(len)?;
self.write_ushort_slice(units.as_slice())
}
#[inline]
pub fn write_endian(&mut self) -> Result<()> {
self.write_bool(Endian::NATIVE.is_little())
}
pub fn write_encapsulation(&mut self, write: impl FnOnce(&mut Self) -> Result<()>) -> Result<()> {
let restore_pos = self.pos;
let restore_len = self.buf.len();
let len_at = self.reserve_slice(4, 4)?;
let origin = self.pos;
let outer_origin = mem::replace(&mut self.origin, origin);
let outer_codesets = self.codesets.take();
let res = self.write_endian().and_then(|()| write(self));
self.origin = outer_origin;
self.codesets = outer_codesets;
if let Err(err) = res {
self.pos = restore_pos;
self.buf.truncate(restore_len);
return Err(err);
}
let len = self.pos - origin;
debug_assert!(u32::try_from(len).is_ok(), "the CDR stream grew past an ulong length");
self.write_num_at(len_at, len as u32);
Ok(())
}
}
impl Default for CdrWriter {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl Debug for CdrWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("CdrWriter")
.field("position", &self.pos)
.field("origin", &self.origin)
.field("buffer", &self.buf)
.field("codesets", &self.codesets)
.finish()
}
}
#[derive(Debug, Default)]
pub struct CdrSender {
writer: CdrWriter,
interleaved: Vec<Interleaved>,
}
#[derive(Debug)]
struct Interleaved {
at: usize,
pos: usize,
buf: Buf<u8, 1>,
}
impl CdrSender {
#[inline]
pub const fn from_writer(writer: CdrWriter) -> Self {
Self {
writer,
interleaved: Vec::new(),
}
}
pub fn interleave(&mut self, buf: Buf<u8, 1>) {
if buf.is_empty() {
return;
}
self.interleaved.push(Interleaved {
at: self.writer.position(),
pos: 0,
buf,
});
}
pub fn buffer(&self) -> &[u8] {
let origin = self.writer.origin();
let Some(interleaved) = self.interleaved.first() else {
return self.writer.buffer();
};
if interleaved.at == origin {
return &interleaved.buf.as_slice()[interleaved.pos..];
}
&self.writer.buffer()[..interleaved.at - origin]
}
pub fn ack(&mut self, len: usize) {
let mut remaining = len;
while remaining != 0 {
let begin = self.writer.origin();
let mut end = self.writer.position();
if let Some(interleaved) = self.interleaved.first_mut() {
end = interleaved.at;
if interleaved.at == begin {
let taken = usize::min(remaining, interleaved.buf.len() - interleaved.pos);
interleaved.pos += taken;
remaining -= taken;
if interleaved.pos == interleaved.buf.len() {
self.interleaved.remove(0);
}
continue;
}
}
let size = end.strict_sub(begin);
let taken = usize::min(remaining, size);
assert!(taken != 0, "acknowledged more octets than were given out");
self.writer.set_origin(begin + taken);
remaining -= taken;
}
}
pub fn drop_off_origin(&mut self) {
let origin = self.writer.origin();
let codesets = self.writer.codesets();
self.writer = CdrWriter::from_buf(mem::take(&mut self.writer).into_buf())
.with_codesets(codesets);
for interleaved in &mut self.interleaved {
interleaved.at -= origin;
}
}
}
impl Deref for CdrSender {
type Target = CdrWriter;
#[inline]
fn deref(&self) -> &Self::Target {
&self.writer
}
}
impl DerefMut for CdrSender {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.writer
}
}
#[derive(Debug)]
pub struct CdrSealed(());
pub trait CdrRead<'d>: Sized {
fn read(buf: &mut CdrReader<'d>) -> Result<Self>;
#[doc(hidden)]
fn _read_slice(reader: &mut CdrReader<'d>, len: usize, _sealed: CdrSealed) -> Option<Result<Cow<'d, [Self]>>>
where
Self: Clone,
{
let _ = (reader, len);
None
}
#[doc(hidden)]
fn _read_vec(reader: &mut CdrReader<'d>, len: usize, _sealed: CdrSealed) -> Option<Result<Vec<Self>>> {
let _ = (reader, len);
None
}
}
pub trait CdrWrite {
fn write(&self, buf: &mut CdrWriter) -> Result<()>;
#[doc(hidden)]
fn _write_slice(values: &[Self], writer: &mut CdrWriter, _sealed: CdrSealed) -> Option<Result<()>>
where
Self: Sized,
{
let _ = (values, writer);
None
}
}
impl<'d> CdrReader<'d> {
#[inline]
#[track_caller]
pub fn read<T: CdrRead<'d>>(&mut self) -> Result<T> {
T::read(self)
}
#[track_caller]
pub fn read_slice<T: CdrRead<'d> + Clone + 'd>(&mut self, len: usize) -> Result<Cow<'d, [T]>> {
if let Some(slice) = T::_read_slice(self, len, CdrSealed(())) {
return slice;
}
self.read_vec(len).map(Cow::Owned)
}
#[track_caller]
pub fn read_vec<T: CdrRead<'d>>(&mut self, len: usize) -> Result<Vec<T>> {
if let Some(vec) = T::_read_vec(self, len, CdrSealed(())) {
return vec;
}
let mut ret = Vec::new();
ret.try_reserve(len)
.map_err(|err| out_of_memory_err(err, format!("Failed to allocate room for a sequence of {len} values")))?;
for _ in 0..len {
ret.push(T::read(self)?);
}
Ok(ret)
}
}
impl CdrWriter {
#[inline]
#[track_caller]
pub fn write<T: CdrWrite + ?Sized>(&mut self, value: &T) -> Result<()> {
value.write(self)
}
#[track_caller]
pub fn write_slice<T: CdrWrite>(&mut self, values: &[T]) -> Result<()> {
if let Some(res) = T::_write_slice(values, self, CdrSealed(())) {
return res;
}
for value in values {
self.write(value)?;
}
Ok(())
}
}
macro_rules! impl_cdr_type_num {
( $name:ident, $ty:ty ) => {
pastey::paste! {
impl<'d> CdrRead<'d> for $ty {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
reader.[<read_ $name>]()
}
#[inline]
fn _read_slice(reader: &mut CdrReader<'d>, len: usize, _sealed: CdrSealed) -> Option<Result<Cow<'d, [Self]>>> {
Some(reader.[<read_ $name _slice>](len))
}
#[inline]
fn _read_vec(reader: &mut CdrReader<'d>, len: usize, _sealed: CdrSealed) -> Option<Result<Vec<Self>>> {
Some(reader.[<read_ $name _slice>](len).map(Cow::into_owned))
}
}
impl CdrWrite for $ty {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.[<write_ $name>](*self)
}
#[inline]
fn _write_slice(values: &[Self], writer: &mut CdrWriter, _sealed: CdrSealed) -> Option<Result<()>> {
Some(writer.[<write_ $name _slice>](values))
}
}
}
};
}
impl_cdr_type_num!(bool, bool);
impl_cdr_type_num!(octet, u8);
impl_cdr_type_num!(ushort, u16);
impl_cdr_type_num!(short, i16);
impl_cdr_type_num!(ulong, u32);
impl_cdr_type_num!(long, i32);
impl_cdr_type_num!(ulonglong, u64);
impl_cdr_type_num!(longlong, i64);
impl_cdr_type_num!(float, f32);
impl_cdr_type_num!(double, f64);
impl<'d> CdrRead<'d> for Cow<'d, str> {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
reader.read_string()
}
}
impl<'d> CdrRead<'d> for String {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
reader.read_string().map(Cow::into_owned)
}
}
impl CdrWrite for str {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_string(self)
}
}
impl CdrWrite for String {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_string(self)
}
}
impl<'d> CdrRead<'d> for Cow<'d, Utf16Str> {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
reader.read_wstring()
}
}
impl<'d> CdrRead<'d> for Utf16String {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
reader.read_wstring().map(Cow::into_owned)
}
}
impl CdrWrite for Utf16Str {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_wstring(self)
}
}
impl CdrWrite for Utf16String {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_wstring(self)
}
}
impl CdrRead<'_> for Codesets {
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
let codeset_raw = reader.read_ulong()?;
let wide_codeset_raw = reader.read_ulong()?;
Ok(Self {
codeset: Codeset::from_id(codeset_raw)
.ok_or_else(|| invalid_data_err(format!("Codeset 0x{codeset_raw:08X} is not supported")))?,
wide_codeset: WideCodeset::from_id(wide_codeset_raw)
.ok_or_else(|| invalid_data_err(format!("Wide codeset 0x{wide_codeset_raw:08X} is not supported")))?,
})
}
}
impl CdrWrite for Codesets {
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_ulong(self.codeset.id())?;
writer.write_ulong(self.wide_codeset.id())?;
Ok(())
}
}
impl<'d, T: CdrRead<'d> + Clone + 'd> CdrRead<'d> for Cow<'d, [T]> {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
let len = reader.read_sequence_len()?;
reader.read_slice(len)
}
}
impl<'d, T: CdrRead<'d>> CdrRead<'d> for Vec<T> {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
let len = reader.read_sequence_len()?;
reader.read_vec(len)
}
}
impl<'d, T> CdrRead<'d> for Box<[T]>
where
Vec<T>: CdrRead<'d>,
{
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
<Vec<T>>::read(reader).map(Vec::into_boxed_slice)
}
}
impl<'d> CdrRead<'d> for &'d [u8] {
#[inline]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
let len = reader.read_sequence_len()?;
reader.read_raw_slice(len, 1)
}
}
impl<T: CdrWrite> CdrWrite for [T] {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_sequence_len(self.len())?;
writer.write_slice(self)
}
}
impl<T: CdrWrite> CdrWrite for Vec<T> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
<[T] as CdrWrite>::write(self, writer)
}
}
impl<T, const N: usize> CdrWrite for [T; N]
where
[T]: CdrWrite,
{
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
<[T] as CdrWrite>::write(&self[..], writer)
}
}
impl<T: CdrWrite + ?Sized> CdrWrite for &'_ T {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
CdrWrite::write(&**self, writer)
}
}
impl<T: CdrWrite + ?Sized> CdrWrite for Box<T> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
CdrWrite::write(&**self, writer)
}
}
impl<T: CdrWrite + ?Sized> CdrWrite for Arc<T> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
CdrWrite::write(&**self, writer)
}
}
impl<T: CdrWrite + ToOwned + ?Sized> CdrWrite for Cow<'_, T> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write::<T>(&**self)
}
}
pub trait CdrElement: PackedElement {}
impl CdrElement for bool {}
impl CdrElement for u8 {}
impl CdrElement for u16 {}
impl CdrElement for i16 {}
impl CdrElement for u32 {}
impl CdrElement for i32 {}
impl CdrElement for u64 {}
impl CdrElement for i64 {}
impl CdrElement for f32 {}
impl CdrElement for f64 {}
impl CdrElement for DevEnum {}
impl<T: CdrElement> CdrRead<'_> for Spectrum<T> {
#[inline]
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
let len = reader.read_sequence_len()?;
reader.read_buf(len)
}
}
impl CdrRead<'_> for Scalar<str> {
#[inline]
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
Ok(reader.read_string_buf(1)?.into_scalar().expect("a single string was read"))
}
}
impl CdrRead<'_> for Spectrum<str> {
#[inline]
fn read(reader: &mut CdrReader<'_>) -> Result<Self> {
let len = reader.read_sequence_len()?;
reader.read_string_buf(len)
}
}
impl<T: CdrElement> CdrWrite for Spectrum<T> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_sequence_len(self.len())?;
writer.write_data(self)
}
}
impl CdrWrite for Spectrum<str> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write_sequence_len(self.len())?;
writer.write_string_data(self)
}
}
macro_rules! impl_cdr_type_tuple {
( $( $member:ident ),* ) => {
impl<'d, $( $member: CdrRead<'d>, )*> CdrRead<'d> for ( $( $member, )* ) {
#[inline]
#[allow(unused)]
fn read(reader: &mut CdrReader<'d>) -> Result<Self> {
Ok(( $( $member::read(reader)?, )* ))
}
}
impl<$( $member: CdrWrite, )*> CdrWrite for ( $( $member, )* ) {
#[inline]
#[allow(unused, non_snake_case)]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
let ( $( $member, )* ) = self;
$( writer.write($member)?; )*
Ok(())
}
}
};
}
impl_cdr_type_tuple!();
impl_cdr_type_tuple!(A);
impl_cdr_type_tuple!(A, B);
impl_cdr_type_tuple!(A, B, C);
impl_cdr_type_tuple!(A, B, C, D);
impl_cdr_type_tuple!(A, B, C, D, E);
impl_cdr_type_tuple!(A, B, C, D, E, F);
impl_cdr_type_tuple!(A, B, C, D, E, F, G);
impl_cdr_type_tuple!(A, B, C, D, E, F, G, H);
impl_cdr_type_tuple!(A, B, C, D, E, F, G, H, I);
impl_cdr_type_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_cdr_type_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_cdr_type_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
impl CdrWrite for BufMut<u8, 1> {
#[inline]
fn write(&self, writer: &mut CdrWriter) -> Result<()> {
writer.write::<[u8]>(self.as_slice())
}
}
unsafe trait CdrNum: Copy + 'static {
fn swap_bytes(self) -> Self;
}
macro_rules! impl_cdr_num {
( $ty:ty, $swap:expr ) => {
unsafe impl CdrNum for $ty {
#[inline]
fn swap_bytes(self) -> Self {
$swap(self)
}
}
};
}
impl_cdr_num!(u8, u8::swap_bytes);
impl_cdr_num!(u16, u16::swap_bytes);
impl_cdr_num!(i16, i16::swap_bytes);
impl_cdr_num!(u32, u32::swap_bytes);
impl_cdr_num!(i32, i32::swap_bytes);
impl_cdr_num!(u64, u64::swap_bytes);
impl_cdr_num!(i64, i64::swap_bytes);
impl_cdr_num!(f32, |val: f32| f32::from_bits(val.to_bits().swap_bytes()));
impl_cdr_num!(f64, |val: f64| f64::from_bits(val.to_bits().swap_bytes()));
#[inline]
const fn align_offset(addr: usize, align: usize) -> usize {
(align - (addr & (align - 1))) & (align - 1)
}
#[cold]
fn invalid_data_err(description: impl Into<Cow<'static, str>>) -> Error {
Error::new(ErrorKind::InvalidData).with_description(description)
}
#[cold]
fn out_of_memory_err(error: TryReserveError, description: impl Into<Cow<'static, str>>) -> Error {
Error::new(ErrorKind::Internal)
.with_description(description)
.with_source(Box::new(error))
}
#[inline]
fn span(start: usize, end: usize) -> Result<Range<u32>> {
u32::try_from(end)
.map(|end| start as u32..end)
.map_err(|_| invalid_data_err(format!("Failed to read strings, their {end} octets do not fit into 32 bits")))
}
#[cfg(test)]
mod tests {
use super::*;
const NON_NATIVE: Endian = match Endian::NATIVE {
Endian::Little => Endian::Big,
Endian::Big => Endian::Little,
};
const CODESETS: Option<Codesets> = Some(Codesets {
codeset: Codeset::Utf8,
wide_codeset: WideCodeset::Utf16,
});
fn aligned(bytes: &[u8]) -> Buf<u8, 1> {
Buf::from(bytes)
}
#[test]
fn logical_align() {
let mut bytes = vec![0u8; 16];
bytes[0] = 0xAA;
bytes[2..4].copy_from_slice(&0x1122u16.to_ne_bytes());
bytes[4..8].copy_from_slice(&0x33445566u32.to_ne_bytes());
bytes[8..16].copy_from_slice(&0x1122334455667788u64.to_ne_bytes());
for origin in [0, 2] {
let buf = aligned(&[&vec![0u8; origin][..], &bytes].concat());
let mut reader = CdrReader::new(&buf).substream(&buf.as_bytes()[origin..]);
assert_eq!(reader.read_octet().unwrap(), 0xAA);
assert_eq!(reader.read_ushort().unwrap(), 0x1122);
assert_eq!(reader.position(), 4);
assert_eq!(reader.read_ulong().unwrap(), 0x33445566);
assert_eq!(reader.read_ulonglong().unwrap(), 0x1122334455667788);
assert!(reader.is_empty());
}
}
#[test]
fn num_borrow() {
let values = [0x11223344u32, 0x55667788];
let bytes = values.map(u32::to_ne_bytes).concat();
let buf = aligned(&bytes);
let borrowed = CdrReader::new(&buf).read_ulong_slice(2).unwrap();
assert!(matches!(borrowed, Cow::Borrowed(_)));
assert_eq!(&*borrowed, &values);
let swapped = CdrReader::new(&buf).with_endian(NON_NATIVE).read_ulong_slice(2).unwrap();
assert!(matches!(swapped, Cow::Owned(_)));
assert_eq!(&*swapped, &values.map(u32::swap_bytes));
assert_eq!(buf.as_bytes(), &bytes[..]);
let buf = aligned(&[&[0u8; 2][..], &bytes].concat());
let copied = CdrReader::new(&buf).substream(&buf.as_bytes()[2..]).read_ulong_slice(2).unwrap();
assert!(matches!(copied, Cow::Owned(_)));
assert_eq!(&*copied, &values);
let buf = aligned(&[0, 1, 0]);
let mut reader = CdrReader::new(&buf).substream(&buf.as_bytes()[1..]).with_endian(NON_NATIVE);
assert!(matches!(reader.read_bool_slice(2).unwrap(), Cow::Borrowed(_)));
assert!(matches!(reader.read_octet_slice(0).unwrap(), Cow::Borrowed(_)));
}
#[test]
fn data_sharing() {
let values = [0x11223344u32, 0x55667788];
let bytes = values.map(u32::to_ne_bytes).concat();
let buf = aligned(&[&[0u8; 4][..], &bytes].concat());
let mut reader = CdrReader::new(&buf);
reader.skip(4).unwrap();
let shared = reader.read_buf::<u32>(2).unwrap();
assert_eq!(shared.as_bytes().as_ptr(), buf.as_bytes()[4..].as_ptr());
let mut reader = CdrReader::new(&buf).with_endian(NON_NATIVE);
reader.skip(4).unwrap();
let swapped = reader.read_buf::<u32>(2).unwrap();
assert_eq!(swapped.as_slice(), &values.map(u32::swap_bytes));
assert!(swapped.is_unique());
assert_eq!(&buf.as_bytes()[4..], &bytes[..]);
assert!(!CdrReader::new(&buf).substream(&buf.as_bytes()[4..]).read_buf::<u32>(2).unwrap().is_unique());
drop(buf);
assert_eq!(shared.as_slice(), &values);
}
#[test]
fn data_invalid() {
let buf = aligned(&[0, 1, 2]);
assert!(CdrReader::new(&buf).read_buf::<bool>(3).is_err());
assert!(CdrReader::new(&buf).read_buf::<bool>(2).is_ok());
}
#[test]
fn string_buf() {
let strings = Spectrum::<str>::from(&["ab", "", "cdé"][..]);
for (codesets, shared) in [(CODESETS, true), (None, false)] {
let mut writer = CdrWriter::new().with_codesets(codesets);
writer.write_string_data(&strings).unwrap();
writer.write_ulong(7).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf).with_codesets(codesets);
let data = reader.read_string_buf(3).unwrap();
assert_eq!(data, strings);
assert_eq!(data.is_unique(), !shared);
assert_eq!(reader.read_ulong().unwrap(), 7);
}
assert!(CdrReader::new(&aligned(&[0; 9])).read_string_buf(2).is_err());
}
#[test]
fn data_seq() {
let doubles = Spectrum::<f64>::from(&[1.5, -2.0][..]);
let strings = Spectrum::<str>::from(&["a", "bc"][..]);
let mut writer = CdrWriter::new();
writer.write(&(&doubles, &strings, &doubles)).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf);
assert_eq!(reader.read::<Spectrum<f64>>().unwrap(), doubles);
assert_eq!(reader.read::<Spectrum<str>>().unwrap(), strings);
assert_eq!(reader.read::<Spectrum<f64>>().unwrap(), doubles);
assert!(reader.is_empty());
}
#[test]
fn bool_bits() {
let buf = aligned(&[0, 1, 0xFF]);
let mut reader = CdrReader::new(&buf);
assert_eq!(&*reader.read_bool_slice(2).unwrap(), &[false, true]);
assert!(reader.read_bool().is_err());
}
#[test]
fn overflow() {
let buf = aligned(&[1, 2, 3, 4, 5, 6]);
let mut reader = CdrReader::new(&buf);
reader.read_octet().unwrap();
assert!(reader.read_ulong().is_err()); assert!(reader.read_ulong_slice(usize::MAX).is_err()); assert_eq!(reader.position(), 1);
assert_eq!(reader.read_ushort().unwrap(), u16::from_ne_bytes([3, 4]));
}
#[test]
fn clone_read() {
let buf = aligned(&0xDEADBEEFu32.to_ne_bytes());
let reader = CdrReader::new(&buf);
let mut ahead = reader.clone();
assert_eq!(ahead.read_ulong().unwrap(), 0xDEADBEEF);
assert_eq!(reader.position(), 0);
assert_eq!(reader.clone().read_ulong().unwrap(), 0xDEADBEEF);
}
#[test]
fn str_borrow() {
let buf = aligned(&[&7u32.to_ne_bytes()[..], b"h\xC3\xA9llo\0"].concat());
let s = CdrReader::new(&buf).with_codesets(CODESETS).read_string().unwrap();
assert!(matches!(s, Cow::Borrowed(_)));
assert_eq!(&*s, "héllo");
let latin1 = Some(Codesets { codeset: Codeset::Iso8859_1, ..CODESETS.unwrap() });
let s = CdrReader::new(&buf).with_codesets(latin1).read_string().unwrap();
assert!(matches!(s, Cow::Owned(_)));
assert_eq!(&*s, "héllo");
}
#[test]
fn str_nul() {
let buf = aligned(&[&0u32.to_ne_bytes()[..]].concat());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_string().is_err());
let buf = aligned(&[&5u32.to_ne_bytes()[..], b"hello"].concat());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_string().is_err());
let buf = aligned(&[&6u32.to_ne_bytes()[..], b"hello"].concat());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_string().is_err());
}
#[test]
fn wstr_octets() {
let units = [0x0068u16, 0x00E9, 0x006C, 0x006C, 0x006F]; let expected = Utf16String::from_str("héllo");
for tail in [&[0u16][..], &[]] {
let units = [&units[..], tail].concat();
let len = (units.len() * 2) as u32;
let mut bytes = len.to_ne_bytes().to_vec();
for unit in &units {
bytes.extend_from_slice(&unit.to_ne_bytes());
}
let buf = aligned(&bytes);
let w = CdrReader::new(&buf).with_codesets(CODESETS).read_wstring().unwrap();
assert!(matches!(w, Cow::Borrowed(_)));
assert_eq!(&*w, &*expected);
let mut swapped = len.swap_bytes().to_ne_bytes().to_vec();
for unit in &units {
swapped.extend_from_slice(&unit.swap_bytes().to_ne_bytes());
}
let buf = aligned(&swapped);
let w = CdrReader::new(&buf).with_endian(NON_NATIVE).with_codesets(CODESETS).read_wstring().unwrap();
assert!(matches!(w, Cow::Owned(_)));
assert_eq!(&*w, &*expected);
}
}
#[test]
fn wstr_codeset() {
let buf = aligned(&[&2u32.to_ne_bytes()[..], &0u16.to_ne_bytes()].concat());
assert!(CdrReader::new(&buf).read_wstring().is_err());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_wstring().unwrap().is_empty());
let buf = aligned(&0u32.to_ne_bytes());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_wstring().unwrap().is_empty());
let buf = aligned(&[&3u32.to_ne_bytes()[..], b"abc"].concat());
assert!(CdrReader::new(&buf).with_codesets(CODESETS).read_wstring().is_err());
}
#[test]
fn encaps_align() {
let mut inner = vec![Endian::NATIVE.is_little() as u8, 0xAA, 0, 0];
inner.extend_from_slice(&0xDEADBEEFu32.to_ne_bytes());
let mut outer = vec![0x2A, 0, 0, 0];
outer.extend_from_slice(&(inner.len() as u32).to_ne_bytes());
outer.extend_from_slice(&inner);
let buf = aligned(&outer);
let mut reader = CdrReader::new(&buf);
assert_eq!(reader.read_octet().unwrap(), 0x2A);
let mut encapsulation = reader.read_encapsulation().unwrap();
assert_eq!(encapsulation.endian(), Endian::NATIVE);
assert_eq!(encapsulation.position(), 1); assert_eq!(encapsulation.read_octet().unwrap(), 0xAA);
assert_eq!(encapsulation.read_ulong().unwrap(), 0xDEADBEEF);
assert!(encapsulation.is_empty());
assert!(reader.is_empty());
assert!(buf.as_bytes().as_ptr_range().contains(&encapsulation.buffer().as_ptr()));
}
#[test]
fn encaps_endian() {
let mut inner = vec![NON_NATIVE.is_little() as u8, 0, 0, 0];
inner.extend_from_slice(&0xDEADBEEFu32.swap_bytes().to_ne_bytes());
inner.extend_from_slice(&4u32.swap_bytes().to_ne_bytes());
inner.extend_from_slice(b"abc\0");
let mut outer = (inner.len() as u32).to_ne_bytes().to_vec();
outer.extend_from_slice(&inner);
outer.extend_from_slice(&0x11223344u32.to_ne_bytes());
let buf = aligned(&outer);
let mut reader = CdrReader::new(&buf).with_codesets(CODESETS);
let mut encapsulation = reader.read_encapsulation().unwrap();
assert_eq!(encapsulation.endian(), NON_NATIVE);
assert_eq!(encapsulation.read_ulong().unwrap(), 0xDEADBEEF);
assert_eq!(encapsulation.codesets(), None);
assert_eq!(encapsulation.codeset(), Codeset::Iso8859_1);
assert_eq!(&*encapsulation.read_string().unwrap(), "abc");
assert_eq!(reader.endian(), Endian::NATIVE);
assert_eq!(reader.read_ulong().unwrap(), 0x11223344);
}
#[test]
fn encaps_flag() {
let buf = aligned(&0u32.to_ne_bytes());
assert!(CdrReader::new(&buf).read_encapsulation().is_err());
let buf = aligned(&[&8u32.to_ne_bytes()[..], &[0; 4]].concat());
assert!(CdrReader::new(&buf).read_encapsulation().is_err());
let buf = aligned(&[&1u32.to_ne_bytes()[..], &[0xFF]].concat());
assert!(CdrReader::new(&buf).read_encapsulation().is_err());
}
#[test]
fn write_padding() {
let mut writer = CdrWriter::new();
writer.write_octet(0xFF).unwrap();
assert_eq!(writer.position(), 1);
writer.write_ulong(0x11223344).unwrap();
assert_eq!(writer.position(), 8);
writer.write_ulonglong(u64::MAX).unwrap();
assert_eq!(writer.position(), 16);
let buf = writer.into_buf().freeze();
assert_eq!(buf.len(), 16);
assert_eq!(&buf.as_bytes()[1..4], &[0, 0, 0]);
assert_eq!(&buf.as_bytes()[4..8], &0x11223344u32.to_ne_bytes());
}
#[test]
fn write_room_back() {
let codesets = Some(Codesets { codeset: Codeset::Iso8859_1, ..CODESETS.unwrap() });
let mut writer = CdrWriter::new().with_codesets(codesets);
writer.write_string("héllo").unwrap();
writer.write_octet(0xAA).unwrap();
let buf = writer.into_buf().freeze();
assert_eq!(buf.as_bytes(), [&6u32.to_ne_bytes()[..], b"h\xE9llo\0\xAA"].concat());
}
#[test]
fn write_room_shrink() {
let codesets = Some(Codesets { codeset: Codeset::Iso8859_1, ..CODESETS.unwrap() });
let mut writer = CdrWriter::new().with_codesets(codesets);
writer.write_string("héllo").unwrap();
assert_eq!(writer.position(), 10);
assert_eq!(writer.buf.len(), 10);
}
#[test]
fn write_rewind_tail() {
let codesets = Some(Codesets { codeset: Codeset::Iso8859_1, ..CODESETS.unwrap() });
let mut writer = CdrWriter::new().with_codesets(codesets);
writer.write_octet_slice(&[0xAA; 32]).unwrap();
writer.set_position(0);
writer.write_string("héllo").unwrap();
assert_eq!(writer.position(), 10);
assert_eq!(writer.buf.len(), 32);
assert_eq!(writer.buf.as_bytes()[10..], [0xAA; 22]);
}
#[test]
fn write_rewind_len() {
let mut writer = CdrWriter::new();
let len_at = writer.position();
writer.write_ulong(0).unwrap();
let start = writer.position();
writer.write_octet_slice(b"abc").unwrap();
let end = writer.position();
writer.set_position(len_at);
writer.write_ulong((end - start) as u32).unwrap();
writer.set_position(end);
let buf = writer.into_buf().freeze();
assert_eq!(buf.as_bytes(), [&3u32.to_ne_bytes()[..], b"abc"].concat());
}
#[test]
fn write_encaps() {
let mut writer = CdrWriter::new();
writer.write_octet(0x2A).unwrap();
writer.write_encapsulation(|encapsulation| {
assert_eq!(encapsulation.position(), 9);
encapsulation.write_octet(0xAA)?;
encapsulation.write_ulong(0xDEADBEEF)?;
assert_eq!(encapsulation.position(), 16);
Ok(())
}).unwrap();
let buf = writer.into_buf().freeze();
assert_eq!(buf.len(), 16);
assert_eq!(&buf.as_bytes()[4..8], &8u32.to_ne_bytes());
let mut reader = CdrReader::new(&buf);
assert_eq!(reader.read_octet().unwrap(), 0x2A);
let mut encapsulation = reader.read_encapsulation().unwrap();
assert_eq!(encapsulation.read_octet().unwrap(), 0xAA);
assert_eq!(encapsulation.read_ulong().unwrap(), 0xDEADBEEF);
assert!(encapsulation.is_empty());
assert!(reader.is_empty());
}
#[test]
fn write_encaps_failed() {
let wide = Utf16String::from_str("hello");
let mut writer = CdrWriter::new();
writer.write_ulonglong(1).unwrap();
let res = writer.write_encapsulation(|encapsulation| {
encapsulation.write_ulong(0xDEADBEEF)?;
encapsulation.write_wstring(&wide)
});
assert!(res.is_err());
assert_eq!(writer.position(), 8);
writer.write_ulonglong(2).unwrap();
let buf = writer.into_buf().freeze();
assert_eq!(buf.len(), 16);
assert_eq!(&*CdrReader::new(&buf).read_ulonglong_slice(2).unwrap(), &[1, 2]);
}
#[test]
fn write_recycle() {
let mut writer = CdrWriter::from_buf(BufMut::from(&[0xFF; 16][..]));
assert_eq!(writer.position(), 16);
writer.write_octet(1).unwrap();
writer.clear();
assert_eq!(writer.position(), 0);
writer.write_octet(1).unwrap();
writer.write_ulong(2).unwrap();
let buf = writer.into_buf().freeze();
assert_eq!(buf.as_bytes(), [&[1, 0, 0, 0][..], &2u32.to_ne_bytes()].concat());
}
#[test]
fn write_no_grow() {
let mut buf = BufMut::<u8, 1>::new();
assert!(buf.try_reserve(usize::MAX).is_err());
assert!(buf.is_empty());
let mut writer = CdrWriter::from_buf(BufMut::from(&[0xAA; 8][..]));
writer.set_position(0);
writer.write_ulonglong(1).unwrap();
assert_eq!(writer.into_buf().freeze().as_bytes(), &1u64.to_ne_bytes());
}
#[test]
fn write_encaps_nested() {
let mut writer = CdrWriter::new();
writer.write_encapsulation(|outer| {
outer.write_octet(0xAA)?;
outer.write_encapsulation(|inner| {
inner.write_octet(0xBB)?;
inner.write_ulong(0xDEADBEEF)
})?;
outer.write_ulong(0x11223344)
}).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf);
let mut outer = reader.read_encapsulation().unwrap();
assert_eq!(outer.read_octet().unwrap(), 0xAA);
let mut inner = outer.read_encapsulation().unwrap();
assert_eq!(inner.read_octet().unwrap(), 0xBB);
assert_eq!(inner.read_ulong().unwrap(), 0xDEADBEEF);
assert!(inner.is_empty());
assert_eq!(outer.read_ulong().unwrap(), 0x11223344);
assert!(outer.is_empty());
assert!(reader.is_empty());
}
#[test]
fn round_trip() {
let wide = Utf16String::from_str("héllo");
let mut writer = CdrWriter::new().with_codesets(CODESETS);
writer.write_endian().unwrap();
writer.write_octet(0x2A).unwrap();
writer.write_bool(true).unwrap();
writer.write_short(-2).unwrap();
writer.write_ulong(0xDEADBEEF).unwrap();
writer.write_ulonglong(0x1122334455667788).unwrap();
writer.write_double(1.5).unwrap();
writer.write_string("hello").unwrap();
writer.write_wstring(&wide).unwrap();
writer.write_sequence_len(3).unwrap();
writer.write_long_slice(&[-1, 0, 1]).unwrap();
writer.write_encapsulation(|encapsulation| {
assert_eq!(encapsulation.codesets(), None);
encapsulation.write_octet(0xAA)?;
encapsulation.write_float_slice(&[0.5, 1.5])?;
encapsulation.write_string("abc")
}).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf).with_codesets(CODESETS);
assert_eq!(reader.read_endian().unwrap(), Endian::NATIVE);
assert_eq!(reader.read_octet().unwrap(), 0x2A);
assert!(reader.read_bool().unwrap());
assert_eq!(reader.read_short().unwrap(), -2);
assert_eq!(reader.read_ulong().unwrap(), 0xDEADBEEF);
assert_eq!(reader.read_ulonglong().unwrap(), 0x1122334455667788);
assert_eq!(reader.read_double().unwrap(), 1.5);
assert_eq!(&*reader.read_string().unwrap(), "hello");
assert_eq!(&*reader.read_wstring().unwrap(), &*wide);
assert_eq!(reader.read_sequence_len().unwrap(), 3);
assert_eq!(&*reader.read_long_slice(3).unwrap(), &[-1, 0, 1]);
let mut encapsulation = reader.read_encapsulation().unwrap();
assert_eq!(encapsulation.read_octet().unwrap(), 0xAA);
assert_eq!(&*encapsulation.read_float_slice(2).unwrap(), &[0.5, 1.5]);
assert_eq!(&*encapsulation.read_string().unwrap(), "abc");
assert!(encapsulation.is_empty());
assert!(reader.is_empty());
}
#[test]
fn num_seq() {
let mut writer = CdrWriter::new().with_codesets(CODESETS);
writer.write::<[u32]>(&[1, 2, 3]).unwrap();
writer.write::<[&str]>(&["hello", "world"]).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf).with_codesets(CODESETS);
let numbers = reader.read::<Cow<'_, [u32]>>().unwrap();
assert!(matches!(numbers, Cow::Borrowed(_)));
assert_eq!(&*numbers, &[1, 2, 3]);
let strings = reader.read::<Cow<'_, [Cow<'_, str>]>>().unwrap();
assert!(matches!(strings, Cow::Owned(_)));
assert!(matches!(strings[0], Cow::Borrowed(_)));
assert_eq!(&*strings, &["hello", "world"]);
assert!(reader.is_empty());
}
#[test]
fn owned_write() {
let strings = vec!["hello".to_string(), "world".to_string()];
let cows = vec![Cow::Borrowed("hello"), Cow::Owned("world".to_string())];
let mut reference = CdrWriter::new().with_codesets(CODESETS);
reference.write::<[&str]>(&["hello", "world"]).unwrap();
let reference = reference.into_buf().freeze();
let mut writers: Vec<(&str, CdrWriter)> = Vec::new();
macro_rules! written {
($name:literal, $value:expr) => {{
let mut writer = CdrWriter::new().with_codesets(CODESETS);
writer.write($value).unwrap();
writers.push(($name, writer));
}};
}
written!("Vec<String>", &strings);
written!("Vec<Cow<str>>", &cows);
written!("[String]", &strings[..]);
written!("Box<[String]>", &strings.clone().into_boxed_slice());
written!("Arc<[String]>", &Arc::<[String]>::from(&strings[..]));
written!("&Vec<String>", &&strings);
for (name, writer) in writers {
assert_eq!(writer.buffer(), reference.as_bytes(), "{name} was not written like the borrowed form");
}
let mut reference = CdrWriter::new().with_codesets(CODESETS);
reference.write("hello").unwrap();
let reference = reference.into_buf().freeze();
for value in [&"hello".to_string() as &dyn CdrWrite, &Box::<str>::from("hello"), &Arc::<str>::from("hello")] {
let mut writer = CdrWriter::new().with_codesets(CODESETS);
writer.write(value).unwrap();
assert_eq!(writer.buffer(), reference.as_bytes());
}
}
#[test]
fn octet_seq() {
let mut bytes = 3u32.swap_bytes().to_ne_bytes().to_vec();
bytes.extend_from_slice(&[1, 2, 3]);
let buf = aligned(&bytes);
let mut reader = CdrReader::new(&buf).with_endian(NON_NATIVE);
assert_eq!(reader.read::<&[u8]>().unwrap(), &[1, 2, 3]);
let mut reader = CdrReader::new(&buf).with_endian(NON_NATIVE);
assert_eq!(reader.read::<&[u8]>().unwrap(), &[1, 2, 3]);
let mut bytes = 2u32.swap_bytes().to_ne_bytes().to_vec();
bytes.extend_from_slice(&[1, 2, 3, 4]);
let buf = aligned(&bytes);
let mut reader = CdrReader::new(&buf).with_endian(NON_NATIVE);
assert!(matches!(reader.read::<Cow<'_, [u16]>>().unwrap(), Cow::Owned(_)));
}
#[test]
fn tuples() {
let mut writer = CdrWriter::new().with_codesets(CODESETS);
writer.write(&(&[1u32, 2, 3][..], "hello", 0x2Au8)).unwrap();
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf).with_codesets(CODESETS);
let (values, name, tail) = reader.read::<(Cow<'_, [u32]>, Cow<'_, str>, u8)>().unwrap();
assert!(matches!(values, Cow::Borrowed(_)));
assert!(matches!(name, Cow::Borrowed(_)));
assert_eq!(&*values, &[1, 2, 3]);
assert_eq!(&*name, "hello");
assert_eq!(tail, 0x2A);
assert!(reader.is_empty());
}
#[test]
fn dyn_write() {
let values: [&dyn CdrWrite; 3] = [&0x2Au8, &0xDEADBEEFu32, &"hello"];
let mut writer = CdrWriter::new().with_codesets(CODESETS);
for value in values {
writer.write(value).unwrap();
}
let buf = writer.into_buf().freeze();
let mut reader = CdrReader::new(&buf).with_codesets(CODESETS);
assert_eq!(reader.read::<u8>().unwrap(), 0x2A);
assert_eq!(reader.read::<u32>().unwrap(), 0xDEADBEEF);
assert_eq!(&*reader.read::<Cow<'_, str>>().unwrap(), "hello");
}
#[test]
fn owned_read() {
let mut writer = CdrWriter::new();
writer.write::<[i32]>(&[-1, 0, 1]).unwrap();
let buf = writer.into_buf().freeze();
assert_eq!(CdrReader::new(&buf).read::<Vec<i32>>().unwrap(), vec![-1, 0, 1]);
assert_eq!(&*CdrReader::new(&buf).read::<Box<[i32]>>().unwrap(), &[-1, 0, 1]);
}
#[test]
fn sender_partial_ack() {
let mut sender = CdrWriter::new().into_sender();
sender.write_octet_slice(&[0xAA; 100]).unwrap();
let mut out = Vec::new();
while !sender.buffer().is_empty() {
let taken = usize::min(3, sender.buffer().len());
out.extend_from_slice(&sender.buffer()[..taken]);
sender.ack(taken);
}
assert_eq!(out, [0xAA; 100]);
}
#[test]
fn sender_interleave() {
let mut sender = CdrWriter::new().into_sender();
sender.write_octet_slice(&[0xAA; 8]).unwrap();
sender.interleave(aligned(&[0xBB; 8]));
sender.write_octet_slice(&[0xCC; 8]).unwrap();
let mut out = Vec::new();
while !sender.buffer().is_empty() {
let taken = usize::min(3, sender.buffer().len());
out.extend_from_slice(&sender.buffer()[..taken]);
sender.ack(taken);
}
assert_eq!(out, [&[0xAA; 8][..], &[0xBB; 8], &[0xCC; 8]].concat());
}
#[test]
fn sender_ack_spans_runs() {
let mut sender = CdrWriter::new().into_sender();
sender.write_octet_slice(&[0xAA; 8]).unwrap();
sender.interleave(aligned(&[0xBB; 8]));
sender.write_octet_slice(&[0xCC; 8]).unwrap();
sender.ack(20);
assert_eq!(sender.buffer(), [0xCC; 4]);
}
#[test]
fn sender_drop_off() {
let mut sender = CdrWriter::new().into_sender();
sender.write_octet_slice(&[0xAA; 8]).unwrap();
sender.interleave(aligned(&[0xBB; 8]));
sender.write_octet_slice(&[0xCC; 8]).unwrap();
sender.ack(4);
sender.drop_off_origin();
let mut out = Vec::new();
while !sender.buffer().is_empty() {
let taken = sender.buffer().len();
out.extend_from_slice(&sender.buffer()[..taken]);
sender.ack(taken);
}
assert_eq!(out, [&[0xAA; 4][..], &[0xBB; 8], &[0xCC; 8]].concat());
}
#[test]
#[should_panic]
fn sender_over_ack() {
let mut sender = CdrWriter::new().into_sender();
sender.write_octet_slice(&[0xAA; 8]).unwrap();
sender.ack(9);
}
}