Devlyn-Nelson / Bondrewd

Rust proc_macro library for bit level field packing.
7 stars 2 forks source link

Bondrewd

A proc-macro crate to safely and efficiently implement from_bytes/into_bytes for structures composed of primitive types and arrays.

The major features of the crate are:

Quickstart

Add the following to the dependencies in Cargo.toml:

[dependencies]
bondrewd = { version = "^0.1", features = ["derive"] }

bondrewd is easily implemented on structures to implement bit-field like structures like:

use bondrewd::{BitfieldEnum, Bitfields};

///! Implement a basic CCSDS 133.0-B-2 Primary Header using rust Enums to specify fields

/// Packet Sequence Flags as per 4.1.3.4.2.2
#[derive(BitfieldEnum, Clone, PartialEq, Eq, Debug)]
pub enum CcsdsPacketSequenceFlags {
  Continuation,
  Start,
  End,
  Unsegmented,
  Invalid(u8),
}

/// CCSDS Packet version as per 4.1.3.2
#[derive(BitfieldEnum, Clone, PartialEq, Eq, Debug)]
#[bondrewd_enum(u8)]
pub enum CcsdsPacketVersion {
  One,
  Two,
  Invalid,
}

/// Primary header object as per 4.1.3
#[derive(Bitfields, Clone, PartialEq, Eq, Debug)]
#[bondrewd(default_endianness = "be", enforce_bytes = 6)]
pub struct CcsdsPacketHeader {
  #[bondrewd(enum_primitive = "u8", bit_length = 3)]
  pub(crate) packet_version_number: CcsdsPacketVersion,
  pub(crate) packet_type: bool,
  pub(crate) sec_hdr_flag: bool,
  #[bondrewd(bit_length = 11)]
  pub(crate) app_process_id: u16,
  #[bondrewd(enum_primitive = "u8", bit_length = 2)]
  pub(crate) sequence_flags: CcsdsPacketSequenceFlags,
  #[bondrewd(bit_length = 14)]
  pub(crate) packet_seq_count: u16,
  pub(crate) packet_data_length: u16,
}

// Now you're on your way to space :)
// Lets see what this can generate
fn main() {
  let packet = CcsdsPacketHeader {
    packet_version_number: CcsdsPacketVersion::Invalid,
    packet_type: true,
    sec_hdr_flag: true,
    app_process_id: 55255 & 0b0000011111111111,
    sequence_flags: CcsdsPacketSequenceFlags::Unsegmented,
    packet_seq_count: 65535 & 0b0011111111111111,
    packet_data_length: 65535,
  };

  // Turn into some bytes (clone used to assert_eq later)
  let bytes = packet.clone().into_bytes();

  // Play with some of the fields
  match CcsdsPacketHeader::read_sequence_flags(&bytes) {
    CcsdsPacketSequenceFlags::Unsegmented => println!("Unsegmented!"),
    CcsdsPacketSequenceFlags::End => println!("End!"),
    _ => println!("Something else")
  }

  // Set the secondary header flag
  CcsdsPacketHeader::write_sec_hdr_flag(&mut bytes, false);

  // Get back from bytes, check them
  let new_packet = CcsdsPacketHeader::from_bytes(bytes);
  assert_eq!(new_packet.sec_hdr_flag, false);
  assert_eq!(new_packet.app_process_id, packet.app_process_id);
}

Usage Derive Details

bondrewd implements several derive attributes for:

struct Derive features:

field Derive features:

enum Derive features:

Why Bondrewd

Historically, the main reason for the crate was to share complex data structures for Space communication protocols (e.g. CCSDS/AOS/TC/TM...) between different software services and dependencies, without performance penalties for decoding/encoding whole structs from bytes. Originally, we wrote code for these formats using crates like modular_bitfield and packed_struct and shared a common crate across all software services. For our software, we were severely constrained by compute while transferring large amounts of data, spending lots of time decoding/encoding data structures. We found that for certain parts of the communications services that we didn't need to decode the whole structure from bytes in order to process the communications packets. In addition, we found many times we'd just set a single field in the structure and pass the packet to the next stage. So, to remedy these issues, we needed a bitfields implementation that allowed us the performance and safety, with the ability to only decode small parts of data to determine which node/process to pass data on to.

Both modular_bitfields/packed_struct are great/stable libraries, and an existing, performant and correct implementation of either of these libraries would be sufficient for almost all use cases. However, this crate looks to fill the following from these two crates: