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