ztmux 3.7.0

A Rust port of tmux — the full terminal multiplexer, server and client
Documentation
use core::mem::MaybeUninit;

// https://www.rfc-editor.org/rfc/rfc4648

/// C `vendor/tmux/compat/base64.c:126`: `int b64_ntop(unsigned char const *src, size_t srclength, char *target, size_t targsize)`
pub unsafe fn b64_ntop(src: *const u8, srclength: usize, target: *mut u8, targsize: usize) -> i32 {
    let src = unsafe { std::slice::from_raw_parts(src, srclength) };
    let dst = unsafe { std::slice::from_raw_parts_mut(target.cast::<MaybeUninit<u8>>(), targsize) };

    match ntop(src, dst) {
        Ok(out) => (out.len() - 1) as i32,
        Err(()) => -1,
    }
}

/// skips all whitespace anywhere.
/// converts characters, four at a time, starting at (or after)
/// src from base - 64 numbers into three 8 bit bytes in the target area.
/// it returns the number of data bytes stored at the target, or -1 on error.
/// C `vendor/tmux/compat/base64.c:187`: `int b64_pton(char const *src, unsigned char *target, size_t targsize)`
pub unsafe fn b64_pton(src: *const u8, target: *mut u8, targsize: usize) -> i32 {
    let srclength: usize = unsafe { crate::libc::strlen(src) };
    let src = unsafe { std::slice::from_raw_parts(src.cast::<u8>(), srclength) };
    let dst = unsafe { std::slice::from_raw_parts_mut(target.cast::<MaybeUninit<u8>>(), targsize) };

    match pton(src, dst) {
        Ok(out) => out.len() as i32,
        Err(()) => -1,
    }
}

/// minimum ascii value used in encoded format
const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const REVERSE: [u8; u8::MAX as usize] = const {
    let mut tmp = [u8::MAX; u8::MAX as usize];

    let mut i: u8 = 0;
    while i < ALPHABET.len() as u8 {
        tmp[ALPHABET[i as usize] as usize] = i;
        i += 1;
    }

    tmp
};

/// decode
fn pton<'out>(src: &'_ [u8], dst: &'out mut [MaybeUninit<u8>]) -> Result<&'out mut [u8], ()> {
    // dst must be at least 3/4 of src, and room for NUL byte
    if src.len().div_ceil(4) * 3 + 1 > dst.len() {
        return Err(());
    }

    let mut i = 0;
    let mut it = src.iter().copied().filter(|b| !b.is_ascii_whitespace());

    while let Some(ch) = it.next() {
        let chunk: [u8; 4] = [
            ch,
            it.next().ok_or(())?,
            it.next().ok_or(())?,
            it.next().ok_or(())?, // TODO consider special handling for missing =
        ];

        for g in chunk {
            if !matches!(g, b'A'..=b'Z' | b'a'..=b'z' | b'+' | b'/') {
                return Err(());
            }
        }

        let a = REVERSE[chunk[0] as usize];
        let b = REVERSE[chunk[1] as usize];
        let c = REVERSE[chunk[2] as usize];
        let d = REVERSE[chunk[3] as usize];

        //        a                 b                 c                 d
        // X X 0 0 0 0 0 0 | X X 0 0 0 0 0 0 | X X 0 0 0 0 0 0 | X X 0 0 0 0 0 0
        //
        // ( a << 2  ) ( b >> 4 )    (b<<4) (    c >> 2    )     (c<<4)(      d       )
        // 0  0  0  0  0  0  0  0  |  0  0  0  0  0  0  0  0  |  0  0  0  0  0  0  0  0
        //
        dst[i] = MaybeUninit::new(a << 2 | b >> 4);
        dst[i + 1] = MaybeUninit::new(b << 4 | c >> 2);
        dst[i + 2] = MaybeUninit::new(c << 6 | d);
        i += 3;
    }

    dst[i] = MaybeUninit::new(0);
    Ok(unsafe { std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<u8>(), i) })
}

