1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-30 12:37:49 +00:00

dd: replace cascading if/else if with enum match

Replace a cascading `if/else if` chain in `conv_block_unblock_helper()`
with a match statement on a new enum, `ConversionMode`, that enumerates
the various modes in which `dd` can operate.
This commit is contained in:
Jeffrey Finkelstein 2022-03-06 23:28:05 -05:00 committed by Sylvestre Ledru
parent 6b8ab37ad9
commit bd626df70e
3 changed files with 95 additions and 81 deletions

View file

@ -6,7 +6,7 @@
// spell-checker:ignore datastructures rstat rposition cflags ctable // spell-checker:ignore datastructures rstat rposition cflags ctable
use crate::conversion_tables::ConversionTable; use crate::conversion_tables::ConversionTable;
use crate::datastructures::InternalError; use crate::datastructures::ConversionMode;
use crate::progress::ReadStat; use crate::progress::ReadStat;
use crate::Input; use crate::Input;
use std::io::Read; use std::io::Read;
@ -65,6 +65,47 @@ fn unblock(buf: &[u8], cbs: usize) -> Vec<u8> {
}) })
} }
/// Given the various command-line parameters, determine the conversion mode.
///
/// The `conv` command-line option can take many different values,
/// each of which may combine with others. For example, `conv=ascii`,
/// `conv=lcase`, `conv=sync`, and so on. The arguments to this
/// function represent the settings of those various command-line
/// parameters. This function translates those settings to a
/// [`ConversionMode`].
fn conversion_mode(
ctable: Option<&ConversionTable>,
block: Option<usize>,
unblock: Option<usize>,
non_ascii: bool,
is_sync: bool,
) -> Option<ConversionMode> {
match (ctable, block, unblock) {
(Some(ct), None, None) => Some(ConversionMode::ConvertOnly(ct)),
(Some(ct), Some(cbs), None) => {
if non_ascii {
Some(ConversionMode::ConvertThenBlock(ct, cbs, is_sync))
} else {
Some(ConversionMode::BlockThenConvert(ct, cbs, is_sync))
}
}
(Some(ct), None, Some(cbs)) => {
if non_ascii {
Some(ConversionMode::ConvertThenUnblock(ct, cbs))
} else {
Some(ConversionMode::UnblockThenConvert(ct, cbs))
}
}
(None, Some(cbs), None) => Some(ConversionMode::BlockOnly(cbs, is_sync)),
(None, None, Some(cbs)) => Some(ConversionMode::UnblockOnly(cbs)),
(None, None, None) => None,
// The remaining variants should never happen because the
// argument parsing above should result in an error before
// getting to this line of code.
_ => unreachable!(),
}
}
/// A helper for teasing out which options must be applied and in which order. /// A helper for teasing out which options must be applied and in which order.
/// Some user options, such as the presence of conversion tables, will determine whether the input is assumed to be ascii. The parser sets the Input::non_ascii flag accordingly. /// Some user options, such as the presence of conversion tables, will determine whether the input is assumed to be ascii. The parser sets the Input::non_ascii flag accordingly.
/// Examples: /// Examples:
@ -76,94 +117,58 @@ pub(crate) fn conv_block_unblock_helper<R: Read>(
mut buf: Vec<u8>, mut buf: Vec<u8>,
i: &mut Input<R>, i: &mut Input<R>,
rstat: &mut ReadStat, rstat: &mut ReadStat,
) -> Result<Vec<u8>, InternalError> { ) -> Vec<u8> {
// Local Predicate Fns ------------------------------------------------- // TODO This function has a mutable input `buf` but also returns a
fn should_block_then_conv<R: Read>(i: &Input<R>) -> bool { // completely new `Vec`; that seems fishy. Could we either make
!i.non_ascii && i.cflags.block.is_some() // the input immutable or make the function not return anything?
}
fn should_conv_then_block<R: Read>(i: &Input<R>) -> bool {
i.non_ascii && i.cflags.block.is_some()
}
fn should_unblock_then_conv<R: Read>(i: &Input<R>) -> bool {
!i.non_ascii && i.cflags.unblock.is_some()
}
fn should_conv_then_unblock<R: Read>(i: &Input<R>) -> bool {
i.non_ascii && i.cflags.unblock.is_some()
}
fn conv_only<R: Read>(i: &Input<R>) -> bool {
i.cflags.ctable.is_some() && i.cflags.block.is_none() && i.cflags.unblock.is_none()
}
// Local Helper Fns ----------------------------------------------------
fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) { fn apply_conversion(buf: &mut [u8], ct: &ConversionTable) {
for idx in 0..buf.len() { for idx in 0..buf.len() {
buf[idx] = ct[buf[idx] as usize]; buf[idx] = ct[buf[idx] as usize];
} }
} }
// --------------------------------------------------------------------
if conv_only(i) {
// no block/unblock
let ct = i.cflags.ctable.unwrap();
apply_conversion(&mut buf, ct);
Ok(buf) let mode = conversion_mode(
} else if should_block_then_conv(i) { i.cflags.ctable,
// ascii input so perform the block first i.cflags.block,
let cbs = i.cflags.block.unwrap(); i.cflags.unblock,
i.non_ascii,
let mut blocks = block(&buf, cbs, i.cflags.sync.is_some(), rstat); i.cflags.sync.is_some(),
)
if let Some(ct) = i.cflags.ctable { .unwrap();
match &mode {
ConversionMode::ConvertOnly(ct) => {
apply_conversion(&mut buf, ct);
buf
}
ConversionMode::BlockThenConvert(ct, cbs, sync) => {
let mut blocks = block(&buf, *cbs, *sync, rstat);
for buf in &mut blocks { for buf in &mut blocks {
apply_conversion(buf, ct); apply_conversion(buf, ct);
} }
blocks.into_iter().flatten().collect()
} }
ConversionMode::ConvertThenBlock(ct, cbs, sync) => {
let blocks = blocks.into_iter().flatten().collect();
Ok(blocks)
} else if should_conv_then_block(i) {
// Non-ascii so perform the conversion first
let cbs = i.cflags.block.unwrap();
if let Some(ct) = i.cflags.ctable {
apply_conversion(&mut buf, ct); apply_conversion(&mut buf, ct);
block(&buf, *cbs, *sync, rstat)
.into_iter()
.flatten()
.collect()
} }
ConversionMode::BlockOnly(cbs, sync) => block(&buf, *cbs, *sync, rstat)
let blocks = block(&buf, cbs, i.cflags.sync.is_some(), rstat)
.into_iter() .into_iter()
.flatten() .flatten()
.collect(); .collect(),
ConversionMode::UnblockThenConvert(ct, cbs) => {
Ok(blocks) let mut buf = unblock(&buf, *cbs);
} else if should_unblock_then_conv(i) {
// ascii input so perform the unblock first
let cbs = i.cflags.unblock.unwrap();
let mut buf = unblock(&buf, cbs);
if let Some(ct) = i.cflags.ctable {
apply_conversion(&mut buf, ct); apply_conversion(&mut buf, ct);
buf
} }
ConversionMode::ConvertThenUnblock(ct, cbs) => {
Ok(buf)
} else if should_conv_then_unblock(i) {
// Non-ascii input so perform the conversion first
let cbs = i.cflags.unblock.unwrap();
if let Some(ct) = i.cflags.ctable {
apply_conversion(&mut buf, ct); apply_conversion(&mut buf, ct);
unblock(&buf, *cbs)
} }
ConversionMode::UnblockOnly(cbs) => unblock(&buf, *cbs),
let buf = unblock(&buf, cbs);
Ok(buf)
} else {
// The following error should not happen, as it results from
// insufficient command line data. This case should be caught
// by the parser before making it this far.
// Producing this error is an alternative to risking an unwrap call
// on 'cbs' if the required data is not provided.
Err(InternalError::InvalidConvBlockUnblockCase)
} }
} }

