g_code/parse/compact/
meatpack.rs1use std::{cell::RefCell, ops::RangeFrom, rc::Rc};
6
7use nom::{
8 bytes::complete::tag,
9 combinator::{cond, flat_map, iterator},
10 number::complete::le_u8,
11 Compare, IResult, InputIter, InputLength, InputTake, Parser, Slice,
12};
13
14pub(crate) const MP_BOTH_UNPACKABLE_HEADER: [u8; 1] = [0xFF];
16
17pub(crate) const MP_SINGLE_UNPACKABLE_MASK: u8 = 0xF;
19
20pub(crate) const MP_COMMAND_HEADER: [u8; 2] = [0xFF, 0xFF];
22
23pub(crate) const MP_COMMAND_ENABLE_PACKING: u8 = 251;
25
26pub(crate) const MP_COMMAND_DISABLE_PACKING: u8 = 250;
28
29pub(crate) const MP_COMMAND_RESET_ALL: u8 = 249;
33
34pub(crate) const MP_COMMAND_QUERY_CONFIG: u8 = 248;
36
37pub(crate) const MP_COMMAND_ENABLE_NO_SPACES: u8 = 247;
41pub(crate) const MP_COMMAND_DISABLE_NO_SPACES: u8 = 246;
42
43pub fn meatpacked_to_string<I>(input: I) -> IResult<I, String, nom::error::Error<I>>
47where
48 I: Clone
49 + Slice<RangeFrom<usize>>
50 + InputIter<Item = u8>
51 + InputTake
52 + InputLength
53 + Compare<&'static [u8]>,
54{
55 let state = Rc::new(RefCell::new(MeatpackState::default()));
56 let mut parser = iterator(input, |input| decode_next(state.clone()).parse(input));
57 let it = &mut parser;
58 let acc = String::from_utf8(it.flatten().collect::<Vec<u8>>()).unwrap();
59 parser.finish().map(|(input, ())| (input, acc))
60}
61
62#[derive(Debug, Default)]
64struct MeatpackState {
65 packing: bool,
66 no_spaces: bool,
67}
68
69fn decode_next<I>(
71 state: Rc<RefCell<MeatpackState>>,
72) -> impl Parser<I, Vec<u8>, nom::error::Error<I>>
73where
74 I: Clone
75 + Slice<RangeFrom<usize>>
76 + InputIter<Item = u8>
77 + InputTake
78 + InputLength
79 + Compare<&'static [u8]>,
80{
81 let state_clone = state.clone();
82 flat_map(tag(MP_COMMAND_HEADER.as_slice()), |_tag| le_u8)
83 .map(move |command| {
84 let mut state = state.borrow_mut();
85 match command {
86 MP_COMMAND_ENABLE_PACKING => state.packing = true,
87 MP_COMMAND_DISABLE_PACKING => state.packing = false,
88 MP_COMMAND_ENABLE_NO_SPACES => state.no_spaces = true,
89 MP_COMMAND_DISABLE_NO_SPACES => state.no_spaces = false,
90 MP_COMMAND_RESET_ALL => state.packing = false,
91 MP_COMMAND_QUERY_CONFIG => {}
92 _other => {}
94 }
95 vec![]
96 })
97 .or(decode_character_pair(state_clone).map(|pair| pair.to_vec()))
98}
99
100fn decode_character_pair<I>(
102 state: Rc<RefCell<MeatpackState>>,
103) -> impl Parser<I, Vec<u8>, nom::error::Error<I>>
104where
105 I: Clone
106 + Slice<RangeFrom<usize>>
107 + InputIter<Item = u8>
108 + InputTake
109 + InputLength
110 + Compare<&'static [u8]>,
111{
112 let both_unpacked_parser = tag(MP_BOTH_UNPACKABLE_HEADER.as_slice())
113 .and(le_u8)
114 .and(le_u8)
115 .map(|((_tag, first), second)| [first, second].to_vec());
116
117 let packed_parser = flat_map(le_u8, move |byte: u8| {
118 let state = state.borrow();
119 let first_unpacked = if state.packing {
120 unpack_character(byte & MP_SINGLE_UNPACKABLE_MASK, state.no_spaces)
121 } else {
122 None
123 };
124 let second_unpacked = if state.packing {
125 unpack_character((byte >> 4) & MP_SINGLE_UNPACKABLE_MASK, state.no_spaces)
126 } else {
127 None
128 };
129 cond(
130 state.packing && (first_unpacked.is_none() || second_unpacked.is_none()),
131 le_u8,
132 )
133 .map(move |next_byte| {
134 let next_char = next_byte.map(|b| b);
135 match (first_unpacked, second_unpacked) {
136 (None, None) => [byte].to_vec(),
137 (None, Some(second)) => [next_char.unwrap(), second].to_vec(),
138 (Some(first), None) => [first, next_char.unwrap()].to_vec(),
139 (Some(first), Some(second)) => [first, second].to_vec(),
140 }
141 })
142 });
143
144 both_unpacked_parser.or(packed_parser)
145}
146
147const fn unpack_character(x: u8, no_spaces: bool) -> Option<u8> {
149 Some(match x {
150 0 => b'0',
151 1 => b'1',
152 2 => b'2',
153 3 => b'3',
154 4 => b'4',
155 5 => b'5',
156 6 => b'6',
157 7 => b'7',
158 8 => b'8',
159 9 => b'9',
160 10 => b'.',
161 11 if !no_spaces => b' ',
162 11 if no_spaces => b'E',
163 12 => b'\n',
164 13 => b'G',
165 14 => b'X',
166 _other => return None,
167 })
168}