Skip to main content

fmf_contract/
volume.rs

1//! Volume label ⇄ 16-byte field — the one implementation of the contract's
2//! "UTF-8 drive label, zero-padded, not a GUID" rule (used by `FmfEvent`,
3//! `FmfVolumeStatus` and the pipe event body).
4
5/// Zero-padded UTF-8, capped at 15 bytes (the last byte stays NUL so the C
6/// side can treat the field as NUL-terminated).
7#[must_use]
8pub fn encode_label(label: &str) -> [u8; 16] {
9    let mut out = [0u8; 16];
10    let bytes = label.as_bytes();
11    let n = bytes.len().min(15);
12    out[..n].copy_from_slice(&bytes[..n]);
13    out
14}
15
16/// Reads up to the first NUL; non-UTF-8 content decodes as "" (defensive —
17/// well-formed peers never produce it).
18#[must_use]
19pub fn decode_label(bytes: &[u8; 16]) -> &str {
20    let len = bytes.iter().position(|&b| b == 0).unwrap_or(16);
21    core::str::from_utf8(&bytes[..len]).unwrap_or("")
22}
23
24#[cfg(test)]
25mod tests {
26    use super::*;
27
28    #[test]
29    fn roundtrip_caps_and_pads() {
30        assert_eq!(decode_label(&encode_label("C:")), "C:");
31        assert_eq!(encode_label("C:")[2..], [0u8; 14]);
32        assert_eq!(decode_label(&encode_label("")), "");
33        // 16+ bytes cap at 15, preserving NUL termination.
34        let long = encode_label("0123456789abcdefgh");
35        assert_eq!(long[15], 0);
36        assert_eq!(decode_label(&long), "0123456789abcde");
37    }
38}