View file

@ -14,6 +14,23 @@ use crate::conversion_tables::*;
type Cbs = usize; type Cbs = usize;
/// How to apply conversion, blocking, and/or unblocking.
///
/// Certain settings of the `conv` parameter to `dd` require a
/// combination of conversion, blocking, or unblocking, applied in a
/// certain order. The variants of this enumeration give the different
/// ways of combining those three operations.
#[derive(Debug, PartialEq)]
pub(crate) enum ConversionMode<'a> {
ConvertOnly(&'a ConversionTable),
BlockOnly(Cbs, bool),
UnblockOnly(Cbs),
BlockThenConvert(&'a ConversionTable, Cbs, bool),
ConvertThenBlock(&'a ConversionTable, Cbs, bool),
UnblockThenConvert(&'a ConversionTable, Cbs),
ConvertThenUnblock(&'a ConversionTable, Cbs),
}
/// Stores all Conv Flags that apply to the input /// Stores all Conv Flags that apply to the input
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]
pub struct IConvFlags { pub struct IConvFlags {
@ -91,19 +108,11 @@ pub enum CountType {
pub enum InternalError { pub enum InternalError {
WrongInputType, WrongInputType,
WrongOutputType, WrongOutputType,
InvalidConvBlockUnblockCase,
} }
impl std::fmt::Display for InternalError { impl std::fmt::Display for InternalError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { write!(f, "Internal dd error: Wrong Input/Output data type")
Self::WrongInputType | Self::WrongOutputType => {
write!(f, "Internal dd error: Wrong Input/Output data type")
}
Self::InvalidConvBlockUnblockCase => {
write!(f, "Invalid Conversion, Block, or Unblock data")
}
}
} }
} }

View file

@ -640,7 +640,7 @@ fn read_helper<R: Read>(i: &mut Input<R>, bsize: usize) -> std::io::Result<(Read
perform_swab(&mut buf); perform_swab(&mut buf);
} }
if is_conv(i) || is_block(i) || is_unblock(i) { if is_conv(i) || is_block(i) || is_unblock(i) {
let buf = conv_block_unblock_helper(buf, i, &mut rstat).unwrap(); let buf = conv_block_unblock_helper(buf, i, &mut rstat);
Ok((rstat, buf)) Ok((rstat, buf))
} else { } else {
Ok((rstat, buf)) Ok((rstat, buf))