pub struct HaitsmaIndex { /* private fields */ }Expand description
An in-memory inverted index over several Haitsma fingerprints.
Uses the same sub-fingerprint LUT strategy as
HaitsmaMatcher: build u32 → Vec<(ref_id, frame_pos)> per frame, probe each query frame to discover candidate
alignments, then verify the best per-reference BER.
§Performance
- Build:
O(Σ frames)— one LUT insertion per frame across all refs. - Query:
O(Q + C × overlap)whereQ= query frames probed,C= candidate refs with LUT hits,overlap= BER verification window. - Memory:
8 bytes × total_frames(LUT) plus4 bytes × total_frames(per-ref frame clone for BER verification).
§Scoring note
Only exact sub-fingerprint matches are probed (no bit-flip
neighbours). This is faster than the probe_bit_flips option on
HaitsmaMatcher but may miss weaker matches.
Prominence uses 0.5 / BER — not directly comparable to
HaitsmaMatcher’s median_BER / (BER + ε) formula.
Implementations§
Source§impl HaitsmaIndex
impl HaitsmaIndex
Sourcepub fn build(refs: &[HaitsmaFingerprint], max_postings_per_hash: u32) -> Self
pub fn build(refs: &[HaitsmaFingerprint], max_postings_per_hash: u32) -> Self
Build from a slice of Haitsma fingerprints.
max_postings_per_hash caps the size of each sub-fingerprint’s
posting list. Hashes that appear in more than this many positions
(silence / DC / highly repetitive content) are dropped entirely —
the same TF-IDF-style stop-hash pruning used by WangIndex and
PanakoIndex. This keeps query-time memory and work bounded on
pathological catalogs (audit B7 / A1).
§Panics
Panics if refs.len() exceeds u32::MAX (reference ids are stored
as u32).
Sourcepub fn insert(
&mut self,
fp: &HaitsmaFingerprint,
max_postings_per_hash: u32,
) -> usize
pub fn insert( &mut self, fp: &HaitsmaFingerprint, max_postings_per_hash: u32, ) -> usize
Append one fingerprint to the catalog, returning its stable ref_id.
Same stop-hash parity contract as WangIndex::insert: pruning the
touched LUT lists yields exactly the map state a full build +
retain would produce for the same append-only input sequence
(pinned by the insert_matches_build_parity test; parity is over
append-only histories — interleaved remove calls physically delete
postings, which build has no equivalent for).
The reference’s full frame vector is cloned for BER verification
(same as build); on vacated-slot reuse the old vector is replaced,
freeing the previous allocation. Ids of live references never change.
§Performance
O(F) amortised for F frames: one LUT lookup per frame plus
touched-list pruning. Pre-reserved map capacity and reused
touched-key scratch keep steady-state insertion free of auxiliary
allocation beyond posting pushes (and rare map growth).
§Panics
Panics if the catalog already holds u32::MAX live references (same
bound HaitsmaIndex::build enforces).
Sourcepub fn remove(&mut self, ref_id: usize) -> bool
pub fn remove(&mut self, ref_id: usize) -> bool
Erase all postings for ref_id. Returns false if the id is
out of range or already vacant (no-op).
Removal is physical: postings are deleted from every LUT list and
the reference’s frame vector is freed immediately (replaced with an
empty Vec), so the dominant HaitsmaIndex memory term — the
per-ref frame clone (4 bytes × frames) — is returned on erase.
query needs no liveness guard; throughput is bit-identical before
and after removes. Ids of live references never shift.
§Performance
O(P) where P = total LUT postings: one in-place retain scan per
list (memmove-compressed, no allocation) plus the frame-vec free.
Lists are pre-scanned read-only for the id first (see
WangIndex::remove): most lists skip the write pass.
Sourcepub fn remove_many(&mut self, ref_ids: &[usize]) -> usize
pub fn remove_many(&mut self, ref_ids: &[usize]) -> usize
Erase many references in a SINGLE LUT pass (plus their frame vectors).
Returns the number of ids actually erased. Same batching rationale as
WangIndex::remove_many: one O(P) pass instead of K. Vacated
ids are pushed ascending (smallest reused first — documented).
Sourcepub fn insert_many(
&mut self,
fps: &[HaitsmaFingerprint],
max_postings_per_hash: u32,
) -> Vec<usize>
pub fn insert_many( &mut self, fps: &[HaitsmaFingerprint], max_postings_per_hash: u32, ) -> Vec<usize>
Append many fingerprints, returning their stable ref_ids in order.
Reserves LUT capacity once for the whole batch; per-fingerprint
semantics identical to HaitsmaIndex::insert.
Sourcepub fn live_count(&self) -> usize
pub fn live_count(&self) -> usize
Live reference count (excludes ids erased by HaitsmaIndex::remove).
O(1) — maintained incrementally.
Sourcepub fn calibrated_score(&self, r: &MatchResult) -> f32
pub fn calibrated_score(&self, r: &MatchResult) -> f32
Estimated P(same recording | evidence) for a
HaitsmaIndex::query result: same haitsma-v1 map as
HaitsmaMatcher::calibrated_score.
Both paths report score = 1 − BER; their prominence formulas
differ (0.5/BER here vs median_BER/(ber+ε) in the matcher) —
see calibrated_haitsma for the bounded-error discussion. Raw
fields untouched.
Sourcepub fn estimated_bytes(&self) -> usize
pub fn estimated_bytes(&self) -> usize
Measured heap footprint in bytes: LUT posting lists (capacity × 8
bytes per (u32, u32) posting) + map slots + per-reference frame
vectors (capacity × 4 bytes — the dominant term) + fps / live /
vacant / touched storage. O(map size) — call rarely, not per
query. Documented approximation (±table overhead, allocator
rounding); for alerting and sharding, not billing.
Sourcepub fn query(
&self,
query: &HaitsmaFingerprint,
cfg: &HaitsmaMatchConfig,
) -> Option<(usize, MatchResult)>
pub fn query( &self, query: &HaitsmaFingerprint, cfg: &HaitsmaMatchConfig, ) -> Option<(usize, MatchResult)>
Query the index, returning the best-matching (ref_id, result).
For each query frame, probes the LUT to gather candidate
(ref_id, delta) pairs. Each candidate reference is then verified
with the exact-BER path at up to the 8 most-hit candidate offsets
(a repeated motif can concentrate hits at a wrong offset while the
true alignment has the better BER).
Only exact sub-fingerprint matches are probed (no bit-flips in
the index path — use match_ranked with explicit
probe_bit_flips when recall under codec distortion matters).
Sourcepub fn len(&self) -> usize
pub fn len(&self) -> usize
Return the number of unique sub-fingerprints (LUT keys) in the index.
Key-space, not catalog size — keys emptied by
HaitsmaIndex::remove are retained. Use
HaitsmaIndex::live_count for the reference count.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Return true if the index holds no LUT keys at all.
Stays false after every reference has been removed (removal
leaves empty keys behind); use
HaitsmaIndex::is_empty_catalog for the liveness test.
Sourcepub fn is_empty_catalog(&self) -> bool
pub fn is_empty_catalog(&self) -> bool
Return true if the catalog holds no live references.
Auto Trait Implementations§
impl Freeze for HaitsmaIndex
impl RefUnwindSafe for HaitsmaIndex
impl Send for HaitsmaIndex
impl Sync for HaitsmaIndex
impl Unpin for HaitsmaIndex
impl UnsafeUnpin for HaitsmaIndex
impl UnwindSafe for HaitsmaIndex
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more