Skip to content

Commit 52237f2

Browse files
cerdelensylvestre
authored andcommitted
unexpand: Refactor functions to use less parameters
1 parent ed05c6e commit 52237f2

File tree

1 file changed

+73
-101
lines changed

1 file changed

+73
-101
lines changed

src/uu/unexpand/src/unexpand.rs

Lines changed: 73 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -347,30 +347,30 @@ fn next_tabstop(tab_config: &TabConfig, col: usize) -> Option<usize> {
347347
fn write_tabs(
348348
output: &mut BufWriter<Stdout>,
349349
tab_config: &TabConfig,
350-
scol: &mut usize,
351-
col: usize,
352-
prevtab: bool,
353-
init: bool,
350+
print_state: &mut PrintState,
354351
amode: bool,
355352
) -> UResult<()> {
356353
// This conditional establishes the following:
357354
// We never turn a single space before a non-blank into
358355
// a tab, unless it's at the start of the line.
359-
let ai = init || amode;
360-
if (ai && !prevtab && col > *scol + 1) || (col > *scol && (init || ai && prevtab)) {
361-
while let Some(nts) = next_tabstop(tab_config, *scol) {
362-
if col < *scol + nts {
356+
let ai = print_state.leading || amode;
357+
if (ai && print_state.pctype != CharType::Tab && print_state.col > print_state.scol + 1)
358+
|| (print_state.col > print_state.scol
359+
&& (print_state.leading || ai && print_state.pctype == CharType::Tab))
360+
{
361+
while let Some(nts) = next_tabstop(tab_config, print_state.scol) {
362+
if print_state.col < print_state.scol + nts {
363363
break;
364364
}
365365

366366
output.write_all(b"\t")?;
367-
*scol += nts;
367+
print_state.scol += nts;
368368
}
369369
}
370370

371-
while col > *scol {
371+
while print_state.col > print_state.scol {
372372
output.write_all(b" ")?;
373-
*scol += 1;
373+
print_state.scol += 1;
374374
}
375375
Ok(())
376376
}
@@ -423,35 +423,49 @@ fn next_char_info(uflag: bool, buf: &[u8], byte: usize) -> (CharType, usize, usi
423423
(ctype, cwidth, nbytes)
424424
}
425425

426+
// This struct is used to store the current state of printing the input buf.
427+
// Things that need to be tracked are
428+
// - are we in the leading whitespaces before any other chars of this line
429+
// - what columns have already been printed
430+
// - how many whitespaces have been found but not yet been printed
431+
// - what type was the last Char
432+
struct PrintState {
433+
col: usize,
434+
scol: usize,
435+
leading: bool,
436+
pctype: CharType,
437+
}
438+
439+
impl PrintState {
440+
// reinitializes the PrintState struct to beginning of line values
441+
fn new_line(&mut self) {
442+
self.col = 0;
443+
self.scol = 0;
444+
self.leading = true;
445+
self.pctype = CharType::Other;
446+
}
447+
}
448+
426449
#[allow(clippy::cognitive_complexity)]
427-
#[allow(clippy::too_many_arguments)]
428450
fn unexpand_buf(
429451
buf: &[u8],
430452
output: &mut BufWriter<Stdout>,
431453
options: &Options,
432454
lastcol: usize,
433455
tab_config: &TabConfig,
434-
col: &mut usize,
435-
scol: &mut usize,
436-
leading: &mut bool,
437-
pctype: &mut CharType,
456+
print_state: &mut PrintState,
438457
) -> UResult<()> {
439458
// We can only fast forward if we don't need to calculate col/scol
440459
if let Some(b'\n') = buf.last() {
441460
// Fast path: if we're not converting all spaces (-a flag not set)
442461
// and the line doesn't start with spaces, just write it directly
443-
if !options.aflag && !buf.is_empty() && ((buf[0] != b' ' && buf[0] != b'\t') || !*leading) {
444-
write_tabs(
445-
output,
446-
tab_config,
447-
scol,
448-
*col,
449-
*pctype == CharType::Tab,
450-
*leading,
451-
options.aflag,
452-
)?;
453-
*scol = *col;
454-
*col += buf.len();
462+
if !options.aflag
463+
&& !buf.is_empty()
464+
&& ((buf[0] != b' ' && buf[0] != b'\t') || !print_state.leading)
465+
{
466+
write_tabs(output, tab_config, print_state, options.aflag)?;
467+
print_state.scol = print_state.col;
468+
print_state.col += buf.len();
455469
output.write_all(buf)?;
456470
return Ok(());
457471
}
@@ -462,40 +476,32 @@ fn unexpand_buf(
462476
// We can only fast forward if we don't need to calculate col/scol
463477
if let Some(b'\n') = buf.last() {
464478
// Fast path for leading spaces in non-UTF8 mode: count consecutive spaces/tabs at start
465-
if !options.uflag && !options.aflag && *leading {
479+
if !options.uflag && !options.aflag && print_state.leading {
466480
// In default mode (not -a), we only convert leading spaces
467481
// So we can batch process them and then copy the rest
468482
while byte < buf.len() {
469483
match buf[byte] {
470484
b' ' => {
471-
*col += 1;
485+
print_state.col += 1;
472486
byte += 1;
473487
}
474488
b'\t' => {
475-
*col += next_tabstop(tab_config, *col).unwrap_or(1);
489+
print_state.col += next_tabstop(tab_config, print_state.col).unwrap_or(1);
476490
byte += 1;
477-
*pctype = CharType::Tab;
491+
print_state.pctype = CharType::Tab;
478492
}
479493
_ => break,
480494
}
481495
}
482496

483497
// If we found spaces/tabs, write them as tabs
484498
if byte > 0 {
485-
write_tabs(
486-
output,
487-
tab_config,
488-
scol,
489-
*col,
490-
*pctype == CharType::Tab,
491-
true,
492-
options.aflag,
493-
)?;
499+
write_tabs(output, tab_config, print_state, options.aflag)?;
494500
}
495501

496502
// Write the rest of the line directly (no more tab conversion needed)
497503
if byte < buf.len() {
498-
*leading = false;
504+
print_state.leading = false;
499505
output.write_all(&buf[byte..])?;
500506
}
501507
return Ok(());
@@ -504,68 +510,52 @@ fn unexpand_buf(
504510

505511
while byte < buf.len() {
506512
// when we have a finite number of columns, never convert past the last column
507-
if lastcol > 0 && *col >= lastcol {
508-
write_tabs(
509-
output,
510-
tab_config,
511-
scol,
512-
*col,
513-
*pctype == CharType::Tab,
514-
*leading,
515-
true,
516-
)?;
513+
if lastcol > 0 && print_state.col >= lastcol {
514+
write_tabs(output, tab_config, print_state, true)?;
517515
output.write_all(&buf[byte..])?;
518-
*scol = *col;
516+
print_state.scol = print_state.col;
519517
break;
520518
}
521519

522520
// figure out how big the next char is, if it's UTF-8
523521
let (ctype, cwidth, nbytes) = next_char_info(options.uflag, buf, byte);
524522

525523
// now figure out how many columns this char takes up, and maybe print it
526-
let tabs_buffered = *leading || options.aflag;
524+
let tabs_buffered = print_state.leading || options.aflag;
527525
match ctype {
528526
CharType::Space | CharType::Tab => {
529527
// compute next col, but only write space or tab chars if not buffering
530-
*col += if ctype == CharType::Space {
528+
print_state.col += if ctype == CharType::Space {
531529
1
532530
} else {
533-
next_tabstop(tab_config, *col).unwrap_or(1)
531+
next_tabstop(tab_config, print_state.col).unwrap_or(1)
534532
};
535533

536534
if !tabs_buffered {
537535
output.write_all(&buf[byte..byte + nbytes])?;
538-
*scol = *col; // now printed up to this column
536+
print_state.scol = print_state.col; // now printed up to this column
539537
}
540538
}
541539
CharType::Other | CharType::Backspace => {
542540
// always
543-
write_tabs(
544-
output,
545-
tab_config,
546-
scol,
547-
*col,
548-
*pctype == CharType::Tab,
549-
*leading,
550-
options.aflag,
551-
)?;
552-
*leading = false; // no longer at the start of a line
553-
*col = if ctype == CharType::Other {
541+
write_tabs(output, tab_config, print_state, options.aflag)?;
542+
print_state.leading = false; // no longer at the start of a line
543+
print_state.col = if ctype == CharType::Other {
554544
// use computed width
555-
*col + cwidth
556-
} else if *col > 0 {
545+
print_state.col + cwidth
546+
} else if print_state.col > 0 {
557547
// Backspace case, but only if col > 0
558-
*col - 1
548+
print_state.col - 1
559549
} else {
560550
0
561551
};
562552
output.write_all(&buf[byte..byte + nbytes])?;
563-
*scol = *col; // we've now printed up to this column
553+
print_state.scol = print_state.col; // we've now printed up to this column
564554
}
565555
}
566556

567557
byte += nbytes; // move on to next char
568-
*pctype = ctype; // save the previous type
558+
print_state.pctype = ctype; // save the previous type
569559
}
570560

571561
Ok(())
@@ -580,47 +570,29 @@ fn unexpand_file(
580570
) -> UResult<()> {
581571
let mut buf = [0u8; 4096];
582572
let mut input = open(file)?;
583-
let mut col = 0;
584-
let mut scol = 0;
585-
let mut leading = true;
586-
let mut pctype = CharType::Other;
573+
let mut print_state = PrintState {
574+
col: 0,
575+
scol: 0,
576+
leading: true,
577+
pctype: CharType::Other,
578+
};
579+
587580
loop {
588581
match input.read(&mut buf) {
589582
Ok(0) => break,
590583
Ok(n) => {
591584
for line in buf[..n].split_inclusive(|b| *b == b'\n') {
592-
unexpand_buf(
593-
line,
594-
output,
595-
options,
596-
lastcol,
597-
tab_config,
598-
&mut col,
599-
&mut scol,
600-
&mut leading,
601-
&mut pctype,
602-
)?;
585+
unexpand_buf(line, output, options, lastcol, tab_config, &mut print_state)?;
603586
if let Some(b'\n') = line.last() {
604-
col = 0;
605-
scol = 0;
606-
leading = true;
607-
pctype = CharType::Other;
587+
print_state.new_line();
608588
}
609589
}
610590
}
611591
Err(e) => return Err(e.map_err_context(|| file.maybe_quote().to_string())),
612592
}
613593
}
614594
// write out anything remaining
615-
write_tabs(
616-
output,
617-
tab_config,
618-
&mut scol,
619-
col,
620-
pctype == CharType::Tab,
621-
leading,
622-
options.aflag,
623-
)?;
595+
write_tabs(output, tab_config, &mut print_state, options.aflag)?;
624596
Ok(())
625597
}
626598

0 commit comments

Comments
 (0)