use crate::index::IndexError;
use crate::java::parse_epoch_milliseconds;
pub const PRECISION_STEP: u32 = 4;
const SHIFT_START_LONG: u8 = 0x20;
const SHIFT_START_INT: u8 = 0x60;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NumericTerm {
pub bytes: Vec<u8>,
pub position_increment: u32,
}
#[must_use]
pub fn long_terms(value: i64) -> Vec<NumericTerm> {
terms(63, SHIFT_START_LONG, (value ^ i64::MIN) as u64)
}
#[must_use]
pub fn integer_terms(value: i32) -> Vec<NumericTerm> {
terms(31, SHIFT_START_INT, u64::from((value ^ i32::MIN) as u32))
}
#[must_use]
pub fn double_terms(value: f64) -> Vec<NumericTerm> {
long_terms(double_to_sortable_long(value))
}
#[must_use]
pub fn double_to_sortable_long(value: f64) -> i64 {
let bits = value.to_bits() as i64;
if bits < 0 { bits ^ i64::MAX } else { bits }
}
pub fn date_to_long(value: &str) -> Result<i64, IndexError> {
parse_epoch_milliseconds(value).ok_or_else(|| IndexError::UnparseableDate {
value: value.to_owned(),
})
}
fn terms(highest_bit: u32, shift_start: u8, sortable: u64) -> Vec<NumericTerm> {
let mut produced = Vec::new();
let mut shift = 0u32;
while shift <= highest_bit {
produced.push(NumericTerm {
bytes: prefix_coded(highest_bit, shift_start, sortable, shift),
position_increment: u32::from(produced.is_empty()),
});
shift += PRECISION_STEP;
}
produced
}
fn prefix_coded(highest_bit: u32, shift_start: u8, sortable: u64, shift: u32) -> Vec<u8> {
let group_count = ((highest_bit - shift) / 7 + 1) as usize;
let mut bytes = vec![0u8; group_count + 1];
bytes[0] = shift_start + shift as u8;
let mut remaining = sortable >> shift;
for at in (1..=group_count).rev() {
bytes[at] = (remaining & 0x7f) as u8;
remaining >>= 7;
}
bytes
}