use crate::{Error, Result};
#[derive(Debug, Clone, Copy, Default)]
pub struct DartsResult {
pub value: i32,
pub length: usize,
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct Unit {
base: i32,
check: u32,
}
#[derive(Debug)]
pub struct DoubleArrayTrie {
units_ptr: *const Unit,
size: usize,
}
unsafe impl Send for DoubleArrayTrie {}
unsafe impl Sync for DoubleArrayTrie {}
impl DoubleArrayTrie {
pub const UNIT_SIZE: usize = 8;
pub fn from_bytes(data: &[u8], size_in_bytes: usize) -> Result<Self> {
if data.len() < size_in_bytes {
return Err(Error::CorruptedDictionary(format!(
"Double-array data too small: expected {} bytes, got {}",
size_in_bytes,
data.len()
)));
}
let size = size_in_bytes / Self::UNIT_SIZE;
let units_ptr = data.as_ptr() as *const Unit;
Ok(Self { units_ptr, size })
}
#[inline]
fn get(&self, index: usize) -> Option<Unit> {
if index < self.size {
Some(unsafe { *self.units_ptr.add(index) })
} else {
None
}
}
pub fn exact_match_search(&self, key: &[u8]) -> DartsResult {
let mut result = DartsResult {
value: -1,
length: 0,
};
let mut b = match self.get(0) {
Some(unit) => unit.base,
None => return result,
};
for &byte in key.iter() {
let p = (b as usize).wrapping_add(byte as usize).wrapping_add(1);
match self.get(p) {
Some(unit) if unit.check == b as u32 => {
b = unit.base;
}
_ => return result,
}
}
let p = b as usize;
if let Some(unit) = self.get(p) {
if unit.check == b as u32 && unit.base < 0 {
result.value = -unit.base - 1;
result.length = key.len();
}
}
result
}
pub fn common_prefix_search(&self, key: &[u8], results: &mut [DartsResult]) -> usize {
let max_results = results.len();
let mut num_results = 0;
let mut b = match self.get(0) {
Some(unit) => unit.base,
None => return 0,
};
for (i, &byte) in key.iter().enumerate() {
let p = b as usize;
if let Some(unit) = self.get(p) {
if unit.check == b as u32 && unit.base < 0 && num_results < max_results {
results[num_results] = DartsResult {
value: -unit.base - 1,
length: i,
};
num_results += 1;
}
}
let p = (b as usize).wrapping_add(byte as usize).wrapping_add(1);
match self.get(p) {
Some(unit) if unit.check == b as u32 => {
b = unit.base;
}
_ => return num_results,
}
}
let p = b as usize;
if let Some(unit) = self.get(p) {
if unit.check == b as u32 && unit.base < 0 && num_results < max_results {
results[num_results] = DartsResult {
value: -unit.base - 1,
length: key.len(),
};
num_results += 1;
}
}
num_results
}
#[inline]
pub fn size(&self) -> usize {
self.size
}
#[inline]
pub fn total_size(&self) -> usize {
self.size * Self::UNIT_SIZE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unit_size() {
assert_eq!(std::mem::size_of::<Unit>(), 8);
assert_eq!(DoubleArrayTrie::UNIT_SIZE, 8);
}
#[test]
fn test_darts_result_default() {
let result = DartsResult::default();
assert_eq!(result.value, 0);
assert_eq!(result.length, 0);
}
}