pub mod index;
pub mod ivf;
pub mod quantization;
#[cfg(target_pointer_width = "64")]
pub(crate) const MAX_DENSE_ANN_DECODE_BYTES: usize = 4 * 1024 * 1024 * 1024;
#[cfg(not(target_pointer_width = "64"))]
pub(crate) const MAX_DENSE_ANN_DECODE_BYTES: usize = 512 * 1024 * 1024;
const ANN_DECODE_EXPANSION_FACTOR: usize = 8;
const ANN_DECODE_FIXED_HEADROOM: usize = 16 * 1024 * 1024;
fn decode_ann_with_limit<T: serde::de::DeserializeOwned, const LIMIT: usize>(
data: &[u8],
) -> std::io::Result<(T, usize)> {
bincode::serde::decode_from_slice(data, bincode::config::standard().with_limit::<LIMIT>())
.map_err(|error| std::io::Error::new(std::io::ErrorKind::InvalidData, error))
}
#[cfg(target_pointer_width = "64")]
fn decode_ann_with_relative_limit<T: serde::de::DeserializeOwned>(
data: &[u8],
budget: usize,
) -> std::io::Result<(T, usize)> {
const MIB: usize = 1024 * 1024;
const GIB: usize = 1024 * MIB;
if budget <= 32 * MIB {
decode_ann_with_limit::<T, { 32 * MIB }>(data)
} else if budget <= 128 * MIB {
decode_ann_with_limit::<T, { 128 * MIB }>(data)
} else if budget <= 512 * MIB {
decode_ann_with_limit::<T, { 512 * MIB }>(data)
} else if budget <= GIB {
decode_ann_with_limit::<T, GIB>(data)
} else if budget <= 2 * GIB {
decode_ann_with_limit::<T, { 2 * GIB }>(data)
} else {
decode_ann_with_limit::<T, { 4 * GIB }>(data)
}
}
#[cfg(not(target_pointer_width = "64"))]
fn decode_ann_with_relative_limit<T: serde::de::DeserializeOwned>(
data: &[u8],
budget: usize,
) -> std::io::Result<(T, usize)> {
const MIB: usize = 1024 * 1024;
if budget <= 32 * MIB {
decode_ann_with_limit::<T, { 32 * MIB }>(data)
} else if budget <= 128 * MIB {
decode_ann_with_limit::<T, { 128 * MIB }>(data)
} else {
decode_ann_with_limit::<T, { 512 * MIB }>(data)
}
}
pub(crate) fn decode_ann_bincode_exact<T: serde::de::DeserializeOwned>(
data: &[u8],
description: &str,
) -> std::io::Result<T> {
if data.len() > MAX_DENSE_ANN_DECODE_BYTES {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"{description} payload is {} bytes, exceeding the {}-byte decode limit",
data.len(),
MAX_DENSE_ANN_DECODE_BYTES
),
));
}
let budget = data
.len()
.checked_mul(ANN_DECODE_EXPANSION_FACTOR)
.and_then(|bytes| bytes.checked_add(ANN_DECODE_FIXED_HEADROOM))
.unwrap_or(MAX_DENSE_ANN_DECODE_BYTES)
.min(MAX_DENSE_ANN_DECODE_BYTES);
let (value, consumed) = decode_ann_with_relative_limit(data, budget)?;
if consumed != data.len() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!(
"{description} payload contains {} trailing bytes",
data.len() - consumed
),
));
}
Ok(value)
}
pub use ivf::{
ClusterData, ClusterStorage, CoarseCentroids, CoarseConfig, MultiAssignment, QuantizedCode,
SoarConfig,
};
pub use quantization::{
DistanceTable, PQCodebook, PQConfig, PQVector, QuantizedQuery, QuantizedVector, Quantizer,
RaBitQCodebook, RaBitQConfig,
};
pub use index::{
BinaryIvfConfig, BinaryIvfIndex, IVFPQConfig, IVFPQIndex, IVFRaBitQConfig, IVFRaBitQIndex,
RaBitQIndex,
};
#[cfg(test)]
mod decode_tests {
use super::decode_ann_bincode_exact;
#[test]
fn ann_bincode_decode_is_exact_and_rejects_large_claims_from_tiny_payloads() {
let encoded =
bincode::serde::encode_to_vec(vec![1u8, 2, 3], bincode::config::standard()).unwrap();
assert_eq!(
decode_ann_bincode_exact::<Vec<u8>>(&encoded, "test").unwrap(),
[1, 2, 3]
);
let mut trailing = encoded;
trailing.push(0);
assert!(decode_ann_bincode_exact::<Vec<u8>>(&trailing, "test").is_err());
let oversized_claim =
bincode::serde::encode_to_vec(64usize * 1024 * 1024, bincode::config::standard())
.unwrap();
assert!(decode_ann_bincode_exact::<Vec<u8>>(&oversized_claim, "test").is_err());
}
}