1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use std::io::{Cursor, Read, Write};

#[cfg(feature = "verbose")]
pub use log::debug;

#[doc(hidden)]
/// Allows for compile time assertions in the generated derive code
pub use static_assertions as sa;

#[cfg(test)]
mod tests;

mod error;
pub use error::*;

mod default_impls;
pub use default_impls::*;

mod helpers;
pub use helpers::*;

pub use simple_parse_derive::*;

/// A context passed around [SpRead] and [SpWrite] functions
pub struct SpCtx {
    /// How many bytes have been read/written so far
    pub cursor: usize,
    /// This value should only be checked inside custom validators (which get called for both Read & Write)
    /// Its contents are considered invalid otherwise
    pub is_reading: bool,
    /// If the abitrary input/output bytes should be treated as little endian
    pub is_little_endian: bool,
    /// If a dynamically sized Self uses an external count field, and what its contents are
    pub count: Option<usize>,
}
impl Default for SpCtx {
    fn default() -> Self {
        Self {
            cursor: 0,
            is_reading: true,
            is_little_endian: cfg!(target_endian = "little"),
            count: None,
        }
    }
}

#[doc(hidden)]
/// This type is used for dynamically sized type's default implementations
pub type DefaultCountType = u32;

#[doc(hidden)]
/// This is a safeguard against reading malicious/malformed dynamically sized types.
/// For example, when reading a String that says it contains INT_MAX characters, chunks of
/// MAX_ALLOC_SIZE will be read at a time instead of allocating INT_MAX bytes in one go.
pub const MAX_ALLOC_SIZE: usize = 4096;

/// Provides optimization hints used by [SpRead] traits.
///
/// # Safety
/// When not using defaults, implementors must be very careful not to return invalid values
/// as it may lead to buffer over reads (e.g. setting `STATIC_SIZE` to 4 and reading 8 bytes in the `unchecked` readers)
pub unsafe trait SpOptHints {
    /// Whether the type has a variable size
    const IS_VAR_SIZE: bool = true;
    /// How many bytes the `unchecked` parsing functions can assume are valid
    const STATIC_SIZE: usize = 0;
    /// Used to substract from STATIC_SIZE when a count is provided
    const COUNT_SIZE: usize = 0;
}

/// Parses `Self` from a source implementing [Read](std::io::Read) ([File](std::fs::File),[TcpStream](std::net::TcpStream), etc...)
///
/// This trait is most usefull when the bytes are coming from some kind of IO stream.
/// When possible, it is recommend to use [SpReadRaw] instead for better performance.
pub trait SpRead {
    /// Converts bytes from a [Reader](std::io::Read) into `Self`
    ///
    /// This functions allows specifying endianness and count fields as opposed to using defaults with `from_reader`
    fn inner_from_reader<R: Read + ?Sized>(
        src: &mut R,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `&mut Read` into `Self` with some assumptions on checked_bytes
    ///
    /// # Safety
    /// This function assumes that checked_bytes points to at least Self::STATIC_SIZE bytes.
    ///
    /// If this is implemented on a dynamic type, the implementors MUST check if count is provided.
    /// If it is provided, Self::COUNT_SIZE less bytes can be trusted from checked_bytes.
    unsafe fn inner_from_reader_unchecked<R: Read + ?Sized>(
        checked_bytes: *mut u8,
        src: &mut R,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `&mut Read` into `Self`
    fn from_reader<R: Read + ?Sized>(src: &mut R) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints,
    {
        Self::inner_from_reader(src, &mut SpCtx::default())
    }
}

/// Parses `Self` from a [Cursor<&\[u8\]>](std::io::Cursor)
pub trait SpReadRaw<'b> {
    /// Converts bytes from a `Cursor<&[u8]>` into `Self`
    ///
    /// This functions allows specifying endianness and count fields as opposed to using defaults with `from_slice`
    fn inner_from_slice(
        src: &mut Cursor<&'b [u8]>,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `Cursor<&[u8]>` into `Self` with some assumptions on checked_bytes
    ///
    /// # Safety
    /// This function assumes that checked_bytes points to at least Self::STATIC_SIZE bytes.
    ///
    /// If this is implemented on a dynamic type, the implementors MUST check if count is provided.
    /// If it is provided, Self::COUNT_SIZE less bytes can be trusted from checked_bytes.
    ///
    /// This function also allows returning references into the `Cursor<&[u8]>` when `Self` is a reference `&T`.
    /// This should not be done if `Self` itself contains non-primitive types, references, slices, etc...
    unsafe fn inner_from_slice_unchecked(
        checked_bytes: *const u8,
        src: &mut Cursor<&'b [u8]>,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `Cursor<&[u8]>` into `Self`
    fn from_slice(src: &mut Cursor<&'b [u8]>) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints,
    {
        Self::inner_from_slice(src, &mut SpCtx::default())
    }
}

/// Parses `Self` from a [Cursor<&mut \[u8\]>](std::io::Cursor)
pub trait SpReadRawMut<'b> {
    /// Converts bytes from a `Cursor<&mut [u8]>` into `Self`
    ///
    /// This functions allows specifying endianness and count fields as opposed to using defaults with `from_slice`
    fn inner_from_mut_slice(
        src: &mut Cursor<&'b mut [u8]>,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `Cursor<&mut [u8]>` into `Self` with some assumptions on checked_bytes
    ///
    /// # Safety
    /// This function assumes that checked_bytes points to at least Self::STATIC_SIZE bytes.
    ///
    /// If this is implemented on a dynamic type, the implementors MUST check if count is provided.
    /// If it is provided, Self::COUNT_SIZE less bytes can be trusted from checked_bytes.
    ///
    /// This function also allows returning references into the `Cursor<&[u8]>` when `Self` is a reference `&T` or `&mut T`.
    /// This should not be done if `Self` itself contains non-primitive types, references, slices, etc...
    unsafe fn inner_from_mut_slice_unchecked(
        checked_bytes: *mut u8,
        src: &mut Cursor<&'b mut [u8]>,
        ctx: &mut SpCtx,
    ) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints;

    /// Converts bytes from a `Cursor<&mut [u8]>` into `Self`
    fn from_mut_slice(src: &mut Cursor<&'b mut [u8]>) -> Result<Self, crate::SpError>
    where
        Self: Sized + SpOptHints,
    {
        Self::inner_from_mut_slice(src, &mut SpCtx::default())
    }
}

/// Writes `T` into a [Writer](std::io::Write) ([File](std::fs::File),[TcpStream](std::net::TcpStream), etc...)
pub trait SpWrite {
    /// Writes the byte representation for Self into a `&mut Write` with control over endianness
    fn inner_to_writer<W: Write + ?Sized>(
        &self,
        ctx: &mut SpCtx,
        dst: &mut W,
    ) -> Result<usize, crate::SpError>;

    /// Writes the byte representation for Self into a `&mut Write`
    fn to_writer<W: Write + ?Sized>(&self, dst: &mut W) -> Result<usize, crate::SpError> {
        self.inner_to_writer(&mut SpCtx::default(), dst)
    }
}