kernal-api 0.1.14

Async OS HAL, profiling, symbolization, and allocator instrumentation
//! Bounded text comparison without application-specific normalization or ranking.

/// Maximum UTF-8 bytes in either argument to [`name_similarity`].
pub const MAX_SIMILARITY_INPUT_BYTES: usize = 4096;
/// Maximum product of the two Unicode scalar counts.
pub const MAX_SIMILARITY_WORK: usize = 1_048_576;

/// Resource rejection before invoking the private scoring implementation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SimilarityError {
    InputTooLarge,
    WorkLimitExceeded,
}

impl std::fmt::Display for SimilarityError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(match self {
            Self::InputTooLarge => "similarity input exceeds the byte limit",
            Self::WorkLimitExceeded => "similarity comparison exceeds the work limit",
        })
    }
}

impl std::error::Error for SimilarityError {}

/// Score two names using Jaro-Winkler similarity, from 0 (unrelated) to 1 (equal).
///
/// Operates on Unicode scalar values, not bytes or grapheme clusters. It performs
/// no case folding or Unicode normalization. Both empty inputs score 1; exactly
/// one empty input scores 0. Callers own candidate selection and tie policy.
///
/// Both byte limits and the scalar-count product limit are checked before the
/// backend allocates matching flags or scores, including for equal inputs.
/// This bounds quadratic comparison work and linear scratch storage, not elapsed
/// time. This synchronous CPU operation has no cancellation or runtime dependency.
pub fn name_similarity(left: &str, right: &str) -> Result<f64, SimilarityError> {
    if left.len() > MAX_SIMILARITY_INPUT_BYTES || right.len() > MAX_SIMILARITY_INPUT_BYTES {
        return Err(SimilarityError::InputTooLarge);
    }
    // Byte limits also bound scalar counts, so this product cannot overflow.
    if left.chars().count() * right.chars().count() > MAX_SIMILARITY_WORK {
        return Err(SimilarityError::WorkLimitExceeded);
    }
    Ok(strsim::jaro_winkler(left, right))
}