/// encode
fn ntop<'out>(src: &'_ [u8], dst: &'out mut [MaybeUninit<u8>]) -> Result<&'out mut [u8], ()> {
    if dst.len() < src.len().div_ceil(3) * 4 + 1 {
        return Err(());
    }

    let mut i = 0;
    let mut it = src.chunks_exact(3);

    macro_rules! enc {
        ($e:expr) => {
            MaybeUninit::new(ALPHABET[($e & 0b00111111) as usize])
        }
    }

    for chunk in &mut it {
        dst[i] = enc!(chunk[0] >> 2);
        dst[i + 1] = enc!(chunk[0] << 4 | chunk[1] >> 4);
        dst[i + 2] = enc!(chunk[1] << 2 | chunk[2] >> 6);
        dst[i + 3] = enc!(chunk[2]);
        i += 4;
    }

    let chunk = it.remainder();
    match chunk.len() {
        0 => (),
        1 => {
            dst[i] = enc!(chunk[0] >> 2);
            dst[i + 1] = enc!(chunk[0] << 4);
            dst[i + 2] = MaybeUninit::new(b'=');
            dst[i + 3] = MaybeUninit::new(b'=');
            i += 4;
        }
        2 => {
            dst[i] = enc!(chunk[0] >> 2);
            dst[i + 1] = enc!(chunk[0] << 4 | chunk[1] >> 4);
            dst[i + 2] = enc!(chunk[1] << 2);
            dst[i + 3] = MaybeUninit::new(b'=');
            i += 4;
        }
        _ => unreachable!(),
    }

    dst[i] = MaybeUninit::new(b'\0');
    Ok(unsafe { std::slice::from_raw_parts_mut(dst.as_mut_ptr().cast::<u8>(), i) })
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_b64_pton_valid() {
        let input = crate::c!("TWFu");
        let mut output = [0u8; 4];
        let expected = [b'M', b'a', b'n', 0];

        unsafe {
            let result = b64_pton(input, output.as_mut_ptr(), output.len());
            assert_eq!(&output, &expected);
            assert_eq!(result, 3);
        }
    }

    #[test]
    fn test_b64_pton_invalid() {
        let input = crate::c!("****");
        let mut output = [0u8; 3];

        unsafe {
            let result = b64_pton(input, output.as_mut_ptr(), output.len());
            assert_eq!(result, -1);
        }
    }

    #[test]
    fn test_b64_pton_partial() {
        let input = crate::c!("TWE=");
        let mut output = [0u8; 2];
        // TODO currently not supporting missing =, but we could

        unsafe {
            let result = b64_pton(input, output.as_mut_ptr(), output.len());
            assert_eq!(result, -1);
        }
    }

    // Encode a whole 3-byte quantum ("Man" -> "TWFu"). The RFC 4648 vector.
    // NOTE: BSD b64_ntop (base64.c:177) returns `datalength` = 4, the number of
    // encoded characters. This port returns `out.len() - 1` (b64.rs:11), i.e.
    // one less than the encoded length. Asserting ACTUAL behavior for the return
    // value while pinning the exact encoded bytes + NUL terminator.
    #[test]
    fn test_b64_ntop_full_quantum() {
        let mut out = [0xffu8; 8];
        let ret = unsafe { b64_ntop(b"Man".as_ptr(), 3, out.as_mut_ptr(), out.len()) };
        assert_eq!(&out[..5], b"TWFu\0");
        assert_eq!(ret, 3);
    }

    // One leftover byte -> two chars + "==" padding.
    #[test]
    fn test_b64_ntop_one_pad() {
        let mut out = [0xffu8; 8];
        let ret = unsafe { b64_ntop(b"M".as_ptr(), 1, out.as_mut_ptr(), out.len()) };
        assert_eq!(&out[..5], b"TQ==\0");
        assert_eq!(ret, 3);
    }

    // Two leftover bytes -> three chars + "=" padding.
    #[test]
    fn test_b64_ntop_two_pad() {
        let mut out = [0xffu8; 8];
        let ret = unsafe { b64_ntop(b"Ma".as_ptr(), 2, out.as_mut_ptr(), out.len()) };
        assert_eq!(&out[..5], b"TWE=\0");
        assert_eq!(ret, 3);
    }

    // Multi-quantum, no padding: "foobar" (6 bytes) -> "Zm9vYmFy".
    #[test]
    fn test_b64_ntop_multi_quantum() {
        let mut out = [0xffu8; 16];
        let ret = unsafe { b64_ntop(b"foobar".as_ptr(), 6, out.as_mut_ptr(), out.len()) };
        assert_eq!(&out[..9], b"Zm9vYmFy\0");
        assert_eq!(ret, 7);
    }

    // Empty input: C b64_ntop writes a lone NUL and returns datalength 0
    // (base64.c:176-177). DISCREPANCY: this port computes `out.len() - 1` on a
    // zero-length encoded slice (b64.rs:11), which underflows. With debug
    // overflow checks (the default `cargo test` profile) that panics. Pinning
    // the ACTUAL behavior so any change to the off-by-one is noticed.
    #[test]
    #[should_panic(expected = "subtract with overflow")]
    fn test_b64_ntop_empty_underflows() {
        let mut out = [0xffu8; 4];
        let _ = unsafe { b64_ntop(b"".as_ptr(), 0, out.as_mut_ptr(), out.len()) };
    }

    // Target too small: "Man" needs 4 chars + NUL = 5 bytes; give 4 -> error.
    #[test]
    fn test_b64_ntop_target_too_small() {
        let mut out = [0xffu8; 4];
        let ret = unsafe { b64_ntop(b"Man".as_ptr(), 3, out.as_mut_ptr(), 4) };
        assert_eq!(ret, -1);
    }

    // High-bit bytes exercise the full 6-bit alphabet including '+' and '/'.
    // 0xFB 0xFF 0xBF -> "+/+/".
    #[test]
    fn test_b64_ntop_plus_slash() {
        let mut out = [0xffu8; 8];
        let ret = unsafe { b64_ntop([0xFB, 0xFF, 0xBFu8].as_ptr(), 3, out.as_mut_ptr(), out.len()) };
        assert_eq!(&out[..5], b"+/+/\0");
        assert_eq!(ret, 3);
    }

    // Round trip on a padding-free (multiple-of-3) buffer. "Man" encodes to
    // "TWFu" (no '=' and, importantly, no digit — see the digit-rejection bug
    // below), so it can be decoded back.
    #[test]
    fn test_b64_roundtrip_no_padding() {
        let src = b"Man"; // 3 bytes -> "TWFu", no padding, no digit chars
        let mut enc = [0u8; 16];
        unsafe {
            b64_ntop(src.as_ptr(), src.len(), enc.as_mut_ptr(), enc.len());
        }
        assert_eq!(&enc[..5], b"TWFu\0");
        // enc is NUL-terminated; feed it back to b64_pton.
        let mut dec = [0u8; 16];
        let n = unsafe { b64_pton(enc.as_ptr(), dec.as_mut_ptr(), dec.len()) };
        assert_eq!(n, 3);
        assert_eq!(&dec[..3], src);
    }

    // DISCREPANCY vs C: BSD b64_pton decodes the full alphabet including the
    // digits (values 52-61, base64.c:203 uses strchr over the whole table).
    // This port's per-character validity gate (b64.rs:65) only allows
    // A-Z | a-z | '+' | '/' — it omits b'0'..=b'9'. So a perfectly valid
    // encoding that contains a digit is rejected. "Zm9vYmFy" is base64 for
    // "foobar" and contains '9'; the port returns -1 instead of decoding it.
    #[test]
    fn test_b64_pton_rejects_digits() {
        let mut out = [0u8; 16];
        let n = unsafe { b64_pton(crate::c!("Zm9vYmFy"), out.as_mut_ptr(), out.len()) };
        assert_eq!(n, -1);
    }

    // Whitespace is skipped anywhere in the input (base64.c:197). "TW Fu"
    // decodes the same as "TWFu".
    #[test]
    fn test_b64_pton_skips_whitespace() {
        let input = crate::c!("TW Fu");
        let mut out = [0u8; 8];
        let n = unsafe { b64_pton(input, out.as_mut_ptr(), out.len()) };
        assert_eq!(n, 3);
        assert_eq!(&out[..3], b"Man");
    }

    // A length that is not a multiple of 4 after filtering has an incomplete
    // final group -> error.
    #[test]
    fn test_b64_pton_incomplete_group() {
        let input = crate::c!("TWF");
        let mut out = [0u8; 8];
        let n = unsafe { b64_pton(input, out.as_mut_ptr(), out.len()) };
        assert_eq!(n, -1);
    }

    // DISCREPANCY vs C: BSD b64_pton (base64.c:200) accepts '=' padding and
    // decodes "TQ==" to one byte. This port has no '=' handling, so any padded
    // input is rejected. Asserting ACTUAL behavior.
    #[test]
    fn test_b64_pton_padding_rejected() {
        let mut out = [0u8; 8];
        let n = unsafe { b64_pton(crate::c!("TQ=="), out.as_mut_ptr(), out.len()) };
        assert_eq!(n, -1);
    }

    // Output buffer too small for the decoded length: "TWFuTWFu" -> 6 bytes,
    // needs div_ceil(8,4)*3+1 = 7 bytes; give 4 -> error.
    #[test]
    fn test_b64_pton_output_too_small() {
        let mut out = [0u8; 4];
        let n = unsafe { b64_pton(crate::c!("TWFuTWFu"), out.as_mut_ptr(), out.len()) };
        assert_eq!(n, -1);
    }
}