use std::fmt;
use std::path::PathBuf;
use crate::common::bitvec::{BitSlice, BitSliceExt as _};
use crate::common::types::{DeferredBehavior, PointOffsetType};
use rand::rngs::StdRng;
use rand::{RngExt, SeedableRng};
use super::point_mappings_ref::PointMappingsRefEnum;
use crate::segment::common::Flusher;
use crate::segment::common::operation_error::OperationResult;
use crate::segment::types::{PointIdType, SeqNumberType};
const SEED: u64 = 0b1011000011011110001110010101001010001011001101001010010001111010;
pub const DELETED_POINT_VERSION: SeqNumberType = 0;
pub trait IdTracker: IdTrackerRead + fmt::Debug {
fn set_internal_version(
&mut self,
internal_id: PointOffsetType,
version: SeqNumberType,
) -> OperationResult<()>;
fn set_link(
&mut self,
external_id: PointIdType,
internal_id: PointOffsetType,
) -> OperationResult<()>;
fn drop(&mut self, external_id: PointIdType) -> OperationResult<()>;
fn drop_internal(&mut self, internal_id: PointOffsetType) -> OperationResult<()>;
fn mapping_flusher(&self) -> Flusher;
fn versions_flusher(&self) -> Flusher;
fn fix_inconsistencies(&mut self) -> OperationResult<Vec<PointOffsetType>> {
let mut to_remove = Vec::new();
let mut to_return = Vec::new();
for (internal_id, version) in self.iter_internal_versions() {
if version != DELETED_POINT_VERSION && self.external_id(internal_id).is_none() {
to_return.push(internal_id);
}
}
for internal_id in self.point_mappings().iter_internal() {
if self.internal_version(internal_id).is_none() {
if let Some(external_id) = self.external_id(internal_id) {
to_remove.push(external_id);
to_return.push(internal_id);
} else {
debug_assert!(false, "internal id {internal_id} has no external id");
}
}
}
for external_id in to_remove {
self.drop(external_id)?;
#[cfg(debug_assertions)]
log::debug!("dropped mapping for point {external_id} without version");
}
Ok(to_return)
}
fn files(&self) -> Vec<PathBuf>;
fn immutable_files(&self) -> Vec<PathBuf> {
Vec::new()
}
fn clear_cache(&self) -> OperationResult<()> {
Ok(())
}
fn clear_cache_if_on_disk(&self) -> OperationResult<()> {
self.clear_cache()
}
}
pub trait IdTrackerRead {
fn point_mappings(&self) -> PointMappingsRefEnum<'_>;
fn internal_version(&self, internal_id: PointOffsetType) -> Option<SeqNumberType>;
fn internal_id(&self, external_id: PointIdType) -> Option<PointOffsetType>;
fn external_id(&self, internal_id: PointOffsetType) -> Option<PointIdType>;
fn total_point_count(&self) -> usize;
fn available_point_count(&self) -> usize {
self.total_point_count() - self.deleted_point_count()
}
fn deleted_point_count(&self) -> usize;
fn deleted_point_bitslice(&self) -> &BitSlice;
fn is_deleted_point(&self, internal_id: PointOffsetType) -> bool;
fn name(&self) -> &'static str;
fn sample_ids<'a>(
&'a self,
deleted_vector_bitslice: Option<&'a BitSlice>,
) -> Box<dyn Iterator<Item = PointOffsetType> + 'a> {
let mut rng = StdRng::seed_from_u64(SEED);
let total = self.total_point_count() as PointOffsetType;
Box::new(
(0..total)
.map(move |_| rng.random_range(0..total))
.filter(move |&x| {
!deleted_vector_bitslice
.and_then(|d| d.get_bit(x as usize))
.unwrap_or(false)
&& !self.is_deleted_point(x)
}),
)
}
fn iter_internal_versions(
&self,
) -> Box<dyn Iterator<Item = (PointOffsetType, SeqNumberType)> + '_>;
fn deferred_internal_id(&self) -> Option<PointOffsetType> {
None
}
fn deferred_deleted_count(&self) -> usize {
0
}
fn resolve_external_ids(
&self,
point_ids: &[PointIdType],
deferred_behavior: DeferredBehavior,
) -> (Vec<PointIdType>, Vec<PointOffsetType>) {
let deferred_cutoff = deferred_behavior.apply(self.deferred_internal_id());
let mut ids = Vec::with_capacity(point_ids.len());
let mut offsets = Vec::with_capacity(point_ids.len());
for &point_id in point_ids {
let Some(internal_id) = self.internal_id(point_id) else {
continue;
};
if deferred_cutoff.is_some_and(|cutoff| internal_id >= cutoff) {
continue;
}
ids.push(point_id);
offsets.push(internal_id);
}
(ids, offsets)
}
}