1use crate::error::{Error, Result};
11use crate::length;
12use alloc::vec::Vec;
13use dvb_common::{Parse, Serialize};
14
15pub mod tags {
17 pub const SB: u8 = 0x80;
19 pub const RCV: u8 = 0x81;
21 pub const CREATE_T_C: u8 = 0x82;
23 pub const C_T_C_REPLY: u8 = 0x83;
25 pub const DELETE_T_C: u8 = 0x84;
27 pub const D_T_C_REPLY: u8 = 0x85;
29 pub const REQUEST_T_C: u8 = 0x86;
31 pub const NEW_T_C: u8 = 0x87;
33 pub const T_C_ERROR: u8 = 0x88;
35 pub const DATA_LAST: u8 = 0xA0;
37 pub const DATA_MORE: u8 = 0xA1;
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize))]
46pub struct SbValue(pub u8);
47
48impl SbValue {
49 #[must_use]
51 pub const fn data_available(self) -> bool {
52 self.0 & 0x80 != 0
53 }
54 #[must_use]
56 pub const fn new(data_available: bool) -> Self {
57 Self(if data_available { 0x80 } else { 0x00 })
58 }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63#[cfg_attr(feature = "serde", derive(serde::Serialize))]
64#[non_exhaustive]
65pub enum DataBlock {
66 Last,
68 More,
70}
71
72impl DataBlock {
73 fn from_tag(tag: u8) -> Option<Self> {
74 match tag {
75 tags::DATA_LAST => Some(Self::Last),
76 tags::DATA_MORE => Some(Self::More),
77 _ => None,
78 }
79 }
80 #[must_use]
82 pub fn to_tag(self) -> u8 {
83 match self {
84 Self::Last => tags::DATA_LAST,
85 Self::More => tags::DATA_MORE,
86 }
87 }
88 #[must_use]
90 pub fn name(&self) -> &'static str {
91 match self {
92 Self::Last => "data_last",
93 Self::More => "data_more",
94 }
95 }
96}
97dvb_common::impl_spec_display!(DataBlock);
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize))]
106pub struct TcObject {
107 pub tag: u8,
110 pub t_c_id: u8,
112}
113
114impl<'a> Parse<'a> for TcObject {
115 type Error = Error;
116 fn parse(bytes: &'a [u8]) -> Result<Self> {
117 if bytes.is_empty() {
118 return Err(Error::BufferTooShort {
119 need: 1,
120 have: 0,
121 what: "TcObject",
122 });
123 }
124 let tag = bytes[0];
125 let (len, hdr) = length::decode(&bytes[1..])?;
126 if len != 1 {
127 return Err(Error::InvalidObject {
128 what: "TcObject",
129 reason: "length_field must be 1",
130 });
131 }
132 let t_c_id = *bytes.get(1 + hdr).ok_or(Error::BufferTooShort {
133 need: 1 + hdr + 1,
134 have: bytes.len(),
135 what: "TcObject t_c_id",
136 })?;
137 Ok(Self { tag, t_c_id })
138 }
139}
140impl Serialize for TcObject {
141 type Error = Error;
142 fn serialized_len(&self) -> usize {
143 3 }
145 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
146 if buf.len() < 3 {
147 return Err(Error::OutputBufferTooSmall {
148 need: 3,
149 have: buf.len(),
150 });
151 }
152 buf[0] = self.tag;
153 buf[1] = 1;
154 buf[2] = self.t_c_id;
155 Ok(3)
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161#[cfg_attr(feature = "serde", derive(serde::Serialize))]
162pub struct NewTc {
163 pub t_c_id: u8,
165 pub new_t_c_id: u8,
167}
168
169impl<'a> Parse<'a> for NewTc {
170 type Error = Error;
171 fn parse(bytes: &'a [u8]) -> Result<Self> {
172 let body = parse_fixed(bytes, tags::NEW_T_C, 2, "New_T_C")?;
173 Ok(Self {
174 t_c_id: body[0],
175 new_t_c_id: body[1],
176 })
177 }
178}
179impl Serialize for NewTc {
180 type Error = Error;
181 fn serialized_len(&self) -> usize {
182 4
183 }
184 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
185 write_fixed(tags::NEW_T_C, &[self.t_c_id, self.new_t_c_id], buf)
186 }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize))]
192pub struct TcError {
193 pub t_c_id: u8,
195 pub error_code: u8,
197}
198
199impl<'a> Parse<'a> for TcError {
200 type Error = Error;
201 fn parse(bytes: &'a [u8]) -> Result<Self> {
202 let body = parse_fixed(bytes, tags::T_C_ERROR, 2, "T_C_Error")?;
203 Ok(Self {
204 t_c_id: body[0],
205 error_code: body[1],
206 })
207 }
208}
209impl Serialize for TcError {
210 type Error = Error;
211 fn serialized_len(&self) -> usize {
212 4
213 }
214 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
215 write_fixed(tags::T_C_ERROR, &[self.t_c_id, self.error_code], buf)
216 }
217}
218
219#[derive(Debug, Clone, PartialEq, Eq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize))]
223pub struct CommandTpdu<'a> {
224 pub tag: u8,
226 pub t_c_id: u8,
228 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
230 pub data: &'a [u8],
231}
232
233impl<'a> Parse<'a> for CommandTpdu<'a> {
234 type Error = Error;
235 fn parse(bytes: &'a [u8]) -> Result<Self> {
236 if bytes.is_empty() {
237 return Err(Error::BufferTooShort {
238 need: 1,
239 have: 0,
240 what: "C_TPDU",
241 });
242 }
243 let tag = bytes[0];
244 let (len, hdr) = length::decode(&bytes[1..])?;
245 if len == 0 {
246 return Err(Error::InvalidObject {
247 what: "C_TPDU",
248 reason: "length_field must include t_c_id (>=1)",
249 });
250 }
251 let start = 1 + hdr;
252 let end = start + len;
253 if bytes.len() < end {
254 return Err(Error::LengthMismatch {
255 what: "C_TPDU",
256 declared: len,
257 actual: bytes.len().saturating_sub(start),
258 });
259 }
260 Ok(Self {
261 tag,
262 t_c_id: bytes[start],
263 data: &bytes[start + 1..end],
264 })
265 }
266}
267impl Serialize for CommandTpdu<'_> {
268 type Error = Error;
269 fn serialized_len(&self) -> usize {
270 let len_value = 1 + self.data.len();
271 1 + length::encoded_len(len_value) + len_value
272 }
273 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
274 let len_value = 1 + self.data.len();
275 let total = 1 + length::encoded_len(len_value) + len_value;
276 if buf.len() < total {
277 return Err(Error::OutputBufferTooSmall {
278 need: total,
279 have: buf.len(),
280 });
281 }
282 buf[0] = self.tag;
283 let mut pos = 1 + length::encode_into(len_value, &mut buf[1..])?;
284 buf[pos] = self.t_c_id;
285 pos += 1;
286 buf[pos..pos + self.data.len()].copy_from_slice(self.data);
287 Ok(pos + self.data.len())
288 }
289}
290
291#[derive(Debug, Clone, PartialEq, Eq)]
294#[cfg_attr(feature = "serde", derive(serde::Serialize))]
295pub struct ResponseTpdu<'a> {
296 pub tag: u8,
298 pub t_c_id: u8,
300 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
302 pub data: &'a [u8],
303 pub sb_value: SbValue,
305 pub block: Option<DataBlock>,
308}
309
310impl<'a> Parse<'a> for ResponseTpdu<'a> {
311 type Error = Error;
312 fn parse(bytes: &'a [u8]) -> Result<Self> {
313 if bytes.is_empty() {
314 return Err(Error::BufferTooShort {
315 need: 1,
316 have: 0,
317 what: "R_TPDU",
318 });
319 }
320 let tag = bytes[0];
321 let (len, hdr) = length::decode(&bytes[1..])?;
322 if len == 0 {
323 return Err(Error::InvalidObject {
324 what: "R_TPDU",
325 reason: "length_field must include t_c_id (>=1)",
326 });
327 }
328 let start = 1 + hdr;
329 let data_end = start + len;
330 let status_end = data_end + 4;
332 if bytes.len() < status_end {
333 return Err(Error::BufferTooShort {
334 need: status_end,
335 have: bytes.len(),
336 what: "R_TPDU status",
337 });
338 }
339 if bytes[data_end] != tags::SB {
340 return Err(Error::UnexpectedTpduTag {
341 got: bytes[data_end],
342 expected: tags::SB,
343 what: "R_TPDU SB_tag",
344 });
345 }
346 if bytes[data_end + 1] != 2 {
347 return Err(Error::InvalidObject {
348 what: "R_TPDU status",
349 reason: "SB length_field must be 2",
350 });
351 }
352 Ok(Self {
354 tag,
355 t_c_id: bytes[start],
356 data: &bytes[start + 1..data_end],
357 sb_value: SbValue(bytes[data_end + 3]),
358 block: DataBlock::from_tag(tag),
359 })
360 }
361}
362impl Serialize for ResponseTpdu<'_> {
363 type Error = Error;
364 fn serialized_len(&self) -> usize {
365 let len_value = 1 + self.data.len();
366 1 + length::encoded_len(len_value) + len_value + 4
367 }
368 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
369 let total = self.serialized_len();
370 if buf.len() < total {
371 return Err(Error::OutputBufferTooSmall {
372 need: total,
373 have: buf.len(),
374 });
375 }
376 let len_value = 1 + self.data.len();
377 buf[0] = self.tag;
378 let mut pos = 1 + length::encode_into(len_value, &mut buf[1..])?;
379 buf[pos] = self.t_c_id;
380 pos += 1;
381 buf[pos..pos + self.data.len()].copy_from_slice(self.data);
382 pos += self.data.len();
383 buf[pos] = tags::SB;
385 buf[pos + 1] = 2;
386 buf[pos + 2] = self.t_c_id;
387 buf[pos + 3] = self.sb_value.0;
388 Ok(pos + 4)
389 }
390}
391
392fn parse_fixed<'a>(
395 bytes: &'a [u8],
396 expected: u8,
397 body_len: usize,
398 what: &'static str,
399) -> Result<&'a [u8]> {
400 if bytes.is_empty() {
401 return Err(Error::BufferTooShort {
402 need: 1,
403 have: 0,
404 what,
405 });
406 }
407 if bytes[0] != expected {
408 return Err(Error::UnexpectedTpduTag {
409 got: bytes[0],
410 expected,
411 what,
412 });
413 }
414 let (len, hdr) = length::decode(&bytes[1..])?;
415 if len != body_len {
416 return Err(Error::InvalidObject {
417 what,
418 reason: "unexpected length_field",
419 });
420 }
421 let start = 1 + hdr;
422 let end = start + body_len;
423 if bytes.len() < end {
424 return Err(Error::BufferTooShort {
425 need: end,
426 have: bytes.len(),
427 what,
428 });
429 }
430 Ok(&bytes[start..end])
431}
432
433fn write_fixed(tag: u8, body: &[u8], buf: &mut [u8]) -> Result<usize> {
434 let total = 2 + body.len();
435 if buf.len() < total {
436 return Err(Error::OutputBufferTooSmall {
437 need: total,
438 have: buf.len(),
439 });
440 }
441 buf[0] = tag;
442 buf[1] = body.len() as u8;
443 buf[2..2 + body.len()].copy_from_slice(body);
444 Ok(total)
445}
446
447#[must_use]
449pub fn create_t_c(t_c_id: u8) -> TcObject {
450 TcObject {
451 tag: tags::CREATE_T_C,
452 t_c_id,
453 }
454}
455
456#[must_use]
458pub fn tc_object_bytes(o: &TcObject) -> Vec<u8> {
459 o.to_bytes()
460}
461
462#[cfg(test)]
463mod tests {
464 use super::*;
465
466 #[test]
467 fn tc_object_round_trip() {
468 let o = create_t_c(0x01);
469 let bytes = o.to_bytes();
470 assert_eq!(bytes, [0x82, 0x01, 0x01]);
471 assert_eq!(TcObject::parse(&bytes).unwrap(), o);
472 }
473
474 #[test]
475 fn new_tc_round_trip() {
476 let n = NewTc {
477 t_c_id: 1,
478 new_t_c_id: 2,
479 };
480 let bytes = n.to_bytes();
481 assert_eq!(bytes, [0x87, 0x02, 0x01, 0x02]);
482 assert_eq!(NewTc::parse(&bytes).unwrap(), n);
483 }
484
485 #[test]
486 fn tc_error_round_trip() {
487 let e = TcError {
488 t_c_id: 3,
489 error_code: 1,
490 };
491 let bytes = e.to_bytes();
492 assert_eq!(bytes, [0x88, 0x02, 0x03, 0x01]);
493 assert_eq!(TcError::parse(&bytes).unwrap(), e);
494 }
495
496 #[test]
497 fn command_tpdu_round_trip() {
498 let c = CommandTpdu {
499 tag: tags::DATA_LAST,
500 t_c_id: 1,
501 data: &[0xAA, 0xBB, 0xCC],
502 };
503 let bytes = c.to_bytes();
504 assert_eq!(bytes, [0xA0, 0x04, 0x01, 0xAA, 0xBB, 0xCC]);
506 assert_eq!(CommandTpdu::parse(&bytes).unwrap(), c);
507 }
508
509 #[test]
510 fn receive_data_command_no_payload() {
511 let c = CommandTpdu {
512 tag: tags::RCV,
513 t_c_id: 1,
514 data: &[],
515 };
516 let bytes = c.to_bytes();
517 assert_eq!(bytes, [0x81, 0x01, 0x01]);
518 assert_eq!(CommandTpdu::parse(&bytes).unwrap(), c);
519 }
520
521 #[test]
522 fn response_tpdu_round_trip_with_status() {
523 let r = ResponseTpdu {
524 tag: tags::DATA_LAST,
525 t_c_id: 1,
526 data: &[0x9F, 0x80, 0x30, 0x00],
527 sb_value: SbValue::new(true),
528 block: Some(DataBlock::Last),
529 };
530 let bytes = r.to_bytes();
531 assert_eq!(
533 bytes,
534 [0xA0, 0x05, 0x01, 0x9F, 0x80, 0x30, 0x00, 0x80, 0x02, 0x01, 0x80]
535 );
536 let parsed = ResponseTpdu::parse(&bytes).unwrap();
537 assert_eq!(parsed, r);
538 assert!(parsed.sb_value.data_available());
539 assert_eq!(parsed.block, Some(DataBlock::Last));
540 }
541
542 #[test]
543 fn mutating_data_changes_bytes() {
544 let c = CommandTpdu {
545 tag: tags::DATA_LAST,
546 t_c_id: 1,
547 data: &[0xAA],
548 };
549 let a = c.to_bytes();
550 let b = CommandTpdu {
551 tag: tags::DATA_LAST,
552 t_c_id: 1,
553 data: &[0xBB],
554 }
555 .to_bytes();
556 assert_ne!(a, b);
557 }
558
559 #[test]
560 fn sb_value_da() {
561 assert!(SbValue::new(true).data_available());
562 assert!(!SbValue::new(false).data_available());
563 }
564}