#byte #byte-stream #serialization

macro no-std jppe_derive

This is a byte stream structured serialization and deserialization library

12 releases (2 stable)

1.1.0 Aug 13, 2024
1.0.0 Apr 4, 2024
0.10.0 Apr 1, 2024
0.9.0 Mar 29, 2024

#77 in #byte-stream

49 downloads per month
Used in 3 crates (via jppe)

MIT/Apache

72KB
1K SLoC

jppe-rs

Crates.io Crates.io License

This is a Rust-based implementation of byte stream structured serialization/deserialization general library, can be applied to network packet parsing, network packet group package, network communication, file content parsing, etc., feel good small partners please click like 👍~

Install

$ rustup install nightly
$ cargo +nightly build release

Usage

Cargo.toml

[dependencies]
jppe = { version="1.1.0", features = ["derive"] }

Or

[dependencies]
jppe = { version="1.1.0", features = ["derive", "serde"] }

no_std:

[dependencies]
jppe = { version="1.1.0", default-features = false, features = ["derive"] } # default use alloc.

Simple Example

use jppe::{ByteEncode, ByteDecode};


// If the value size is less than 0xff, byte_count does not need to be specified,otherwise, byte_count=<2|4|8>


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode)]
pub struct SimpleExample {
    pub version: u8,
    // #[jppe(byte_count=1)]
    pub value: String,
    // #[jppe(byte_count=1)]
    pub body: SimpleExampleBody,
}


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode)]
#[repr(u8)]
pub enum SimpleExampleBody {
    Read {
        address: u8,
    } = 1,
    Write {
        address: u8,
        value: [u8; 3],
    },
    #[jppe(enum_default)]
    Unknown, 
}


fn main() {
    let input = b"\x01\x03\x31\x32\x33\x01\x05";
    let (input_remain, value) = jppe::decode::<SimpleExample>(input).unwrap();
    assert_eq!(value, SimpleExample { version: 1, value: "123".to_string(), body: SimpleExampleBody::Read { address: 5 } });
    assert_eq!(input_remain.is_empty(), true);
    assert_eq!(jppe::encode(value), input);
}

Simple Example2


use jppe::{ByteEncode, ByteDecode};


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode)]
pub struct SimpleExample {
    pub length: u16,
    #[jppe(length="length")]
    pub value: String,
    pub cmd: u8,
    #[jppe(branch="cmd")]
    pub body: SimpleExampleBody,
}


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode)]
#[repr(u8)]
pub enum SimpleExampleBody {
    Read {
        address: u8,
    } = 1,
    Write {
        address: u8,
        value: [u8; 3],
    },
    #[jppe(enum_default)]
    Unknown, 
}


fn main() {
    let input = b"\x00\x03\x31\x32\x33\x01\x05";
    let (input_remain, value) = jppe::decode::<SimpleExample>(input).unwrap();
    assert_eq!(value, SimpleExample { length: 3, value: "123".to_string(), cmd: 1, body: SimpleExampleBody::Read { address: 5 } });
    assert_eq!(input_remain.is_empty(), true);
    assert_eq!(jppe::encode(value), input);
}

Default Example

[dependencies]
jppe = { version="1.1.0", features = ["derive", "jdefault"] }

use jppe::{ByteEncode, ByteDecode, Jdefault};


// If the value size is less than 0xff, byte_count does not need to be specified,otherwise, byte_count=<2|4|8>


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode, Jdefault)]
pub struct SimpleExample {
    #[jppe(byte_count=1, default="\"123\".to_string()")]
    pub value: String,
    #[jppe(byte_count=1)]
    pub body: SimpleExampleBody,
}


#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode, Jdefault)]
#[repr(u8)]
pub enum SimpleExampleBody {
    Read {
        address: u8,
    } = 1,
    Write {
        address: u8,
        value: [u8; 3],
    },
    #[jppe(branch_default)]
    Unknown {
        #[jppe(default=10)]
        value: u8,
    },
}


