revisionstore: implement iter for DataStore trait

Summary:
We need the ability to iterate over a datastore so we can implement
repack and cleanup. In a later diff we'll use this trait to implement repack
functionality in a way that it can apply to any store that implements
IterableStore.

Reviewed By: quark-zju

Differential Revision: D8885094

fbshipit-source-id: 0a2b1ab8cf524392d890302c33e386f1cd218d24
This commit is contained in:
Durham Goode 2018-07-26 12:14:20 -07:00 committed by Facebook Github Bot
parent 8f3b150224
commit c18a1d04f1
3 changed files with 106 additions and 0 deletions

View File

@ -87,6 +87,7 @@ use datastore::{DataStore, Delta, Metadata};
use error::Result;
use key::Key;
use node::Node;
use repack::IterableStore;
#[derive(Debug, Fail)]
#[fail(display = "Datapack Error: {:?}", _0)]
@ -357,6 +358,44 @@ impl DataStore for DataPack {
}
}
impl IterableStore for DataPack {
fn iter<'a>(&'a self) -> Box<Iterator<Item = Result<Key>> + 'a> {
Box::new(DataPackIterator::new(self))
}
}
struct DataPackIterator<'a> {
pack: &'a DataPack,
offset: u64,
}
impl<'a> DataPackIterator<'a> {
pub fn new(pack: &'a DataPack) -> Self {
DataPackIterator {
pack: pack,
offset: 1, // Start after the header byte
}
}
}
impl<'a> Iterator for DataPackIterator<'a> {
type Item = Result<Key>;
fn next(&mut self) -> Option<Self::Item> {
if self.offset as usize >= self.pack.len() {
return None;
}
let entry = self.pack.read_entry(self.offset);
Some(match entry {
Ok(ref e) => {
self.offset = e.next_offset;
Ok(Key::new(e.filename.to_vec().into_boxed_slice(), e.node))
}
Err(e) => Err(e),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -562,4 +601,64 @@ mod tests {
assert_eq!(&chains[i], &chain);
}
}
#[test]
fn test_iter() {
let mut rng = ChaChaRng::from_seed([0u8; 32]);
let tempdir = TempDir::new().unwrap();
let revisions = vec![
(
Delta {
data: Rc::new([1, 2, 3, 4]),
base: Some(Key::new(Box::new([0]), Node::random(&mut rng))),
key: Key::new(Box::new([0]), Node::random(&mut rng)),
},
None,
),
(
Delta {
data: Rc::new([1, 2, 3, 4]),
base: Some(Key::new(Box::new([0]), Node::random(&mut rng))),
key: Key::new(Box::new([0]), Node::random(&mut rng)),
},
None,
),
];
let pack = make_pack(&tempdir, &revisions);
assert_eq!(
pack.iter().collect::<Result<Vec<Key>>>().unwrap(),
revisions
.iter()
.map(|d| d.0.key.clone())
.collect::<Vec<Key>>()
);
}
quickcheck! {
fn test_iter_quickcheck(keys: Vec<(Vec<u8>, Key)>) -> bool {
let tempdir = TempDir::new().unwrap();
let mut revisions: Vec<(Delta, Option<Metadata>)> = vec![];
for (data, key) in keys {
revisions.push((
Delta {
data: Rc::from(data.clone()),
base: None,
key: key.clone(),
},
None,
));
}
let pack = make_pack(&tempdir, &revisions);
let same = pack.iter().collect::<Result<Vec<Key>>>().unwrap()
== revisions
.iter()
.map(|d| d.0.key.clone())
.collect::<Vec<Key>>();
same
}
}
}

View File

@ -28,5 +28,6 @@ pub mod key;
pub mod loosefile;
pub mod mutabledatapack;
pub mod node;
pub mod repack;
pub mod uniondatastore;
pub mod unionhistorystore;

View File

@ -0,0 +1,6 @@
use error::Result;
use key::Key;
pub trait IterableStore {
fn iter<'a>(&'a self) -> Box<Iterator<Item = Result<Key>> + 'a>;
}