1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! Shared wire plumbing for the "language code + length-prefixed text"
//! entry loops: `multilingual_network_name` (§6.2.24), `multilingual_bouquet_name`
//! (§6.2.22), `multilingual_service_name` (§6.2.25), `multilingual_component`
//! (§6.2.23) and the AIT `application_name_descriptor` (TS 102 809 §5.3.5.6.1).
//!
//! Every one is a loop of `ISO_639_language_code (24) + text_length (8) +
//! text`, differing only in the field *names* (kept on each descriptor's own
//! public entry type) and, for the service-name descriptor, in carrying two
//! strings per entry. The five modules carried ~1,000 duplicated lines in
//! which the over-range check had drifted (some returned `InvalidDescriptor`,
//! some `FieldOverflow`) — audit r03-O1, #1141. The reader and writer below
//! are the one implementation; every serializer length goes through
//! `broadcast_common::len::fit_u8`.
use crate::error::{Error, Result};
use crate::text::{DvbText, LangCode};
/// `ISO_639_language_code` width, bytes.
pub(super) const LANG_LEN: usize = 3;
/// Width of the 8-bit `text_length` prefix, bytes.
pub(super) const LEN_FIELD: usize = 1;
/// Wire size of one length-prefixed text field.
pub(super) fn text_field_len(text: &DvbText<'_>) -> usize {
LEN_FIELD + text.len()
}
/// Forward cursor over a descriptor body's entry loop.
pub(super) struct EntryReader<'a> {
body: &'a [u8],
pos: usize,
tag: u8,
}
impl<'a> EntryReader<'a> {
/// Start reading `body` at byte `pos` (past any fixed leading fields).
pub(super) fn new(body: &'a [u8], pos: usize, tag: u8) -> Self {
Self { body, pos, tag }
}
/// Whether any bytes remain to be read.
pub(super) fn has_more(&self) -> bool {
self.pos < self.body.len()
}
fn err(&self, reason: &'static str) -> Error {
Error::InvalidDescriptor {
tag: self.tag,
reason,
}
}
/// Read the language code, requiring the first `text_length` byte to be
/// present too (the entry header).
pub(super) fn lang(&mut self) -> Result<LangCode> {
if self.pos + LANG_LEN + LEN_FIELD > self.body.len() {
return Err(self.err("entry header runs past descriptor end"));
}
let b = self.body;
let p = self.pos;
self.pos += LANG_LEN;
Ok(LangCode([b[p], b[p + 1], b[p + 2]]))
}
/// Read one length-prefixed text. `reserve` is the number of bytes that
/// must still follow the text (e.g. the next `text_length` byte of a
/// two-string entry); a text whose end plus `reserve` overruns the body
/// is rejected with `overrun_reason`.
pub(super) fn text(
&mut self,
overrun_reason: &'static str,
reserve: usize,
) -> Result<DvbText<'a>> {
// Checked indexing throughout: a missing length byte or an overrun is
// an error, never a panic, whatever the caller's `lang()` history.
let len = usize::from(
*self
.body
.get(self.pos)
.ok_or_else(|| self.err("entry header runs past descriptor end"))?,
);
let start = self.pos + LEN_FIELD;
let end = start + len;
let text = (end + reserve <= self.body.len())
.then(|| self.body.get(start..end))
.flatten()
.ok_or_else(|| self.err(overrun_reason))?;
self.pos = end;
Ok(DvbText::new(text))
}
}
/// Write `lang` at `pos`, returning the position after it.
pub(super) fn write_lang(buf: &mut [u8], pos: usize, lang: &LangCode) -> usize {
buf[pos..pos + LANG_LEN].copy_from_slice(&lang.0);
pos + LANG_LEN
}
/// Write one length-prefixed text at `pos`, returning the position after it.
/// The length goes through the checked `fit_u8` (a text over 255 bytes is
/// `FieldOverflow`, never a wrapped length).
pub(super) fn write_text(
buf: &mut [u8],
pos: usize,
text: &DvbText<'_>,
length_field: &'static str,
) -> Result<usize> {
buf[pos] = broadcast_common::len::fit_u8(text.len(), length_field)?;
let start = pos + LEN_FIELD;
buf[start..start + text.len()].copy_from_slice(text.raw());
Ok(start + text.len())
}
#[cfg(test)]
mod tests {
use super::*;
/// `text()` on an exhausted or truncated body is an error, not a panic.
#[test]
fn text_never_panics_on_short_bodies() {
let mut r = EntryReader::new(&[], 0, 0x5C);
assert!(r.text("overrun", 0).is_err());
let mut r = EntryReader::new(&[5, b'a'], 0, 0x5C);
assert!(r.text("overrun", 0).is_err());
let mut r = EntryReader::new(&[1, b'a'], 0, 0x5C);
assert!(r.text("overrun", 1).is_err());
let mut r = EntryReader::new(&[1, b'a'], 0, 0x5C);
assert_eq!(r.text("overrun", 0).unwrap().raw(), b"a");
}
}