#![deny(clippy::all)]
mod io_stats;
pub use io_stats::{IoStats, Kind as IoStatsKind};
use std::future::Future;
use std::io;
use std::pin::Pin;
pub const PREFIX_LEN: usize = 12;
pub type DBValue = Vec<u8>;
pub type DBKey = Vec<u8>;
pub type DBKeyValue = (DBKey, DBValue);
pub type DBKeyRef<'a> = &'a DBKey;
pub type DBKeyValueRef<'a> = (&'a DBKey, &'a DBValue);
#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct DBTransaction {
pub ops: Vec<DBOp>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DBOp {
Insert {
col: u32,
key: DBKey,
value: DBValue,
},
Delete {
col: u32,
key: DBKey,
},
DeletePrefix {
col: u32,
prefix: DBKey,
},
}
impl DBOp {
pub fn key(&self) -> &DBKey {
match *self {
DBOp::Insert { ref key, .. } => key,
DBOp::Delete { ref key, .. } => key,
DBOp::DeletePrefix { ref prefix, .. } => prefix,
}
}
pub fn value(&self) -> Option<&DBValue> {
match *self {
DBOp::Insert { ref value, .. } => Some(value),
DBOp::Delete { .. } => None,
DBOp::DeletePrefix { .. } => None,
}
}
pub fn col(&self) -> u32 {
match *self {
DBOp::Insert { col, .. } => col,
DBOp::Delete { col, .. } => col,
DBOp::DeletePrefix { col, .. } => col,
}
}
}
impl DBTransaction {
pub fn new() -> DBTransaction {
DBTransaction::with_capacity(256)
}
pub fn with_capacity(cap: usize) -> DBTransaction {
DBTransaction {
ops: Vec::with_capacity(cap),
}
}
pub fn put<K, V>(&mut self, col: u32, key: K, value: V)
where
K: AsRef<[u8]>,
V: AsRef<[u8]>,
{
self.ops.push(DBOp::Insert {
col,
key: DBKey::from(key.as_ref()),
value: DBValue::from(value.as_ref()),
})
}
pub fn put_owned(&mut self, col: u32, key: Vec<u8>, value: Vec<u8>) {
self.ops.push(DBOp::Insert { col, key, value })
}
pub fn delete<K>(&mut self, col: u32, key: K)
where
K: AsRef<[u8]>,
{
self.ops.push(DBOp::Delete {
col,
key: DBKey::from(key.as_ref()),
});
}
pub fn delete_owned(&mut self, col: u32, key: Vec<u8>) {
self.ops.push(DBOp::Delete { col, key });
}
pub fn delete_prefix<K>(&mut self, col: u32, prefix: K)
where
K: AsRef<[u8]>,
{
self.ops.push(DBOp::DeletePrefix {
col,
prefix: DBKey::from(prefix.as_ref()),
});
}
pub fn delete_prefix_owned(&mut self, col: u32, prefix: Vec<u8>) {
self.ops.push(DBOp::DeletePrefix { col, prefix });
}
}
#[derive(Debug)]
pub struct DBTransactionError {
pub error: io::Error,
pub transaction: DBTransaction,
}
impl std::fmt::Display for DBTransactionError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TransactionError")
.field("error", &self.error)
.finish()
}
}
impl std::error::Error for DBTransactionError {}
pub type KeyValueDBPinBoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
pub trait KeyValueDB: Sync + Send + Clone + 'static {
fn transaction(&self) -> DBTransaction {
DBTransaction::new()
}
fn get<'a>(
&'a self,
col: u32,
key: &'a [u8],
) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBValue>>>;
fn delete<'a>(
&'a self,
col: u32,
key: &'a [u8],
) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBValue>>>;
fn write(
&self,
transaction: DBTransaction,
) -> KeyValueDBPinBoxFuture<'_, Result<(), DBTransactionError>>;
fn iter<
'a,
T: Send + 'static,
C: Send + 'static,
F: FnMut(&mut C, DBKeyValueRef) -> io::Result<Option<T>> + Send + Sync + 'static,
>(
&'a self,
col: u32,
prefix: Option<&'a [u8]>,
context: C,
f: F,
) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>>;
fn iter_keys<
'a,
T: Send + 'static,
C: Send + 'static,
F: FnMut(&mut C, DBKeyRef) -> io::Result<Option<T>> + Send + Sync + 'static,
>(
&'a self,
col: u32,
prefix: Option<&'a [u8]>,
context: C,
f: F,
) -> KeyValueDBPinBoxFuture<'a, io::Result<(C, Option<T>)>>;
fn io_stats(&self, _kind: IoStatsKind) -> IoStats {
IoStats::empty()
}
fn num_columns(&self) -> io::Result<u32>;
fn num_keys(&self, col: u32) -> KeyValueDBPinBoxFuture<'_, io::Result<u64>>;
fn has_key<'a>(
&'a self,
col: u32,
key: &'a [u8],
) -> KeyValueDBPinBoxFuture<'a, io::Result<bool>> {
let this = self.clone();
Box::pin(async move { Ok(this.get(col, key).await?.is_some()) })
}
fn has_prefix<'a>(
&'a self,
col: u32,
prefix: &'a [u8],
) -> KeyValueDBPinBoxFuture<'a, io::Result<bool>> {
let this = self.clone();
Box::pin(async move {
let (_, out) = this
.iter_keys(col, Some(prefix), (), |_, _| Ok(Some(())))
.await?;
Ok(out.is_some())
})
}
fn first_with_prefix<'a>(
&'a self,
col: u32,
prefix: &'a [u8],
) -> KeyValueDBPinBoxFuture<'a, io::Result<Option<DBKeyValue>>> {
let this = self.clone();
Box::pin(async move {
let (_, out) = this
.iter(col, Some(prefix), (), |_, (k, v)| {
Ok(Some((k.to_vec(), v.to_vec())))
})
.await?;
Ok(out)
})
}
fn cleanup(&self) -> KeyValueDBPinBoxFuture<'_, io::Result<()>> {
Box::pin(async { Ok(()) })
}
}
pub fn end_prefix(prefix: &[u8]) -> Option<Vec<u8>> {
let mut end_range = prefix.to_vec();
while let Some(0xff) = end_range.last() {
end_range.pop();
}
if let Some(byte) = end_range.last_mut() {
*byte += 1;
Some(end_range)
} else {
None
}
}
#[cfg(test)]
mod test {
use super::end_prefix;
#[test]
fn end_prefix_test() {
assert_eq!(end_prefix(&[5, 6, 7]), Some(vec![5, 6, 8]));
assert_eq!(end_prefix(&[5, 6, 255]), Some(vec![5, 7]));
assert_ne!(end_prefix(&[5, 255, 255]), Some(vec![5, 255]));
assert_eq!(end_prefix(&[5, 255, 255]), Some(vec![6]));
assert_eq!(end_prefix(&[255, 255, 255]), None);
assert_eq!(end_prefix(&[0x00, 0xff]), Some(vec![0x01]));
assert_eq!(end_prefix(&[0xff]), None);
assert_eq!(end_prefix(&[]), None);
assert_eq!(end_prefix(b"0"), Some(b"1".to_vec()));
}
}