#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![warn(clippy::all)]
pub mod bits;
pub mod idxsets;
mod internal;
pub mod prelude;
pub mod queries;
pub mod reductions;
pub mod traits;
pub mod types;
#[cfg(test)]
mod test {
use crate::prelude::*;
use crate::types::reduction::Reduction;
use std::borrow::Cow;
static_assertions::assert_impl_all!(Storage<u64,u64,(u64,u64,u64)>: Send, Sync);
static_assertions::assert_impl_all!(Reduction<u64, (u64,u64,u64), u64>: Send, Sync);
static_assertions::assert_impl_all!(SecondaryIndex<u64, (u64,u64,u64), std::collections::HashSet<u64>, u64>: Send, Sync);
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct X(u64, u64);
impl Record<u64, u64> for X {
fn chunk_key(&self) -> Cow<u64> {
Cow::Owned((self.0 & 0x00F0) >> 4)
}
fn item_key(&self) -> Cow<u64> {
Cow::Borrowed(&self.0)
}
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
struct S(String, String, String);
impl Record<str, str> for S {
fn chunk_key(&self) -> Cow<str> {
Cow::Borrowed(&self.0)
}
fn item_key(&self) -> Cow<str> {
Cow::Borrowed(&self.1)
}
}
#[test]
fn test_remove_and_replace_chunk_with_secondary_index() {
let mut storage: Storage<u64, u64, X> = Storage::new();
let index: SecondaryIndex<u64, X, Option<u64>, u64> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1 & 0x1)));
storage.add(X(0x101, 0x101));
storage.add(X(0x102, 0x102));
storage.add(X(0x103, 0x103));
storage.add(X(0x104, 0x104));
storage.add(X(0x105, 0x105));
storage.add(X(0x106, 0x106));
storage.add(X(0x107, 0x107));
storage.add(X(0x108, 0x108));
assert_eq!(
4,
storage
.query(&Everything.matching(&index, Cow::Owned(0)))
.count()
);
storage.remove_chunk(&0);
storage.add(X(0x101, 0x101));
storage.add(X(0x102, 0x102));
storage.add(X(0x103, 0x103));
storage.add(X(0x104, 0x104));
storage.add(X(0x105, 0x105));
storage.add(X(0x106, 0x106));
storage.add(X(0x107, 0x107));
storage.add(X(0x108, 0x108));
assert_eq!(
4,
storage
.query(&Everything.matching(&index, Cow::Owned(0)))
.count()
);
}
#[test]
fn test_editor() {
let mut storage: Storage<u64, u64, X> = Storage::new();
storage.add(X(0x101, 0x101));
storage.add(X(0x202, 0x101));
storage.add(X(0x111, 0x101));
storage.modify(&Id(0x0, 0x202), |mut editor| {
assert_eq!(&Id(&0x0, &0x202), editor.id());
assert_eq!(&X(0x202, 0x101), editor.get());
editor.get_mut().1 = 0x102;
assert_eq!(&X(0x202, 0x102), editor.get());
});
storage.validate();
}
#[test]
fn test_filter() {
let mut storage: Storage<u64, u64, X> = Storage::new();
storage.add(X(0x101, 0x101));
storage.add(X(0x202, 0x999));
storage.add(X(0x111, 0x111));
storage.remove(&Chunks([0x0]).filter(|x: &X| x.1 == 0x999), std::mem::drop);
assert_eq!(2, storage.iter().count());
assert!(storage.get(&Id(0x0, 0x101)).is_some());
assert!(storage.get(&Id(0x1, 0x111)).is_some());
assert!(storage.get(&Id(0x0, 0x202)).is_none());
storage.validate();
}
#[test]
fn test_query_by_id() {
let mut storage: Storage<u64, u64, X> = Storage::new();
let even_odd: SecondaryIndex<u64, X, Option<bool>, bool> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1 % 2 == 1)));
storage.add(X(0x000, 0x000));
storage.add(X(0x101, 0x111));
storage.add(X(0x202, 0x222));
assert_eq!(
Some(&X(0x101, 0x111)),
storage.query(&Id(0x0, 0x101)).next()
);
assert_eq!(
Some(&X(0x101, 0x111)),
storage
.query(&Id(0x0, 0x101).matching(&even_odd, Cow::Owned(true)))
.next()
);
assert_eq!(
None,
storage
.query(&Id(0x0, 0x101).matching(&even_odd, Cow::Owned(false)))
.next()
);
storage.validate();
even_odd.validate(&storage);
}
#[test]
fn test_query_by_chunks() {
let mut storage: Storage<u64, u64, X> = Storage::new();
let even_odd: SecondaryIndex<u64, X, Option<bool>, bool> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1 % 2 == 1)));
storage.add(X(0x000, 0x000));
storage.add(X(0x101, 0x111));
storage.add(X(0x202, 0x222));
storage.add(X(0x010, 0x000));
storage.add(X(0x111, 0x111));
storage.add(X(0x212, 0x222));
storage.add(X(0x020, 0x000));
storage.add(X(0x121, 0x111));
storage.add(X(0x222, 0x222));
let odd_items_even_chunks: Vec<X> = storage
.query(&Chunks([0x0, 0x2]).matching(&even_odd, Cow::Owned(true)))
.cloned()
.collect();
assert_eq!(
&[X(0x101, 0x111), X(0x121, 0x111)],
odd_items_even_chunks.as_slice()
);
storage.validate();
even_odd.validate(&storage);
}
#[test]
fn test_index_intersections() {
let mut storage: Storage<u64, u64, X> = Storage::new();
let even_odd: SecondaryIndex<u64, X, Option<bool>, bool> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1 % 2 == 1)));
let small: SecondaryIndex<u64, X, Option<bool>, bool> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1 < 0x600)));
storage.add(X(0x000, 0x000));
storage.add(X(0x101, 0x111));
storage.add(X(0x202, 0x222));
storage.add(X(0x303, 0x333));
storage.add(X(0x404, 0x444));
storage.add(X(0x505, 0x555));
storage.add(X(0x606, 0x666));
storage.add(X(0x707, 0x777));
let mut small_odds: Vec<X> = storage
.query(
&Everything
.matching(&even_odd, Cow::Owned(true))
.matching(&small, Cow::Owned(true)),
)
.cloned()
.collect();
assert_eq!(3, small_odds.len());
assert!(small_odds.contains(&X(0x101, 0x111)));
assert!(small_odds.contains(&X(0x303, 0x333)));
assert!(small_odds.contains(&X(0x505, 0x555)));
assert!(!small_odds.contains(&X(0x202, 0x222)));
assert!(!small_odds.contains(&X(0x707, 0x777)));
let mut odd_smalls: Vec<X> = storage
.query(
&Everything
.matching(&small, Cow::Owned(true))
.matching(&even_odd, Cow::Owned(true)),
)
.cloned()
.collect();
assert_eq!(3, small_odds.len());
assert!(odd_smalls.contains(&X(0x101, 0x111)));
assert!(odd_smalls.contains(&X(0x303, 0x333)));
assert!(odd_smalls.contains(&X(0x505, 0x555)));
assert!(!odd_smalls.contains(&X(0x202, 0x222)));
assert!(!odd_smalls.contains(&X(0x707, 0x777)));
small_odds.sort();
odd_smalls.sort();
assert_eq!(small_odds, odd_smalls);
storage.validate();
even_odd.validate(&storage);
small.validate(&storage);
}
#[test]
fn test_random_edits() {
use rand::Rng;
let mut storage: Storage<u64, u64, X> = Storage::new();
let mut reduction: Reduction<u64, X, u64> = Reduction::new(
&storage,
16,
|x: &X, was| {
if x.1 != *was {
Some(x.1)
} else {
None
}
},
|xs: &[u64], was| {
let total = xs.iter().cloned().sum::<u64>();
if total != *was {
Some(total)
} else {
None
}
},
);
let index: SecondaryIndex<u64, X, Option<u64>, u64> =
SecondaryIndex::new(&storage, |x: &X| Cow::Owned(Some(x.1)));
let k = 100_000;
for i in 0..k {
storage.add(X(i, rand::thread_rng().gen_range(0, k / 10)));
}
for _ in 0..k {
let id = rand::thread_rng().gen_range(0, k);
storage
.entry(&X(id, 0))
.and_modify(|x| {
x.1 = rand::thread_rng().gen_range(0, k / 10);
})
.or_panic();
storage
.query(
&Everything.matching(&index, Cow::Owned(rand::thread_rng().gen_range(0, 10))),
)
.count();
reduction.reduce(&storage);
}
storage.validate();
index.validate(&storage);
}
#[test]
fn test_chunk_chaos() {
use rand::Rng;
let mut storage: Storage<u8, u8, (u8, u8, u8)> = Storage::new();
let k = 255;
for i in 0..k {
storage.add((i, 0, 0));
}
for i in 0..k {
if rand::thread_rng().gen() {
storage.remove(ID.chunk(i).item(0), std::mem::drop);
}
}
for i in 0..k {
if rand::thread_rng().gen() {
storage.add((i, 1, 0));
}
}
storage.validate();
}
#[test]
fn test_entry() {
let mut storage: Storage<u64, u64, X> = Storage::new();
storage
.entry(&ID.chunk(0).item(0))
.or_insert_with(|| X(0, 0));
storage
.entry(&ID.chunk(0).item(0))
.or_insert_with(|| X(0, 0))
.1 += 1;
storage.entry(&ID.chunk(0).item(0)).and_modify(|x| {
x.1 += 10;
});
storage
.entry(&ID.chunk(0).item(0))
.or_insert_with(|| X(0, 0))
.1 += 1;
assert_eq!(Some(&X(0, 12)), storage.entry(&ID.chunk(0).item(0)).get());
storage.entry(&ID.chunk(0).item(0)).remove_if(|x| x.1 != 12);
storage.entry(&ID.chunk(0).item(0)).or_panic();
storage.entry(&ID.chunk(0).item(0)).remove_if(|x| x.1 == 12);
assert_eq!(None, storage.entry(&ID.chunk(0).item(0)).get());
}
#[test]
#[should_panic]
fn test_entry_with_bogus_chunk() {
let mut storage: Storage<u64, u64, X> = Storage::new();
storage
.entry(&ID.chunk(0).item(16))
.or_insert_with(|| X(16, 0));
}
#[test]
#[should_panic]
fn test_entry_with_bogus_item() {
let mut storage: Storage<u64, u64, X> = Storage::new();
storage
.entry(&ID.chunk(0).item(16))
.or_insert_with(|| X(1, 0));
}
#[test]
fn test_str() {
let mut storage: Storage<str, str, S> = Storage::new();
storage.add(S(
String::from("broberts"),
String::from("name"),
String::from("Bob Roberts"),
));
storage.add(S(
String::from("broberts"),
String::from("password"),
String::from("password1"),
));
storage.add(S(
String::from("ssmith"),
String::from("name"),
String::from("Sue Smith"),
));
storage.add(S(
String::from("ssmith"),
String::from("password"),
String::from("1234"),
));
assert_eq!(
Some("Bob Roberts"),
storage
.get(&ID.chunk("broberts").item("name"))
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(&ID.chunk(String::from("broberts")).item("name"))
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(&ID.chunk("broberts").item(String::from("name")))
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(
&ID.chunk(String::from("broberts"))
.item(String::from("name"))
)
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(
&ID.chunk(Cow::Borrowed("broberts"))
.item(String::from("name"))
)
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(
&ID.chunk(Cow::Owned(String::from("broberts")))
.item(Cow::Borrowed("name"))
)
.map(|s| s.2.as_str())
);
assert_eq!(
Some("Bob Roberts"),
storage
.get(
&ID.chunk(Cow::Owned(String::from("broberts")))
.item(Cow::Owned(String::from("name")))
)
.map(|s| s.2.as_str())
);
assert_eq!(
2,
storage
.query(Chunks(vec![String::from("broberts")]))
.count()
);
assert_eq!(
2,
storage
.query(Chunks(vec![Cow::Borrowed("broberts")]))
.count()
);
assert_eq!(2, storage.query(Chunks(vec!["broberts"])).count());
}
}