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,196 +540,215 @@ 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); info!(
}; "Patching {}/{}: {} -> {}",
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => { vgpu_type,
$( stringify!($target_field),
handle_copy_overrides!($source_field $(=> $target_field)?); $preprocess(&config.$target_field),
)* $value
);
}; };
} }
macro_rules! handle_bool_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 { error!(
let value = cmp::max(cmp::min(value, 0), 1); "Patching {}/{}: value '{}' is too long",
vgpu_type,
stringify!($target_field),
$value
);
info!( return false;
"Patching {}/{}: {} -> {}",
vgpu_type,
stringify!($target_field),
config.$target_field,
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 {
($source_field:ident => $target_field:ident) => { 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 { if let Some(value) = config_override.$source_field {
let value_bytes = value.as_bytes(); handle_override! {
class: $class,
// Use `len - 1` to account for the required NULL terminator. value: value,
if value_bytes.len() > config.$target_field.len() - 1 { source_field: $source_field,
error!( target_field: $target_field,
"Patching {}/{}: value '{}' is too long",
vgpu_type,
stringify!($target_field),
value
);
return false;
} else {
info!(
"Patching {}/{}: '{}' -> '{}'",
vgpu_type,
stringify!($target_field),
from_c_str(&config.$target_field),
value
);
// Zero out the field first.
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
for v in config.$target_field.iter_mut() {
*v = 0;
}
// Write the string bytes.
let _ = config.$target_field[..].as_mut().write_all(value_bytes);
} }
} }
}; };
($field:ident) => {
handle_str_overrides!($field => $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;
}; };
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => { (
$( class: copy,
handle_str_overrides!($source_field $(=> $target_field)?); value: $value:ident,
)* source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
patch_msg!($target_field, $value);
config.$target_field = $value;
}; };
} (
macro_rules! handle_wide_str_overrides { class: str,
($source_field:ident => $target_field:ident) => { value: $value:ident,
if let Some(value) = config_override.$source_field { source_field: $source_field:ident,
// Use `len - 1` to account for the required NULL terminator. target_field: $target_field:ident,
if value.encode_utf16().count() > config.$target_field.len() - 1 { ) => {
error!( let value_bytes = $value.as_bytes();
"Patching {}/{}: value '{}' is too long",
vgpu_type,
stringify!($target_field),
value
);
return false; // Use `len - 1` to account for the required NULL terminator.
} else { if value_bytes.len() > config.$target_field.len() - 1 {
info!( error_too_long!($target_field, $value);
"Patching {}/{}: '{}' -> '{}'", } else {
vgpu_type, patch_msg!($target_field, from_c_str, $value);
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)
for v in config.$target_field.iter_mut() { for v in config.$target_field.iter_mut() {
*v = 0; *v = 0;
} }
// Write the string bytes. // Write the string bytes.
for (v, ch) in config.$target_field[..] let _ = config.$target_field[..].as_mut().write_all(value_bytes);
.iter_mut() }
.zip(value.encode_utf16().chain(Some(0))) };
{ (
*v = ch; class: wide_str,
} value: $value:ident,
source_field: $source_field:ident,
target_field: $target_field:ident,
) => {
// Use `len - 1` to account for the required NULL terminator.
if $value.encode_utf16().count() > config.$target_field.len() - 1 {
error_too_long!($target_field, $value);
} else {
patch_msg!($target_field, WideCharFormat, $value);
// Zero out the field first.
// (`fill` was stabilized in Rust 1.50, but Debian Bullseye ships with 1.48)
for v in config.$target_field.iter_mut() {
*v = 0;
}
// Write the string bytes.
for (v, ch) in config.$target_field[..]
.iter_mut()
.zip($value.encode_utf16().chain(Some(0)))
{
*v = ch;
} }
} }
}; };
($field:ident) => { }
handle_wide_str_overrides!($field => $field); macro_rules! handle_overrides {
}; (
($($source_field:ident $(=> $target_field:ident)?),*$(,)?) => { $($class: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! {
gpu_type => vgpu_type, copy: [
} gpu_type => vgpu_type,
handle_str_overrides! { ],
card_name => vgpu_name, str: [
vgpu_type => vgpu_class, card_name => vgpu_name,
features, vgpu_type => vgpu_class,
} features,
handle_copy_overrides! { ],
max_instances, copy: [
num_displays => num_heads, max_instances,
display_width => max_resolution_x, num_displays => num_heads,
display_height => max_resolution_y, display_width => max_resolution_x,
max_pixels, display_height => max_resolution_y,
frl_config, max_pixels,
} frl_config,
handle_bool_overrides! { ],
cuda_enabled, bool: [
ecc_supported, cuda_enabled,
} ecc_supported,
handle_copy_overrides! { ],
mig_instance_size, copy: [
} mig_instance_size,
handle_bool_overrides! { ],
multi_vgpu_supported, bool: [
} multi_vgpu_supported,
handle_copy_overrides! { ],
pci_id => vdev_id, copy: [
pci_device_id => pdev_id, pci_id => vdev_id,
framebuffer => fb_length, pci_device_id => pdev_id,
mappable_video_size, framebuffer => fb_length,
framebuffer_reservation => fb_reservation, mappable_video_size,
encoder_capacity, framebuffer_reservation => fb_reservation,
bar1_length, encoder_capacity,
} bar1_length,
handle_bool_overrides! { ],
frl_enabled => frl_enable, bool: [
} frl_enabled => frl_enable,
handle_str_overrides! { ],
adapter_name, str: [
} adapter_name,
handle_wide_str_overrides! { ],
adapter_name => adapter_name_unicode, wide_str: [
} adapter_name => adapter_name_unicode,
handle_str_overrides! { ],
short_gpu_name => short_gpu_name_string, str: [
license_type => licensed_product_name, short_gpu_name => short_gpu_name_string,
license_type => licensed_product_name,
],
} }
true true