treedirstate: add Store and StoreView traits

These traits represent abstract store objects than can store arbitrary data
blocks with store-generated indexes.

A NullStore implementation is provided which acts an always-empty StoreView.

Differential Revision: https://phab.mercurial-scm.org/D1397
This commit is contained in:
Mark Thomas 2017-11-28 04:51:38 -08:00
parent 5931d6cf5d
commit 1537d3833e
5 changed files with 192 additions and 0 deletions

View File

@ -6,11 +6,53 @@ dependencies = [
"memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "dbghelp-sys"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "either"
version = "1.4.0"
@ -25,6 +67,14 @@ dependencies = [
"regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "error-chain"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "fuchsia-zircon"
version = "0.2.1"
@ -112,10 +162,16 @@ name = "regex-syntax"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc-demangle"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rusttreedirstate"
version = "0.1.0"
dependencies = [
"error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quickcheck 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -154,9 +210,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66"
"checksum backtrace 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "99f2ce94e22b8e664d95c57fff45b98a966c2252b60691d0b7aeeccd88d70983"
"checksum backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "44585761d6161b0f57afc49482ab6bd067e4edef48c12a152c237eb0203f7661"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
"checksum cc 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719"
"checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
"checksum either 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "740178ddf48b1a9e878e6d6509a1442a2d42fd2928aae8e7a6f8a36fb01981b3"
"checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f"
"checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3"
"checksum fuchsia-zircon 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f6c0581a4e363262e52b87f59ee2afe3415361c6ec35e665924eb08afe8ff159"
"checksum fuchsia-zircon-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43f3795b4bae048dc6123a6b972cadde2e676f9ded08aef6bb77f5f157684a82"
"checksum itertools 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2c52051d3fd3b505796a0ee90f2e5ec43213808585e8adc4d0182492cf62751a"
@ -168,6 +230,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum rand 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "6475140dfd8655aeb72e1fd4b7a1cc1c202be65d71669476e392fe62532b9edd"
"checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f"
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e"
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"

View File

@ -10,6 +10,9 @@ lto = true
name = "rusttreedirstate"
crate-type = ["cdylib"]
[dependencies]
error-chain = "*"
[dev-dependencies]
itertools = "0.7.2"
quickcheck = "*"

View File

@ -0,0 +1,16 @@
// Copyright Facebook, Inc. 2017
//! Errors.
error_chain! {
errors {
InvalidStoreId(id: u64) {
description("invalid store id"),
display("invalid store id: {}", id),
}
}
foreign_links {
Io(::std::io::Error);
Utf8(::std::str::Utf8Error);
Utf8String(::std::string::FromUtf8Error);
}
}

View File

@ -12,6 +12,9 @@
//! The directory state also stores files that are in the working copy parent manifest but have
//! been marked as removed.
#[macro_use]
extern crate error_chain;
#[cfg(test)]
extern crate itertools;
@ -19,4 +22,8 @@ extern crate itertools;
#[macro_use]
extern crate quickcheck;
pub mod errors;
pub mod store;
pub mod vecmap;
pub use errors::*;

View File

@ -0,0 +1,103 @@
// Copyright Facebook, Inc. 2017
//! Trait defining an append-only storage system.
use errors::*;
use std::borrow::Cow;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct BlockId(u64);
/// Append-only storage. Blocks of data may be stored in an instance of a Store. Once written,
/// blocks are immutable.
pub trait Store {
/// Append a new block of data to the store. Returns the ID of the block. Note that blocks
/// may be buffered until `flush` is called.
fn append(&mut self, data: &[u8]) -> Result<BlockId>;
/// Flush all appended blocks to the backing store.
fn flush(&mut self) -> Result<()>;
}
/// Read-only view of a store.
pub trait StoreView {
/// Read a block of data from the store. Blocks are immutiable, so the result may be a
/// reference to the internal copy of the data in the store.
fn read<'a>(&'a self, id: BlockId) -> Result<Cow<'a, [u8]>>;
}
/// Null implementation of a store. This cannot be used to store new blocks of data, and returns
/// an error if any attempts to read are made.
pub struct NullStore;
impl NullStore {
pub fn new() -> NullStore {
NullStore
}
}
impl StoreView for NullStore {
fn read<'a>(&'a self, id: BlockId) -> Result<Cow<'a, [u8]>> {
bail!(ErrorKind::InvalidStoreId(id.0))
}
}
#[cfg(test)]
pub mod tests {
use std::collections::HashMap;
use errors::*;
use store::{BlockId, Store, StoreView};
use std::borrow::Cow;
/// Define a Store to be used in tests. This doesn't store the data on disk, but rather
/// keeps it in memory in a hash map.
pub struct MapStore {
next_id: BlockId,
data: HashMap<BlockId, Vec<u8>>,
}
impl MapStore {
pub fn new() -> MapStore {
// Initial ID is set to 24 to simulate a header.
MapStore {
next_id: BlockId(24),
data: HashMap::new(),
}
}
}
impl Store for MapStore {
fn append(&mut self, data: &[u8]) -> Result<BlockId> {
let id = self.next_id;
self.data.insert(id, data.to_vec());
self.next_id.0 += data.len() as u64;
Ok(id)
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
impl StoreView for MapStore {
fn read<'a>(&'a self, id: BlockId) -> Result<Cow<'a, [u8]>> {
match self.data.get(&id) {
Some(data) => Ok(Cow::from(data.as_slice())),
None => bail!(ErrorKind::InvalidStoreId(id.0)),
}
}
}
#[test]
fn basic_test() {
let mut ms = MapStore::new();
let key1 = ms.append("12345".as_bytes()).expect("append key1");
let key2 = ms.append("67890".as_bytes()).expect("append key2");
ms.flush().expect("flush");
assert_eq!(ms.read(key2).unwrap(), "67890".as_bytes());
assert_eq!(ms.read(key1).unwrap(), "12345".as_bytes());
assert_eq!(
ms.read(BlockId(999)).unwrap_err().to_string(),
"invalid store id: 999"
);
}
}