Skip to main content

haem_frames/embedding/
vector.rs

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