Expand description
Shared primitives for the dvb_si / dvb_t2mi / dvb_bbframe family.
See individual modules for documentation: the Parse / Serialize
traits every wire type implements, the MPEG-2 crc32_mpeg2 CRC, and the
bcd / time codecs.
§Quick start
use dvb_common::{bcd, crc32_mpeg2};
// Binary-coded decimal (as used in MJD/BCD time fields):
assert_eq!(bcd::from_bcd_byte(0x42), Some(42));
assert_eq!(bcd::to_bcd_byte(42), Some(0x42));
// MPEG-2 CRC-32 over a section body (deterministic):
let crc = crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]);
assert_eq!(crc, crc32_mpeg2::compute(&[0xDE, 0xAD, 0xBE, 0xEF]));§Examples
Two runnable examples ship with this crate (cargo run -p dvb-common --example <name>).
§crc_and_bcd
ⓘ
//! Basic: the shared building blocks — MPEG-2 CRC-32 and BCD coding.
//!
//! Run with: `cargo run -p dvb-common --example crc_and_bcd`
use dvb_common::{bcd, crc32_mpeg2};
fn main() {
// Every CRC-bearing PSI/SI section is protected by this CRC-32/MPEG-2.
let payload = [0xDE, 0xAD, 0xBE, 0xEF];
let crc = crc32_mpeg2::compute(&payload);
println!("CRC-32/MPEG-2 of {payload:02X?} = {crc:#010X}");
// Appending the CRC and re-running over the whole buffer yields 0 — the
// standard "is this section intact?" check.
let mut framed = payload.to_vec();
framed.extend_from_slice(&crc.to_be_bytes());
assert_eq!(
crc32_mpeg2::compute(&framed),
0,
"CRC over data+CRC is zero"
);
println!(
"verify (data+CRC) = {:#010X} (0 ⇒ intact)",
crc32_mpeg2::compute(&framed)
);
// BCD is how SI encodes things like service IDs and dates.
let byte = bcd::to_bcd_byte(42).expect("0..=99 fits one BCD byte");
println!("42 as BCD = {byte:#04X}");
assert_eq!(bcd::from_bcd_byte(byte), Some(42));
}§implement_parse_serialize
ⓘ
//! Advanced: implement the project's symmetric `Parse` / `Serialize` contract
//! for your own wire type, then round-trip it.
//!
//! Every wire structure in every `dvb-*` crate implements this same pair, and
//! is round-trip tested (parse → serialize → byte-identical). This shows the
//! shape you follow when adding a new type.
//!
//! Run with: `cargo run -p dvb-common --example implement_parse_serialize`
use dvb_common::{Parse, Serialize};
/// A toy 3-byte header: a 1-byte tag and a big-endian 16-bit value.
#[derive(Debug, PartialEq, Eq)]
struct Toy {
tag: u8,
value: u16,
}
const LEN: usize = 3;
#[derive(Debug)]
#[allow(dead_code)] // fields are surfaced via Debug in the error path
enum ToyError {
TooShort { need: usize, have: usize },
}
impl<'a> Parse<'a> for Toy {
type Error = ToyError;
fn parse(bytes: &'a [u8]) -> Result<Self, Self::Error> {
if bytes.len() < LEN {
return Err(ToyError::TooShort {
need: LEN,
have: bytes.len(),
});
}
Ok(Toy {
tag: bytes[0],
value: u16::from_be_bytes([bytes[1], bytes[2]]),
})
}
}
impl Serialize for Toy {
type Error = ToyError;
fn serialized_len(&self) -> usize {
LEN
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize, Self::Error> {
if buf.len() < LEN {
return Err(ToyError::TooShort {
need: LEN,
have: buf.len(),
});
}
buf[0] = self.tag;
buf[1..3].copy_from_slice(&self.value.to_be_bytes());
Ok(LEN)
}
}
fn main() {
let wire = [0x42, 0x12, 0x34];
let toy = Toy::parse(&wire).expect("parse");
println!("parsed: {toy:?}");
let back = toy.to_bytes();
println!("re-serialized: {back:02X?}");
// The hard project invariant: parse → serialize is byte-identical.
assert_eq!(back, wire);
assert_eq!(Toy::parse(&back).unwrap(), toy);
println!("round-trip holds ✔");
}Re-exports§
Modules§
- bcd
- Binary-coded decimal (BCD) codec for DVB wire fields.
- bits
- Big-endian, MSB-first bit reader/writer for dense sub-byte wire fields.
- crc32_
mpeg2 - CRC-32 MPEG-2 — Annex C of ETSI EN 300 468, Annex A of ETSI TS 102 773.
- time
- UTC time and duration codecs for DVB wire fields.
- traits
- Canonical
ParseandSerializetraits for the DVB crate family.
Macros§
- impl_
spec_ display - Generate a
core::fmt::Displayimpl for a spec/field enum that delegates to an inherentfn name(&self) -> &'static str.