ctoolbox/formats/eite/encoding/
pack32.rs

1use anyhow::{Result, anyhow, bail};
2
3use crate::formats::wtf8::{
4    decode_wtf8_single, encode_wtf8_single, is_unpackable_wtf8,
5};
6
7/// Pack a (signed) 32-bit value (interpreted as Unicode/WTF-8 codepoint domain)
8/// into a WTF-8 byte sequence representing a single logical codepoint.
9///
10/// Currently a thin wrapper around WTF-8 single-codepoint encoding.
11///
12/// (Original: pack32)
13pub fn pack32(value: u32) -> Result<Vec<u8>> {
14    encode_wtf8_single(value)
15}
16
17/// Unpack a single WTF-8 sequence into its codepoint (as u32).
18///
19/// Errors if the input is not a single valid WTF-8 codepoint sequence
20/// or if extra bytes remain after the first decoded codepoint.
21///
22/// (Original: unpack32)
23pub fn unpack32(bytes: &[u8]) -> Result<u32> {
24    let (cp, used) =
25        decode_wtf8_single(bytes).map_err(|e| anyhow!("unpack32: {e}"))?;
26    if used != bytes.len() {
27        bail!(
28            "unpack32: input contains extra bytes (decoded {} used {}, total {}, input {:?})",
29            cp,
30            used,
31            bytes.len(),
32            bytes
33        );
34    }
35    Ok(cp)
36}
37
38/// Test whether the given byte slice is a valid single WTF-8 codepoint
39/// sequence.
40///
41/// (Original: isPack32Char)
42pub fn is_pack32_char(bytes: &[u8]) -> bool {
43    is_unpackable_wtf8(bytes)
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[crate::ctb_test]
51    fn test_pack32_roundtrip_basic() {
52        // ASCII 'A'
53        let bytes = pack32(0x41).unwrap();
54        assert!(is_pack32_char(&bytes));
55        let cp = unpack32(&bytes).unwrap();
56        assert_eq!(cp, 0x41);
57
58        // BMP codepoint
59        let smile = 0x263A;
60        let bytes2 = pack32(smile).unwrap();
61        assert!(is_pack32_char(&bytes2));
62        let cp2 = unpack32(&bytes2).unwrap();
63        assert_eq!(cp2, smile);
64    }
65
66    #[crate::ctb_test]
67    fn test_pack32_surrogate_wtf8() {
68        // Surrogate (valid in WTF-8)
69        let hi = 0xD83D;
70        let bytes = pack32(hi).unwrap();
71        assert!(is_pack32_char(&bytes));
72        let round = unpack32(&bytes).unwrap();
73        assert_eq!(round, hi);
74    }
75
76    #[crate::ctb_test]
77    fn test_pack32_round_trip() {
78        for &val in &[0, 10, 100, 1000, 10_000] {
79            let packed = pack32(val).unwrap();
80            let unpacked = unpack32(&packed).expect("unpack32 failed");
81            assert_eq!(val, unpacked, "pack32/unpack32 mismatch for {}", val);
82        }
83    }
84}