1 | #![allow (clippy::exhaustive_enums)] |
2 | |
3 | use core::mem; |
4 | |
5 | #[derive (Debug, Copy, Clone, PartialEq, Eq)] |
6 | #[repr (u8)] |
7 | #[derive (Default)] |
8 | pub enum State { |
9 | Anywhere = 0, |
10 | CsiEntry = 1, |
11 | CsiIgnore = 2, |
12 | CsiIntermediate = 3, |
13 | CsiParam = 4, |
14 | DcsEntry = 5, |
15 | DcsIgnore = 6, |
16 | DcsIntermediate = 7, |
17 | DcsParam = 8, |
18 | DcsPassthrough = 9, |
19 | Escape = 10, |
20 | EscapeIntermediate = 11, |
21 | #[default] |
22 | Ground = 12, |
23 | OscString = 13, |
24 | SosPmApcString = 14, |
25 | Utf8 = 15, |
26 | } |
27 | |
28 | impl TryFrom<u8> for State { |
29 | type Error = u8; |
30 | |
31 | #[inline (always)] |
32 | fn try_from(raw: u8) -> Result<Self, Self::Error> { |
33 | STATES.get(raw as usize).ok_or(err:raw).copied() |
34 | } |
35 | } |
36 | |
37 | const STATES: [State; 16] = [ |
38 | State::Anywhere, |
39 | State::CsiEntry, |
40 | State::CsiIgnore, |
41 | State::CsiIntermediate, |
42 | State::CsiParam, |
43 | State::DcsEntry, |
44 | State::DcsIgnore, |
45 | State::DcsIntermediate, |
46 | State::DcsParam, |
47 | State::DcsPassthrough, |
48 | State::Escape, |
49 | State::EscapeIntermediate, |
50 | State::Ground, |
51 | State::OscString, |
52 | State::SosPmApcString, |
53 | State::Utf8, |
54 | ]; |
55 | |
56 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
57 | #[repr (u8)] |
58 | #[derive (Default)] |
59 | pub enum Action { |
60 | #[default] |
61 | Nop = 0, |
62 | Clear = 1, |
63 | Collect = 2, |
64 | CsiDispatch = 3, |
65 | EscDispatch = 4, |
66 | Execute = 5, |
67 | Hook = 6, |
68 | Ignore = 7, |
69 | OscEnd = 8, |
70 | OscPut = 9, |
71 | OscStart = 10, |
72 | Param = 11, |
73 | Print = 12, |
74 | Put = 13, |
75 | Unhook = 14, |
76 | BeginUtf8 = 15, |
77 | } |
78 | |
79 | impl TryFrom<u8> for Action { |
80 | type Error = u8; |
81 | |
82 | #[inline (always)] |
83 | fn try_from(raw: u8) -> Result<Self, Self::Error> { |
84 | ACTIONS.get(raw as usize).ok_or(err:raw).copied() |
85 | } |
86 | } |
87 | |
88 | const ACTIONS: [Action; 16] = [ |
89 | Action::Nop, |
90 | Action::Clear, |
91 | Action::Collect, |
92 | Action::CsiDispatch, |
93 | Action::EscDispatch, |
94 | Action::Execute, |
95 | Action::Hook, |
96 | Action::Ignore, |
97 | Action::OscEnd, |
98 | Action::OscPut, |
99 | Action::OscStart, |
100 | Action::Param, |
101 | Action::Print, |
102 | Action::Put, |
103 | Action::Unhook, |
104 | Action::BeginUtf8, |
105 | ]; |
106 | |
107 | /// Unpack a u8 into a State and Action |
108 | /// |
109 | /// The implementation of this assumes that there are *precisely* 16 variants for both Action and |
110 | /// State. Furthermore, it assumes that the enums are tag-only; that is, there is no data in any |
111 | /// variant. |
112 | /// |
113 | /// Bad things will happen if those invariants are violated. |
114 | #[inline (always)] |
115 | pub(crate) const fn unpack(delta: u8) -> (State, Action) { |
116 | unsafe { |
117 | ( |
118 | // State is stored in bottom 4 bits |
119 | mem::transmute::<u8, State>(src:delta & 0x0f), |
120 | // Action is stored in top 4 bits |
121 | mem::transmute::<u8, Action>(src:delta >> 4), |
122 | ) |
123 | } |
124 | } |
125 | |
126 | #[inline (always)] |
127 | #[cfg (test)] |
128 | pub(crate) const fn pack(state: State, action: Action) -> u8 { |
129 | (action as u8) << 4 | state as u8 |
130 | } |
131 | |
132 | #[cfg (test)] |
133 | mod tests { |
134 | use super::*; |
135 | |
136 | #[test ] |
137 | fn unpack_state_action() { |
138 | match unpack(0xee) { |
139 | (State::SosPmApcString, Action::Unhook) => (), |
140 | _ => panic!("unpack failed" ), |
141 | } |
142 | |
143 | match unpack(0x0f) { |
144 | (State::Utf8, Action::Nop) => (), |
145 | _ => panic!("unpack failed" ), |
146 | } |
147 | |
148 | match unpack(0xff) { |
149 | (State::Utf8, Action::BeginUtf8) => (), |
150 | _ => panic!("unpack failed" ), |
151 | } |
152 | } |
153 | |
154 | #[test ] |
155 | fn pack_state_action() { |
156 | match unpack(0xee) { |
157 | (State::SosPmApcString, Action::Unhook) => (), |
158 | _ => panic!("unpack failed" ), |
159 | } |
160 | |
161 | match unpack(0x0f) { |
162 | (State::Utf8, Action::Nop) => (), |
163 | _ => panic!("unpack failed" ), |
164 | } |
165 | |
166 | match unpack(0xff) { |
167 | (State::Utf8, Action::BeginUtf8) => (), |
168 | _ => panic!("unpack failed" ), |
169 | } |
170 | } |
171 | } |
172 | |