fn main() {
    let value = SimpleExample::default();
    assert_eq!(value, SimpleExample {
        value: "123".to_string(),
        body: SimpleExampleBody::Unknown { value: 10 },
    });

    assert_eq!(jppe::encode(value), b"\x03\x31\x32\x33\x03\x0a");

    let (input_remain, value) = jppe::decode::<SimpleExample>(b"\x03\x31\x32\x33\x03\x0a").unwrap();
    assert_eq!(value, SimpleExample {
        value: "123".to_string(),
        body: SimpleExampleBody::Unknown { value: 10 },
    });
    assert_eq!(input_remain.is_empty(), true);
}

Other Example

Feature

ContainerAttrModifiers

  • byteorder=<"BE"|"LE">: The global byte order of struct and enum, eg: #[jppe(byteorder="LE")].
  • encode_with: custom encode function.
  • decode_with: custom decode function.
  • with: custom encode/decode function.
  • get_variable_name: Get cache variable, must be used with variable_name, eg: variable_name_example.

enum branch

  • byte_count_disable
  • branch_enum

FieldAttrModifiers

  • byteorder=<"BE"|"LE">: The byte order of locality field, eg:#[jppe(byteorder="LE")]
  • length=<num|variable>: Data length, support int/&str/String/&[u8] type, eg: length_example.
  • offset=<num|variable>: Byte stream offset.
  • count==<num|variable>: Data count, support Vec/HashMap type.
  • try_count==<num|variable>: Data count, support Vec/HashMap type.
  • full=<int>: encode full value.
  • untake: Bytes are not taken.
  • linend|end_with=<string|bytes>: Supporting String/&str/&[u8] type.
  • key|starts_with: It is suitable for accurate parsing of key/value structure data, supporting string/&str/&[u8] types.
  • split: Supporting HashMap type, eg: split_example
  • if_expr: Supporting Option<T> type, eg: if_expr_example.
  • encode_with: custom encode function, eg: with_example.
  • decode_with: custom decode function, eg: with_example.
  • with: custom encode/decode function, eg: with_example.
  • with_args: custom encode/decode function args, eg: with_args_example.
  • encode_value: value processing expression, eg: #[jppe(encode_value="length * 2")].
  • decode_value: value processing expression, eg: #[jppe(decode_value="length / 2")].
  • variable_name: Set integer cache variable, eg: variable_name_example.
  • byte_count=<1|2|4|8>: Specifies the byte count, automatic decode/encode length or other.
    • String/&str/&[u8]: Fetches n byte mapping length in advance, eg: byte_count.
    • HexString/HexBytes: Fetches n byte mapping length in advance, eg: byte_count.
    • Enum: The byte_count byte mapping enumeration is taken in advance and encoded through the enumeration inidex, eg: enum_byte_count
    • Vec<T>
  • skip: Require implement Default trait for data type.
  • skip_encode: Skip encode function.
  • skip_decode: Require implement Default trait for data type.
  • check_value
  • default: eg: default example
  • from_str

enum branch

DataType

  • u8/u16/u32/u64/usize/u128
  • i8/i16/i32/i64/isize/i128
  • bool
  • f32/f64
  • String and &str
  • array[T; N]
  • Tuple
  • Vec<T>
  • &[u8]
  • Option<T>
  • Struct
  • Enum
  • PhantomData
  • HashMap: Support String/&str/&[u8], eg: hashmap_example.
  • HashSet<T>: The HashSet type must specify #[jppe(count=xxx)] modifier, only supporting decode function, default count=0, eg: hashset_example.
  • MacAddress: eg: mac_example.
  • std::net::Ipv4Addr/Ipv6Addr/IpAddr: IpAddr type requres specifying the length=<16|4> modifier, Otherwise return an error, eg: ip_address_example.
  • PpeAddress: Requres specifying the length=<16|4|6|usize> modifier, Otherwise return an error, eg: ppe_address_example.
  • HexString: eg: hex_example
  • HexBytes: eg: hex_bytes_example
  • DateTime
  • Bit

TODO

  • jnet-rs library.
  • jget-rs library.
  • jdefault-rs library.
  • jfmt-rs library.

Dependencies

~0.4–0.9MB
~20K SLoC