jppe-rs
This is a byte stream structured serialization and deserialization library.
Usage
Cargo.toml
[dependencies]
jppe = { version="0.3.0", features = ["derive"] }
Simple Example
#![feature(let_chains)]
use jppe::{ByteDecode, ByteEncode};
use jppe_derive::{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) = SimpleExample::decode(input, None, None).unwrap();
println!("{value:?} {input_remain:?}");
let mut buf = vec![];
value.encode(&mut buf, None, None);
assert_eq!(buf, input);
}
Ethernet Example
#![feature(let_chains)]
use std::str::FromStr;
use jppe::{ByteDecode, ByteEncode};
use jppe_derive::{ByteEncode, ByteDecode};
use jppe::prelude::MacAddress;
#[derive(Debug, PartialEq, Eq, ByteEncode, ByteDecode)]
pub struct Ethernet {
pub smac: MacAddress,
pub dmac: MacAddress,
pub r#type: u16,
}
fn main() {
let input = b"\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00\x45\x00";
// decode
let (input_remain, value) = Ethernet::decode(input, None, None).unwrap();
println!("{value:?} {input_remain:?}");
assert_eq!(value, Ethernet {
smac: MacAddress::from_str("ff:ff:ff:ff:ff:ff").unwrap(),
dmac: MacAddress::from_str("00:00:00:00:00:00").unwrap(),
r#type: 0x0800,
});
// encode
let mut buf = vec![];
value.encode(&mut buf, None, None);
assert_eq!(buf, b"\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x08\x00");
assert_eq!(input_remain, b"\x45\x00");
}
Ipv4 Example
#![feature(let_chains)]
use std::net::Ipv4Addr;
use std::str::FromStr;
use jppe::{BorrowByteDecode, BorrowByteEncode};
use jppe_derive::{BorrowByteEncode, BorrowByteDecode};
#[derive(Debug, PartialEq, Eq, BorrowByteEncode, BorrowByteDecode)]
pub struct Ipv4<'a> {
#[jppe(bits_start=0xf0, untake)]
pub version: u8,
#[jppe(bits=0x0f, decode_value="header_length << 2", encode_value="header_length >> 2")]
pub header_length: u8,
pub tos: u8,
pub total_length: u16,
pub identification: u16,
#[jppe(bits_start=0xe000, untake)]
pub flags: u16,
#[jppe(bits=0x1fff)]
pub fragment_offset: u16,
pub ttl: u8,
pub protocol: u8,
pub checksum: u16,
pub src: Ipv4Addr,
pub dst: Ipv4Addr,
#[jppe(length="header_length - 20")]
pub options: &'a [u8],
}
fn main() {
let input = b"\x45\x00\x00\x40\xb5\xf2\x00\x00\x40\x06\xa9\x7c\x0a\x01\x01\xea\x0a\x0a\x05\x55";
// decode
let (input_remain, value) = Ipv4::decode(input, None, None).unwrap();
println!("{value:?} {input_remain:?}");
assert_eq!(value, Ipv4 {
version: 4,
header_length: 20,
tos: 0,
total_length: 64,
identification: 46578,
flags: 0,
fragment_offset: 0,
ttl: 64,
protocol: 6,
checksum: 43388,
src: Ipv4Addr::from_str("10.1.1.234").unwrap(),
dst: Ipv4Addr::from_str("10.10.5.85").unwrap(),
options: &[],
});
// encode
let mut buf = vec![];
value.encode(&mut buf, None, None);
assert_eq!(buf, input);
assert_eq!(input_remain.is_empty(), true);
}
Tcp Example
#![feature(let_chains)]
use jppe::{BorrowByteDecode, BorrowByteEncode};
use jppe_derive::{BorrowByteEncode, BorrowByteDecode};
#[derive(Debug, Default, PartialEq, Eq, BorrowByteEncode, BorrowByteDecode)]
pub struct Tcp<'a> {
pub sport: u16,
pub dport: u16,
pub seq: u32,
pub ack: u32,
#[jppe(bits_start=0xf000, decode_value="header_length * 4", encode_value="header_length / 4", untake)]
pub header_length: u16,
#[jppe(bits=0x0fff)]
pub flags: u16,
pub window: u16,
pub checksum: u16,
pub urgent_pointer: u16,
#[jppe(length="header_length - 20")]
pub options: &'a [u8],
}
fn main() {
let input = b"\xc8\xd3\x01\xf6\xe0\x76\x90\x16\xc4\x44\x9b\x5a\x80\x18\xff\xff\
\x6c\x1c\x00\x00\x01\x01\x08\x0a\x37\xc4\x50\xe2\x00\xba\x7c\x1c";
// decode
let (input_remain, value) = Tcp::decode(input, None, None).unwrap();
println!("{value:?} {input_remain:?}");
assert_eq!(value, Tcp {
sport: 51411,
dport: 502,
seq: 3765866518,
ack: 3292830554,
header_length: 32,
flags: 24,
window: 65535,
checksum: 27676,
urgent_pointer: 0,
options: b"\x01\x01\x08\x0a\x37\xc4\x50\xe2\x00\xba\x7c\x1c",
} );
// encode
let mut buf = vec![];
value.encode(&mut buf, None, None);
assert_eq!(buf, input);
assert_eq!(input_remain.is_empty(), true);
}
HTTP Example
#![feature(let_chains)]
use std::collections::HashMap;
use jppe::{BorrowByteDecode, BorrowByteEncode};
use jppe_derive::{BorrowByteEncode, BorrowByteDecode};
#[derive(Debug, Default, PartialEq, Eq, BorrowByteEncode, BorrowByteDecode)]
pub struct Http<'a> {
#[jppe(linend="\x20")]
pub method: &'a str,
#[jppe(linend="\x20")]
pub uri: &'a str,
#[jppe(linend="\r\n")]
pub http: &'a str,
#[jppe(linend="\r\n")]
pub headers: HashMap<&'a str, &'a str>,
}
fn main() {
let input = b"GET http://www.jankincai.com/ HTTP/1.1\r\nHost: www.jankincai.com\r\nAccept-Encoding: gzip, deflate\r\n";
let (input_remain, value) = Http::decode(input, None, None).unwrap();
println!("{value:?} {input_remain:?}");
// encode
let mut buf = vec![];
value.encode(&mut buf, None, None);
// The headers hashmap is out of order and cannot be compared.
// assert_eq!(buf, input);
assert_eq!(input_remain.is_empty(), true);
}
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 withvariable_name
, only for decode, eg:test_modifier_variable_name.rs
.
enum branch
-
branch_byte
-
branch_byteorder
-
branch_func
-
branch_enum
FieldAttrModifiers
-
byteorder=<"BE"|"LE">
: The byte order of locality field, eg:#[jppe(byteorder="LE")]
-
length=<num|variable>
: Data length, eg:int/&str/String
. -
offset=<num|variable>
: Byte stream offset. -
count==<num|variable>
: Data count, eg:Vec
; -
full=<int>
: encode full value. -
untake
: Bytes are not taken. -
linend|end_with=<string|bytes>
: eg:string
. -
key|starts_with
: It is suitable for accurate analysis of key/value structure data, supportingstring//&str/&[u8]
types. -
split
: eg:hashmap
-
if_expr
-
encode_with
: custom encode function. -
decode_with
: custom decode function. -
with
: custom encode/decode function. -
with_args
: custom encode/decode function args. -
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, only for decode, eg:test_modifier_variable_name.rs
. - regex
enum branch
-
branch
-
branch_default
-
branch_bits
-
branch_range
-
branch_value
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
-
HashSet
-
Macaddress
-
IPv4
orIPv6
-
Hex
-
DateTime
-
Bit