408 lines
12 KiB
Rust
408 lines
12 KiB
Rust
use std::iter;
|
|
|
|
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
|
|
pub(crate) struct BoolSettingIndex(usize);
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct BoolSetting {
|
|
pub default: bool,
|
|
pub bit_offset: u8,
|
|
pub predicate_number: u8,
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum SpecificSetting {
|
|
Bool(BoolSetting),
|
|
Enum(Vec<&'static str>),
|
|
Num(u8),
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct Setting {
|
|
pub name: &'static str,
|
|
pub comment: &'static str,
|
|
pub specific: SpecificSetting,
|
|
pub byte_offset: u8,
|
|
}
|
|
|
|
impl Setting {
|
|
pub fn default_byte(&self) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting {
|
|
default,
|
|
bit_offset,
|
|
..
|
|
}) => {
|
|
if default {
|
|
1 << bit_offset
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
SpecificSetting::Enum(_) => 0,
|
|
SpecificSetting::Num(default) => default,
|
|
}
|
|
}
|
|
|
|
fn byte_for_value(&self, v: bool) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => {
|
|
if v {
|
|
1 << bit_offset
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
|
}
|
|
}
|
|
|
|
fn byte_mask(&self) -> u8 {
|
|
match self.specific {
|
|
SpecificSetting::Bool(BoolSetting { bit_offset, .. }) => 1 << bit_offset,
|
|
_ => panic!("byte_for_value shouldn't be used for non-boolean settings."),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct PresetIndex(usize);
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum PresetType {
|
|
BoolSetting(BoolSettingIndex),
|
|
OtherPreset(PresetIndex),
|
|
}
|
|
|
|
impl Into<PresetType> for BoolSettingIndex {
|
|
fn into(self) -> PresetType {
|
|
PresetType::BoolSetting(self)
|
|
}
|
|
}
|
|
impl Into<PresetType> for PresetIndex {
|
|
fn into(self) -> PresetType {
|
|
PresetType::OtherPreset(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) struct Preset {
|
|
pub name: &'static str,
|
|
values: Vec<BoolSettingIndex>,
|
|
}
|
|
|
|
impl Preset {
|
|
pub fn layout(&self, group: &SettingGroup) -> Vec<(u8, u8)> {
|
|
let mut layout: Vec<(u8, u8)> = iter::repeat((0, 0))
|
|
.take(group.settings_size as usize)
|
|
.collect();
|
|
for bool_index in &self.values {
|
|
let setting = &group.settings[bool_index.0];
|
|
let mask = setting.byte_mask();
|
|
let val = setting.byte_for_value(true);
|
|
assert!((val & !mask) == 0);
|
|
let (ref mut l_mask, ref mut l_val) =
|
|
*layout.get_mut(setting.byte_offset as usize).unwrap();
|
|
*l_mask |= mask;
|
|
*l_val = (*l_val & !mask) | val;
|
|
}
|
|
layout
|
|
}
|
|
}
|
|
|
|
pub(crate) struct SettingGroup {
|
|
pub name: &'static str,
|
|
pub settings: Vec<Setting>,
|
|
pub bool_start_byte_offset: u8,
|
|
pub settings_size: u8,
|
|
pub presets: Vec<Preset>,
|
|
pub predicates: Vec<Predicate>,
|
|
}
|
|
|
|
impl SettingGroup {
|
|
fn num_bool_settings(&self) -> u8 {
|
|
self.settings
|
|
.iter()
|
|
.filter(|s| {
|
|
if let SpecificSetting::Bool(_) = s.specific {
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
})
|
|
.count() as u8
|
|
}
|
|
|
|
pub fn byte_size(&self) -> u8 {
|
|
let num_predicates = self.num_bool_settings() + (self.predicates.len() as u8);
|
|
self.bool_start_byte_offset + (num_predicates + 7) / 8
|
|
}
|
|
|
|
pub fn get_bool(&self, name: &'static str) -> (BoolSettingIndex, &Self) {
|
|
for (i, s) in self.settings.iter().enumerate() {
|
|
if let SpecificSetting::Bool(_) = s.specific {
|
|
if s.name == name {
|
|
return (BoolSettingIndex(i), self);
|
|
}
|
|
}
|
|
}
|
|
panic!("Should have found bool setting by name.");
|
|
}
|
|
|
|
pub fn predicate_by_name(&self, name: &'static str) -> SettingPredicateNumber {
|
|
self.predicates
|
|
.iter()
|
|
.find(|pred| pred.name == name)
|
|
.unwrap_or_else(|| panic!("unknown predicate {}", name))
|
|
.number
|
|
}
|
|
}
|
|
|
|
/// This is the basic information needed to track the specific parts of a setting when building
|
|
/// them.
|
|
pub(crate) enum ProtoSpecificSetting {
|
|
Bool(bool),
|
|
Enum(Vec<&'static str>),
|
|
Num(u8),
|
|
}
|
|
|
|
/// This is the information provided during building for a setting.
|
|
struct ProtoSetting {
|
|
name: &'static str,
|
|
comment: &'static str,
|
|
specific: ProtoSpecificSetting,
|
|
}
|
|
|
|
#[derive(Hash, PartialEq, Eq)]
|
|
pub(crate) enum PredicateNode {
|
|
OwnedBool(BoolSettingIndex),
|
|
SharedBool(&'static str, &'static str),
|
|
Not(Box<PredicateNode>),
|
|
And(Box<PredicateNode>, Box<PredicateNode>),
|
|
}
|
|
|
|
impl Into<PredicateNode> for BoolSettingIndex {
|
|
fn into(self) -> PredicateNode {
|
|
PredicateNode::OwnedBool(self)
|
|
}
|
|
}
|
|
impl<'a> Into<PredicateNode> for (BoolSettingIndex, &'a SettingGroup) {
|
|
fn into(self) -> PredicateNode {
|
|
let (index, group) = (self.0, self.1);
|
|
let setting = &group.settings[index.0];
|
|
PredicateNode::SharedBool(group.name, setting.name)
|
|
}
|
|
}
|
|
|
|
impl PredicateNode {
|
|
fn render(&self, group: &SettingGroup) -> String {
|
|
match *self {
|
|
PredicateNode::OwnedBool(bool_setting_index) => format!(
|
|
"{}.{}()",
|
|
group.name, group.settings[bool_setting_index.0].name
|
|
),
|
|
PredicateNode::SharedBool(ref group_name, ref bool_name) => {
|
|
format!("{}.{}()", group_name, bool_name)
|
|
}
|
|
PredicateNode::And(ref lhs, ref rhs) => {
|
|
format!("{} && {}", lhs.render(group), rhs.render(group))
|
|
}
|
|
PredicateNode::Not(ref node) => format!("!({})", node.render(group)),
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ProtoPredicate {
|
|
pub name: &'static str,
|
|
node: PredicateNode,
|
|
}
|
|
|
|
pub(crate) type SettingPredicateNumber = u8;
|
|
|
|
pub(crate) struct Predicate {
|
|
pub name: &'static str,
|
|
node: PredicateNode,
|
|
pub number: SettingPredicateNumber,
|
|
}
|
|
|
|
impl Predicate {
|
|
pub fn render(&self, group: &SettingGroup) -> String {
|
|
self.node.render(group)
|
|
}
|
|
}
|
|
|
|
pub(crate) struct SettingGroupBuilder {
|
|
name: &'static str,
|
|
settings: Vec<ProtoSetting>,
|
|
presets: Vec<Preset>,
|
|
predicates: Vec<ProtoPredicate>,
|
|
}
|
|
|
|
impl SettingGroupBuilder {
|
|
pub fn new(name: &'static str) -> Self {
|
|
Self {
|
|
name,
|
|
settings: Vec::new(),
|
|
presets: Vec::new(),
|
|
predicates: Vec::new(),
|
|
}
|
|
}
|
|
|
|
fn add_setting(
|
|
&mut self,
|
|
name: &'static str,
|
|
comment: &'static str,
|
|
specific: ProtoSpecificSetting,
|
|
) {
|
|
self.settings.push(ProtoSetting {
|
|
name,
|
|
comment,
|
|
specific,
|
|
})
|
|
}
|
|
|
|
pub fn add_bool(
|
|
&mut self,
|
|
name: &'static str,
|
|
comment: &'static str,
|
|
default: bool,
|
|
) -> BoolSettingIndex {
|
|
assert!(
|
|
self.predicates.is_empty(),
|
|
"predicates must be added after the boolean settings"
|
|
);
|
|
self.add_setting(name, comment, ProtoSpecificSetting::Bool(default));
|
|
BoolSettingIndex(self.settings.len() - 1)
|
|
}
|
|
|
|
pub fn add_enum(
|
|
&mut self,
|
|
name: &'static str,
|
|
comment: &'static str,
|
|
values: Vec<&'static str>,
|
|
) {
|
|
self.add_setting(name, comment, ProtoSpecificSetting::Enum(values));
|
|
}
|
|
|
|
pub fn add_num(&mut self, name: &'static str, comment: &'static str, default: u8) {
|
|
self.add_setting(name, comment, ProtoSpecificSetting::Num(default));
|
|
}
|
|
|
|
pub fn add_predicate(&mut self, name: &'static str, node: PredicateNode) {
|
|
self.predicates.push(ProtoPredicate { name, node });
|
|
}
|
|
|
|
pub fn add_preset(&mut self, name: &'static str, args: Vec<PresetType>) -> PresetIndex {
|
|
let mut values = Vec::new();
|
|
for arg in args {
|
|
match arg {
|
|
PresetType::OtherPreset(index) => {
|
|
values.extend(self.presets[index.0].values.iter());
|
|
}
|
|
PresetType::BoolSetting(index) => values.push(index),
|
|
}
|
|
}
|
|
self.presets.push(Preset { name, values });
|
|
PresetIndex(self.presets.len() - 1)
|
|
}
|
|
|
|
/// Compute the layout of the byte vector used to represent this settings
|
|
/// group.
|
|
///
|
|
/// The byte vector contains the following entries in order:
|
|
///
|
|
/// 1. Byte-sized settings like `NumSetting` and `EnumSetting`.
|
|
/// 2. `BoolSetting` settings.
|
|
/// 3. Precomputed named predicates.
|
|
/// 4. Other numbered predicates, including parent predicates that need to be accessible by
|
|
/// number.
|
|
///
|
|
/// Set `self.settings_size` to the length of the byte vector prefix that
|
|
/// contains the settings. All bytes after that are computed, not
|
|
/// configured.
|
|
///
|
|
/// Set `self.boolean_offset` to the beginning of the numbered predicates,
|
|
/// 2. in the list above.
|
|
///
|
|
/// Assign `byte_offset` and `bit_offset` fields in all settings.
|
|
pub fn build(self) -> SettingGroup {
|
|
let mut group = SettingGroup {
|
|
name: self.name,
|
|
settings: Vec::new(),
|
|
bool_start_byte_offset: 0,
|
|
settings_size: 0,
|
|
presets: Vec::new(),
|
|
predicates: Vec::new(),
|
|
};
|
|
|
|
let mut byte_offset = 0;
|
|
|
|
// Assign the non-boolean settings first.
|
|
for s in &self.settings {
|
|
let specific = match s.specific {
|
|
ProtoSpecificSetting::Bool(..) => continue,
|
|
ProtoSpecificSetting::Enum(ref values) => SpecificSetting::Enum(values.clone()),
|
|
ProtoSpecificSetting::Num(default) => SpecificSetting::Num(default),
|
|
};
|
|
|
|
group.settings.push(Setting {
|
|
name: s.name,
|
|
comment: s.comment,
|
|
byte_offset,
|
|
specific,
|
|
});
|
|
|
|
byte_offset += 1;
|
|
}
|
|
|
|
group.bool_start_byte_offset = byte_offset;
|
|
|
|
let mut predicate_number = 0;
|
|
|
|
// Then the boolean settings.
|
|
for s in &self.settings {
|
|
let default = match s.specific {
|
|
ProtoSpecificSetting::Bool(default) => default,
|
|
ProtoSpecificSetting::Enum(_) | ProtoSpecificSetting::Num(_) => continue,
|
|
};
|
|
group.settings.push(Setting {
|
|
name: s.name,
|
|
comment: s.comment,
|
|
byte_offset: byte_offset + predicate_number / 8,
|
|
specific: SpecificSetting::Bool(BoolSetting {
|
|
default,
|
|
bit_offset: predicate_number % 8,
|
|
predicate_number,
|
|
}),
|
|
});
|
|
predicate_number += 1;
|
|
}
|
|
|
|
assert!(
|
|
group.predicates.is_empty(),
|
|
"settings_size is the byte size before adding predicates"
|
|
);
|
|
group.settings_size = group.byte_size();
|
|
|
|
// Sort predicates by name to ensure the same order as the Python code.
|
|
let mut predicates = self.predicates;
|
|
predicates.sort_by_key(|predicate| predicate.name);
|
|
|
|
group
|
|
.predicates
|
|
.extend(predicates.into_iter().map(|predicate| {
|
|
let number = predicate_number;
|
|
predicate_number += 1;
|
|
Predicate {
|
|
name: predicate.name,
|
|
node: predicate.node,
|
|
number,
|
|
}
|
|
}));
|
|
|
|
group.presets.extend(self.presets);
|
|
|
|
group
|
|
}
|
|
}
|