tinybufr/
descriptor.rs

1//! Descriptors (FXY)
2
3use std::fmt::Debug;
4use std::io::Read;
5
6use byteorder::{BigEndian, ReadBytesExt};
7
8use crate::{
9    Error,
10    tables::{TableBEntry, TableDEntry, Tables},
11};
12
13/// Descriptor (FXY).
14#[derive(Hash, Copy, Clone, Eq, PartialEq)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16pub struct Descriptor {
17    pub f: u8,
18    pub x: u8,
19    pub y: u8,
20}
21
22impl Descriptor {
23    pub fn read<R: Read>(reader: &mut R) -> Result<Self, Error> {
24        let val = reader.read_u16::<BigEndian>()?;
25        Ok(Descriptor {
26            f: (val >> 14) as u8,
27            x: ((val >> 8) & 0x3f) as u8,
28            y: (val & 0xff) as u8,
29        })
30    }
31}
32
33impl Debug for Descriptor {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "Descriptor {0:1}{1:02}{2:03}", self.f, self.x, self.y)
36    }
37}
38
39impl Descriptor {
40    pub fn xy(&self) -> XY {
41        XY {
42            x: self.x,
43            y: self.y,
44        }
45    }
46}
47
48/// X and Y parts of a descriptor.
49#[derive(Hash, Debug, Clone, Copy, Eq, PartialEq)]
50pub struct XY {
51    pub x: u8,
52    pub y: u8,
53}
54
55/// A descriptor that has been resolved with table lookups.
56#[derive(Debug)]
57pub enum ResolvedDescriptor<'a> {
58    Data(&'a TableBEntry),
59    Replication {
60        y: u8,
61        delayed_bits: u8,
62        descriptors: Vec<ResolvedDescriptor<'a>>,
63    },
64    Operator(XY),
65    Sequence(&'a TableDEntry, Vec<ResolvedDescriptor<'a>>),
66}
67
68impl<'a> ResolvedDescriptor<'a> {
69    pub fn from_descriptor(desc: &Descriptor, tables: &Tables) -> Result<Self, Error> {
70        Ok(match desc.f {
71            0 => {
72                let Some(b) = tables.table_b.get(&desc.xy()) else {
73                    return Err(Error::Table(format!(
74                        "Table B entry not found for xy: {:?}",
75                        desc.xy()
76                    )));
77                };
78                ResolvedDescriptor::Data(b)
79            }
80            1 => unreachable!(),
81            2 => ResolvedDescriptor::Operator(desc.xy()),
82            3 => {
83                let Some(d) = tables.table_d.get(&desc.xy()) else {
84                    return Err(Error::Table(format!(
85                        "Table D entry not found for xy: {:?}",
86                        desc.xy()
87                    )));
88                };
89                let resolved_elements = resolve_descriptors(tables, d.elements)?;
90                ResolvedDescriptor::Sequence(d, resolved_elements)
91            }
92            _ => {
93                return Err(Error::Table(format!(
94                    "Table B entry not found for xy: {:?}",
95                    desc.xy()
96                )));
97            }
98        })
99    }
100}
101
102pub(crate) fn resolve_descriptors<'a>(
103    tables: &Tables,
104    descriptors: &'a [Descriptor],
105) -> Result<Vec<ResolvedDescriptor<'a>>, Error> {
106    let mut resolved = vec![];
107    let mut pos = 0;
108    while pos < descriptors.len() {
109        match &descriptors[pos] {
110            &Descriptor { f: 1, x, y } => {
111                let delayed_bits = match y {
112                    // delayed replication when YYY = 0
113                    0 => {
114                        pos += 1;
115                        match descriptors[pos] {
116                            Descriptor { f: 0, x: 31, y: 0 } => 1,
117                            Descriptor { f: 0, x: 31, y: 1 } => 8,
118                            Descriptor { f: 0, x: 31, y: 2 } => 16,
119                            Descriptor { f: 0, x: 31, y: 3 } => 8, // Note: JMA-local?
120                            desc => {
121                                return Err(Error::NotSupported(format!(
122                                    "Unsupported delayed descriptor replication factor: {desc:#?}",
123                                )));
124                            }
125                        }
126                    }
127                    _ => 0,
128                };
129                pos += 1;
130                if pos + x as usize > descriptors.len() {
131                    return Err(Error::Invalid(
132                        "Replication range out of bounds".to_string(),
133                    ));
134                }
135                resolved.push(ResolvedDescriptor::Replication {
136                    y,
137                    descriptors: resolve_descriptors(tables, &descriptors[pos..pos + x as usize])?,
138                    delayed_bits,
139                });
140                pos += x as usize;
141            }
142            desc => {
143                resolved.push(ResolvedDescriptor::from_descriptor(desc, tables)?);
144                pos += 1;
145            }
146        }
147    }
148
149    Ok(resolved)
150}