src/render/ansi/hash/source_color.rs

//! Deterministic mapping from origin label to palette index.
//!
//! FxHash-style shuffled multiplier. The point is stability across process
//! restarts, not cryptographic strength.

use crate::render::ansi::sgr::palette::{Color, PALETTE};

const MIX: u64 = 0x517c_c1b7_2722_0a95;

pub fn pick_color(origin: &str) -> Color {
    let h = hash(origin.as_bytes());
    PALETTE[(h as usize) % PALETTE.len()]
}

fn hash(bytes: &[u8]) -> u64 {
    let mut acc: u64 = 0xcbf2_9ce4_8422_2325;
    for &b in bytes {
        acc = acc.wrapping_add(b as u64);
        acc = acc.wrapping_mul(MIX);
        acc ^= acc >> 27;
    }
    acc
}

/// Attempt to pick a distinct color for each source in `origins`. Falls back
/// to [`pick_color`] as soon as the palette is exhausted.
pub fn distinct_colors(origins: &[String]) -> Vec<Color> {
    let mut out = Vec::with_capacity(origins.len());
    let mut used = std::collections::HashSet::new();
    for o in origins {
        let primary = pick_color(o);
        if used.insert(primary) {
            out.push(primary);
            continue;
        }
        let mut chosen = primary;
        for offset in 1..PALETTE.len() {
            let h = hash(o.as_bytes()).wrapping_add(offset as u64);
            let alt = PALETTE[(h as usize) % PALETTE.len()];
            if used.insert(alt) {
                chosen = alt;
                break;
            }
        }
        out.push(chosen);
    }
    out
}