1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
use anyhow::{Context, Result};
use clap::Subcommand;
#[derive(Subcommand)]
pub enum BinaryAction {
#[command(about = "Encode text to binary")]
Encode {
#[arg(help = "Input text")]
input: String,
},
#[command(about = "Decode binary to text")]
Decode {
#[arg(help = "Binary string (space or no space separated)")]
input: String,
},
}
pub fn run(action: BinaryAction) -> Result<()> {
match action {
BinaryAction::Encode { input } => {
println!("{}", encode(&input));
}
BinaryAction::Decode { input } => {
println!("{}", decode(&input)?);
}
}
Ok(())
}
pub fn encode(input: &str) -> String {
if input.is_empty() {
return String::new();
}
// Optimization: Pre-allocate the exact string capacity needed to avoid multiple allocations.
// Length is (8 bits + 1 space) per byte, minus the trailing space.
let mut out = String::with_capacity(input.len() * 9 - 1);
for (i, b) in input.bytes().enumerate() {
if i > 0 {
out.push(' ');
}
// Optimization: Manually compute bits instead of using format!("{:08b}", b)
// which introduces dynamic formatting overhead.
let mut bits = [0u8; 8];
for j in 0..8 {
bits[7 - j] = b'0' + ((b >> j) & 1);
}
// Security: use safe std::str::from_utf8 instead of unsafe from_utf8_unchecked.
// While the current logic only produces b'0' and b'1' (valid ASCII/UTF-8),
// using unsafe here is unnecessary and risks undefined behavior if a future
// refactor breaks this invariant.
out.push_str(std::str::from_utf8(&bits).expect("bits are ASCII digits"));
}
out
}
pub fn decode(input: &str) -> Result<String> {
// Optimization: Filter at the byte level directly into a pre-allocated Vec<u8>
// to bypass `char` conversion overhead and intermediate String allocation.
let mut cleaned_bytes = Vec::with_capacity(input.len());
for b in input.bytes() {
if b == b'0' || b == b'1' {
cleaned_bytes.push(b);
}
}
if !cleaned_bytes.len().is_multiple_of(8) {
anyhow::bail!("Binary string length must be a multiple of 8");
}
let bytes: Result<Vec<u8>, _> = cleaned_bytes
.chunks(8)
.map(|chunk| {
// Security: use safe std::str::from_utf8 instead of unsafe from_utf8_unchecked
// to prevent undefined behavior if the invariant is ever broken by refactoring.
let s = std::str::from_utf8(chunk).expect("cleaned_bytes are ASCII digits");
u8::from_str_radix(s, 2)
})
.collect();
let bytes = bytes.context("Failed to parse binary")?;
String::from_utf8(bytes).context("Decoded data is not valid UTF-8")
}