Skip to content

A derive macro for parsing fixed-length binary data structures. This crate provides a convenient way to work with fixed-length binary data formats, commonly used in financial market data and legacy systems.

License

Notifications You must be signed in to change notification settings

Yvictor/binary_mirror

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

binary-mirror

A derive macro for parsing fixed-length binary data structures. This crate provides a convenient way to work with fixed-length binary data formats, commonly used in financial market data and legacy systems.

Features

  • Parse fixed-length binary data into Rust structs
  • Support for various data types:
    • Strings (str)
    • Numbers (i32, i64, u32, u64, f32, f64)
    • Decimals
    • Dates and Times
    • Custom Enums
  • Debug and Display implementations
  • Zero-copy parsing
  • Custom field aliases
  • Skip fields in display output
  • Serde support with to_native method from_native method
  • Error reporting with raw bytes display

Installation

cargo add binary-mirror binary-mirror-derive

Usage Examples

Basic Structure

use binary_mirror_derive::BinaryMirror;

#[repr(C)]
#[derive(BinaryMirror)]
struct Trade {
    #[bm(type = "str")]
    name: [u8; 10],
    #[bm(type = "i32")]
    value: [u8; 4],
    #[bm(type = "f32")]
    qty: [u8; 5],
}

let trade = Trade {
    name: b"AAPL ",
    value: b"123 ",
    qty: b"123.4",
};
assert_eq!(trade.name(), "AAPL");
assert_eq!(trade.value(), Some(123));
assert_eq!(trade.qty(), Some(123.4));

Serde Support

cargo add serde
use serde::{Deserialize, Serialize};
let trade = Trade {
    name: b"AAPL ",
    value: b"123 ",
    qty: b"123.4",
};
let trade_native = trade.to_native();
let json = serde_json::to_string(&trade_native).unwrap();
println!("{}", json);
let parsed = serde_json::from_str::<TradeNative>(&json).unwrap();
println!("{:?}", parsed);
let trade_from_native = Trade::from_native(&parsed);
println!("{}", trade_from_native);

Custom Enums

use binary_mirror_derive::{BinaryMirror, BinaryEnum};

#[derive(Debug, PartialEq, BinaryEnum)]
enum OrderSide {
    #[bv(value = b"B")]
    Buy,
    #[bv(value = b"S")]
    Sell,
}
// Default first character behavior
#[derive(Debug, PartialEq, BinaryEnum)]
enum Direction {
    Up, // Will use b'U'
    Down, // Will use b'D'
}

#[repr(C)]
#[derive(BinaryMirror)]
struct Order {
    #[bm(type = "enum", enum_type = "OrderSide")]
    side: [u8; 1],
}
let order = Order { side: b"B" };

assert_eq!(order.side(), Some(OrderSide::Buy));

Date and Time Handling

#[repr(C)]
#[derive(BinaryMirror)]
struct MarketData {
    #[bm(type = "date", format = "%Y%m%d")]
    date: [u8; 8],
    #[bm(type = "time", format = "%H%M%S")]
    time: [u8; 6],
    // Combine date and time into a datetime
    #[bm(type = "date", format = "%Y%m%d", datetime_with = "time", alias = "datetime")]
    trade_date: [u8; 8],
}
let data = MarketData {
    date: b"20240101",
    time: b"123456",
    trade_date: b"20240101",
};
assert_eq!(data.trade_date(), Some(NaiveDate::from_ymd_opt(2024, 1, 1).unwrap()));
assert_eq!(data.time(), Some(NaiveTime::from_hms_opt(12, 34, 56).unwrap()));
assert_eq!(
    data.datetime(),
    Some(NaiveDateTime::new(
        NaiveDate::from_ymd_opt(2024, 1, 1).unwrap(),
        NaiveTime::from_hms_opt(12, 34, 56).unwrap()
    ))
);

Field Aliases and Skip

#[repr(C)]
#[derive(BinaryMirror)]
struct Quote {
    #[bm(type = "str", alias = "exchange")]
    exh: [u8; 4],
    #[bm(type = "str", skip = true)] // Skip in Display output
    internal_code: [u8; 10],
}

let quote = Quote {
    exh: b"NYSE",
    internal_code: b"SECRET ",
};
assert_eq!(quote.exchange(), "NYSE");

Parse from Bytes

#[repr(C)]
#[derive(BinaryMirror)]
struct Data {
    #[bm(type = "str")]
    name: [u8; 10],
    #[bm(type = "i32")]
    value: [u8; 4],
}

let bytes = b"Hello 123 ";
let data = Data::from_bytes(bytes).expect("Invalid data");
assert_eq!(data.name(), "Hello");
assert_eq!(data.value(), Some(123));

// Size mismatch error handling
let wrong_size = b"too short";
let err = Data::from_bytes(wrong_size).unwrap_err();
println!("{}", err); // Will show size mismatch and content

Safety

The from_bytes method uses unsafe code to create a reference to the struct. It's safe when:

  1. The struct is marked with #[repr(C)]
  2. The input bytes match the exact size of the struct
  3. The bytes represent a valid instance of the struct

About

A derive macro for parsing fixed-length binary data structures. This crate provides a convenient way to work with fixed-length binary data formats, commonly used in financial market data and legacy systems.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages