Skip to main content

haem_frames/embedding/
vector.rs

1use super::{Error, FailedCode};
2
3const DIGITS: &[u8; 16] = b"0123456789abcdef";
4
5/// Encode finite float members as lowercase little-endian hexadecimal.
6///
7/// # Errors
8/// Returns `embed_limits` if the text size overflows, or
9/// `embed_output_invalid` if a member is not finite.
10pub fn encode_vector(vector: &[f32]) -> Result<String, Error> {
11    let capacity = vector
12        .len()
13        .checked_mul(8)
14        .ok_or_else(|| Error::new(FailedCode::EmbedLimits, "vector text length overflows"))?;
15    let mut text = String::with_capacity(capacity);
16    for member in vector {
17        if !member.is_finite() {
18            return Err(Error::new(
19                FailedCode::EmbedOutputInvalid,
20                "vector member is not finite",
21            ));
22        }
23        for byte in member.to_le_bytes() {
24            text.push(char::from(DIGITS[usize::from(byte >> 4)]));
25            text.push(char::from(DIGITS[usize::from(byte & 15)]));
26        }
27    }
28    Ok(text)
29}
30
31fn nibble(byte: u8) -> Result<u8, Error> {
32    match byte {
33        b'0'..=b'9' => Ok(byte - b'0'),
34        b'a'..=b'f' => Ok(byte - b'a' + 10),
35        _ => Err(Error::new(
36            FailedCode::EmbedProtocol,
37            "vector text must contain only lowercase hexadecimal",
38        )),
39    }
40}
41
42/// Decode lowercase little-endian hexadecimal into finite float members.
43///
44/// # Errors
45/// Returns `embed_protocol` for malformed text, or `embed_output_invalid`
46/// if a decoded member is not finite.
47pub fn decode_vector(text: &str) -> Result<Vec<f32>, Error> {
48    let (words, rest) = text.as_bytes().as_chunks::<8>();
49    if !rest.is_empty() {
50        return Err(Error::new(
51            FailedCode::EmbedProtocol,
52            "vector text length must be a multiple of eight",
53        ));
54    }
55    words
56        .iter()
57        .map(|word| {
58            let mut bytes = [0; 4];
59            for (byte, pair) in bytes.iter_mut().zip(word.as_chunks::<2>().0) {
60                *byte = (nibble(pair[0])? << 4) | nibble(pair[1])?;
61            }
62            let member = f32::from_le_bytes(bytes);
63            if !member.is_finite() {
64                return Err(Error::new(
65                    FailedCode::EmbedOutputInvalid,
66                    "vector member is not finite",
67                ));
68            }
69            Ok(member)
70        })
71        .collect()
72}