lib: rework the configuration macros to make them more succinct

- Surprisingly, this ended up having more lines of code, but in my
  opinion the the code is cleaner.
This commit is contained in:
Matt Bilker
2022-08-25 10:40:05 +00:00
parent 1878829903
commit 087a5c8713

View File

@@ -9,8 +9,8 @@
//! configuration structure //! configuration structure
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap;
use std::cmp; use std::cmp;
use std::collections::HashMap;
use std::env; use std::env;
use std::fmt; use std::fmt;
use std::fs; use std::fs;
@@ -540,77 +540,106 @@ fn apply_profile_override(
vgpu_type: &str, vgpu_type: &str,
config_override: &VgpuProfileOverride, config_override: &VgpuProfileOverride,
) -> bool { ) -> bool {
macro_rules! handle_copy_overrides { macro_rules! patch_msg {
($source_field:ident => $target_field:ident) => { ($target_field:ident, $value:expr) => {
if let Some(value) = config_override.$source_field {
info!( info!(
"Patching {}/{}: {} -> {}", "Patching {}/{}: {} -> {}",
vgpu_type, vgpu_type,
stringify!($target_field), stringify!($target_field),
config.$target_field, config.$target_field,
value $value
); );
config.$target_field = value;
}
}; };
($field:ident) => { ($target_field:ident, $preprocess:ident, $value:expr) => {
handle_copy_overrides!($field => $field);
};
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => {
$(
handle_copy_overrides!($source_field $(=> $target_field)?);
)*
};
}
macro_rules! handle_bool_overrides {
($source_field:ident => $target_field:ident) => {
if let Some(value) = config_override.$source_field {
let value = cmp::max(cmp::min(value, 0), 1);
info!( info!(
"Patching {}/{}: {} -> {}", "Patching {}/{}: {} -> {}",
vgpu_type, vgpu_type,
stringify!($target_field), stringify!($target_field),
config.$target_field, $preprocess(&config.$target_field),
value $value
); );
config.$target_field = value;
}
};
($field:ident) => {
handle_bool_overrides!($field => $field);
};
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => {
$(
handle_bool_overrides!($source_field $(=> $target_field)?);
)*
}; };
} }
macro_rules! handle_str_overrides { macro_rules! error_too_long {
($source_field:ident => $target_field:ident) => { ($target_field:ident, $value:expr) => {
if let Some(value) = config_override.$source_field {
let value_bytes = value.as_bytes();
// Use `len - 1` to account for the required NULL terminator.
if value_bytes.len() > config.$target_field.len() - 1 {
error!( error!(
"Patching {}/{}: value '{}' is too long", "Patching {}/{}: value '{}' is too long",
vgpu_type, vgpu_type,
stringify!($target_field), stringify!($target_field),
value $value
); );
return false; return false;
};
}
macro_rules! handle_override {
// Override entrypoint when the same field name is used as the source and target without
// an explicit `=>`.
(
class: $class:ident,
source_field: $field:ident,
) => {
handle_override! {
class: $class,
source_field: $field,
target_field: $field,
}
};
// Override entrypoint when both the source and target field names are defined explicitly.
(
class: $class:ident,
source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
if let Some(value) = config_override.$source_field {
handle_override! {
class: $class,
value: value,
source_field: $source_field,
target_field: $target_field,
}
}
};
// The following are override handlers for each field class type (`bool`, `copy`, `str`,
// and `wide_str`).
(
class: bool,
value: $value:ident,
source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
let $value = cmp::max(cmp::min($value, 0), 1);
patch_msg!($target_field, $value);
config.$target_field = $value;
};
(
class: copy,
value: $value:ident,
source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
patch_msg!($target_field, $value);
config.$target_field = $value;
};
(
class: str,
value: $value:ident,
source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
let value_bytes = $value.as_bytes();
// Use `len - 1` to account for the required NULL terminator.
if value_bytes.len() > config.$target_field.len() - 1 {
error_too_long!($target_field, $value);
} else { } else {
info!( patch_msg!($target_field, from_c_str, $value);
"Patching {}/{}: '{}' -> '{}'",
vgpu_type,
stringify!($target_field),
from_c_str(&config.$target_field),
value
);
// Zero out the field first. // Zero out the field first.
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48) // (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
@@ -621,38 +650,18 @@ fn apply_profile_override(
// Write the string bytes. // Write the string bytes.
let _ = config.$target_field[..].as_mut().write_all(value_bytes); let _ = config.$target_field[..].as_mut().write_all(value_bytes);
} }
}
}; };
($field:ident) => { (
handle_str_overrides!($field => $field); class: wide_str,
}; value: $value:ident,
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => { source_field: $source_field:ident,
$( target_field: $target_field:ident,
handle_str_overrides!($source_field $(=> $target_field)?); ) => {
)*
};
}
macro_rules! handle_wide_str_overrides {
($source_field:ident => $target_field:ident) => {
if let Some(value) = config_override.$source_field {
// Use `len - 1` to account for the required NULL terminator. // Use `len - 1` to account for the required NULL terminator.
if value.encode_utf16().count() > config.$target_field.len() - 1 { if $value.encode_utf16().count() > config.$target_field.len() - 1 {
error!( error_too_long!($target_field, $value);
"Patching {}/{}: value '{}' is too long",
vgpu_type,
stringify!($target_field),
value
);
return false;
} else { } else {
info!( patch_msg!($target_field, WideCharFormat, $value);
"Patching {}/{}: '{}' -> '{}'",
vgpu_type,
stringify!($target_field),
WideCharFormat(&config.$target_field),
value
);
// Zero out the field first. // Zero out the field first.
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48) // (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
@@ -663,53 +672,62 @@ fn apply_profile_override(
// Write the string bytes. // Write the string bytes.
for (v, ch) in config.$target_field[..] for (v, ch) in config.$target_field[..]
.iter_mut() .iter_mut()
.zip(value.encode_utf16().chain(Some(0))) .zip($value.encode_utf16().chain(Some(0)))
{ {
*v = ch; *v = ch;
} }
} }
};
} }
}; macro_rules! handle_overrides {
($field:ident) => { (
handle_wide_str_overrides!($field => $field); $($class:ident: [
}; $($source_field:ident $(=> $target_field:ident)?),*$(,)?
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => { ]),*$(,)?
) => {
$( $(
handle_wide_str_overrides!($source_field $(=> $target_field)?); $(
handle_override! {
class: $class,
source_field: $source_field,
$(target_field: $target_field,)?
}
)*
)* )*
}; };
} }
// While the following could be done with two statements. I wanted the log statements to be in // While the following could be done with fewer branches, I wanted the log statements to be in
// field order. // field order.
handle_copy_overrides! { handle_overrides! {
copy: [
gpu_type => vgpu_type, gpu_type => vgpu_type,
} ],
handle_str_overrides! { str: [
card_name => vgpu_name, card_name => vgpu_name,
vgpu_type => vgpu_class, vgpu_type => vgpu_class,
features, features,
} ],
handle_copy_overrides! { copy: [
max_instances, max_instances,
num_displays => num_heads, num_displays => num_heads,
display_width => max_resolution_x, display_width => max_resolution_x,
display_height => max_resolution_y, display_height => max_resolution_y,
max_pixels, max_pixels,
frl_config, frl_config,
} ],
handle_bool_overrides! { bool: [
cuda_enabled, cuda_enabled,
ecc_supported, ecc_supported,
} ],
handle_copy_overrides! { copy: [
mig_instance_size, mig_instance_size,
} ],
handle_bool_overrides! { bool: [
multi_vgpu_supported, multi_vgpu_supported,
} ],
handle_copy_overrides! { copy: [
pci_id => vdev_id, pci_id => vdev_id,
pci_device_id => pdev_id, pci_device_id => pdev_id,
framebuffer => fb_length, framebuffer => fb_length,
@@ -717,19 +735,20 @@ fn apply_profile_override(
framebuffer_reservation => fb_reservation, framebuffer_reservation => fb_reservation,
encoder_capacity, encoder_capacity,
bar1_length, bar1_length,
} ],
handle_bool_overrides! { bool: [
frl_enabled => frl_enable, frl_enabled => frl_enable,
} ],
handle_str_overrides! { str: [
adapter_name, adapter_name,
} ],
handle_wide_str_overrides! { wide_str: [
adapter_name => adapter_name_unicode, adapter_name => adapter_name_unicode,
} ],
handle_str_overrides! { str: [
short_gpu_name => short_gpu_name_string, short_gpu_name => short_gpu_name_string,
license_type => licensed_product_name, license_type => licensed_product_name,
],
} }
true true