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