diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/decodable.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/decodable.rs index 1ec7037f7a..84f99cc2ba 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/decodable.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/decodable.rs @@ -10,13 +10,28 @@ use core::convert::TryFrom; #[cfg(not(feature = "no_std"))] use std::io::{Cursor, Read}; -/// Implmented by all the decodable structure, it can be derived for every structure composed only -/// by primitives or other Decodable. +/// Custom deserialization of types from binary data. +/// +/// Defines the process of reconstructing a type from a sequence of bytes. It handles both simple +/// and nested or complex data structures. pub trait Decodable<'a>: Sized { + /// Defines the expected structure of a type based on binary data. + /// + /// Returns a vector of [`FieldMarker`]s, each representing a component of the structure. + /// Useful for guiding the decoding process. fn get_structure(data: &[u8]) -> Result, Error>; + /// Constructs the type from a vector of decoded fields. + /// + /// After the data has been split into fields, this method combines those fields + /// back into the original type, handling nested structures or composite fields. fn from_decoded_fields(data: Vec>) -> Result; + /// Decodes the type from raw bytes. + /// + /// Orchestrates the decoding process, calling `get_structure` to break down + /// the raw data, decoding each field, and then using `from_decoded_fields` to reassemble + /// the fields into the original type. fn from_bytes(data: &'a mut [u8]) -> Result { let structure = Self::get_structure(data)?; let mut fields = Vec::new(); @@ -34,6 +49,10 @@ pub trait Decodable<'a>: Sized { Self::from_decoded_fields(fields) } + /// Converts a readable input to self representation. + /// + /// Reads data from an input which implements [`std::ioRead`] and constructs the original struct + /// out of it. #[cfg(not(feature = "no_std"))] fn from_reader(reader: &mut impl Read) -> Result { let mut data = Vec::new(); @@ -51,7 +70,10 @@ pub trait Decodable<'a>: Sized { } } -/// Passed to a decoder to define the structure of the data to be decoded +// Primitive data marker. +// +// Fundamental data types that can be passed to a decoder to define the structure of the type to be +// decoded in a standardized way. #[derive(Debug, Clone, Copy)] pub enum PrimitiveMarker { U8, @@ -71,17 +93,31 @@ pub enum PrimitiveMarker { B016M, } -/// Passed to a decoder to define the structure of the data to be decoded +/// Recursive enum representing data structure fields. +/// +/// A `FieldMarker` can either be a primitive or a nested structure. The marker helps the decoder +/// understand the layout and type of each field in the data, guiding the decoding process. #[derive(Debug, Clone)] pub enum FieldMarker { + /// A primitive data type. Primitive(PrimitiveMarker), + + /// A structured type composed of multiple fields, allowing for nested data. Struct(Vec), } + +/// Trait for retrieving the [`FieldMarker`] associated with a type. +/// +/// Provides a standardized way to retrieve a `FieldMarker` for a type, allowing the protocol to +/// identify the structure and layout of data fields during decoding. pub trait GetMarker { + /// Defines the structure of a type for decoding purposes, supporting both primitive and + /// structured types. It helps getting a marker for a type. fn get_marker() -> FieldMarker; } -/// Used to contrustuct primitives is returned by the decoder +// Represents a list of decode-able primitive data types. +// #[derive(Debug)] pub enum DecodablePrimitive<'a> { U8(u8), @@ -101,15 +137,24 @@ pub enum DecodablePrimitive<'a> { B016M(B016M<'a>), } -/// Used to contrustuct messages is returned by the decoder +/// Recursive enum representing a Decode-able field. +/// +/// May be primitive or a nested struct. +/// +/// Once the raw data is decoded, it is either classified as a primitive (e.g., integer, Boolean) +/// or a struct, which may itself contain multiple decoded fields. This type encapsulates that +/// distinction. #[derive(Debug)] pub enum DecodableField<'a> { + /// Primitive field. Primitive(DecodablePrimitive<'a>), + + /// Structured field, allowing for nested data structures. Struct(Vec>), } impl SizeHint for PrimitiveMarker { - // PrimitiveMarker need introspection to return a size hint. This method is not implementeable + // PrimitiveMarker needs introspection to return a size hint. This method is not implementable. fn size_hint(_data: &[u8], _offset: usize) -> Result { unimplemented!() } @@ -203,6 +248,9 @@ impl<'a> From> for Vec> { } impl PrimitiveMarker { + // Decodes a primitive value from a byte slice at the given offset, returning the corresponding + // `DecodablePrimitive`. The specific decoding logic depends on the type of the primitive (e.g., + // `u8`, `u16`, etc.). fn decode<'a>(&self, data: &'a mut [u8], offset: usize) -> DecodablePrimitive<'a> { match self { Self::U8 => DecodablePrimitive::U8(u8::from_bytes_unchecked(&mut data[offset..])), @@ -235,9 +283,11 @@ impl PrimitiveMarker { } } + // Decodes a primitive value from a reader stream, returning the corresponding + // `DecodablePrimitive`. This is useful when reading data from a file or network socket, + // where the data is not immediately available as a slice but must be read incrementally. #[allow(clippy::wrong_self_convention)] #[cfg(not(feature = "no_std"))] - #[allow(clippy::wrong_self_convention)] fn from_reader<'a>(&self, reader: &mut impl Read) -> Result, Error> { match self { Self::U8 => Ok(DecodablePrimitive::U8(u8::from_reader_(reader)?)), @@ -288,6 +338,10 @@ impl<'a> GetSize for DecodablePrimitive<'a> { } impl FieldMarker { + // Implements the decoding functionality for a `FieldMarker`. + // Depending on whether the field is primitive or structured, this method decodes the + // corresponding data. If the field is a structure, it recursively decodes each nested field + // and returns the resulting `DecodableField`. pub(crate) fn decode<'a>(&self, data: &'a mut [u8]) -> Result, Error> { match self { Self::Primitive(p) => Ok(DecodableField::Primitive(p.decode(data, 0))), diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/encodable.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/encodable.rs index 70be94ecbd..69c0cc5502 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/encodable.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/encodable.rs @@ -9,16 +9,45 @@ use alloc::vec::Vec; #[cfg(not(feature = "no_std"))] use std::io::{Error as E, Write}; +/// The `Encodable` trait defines the interface for encoding a type into bytes. +/// +/// The trait provides methods for serializing an instance of a type into a byte +/// array or writing it directly into an output writer. The trait is flexible, +/// allowing various types, including primitives, structures, and collections, +/// to implement custom serialization logic. +/// +/// The trait offers two key methods for encoding: +/// +/// - The first, `to_bytes`, takes a mutable byte slice as a destination buffer. This method encodes +/// the object directly into the provided buffer, returning the number of bytes written or an +/// error if the encoding process fails. +/// - The second, `to_writer`, (only available when not compiling for `no-std`) accepts a writer as +/// a destination for the encoded bytes, allowing the serialized data to be written to any +/// implementor of the `Write` trait. +/// +/// Implementing types can define custom encoding logic, and this trait is +/// especially useful when dealing with different data structures that need +/// to be serialized for transmission. pub trait Encodable { + /// Encodes the object into the provided byte slice. + /// + /// The method uses the destination buffer `dst` to write the serialized + /// bytes. It returns the number of bytes written on success or an `Error` + /// if encoding fails. #[allow(clippy::wrong_self_convention)] fn to_bytes(self, dst: &mut [u8]) -> Result; + /// Write the encoded object into the provided writer. + /// + /// Serializes the object and writes it directly + /// to the `dst` writer. It is only available in environments + /// where `std` is available. If the encoding fails, error is + /// returned. #[cfg(not(feature = "no_std"))] #[allow(clippy::wrong_self_convention)] fn to_writer(self, dst: &mut impl Write) -> Result<(), E>; } -// impl<'a, T: Into>> Encodable for T { #[allow(clippy::wrong_self_convention)] fn to_bytes(self, dst: &mut [u8]) -> Result { @@ -34,27 +63,53 @@ impl<'a, T: Into>> Encodable for T { } } +/// The `EncodablePrimitive` enum defines primitive types that can be encoded. +/// +/// The enum represents various data types, such a integers, bool, and byte array +/// that can be encoded into a byte representation. Each variant holds a specific +/// type, and encoding logic is provided through the `encode` method. #[derive(Debug)] pub enum EncodablePrimitive<'a> { + /// U8 Primitive, representing a byte U8(u8), + /// Owned U8 Primitive, representing an owned byte OwnedU8(u8), + /// U16 Primitive, representing a u16 type U16(u16), + /// Bool Primitive, representing a bool type Bool(bool), + /// U24 Primitive, representing a U24 type U24(U24), + /// U256 Primitive, representing a U256 type U256(U256<'a>), + /// ShortTxId Primitive, representing a ShortTxId type ShortTxId(ShortTxId<'a>), + /// Signature Primitive, representing a Signature type Signature(Signature<'a>), + /// U32 Primitive, representing a u32 type U32(u32), + /// U32AsRef Primitive, representing a U32AsRef type U32AsRef(U32AsRef<'a>), + /// F32 Primitive, representing a f32 type F32(f32), + /// U64 Primitive, representing a u64 type U64(u64), + /// B032 Primitive, representing a B032 type B032(B032<'a>), + /// B0255 Primitive, representing a B0255 type B0255(B0255<'a>), + /// B064K Primitive, representing a B064K type B064K(B064K<'a>), + /// B016M Primitive, representing a B016M type B016M(B016M<'a>), } impl<'a> EncodablePrimitive<'a> { + // Provides the encoding logic for each primitive type. + // + // The `encode` method takes the `EncodablePrimitive` variant and serializes it + // into the destination buffer `dst`. The method returns the number of bytes written + // . If the buffer is too small or encoding fails, it returns an error. fn encode(&self, dst: &mut [u8]) -> Result { match self { Self::U8(v) => v.to_slice(dst), @@ -76,6 +131,11 @@ impl<'a> EncodablePrimitive<'a> { } } + // Write the encoded object into the provided writer. + // + // Serializes the object and writes it directly to the + // provided writer. It is only available in environments where `std` + // is available. #[cfg(not(feature = "no_std"))] pub fn write(&self, writer: &mut impl Write) -> Result<(), E> { match self { @@ -99,6 +159,7 @@ impl<'a> EncodablePrimitive<'a> { } } +// Provides the logic for calculating the size of the encodable field. impl<'a> GetSize for EncodablePrimitive<'a> { fn get_size(&self) -> usize { match self { @@ -122,13 +183,28 @@ impl<'a> GetSize for EncodablePrimitive<'a> { } } +/// The [`EncodableField`] enum defines encodable fields, which may be a primitive or struct. +/// +/// Each [`EncodableField`] represents either a primitive value or a collection of values +/// (a struct). The encoding process for [`EncodableField`] supports nesting, allowing +/// for complex hierarchical data structures to be serialized. #[derive(Debug)] pub enum EncodableField<'a> { + /// Represents a primitive value + /// + /// For the full supported list please see [`EncodablePrimitive`] Primitive(EncodablePrimitive<'a>), + /// Represents a struct like field structure. + /// + /// Note that this is a recursive enum type. Struct(Vec>), } impl<'a> EncodableField<'a> { + /// The `encode` method serializes a field into the destination buffer `dst`, starting + /// at the provided `offset`. If the field is a structure, it recursively encodes + /// each contained field. If the buffer is too small or encoding fails, the method + /// returns an error. pub fn encode(&self, dst: &mut [u8], mut offset: usize) -> Result { match (self, dst.len() >= offset) { (Self::Primitive(p), true) => p.encode(&mut dst[offset..]), diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/mod.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/mod.rs index a26ae8e72f..32a3e0847e 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/mod.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/codec/mod.rs @@ -1,9 +1,63 @@ -// At lower level I generally prefer to work with slices as more efficient than Read/Write streams -// eg: Read for & [u8] is implemented with memcpy but here is more than enough to just get a -// pointer to the original data - -// ANche se enum decode sarebbe faclie da implementare non viene fatto dato che ogni messaggio puo -// essere derivato dal suo numero! +// Provides traits and utilities for encoding and decoding data at a low level, +// prioritizing efficiency and memory safety by operating directly on slices rather than +// relying on `Read` or `Write` streams. +// +// ## Overview +// +// Optimized for performance, this module directly manipulates byte slices, avoiding +// the overhead of stream-based I/O. It uses memory-efficient techniques like obtaining +// pointers to original data instead of copying it. Enums are avoided for decoding, as +// each message type can be identified by its numeric identifier, streamlining the process. +// +// ### Key Components +// +// - **Traits**: Defines core traits (`SizeHint`, `GetSize`, `Fixed`, `Variable`) that establish a +// consistent interface for encoding and decoding operations. +// - **Buffer Management**: With the `with_buffer_pool` feature enabled, the `Slice` type from +// `buffer_sv2` is included, supporting memory pooling and efficient slice handling for +// high-performance buffer management scenarios. +// +// ### Traits Overview +// +// - **`SizeHint`**: Estimates the size of a decodable type, useful for variable-length data where +// the size must be determined dynamically. +// - **`GetSize`**: Provides the exact size of an encodable type in bytes, crucial for buffer +// allocation. +// - **`Fixed`**: Marks types with a compile-time constant size, simplifying encoding and decoding. +// - **`Variable`**: For types with dynamic sizes, manages size variability and calculates inner +// sizes. +// +// ## Build Options +// +// - **`no_std` Compatibility**: This module can be compiled without the standard library for +// constrained environments. Some methods and traits are conditionally available when `std` is +// included. +// - **`with_buffer_pool`**: When enabled, includes the `Slice` type for managing pooled memory +// slices, improving memory handling and efficiency in high-performance scenarios. +// +// ## Detailed Trait Descriptions +// +// ### `SizeHint` +// Defines methods to calculate the size of encoded data for types with variable sizes. +// - **`size_hint`**: Returns the total size of the encoded data for raw data and an offset. +// - **`size_hint_`**: Returns the size for a specific instance, offering flexibility. +// +// ### `GetSize` +// Provides a `get_size` method that returns the exact size in bytes of an encodable type. +// +// ### `Fixed` +// For types with a fixed size, this trait defines a constant `SIZE`, simplifying work with +// fixed-size types. +// +// ### `Variable` +// Types with variable sizes implement this trait, providing constants (`HEADER_SIZE`, `MAX_SIZE`) +// and methods for size management and inner size calculation. +// +// ## Summary +// +// This module supports efficient, low-level encoding and decoding by operating directly on slices, +// avoiding excess data copying. It offers capabilities for both fixed and variable-sized data, +// making it versatile for a wide range of encoding tasks. use crate::Error; pub mod decodable; pub mod encodable; @@ -13,14 +67,28 @@ use buffer_sv2::Slice; use alloc::vec::Vec; -/// Return the encoded byte size or a `Decodable` +/// The `SizeHint` trait provides a mechanism to return the encoded bytes size of a decodable type. +/// +/// It defines two methods for retrieving the size of an encoded message: +/// +/// +/// These methods are crucial in decoding scenarios where the full size of the message +/// is not immediately available, helping to determine how many bytes need to be read. pub trait SizeHint { + /// `size_hint` is a static method that takes the raw data and an offset and returns the total + /// size of the encoded message. This is particularly useful for types where the encoded size + /// may vary based on the contents of the data, and we need to calculate how much space is + /// required for decoding. fn size_hint(data: &[u8], offset: usize) -> Result; + /// `size_hint_` is an instance method that performs the same function but allows the size to be + /// be determined with respect to the current instance of the type. fn size_hint_(&self, data: &[u8], offset: usize) -> Result; } -/// Return the encoded byte size of an `Encodable` comprehensive of the header, if any +/// [`GetSize`] is a trait defining a single function that calculates the total size of an +/// encodable type. pub trait GetSize { + /// `get_size` returns total size of the type in bytes. fn get_size(&self) -> usize; } @@ -30,12 +98,20 @@ impl GetSize for Slice { self.len() } } - -/// Implemented by all the primitives with a fixed size +/// [`Fixed`] is a trait is defining a single element representing a size of a constant. +/// +/// This is useful for primitives with a constant fixed size +/// +/// Types implementing this trait must define the constant `SIZE`, representing the +/// fixed number of bytes needed to encode or decode the type. This trait is used for +/// types that have a know size at compile time , such as integers, fixed-size arrays, etc. pub trait Fixed { + ///the constant `SIZE`, represents the fixed number of bytes needed to encode or decode the + /// type. const SIZE: usize; } +// Not used and will be removed during refactoring pub trait Variable { const HEADER_SIZE: usize; //const ELEMENT_SIZE: usize; @@ -43,12 +119,12 @@ pub trait Variable { fn inner_size(&self) -> usize; - // That could be [u8; Self::HEADER_SIZE] but rust do not allow it + // Retrieves the header as a byte vector. This header typically contains information + // about the size or type of the data that follows. fn get_header(&self) -> Vec; } impl SizeHint for T { - /// Total size of the encoded data type compreensive of the header when present fn size_hint(_data: &[u8], _offset: usize) -> Result { Ok(Self::SIZE) } diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/copy_data_types.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/copy_data_types.rs index 690f719fd9..d9ca0a4abd 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/copy_data_types.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/copy_data_types.rs @@ -1,4 +1,32 @@ -//! Copy data types +// Provides implementations for encoding and decoding copy data types as required by the SV2 +// protocol. Facilitates byte-level serialization and deserialization, particularly for types +// without dynamically-sized data. +// +// ## Traits and Implementations +// +// ### `Fixed` +// The `Fixed` trait is implemented for various data types to specify a fixed size for each, +// enabling consistent memory allocation during serialization. The `SIZE` constant for each type +// defines its byte size, with implementations provided for `bool`, unsigned integers (e.g., `u8`, +// `u16`, `u32`, `u64`), and custom types like `U24`. +// +// ### `Sv2DataType` +// The `Sv2DataType` trait is implemented for these data types, providing methods for encoding and +// decoding operations such as `from_bytes_unchecked`, `from_vec_`, `from_reader_` (if `std` is +// available), and `to_slice_unchecked`. The methods use little-endian byte order for consistency +// across platforms. +// +// ## Special Types +// +// ### `U24` +// A custom 24-bit unsigned integer, represented as a `U24` struct, handles 3-byte data often used +// in SV2 protocols for memory-efficient encoding. Provides conversion methods to and from `u32`, +// with `TryFrom` ensuring values stay within the 24-bit range (0 to 16,777,215). +// +// ## Macros +// The `impl_sv2_for_unsigned` macro streamlines the implementation of the `Sv2DataType` trait for +// unsigned integer types, ensuring little-endian byte ordering for serialization and handling both +// in-memory buffers and `std::io::Read`/`Write` interfaces when `std` is available. use crate::{codec::Fixed, datatypes::Sv2DataType, Error}; use alloc::vec::Vec; @@ -13,15 +41,6 @@ impl Fixed for bool { const SIZE: usize = 1; } -// Boolean value. Encoded as an unsigned 1-bit integer, -// True = 1, False = 0 with 7 additional padding bits in -// the high positions. -// x -// Recipients MUST NOT interpret bits outside of the -// least significant bit. Senders MAY set bits outside of -// the least significant bit to any value without any -// impact on meaning. This allows future use of other -// bits as flag bits. impl<'a> Sv2DataType<'a> for bool { fn from_bytes_unchecked(data: &'a mut [u8]) -> Self { match data @@ -87,6 +106,11 @@ impl Fixed for u64 { const SIZE: usize = 8; } +/// Macro to implement the `Sv2DataType` trait for unsigned integer types. +/// +/// Simplifies encoding and decoding for various unsigned integer types, making them +/// compatible with the SV2 protocol. Each implementation uses the little-endian byte order for +/// serialization and deserialization, ensuring consistency across platforms. macro_rules! impl_sv2_for_unsigned { ($a:ty) => { impl<'a> Sv2DataType<'a> for $a { @@ -132,14 +156,15 @@ impl_sv2_for_unsigned!(u16); impl_sv2_for_unsigned!(u32); impl_sv2_for_unsigned!(u64); -// Impl f32 as a primitives - impl Fixed for f32 { const SIZE: usize = 4; } impl_sv2_for_unsigned!(f32); +/// Represents a 24-bit unsigned integer (`U24`), supporting SV2 serialization and deserialization. +/// Only first 3 bytes of a u32 is considered to get the SV2 value, and rest are ignored (in little +/// endian). #[repr(C)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct U24(pub(crate) u32); diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/mod.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/mod.rs index 8ab1bc9249..468e82d503 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/mod.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/mod.rs @@ -1,3 +1,33 @@ +// Provides implementations for encoding and decoding data types in the SV2 protocol, +// supporting both fixed-size and dynamically-sized types. Defines the `Sv2DataType` trait, +// which standardizes serialization and deserialization across various types, including +// those with custom requirements like byte padding and dynamic sizing. +// +// ## Structure and Contents +// +// ### `Sv2DataType` Trait +// The `Sv2DataType` trait offers methods to: +// - **Deserialize**: Convert byte slices or reader sources into Rust types. +// - **Serialize**: Encode Rust types into byte slices or write them to I/O streams. +// +// Supports both **checked** and **unchecked** variants for serialization and deserialization. +// Checked functions validate data lengths, while unchecked versions assume size correctness for +// optimized performance. +// +// ### Modules +// - **`copy_data_types`**: Defines fixed-size types directly copied into or from byte slices, such +// as `U24` (24-bit unsigned integer). +// - **`non_copy_data_types`**: Manages dynamically-sized types, like sequences, public keys, and +// strings, requiring size handling logic for SV2 compatibility. +// +// ### Re-exports +// Re-exports common data types used in SV2 serialization, such as `PubKey`, `Signature`, `Seq0255`, +// and others, simplifying protocol data handling with concrete implementations of `Sv2DataType`. +// +// The `Sv2DataType` trait and its implementations enable seamless conversion between in-memory +// representations and serialized forms, ensuring efficient protocol communication and +// interoperability. + use crate::{ codec::{GetSize, SizeHint}, Error, @@ -17,21 +47,40 @@ use core::convert::TryInto; #[cfg(not(feature = "no_std"))] use std::io::{Error as E, Read, Write}; +/// `Sv2DataType` is a trait that defines methods for encoding and decoding Stratum V2 data. +/// It is used for serializing and deserializing both fixed-size and dynamically-sized types. +/// +/// Key Responsibilities: +/// - Serialization: Converting data from in-memory representations to byte slices or streams. +/// - Deserialization: Converting byte slices or streams back into the in-memory representation of +/// the data. +/// +/// This trait includes functions for both checked and unchecked conversions, providing flexibility +/// in situations where error handling can be safely ignored. pub trait Sv2DataType<'a>: Sized + SizeHint + GetSize + TryInto { + /// Creates an instance of the type from a mutable byte slice, checking for size constraints. + /// + /// This function verifies that the provided byte slice has the correct size according to the + /// type's size hint. fn from_bytes_(data: &'a mut [u8]) -> Result { Self::size_hint(data, 0)?; Ok(Self::from_bytes_unchecked(data)) } + /// Constructs an instance from a mutable byte slice without verifying size constraints. fn from_bytes_unchecked(data: &'a mut [u8]) -> Self; + /// Constructs an instance from a vector, checking for the correct size. fn from_vec_(data: Vec) -> Result; + /// Constructs an instance from a vector without validating its size. fn from_vec_unchecked(data: Vec) -> Self; + // Constructs an instance from a reader source, checking for size constraints. #[cfg(not(feature = "no_std"))] fn from_reader_(reader: &mut impl Read) -> Result; + /// Serializes the instance to a mutable slice, checking the destination size. fn to_slice(&'a self, dst: &mut [u8]) -> Result { if dst.len() >= self.get_size() { self.to_slice_unchecked(dst); @@ -41,8 +90,10 @@ pub trait Sv2DataType<'a>: Sized + SizeHint + GetSize + TryInto { } } + /// Serializes the instance to a mutable slice without checking the destination size. fn to_slice_unchecked(&'a self, dst: &mut [u8]); + // Serializes the instance to a writer destination, checking for I/O errors. #[cfg(not(feature = "no_std"))] fn to_writer_(&self, writer: &mut impl Write) -> Result<(), E>; } diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/inner.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/inner.rs index e0f30bf38a..d3deeae09e 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/inner.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/inner.rs @@ -1,3 +1,39 @@ +// Provides a flexible container for managing either owned or mutable references to byte arrays. +// +// # Overview +// Defines the `Inner` enum to manage both mutable references to byte slices and owned vectors +// (`Vec`). Accommodates both fixed-size and variable-size data using const generics, offering +// control over size and header length constraints. +// +// # `Inner` Enum +// The `Inner` enum has two variants for data management: +// - `Ref(&'a mut [u8])`: A mutable reference to a byte slice, allowing in-place data modification. +// - `Owned(Vec)`: An owned byte vector, providing full control over data and supporting move +// semantics. +// +// ## Const Parameters +// Configured using const generics for the following constraints: +// - `ISFIXED`: Indicates whether the data has a fixed size. +// - `SIZE`: Specifies the size when `ISFIXED` is true. +// - `HEADERSIZE`: Defines the size of the header, useful for variable-size data with a prefix +// length. +// - `MAXSIZE`: Limits the maximum allowable size of the data. +// +// # Usage +// `Inner` offers several methods for data manipulation, including: +// - `to_vec()`: Returns a `Vec`, cloning the slice or owned data. +// - `inner_as_ref()` and `inner_as_mut()`: Provide immutable or mutable access to the data. +// - `expected_length(data: &[u8])`: Computes the expected length, validating it against +// constraints. +// - `get_header()`: Returns the data's header based on `HEADERSIZE`. +// +// # Implementations +// The `Inner` enum implements `PartialEq`, `Eq`, `GetSize`, `SizeHint`, and `Sv2DataType` traits, +// enabling buffer size calculations, reading, and writing to byte slices. +// +// # Error Handling +// Methods return `Error` types when data exceeds size limits or deviates from the configuration, +// ensuring compliance with defined constraints. use super::IntoOwned; use crate::{ codec::{GetSize, SizeHint}, @@ -10,6 +46,21 @@ use core::convert::{TryFrom, TryInto}; #[cfg(not(feature = "no_std"))] use std::io::{Error as E, Read, Write}; +// The `Inner` enum represents a flexible container for managing both reference to mutable +// slices and owned bytes arrays (`Vec`). This design allows the container to either own +// its data or simply reference existing mutable data. It uses const generics to differentiate +// between fixed-size and variable-size data, as well as to specify key size-related parameters. +// +// It has two variants: +// - `Ref(&'a mut [u8])`: A mutable reference to an external byte slice. +// - `Owned (Vec)`: A vector that owns its data, enabling dynamic ownership. +// +// The const parameters that govern the behavior of this enum are: +// - `ISFIXED`: A boolean indicating whether the data has a fixed size. +// - `SIZE`: The size of the data if `ISFIXED` is true. +// - `HEADERSIZE`: The size of the header, which is used for types that require a prefix to +// describe the content's length. +// - `MAXSIZE`: The maximum allowable size for the data. #[repr(C)] #[derive(Debug)] pub enum Inner< @@ -25,18 +76,24 @@ pub enum Inner< // TODO add test for that and implement it also with serde!!!! impl<'a, const SIZE: usize> Inner<'a, true, SIZE, 0, 0> { + // Converts the inner data to a vector, either by cloning the referenced slice or + // returning a clone of the owned vector. pub fn to_vec(&self) -> Vec { match self { Inner::Ref(ref_) => ref_.to_vec(), Inner::Owned(v) => v.clone(), } } + // Returns an immutable reference to the inner data, whether it's a reference or + // an owned vector. pub fn inner_as_ref(&self) -> &[u8] { match self { Inner::Ref(ref_) => ref_, Inner::Owned(v) => v, } } + // Provides a mutable reference to the inner data, allowing modification if the + // data is being referenced. pub fn inner_as_mut(&mut self) -> &mut [u8] { match self { Inner::Ref(ref_) => ref_, @@ -48,12 +105,17 @@ impl<'a, const SIZE: usize> Inner<'a, true, SIZE, 0, 0> { impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Inner<'a, false, SIZE, HEADERSIZE, MAXSIZE> { + // Similar to the fixed-size variant, this method converts the inner data into a vector. + // The data is either cloned from the referenced slice or returned as a clone of the + // owned vector. pub fn to_vec(&self) -> Vec { match self { Inner::Ref(ref_) => ref_[..].to_vec(), Inner::Owned(v) => v[..].to_vec(), } } + // Returns an immutable reference to the inner data for variable-size types, either + // referencing a slice or an owned vector. pub fn inner_as_ref(&self) -> &[u8] { match self { Inner::Ref(ref_) => &ref_[..], @@ -65,6 +127,8 @@ impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> PartialEq for Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { + // Provides equality comparison between two `Inner` instances by checking the equality + // of their data, regardless of whether they are references or owned vectors. fn eq(&self, other: &Self) -> bool { match (self, other) { (Inner::Ref(b), Inner::Owned(a)) => *b == &a[..], @@ -83,6 +147,10 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE> { + // Calculates the expected length of the data based on the type's parameters (fixed-size + // or variable-size). It checks if the length conforms to the specified constraints like + // `SIZE`, `MAXSIZE`, and `HEADERSIZE`, returning the length or an error if the data + // exceeds the limits. fn expected_length(data: &[u8]) -> Result { let expected_length = match ISFIXED { true => Self::expected_length_fixed(), @@ -95,10 +163,14 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const } } + // For fixed-size data, the expected length is always `SIZE`. fn expected_length_fixed() -> usize { SIZE } + // For variable-size data, this method calculates the size based on the header. + // The header describes the length of the data, and this method ensures the data + // is correctly sized relative to the header information. fn expected_length_variable(data: &[u8]) -> Result { if data.len() >= HEADERSIZE { let size = match HEADERSIZE { @@ -116,6 +188,8 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const } } + // Similar to the above but operates on a reader instead of a byte slice, reading + // the header from the input and calculating the expected length of the data to be read. #[cfg(not(feature = "no_std"))] fn expected_length_for_reader(mut reader: impl Read) -> Result { if ISFIXED { @@ -140,6 +214,8 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const } } + /// Returns the length of the data, either from the reference or the owned vector, + /// or the fixed size if `ISFIXED` is true. pub fn len(&self) -> usize { match (self, ISFIXED) { (Inner::Ref(data), false) => data.len(), @@ -148,6 +224,8 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const } } + // Retrieves the header as a byte vector. If `HEADERSIZE` is zero, an empty vector is + // returned. Otherwise, the header is constructed from the length of the data. fn get_header(&self) -> Vec { if HEADERSIZE == 0 { Vec::new() diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/mod.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/mod.rs index 40c47a7ba4..af93995155 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/mod.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/mod.rs @@ -1,3 +1,35 @@ +// Provides a flexible, low-level interface for representing fixed-size and variable-size byte +// arrays, simplifying serialization and deserialization of cryptographic and protocol data. +// +// The core component is the [`Inner`] type, a wrapper for managing both fixed and variable-length +// data slices or owned values. It offers aliases for commonly used data types like 32-byte hashes +// (`U256`), public keys (`PubKey`), cryptographic signatures (`Signature`), and dynamically-sized +// arrays (`B0255`, `B064K`). + +// # Features +// - **Fixed-size Aliases**: Types like [`U32AsRef`], [`U256`], [`ShortTxId`], [`PubKey`], and +// [`Signature`] represent specific byte sizes, often used in cryptographic contexts or protocol +// identifiers. +// - **Variable-size Aliases**: Types like [`B032`], [`B0255`], [`Str0255`], [`B064K`], and +// [`B016M`] handle data with bounded sizes, providing flexibility for dynamic data. +// - **Traits and Conversions**: Implements traits like `From`, `TryFrom`, and [`IntoOwned`] for +// seamless transformations between owned and reference-based values. +// - **Property Testing** (optional, requires the `prop_test` feature): Supports generating +// arbitrary test data for property-based testing. + +// # Type Aliases +// - **[`U32AsRef`]**: 4-byte representation for small identifiers or integer values. +// - **[`U256`]**: 32-byte cryptographic hash (e.g., SHA-256 or protocol IDs). +// - **[`ShortTxId`]**: 6-byte transaction ID. +// - **[`PubKey`]**: 32-byte public key (e.g., Ed25519). +// - **[`Signature`]**: 64-byte cryptographic signature. +// - **[`B032`], [`B0255`], [`Str0255`]**: Variable-size representations for optional fields or +// protocol data. + +// # Feature Flags +// - **`prop_test`**: Enables property-based testing with the `quickcheck` crate. When enabled, +// types like `U256` and `B016M` gain methods to generate arbitrary test data for testing +// serialization and deserialization. #[cfg(feature = "prop_test")] use quickcheck::{Arbitrary, Gen}; @@ -15,15 +47,35 @@ trait IntoOwned { pub use inner::Inner; pub use seq_inner::{Seq0255, Seq064K, Sv2Option}; +/// Type alias for a 4-byte slice or owned data represented using the `Inner` +/// type with fixed-size configuration. pub type U32AsRef<'a> = Inner<'a, true, 4, 0, 0>; +/// Type alias for a 32-byte slice or owned data (commonly used for cryptographic +/// hashes or IDs) represented using the `Inner` type with fixed-size configuration. pub type U256<'a> = Inner<'a, true, 32, 0, 0>; +/// Type alias for a 6-byte transaction ID (TxId) represented using the `Inner` +/// type with fixed-size configuration. pub type ShortTxId<'a> = Inner<'a, true, 6, 0, 0>; +/// Type alias for a 32-byte public key represented using the `Inner` type +/// with fixed-size configuration. pub type PubKey<'a> = Inner<'a, true, 32, 0, 0>; +/// Type alias for a 64-byte cryptographic signature represented using the +/// `Inner` type with fixed-size configuration. pub type Signature<'a> = Inner<'a, true, 64, 0, 0>; +/// Type alias for a variable-sized byte array with a maximum size of 32 bytes, +/// represented using the `Inner` type with a 1-byte header. pub type B032<'a> = Inner<'a, false, 1, 1, 32>; +/// Type alias for a variable-sized byte array with a maximum size of 255 bytes, +/// represented using the `Inner` type with a 1-byte header. pub type B0255<'a> = Inner<'a, false, 1, 1, 255>; +/// Type alias for a variable-sized string with a maximum size of 255 bytes, +/// represented using the `Inner` type with a 1-byte header. pub type Str0255<'a> = Inner<'a, false, 1, 1, 255>; +/// Type alias for a variable-sized byte array with a maximum size of 64 KB, +/// represented using the `Inner` type with a 2-byte header. pub type B064K<'a> = Inner<'a, false, 1, 2, { u16::MAX as usize }>; +/// Type alias for a variable-sized byte array with a maximum size of ~16 MB, +/// represented using the `Inner` type with a 3-byte header. pub type B016M<'a> = Inner<'a, false, 1, 3, { 2_usize.pow(24) - 1 }>; impl<'decoder> From<[u8; 32]> for U256<'decoder> { @@ -53,6 +105,7 @@ impl<'a> B016M<'a> { use core::convert::{TryFrom, TryInto}; +// Attempts to convert a `String` into a `Str0255<'a>`. impl<'a> TryFrom for Str0255<'a> { type Error = crate::Error; @@ -61,7 +114,10 @@ impl<'a> TryFrom for Str0255<'a> { } } +/// Represents a reference to a 32-bit unsigned integer (`u32`), +/// providing methods for convenient conversions. impl<'a> U32AsRef<'a> { + /// Returns the `u32` value represented by this reference. pub fn as_u32(&self) -> u32 { let inner = self.inner_as_ref(); u32::from_le_bytes([inner[0], inner[1], inner[2], inner[3]]) diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs index 9da799ba92..af74710192 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/datatypes/non_copy_data_types/seq_inner.rs @@ -1,3 +1,54 @@ +// # Sequence and Optional Data Structures +// +// Provides specialized implementations of sequences and optional data types, primarily +// designed to handle serialized data with fixed size constraints. These structures are particularly +// suited for encoding and decoding variable-length and optional data fields within serialized +// formats. +// +// ## Provided Types +// +// ### `Seq0255` +// - Represents a sequence of up to 255 elements. +// - Includes utility methods such as: +// - `to_vec()`: Converts each element into its byte vector representation. +// - `inner_as_ref()`: Provides references to the inner data for each element. +// - `new()`: Creates a `Seq0255` instance, enforcing the maximum length constraint. +// - Implements the `Decodable` trait for seamless deserialization, and `GetSize` to calculate the +// encoded size, ensuring compatibility with various serialization formats. +// +// ### `Seq064K` +// - Represents a sequence of up to 65535 elements. +// - Similar to `Seq0255`, it provides: +// - `to_vec()` and `inner_as_ref()` methods to convert or reference each element. +// - `new()` enforces the maximum size limit, preventing excess memory usage. +// - Like `Seq0255`, `Seq064K` is `Decodable` and implements `GetSize`, making it versatile for +// serialization scenarios. +// +// ### `Sv2Option` +// - Represents an optional data type, encoding a single or absent element. +// - Provides `to_option()` to convert to a standard `Option>`. +// - `new()` and `into_inner()` enable flexible conversions between `Option` and `Sv2Option`. +// +// ## Utility Macros +// +// - `impl_codec_for_sequence!`: Implements the `Decodable` trait for a sequence type, allowing for +// a custom deserialization process that interprets field markers. +// - `impl_into_encodable_field_for_seq!`: Implements conversions to `EncodableField` for a +// sequence, adapting the sequence for inclusion in serialized structures. +// +// ## Notes on Serialization +// +// Types are designed to interoperate with the `serde-sv2` framework, using lifetimes +// (`'a`) for compatibility with external lifetimes and ensuring the types can be converted into +// various serialized forms with or without `serde` support. +// +// ## Build Options +// +// - `prop_test`: Enables property-based testing compatibility by implementing `TryFrom` for `Vec` +// conversions. +// - `no_std`: Allows the module to be used in `no_std` environments by disabling `std::io::Read` +// dependencies. + use crate::{ codec::{ decodable::{Decodable, DecodableField, FieldMarker, GetMarker, PrimitiveMarker}, @@ -13,9 +64,11 @@ use core::marker::PhantomData; impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Seq0255<'a, super::inner::Inner<'a, false, SIZE, HEADERSIZE, MAXSIZE>> { + /// Converts the inner types to owned vector, and collects. pub fn to_vec(&self) -> Vec> { self.0.iter().map(|x| x.to_vec()).collect() } + /// Converts the inner types to shared reference, and collects. pub fn inner_as_ref(&self) -> Vec<&[u8]> { self.0.iter().map(|x| x.inner_as_ref()).collect() } @@ -23,9 +76,12 @@ impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> // TODO add test for that and implement it also with serde!!!! impl<'a, const SIZE: usize> Seq0255<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { + /// Converts the inner types to owned vector, and collects. pub fn to_vec(&self) -> Vec> { self.0.iter().map(|x| x.to_vec()).collect() } + + /// Converts the inner types to shared reference, and collects. pub fn inner_as_ref(&self) -> Vec<&[u8]> { self.0.iter().map(|x| x.inner_as_ref()).collect() } @@ -34,9 +90,12 @@ impl<'a, const SIZE: usize> Seq0255<'a, super::inner::Inner<'a, true, SIZE, 0, 0 impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Seq064K<'a, super::inner::Inner<'a, false, SIZE, HEADERSIZE, MAXSIZE>> { + /// Converts the inner types to owned vector, and collects. pub fn to_vec(&self) -> Vec> { self.0.iter().map(|x| x.to_vec()).collect() } + + /// Converts the inner types to shared reference, and collects. pub fn inner_as_ref(&self) -> Vec<&[u8]> { self.0.iter().map(|x| x.inner_as_ref()).collect() } @@ -44,9 +103,12 @@ impl<'a, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> // TODO add test for that and implement it also with serde!!!! impl<'a, const SIZE: usize> Seq064K<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { + /// Converts the inner types to owned vector, and collects. pub fn to_vec(&self) -> Vec> { self.0.iter().map(|x| x.to_vec()).collect() } + + /// Converts the inner types to shared reference, and collects. pub fn inner_as_ref(&self) -> Vec<&[u8]> { self.0.iter().map(|x| x.inner_as_ref()).collect() } @@ -55,7 +117,9 @@ impl<'a, const SIZE: usize> Seq064K<'a, super::inner::Inner<'a, true, SIZE, 0, 0 #[cfg(not(feature = "no_std"))] use std::io::Read; -/// The liftime is here only for type compatibility with serde-sv2 +/// [`Seq0255`] represents a sequence with a maximum length of 255 elements. +/// This structure uses a generic type `T` and a lifetime parameter `'a` +/// to ensure compatibility with `serde-sv2`. #[repr(C)] #[derive(Debug, Clone, Eq, PartialEq)] pub struct Seq0255<'a, T>(pub Vec, PhantomData<&'a T>); @@ -63,7 +127,7 @@ pub struct Seq0255<'a, T>(pub Vec, PhantomData<&'a T>); impl<'a, T: 'a> Seq0255<'a, T> { const HEADERSIZE: usize = 1; - /// Return the len of the inner vector + // Determines the expected length of the sequence by examining the first byte of `data`. fn expected_len(data: &[u8]) -> Result { if data.len() >= Self::HEADERSIZE { Ok(data[0] as usize) @@ -72,6 +136,7 @@ impl<'a, T: 'a> Seq0255<'a, T> { } } + /// Creates a new `Seq0255` instance with the given inner vector. pub fn new(inner: Vec) -> Result { if inner.len() <= 255 { Ok(Self(inner, PhantomData)) @@ -80,12 +145,14 @@ impl<'a, T: 'a> Seq0255<'a, T> { } } + /// Consumes the `Seq0255` and returns the inner vector of elements. pub fn into_inner(self) -> Vec { self.0 } } impl<'a, T: GetSize> GetSize for Seq0255<'a, T> { + // Calculates the total size of the sequence in bytes. fn get_size(&self) -> usize { let mut size = Self::HEADERSIZE; for with_size in &self.0 { @@ -95,14 +162,16 @@ impl<'a, T: GetSize> GetSize for Seq0255<'a, T> { } } -/// The liftime is here only for type compatibility with serde-sv2 +/// [`Seq064K`] represents a sequence with a maximum length of 65535 elements. +/// This structure uses a generic type `T` and a lifetime parameter `'a` +/// to ensure compatibility with `serde-sv2`. #[derive(Debug, Clone, Eq, PartialEq)] pub struct Seq064K<'a, T>(pub(crate) Vec, PhantomData<&'a T>); impl<'a, T: 'a> Seq064K<'a, T> { const HEADERSIZE: usize = 2; - /// Return the len of the inner vector + // Determines the expected length of the sequence by examining the first two bytes of `data`. fn expected_len(data: &[u8]) -> Result { if data.len() >= Self::HEADERSIZE { Ok(u16::from_le_bytes([data[0], data[1]]) as usize) @@ -111,6 +180,7 @@ impl<'a, T: 'a> Seq064K<'a, T> { } } + /// Creates a new `Seq064K` instance with the given inner vector. pub fn new(inner: Vec) -> Result { if inner.len() <= 65535 { Ok(Self(inner, PhantomData)) @@ -119,6 +189,7 @@ impl<'a, T: 'a> Seq064K<'a, T> { } } + /// Consumes the `Seq064K` and returns the inner vector of elements. pub fn into_inner(self) -> Vec { self.0 } @@ -134,6 +205,8 @@ impl<'a, T: GetSize> GetSize for Seq064K<'a, T> { } } +/// Macro to implement encoding and decoding traits for sequence types (`Seq0255`, `Seq064K`, and +/// `Sv2Option`). macro_rules! impl_codec_for_sequence { ($a:ty) => { impl<'a, T: 'a + Sv2DataType<'a> + GetMarker + GetSize + Decodable<'a>> Decodable<'a> @@ -210,10 +283,14 @@ macro_rules! impl_codec_for_sequence { }; } +// Implementations for encoding/decoding impl_codec_for_sequence!(Seq0255<'a, T>); impl_codec_for_sequence!(Seq064K<'a, T>); impl_codec_for_sequence!(Sv2Option<'a, T>); +/// The `impl_into_encodable_field_for_seq` macro provides implementations of the `From` trait +/// to convert `Seq0255`, `Seq064K`, and `Sv2Option` types into `EncodableField`, making these +/// sequence types compatible with encoding. macro_rules! impl_into_encodable_field_for_seq { ($a:ty) => { impl<'a> From> for EncodableField<'a> { @@ -316,12 +393,14 @@ impl<'a, T> From> for Seq064K<'a, T> { } impl<'a, T: Fixed> Seq0255<'a, T> { + /// converts the lifetime to static pub fn into_static(self) -> Seq0255<'static, T> { // Safe unwrap cause the initial value is a valid Seq0255 Seq0255::new(self.0).unwrap() } } impl<'a, T: Fixed> Sv2Option<'a, T> { + /// converts the lifetime to static pub fn into_static(self) -> Sv2Option<'static, T> { Sv2Option::new(self.into_inner()) } @@ -330,6 +409,7 @@ impl<'a, T: Fixed> Sv2Option<'a, T> { impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Seq0255<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + /// converts the lifetime to static pub fn into_static( self, ) -> Seq0255<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { @@ -343,6 +423,7 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Sv2Option<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + /// converts the lifetime to static pub fn into_static( self, ) -> Sv2Option<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { @@ -353,6 +434,7 @@ impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const } impl<'a, T: Fixed> Seq064K<'a, T> { + /// converts the lifetime to static pub fn into_static(self) -> Seq064K<'static, T> { // Safe unwrap cause the initial value is a valid Seq064K Seq064K::new(self.0).unwrap() @@ -362,6 +444,7 @@ impl<'a, T: Fixed> Seq064K<'a, T> { impl<'a, const ISFIXED: bool, const SIZE: usize, const HEADERSIZE: usize, const MAXSIZE: usize> Seq064K<'a, Inner<'a, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { + /// converts the lifetime to static pub fn into_static( self, ) -> Seq064K<'static, Inner<'static, ISFIXED, SIZE, HEADERSIZE, MAXSIZE>> { @@ -379,6 +462,7 @@ pub struct Sv2Option<'a, T>(pub Vec, PhantomData<&'a T>); // TODO add test for that and implement it also with serde!!!! impl<'a, const SIZE: usize> Sv2Option<'a, super::inner::Inner<'a, true, SIZE, 0, 0>> { + /// Gets the owned first element of the sequence, if present pub fn to_option(&self) -> Option> { let v: Vec> = self.0.iter().map(|x| x.to_vec()).collect(); match v.len() { @@ -388,6 +472,7 @@ impl<'a, const SIZE: usize> Sv2Option<'a, super::inner::Inner<'a, true, SIZE, 0, _ => unreachable!(), } } + /// Gets the reference to first element of the sequence, if present pub fn inner_as_ref(&self) -> Option<&[u8]> { let v: Vec<&[u8]> = self.0.iter().map(|x| x.inner_as_ref()).collect(); match v.len() { @@ -415,6 +500,7 @@ impl<'a, T: 'a> Sv2Option<'a, T> { } } + /// Initializes a new option type pub fn new(inner: Option) -> Self { match inner { Some(x) => Self(vec![x], PhantomData), @@ -422,6 +508,7 @@ impl<'a, T: 'a> Sv2Option<'a, T> { } } + /// Gets the inner value of Sv2Option pub fn into_inner(mut self) -> Option { let len = self.0.len(); match len { diff --git a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/lib.rs b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/lib.rs index cbca39977d..055d27ebcf 100644 --- a/protocols/v2/binary-sv2/no-serde-sv2/codec/src/lib.rs +++ b/protocols/v2/binary-sv2/no-serde-sv2/codec/src/lib.rs @@ -1,16 +1,30 @@ +#![deny(missing_docs)] +//! Defines types, encodings, and conversions between Serde and SV2 protocols, +//! providing abstractions for encoding, decoding, and error handling of SV2 data types. +//! +//! # Overview +//! +//! Enables conversion between various Rust types and SV2-specific data formats for efficient +//! network communication. Provides utilities to encode and decode data types according to the SV2 +//! specifications. +//! +//! ## Type Mappings +//! The following table illustrates how standard Rust types or serde data model map to their SV2 +//! counterparts: +//! //! ```txt //! SERDE <-> Sv2 //! bool <-> BOOL //! u8 <-> U8 //! u16 <-> U16 //! U24 <-> U24 -//! u32 <-> u32 -//! f32 <-> f32 // not in the spec but used -//! u64 <-> u64 // not in the spec but used +//! u32 <-> U32 +//! f32 <-> F32 // Not in the spec, but used +//! u64 <-> U64 //! U256 <-> U256 //! Str0255 <-> STRO_255 //! Signature<-> SIGNATURE -//! B032 <-> B0_32 // not in the spec but used +//! B032 <-> B0_32 //! B0255 <-> B0_255 //! B064K <-> B0_64K //! B016M <-> B0_16M @@ -19,6 +33,49 @@ //! Seq0255 <-> SEQ0_255[T] //! Seq064K <-> SEQ0_64K[T] //! ``` +//! +//! # Encoding & Decoding +//! +//! Enables conversion between various Rust types and SV2-specific data formats for efficient +//! network communication. Provides utilities to encode and decode data types according to the SV2 +//! specifications. +//! +//! - **to_bytes**: Encodes an SV2 data type into a byte vector. +//! - **to_writer**: Encodes an SV2 data type into a byte slice. +//! - **from_bytes**: Decodes an SV2-encoded byte slice into the specified data type. +//! +//! # Error Handling +//! +//! Defines an `Error` enum for handling failure conditions during encoding, decoding, and data +//! manipulation. Common errors include: +//! - Out-of-bounds accesses +//! - Size mismatches during encoding/decoding +//! - Invalid data representations, such as non-boolean values interpreted as booleans. +//! +//! # Cross-Language Interoperability +//! +//! To support foreign function interface (FFI) use cases, the module includes `CError` and `CVec` +//! types that represent SV2 data and errors in a format suitable for cross-language compatibility. +//! +//! # Build Options +//! +//! Supports optional features like `no_std` for environments without standard library support. +//! Error types are conditionally compiled to work with or without `std`. +//! +//! ## Conditional Compilation +//! - With the `no_std` feature enabled, I/O-related errors use a simplified `IoError` +//! representation. +//! - Standard I/O errors (`std::io::Error`) are used when `no_std` is disabled. +//! +//! # FFI Interoperability +//! +//! Provides utilities for FFI (Foreign Function Interface) to enable data passing between Rust and +//! other languages. Includes: +//! - `CVec`: Represents a byte vector for safe passing between C and Rust. +//! - `CError`: A C-compatible error type. +//! - `CVec2`: Manages collections of `CVec` objects across FFI boundaries. +//! +//! Facilitates integration of SV2 functionality into cross-language projects. #![cfg_attr(feature = "no_std", no_std)] @@ -40,6 +97,7 @@ pub use crate::codec::{ use alloc::vec::Vec; +/// Converts the provided SV2 data type to a byte vector based on the SV2 encoding format. #[allow(clippy::wrong_self_convention)] pub fn to_bytes(src: T) -> Result, Error> { let mut result = vec![0_u8; src.get_size()]; @@ -47,21 +105,106 @@ pub fn to_bytes(src: T) -> Result, Error> { Ok(result) } +/// Encodes the SV2 data type to the provided byte slice. #[allow(clippy::wrong_self_convention)] pub fn to_writer(src: T, dst: &mut [u8]) -> Result<(), Error> { src.to_bytes(dst)?; Ok(()) } +/// Decodes an SV2-encoded byte slice into the specified data type. pub fn from_bytes<'a, T: Decodable<'a>>(data: &'a mut [u8]) -> Result { T::from_bytes(data) } +/// Provides an interface and implementation details for decoding complex data structures +/// from raw bytes or I/O streams. Handles deserialization of nested and primitive data +/// structures through traits, enums, and helper functions for managing the decoding process. +/// +/// # Overview +/// The [`Decodable`] trait serves as the core component, offering methods to define a type's +/// structure, decode raw byte data, and construct instances from decoded fields. It supports both +/// in-memory byte slices and I/O streams for flexibility across deserialization use cases. +/// +/// # Key Concepts and Types +/// - **[`Decodable`] Trait**: Defines methods to decode types from byte data, process individual +/// fields, and construct complete types. +/// - **[`FieldMarker`] and `PrimitiveMarker`**: Enums that represent data types or structures, +/// guiding the decoding process by defining field structures and types. +/// - **[`DecodableField`] and `DecodablePrimitive`**: Represent decoded fields as either primitives +/// or nested structures, forming the building blocks for complex data types. +/// +/// # Error Handling +/// Custom error types manage issues during decoding, such as insufficient data or unsupported +/// types. Errors are surfaced through `Result` types to ensure reliability in data parsing tasks. +/// +/// # `no_std` Support +/// Compatible with `no_std` environments through conditional compilation. Omits I/O-dependent +/// methods like `from_reader` when `no_std` is enabled, ensuring lightweight builds for constrained +/// environments. pub mod decodable { pub use crate::codec::decodable::{Decodable, DecodableField, FieldMarker}; //pub use crate::codec::decodable::PrimitiveMarker; } +/// Provides an encoding framework for serializing various data types into bytes. +/// +/// The [`Encodable`] trait is the core of this framework, enabling types to define +/// how they serialize data into bytes. This is essential for transmitting data +/// between components or systems in a consistent, byte-oriented format. +/// +/// ## Overview +/// +/// Supports a wide variety of data types, including basic types (e.g., integers, +/// booleans, and byte arrays) and complex structures. Each type’s encoding logic is +/// encapsulated in enums like [`EncodablePrimitive`] and [`EncodableField`], enabling +/// structured and hierarchical data serialization. +/// +/// ### Key Types +/// +/// - **[`Encodable`]**: Defines methods for converting an object into a byte array or writing it +/// directly to an output stream. It supports both primitive types and complex structures. +/// - **[`EncodablePrimitive`]**: Represents basic types that can be serialized directly. Includes +/// data types like integers, booleans, and byte arrays. +/// - **[`EncodableField`]**: Extends [`EncodablePrimitive`] to support structured and nested data, +/// enabling recursive encoding of complex structures. +/// +/// ### `no_std` Compatibility +/// +/// When compiled with the `no_std` feature enabled, this module omits the `to_writer` method +/// to support environments without the standard library. Only buffer-based encoding +/// (`to_bytes`) is available in this mode. +/// +/// ## Error Handling +/// +/// Errors during encoding are handled through the [`Error`] type. Common failure scenarios include +/// buffer overflows and type-specific serialization errors. Each encoding method returns an +/// appropriate error if encoding fails, supporting comprehensive error management. +/// +/// ## Trait Details +/// +/// ### [`Encodable`] +/// - **`to_bytes`**: Encodes the instance into a byte slice, returning the number of bytes written +/// or an error if encoding fails. +/// - **`to_writer`** (requires `std`): Encodes the instance into any [`Write`] implementor, such as +/// a file or network stream. +/// +/// ### Additional Enums and Methods +/// +/// Includes utility types and methods for calculating sizes, encoding hierarchical data, +/// and supporting both owned and reference-based data variants. +/// +/// - **[`EncodablePrimitive`]**: Handles encoding logic for primitive types, addressing +/// serialization requirements specific to each type. +/// - **[`EncodableField`]**: Extends encoding to support composite types and structured data, +/// enabling recursive encoding of nested structures. +/// +/// ## Summary +/// +/// Designed for flexibility and extensibility, this module supports a wide range of data +/// serialization needs through customizable encoding strategies. Implementing the +/// [`Encodable`] trait for custom types ensures efficient and consistent data serialization +/// across applications. pub mod encodable { pub use crate::codec::encodable::{Encodable, EncodableField, EncodablePrimitive}; } @@ -69,39 +212,89 @@ pub mod encodable { #[macro_use] extern crate alloc; +/// Error types used within the protocol library to indicate various failure conditions. #[derive(Debug, PartialEq, Eq, Clone)] pub enum Error { + /// Indicates an attempt to read beyond a valid range. OutOfBound, + + /// Raised when a non-binary value is interpreted as a boolean. NotABool(u8), - /// -> (expected size, actual size) + + /// Occurs when an unexpected size mismatch arises during a write operation, specifying + /// expected and actual sizes. WriteError(usize, usize), + + /// Signifies an overflow condition where a `u32` exceeds the maximum allowable `u24` value. U24TooBig(u32), + + /// Reports a size mismatch for a signature, such as when it does not match the expected size. InvalidSignatureSize(usize), + + /// Raised when a `u256` value is invalid, typically due to size discrepancies. InvalidU256(usize), + + /// Indicates an invalid `u24` representation. InvalidU24(u32), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B0255`. InvalidB0255Size(usize), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B064K`. InvalidB064KSize(usize), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B016M`. InvalidB016MSize(usize), + + /// Raised when a sequence size exceeds `0255`. InvalidSeq0255Size(usize), - /// Error when trying to encode a non-primitive data type + + /// Error when trying to encode a non-primitive data type. NonPrimitiveTypeCannotBeEncoded, + + /// Generic conversion error related to primitive types. PrimitiveConversionError, + + /// Error occurring during decoding due to conversion issues. DecodableConversionError, + + /// Error triggered when a decoder is used without initialization. UnInitializedDecoder, + #[cfg(not(feature = "no_std"))] + /// Represents I/O-related errors, compatible with `no_std` mode where specific error types may + /// vary. IoError(E), + #[cfg(feature = "no_std")] + /// Represents I/O-related errors, compatible with `no_std` mode. IoError, + + /// Raised when an unexpected mismatch occurs during read operations, specifying expected and + /// actual read sizes. ReadError(usize, usize), + + /// Used as a marker error for fields that should remain void or empty. VoidFieldMarker, - /// Error when `Inner` type value exceeds max size. - /// (ISFIXED, SIZE, HEADERSIZE, MAXSIZE, bad value vec, bad value length) + + /// Signifies a value overflow based on protocol restrictions, containing details about + /// fixed/variable size, maximum size allowed, and the offending value details. ValueExceedsMaxSize(bool, usize, usize, usize, Vec, usize), - /// Error when sequence value (`Seq0255`, `Seq064K`) exceeds max size + + /// Triggered when a sequence type (`Seq0255`, `Seq064K`) exceeds its maximum allowable size. SeqExceedsMaxSize, + + /// Raised when no valid decodable field is provided during decoding. NoDecodableFieldPassed, + + /// Error for protocol-specific invalid values. ValueIsNotAValidProtocol(u8), + + /// Raised when an unsupported or unknown message type is encountered. UnknownMessageType(u8), + + /// Indicates a protocol constraint violation where `Sv2Option` unexpectedly contains multiple + /// elements. Sv2OptionHaveMoreThenOneElement(u8), } @@ -115,41 +308,91 @@ impl From for Error { } } -/// FFI-safe Error +/// `CError` is a foreign function interface (FFI)-compatible version of the `Error` enum to +/// facilitate cross-language compatibility. #[repr(C)] #[derive(Debug)] pub enum CError { + /// Indicates an attempt to read beyond a valid range. OutOfBound, + + /// Raised when a non-binary value is interpreted as a boolean. NotABool(u8), - /// -> (expected size, actual size) + + /// Occurs when an unexpected size mismatch arises during a write operation, specifying + /// expected and actual sizes. WriteError(usize, usize), + + /// Signifies an overflow condition where a `u32` exceeds the maximum allowable `u24` value. U24TooBig(u32), + + /// Reports a size mismatch for a signature, such as when it does not match the expected size. InvalidSignatureSize(usize), + + /// Raised when a `u256` value is invalid, typically due to size discrepancies. InvalidU256(usize), + + /// Indicates an invalid `u24` representation. InvalidU24(u32), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B0255`. InvalidB0255Size(usize), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B064K`. InvalidB064KSize(usize), + + /// Error indicating that a byte array exceeds the maximum allowed size for `B016M`. InvalidB016MSize(usize), + + /// Raised when a sequence size exceeds `0255`. InvalidSeq0255Size(usize), - /// Error when trying to encode a non-primitive data type + + /// Error when trying to encode a non-primitive data type. NonPrimitiveTypeCannotBeEncoded, + + /// Generic conversion error related to primitive types. PrimitiveConversionError, + + /// Error occurring during decoding due to conversion issues. DecodableConversionError, + + /// Error triggered when a decoder is used without initialization. UnInitializedDecoder, + #[cfg(not(feature = "no_std"))] + /// Represents I/O-related errors, compatible with `no_std` mode where specific error types may + /// vary. IoError(E), + #[cfg(feature = "no_std")] + /// Represents I/O-related errors, compatible with `no_std` mode. IoError, + + /// Raised when an unexpected mismatch occurs during read operations, specifying expected and + /// actual read sizes. ReadError(usize, usize), + + /// Used as a marker error for fields that should remain void or empty. VoidFieldMarker, - /// Error when `Inner` type value exceeds max size. - /// (ISFIXED, SIZE, HEADERSIZE, MAXSIZE, bad value vec, bad value length) + + /// Signifies a value overflow based on protocol restrictions, containing details about + /// fixed/variable size, maximum size allowed, and the offending value details. ValueExceedsMaxSize(bool, usize, usize, usize, CVec, usize), - /// Error when sequence value (`Seq0255`, `Seq064K`) exceeds max size + + /// Triggered when a sequence type (`Seq0255`, `Seq064K`) exceeds its maximum allowable size. SeqExceedsMaxSize, + + /// Raised when no valid decodable field is provided during decoding. NoDecodableFieldPassed, + + /// Error for protocol-specific invalid values. ValueIsNotAValidProtocol(u8), + + /// Raised when an unsupported or unknown message type is encountered. UnknownMessageType(u8), + + /// Indicates a protocol constraint violation where `Sv2Option` unexpectedly contains multiple + /// elements. Sv2OptionHaveMoreThenOneElement(u8), } @@ -246,6 +489,7 @@ impl<'a> From for EncodableField<'a> { } } +/// A struct to facilitate transferring a `Vec` across FFI boundaries. #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct CVec { @@ -255,16 +499,22 @@ pub struct CVec { } impl CVec { + /// Returns a mutable slice of the contained data. + /// + /// # Safety + /// + /// The caller must ensure that the data pointed to by `self.data` + /// remains valid for the duration of the returned slice. pub fn as_mut_slice(&mut self) -> &mut [u8] { unsafe { core::slice::from_raw_parts_mut(self.data, self.len) } } - /// Used when we need to fill a buffer allocated in rust from C. + /// Fills a buffer allocated in Rust from C. /// /// # Safety /// - /// This function construct a CVec without taking ownership of the pointed buffer so if the - /// owner drop them the CVec will point to garbage. + /// Constructs a `CVec` without taking ownership of the pointed buffer. If the owner drops the + /// buffer, the `CVec` will point to invalid memory. #[allow(clippy::wrong_self_convention)] pub fn as_shared_buffer(v: &mut [u8]) -> Self { let (data, len) = (v.as_mut_ptr(), v.len()); @@ -296,9 +546,11 @@ impl From<&[u8]> for CVec { } } -/// Given a C allocated buffer return a rust allocated CVec +/// Creates a `CVec` from a buffer that was allocated in C. /// /// # Safety +/// The caller must ensure that the buffer is valid and that +/// the data length does not exceed the allocated size. #[no_mangle] pub unsafe extern "C" fn cvec_from_buffer(data: *const u8, len: usize) -> CVec { let input = core::slice::from_raw_parts(data, len); @@ -319,6 +571,7 @@ pub unsafe extern "C" fn cvec_from_buffer(data: *const u8, len: usize) -> CVec { } } +/// A struct to manage a collection of `CVec` objects across FFI boundaries. #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct CVec2 { @@ -328,6 +581,7 @@ pub struct CVec2 { } impl CVec2 { + /// `as_mut_slice`: helps to get a mutable slice pub fn as_mut_slice(&mut self) -> &mut [CVec] { unsafe { core::slice::from_raw_parts_mut(self.data, self.len) } } @@ -338,10 +592,12 @@ impl From for Vec { } } +/// Frees the underlying memory of a `CVec`. pub fn free_vec(buf: &mut CVec) { let _: Vec = unsafe { Vec::from_raw_parts(buf.data, buf.len, buf.capacity) }; } +/// Frees the underlying memory of a `CVec2` and all its elements. pub fn free_vec_2(buf: &mut CVec2) { let vs: Vec = unsafe { Vec::from_raw_parts(buf.data, buf.len, buf.capacity) }; for mut s in vs { @@ -389,7 +645,10 @@ impl<'a, const A: bool, const B: usize, const C: usize, const D: usize> } } +/// Initializes an empty `CVec2`. +/// /// # Safety +/// The caller is responsible for freeing the `CVec2` when it is no longer needed. #[no_mangle] pub unsafe extern "C" fn init_cvec2() -> CVec2 { let mut buffer = Vec::::new(); @@ -407,9 +666,11 @@ pub unsafe extern "C" fn init_cvec2() -> CVec2 { } } -/// The caller is reponsible for NOT adding duplicate cvecs to the cvec2 structure, -/// as this can lead to double free errors when the message is dropped. +/// Adds a `CVec` to a `CVec2`. +/// /// # Safety +/// The caller must ensure no duplicate `CVec`s are added, as duplicates may +/// lead to double-free errors when the message is dropped. #[no_mangle] pub unsafe extern "C" fn cvec2_push(cvec2: &mut CVec2, cvec: CVec) { let mut buffer: Vec = Vec::from_raw_parts(cvec2.data, cvec2.len, cvec2.capacity); @@ -459,9 +720,12 @@ impl<'a, T: Into> From> for CVec2 { } } +/// Exported FFI functions for interoperability with C code for u24 #[no_mangle] pub extern "C" fn _c_export_u24(_a: U24) {} +/// Exported FFI functions for interoperability with C code for CVec #[no_mangle] pub extern "C" fn _c_export_cvec(_a: CVec) {} +/// Exported FFI functions for interoperability with C code for CVec2 #[no_mangle] pub extern "C" fn _c_export_cvec2(_a: CVec2) {}