refactor(core): move Encode/Decode to core

ironrdp-pdu contains lots of code that we don’t actually need in other crates such as the virtual channels.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2024-08-27 11:18:49 +04:00 committed by Benoît Cortier
parent ab5760d47b
commit 402ffd56c9
197 changed files with 575 additions and 457 deletions

View file

@ -3,13 +3,15 @@ use alloc::string::String;
use core::fmt;
use crate::{
InvalidFieldErr, NotEnoughBytesErr, OtherErr, UnexpectedMessageTypeErr, UnsupportedValueErr, UnsupportedVersionErr,
InvalidFieldErr, NotEnoughBytesErr, OtherErr, ReadCursor, UnexpectedMessageTypeErr, UnsupportedValueErr,
UnsupportedVersionErr,
};
/// Result type for decode operations, wrapping a value or a DecodeError.
/// A result type for decoding operations, which can either succeed with a value of type `T`
/// or fail with an [`DecodeError`].
pub type DecodeResult<T> = Result<T, DecodeError>;
/// Custom error type for decode operations.
/// An error type specifically for encoding operations, wrapping an [`DecodeErrorKind`].
pub type DecodeError = ironrdp_error::Error<DecodeErrorKind>;
/// Enum representing different kinds of decode errors.
@ -135,3 +137,111 @@ impl OtherErr for DecodeError {
Self::new(context, DecodeErrorKind::Other { description })
}
}
/// Trait for types that can be decoded from a byte stream.
///
/// This trait is implemented by types that can be deserialized from a sequence of bytes.
pub trait Decode<'de>: Sized {
/// Decodes an instance of `Self` from the given byte stream.
///
/// # Arguments
///
/// * `src` - A mutable reference to a `ReadCursor` containing the bytes to decode.
///
/// # Returns
///
/// Returns a `DecodeResult<Self>`, which is either the successfully decoded instance
/// or a `DecodeError` if decoding fails.
fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self>;
}
/// Decodes a value of type `T` from a byte slice.
///
/// This function creates a `ReadCursor` from the input byte slice and uses it to decode
/// a value of type `T` that implements the `Decode` trait.
///
/// # Arguments
///
/// * `src` - A byte slice containing the data to be decoded.
///
/// # Returns
///
/// Returns a `DecodeResult<T>`, which is either the successfully decoded value
/// or a `DecodeError` if decoding fails.
pub fn decode<'de, T>(src: &'de [u8]) -> DecodeResult<T>
where
T: Decode<'de>,
{
let mut cursor = ReadCursor::new(src);
T::decode(&mut cursor)
}
/// Decodes a value of type `T` from a `ReadCursor`.
///
/// This function uses the provided `ReadCursor` to decode a value of type `T`
/// that implements the `Decode` trait.
///
/// # Arguments
///
/// * `src` - A mutable reference to a `ReadCursor` containing the bytes to be decoded.
///
/// # Returns
///
/// Returns a `DecodeResult<T>`, which is either the successfully decoded value
/// or a `DecodeError` if decoding fails.
pub fn decode_cursor<'de, T>(src: &mut ReadCursor<'de>) -> DecodeResult<T>
where
T: Decode<'de>,
{
T::decode(src)
}
/// Similar to `Decode` but unconditionally returns an owned type.
pub trait DecodeOwned: Sized {
/// Decodes an instance of `Self` from the given byte stream.
///
/// # Arguments
///
/// * `src` - A mutable reference to a `ReadCursor` containing the bytes to decode.
///
/// # Returns
///
/// Returns a `DecodeResult<Self>`, which is either the successfully decoded instance
/// or a `DecodeError` if decoding fails.
fn decode_owned(src: &mut ReadCursor<'_>) -> DecodeResult<Self>;
}
/// Decodes an owned value of type `T` from a byte slice.
///
/// This function creates a `ReadCursor` from the input byte slice and uses it to decode
/// an owned value of type `T` that implements the `DecodeOwned` trait.
///
/// # Arguments
///
/// * `src` - A byte slice containing the data to be decoded.
///
/// # Returns
///
/// Returns a `DecodeResult<T>`, which is either the successfully decoded owned value
/// or a `DecodeError` if decoding fails.
pub fn decode_owned<T: DecodeOwned>(src: &[u8]) -> DecodeResult<T> {
let mut cursor = ReadCursor::new(src);
T::decode_owned(&mut cursor)
}
/// Decodes an owned value of type `T` from a `ReadCursor`.
///
/// This function uses the provided `ReadCursor` to decode an owned value of type `T`
/// that implements the `DecodeOwned` trait.
///
/// # Arguments
///
/// * `src` - A mutable reference to a `ReadCursor` containing the bytes to be decoded.
///
/// # Returns
///
/// Returns a `DecodeResult<T>`, which is either the successfully decoded owned value
/// or a `DecodeError` if decoding fails.
pub fn decode_owned_cursor<T: DecodeOwned>(src: &mut ReadCursor<'_>) -> DecodeResult<T> {
T::decode_owned(src)
}

View file

@ -1,16 +1,21 @@
#[cfg(feature = "alloc")]
use crate::WriteBuf;
#[cfg(feature = "alloc")]
use alloc::string::String;
#[cfg(feature = "alloc")]
use alloc::{vec, vec::Vec};
use core::fmt;
use crate::{
InvalidFieldErr, NotEnoughBytesErr, OtherErr, UnexpectedMessageTypeErr, UnsupportedValueErr, UnsupportedVersionErr,
WriteCursor,
};
/// A result type for encoding operations, which can either succeed with a value of type `T`
/// or fail with an `EncodeError`.
/// or fail with an [`EncodeError`].
pub type EncodeResult<T> = Result<T, EncodeError>;
/// An error type specifically for encoding operations, wrapping an `EncodeErrorKind`.
/// An error type specifically for encoding operations, wrapping an [`EncodeErrorKind`].
pub type EncodeError = ironrdp_error::Error<EncodeErrorKind>;
/// Represents the different kinds of errors that can occur during encoding operations.
@ -136,3 +141,106 @@ impl OtherErr for EncodeError {
Self::new(context, EncodeErrorKind::Other { description })
}
}
/// PDU that can be encoded into its binary form.
///
/// The resulting binary payload is a fully encoded PDU that may be sent to the peer.
///
/// This trait is object-safe and may be used in a dynamic context.
pub trait Encode {
/// Encodes this PDU in-place using the provided `WriteCursor`.
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()>;
/// Returns the associated PDU name associated.
fn name(&self) -> &'static str;
/// Computes the size in bytes for this PDU.
fn size(&self) -> usize;
}
crate::assert_obj_safe!(Encode);
/// Encodes the given PDU in-place into the provided buffer and returns the number of bytes written.
pub fn encode<T>(pdu: &T, dst: &mut [u8]) -> EncodeResult<usize>
where
T: Encode + ?Sized,
{
let mut cursor = WriteCursor::new(dst);
encode_cursor(pdu, &mut cursor)?;
Ok(cursor.pos())
}
/// Encodes the given PDU in-place using the provided `WriteCursor`.
pub fn encode_cursor<T>(pdu: &T, dst: &mut WriteCursor<'_>) -> EncodeResult<()>
where
T: Encode + ?Sized,
{
pdu.encode(dst)
}
/// Same as `encode` but resizes the buffer when it is too small to fit the PDU.
#[cfg(feature = "alloc")]
pub fn encode_buf<T>(pdu: &T, buf: &mut WriteBuf) -> EncodeResult<usize>
where
T: Encode + ?Sized,
{
let pdu_size = pdu.size();
let dst = buf.unfilled_to(pdu_size);
let written = encode(pdu, dst)?;
debug_assert_eq!(written, pdu_size);
buf.advance(written);
Ok(written)
}
/// Same as `encode` but allocates and returns a new buffer each time.
///
/// This is a convenience function, but its not very resource efficient.
#[cfg(any(feature = "alloc", test))]
pub fn encode_vec<T>(pdu: &T) -> EncodeResult<Vec<u8>>
where
T: Encode + ?Sized,
{
let pdu_size = pdu.size();
let mut buf = vec![0; pdu_size];
let written = encode(pdu, buf.as_mut_slice())?;
debug_assert_eq!(written, pdu_size);
Ok(buf)
}
/// Gets the name of this PDU.
pub fn name<T: Encode>(pdu: &T) -> &'static str {
pdu.name()
}
/// Computes the size in bytes for this PDU.
pub fn size<T: Encode>(pdu: &T) -> usize {
pdu.size()
}
#[cfg(feature = "alloc")]
pub use legacy::*;
#[cfg(feature = "alloc")]
mod legacy {
use super::{Encode, EncodeResult};
use crate::WriteCursor;
impl Encode for alloc::vec::Vec<u8> {
fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
ensure_size!(in: dst, size: self.len());
dst.write_slice(self);
Ok(())
}
/// Returns the associated PDU name associated.
fn name(&self) -> &'static str {
"legacy-pdu-encode"
}
/// Computes the size in bytes for this PDU.
fn size(&self) -> usize {
self.len()
}
}
}