# Changelog

## 3.1.2 — 2026-06-07

### Fixed
- `ts::SectionReassembler::feed` now completes a section that spans **into a
  PUSI packet**. When a section started in packet A and spilled into packet B,
  and B was itself PUSI=1 (new sections begin in it), the `pointer_field` tail
  bytes belonging to A's section were skipped and the buffer cleared — so the
  spanning section was dropped (ISO/IEC 13818-1 §2.4.4: those bytes complete
  the in-progress section before new ones begin). Complements the 3.1.1
  within-payload concatenation fix; together they close all of #29. On a real
  EIT-heavy DVB-T2 capture this recovered emitted sections 51 → 237 (484
  completed), all CRC-valid. (#29)

## 3.1.1 — 2026-06-07

### Fixed
- `ts::SectionReassembler::feed` now extracts **all** sections concatenated in
  a single TS payload, not just the first. Sections packed after the
  `pointer_field` (legal per EN 300 468 §5.1.4; common on EMM PIDs) were
  silently dropped — table-agnostic SI/EMM data loss. Consumers should drain
  with `while let Some(s) = r.pop_section()`. (#29)

## 3.1.0 — 2026-06-05

The 3.x line: descriptor loops become typed, zero-copy `DescriptorLoop`s; serde
goes Serialize-only across the workspace (JSON is a display/export format); the
SIT service loop is typed; `Cat`/`Tsdt`/`Sit` move from owned to borrowed; and a
new optional `yoke` feature lets a parsed view outlive its input buffer. Wire
parsing is byte-identical throughout — only field types and JSON output change.
See [`MIGRATION-3.1.md`](MIGRATION-3.1.md) for every breaking change with
before/after code. (#21, #23, #27)

### Breaking

- **`DescriptorLoop<'a>` replaces raw descriptor-loop byte fields.** Every true
  SI descriptor loop on a table is now `DescriptorLoop<'a>` instead of `&'a [u8]`
  (or `Vec<u8>`). Call `.iter()` to walk it into typed `AnyDescriptor`s, `.raw()`
  for the wire bytes, or rely on `Deref<Target=[u8]>` for length/indexing.
  `parse_loop` is unchanged and still works on free slices.
  Affected fields: `SdtService.descriptors`, `EitEvent.descriptors`,
  `PmtStream.es_info`, `Pmt.program_info`, `NitTransportStream.descriptors`,
  `Nit.network_descriptors`, `BatTransportStream.descriptors`,
  `Bat.bouquet_descriptors`, `AitApplication.descriptors`,
  `Ait.common_descriptors`, `Tot.descriptors`, `Rct.descriptors`,
  `Rnt.common_descriptors`, `Int.platform_descriptors`,
  `Unt.common_descriptors`, `Cat.descriptors`, `Tsdt.descriptors`,
  `Sit.transmission_info_descriptors`.
  See [MIGRATION-3.1.md §1](MIGRATION-3.1.md#1-descriptor-loop-fields-u8--vecu8--descriptorloopa).
- **`Cat`, `Tsdt`, `Sit` are now borrowed (`<'a>`).** They previously owned their
  loops (`Vec<u8>`) and had no lifetime; they now borrow the section bytes like
  every other table. See
  [MIGRATION-3.1.md §2](MIGRATION-3.1.md#2-three-tables-moved-from-owned-to-borrowed).
- **`Deserialize` dropped — serde is Serialize-only.** Every `Deserialize`
  derive/impl is removed (including the manual `text::LangCode` impl) along with
  the now-dead `serde(borrow)` / `serde(bound(deserialize = …))` attributes. JSON
  is a display/export format; reconstruct values by re-`parse`-ing the wire bytes.
  `Serialize` output is unchanged. Affected container structs include `Sdt`,
  `SdtService`, `Eit`, `EitEvent`, `Pmt`, `PmtStream`, `Nit`,
  `NitTransportStream`, `Bat`, `BatTransportStream`, `Ait`, `AitApplication`,
  `Tot`, `Rct`, `Rnt`, `Int`, `Unt`, `Cat`, `Tsdt`, `Sit`. See
  [MIGRATION-3.1.md §3](MIGRATION-3.1.md#3-deserialize-dropped--serde-is-serialize-only).
- **serde JSON shape change.** A descriptor-loop field serializes as an array of
  typed, decoded descriptors (camelCase variant keys) instead of an array of raw
  bytes; per-entry parse errors surface as `{"parseError": "…"}`. See
  [MIGRATION-3.1.md §4](MIGRATION-3.1.md#4-serde-json-shape-change).
- **SIT service loop is typed.** `Sit.service_loop: &'a [u8]` is replaced by
  `Sit.services: Vec<SitService<'a>>`, where
  `SitService { service_id: u16, running_status: u8, descriptors: DescriptorLoop<'a> }`
  — mirroring `SdtService` (EN 300 468 §7.1.2, Table 164). The serialized JSON
  drops the raw `service_loop` byte array in favour of typed `services` entries,
  each with their own typed `descriptors` sequence. See
  [MIGRATION-3.1.md §5](MIGRATION-3.1.md#5-sit-service-loop-is-typed).

### Added

- `descriptors::DescriptorLoop<'a>` — a borrowed, zero-copy descriptor-loop
  newtype (the table-loop analogue of `DvbText`): `new`/`raw`/`iter`,
  `Deref<[u8]>`, `From<&[u8]>`, `IntoIterator`, a cheap `Debug`
  (`DescriptorLoop(<N bytes>)`), and serialize-only serde over the typed walk.
- **`yoke` feature (off by default, additive).** `yoke::Yokeable` is derived on
  every public zero-copy view type — all table views (`Pmt`/`PmtStream`, `Sdt`/
  `SdtService`, `Eit`/`EitEvent`, `Nit`/`Bat`/`Cat`/`Tsdt`/`Sit`/`Ait`/`Tot`/
  `Int`/`Unt`/`Rct`/`Rnt`/`Cit`/`Container`, the DSM-CC/MPE/protection/SAT/font
  sections, …), every borrowing descriptor struct, `DescriptorLoop`, `DvbText`,
  and the `AnyTable` / `AnyDescriptor` enums. A new `owned` module adds
  `Owned<Y>`, a `'static`, `Send + Sync`, cheaply-`Clone` bundle of the backing
  `Arc<[u8]>` and the parsed view: own a parsed table past the input buffer's
  borrow (struct field, cache, `watch`/broadcast channel, cross-thread) without
  re-parsing or a hand-written mirror type. The feature is optional and adds no
  dependencies to default builds. See
  [MIGRATION-3.1.md §6](MIGRATION-3.1.md#6-owning-a-parsed-view-the-yoke-feature). (#27)

## 2.1.0 — 2026-06-05

### Added
- `AnyTable::name()` — macro-generated diagnostic label from each type's
  `TableDef::NAME` (`"PROGRAM_ASSOCIATION"`, …); `"UNKNOWN"` for the
  fallthrough variant. (#18)
- `AnyDescriptor::name()` — same, with `"CUSTOM"` for runtime-registered
  `Other` descriptors.
- `si_dump` example simplified accordingly (the 35-line variant match is gone).

## 2.0.0 — 2026-06-05

Typed, trait-driven client API: feed 188-byte TS packets, get decoded `SectionEvent`s
with structured typed tables and descriptors. See [`MIGRATION-2.0.md`](MIGRATION-2.0.md)
for every breaking change with before/after code. Tracks issue #16.

### Breaking

- **`DvbText<'a>` replaces `&'a [u8]` for Annex-A text fields.** Fields such as
  `event_name`, `service_name`, `provider_name`, `network_name`, and all multilingual
  name fields are now `DvbText<'a>`. Call `.raw()` to recover the wire bytes, `.decode()`
  for a `Cow<str>`, or rely on `Deref<Target=[u8]>` for length/indexing — all work as
  before. See [MIGRATION-2.0.md §1](MIGRATION-2.0.md#1-text-fields-u8--dvbtexta).
- **`LangCode` replaces `[u8; 3]` for language / country codes.** The raw bytes are in
  `.0`; `as_str()` returns a lossy `Cow<str>`; `Deref<Target=[u8;3]>` still works. See
  [MIGRATION-2.0.md §2](MIGRATION-2.0.md#2-language--country-codes-u8-3--langcode).
- **`Deserialize` dropped on text-bearing structs.** Structs that contain `DvbText` now
  derive `Serialize` only (re-encoding decoded UTF-8 back to DVB charset bytes is lossy).
  Affected: `BouquetNameDescriptor`, `ComponentDescriptor`, `DataBroadcastDescriptor`,
  `ExtendedEventDescriptor`, `ExtensionDescriptor`,
  `MultilingualBouquetNameDescriptor`, `MultilingualComponentDescriptor`,
  `MultilingualNetworkNameDescriptor`, `MultilingualServiceNameDescriptor`,
  `NetworkNameDescriptor`, `ServiceDescriptor`, `ShortEventDescriptor`. See
  [MIGRATION-2.0.md §3](MIGRATION-2.0.md#3-deserialize-dropped-on-text-bearing-structs).
- **Subset `Descriptor` enum removed — replaced by `AnyDescriptor` + `parse_loop`.**
  The 1.x `descriptors::Descriptor` enum (a handful of context-free tags) is gone.
  Use `descriptors::parse_loop(loop_bytes)` for a lazy iterator that covers all
  0x05–0x7F tags plus `Unknown` passthrough, and `AnyDescriptor` as the item type. See
  [MIGRATION-2.0.md §4](MIGRATION-2.0.md#4-subset-descriptor-enum-removed--anydescriptor--parse_loop).
- **serde JSON shape changed.** `DvbText` serializes as a decoded UTF-8 string (not a
  byte array); `LangCode` serializes as a 3-char string. `AnyTable` / `AnyDescriptor`
  use external camelCase tagging (`{"shortEvent":{…}}`); inner struct field names stay
  `snake_case`. See
  [MIGRATION-2.0.md §5](MIGRATION-2.0.md#5-serde-json-shape-change).
- **`pid::well_known` constants are now `Pid` values, not `u16`.** Call `.value()` or
  `u16::from(pid)` to recover the raw integer. See
  [MIGRATION-2.0.md §6](MIGRATION-2.0.md#6-pidwell_known-constants-u16--pid).

### Added

- **`demux::SiDemux`** (feature `ts`) — PID-filtered, version-gated, PAT-following
  section pump. Feed 188-byte TS packets with `SiDemux::feed(&packet)`, iterate over
  `SectionEvent` values for changed sections only. Builder: `follow_pat`, `dvb_si_pids`,
  `.pid(Pid)`, `emit_repeats`, `gate_capacity`. Stats in `SiDemux::stats()`.
- **`demux::SectionEvent`** — owning-`Bytes` event: `.pid()`, `.table_id()`,
  `.version()`, `.table() -> Result<AnyTable<'_>>`, `.parse::<T>()`.
- **`tables::AnyTable`** — macro-generated dispatch enum; `AnyTable::parse(bytes)`
  dispatches on `table_id` across all 29 implemented table types; `Unknown` fallthrough.
  Driven by `TableDef` trait + `declare_tables!` macro (single source of truth, drift
  tested).
- **`descriptors::AnyDescriptor`** — macro-generated dispatch enum covering all
  0x05–0x7F tags plus `LogicalChannel` (opt-in via `DescriptorRegistry`) and `Unknown`.
  Driven by `DescriptorDef` trait + `declare_descriptors!` macro.
- **`descriptors::parse_loop`** — lazy `DescriptorIter` over a raw descriptor loop.
  Never panics; per-descriptor parse errors yield `Err` and iteration continues;
  truncated tail yields one `Err` then fuses; unknown tags become `Unknown`.
- **`descriptors::DescriptorRegistry`** — runtime registration of private/context-
  dependent descriptors (`register::<T>()`); `.with_logical_channel()` enables the 0x83
  built-in. `Other { tag, value }` variant supports `downcast_ref::<T>()`; `erased-serde`
  serializes custom types when `serde` feature is active.
- **`pid::Pid`** newtype — `Copy`, `Ord`, `Hash`, `Display` as `0xNNNN`; `well_known`
  constants upgraded to this type.
- **`examples/si_dump.rs`** — CLI demux tool: `cargo run -p dvb-si --example si_dump
  -- file.ts [--json]`. Prints one line per changed section; `--json` emits decoded JSON.
- **End-to-end capture tests** — `tests/demux_e2e.rs` runs the full M6 HbbTV fixture
  through `SiDemux`, asserts typed variant coverage, double-feed produces zero events,
  and EIT descriptor JSON contains decoded text strings.
- **Hostility suite** — `tests/hostility.rs`: 10 000 PRNG-garbage + every truncation
  length into `SiDemux` / `parse_loop` — no panics, counters advance. Issue #16.

## 1.1.0 — 2026-06-04

**Coverage milestone: every allocated `descriptor_tag` in EN 300 468 V1.19.1
Table 12 (0x40–0x7F) is implemented** — 41 new descriptor modules, each with
a symmetric `Parse`/`Serialize` pair, spec-cited layout, and round-trip tests.

### Added

**Descriptors** (EN 300 468 unless noted)
- 0x42 stuffing, 0x45 VBI_data, 0x46 VBI_teletext, 0x49 country_availability,
  0x4B NVOD_reference, 0x4C time_shifted_service, 0x4F time_shifted_event,
  0x51 mosaic (typed cell/elementary-cell loops + cell_linkage variants),
  0x53 CA_identifier, 0x57 telephone, 0x5B–0x5E multilingual
  network_name/bouquet_name/service_name/component, 0x5F
  private_data_specifier, 0x60 service_move, 0x61 short_smoothing_buffer,
  0x63 partial_transport_stream, 0x64 data_broadcast, 0x65 scrambling,
  0x66 data_broadcast_id, 0x67 transport_stream, 0x68 DSNG, 0x69 PDC,
  0x6B ancillary_data, 0x6C cell_list, 0x6D cell_frequency_link,
  0x6E announcement_support, 0x70 adaptation_field_data,
  0x72 service_availability, 0x7B DTS (Annex G), 0x7C AAC (Annex H),
  0x7E FTA_content_management
- 0x6F application_signalling, 0x71 service_identifier (TS 102 809)
- 0x74 related_content, 0x75 TVA_id (TS 102 323)
- 0x77 time_slice_fec_identifier, 0x78 ECM_repetition_rate (EN 301 192)
- 0x7D XAIT_location (TS 102 727, newly vendored)
- 0x7F extension descriptor — typed `descriptor_tag_extension` discriminant
  with 14 typed extension bodies (T2/C2/C2-bundle/S2X delivery systems,
  supplementary_audio, network_change_notify, message, target_region(_name),
  service_relocated, URI_linkage, AC-4, audio_preselection, TTML_subtitling)
  and a raw-preserving fallthrough: unknown tag_extensions round-trip
  byte-exact.

**Text** — full Annex A Table A.3 selector coverage: 0x12 KS X 1001 (EUC-KR),
0x13 GB-2312 (via GBK), 0x14 Big5, 0x1F encoding_type_id escape; two-byte
control codes (U+E080–U+E09F, Table A.2) now honored alongside the
single-byte set.

### Fixed (audit round 5, pre-release)
- extension/S2X_satellite_delivery_system (0x7F/0x17): byte-1 bit layout
  corrected to Table 140 — S2X_mode at bits [7:6] and
  scrambling_sequence_selector at bit [5] (were [4:3] and [2]).
- extension/C2_bundle_delivery_system (0x7F/0x16): bundle entries are 8 bytes
  per Table 139, not 9 — multi-entry descriptors no longer misalign.
- extension/supplementary_audio (0x7F/0x06): flags-byte bit 1 is plain
  `reserved_future_use` → now serialized as 1 per the crate convention.
- Doc-cite corrections: time_shifted_event §6.2.44, time_shifted_service
  §6.2.45, multilingual_component §6.2.23, multilingual_service_name §6.2.25,
  related_content §10.4.1.

## 1.0.1 — 2026-06-04

Docs-only: README rewritten around an explicit implementation matrix — per
table_id status, carousel message coverage, typed-descriptor list, spec
grounding. No code changes.

## 1.0.0 — 2026-06-04

First substantive release covering the MPEG-2 PSI and DVB SI table set, the
common descriptors, and the DVB-allocated companion tables.

### Added

**Framing**
- `Section<'a>` — long/short-form PSI/SI section header with CRC-32 validation
- `TsPacket<'a>` + `SectionReassembler` under feature `ts`

**Tables** (each with `Parse` + `Serialize` round-trip tests)
- MPEG-2 PSI: `Pat` (0x00), `Cat` (0x01), `Pmt` (0x02), `Tsdt` (0x03)
- DVB SI: `Nit` (0x40/0x41), `Sdt` (0x42/0x46), `Bat` (0x4A), `Eit` (0x4E–0x6F),
  `Tdt` (0x70), `Rst` (0x71), `St` (0x72), `Tot` (0x73), `Dit` (0x7E), `Sit` (0x7F)
- `Sat` — Satellite Access Table family (0x4D, §5.2.11)
- `Ait` (0x74, TS 102 809), `DsmccSection` (0x3A–0x3F)
- Companion tables: `Unt` (0x4B, TS 102 006), `Int` (0x4C, EN 301 192),
  `Container` (0x75), `Rct` (0x76), `Cit` (0x77), `Rnt` (0x79) (TS 102 323)
- MPE family: `MpeDatagramSection` (0x3E, EN 301 192 §7 — typed IP/MAC view),
  `MpeFec` (0x78, EN 301 192 §9.9), `MpeIfec` (0x7A, TS 102 772)
- `ProtectionMessageSection` (0x7B, TS 102 809 §9) — authentication-message
  and certificate-collection variants discriminated by table_id_extension
- `DownloadableFontInfoSection` (0x7C, EN 303 560; table_id per EN 300 468
  V1.19.1 Table 2 NOTE 2 — the spec's own 0x4C was an acknowledged accident)
- **Coverage milestone: every allocated table_id in EN 300 468 V1.19.1
  Table 2 is implemented.**
- `carousel` module — DSM-CC data-carousel download protocol (ISO/IEC
  13818-6 §7.2/§7.3, DVB-profiled): typed `Dsi`/`Dii`/`DownloadDataBlock`
  messages over the `tables::dsmcc` section framing, plus
  `ModuleReassembler` for DDB→module collection. Layout provenance in
  `docs/iso_13818_6_carousel.md`; pinned against the live m6-single.ts
  capture and round-tripped byte-exact against broadcast bytes.

**Descriptors** — typed parsers for the common DVB + MPEG-2 descriptors
(network_name, service, service_list, linkage, short/extended_event, component,
content, parental_rating, CA, satellite/cable/terrestrial/S2 delivery system,
local_time_offset, subtitling, teletext, AC-3 / Enhanced AC-3, logical_channel,
default_authority, content_identifier, registration, stream_identifier,
data_stream_alignment, frequency_list, bouquet_name, private_data_indicator,
iso_639_language). Descriptors not yet typed pass through as raw bytes.

**Text** — Annex A subset: ISO 6937 (with diacritic combining), ISO 8859-n,
UTF-8 (selector 0x15), UCS-2 BE (0x11); Annex A.2 control codes.

**CRC** — Annex C MPEG-2 CRC-32 (from `dvb_common`).

**Typed constants** — `TableId`, `DescriptorTag`, `pid::well_known`.

**Feature flags** — `chrono`, `ts`, `serde`.

### Fixed
- `Tsdt` (0x03): removed a phantom `descriptor_loop_length` field — per
  ISO/IEC 13818-1 §2.4.4.12 descriptors run directly from byte 8 to the CRC,
  bounded by `section_length`.
- `Nit` / `Sdt`: added the `section_number` / `last_section_number` fields
  (previously parsed-and-discarded, serialized as 0), so multi-section
  sub-tables round-trip faithfully.
- `St` (0x72): byte-1 reserved nibble now `0x70` (reserved_future_use = 1),
  matching the other short-form sections (DIT/RST/TDT/TOT).
- `satellite_delivery_system` (0x43): corrected table cites — Polarization
  Table 38, Roll-off Table 39, Modulation system Table 40, Modulation type
  Table 41; `symbol_rate_bcd` masked to 28 bits on serialize.
- `s2_satellite_delivery_system` (0x79): `reserved_zero_future_use` (bit 5) now
  serialized as 0 per §6.2.13.3 Table 42 (was incorrectly 1).
- `content_identifier` (0x76): dropped the unreachable `CridLocation::Reserved`
  variant (reserved locations have no defined length and are rejected on parse).
- Doc-cite fixes: `teletext` type coding is Table 102 (not 99); `bouquet_name`
  §6.2.6, `network_name` §6.2.28, `parental_rating` §6.2.30, `service_list`
  §6.2.36, `stream_identifier` §6.2.41.
- `St`: accepts any `data_byte` value per §5.2.8 ("may take any value and has
  no meaning") — previously rejected non-zero stuffing, breaking real 0xFF fill.
- `Tot`: serialize emits `section_syntax_indicator = 0` per §5.2.6 (the TOT
  exception: SSI=0 yet CRC_32 present); was emitting SSI=1.
- `Sat`: PID corrected to 0x001B (EN 300 468 Table 1) — was wrongly 0x0010
  (the NIT PID); `pid::well_known::SAT` added.
- `Cat`: descriptor loop now preserved raw (`descriptors: Vec<u8>`) so non-CA
  descriptors round-trip; typed CA view via `Cat::ca_descriptors()`.
- `Cit`: dropped the desync-prone `prepend_strings_length` field (derived from
  the slice on serialize, guarded ≤ 255).
- `Bat`: no longer verifies CRC inside `parse` — crate-wide contract is that
  CRC validation belongs to `Section::validate_crc`; BAT was the lone
  inconsistent exception.
- `Nit`/`Bat`: per-entry `BufferTooShort` had `need`/`have` swapped.
- Removed the advertised-but-unimplemented `smallvec` and `rayon` feature
  flags (no code used either dependency).
- Text decoding: the default Latin table is now glyph-for-glyph faithful to
  EN 300 468 V1.19.1 Figure A.1 (transcribed in `docs/en_300_468.md`). Notable
  corrections: 0xA4 → € U+20AC (was ¤), 0xFC → þ (was œ), 0xFD → ŧ (was ı),
  0xFF → soft hyphen; quotes/arrows/fractions and the D/E/F rows no longer
  fall back to Latin-1. The full non-spacing row (macron, breve, dot, ring,
  double acute, ogonek, caron) is handled with precomposed forms and a
  base + combining-mark fallback. 0xA8 = ¤ confirmed correct against the PDF.

### Notes
- Tables and descriptors parse their outer structure with typed fields; nested
  descriptor and repeated loops are borrowed as raw `&[u8]` slices for the
  consumer to walk with the descriptor parsers.
