use super::builder::BuilderTrait;
use crate::bases::*;
use std::cmp::Ordering;
pub trait CompareTrait {
fn ordered(&self) -> bool;
fn compare_entry(&self, idx: EntryIdx) -> Result<Ordering>;
}
pub trait RangeTrait {
fn count(&self) -> EntryCount;
fn offset(&self) -> EntryIdx;
fn get_entry<Builder: BuilderTrait>(
&self,
builder: &Builder,
id: EntryIdx,
) -> std::result::Result<Option<Builder::Entry>, Builder::Error> {
if id.is_valid(*self.count()) {
builder.create_entry(self.offset() + id)
} else {
Ok(None)
}
}
fn find<Comparator: CompareTrait>(&self, comparator: &Comparator) -> Result<Option<EntryIdx>> {
if comparator.ordered() {
let mut size = self.count();
let mut left = EntryIdx::from(0);
let mut right = left + size;
while left < right {
let mid = left + size / 2;
let cmp = comparator.compare_entry(self.offset() + mid)?;
if cmp == Ordering::Less {
left = mid + EntryCount::from(1);
} else if cmp == Ordering::Greater {
right = mid;
} else {
return Ok(Some(mid));
}
size = right - left;
}
Ok(None)
} else {
for idx in self.count() {
let cmp = comparator.compare_entry(self.offset() + idx)?;
if cmp.is_eq() {
return Ok(Some(idx));
}
}
Ok(None)
}
}
}
impl RangeTrait for EntryRange {
fn count(&self) -> EntryCount {
self.size()
}
fn offset(&self) -> EntryIdx {
self.begin()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reader::directory_pack::builder;
use crate::reader::directory_pack::EntryTrait;
use crate::reader::RawValue;
mod mock {
use super::*;
#[derive(PartialEq, Eq, Debug)]
pub struct Entry {
v: RawValue,
}
impl Entry {
fn new(v: u16) -> Self {
let v = RawValue::U16(v);
Self { v }
}
}
impl EntryTrait for Entry {
fn get_variant_id(&self) -> Result<Option<VariantIdx>> {
Ok(None)
}
fn get_value(&self, name: impl AsRef<str>) -> Result<Option<RawValue>> {
if name.as_ref() == "foo" {
Ok(Some(self.v.clone()))
} else {
Ok(None)
}
}
}
pub struct EntryCompare {
reference: u32,
ordered: bool,
}
impl EntryCompare {
pub fn new(reference: u32, ordered: bool) -> Self {
Self { reference, ordered }
}
}
impl CompareTrait for EntryCompare {
fn compare_entry(&self, index: EntryIdx) -> Result<Ordering> {
Ok(index.into_u32().cmp(&self.reference))
}
fn ordered(&self) -> bool {
self.ordered
}
}
pub struct Builder {}
impl builder::BuilderTrait for Builder {
type Entry = Entry;
type Error = Error;
fn create_entry(&self, idx: EntryIdx) -> Result<Option<Self::Entry>> {
Ok(Some(Entry::new(idx.into_u32() as u16)))
}
}
}
#[rustest::test]
fn test_finder() -> rustest::Result {
let builder = mock::Builder {};
let range = EntryRange::new_from_size(EntryIdx::from(0), EntryCount::from(10));
for i in 0..10 {
let entry = range.get_entry(&builder, i.into())?.unwrap();
let value0 = entry.get_value("foo")?.unwrap();
assert_eq!(value0.as_unsigned(), i as u64);
}
Ok(())
}
#[rustest::test]
fn test_comparator_false() -> rustest::Result {
let builder = mock::Builder {};
let range = EntryRange::new_from_size(EntryIdx::from(0), EntryCount::from(10));
for i in 0..10 {
let comparator = mock::EntryCompare::new(i, false);
let idx = range.find(&comparator)?.unwrap();
let entry = range.get_entry(&builder, idx)?.unwrap();
let value0 = entry.get_value("foo")?.unwrap();
assert_eq!(value0.as_unsigned(), i as u64);
}
let comparator = mock::EntryCompare::new(10, false);
let result = range.find(&comparator)?;
assert_eq!(result, None);
Ok(())
}
#[rustest::test]
fn test_comparator_true() -> rustest::Result {
let builder = mock::Builder {};
let range = EntryRange::new_from_size(EntryIdx::from(0), EntryCount::from(10));
for i in 0..10 {
let comparator = mock::EntryCompare::new(i, true);
let idx = range.find(&comparator)?.unwrap();
let entry = range.get_entry(&builder, idx)?.unwrap();
let value0 = entry.get_value("foo")?.unwrap();
assert_eq!(value0.as_unsigned(), i as u64);
}
let comparator = mock::EntryCompare::new(10, true);
let result = range.find(&comparator).unwrap();
assert_eq!(result, None);
Ok(())
}
}