cea608_types/
tables.rs

1// Copyright (C) 2024 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the MIT license <LICENSE-MIT> or
4// http://opensource.org/licenses/MIT>, at your option. This file may not be
5// copied, modified, or distributed except according to those terms.
6
7//! Module for the [Code] table
8
9/// Errors when parsing a [`Code`]
10#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
11pub enum CodeError {
12    /// Invalid parity
13    #[error("Invalid parity")]
14    InvalidParity,
15    /// Length of data does not match length advertised
16    #[error("Length of the data ({actual}) does not match the expected length ({expected})")]
17    LengthMismatch {
18        /// The expected size
19        expected: usize,
20        /// The actual size
21        actual: usize,
22    },
23}
24
25/// The channel the control code references
26#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
27pub struct Channel(pub(crate) bool);
28
29impl Channel {
30    /// Channel 1
31    pub const ONE: Channel = Channel(true);
32    /// Channel 2
33    pub const TWO: Channel = Channel(false);
34
35    /// The numerical identifier of this channel
36    pub fn id(&self) -> u8 {
37        if self.0 {
38            1
39        } else {
40            2
41        }
42    }
43}
44
45/// The field that the control code references
46#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
47pub struct Field(pub(crate) bool);
48
49impl Field {
50    /// Field 1
51    pub const ONE: Field = Field(true);
52    /// Field 2
53    pub const TWO: Field = Field(false);
54
55    /// The numerical identifier of this field
56    pub fn id(&self) -> u8 {
57        if self.0 {
58            1
59        } else {
60            2
61        }
62    }
63}
64
65/// A control code
66#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
67// must be ordered the same as the byte values
68// These codes start with 0x11 (channel 1, odd-parity: 0x91) or 0x19 (channel 2, odd-parity: 0x19)
69pub struct ControlCode {
70    /// The field
71    pub field: Option<Field>,
72    /// The channel
73    pub channel: Channel,
74    /// The control code
75    pub control: Control,
76}
77
78impl ControlCode {
79    /// Construct a new [`ControlCode`]
80    pub fn new(field: Field, channel: Channel, control: Control) -> Self {
81        Self {
82            field: Some(field),
83            channel,
84            control,
85        }
86    }
87
88    /// The [`Channel`] for this [`ControlCode`]
89    pub fn channel(&self) -> Channel {
90        self.channel
91    }
92
93    /// The [`Field`] for this [`ControlCode`]
94    pub fn field(&self) -> Option<Field> {
95        self.field
96    }
97
98    /// The [`Control`] code for this [`ControlCode`]
99    pub fn code(&self) -> Control {
100        self.control
101    }
102
103    fn write(&self) -> [u8; 2] {
104        let mut data;
105        match self.control {
106            Control::Unknown(unk) => {
107                data = [unk[0], unk[1]];
108            }
109            Control::MidRow(midrow) => {
110                data = midrow.to_bytes();
111            }
112            Control::PreambleAddress(preamble) => {
113                data = preamble.to_bytes();
114            }
115            _ => {
116                if let Ok(idx) = CONTROL_MAP_TABLE
117                    .binary_search_by_key(&self.control, |control_map| control_map.control)
118                {
119                    data = CONTROL_MAP_TABLE[idx].cea608_bytes;
120                } else {
121                    unreachable!();
122                }
123            }
124        }
125        if (0x20..=0x2f).contains(&data[1]) && data[0] == 0x14 && self.field == Some(Field::TWO) {
126            data[0] |= 0x01;
127        }
128        if self.channel == Channel::TWO {
129            data[0] |= 0x08;
130        }
131        for data in data.iter_mut() {
132            *data = add_parity(*data);
133        }
134        data
135    }
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
139enum MidRowColor {
140    Color(Color),
141    Italics,
142}
143
144/// A mid-row change command
145#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
146pub struct MidRow {
147    color: MidRowColor,
148    underline: bool,
149}
150
151impl MidRow {
152    /// Construct a new mid row command signalling a color
153    pub fn new_color(color: Color, underline: bool) -> Self {
154        Self {
155            color: MidRowColor::Color(color),
156            underline,
157        }
158    }
159
160    /// Construct a new mid row command signalling italics
161    pub fn new_italics(underline: bool) -> Self {
162        Self {
163            color: MidRowColor::Italics,
164            underline,
165        }
166    }
167
168    /// The color of this mid row command
169    pub fn color(&self) -> Option<Color> {
170        if let MidRowColor::Color(color) = self.color {
171            Some(color)
172        } else {
173            None
174        }
175    }
176
177    /// Whether underline is signalled with this mid row command
178    pub fn underline(&self) -> bool {
179        self.underline
180    }
181
182    /// Whether italics is signalled with this mid row command
183    pub fn italics(&self) -> bool {
184        matches!(self.color, MidRowColor::Italics)
185    }
186
187    fn to_bytes(self) -> [u8; 2] {
188        let underline = if self.underline { 0x01 } else { 0x0 };
189        let color = match self.color {
190            MidRowColor::Color(Color::White) => 0x20,
191            MidRowColor::Color(Color::Green) => 0x22,
192            MidRowColor::Color(Color::Blue) => 0x24,
193            MidRowColor::Color(Color::Cyan) => 0x26,
194            MidRowColor::Color(Color::Red) => 0x28,
195            MidRowColor::Color(Color::Yellow) => 0x2a,
196            MidRowColor::Color(Color::Magenta) => 0x2c,
197            MidRowColor::Italics => 0x2e,
198        };
199        [0x11, color + underline]
200    }
201}
202
203/// The color options available
204#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
205pub enum Color {
206    /// White. RGB value 1.0, 1.0, 1.0.
207    White,
208    /// Green. RGB value 0.0, 1.0, 0.0.
209    Green,
210    /// Blue. RGB value 0.0, 0.0, 1.0.
211    Blue,
212    /// Cyan. RGB value 0.0, 1.0, 1.0.
213    Cyan,
214    /// Red. RGB value 1.0, 0.0, 0.0.
215    Red,
216    /// Yellow. RGB value 1.0, 1.0, 0.0.
217    Yellow,
218    /// Magenta. RGB value 1.0, 0.0, 1.0.
219    Magenta,
220}
221
222/// Enum representing control commands
223#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
224// must be ordered the same as the byte values
225// These codes start with 0x11 (channel 1, odd-parity: 0x91) or 0x19 (channel 2, odd-parity: 0x19)
226pub enum Control {
227    /// A midrow control code.
228    MidRow(MidRow),
229    /// Ⓡ
230    RegisteredTrademarkSign,
231    /// °
232    DegreeSign,
233    /// ½
234    Fraction12,
235    /// ¿
236    InvertedQuestionMark,
237    /// ™
238    TradeMarkSign,
239    /// ¢
240    CentSign,
241    /// £
242    PoundSign,
243    /// ♪
244    MusicalNote,
245    /// à
246    LatinLowerAWithGrave,
247    /// (Transparent)
248    TransparentSpace,
249    /// è
250    LatinLowerEWithGrave,
251    /// â
252    LatinLowerAWithCircumflex,
253    /// ê
254    LatinLowerEWithCircumflex,
255    /// î
256    LatinLowerIWithCircumflex,
257    /// ô
258    LatinLowerOWithCircumflex,
259    /// û
260    LatinLowerUWithCircumflex,
261
262    /// Á
263    LatinCapitalAWithAcute,
264    /// É
265    LatinCapitalEWithAcute,
266    /// Ó
267    LatinCapitalOWithAcute,
268    /// Ú
269    LatinCapitalUWithAcute,
270    /// Ü
271    LatinCapitalUWithDiaeseresis,
272    /// ü
273    LatinLowerUWithDiaeseresis,
274    /// ‘
275    OpeningSingleQuote,
276    /// ¡
277    InvertedExclamationMark,
278    /// *
279    Asterisk,
280    /// '
281    SingleOpenQuote,
282    /// _
283    EmDash,
284    /// Ⓒ
285    CopyrightSign,
286    /// ℠
287    ServiceMarkSign,
288    /// •
289    RoundBullet,
290    /// “
291    DoubleOpenQuote,
292    /// ”
293    DoubleCloseQuote,
294    /// À
295    LatinCapitalAWithGrave,
296    /// Â
297    LatinCapitalAWithCircumflex,
298    /// Ç
299    LatinCapitalCWithCedilla,
300    /// È
301    LatinCapitalEWithGrave,
302    /// Ê
303    LatinCapitalEWithCircumflex,
304    /// Ë
305    LatinCapitalEWithDiaeresis,
306    /// ë
307    LatinLowerEWithDiaeresis,
308    /// Î
309    LatinCapitalIWithCircumflex,
310    /// Ï
311    LatinCapitalIWithDiaeresis,
312    /// ï
313    LatinLowerIWithDiaeresis,
314    /// Ô
315    LatinCapitalOWithCircumflex,
316    /// Ù
317    LatinCapitalUWithGrave,
318    /// ù
319    LatinLowerUWithGrave,
320    /// Û
321    LatinCapitalUWithCircumflex,
322    /// «
323    OpeningGuillemets,
324    /// »
325    ClosingGuillemets,
326
327    /// Ã
328    LatinCapitalAWithTilde,
329    /// ã
330    LatinLowerAWithTilde,
331    /// Í
332    LatinCapitalIWithAcute,
333    /// Ì
334    LatinCapitalIWithGrave,
335    /// ì
336    LatinLowerIWithGrave,
337    /// Ò
338    LatinCapitalOWithGrave,
339    /// ò
340    LatinLowerOWithGrave,
341    /// Õ
342    LatinCapitalOWithTilde,
343    /// õ
344    LatinLowerOWithTilde,
345    /// {
346    OpeningBrace,
347    /// }
348    ClosingBrace,
349    /// \
350    ReverseSolidus,
351    /// ^
352    Caret,
353    /// _
354    Underbar,
355    /// |
356    Pipe,
357    /// ~
358    Tilde,
359    /// Ä
360    LatinCapitalAWithDiaeresis,
361    /// ä
362    LatinLowerAWithDiaeresis,
363    /// Ö
364    LatinCapitalOWithDiaeresis,
365    /// ö
366    LatinLowerOWithDiaeresis,
367    /// ß
368    LatinLowerSharpS,
369    /// ¥
370    YenSign,
371    /// ¤
372    GeneralCurrencySign,
373    /// ¦
374    VerticalBar,
375    /// Å
376    LatinCapitalAWithRingAbove,
377    /// å
378    LatinLowerAWithRingAbove,
379    /// Ø
380    LatinCapitalOWithStroke,
381    /// ø
382    LatinLowerOWithStroke,
383    /// ⌜
384    UpperLeftBorder,
385    /// ⌝
386    UpperRightBorder,
387    /// ⌞
388    LowerLeftBorder,
389    /// ⌟
390    LowerRightBorder,
391
392    /// Changes the mode of captioning to Pop-on.  Existing displayed captions are not affected.
393    ResumeCaptionLoading,
394    /// Remove the character at the previous location and move the cursor one character backwards.
395    Backspace,
396    /// Reserved (was Alarm Off).
397    AlarmOff,
398    /// Reserved (was Alarm On).
399    AlarmOn,
400    /// Delete all characters from the current cursor position to the end of the row.
401    DeleteToEndOfRow,
402    /// Change the mode of captioning to Roll-Up with 2 rows.
403    RollUp2,
404    /// Change the mode of captioning to Roll-Up with 3 rows.
405    RollUp3,
406    /// Change the mode of captioning to Roll-Up with 4 rows.
407    RollUp4,
408    /// Indicate that the character flash on and off.
409    FlashOn,
410    /// Changes the mode of captioning to Paint-on.  Existing displayed captions are not affected.
411    ResumeDirectionCaptioning,
412    /// Enter Text mode, clearing the Text screen buffer of any contents.
413    TextRestart,
414    /// Enter Text mode, keeping the Text screen buffer intact.
415    ResumeTextDisplay,
416    /// Remove all contents from the displayed screen buffer.
417    EraseDisplayedMemory,
418    /// Move the cursor to the next row and column 0.  Depending on the current mode, this will
419    /// result in different visual output.  See the CEA-608 specification for details.
420    CarriageReturn,
421    /// Remove all contents from the no displayed screen buffer.
422    EraseNonDisplayedMemory,
423    /// Flip the non displayed and displayed screen buffer.
424    EndOfCaption,
425
426    /// Move the cursor one character to the right.
427    TabOffset1,
428    /// Move the cursor two characters to the right.
429    TabOffset2,
430    /// Move the cursor three characters to the right.
431    TabOffset3,
432
433    /// A preamble address code signalling row and column position as well as some text formatting
434    /// information.
435    PreambleAddress(PreambleAddressCode),
436    /// An unknown command.
437    Unknown([u8; 2]),
438}
439
440impl Control {
441    /// Construct a new tab offset control code.
442    pub fn tab_offset(offset: u8) -> Option<Control> {
443        match offset {
444            1 => Some(Control::TabOffset1),
445            2 => Some(Control::TabOffset2),
446            3 => Some(Control::TabOffset3),
447            _ => None,
448        }
449    }
450}
451
452/// A preamble address code command contents
453#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
454pub struct PreambleAddressCode {
455    row: u8,
456    underline: bool,
457    ty: PreambleType,
458}
459
460impl PreambleAddressCode {
461    /// Construct a new preamble
462    pub fn new(base_row: u8, underline: bool, code: PreambleType) -> Self {
463        Self {
464            row: base_row,
465            underline,
466            ty: code,
467        }
468    }
469
470    /// The row specified in this preamble (0-indexed)
471    pub fn row(&self) -> u8 {
472        self.row
473    }
474
475    /// The column specified in this preamble
476    pub fn column(&self) -> u8 {
477        match self.ty {
478            PreambleType::Indent0 => 0,
479            PreambleType::Indent4 => 4,
480            PreambleType::Indent8 => 8,
481            PreambleType::Indent12 => 12,
482            PreambleType::Indent16 => 16,
483            PreambleType::Indent20 => 20,
484            PreambleType::Indent24 => 24,
485            PreambleType::Indent28 => 28,
486            _ => 0,
487        }
488    }
489
490    /// Whether underline is signaled in this preamble
491    pub fn underline(&self) -> bool {
492        self.underline
493    }
494
495    /// The complete preamble code
496    pub fn code(&self) -> PreambleType {
497        self.ty
498    }
499
500    /// Whether italics is signaled in this preamble
501    pub fn italics(&self) -> bool {
502        matches!(self.ty, PreambleType::WhiteItalics)
503    }
504
505    /// The color of this preamble
506    pub fn color(&self) -> Color {
507        self.ty.color()
508    }
509
510    fn to_bytes(self) -> [u8; 2] {
511        let underline = if self.underline { 0x1 } else { 0x0 };
512        let (row0, row1) = match self.row {
513            0 => (0x11, 0x40),
514            1 => (0x11, 0x60),
515            2 => (0x12, 0x40),
516            3 => (0x12, 0x60),
517            4 => (0x15, 0x40),
518            5 => (0x15, 0x60),
519            6 => (0x16, 0x40),
520            7 => (0x16, 0x60),
521            8 => (0x17, 0x40),
522            9 => (0x17, 0x60),
523            10 => (0x10, 0x40),
524            11 => (0x13, 0x40),
525            12 => (0x13, 0x60),
526            13 => (0x14, 0x40),
527            14 => (0x14, 0x60),
528            _ => unreachable!(),
529        };
530        let ty = match self.ty {
531            PreambleType::Color(Color::White) => 0x00,
532            PreambleType::Color(Color::Green) => 0x02,
533            PreambleType::Color(Color::Blue) => 0x04,
534            PreambleType::Color(Color::Cyan) => 0x06,
535            PreambleType::Color(Color::Red) => 0x08,
536            PreambleType::Color(Color::Yellow) => 0x0a,
537            PreambleType::Color(Color::Magenta) => 0x0c,
538            PreambleType::WhiteItalics => 0x0e,
539            PreambleType::Indent0 => 0x10,
540            PreambleType::Indent4 => 0x12,
541            PreambleType::Indent8 => 0x14,
542            PreambleType::Indent12 => 0x16,
543            PreambleType::Indent16 => 0x18,
544            PreambleType::Indent20 => 0x1a,
545            PreambleType::Indent24 => 0x1c,
546            PreambleType::Indent28 => 0x1e,
547        };
548        [row0, row1 | ty | underline]
549    }
550}
551
552/// The type of the preamble
553#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
554pub enum PreambleType {
555    /// A CEA-608 color.
556    Color(Color),
557    /// White Italics.
558    WhiteItalics,
559    /// No initial indentation.
560    Indent0,
561    /// Cursor placed 4 characters from the left of the screen.
562    Indent4,
563    /// Cursor placed 8 characters from the left of the screen.
564    Indent8,
565    /// Cursor placed 12 characters from the left of the screen.
566    Indent12,
567    /// Cursor placed 16 characters from the left of the screen.
568    Indent16,
569    /// Cursor placed 20 characters from the left of the screen.
570    Indent20,
571    /// Cursor placed 24 characters from the left of the screen.
572    Indent24,
573    /// Cursor placed 28 characters from the left of the screen.
574    Indent28,
575}
576
577impl PreambleType {
578    /// Create a new [`PreambleType`] from an indent value
579    pub fn from_indent(indent: u8) -> Option<Self> {
580        match indent {
581            0 => Some(Self::Indent0),
582            4 => Some(Self::Indent4),
583            8 => Some(Self::Indent8),
584            12 => Some(Self::Indent12),
585            16 => Some(Self::Indent16),
586            20 => Some(Self::Indent20),
587            24 => Some(Self::Indent24),
588            28 => Some(Self::Indent28),
589            _ => None,
590        }
591    }
592
593    /// Create a new [`PreambleType`] from a [`Color`]
594    pub fn from_color(color: Color) -> Self {
595        Self::Color(color)
596    }
597
598    /// The color of this preamble
599    pub fn color(&self) -> Color {
600        if let PreambleType::Color(color) = self {
601            *color
602        } else {
603            // all indents assign white as the color
604            Color::White
605        }
606    }
607
608    /// The indent value of this [`PreambleType`]
609    pub fn indent(&self) -> Option<u8> {
610        match self {
611            Self::Indent0 => Some(0),
612            Self::Indent4 => Some(4),
613            Self::Indent8 => Some(8),
614            Self::Indent12 => Some(12),
615            Self::Indent16 => Some(16),
616            Self::Indent20 => Some(20),
617            Self::Indent24 => Some(24),
618            Self::Indent28 => Some(28),
619            _ => None,
620        }
621    }
622}
623
624/// Enum of all possible characters or commands available
625#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
626// must be ordered the same as the byte values for binary search to be successful
627pub enum Code {
628    /// Null value.  Used as padding.
629    NUL,
630    /// Control code.
631    Control(ControlCode),
632    /// (space)
633    Space, // 0x20
634    /// !
635    ExclamationMark,
636    /// "
637    QuotationMark,
638    /// \#
639    NumberSign,
640    /// $
641    DollarSign,
642    /// %
643    PercentSign,
644    /// &
645    Ampersand,
646    /// ’
647    Apostrophe,
648    /// (
649    LeftParenthesis,
650    /// )
651    RightParenthesis,
652    /// á
653    LatinLowerAWithAcute,
654    /// \+
655    PlusSign,
656    /// ,
657    Comma,
658    /// \-
659    HyphenMinus,
660    /// .
661    FullStop,
662    /// /
663    Solidus,
664    /// 0
665    Zero,
666    /// 1
667    One,
668    /// 2
669    Two,
670    /// 3
671    Three,
672    /// 4
673    Four,
674    /// 5
675    Five,
676    /// 6
677    Six,
678    /// 7
679    Seven,
680    /// 8
681    Eight,
682    /// 9
683    Nine,
684    /// :
685    Colon,
686    /// ;
687    SemiColon,
688    /// <
689    LessThan,
690    /// =
691    Equals,
692    /// \>
693    GreaterThan,
694    /// ?
695    QuestionMark,
696    /// @
697    CommercialAt,
698    /// A
699    LatinCapitalA,
700    /// B
701    LatinCapitalB,
702    /// C
703    LatinCapitalC,
704    /// D
705    LatinCapitalD,
706    /// E
707    LatinCapitalE,
708    /// F
709    LatinCapitalF,
710    /// G
711    LatinCapitalG,
712    /// H
713    LatinCapitalH,
714    /// I
715    LatinCapitalI,
716    /// J
717    LatinCapitalJ,
718    /// K
719    LatinCapitalK,
720    /// L
721    LatinCapitalL,
722    /// M
723    LatinCapitalM,
724    /// N
725    LatinCapitalN,
726    /// O
727    LatinCapitalO,
728    /// P
729    LatinCapitalP,
730    /// Q
731    LatinCapitalQ,
732    /// R
733    LatinCapitalR,
734    /// S
735    LatinCapitalS,
736    /// T
737    LatinCapitalT,
738    /// U
739    LatinCapitalU,
740    /// V
741    LatinCapitalV,
742    /// W
743    LatinCapitalW,
744    /// X
745    LatinCapitalX,
746    /// Y
747    LatinCapitalY,
748    /// Z
749    LatinCapitalZ,
750    /// [
751    LeftSquareBracket,
752    /// é
753    LatinLowerEWithAcute,
754    /// ]
755    RightSquareBracket,
756    /// í
757    LatinLowerIWithAcute,
758    /// ó
759    LatinLowerOWithAcute,
760    /// ú
761    LatinLowerUWithAcute,
762    /// a
763    LatinLowerA,
764    /// b
765    LatinLowerB,
766    /// c
767    LatinLowerC,
768    /// d
769    LatinLowerD,
770    /// e
771    LatinLowerE,
772    /// f
773    LatinLowerF,
774    /// g
775    LatinLowerG,
776    /// h
777    LatinLowerH,
778    /// i
779    LatinLowerI,
780    /// j
781    LatinLowerJ,
782    /// k
783    LatinLowerK,
784    /// l
785    LatinLowerL,
786    /// m
787    LatinLowerM,
788    /// n
789    LatinLowerN,
790    /// o
791    LatinLowerO,
792    /// p
793    LatinLowerP,
794    /// q
795    LatinLowerQ,
796    /// r
797    LatinLowerR,
798    /// s
799    LatinLowerS,
800    /// t
801    LatinLowerT,
802    /// u
803    LatinLowerU,
804    /// v
805    LatinLowerV,
806    /// w
807    LatinLowerW,
808    /// x
809    LatinLowerX,
810    /// y
811    LatinLowerY,
812    /// z
813    LatinLowerZ,
814    /// ç
815    LatinLowerCWithCedilla,
816    /// ÷
817    DivisionSign,
818    /// Ñ
819    LatinCapitalNWithTilde,
820    /// ñ
821    LatinLowerNWithTilde,
822    /// █
823    SolidBlock, // 0x7F
824
825    /// An unknown value.
826    Unknown(u8),
827}
828
829#[derive(Debug, Clone)]
830struct CodeMap<'a> {
831    pub cea608_bytes: &'a [u8],
832    pub code: Code,
833    pub utf8: Option<char>,
834}
835
836macro_rules! code_map_bytes {
837    ($bytes:expr, $code:expr, $utf8:expr) => {
838        CodeMap {
839            cea608_bytes: &$bytes,
840            code: $code,
841            utf8: $utf8,
842        }
843    };
844}
845
846macro_rules! code_map_single_byte {
847    ($byte:expr, $code:expr, $utf8:expr) => {
848        code_map_bytes!([$byte], $code, $utf8)
849    };
850}
851
852// needs to be sorted by bytes and Code
853static CODE_MAP_TABLE: [CodeMap; 97] = [
854    code_map_single_byte!(0x00, Code::NUL, None),
855    code_map_single_byte!(0x20, Code::Space, Some(' ')),
856    code_map_single_byte!(0x21, Code::ExclamationMark, Some('!')),
857    code_map_single_byte!(0x22, Code::QuotationMark, Some('\"')),
858    code_map_single_byte!(0x23, Code::NumberSign, Some('#')),
859    code_map_single_byte!(0x24, Code::DollarSign, Some('$')),
860    code_map_single_byte!(0x25, Code::PercentSign, Some('%')),
861    code_map_single_byte!(0x26, Code::Ampersand, Some('&')),
862    code_map_single_byte!(0x27, Code::Apostrophe, Some('’')),
863    code_map_single_byte!(0x28, Code::LeftParenthesis, Some('(')),
864    code_map_single_byte!(0x29, Code::RightParenthesis, Some(')')),
865    code_map_single_byte!(0x2A, Code::LatinLowerAWithAcute, Some('á')),
866    //code_map_single_byte!(0x2A, Code::Asterisk, Some('*')),
867    code_map_single_byte!(0x2B, Code::PlusSign, Some('+')),
868    code_map_single_byte!(0x2C, Code::Comma, Some(',')),
869    code_map_single_byte!(0x2D, Code::HyphenMinus, Some('-')),
870    code_map_single_byte!(0x2E, Code::FullStop, Some('.')),
871    code_map_single_byte!(0x2F, Code::Solidus, Some('/')),
872    code_map_single_byte!(0x30, Code::Zero, Some('0')),
873    code_map_single_byte!(0x31, Code::One, Some('1')),
874    code_map_single_byte!(0x32, Code::Two, Some('2')),
875    code_map_single_byte!(0x33, Code::Three, Some('3')),
876    code_map_single_byte!(0x34, Code::Four, Some('4')),
877    code_map_single_byte!(0x35, Code::Five, Some('5')),
878    code_map_single_byte!(0x36, Code::Six, Some('6')),
879    code_map_single_byte!(0x37, Code::Seven, Some('7')),
880    code_map_single_byte!(0x38, Code::Eight, Some('8')),
881    code_map_single_byte!(0x39, Code::Nine, Some('9')),
882    code_map_single_byte!(0x3A, Code::Colon, Some(':')),
883    code_map_single_byte!(0x3B, Code::SemiColon, Some(';')),
884    code_map_single_byte!(0x3C, Code::LessThan, Some('<')),
885    code_map_single_byte!(0x3D, Code::Equals, Some('=')),
886    code_map_single_byte!(0x3E, Code::GreaterThan, Some('>')),
887    code_map_single_byte!(0x3F, Code::QuestionMark, Some('?')),
888    code_map_single_byte!(0x40, Code::CommercialAt, Some('@')),
889    code_map_single_byte!(0x41, Code::LatinCapitalA, Some('A')),
890    code_map_single_byte!(0x42, Code::LatinCapitalB, Some('B')),
891    code_map_single_byte!(0x43, Code::LatinCapitalC, Some('C')),
892    code_map_single_byte!(0x44, Code::LatinCapitalD, Some('D')),
893    code_map_single_byte!(0x45, Code::LatinCapitalE, Some('E')),
894    code_map_single_byte!(0x46, Code::LatinCapitalF, Some('F')),
895    code_map_single_byte!(0x47, Code::LatinCapitalG, Some('G')),
896    code_map_single_byte!(0x48, Code::LatinCapitalH, Some('H')),
897    code_map_single_byte!(0x49, Code::LatinCapitalI, Some('I')),
898    code_map_single_byte!(0x4A, Code::LatinCapitalJ, Some('J')),
899    code_map_single_byte!(0x4B, Code::LatinCapitalK, Some('K')),
900    code_map_single_byte!(0x4C, Code::LatinCapitalL, Some('L')),
901    code_map_single_byte!(0x4D, Code::LatinCapitalM, Some('M')),
902    code_map_single_byte!(0x4E, Code::LatinCapitalN, Some('N')),
903    code_map_single_byte!(0x4F, Code::LatinCapitalO, Some('O')),
904    code_map_single_byte!(0x50, Code::LatinCapitalP, Some('P')),
905    code_map_single_byte!(0x51, Code::LatinCapitalQ, Some('Q')),
906    code_map_single_byte!(0x52, Code::LatinCapitalR, Some('R')),
907    code_map_single_byte!(0x53, Code::LatinCapitalS, Some('S')),
908    code_map_single_byte!(0x54, Code::LatinCapitalT, Some('T')),
909    code_map_single_byte!(0x55, Code::LatinCapitalU, Some('U')),
910    code_map_single_byte!(0x56, Code::LatinCapitalV, Some('V')),
911    code_map_single_byte!(0x57, Code::LatinCapitalW, Some('W')),
912    code_map_single_byte!(0x58, Code::LatinCapitalX, Some('X')),
913    code_map_single_byte!(0x59, Code::LatinCapitalY, Some('Y')),
914    code_map_single_byte!(0x5A, Code::LatinCapitalZ, Some('Z')),
915    code_map_single_byte!(0x5B, Code::LeftSquareBracket, Some('[')),
916    code_map_single_byte!(0x5C, Code::LatinLowerEWithAcute, Some('é')),
917    code_map_single_byte!(0x5D, Code::RightSquareBracket, Some(']')),
918    code_map_single_byte!(0x5E, Code::LatinLowerIWithAcute, Some('í')),
919    code_map_single_byte!(0x5F, Code::LatinLowerOWithAcute, Some('ó')),
920    code_map_single_byte!(0x60, Code::LatinLowerUWithAcute, Some('ú')),
921    code_map_single_byte!(0x61, Code::LatinLowerA, Some('a')),
922    code_map_single_byte!(0x62, Code::LatinLowerB, Some('b')),
923    code_map_single_byte!(0x63, Code::LatinLowerC, Some('c')),
924    code_map_single_byte!(0x64, Code::LatinLowerD, Some('d')),
925    code_map_single_byte!(0x65, Code::LatinLowerE, Some('e')),
926    code_map_single_byte!(0x66, Code::LatinLowerF, Some('f')),
927    code_map_single_byte!(0x67, Code::LatinLowerG, Some('g')),
928    code_map_single_byte!(0x68, Code::LatinLowerH, Some('h')),
929    code_map_single_byte!(0x69, Code::LatinLowerI, Some('i')),
930    code_map_single_byte!(0x6A, Code::LatinLowerJ, Some('j')),
931    code_map_single_byte!(0x6B, Code::LatinLowerK, Some('k')),
932    code_map_single_byte!(0x6C, Code::LatinLowerL, Some('l')),
933    code_map_single_byte!(0x6D, Code::LatinLowerM, Some('m')),
934    code_map_single_byte!(0x6E, Code::LatinLowerN, Some('n')),
935    code_map_single_byte!(0x6F, Code::LatinLowerO, Some('o')),
936    code_map_single_byte!(0x70, Code::LatinLowerP, Some('p')),
937    code_map_single_byte!(0x71, Code::LatinLowerQ, Some('q')),
938    code_map_single_byte!(0x72, Code::LatinLowerR, Some('r')),
939    code_map_single_byte!(0x73, Code::LatinLowerS, Some('s')),
940    code_map_single_byte!(0x74, Code::LatinLowerT, Some('t')),
941    code_map_single_byte!(0x75, Code::LatinLowerU, Some('u')),
942    code_map_single_byte!(0x76, Code::LatinLowerV, Some('v')),
943    code_map_single_byte!(0x77, Code::LatinLowerW, Some('w')),
944    code_map_single_byte!(0x78, Code::LatinLowerX, Some('x')),
945    code_map_single_byte!(0x79, Code::LatinLowerY, Some('y')),
946    code_map_single_byte!(0x7A, Code::LatinLowerZ, Some('z')),
947    code_map_single_byte!(0x7B, Code::LatinLowerCWithCedilla, Some('ç')),
948    code_map_single_byte!(0x7C, Code::DivisionSign, Some('÷')),
949    code_map_single_byte!(0x7D, Code::LatinCapitalNWithTilde, Some('Ñ')),
950    code_map_single_byte!(0x7E, Code::LatinLowerNWithTilde, Some('ñ')),
951    code_map_single_byte!(0x7F, Code::SolidBlock, Some('█')),
952];
953
954#[derive(Debug, Clone)]
955struct ControlMap {
956    cea608_bytes: [u8; 2],
957    control: Control,
958    utf8: Option<char>,
959}
960
961macro_rules! control_map_bytes {
962    ($bytes:expr, $control:expr, $utf8:expr) => {
963        ControlMap {
964            cea608_bytes: $bytes,
965            control: $control,
966            utf8: $utf8,
967        }
968    };
969}
970
971static CONTROL_MAP_TABLE: [ControlMap; 99] = [
972    control_map_bytes!([0x11, 0x30], Control::RegisteredTrademarkSign, Some('Ⓡ')),
973    control_map_bytes!([0x11, 0x31], Control::DegreeSign, Some('°')),
974    control_map_bytes!([0x11, 0x32], Control::Fraction12, Some('½')),
975    control_map_bytes!([0x11, 0x33], Control::InvertedQuestionMark, Some('¿')),
976    control_map_bytes!([0x11, 0x34], Control::TradeMarkSign, Some('™')),
977    control_map_bytes!([0x11, 0x35], Control::CentSign, Some('¢')),
978    control_map_bytes!([0x11, 0x36], Control::PoundSign, Some('£')),
979    control_map_bytes!([0x11, 0x37], Control::MusicalNote, Some('♪')),
980    control_map_bytes!([0x11, 0x38], Control::LatinLowerAWithGrave, Some('à')),
981    control_map_bytes!([0x11, 0x39], Control::TransparentSpace, None),
982    control_map_bytes!([0x11, 0x3a], Control::LatinLowerEWithGrave, Some('è')),
983    control_map_bytes!([0x11, 0x3b], Control::LatinLowerAWithCircumflex, Some('â')),
984    control_map_bytes!([0x11, 0x3c], Control::LatinLowerEWithCircumflex, Some('ê')),
985    control_map_bytes!([0x11, 0x3d], Control::LatinLowerIWithCircumflex, Some('î')),
986    control_map_bytes!([0x11, 0x3e], Control::LatinLowerOWithCircumflex, Some('ô')),
987    control_map_bytes!([0x11, 0x3f], Control::LatinLowerUWithCircumflex, Some('û')),
988    control_map_bytes!([0x12, 0x20], Control::LatinCapitalAWithAcute, Some('Á')),
989    control_map_bytes!([0x12, 0x21], Control::LatinCapitalEWithAcute, Some('É')),
990    control_map_bytes!([0x12, 0x22], Control::LatinCapitalOWithAcute, Some('Ó')),
991    control_map_bytes!([0x12, 0x23], Control::LatinCapitalUWithAcute, Some('Ú')),
992    control_map_bytes!(
993        [0x12, 0x24],
994        Control::LatinCapitalUWithDiaeseresis,
995        Some('Ü')
996    ),
997    control_map_bytes!([0x12, 0x25], Control::LatinLowerUWithDiaeseresis, Some('ü')),
998    control_map_bytes!([0x12, 0x26], Control::OpeningSingleQuote, Some('‘')),
999    control_map_bytes!([0x12, 0x27], Control::InvertedExclamationMark, Some('¡')),
1000    control_map_bytes!([0x12, 0x28], Control::Asterisk, Some('*')),
1001    control_map_bytes!([0x12, 0x29], Control::SingleOpenQuote, Some('\'')),
1002    control_map_bytes!([0x12, 0x2a], Control::EmDash, Some('—')),
1003    control_map_bytes!([0x12, 0x2b], Control::CopyrightSign, Some('Ⓒ')),
1004    control_map_bytes!([0x12, 0x2c], Control::ServiceMarkSign, Some('℠')),
1005    control_map_bytes!([0x12, 0x2d], Control::RoundBullet, None),
1006    control_map_bytes!([0x12, 0x2e], Control::DoubleOpenQuote, Some('“')),
1007    control_map_bytes!([0x12, 0x2f], Control::DoubleCloseQuote, Some('”')),
1008    control_map_bytes!([0x12, 0x30], Control::LatinCapitalAWithGrave, Some('À')),
1009    control_map_bytes!(
1010        [0x12, 0x31],
1011        Control::LatinCapitalAWithCircumflex,
1012        Some('Â')
1013    ),
1014    control_map_bytes!([0x12, 0x32], Control::LatinCapitalCWithCedilla, Some('Ç')),
1015    control_map_bytes!([0x12, 0x33], Control::LatinCapitalEWithGrave, Some('È')),
1016    control_map_bytes!(
1017        [0x12, 0x34],
1018        Control::LatinCapitalEWithCircumflex,
1019        Some('Ê')
1020    ),
1021    control_map_bytes!([0x12, 0x35], Control::LatinCapitalEWithDiaeresis, Some('Ë')),
1022    control_map_bytes!([0x12, 0x36], Control::LatinLowerEWithDiaeresis, Some('ë')),
1023    control_map_bytes!(
1024        [0x12, 0x37],
1025        Control::LatinCapitalIWithCircumflex,
1026        Some('Î')
1027    ),
1028    control_map_bytes!([0x12, 0x38], Control::LatinCapitalIWithDiaeresis, Some('Ï')),
1029    control_map_bytes!([0x12, 0x39], Control::LatinLowerIWithDiaeresis, Some('ï')),
1030    control_map_bytes!(
1031        [0x12, 0x3a],
1032        Control::LatinCapitalOWithCircumflex,
1033        Some('Ô')
1034    ),
1035    control_map_bytes!([0x12, 0x3b], Control::LatinCapitalUWithGrave, Some('Ù')),
1036    control_map_bytes!([0x12, 0x3c], Control::LatinLowerUWithGrave, Some('ù')),
1037    control_map_bytes!(
1038        [0x12, 0x3d],
1039        Control::LatinCapitalUWithCircumflex,
1040        Some('Û')
1041    ),
1042    control_map_bytes!([0x12, 0x3e], Control::OpeningGuillemets, Some('«')),
1043    control_map_bytes!([0x12, 0x3f], Control::ClosingGuillemets, Some('»')),
1044    control_map_bytes!([0x13, 0x20], Control::LatinCapitalAWithTilde, Some('Ã')),
1045    control_map_bytes!([0x13, 0x21], Control::LatinLowerAWithTilde, Some('ã')),
1046    control_map_bytes!([0x13, 0x22], Control::LatinCapitalIWithAcute, Some('Í')),
1047    control_map_bytes!([0x13, 0x23], Control::LatinCapitalIWithGrave, Some('Ì')),
1048    control_map_bytes!([0x13, 0x24], Control::LatinLowerIWithGrave, Some('ì')),
1049    control_map_bytes!([0x13, 0x25], Control::LatinCapitalOWithGrave, Some('Ò')),
1050    control_map_bytes!([0x13, 0x26], Control::LatinLowerOWithGrave, Some('ò')),
1051    control_map_bytes!([0x13, 0x27], Control::LatinCapitalOWithTilde, Some('Õ')),
1052    control_map_bytes!([0x13, 0x28], Control::LatinLowerOWithTilde, Some('õ')),
1053    control_map_bytes!([0x13, 0x29], Control::OpeningBrace, Some('{')),
1054    control_map_bytes!([0x13, 0x2a], Control::ClosingBrace, Some('}')),
1055    control_map_bytes!([0x13, 0x2b], Control::ReverseSolidus, Some('\\')),
1056    control_map_bytes!([0x13, 0x2c], Control::Caret, Some('^')),
1057    control_map_bytes!([0x13, 0x2d], Control::Underbar, Some('_')),
1058    control_map_bytes!([0x13, 0x2e], Control::Pipe, Some('|')),
1059    control_map_bytes!([0x13, 0x2f], Control::Tilde, Some('~')),
1060    control_map_bytes!([0x13, 0x30], Control::LatinCapitalAWithDiaeresis, Some('Ä')),
1061    control_map_bytes!([0x13, 0x31], Control::LatinLowerAWithDiaeresis, Some('ä')),
1062    control_map_bytes!([0x13, 0x32], Control::LatinCapitalOWithDiaeresis, Some('Ö')),
1063    control_map_bytes!([0x13, 0x33], Control::LatinLowerOWithDiaeresis, Some('ö')),
1064    control_map_bytes!([0x13, 0x34], Control::LatinLowerSharpS, Some('ß')),
1065    control_map_bytes!([0x13, 0x35], Control::YenSign, Some('¥')),
1066    control_map_bytes!([0x13, 0x36], Control::GeneralCurrencySign, Some('¤')),
1067    control_map_bytes!([0x13, 0x37], Control::VerticalBar, Some('¦')),
1068    control_map_bytes!([0x13, 0x38], Control::LatinCapitalAWithRingAbove, Some('Å')),
1069    control_map_bytes!([0x13, 0x39], Control::LatinLowerAWithRingAbove, Some('å')),
1070    control_map_bytes!([0x13, 0x3a], Control::LatinCapitalOWithStroke, Some('Ø')),
1071    control_map_bytes!([0x13, 0x3b], Control::LatinLowerOWithStroke, Some('ø')),
1072    control_map_bytes!([0x13, 0x3c], Control::UpperLeftBorder, None),
1073    control_map_bytes!([0x13, 0x3d], Control::UpperRightBorder, None),
1074    control_map_bytes!([0x13, 0x3e], Control::LowerLeftBorder, None),
1075    control_map_bytes!([0x13, 0x3f], Control::LowerRightBorder, None),
1076    control_map_bytes!([0x14, 0x20], Control::ResumeCaptionLoading, None),
1077    control_map_bytes!([0x14, 0x21], Control::Backspace, None),
1078    control_map_bytes!([0x14, 0x22], Control::AlarmOff, None),
1079    control_map_bytes!([0x14, 0x23], Control::AlarmOn, None),
1080    control_map_bytes!([0x14, 0x24], Control::DeleteToEndOfRow, None),
1081    control_map_bytes!([0x14, 0x25], Control::RollUp2, None),
1082    control_map_bytes!([0x14, 0x26], Control::RollUp3, None),
1083    control_map_bytes!([0x14, 0x27], Control::RollUp4, None),
1084    control_map_bytes!([0x14, 0x28], Control::FlashOn, None),
1085    control_map_bytes!([0x14, 0x29], Control::ResumeDirectionCaptioning, None),
1086    control_map_bytes!([0x14, 0x2a], Control::TextRestart, None),
1087    control_map_bytes!([0x14, 0x2b], Control::ResumeTextDisplay, None),
1088    control_map_bytes!([0x14, 0x2c], Control::EraseDisplayedMemory, None),
1089    control_map_bytes!([0x14, 0x2d], Control::CarriageReturn, None),
1090    control_map_bytes!([0x14, 0x2e], Control::EraseNonDisplayedMemory, None),
1091    control_map_bytes!([0x14, 0x2f], Control::EndOfCaption, None),
1092    control_map_bytes!([0x17, 0x21], Control::TabOffset1, None),
1093    control_map_bytes!([0x17, 0x22], Control::TabOffset2, None),
1094    control_map_bytes!([0x17, 0x23], Control::TabOffset3, None),
1095];
1096
1097fn strip_parity(byte: u8) -> u8 {
1098    byte & 0x7F
1099}
1100
1101fn add_parity(byte: u8) -> u8 {
1102    debug_assert!((byte & 0x80) == 0);
1103    if check_odd_parity(byte) {
1104        byte
1105    } else {
1106        byte | 0x80
1107    }
1108}
1109
1110fn check_odd_parity(byte: u8) -> bool {
1111    byte.count_ones() % 2 == 1
1112}
1113
1114fn parse_control_code(data: [u8; 2]) -> ControlCode {
1115    let channel = data[0] & 0x08;
1116    let underline = data[1] & 0x1 != 0;
1117    let mut byte0 = data[0] & !0x08;
1118    let field = if (0x20..=0x2f).contains(&data[1]) {
1119        match data[0] & !0x08 {
1120            0x14 => Some(Field::ONE),
1121            0x15 => {
1122                byte0 &= !0x01;
1123                Some(Field::TWO)
1124            }
1125            _ => None,
1126        }
1127    } else {
1128        None
1129    };
1130
1131    ControlCode {
1132        field,
1133        channel: Channel(channel == 0),
1134        control: match (byte0, data[1]) {
1135            (0x11, 0x20 | 0x21) => Control::MidRow(MidRow {
1136                color: MidRowColor::Color(Color::White),
1137                underline,
1138            }),
1139            (0x11, 0x22 | 0x23) => Control::MidRow(MidRow {
1140                color: MidRowColor::Color(Color::Green),
1141                underline,
1142            }),
1143            (0x11, 0x24 | 0x25) => Control::MidRow(MidRow {
1144                color: MidRowColor::Color(Color::Blue),
1145                underline,
1146            }),
1147            (0x11, 0x26 | 0x27) => Control::MidRow(MidRow {
1148                color: MidRowColor::Color(Color::Cyan),
1149                underline,
1150            }),
1151            (0x11, 0x28 | 0x29) => Control::MidRow(MidRow {
1152                color: MidRowColor::Color(Color::Red),
1153                underline,
1154            }),
1155            (0x11, 0x2a | 0x2b) => Control::MidRow(MidRow {
1156                color: MidRowColor::Color(Color::Yellow),
1157                underline,
1158            }),
1159            (0x11, 0x2c | 0x2d) => Control::MidRow(MidRow {
1160                color: MidRowColor::Color(Color::Magenta),
1161                underline,
1162            }),
1163            (0x11, 0x2e | 0x2f) => Control::MidRow(MidRow {
1164                color: MidRowColor::Italics,
1165                underline,
1166            }),
1167            (0x10..=0x19, 0x20..=0x3f) => {
1168                let idx = CONTROL_MAP_TABLE
1169                    .binary_search_by_key(&[byte0, data[1]], |control_map| {
1170                        control_map.cea608_bytes
1171                    });
1172                idx.map(|idx| CONTROL_MAP_TABLE[idx].control)
1173                    .unwrap_or_else(|_| Control::Unknown(data))
1174            }
1175            (byte0, 0x40..=0x7f) => {
1176                if let Some(preamble) = parse_preamble(byte0, data[1]) {
1177                    Control::PreambleAddress(preamble)
1178                } else {
1179                    Control::Unknown(data)
1180                }
1181            }
1182            _ => Control::Unknown(data),
1183        },
1184    }
1185}
1186
1187fn parse_preamble(byte0: u8, byte1: u8) -> Option<PreambleAddressCode> {
1188    let underline = byte1 & 0x1 != 0;
1189    let row = match (byte0, byte1) {
1190        (0x11, 0x40..=0x5f) => 0,
1191        (0x11, 0x60..=0x7f) => 1,
1192        (0x12, 0x40..=0x5f) => 2,
1193        (0x12, 0x60..=0x7f) => 3,
1194        (0x15, 0x40..=0x5f) => 4,
1195        (0x15, 0x60..=0x7f) => 5,
1196        (0x16, 0x40..=0x5f) => 6,
1197        (0x16, 0x60..=0x7f) => 7,
1198        (0x17, 0x40..=0x5f) => 8,
1199        (0x17, 0x60..=0x7f) => 9,
1200        (0x10, 0x40..=0x5f) => 10,
1201        (0x13, 0x40..=0x5f) => 11,
1202        (0x13, 0x60..=0x7f) => 12,
1203        (0x14, 0x40..=0x5f) => 13,
1204        (0x14, 0x60..=0x7f) => 14,
1205        _ => return None,
1206    };
1207    let ty = match byte1 & 0x1e {
1208        0x00 => PreambleType::Color(Color::White),
1209        0x02 => PreambleType::Color(Color::Green),
1210        0x04 => PreambleType::Color(Color::Blue),
1211        0x06 => PreambleType::Color(Color::Cyan),
1212        0x08 => PreambleType::Color(Color::Red),
1213        0x0a => PreambleType::Color(Color::Yellow),
1214        0x0c => PreambleType::Color(Color::Magenta),
1215        0x0e => PreambleType::WhiteItalics,
1216        0x10 => PreambleType::Indent0,
1217        0x12 => PreambleType::Indent4,
1218        0x14 => PreambleType::Indent8,
1219        0x16 => PreambleType::Indent12,
1220        0x18 => PreambleType::Indent16,
1221        0x1a => PreambleType::Indent20,
1222        0x1c => PreambleType::Indent24,
1223        0x1e => PreambleType::Indent28,
1224        _ => return None,
1225    };
1226    Some(PreambleAddressCode { row, underline, ty })
1227}
1228
1229impl Code {
1230    /// The length in bytes of this [Code]
1231    ///
1232    /// # Examples
1233    /// ```
1234    /// # use cea608_types::tables::Code;
1235    /// assert_eq!(Code::LatinCapitalA.byte_len(), 1);
1236    /// ```
1237    pub fn byte_len(&self) -> usize {
1238        match self {
1239            Code::Control(_) => 2,
1240            _ => 1,
1241        }
1242    }
1243
1244    /// Parse a byte sequence into a list of [Code]s
1245    ///
1246    /// # Examples
1247    /// ```
1248    /// # use cea608_types::tables::Code;
1249    /// assert_eq!(Code::from_data([0xC1, 0x80]), Ok([Code::LatinCapitalA, Code::NUL]));
1250    /// ```
1251    pub fn from_data(data: [u8; 2]) -> Result<[Code; 2], CodeError> {
1252        if !check_odd_parity(data[0]) {
1253            return Err(CodeError::InvalidParity);
1254        }
1255        if !check_odd_parity(data[1]) {
1256            return Err(CodeError::InvalidParity);
1257        }
1258        let data = [strip_parity(data[0]), strip_parity(data[1])];
1259
1260        if (0x10..=0x1F).contains(&data[0]) {
1261            Ok([Code::Control(parse_control_code(data)), Code::NUL])
1262        } else {
1263            let code0 = CODE_MAP_TABLE
1264                .binary_search_by_key(&[data[0]].as_slice(), |code_map| code_map.cea608_bytes);
1265            let code1 = CODE_MAP_TABLE
1266                .binary_search_by_key(&[data[1]].as_slice(), |code_map| code_map.cea608_bytes);
1267            Ok([
1268                code0
1269                    .map(|idx| CODE_MAP_TABLE[idx].code)
1270                    .unwrap_or_else(|_| Code::Unknown(data[0])),
1271                code1
1272                    .map(|idx| CODE_MAP_TABLE[idx].code)
1273                    .unwrap_or_else(|_| Code::Unknown(data[1])),
1274            ])
1275        }
1276    }
1277
1278    /// Write a [Code] to a byte stream
1279    ///
1280    /// # Examples
1281    /// ```
1282    /// # use cea608_types::tables::Code;
1283    /// let mut written = vec![];
1284    /// Code::LatinCapitalC.write(&mut written).unwrap();
1285    /// assert_eq!(written, [0x43]);
1286    /// ```
1287    pub fn write<W: std::io::Write>(&self, w: &mut W) -> Result<(), std::io::Error> {
1288        match self {
1289            Code::Unknown(data) => {
1290                return w.write_all(&[add_parity(*data)]);
1291            }
1292            Code::Control(control) => return w.write_all(&control.write()),
1293            _ => {
1294                if let Ok(idx) =
1295                    CODE_MAP_TABLE.binary_search_by_key(&self, |code_map| &code_map.code)
1296                {
1297                    let data = CODE_MAP_TABLE[idx]
1298                        .cea608_bytes
1299                        .iter()
1300                        .map(|b| add_parity(*b))
1301                        .collect::<Vec<_>>();
1302                    return w.write_all(&data);
1303                }
1304            }
1305        }
1306        unreachable!()
1307    }
1308
1309    /// Write a [Code] to a pair of bytes
1310    ///
1311    /// # Examples
1312    /// ```
1313    /// # use cea608_types::tables::Code;
1314    /// let mut written = [0; 2];
1315    /// assert_eq!(1, Code::LatinCapitalC.write_into(&mut written));
1316    /// assert_eq!(written, [0x43, 0x80]);
1317    /// ```
1318    pub fn write_into(&self, bytes: &mut [u8; 2]) -> usize {
1319        match self {
1320            Code::Unknown(data) => {
1321                bytes[0] = add_parity(*data);
1322                bytes[1] = 0x80;
1323                return 1;
1324            }
1325            Code::Control(control) => {
1326                bytes.copy_from_slice(&control.write());
1327                return 2;
1328            }
1329            _ => {
1330                if let Ok(idx) =
1331                    CODE_MAP_TABLE.binary_search_by_key(&self, |code_map| &code_map.code)
1332                {
1333                    let len = CODE_MAP_TABLE[idx].cea608_bytes.len();
1334                    for (i, b) in CODE_MAP_TABLE[idx]
1335                        .cea608_bytes
1336                        .iter()
1337                        .map(|b| add_parity(*b))
1338                        .chain([0x80, 0x80].into_iter())
1339                        .enumerate()
1340                        .take(2)
1341                    {
1342                        bytes[i] = b;
1343                    }
1344                    return len;
1345                }
1346            }
1347        }
1348        unreachable!()
1349    }
1350
1351    /// The utf8 char for this [Code]
1352    ///
1353    /// [Code]s that represent a command will return None.
1354    ///
1355    /// # Examples
1356    /// ```
1357    /// # use cea608_types::tables::Code;
1358    /// assert_eq!(Code::LatinCapitalA.char(), Some('A'));
1359    /// ```
1360    pub fn char(&self) -> Option<char> {
1361        // table is not currently sorted by utf8 value so cannot binary search through it.  May
1362        // need another lookup table if this is a performance concern
1363        if let Code::Control(ControlCode { control, .. }) = self {
1364            return CONTROL_MAP_TABLE.iter().find_map(|control_map| {
1365                if control_map.control == *control {
1366                    control_map.utf8
1367                } else {
1368                    None
1369                }
1370            });
1371        }
1372
1373        CODE_MAP_TABLE.iter().find_map(|code_map| {
1374            if code_map.code == *self {
1375                code_map.utf8
1376            } else {
1377                None
1378            }
1379        })
1380    }
1381
1382    /// Retrieve a [Code] for a utf8 char
1383    ///
1384    /// If the char is not representable as a [Code], None will be returned.
1385    ///
1386    /// # Examples
1387    /// ```
1388    /// # use cea608_types::tables::{Code, Channel};
1389    /// assert_eq!(Code::from_char('A', Channel::ONE), Some(Code::LatinCapitalA));
1390    /// ```
1391    pub fn from_char(c: char, channel: Channel) -> Option<Code> {
1392        // table is not currently sorted by utf8 value so cannot binary search through it.  May
1393        // need another lookup table if this is a performance concern
1394        CODE_MAP_TABLE
1395            .iter()
1396            .find_map(|code_map| {
1397                if code_map.utf8 == Some(c) {
1398                    Some(code_map.code)
1399                } else {
1400                    None
1401                }
1402            })
1403            .or_else(|| {
1404                CONTROL_MAP_TABLE.iter().find_map(|control_map| {
1405                    if control_map.utf8 == Some(c) {
1406                        Some(Code::Control(ControlCode {
1407                            field: None,
1408                            channel,
1409                            control: control_map.control,
1410                        }))
1411                    } else {
1412                        None
1413                    }
1414                })
1415            })
1416    }
1417
1418    /// Whether or not this code requires there to have a backspace prepended for correct display
1419    pub fn needs_backspace(&self) -> bool {
1420        let Code::Control(ControlCode {
1421            field: _,
1422            channel: _,
1423            control,
1424        }) = self
1425        else {
1426            return false;
1427        };
1428        matches!(
1429            control,
1430            Control::MidRow(_)
1431            | Control::LatinCapitalAWithAcute
1432            | Control::LatinCapitalEWithAcute
1433            | Control::LatinCapitalOWithAcute
1434            | Control::LatinCapitalUWithAcute
1435            | Control::LatinCapitalUWithDiaeseresis
1436            | Control::LatinLowerUWithDiaeseresis
1437            | Control::OpeningSingleQuote
1438            | Control::InvertedExclamationMark
1439            // table 6
1440            | Control::Asterisk
1441            | Control::SingleOpenQuote
1442            | Control::EmDash
1443            | Control::CopyrightSign
1444            | Control::ServiceMarkSign
1445            | Control::RoundBullet
1446            | Control::DoubleOpenQuote
1447            | Control::DoubleCloseQuote
1448            // table 7
1449            | Control::LatinCapitalAWithGrave
1450            | Control::LatinCapitalAWithCircumflex
1451            | Control::LatinCapitalCWithCedilla
1452            | Control::LatinCapitalEWithGrave
1453            | Control::LatinCapitalEWithCircumflex
1454            | Control::LatinCapitalEWithDiaeresis
1455            | Control::LatinLowerEWithDiaeresis
1456            | Control::LatinCapitalIWithCircumflex
1457            | Control::LatinCapitalIWithDiaeresis
1458            | Control::LatinLowerIWithDiaeresis
1459            | Control::LatinCapitalOWithCircumflex
1460            | Control::LatinCapitalUWithGrave
1461            | Control::LatinLowerUWithGrave
1462            | Control::LatinCapitalUWithCircumflex
1463            | Control::OpeningGuillemets
1464            | Control::ClosingGuillemets
1465            // table 8
1466            | Control::LatinCapitalAWithTilde
1467            | Control::LatinLowerAWithTilde
1468            | Control::LatinCapitalIWithAcute
1469            | Control::LatinCapitalIWithGrave
1470            | Control::LatinLowerIWithGrave
1471            | Control::LatinCapitalOWithGrave
1472            | Control::LatinLowerOWithGrave
1473            | Control::LatinCapitalOWithTilde
1474            | Control::LatinLowerOWithTilde
1475            | Control::OpeningBrace
1476            | Control::ClosingBrace
1477            | Control::ReverseSolidus
1478            | Control::Caret
1479            | Control::Underbar
1480            | Control::Pipe
1481            | Control::Tilde
1482            // table 9
1483            | Control::LatinCapitalAWithDiaeresis
1484            | Control::LatinLowerAWithDiaeresis
1485            | Control::LatinCapitalOWithDiaeresis
1486            | Control::LatinLowerOWithDiaeresis
1487            | Control::LatinLowerSharpS
1488            | Control::YenSign
1489            | Control::GeneralCurrencySign
1490            | Control::VerticalBar
1491            // table 10
1492            | Control::LatinCapitalAWithRingAbove
1493            | Control::LatinLowerAWithRingAbove
1494            | Control::LatinCapitalOWithStroke
1495            | Control::LatinLowerOWithStroke
1496            | Control::UpperLeftBorder
1497            | Control::UpperRightBorder
1498            | Control::LowerLeftBorder
1499            | Control::LowerRightBorder
1500        )
1501    }
1502}
1503
1504#[cfg(test)]
1505mod test {
1506    use super::*;
1507    use crate::tests::*;
1508
1509    #[test]
1510    fn codes_table_ordered() {
1511        test_init_log();
1512        let mut iter = CODE_MAP_TABLE.iter().peekable();
1513        while let Some(code_map) = iter.next() {
1514            if let Some(peek) = iter.peek() {
1515                trace!("checking ordinality for {code_map:?} and {peek:?}");
1516                assert!(peek.code > code_map.code);
1517                assert!(peek.cea608_bytes > code_map.cea608_bytes);
1518            }
1519        }
1520    }
1521
1522    #[test]
1523    fn control_table_ordered() {
1524        test_init_log();
1525        let mut iter = CONTROL_MAP_TABLE.iter().peekable();
1526        while let Some(control_map) = iter.next() {
1527            if let Some(peek) = iter.peek() {
1528                trace!("checking ordinality for {control_map:?} and {peek:?}");
1529                assert!(peek.control > control_map.control);
1530                assert!(peek.cea608_bytes > control_map.cea608_bytes);
1531            }
1532        }
1533    }
1534
1535    #[test]
1536    fn codes_to_from_bytes() {
1537        test_init_log();
1538        for code_map in CODE_MAP_TABLE.iter() {
1539            trace!("parsing {code_map:?}");
1540            let mut data = Vec::from_iter(code_map.cea608_bytes.iter().map(|b| add_parity(*b)));
1541            data.resize(2, 0x80);
1542            let parsed_code = Code::from_data(data.try_into().unwrap()).unwrap();
1543            assert_eq!(parsed_code[0], code_map.code);
1544            let mut written = vec![];
1545            parsed_code[0].write(&mut written).unwrap();
1546            assert_eq!(written.len(), code_map.code.byte_len());
1547            let written = written.iter().map(|b| strip_parity(*b)).collect::<Vec<_>>();
1548            assert_eq!(written, code_map.cea608_bytes);
1549        }
1550    }
1551
1552    #[test]
1553    fn codes_to_from_char() {
1554        test_init_log();
1555        for code_map in CODE_MAP_TABLE.iter() {
1556            trace!("parsing {code_map:?}");
1557            if let Some(c) = code_map.utf8 {
1558                let parsed_code = Code::from_char(c, Channel(true)).unwrap();
1559                assert_eq!(parsed_code.char(), code_map.utf8);
1560                assert_eq!(parsed_code, code_map.code);
1561                let mut written = vec![];
1562                parsed_code.write(&mut written).unwrap();
1563                let written = written.iter().map(|b| strip_parity(*b)).collect::<Vec<_>>();
1564                assert_eq!(written, code_map.cea608_bytes);
1565            }
1566        }
1567    }
1568
1569    #[test]
1570    fn preamble_to_from_bytes() {
1571        test_init_log();
1572        let tys = [
1573            PreambleType::Color(Color::White),
1574            PreambleType::Color(Color::Green),
1575            PreambleType::Color(Color::Blue),
1576            PreambleType::Color(Color::Cyan),
1577            PreambleType::Color(Color::Red),
1578            PreambleType::Color(Color::Yellow),
1579            PreambleType::Color(Color::Magenta),
1580            PreambleType::WhiteItalics,
1581            PreambleType::Indent0,
1582            PreambleType::Indent4,
1583            PreambleType::Indent8,
1584            PreambleType::Indent12,
1585            PreambleType::Indent16,
1586            PreambleType::Indent20,
1587            PreambleType::Indent24,
1588            PreambleType::Indent28,
1589        ];
1590        for row in 0..=14 {
1591            for underline in [true, false] {
1592                for ty in tys {
1593                    for channel in [Channel::ONE, Channel::TWO] {
1594                        let preamble = Code::Control(ControlCode {
1595                            field: None,
1596                            channel,
1597                            control: Control::PreambleAddress(PreambleAddressCode {
1598                                row,
1599                                underline,
1600                                ty,
1601                            }),
1602                        });
1603                        debug!("{preamble:?}");
1604                        let mut data = vec![];
1605                        preamble.write(&mut data).unwrap();
1606                        debug!("{data:x?}");
1607                        let parsed = Code::from_data([data[0], data[1]]).unwrap();
1608                        assert_eq!(preamble, parsed[0]);
1609                    }
1610                }
1611            }
1612        }
1613    }
1614
1615    #[test]
1616    fn midrow_to_from_bytes() {
1617        test_init_log();
1618        let colors = [
1619            MidRowColor::Color(Color::White),
1620            MidRowColor::Color(Color::Green),
1621            MidRowColor::Color(Color::Blue),
1622            MidRowColor::Color(Color::Cyan),
1623            MidRowColor::Color(Color::Red),
1624            MidRowColor::Color(Color::Yellow),
1625            MidRowColor::Color(Color::Magenta),
1626            MidRowColor::Italics,
1627        ];
1628        for underline in [true, false] {
1629            for color in colors {
1630                for channel in [Channel::ONE, Channel::TWO] {
1631                    let midrow = Code::Control(ControlCode {
1632                        field: None,
1633                        channel,
1634                        control: Control::MidRow(MidRow { underline, color }),
1635                    });
1636                    debug!("{midrow:?}");
1637                    let mut data = vec![];
1638                    midrow.write(&mut data).unwrap();
1639                    debug!("{data:x?}");
1640                    let parsed = Code::from_data([data[0], data[1]]).unwrap();
1641                    assert_eq!(midrow, parsed[0]);
1642                }
1643            }
1644        }
1645    }
1646
1647    #[test]
1648    fn field2_control_to_from_bytes() {
1649        test_init_log();
1650        let codes = [
1651            Control::ResumeCaptionLoading,
1652            Control::Backspace,
1653            Control::AlarmOff,
1654            Control::AlarmOn,
1655            Control::DeleteToEndOfRow,
1656            Control::RollUp2,
1657            Control::RollUp3,
1658            Control::RollUp4,
1659            Control::FlashOn,
1660            Control::ResumeDirectionCaptioning,
1661            Control::TextRestart,
1662            Control::ResumeTextDisplay,
1663            Control::EraseDisplayedMemory,
1664            Control::CarriageReturn,
1665            Control::EraseNonDisplayedMemory,
1666            Control::EndOfCaption,
1667        ];
1668        for control in codes {
1669            for field in [Field::ONE, Field::TWO] {
1670                for channel in [Channel::ONE, Channel::TWO] {
1671                    let control = Code::Control(ControlCode {
1672                        field: Some(field),
1673                        channel,
1674                        control,
1675                    });
1676                    debug!("{control:?}");
1677                    let mut data = vec![];
1678                    control.write(&mut data).unwrap();
1679                    debug!("{data:x?}");
1680                    let parsed = Code::from_data([data[0], data[1]]).unwrap();
1681                    assert_eq!(control, parsed[0]);
1682                }
1683            }
1684        }
1685    }
1686
1687    #[test]
1688    fn control_code_to_from_char() {
1689        test_init_log();
1690
1691        for control in CONTROL_MAP_TABLE.iter() {
1692            let Some(utf8) = control.utf8 else {
1693                continue;
1694            };
1695
1696            debug!("{control:?}");
1697            let orig = Code::Control(ControlCode {
1698                field: None,
1699                channel: Channel::ONE,
1700                control: control.control,
1701            });
1702            assert_eq!(Some(utf8), orig.char());
1703            let code = Code::from_char(utf8, Channel::ONE).unwrap();
1704            assert_eq!(orig, code);
1705            assert_eq!(Some(utf8), code.char());
1706        }
1707    }
1708}