happy-cracking 0.3.0

A fast, comprehensive CTF toolkit for cryptographic encoding/decoding, classic ciphers, hash operations, and analysis tools
Documentation
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);
        }
        // SAFETY: bits only contains b'0' and b'1' which are valid ASCII/UTF-8
        out.push_str(unsafe { std::str::from_utf8_unchecked(&bits) });
    }
    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| {
            // SAFETY: We only pushed b'0' and b'1' into cleaned_bytes, which are valid ASCII/UTF-8
            let s = unsafe { std::str::from_utf8_unchecked(chunk) };
            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")
}