1 use std::iter;
2 
3 #[derive(Clone, Copy, Hash, PartialEq, Eq)]
4 pub(crate) struct BoolSettingIndex(usize);
5 
6 #[derive(Hash, PartialEq, Eq)]
7 pub(crate) struct BoolSetting {
8     pub default: bool,
9     pub bit_offset: u8,
10     pub predicate_number: u8,
11 }
12 
13 #[derive(Hash, PartialEq, Eq)]
14 pub(crate) enum SpecificSetting {
15     Bool(BoolSetting),
16     Enum(Vec<&'static str>),
17     Num(u8),
18 }
19 
20 #[derive(Hash, PartialEq, Eq)]
21 pub(crate) struct Setting {
22     pub name: &'static str,
23     pub description: &'static str,
24     pub comment: &'static str,
25     pub specific: SpecificSetting,
26     pub byte_offset: u8,
27 }
28 
29 impl Setting {
default_byte(&self) -> u830     pub fn default_byte(&self) -> u8 {
31         match self.specific {
32             SpecificSetting::Bool(BoolSetting {
33                 default,
34                 bit_offset,
35                 ..
36             }) => {
37                 if default {
38                     1 << bit_offset
39                 } else {
40                     0
41                 }
42             }
43             SpecificSetting::Enum(_) => 0,
44             SpecificSetting::Num(default) => default,
45         }
46     }
47 
byte_for_value(&self, v: bool) -> u848     fn byte_for_value(&self, v: bool) -> u8 {
49         match self.specific {
50             SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
51                 if v {
52                     1 << bit_offset
53                 } else {
54                     0
55                 }
56             }
57             _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
58         }
59     }
60 
byte_mask(&self) -> u861     fn byte_mask(&self) -> u8 {
62         match self.specific {
63             SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
64             _ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
65         }
66     }
67 }
68 
69 #[derive(Hash, PartialEq, Eq, Copy, Clone)]
70 pub(crate) struct PresetIndex(usize);
71 
72 #[derive(Hash, PartialEq, Eq)]
73 pub(crate) enum PresetType {
74     BoolSetting(BoolSettingIndex),
75     OtherPreset(PresetIndex),
76 }
77 
78 impl From<BoolSettingIndex> for PresetType {
from(bool_setting_index: BoolSettingIndex) -> Self79     fn from(bool_setting_index: BoolSettingIndex) -> Self {
80         PresetType::BoolSetting(bool_setting_index)
81     }
82 }
83 impl From<PresetIndex> for PresetType {
from(value: PresetIndex) -> Self84     fn from(value: PresetIndex) -> Self {
85         PresetType::OtherPreset(value)
86     }
87 }
88 
89 #[derive(Hash, PartialEq, Eq)]
90 pub(crate) struct Preset {
91     pub name: &'static str,
92     pub description: &'static str,
93     values: Vec<BoolSettingIndex>,
94 }
95 
96 impl Preset {
layout(&self, group: &SettingGroup) -> Vec<(u8, u8)>97     pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
98         let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
99             .take(group.settings_size as usize)
100             .collect();
101         for bool_index in &self.values {
102             let setting = &group.settings[bool_index.0];
103             let mask = setting.byte_mask();
104             let val = setting.byte_for_value(true);
105             assert!((val & !mask) == 0);
106             let (ref mut l_mask, ref mut l_val) =
107                 *layout.get_mut(setting.byte_offset as usize).unwrap();
108             *l_mask |= mask;
109             *l_val = (*l_val & !mask) | val;
110         }
111         layout
112     }
113 
setting_names<'a>( &'a self, group: &'a SettingGroup, ) -> impl Iterator<Item = &'static str> + 'a114     pub fn setting_names<'a>(
115         &'a self,
116         group: &'a SettingGroup,
117     ) -> impl Iterator<Item = &'static str> + 'a {
118         self.values
119             .iter()
120             .map(|bool_index| group.settings[bool_index.0].name)
121     }
122 }
123 
124 pub(crate) struct SettingGroup {
125     pub name: &'static str,
126     pub settings: Vec<Setting>,
127     pub bool_start_byte_offset: u8,
128     pub settings_size: u8,
129     pub presets: Vec<Preset>,
130 }
131 
132 impl SettingGroup {
num_bool_settings(&self) -> u8133     fn num_bool_settings(&self) -> u8 {
134         self.settings
135             .iter()
136             .filter(|s| matches!(s.specific, SpecificSetting::Bool(_)))
137             .count() as u8
138     }
139 
byte_size(&self) -> u8140     pub fn byte_size(&self) -> u8 {
141         self.bool_start_byte_offset + (self.num_bool_settings() + 7) / 8
142     }
143 }
144 
145 /// This is the basic information needed to track the specific parts of a setting when building
146 /// them.
147 pub(crate) enum ProtoSpecificSetting {
148     Bool(bool),
149     Enum(Vec<&'static str>),
150     Num(u8),
151 }
152 
153 /// This is the information provided during building for a setting.
154 struct ProtoSetting {
155     name: &'static str,
156     description: &'static str,
157     comment: &'static str,
158     specific: ProtoSpecificSetting,
159 }
160 
161 pub(crate) struct SettingGroupBuilder {
162     name: &'static str,
163     settings: Vec<ProtoSetting>,
164     presets: Vec<Preset>,
165 }
166 
167 impl SettingGroupBuilder {
new(name: &'static str) -> Self168     pub fn new(name: &'static str) -> Self {
169         Self {
170             name,
171             settings: Vec::new(),
172             presets: Vec::new(),
173         }
174     }
175 
add_setting( &mut self, name: &'static str, description: &'static str, comment: &'static str, specific: ProtoSpecificSetting, )176     fn add_setting(
177         &mut self,
178         name: &'static str,
179         description: &'static str,
180         comment: &'static str,
181         specific: ProtoSpecificSetting,
182     ) {
183         self.settings.push(ProtoSetting {
184             name,
185             description,
186             comment,
187             specific,
188         })
189     }
190 
add_bool( &mut self, name: &'static str, description: &'static str, comment: &'static str, default: bool, ) -> BoolSettingIndex191     pub fn add_bool(
192         &mut self,
193         name: &'static str,
194         description: &'static str,
195         comment: &'static str,
196         default: bool,
197     ) -> BoolSettingIndex {
198         self.add_setting(
199             name,
200             description,
201             comment,
202             ProtoSpecificSetting::Bool(default),
203         );
204         BoolSettingIndex(self.settings.len() - 1)
205     }
206 
add_enum( &mut self, name: &'static str, description: &'static str, comment: &'static str, values: Vec<&'static str>, )207     pub fn add_enum(
208         &mut self,
209         name: &'static str,
210         description: &'static str,
211         comment: &'static str,
212         values: Vec<&'static str>,
213     ) {
214         self.add_setting(
215             name,
216             description,
217             comment,
218             ProtoSpecificSetting::Enum(values),
219         );
220     }
221 
add_num( &mut self, name: &'static str, description: &'static str, comment: &'static str, default: u8, )222     pub fn add_num(
223         &mut self,
224         name: &'static str,
225         description: &'static str,
226         comment: &'static str,
227         default: u8,
228     ) {
229         self.add_setting(
230             name,
231             description,
232             comment,
233             ProtoSpecificSetting::Num(default),
234         );
235     }
236 
add_preset( &mut self, name: &'static str, description: &'static str, args: Vec<PresetType>, ) -> PresetIndex237     pub fn add_preset(
238         &mut self,
239         name: &'static str,
240         description: &'static str,
241         args: Vec<PresetType>,
242     ) -> PresetIndex {
243         let mut values = Vec::new();
244         for arg in args {
245             match arg {
246                 PresetType::OtherPreset(index) => {
247                     values.extend(self.presets[index.0].values.iter());
248                 }
249                 PresetType::BoolSetting(index) => values.push(index),
250             }
251         }
252         self.presets.push(Preset {
253             name,
254             description,
255             values,
256         });
257         PresetIndex(self.presets.len() - 1)
258     }
259 
260     /// Compute the layout of the byte vector used to represent this settings
261     /// group.
262     ///
263     /// The byte vector contains the following entries in order:
264     ///
265     /// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
266     /// 2. `BoolSetting` settings.
267     ///
268     /// Set `self.settings_size` to the length of the byte vector prefix that
269     /// contains the settings. All bytes after that are computed, not
270     /// configured.
271     ///
272     /// Set `self.boolean_offset` to the beginning of the numbered predicates,
273     /// 2. in the list above.
274     ///
275     /// Assign `byte_offset` and `bit_offset` fields in all settings.
build(self) -> SettingGroup276     pub fn build(self) -> SettingGroup {
277         let mut group = SettingGroup {
278             name: self.name,
279             settings: Vec::new(),
280             bool_start_byte_offset: 0,
281             settings_size: 0,
282             presets: Vec::new(),
283         };
284 
285         let mut byte_offset = 0;
286 
287         // Assign the non-boolean settings first.
288         for s in &self.settings {
289             let specific = match s.specific {
290                 ProtoSpecificSetting::Bool(..) => continue,
291                 ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
292                 ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
293             };
294 
295             group.settings.push(Setting {
296                 name: s.name,
297                 description: s.description,
298                 comment: s.comment,
299                 byte_offset,
300                 specific,
301             });
302 
303             byte_offset += 1;
304         }
305 
306         group.bool_start_byte_offset = byte_offset;
307 
308         let mut predicate_number = 0;
309 
310         // Then the boolean settings.
311         for s in &self.settings {
312             let default = match s.specific {
313                 ProtoSpecificSetting::Bool(default) => default,
314                 ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
315             };
316             group.settings.push(Setting {
317                 name: s.name,
318                 description: s.description,
319                 comment: s.comment,
320                 byte_offset: byte_offset + predicate_number / 8,
321                 specific: SpecificSetting::Bool(BoolSetting {
322                     default,
323                     bit_offset: predicate_number % 8,
324                     predicate_number,
325                 }),
326             });
327             predicate_number += 1;
328         }
329 
330         group.settings_size = group.byte_size();
331 
332         group.presets.extend(self.presets);
333 
334         group
335     }
336 }
337