use super::{Error, FailedCode};
const DIGITS: &[u8; 16] = b"0123456789abcdef";
pub fn encode_vector(vector: &[f32]) -> Result<String, Error> {
let capacity = vector
.len()
.checked_mul(8)
.ok_or_else(|| Error::new(FailedCode::EmbedLimits, "vector text length overflows"))?;
let mut text = String::with_capacity(capacity);
for member in vector {
if !member.is_finite() {
return Err(Error::new(
FailedCode::EmbedOutputInvalid,
"vector member is not finite",
));
}
for byte in member.to_le_bytes() {
text.push(char::from(DIGITS[usize::from(byte >> 4)]));
text.push(char::from(DIGITS[usize::from(byte & 15)]));
}
}
Ok(text)
}
fn nibble(byte: u8) -> Result<u8, Error> {
match byte {
b'0'..=b'9' => Ok(byte - b'0'),
b'a'..=b'f' => Ok(byte - b'a' + 10),
_ => Err(Error::new(
FailedCode::EmbedProtocol,
"vector text must contain only lowercase hexadecimal",
)),
}
}
pub fn decode_vector(text: &str) -> Result<Vec<f32>, Error> {
let (words, rest) = text.as_bytes().as_chunks::<8>();
if !rest.is_empty() {
return Err(Error::new(
FailedCode::EmbedProtocol,
"vector text length must be a multiple of eight",
));
}
words
.iter()
.map(|word| {
let mut bytes = [0; 4];
for (byte, pair) in bytes.iter_mut().zip(word.as_chunks::<2>().0) {
*byte = (nibble(pair[0])? << 4) | nibble(pair[1])?;
}
let member = f32::from_le_bytes(bytes);
if !member.is_finite() {
return Err(Error::new(
FailedCode::EmbedOutputInvalid,
"vector member is not finite",
));
}
Ok(member)
})
.collect()
}