use std::io::Cursor;
use std::path::Path;
use crate::common::mmap::{Advice, AdviceSetting};
use crate::common::types::PointOffsetType;
use crate::common::universal_io::{OpenOptions, Populate, UniversalReadFs};
use super::format::{GraphLinksFormat, GraphLinksFormatParam};
use super::serializer::serialize_graph_links;
use super::storage::GraphLinksEnum;
use super::view::{CompressionInfo, GraphLinksView, LinksIterator, LinksWithVectorsIterator};
use crate::segment::common::operation_error::OperationResult;
use crate::segment::index::hnsw_index::HnswM;
self_cell::self_cell! {
pub struct GraphLinks {
owner: GraphLinksEnum,
#[covariant]
dependent: GraphLinksView,
}
impl {Debug}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GraphLinksResidency {
Cold,
Cached,
Pinned,
}
impl GraphLinks {
pub(in crate::segment::index::hnsw_index) fn open_options(
residency: GraphLinksResidency,
) -> OpenOptions {
let populate = match residency {
GraphLinksResidency::Cold | GraphLinksResidency::Pinned => Populate::No,
GraphLinksResidency::Cached => Populate::Blocking,
};
OpenOptions {
writeable: false,
need_sequential: false,
populate,
advice: AdviceSetting::Advice(Advice::Random),
}
}
pub fn load_universal<Fs>(
fs: &Fs,
path: &Path,
format: GraphLinksFormat,
residency: GraphLinksResidency,
) -> OperationResult<Self>
where
Fs: UniversalReadFs,
Fs::File: 'static,
{
let storage = fs.open(path, Self::open_options(residency), Default::default())?;
let owner = match residency {
GraphLinksResidency::Cold | GraphLinksResidency::Cached => {
GraphLinksEnum::from_storage(storage)?
}
GraphLinksResidency::Pinned => GraphLinksEnum::pinned_from_storage(storage)?,
};
Self::try_new(owner, |x| GraphLinksView::load(x.as_bytes()?, format))
}
pub fn new_from_edges(
edges: Vec<Vec<Vec<PointOffsetType>>>,
format_param: GraphLinksFormatParam<'_>,
hnsw_m: HnswM,
) -> OperationResult<Self> {
let mut cursor = Cursor::new(Vec::<u8>::new());
serialize_graph_links(edges, format_param, hnsw_m, &mut cursor)?;
let mut bytes = cursor.into_inner();
bytes.shrink_to_fit();
Self::try_new(GraphLinksEnum::Ram(bytes), |x| {
GraphLinksView::load(x.as_bytes()?, format_param.as_format())
})
}
pub(super) fn view(&self) -> &GraphLinksView<'_> {
self.borrow_dependent()
}
pub fn as_bytes(&self) -> OperationResult<&[u8]> {
self.borrow_owner().as_bytes()
}
pub fn heap_size_bytes(&self) -> usize {
self.borrow_owner().heap_size_bytes()
}
pub fn format(&self) -> GraphLinksFormat {
match self.view().compression {
CompressionInfo::Uncompressed { .. } => GraphLinksFormat::Plain,
CompressionInfo::Compressed { .. } => GraphLinksFormat::Compressed,
CompressionInfo::CompressedWithVectors { .. } => {
GraphLinksFormat::CompressedWithVectors
}
}
}
pub fn num_points(&self) -> usize {
self.view().reindex.len()
}
pub fn for_each_link(
&self,
point_id: PointOffsetType,
level: usize,
f: impl FnMut(PointOffsetType),
) {
self.links(point_id, level).for_each(f);
}
#[inline]
pub fn links(&self, point_id: PointOffsetType, level: usize) -> LinksIterator<'_> {
self.view().links(point_id, level)
}
#[inline]
pub fn links_empty(&self, point_id: PointOffsetType, level: usize) -> bool {
self.view().links_empty(point_id, level)
}
#[inline]
pub fn links_with_vectors(
&self,
point_id: PointOffsetType,
level: usize,
) -> (&[u8], LinksWithVectorsIterator<'_>) {
let (base_vector, links, vectors) = self.view().links_with_vectors(point_id, level);
(base_vector, links.zip(vectors))
}
pub fn point_level(&self, point_id: PointOffsetType) -> usize {
self.view().point_level(point_id)
}
pub fn to_edges(&self) -> Vec<Vec<Vec<PointOffsetType>>> {
self.to_edges_impl(|point_id, level| self.links(point_id, level).collect())
}
pub fn to_edges_impl<Container>(
&self,
mut f: impl FnMut(PointOffsetType, usize) -> Container,
) -> Vec<Vec<Container>> {
let mut edges = Vec::with_capacity(self.num_points());
for point_id in 0..self.num_points() {
let num_levels = self.point_level(point_id as PointOffsetType) + 1;
let mut levels = Vec::with_capacity(num_levels);
levels.extend((0..num_levels).map(|level| f(point_id as PointOffsetType, level)));
edges.push(levels);
}
edges
}
pub fn populate(&self) -> OperationResult<()> {
self.borrow_owner().populate()
}
pub fn clear_cache(&self) -> OperationResult<()> {
self.borrow_owner().clear_cache()
}
}