use std::marker::PhantomData;
use std::path::Path;
use anyhow::Result;
use leveldb::batch::WriteBatch;
use leveldb::iterator::Iterable;
use leveldb::options::Options;
use leveldb::options::ReadOptions;
use leveldb::options::WriteOptions;
use leveldb_sys::Compression;
use serde::de::DeserializeOwned;
use serde::Serialize;
use tokio::task;
use tracing::debug;
use super::leveldb::DB;
struct NeptuneLevelDbInternal<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
database: DB,
_key: PhantomData<Key>,
_value: PhantomData<Value>,
}
impl<Key, Value> From<DB> for NeptuneLevelDbInternal<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
fn from(database: DB) -> Self {
Self {
database,
_key: Default::default(),
_value: Default::default(),
}
}
}
impl<Key, Value> Clone for NeptuneLevelDbInternal<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
fn clone(&self) -> Self {
Self {
database: self.database.clone(),
_key: Default::default(),
_value: Default::default(),
}
}
}
impl<Key, Value> core::fmt::Debug for NeptuneLevelDbInternal<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("").finish()
}
}
pub fn create_db_if_missing() -> Options {
let mut opts = Options::new();
opts.create_if_missing = true;
opts
}
impl<Key, Value> NeptuneLevelDbInternal<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
fn new(db_path: &Path, options: &Options) -> Result<Self> {
let mut write_options = WriteOptions::new();
write_options.sync = true;
let mut read_options = ReadOptions::new();
read_options.verify_checksums = true;
read_options.fill_cache = true;
let database = DB::open_with_options(db_path, options, read_options, write_options)?;
let database = Self {
database,
_key: PhantomData,
_value: PhantomData,
};
Ok(database)
}
fn get(&self, key: Key) -> Option<Value> {
let key_bytes: Vec<u8> = bincode::serialize(&key).unwrap();
let value_bytes: Option<Vec<u8>> = self.database.get(&key_bytes).unwrap();
value_bytes.map(|bytes| bincode::deserialize(&bytes).unwrap())
}
fn get_u8(&mut self, key: &[u8]) -> Option<Vec<u8>> {
self.database.get_u8(key).unwrap()
}
fn put(&mut self, key: Key, value: Value) {
let key_bytes: Vec<u8> = bincode::serialize(&key).unwrap();
let value_bytes: Vec<u8> = bincode::serialize(&value).unwrap();
self.database.put(&key_bytes, &value_bytes).unwrap();
}
fn put_u8(&mut self, key: &[u8], value: &[u8]) {
self.database.put_u8(key, value).unwrap()
}
fn batch_write(&mut self, entries: WriteBatchAsync<Key, Value>) {
let batch = WriteBatch::new();
for op in entries.0 {
match op {
WriteBatchOpAsync::Write(key, value) => {
let key_bytes: Vec<u8> = bincode::serialize(&key).unwrap();
let value_bytes: Vec<u8> = bincode::serialize(&value).unwrap();
batch.put(&key_bytes, &value_bytes);
}
WriteBatchOpAsync::Delete(key) => {
let key_bytes: Vec<u8> = bincode::serialize(&key).unwrap();
batch.delete(&key_bytes);
}
}
}
self.database.write(&batch, true).unwrap();
}
fn delete(&mut self, key: Key) -> Option<Value> {
let key_bytes: Vec<u8> = bincode::serialize(&key).unwrap(); let value_bytes: Option<Vec<u8>> = self.database.get(&key_bytes).unwrap();
let value_object = value_bytes.map(|bytes| bincode::deserialize(&bytes).unwrap());
let status = self.database.delete(&key_bytes);
match status {
Ok(_) => value_object, Err(err) => panic!("database failure: {}", err),
}
}
fn flush(&mut self) {
self.database
.write(&WriteBatch::new(), true)
.expect("Database flushing to disk must succeed");
}
fn dump_database(&self) {
use std::io::Write;
for (key, val) in self.database.iter(&ReadOptions::new()) {
print!("Key (hex): ");
for byte in &key {
print!("{:02x} ", byte);
}
println!();
print!("Value (hex): ");
for byte in &val {
print!("{:02x} ", byte);
}
println!();
println!(); std::io::stdout().flush().unwrap(); }
}
}
#[derive(Clone)]
pub struct NeptuneLevelDb<Key, Value>(NeptuneLevelDbInternal<Key, Value>)
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned;
impl<Key, Value> core::fmt::Debug for NeptuneLevelDb<Key, Value>
where
Key: Serialize + DeserializeOwned,
Value: Serialize + DeserializeOwned,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NeptuneLevelDb").finish()
}
}
impl<Key, Value> NeptuneLevelDb<Key, Value>
where
Key: Serialize + DeserializeOwned + Send + Sync + 'static,
Value: Serialize + DeserializeOwned + Send + Sync + 'static,
{
pub fn iter(&self) -> Box<dyn Iterator<Item = (Key, Value)> + '_> {
let inner = self.0.clone();
let keys: Vec<_> = inner.database.keys_iter(&ReadOptions::new()).collect();
Box::new(keys.into_iter().map(move |k| {
let v = inner.database.get_u8(&k).unwrap().unwrap();
(
bincode::deserialize(&k).unwrap(),
bincode::deserialize(&v).unwrap(),
)
}))
}
pub async fn new(db_path: &Path, options: &Options) -> Result<Self> {
let options_async = OptionsAsync::from(options);
let path = db_path.to_path_buf();
debug!(
"Attempting to open new wallet database from: {}",
path.to_string_lossy()
);
let db =
task::spawn_blocking(move || NeptuneLevelDbInternal::new(&path, &options_async.into()))
.await??;
Ok(Self(db))
}
pub async fn get(&self, key: Key) -> Option<Value> {
let inner = self.0.clone();
task::spawn_blocking(move || inner.get(key)).await.unwrap()
}
pub async fn get_u8(&self, key: Vec<u8>) -> Option<Vec<u8>> {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.get_u8(&key))
.await
.unwrap()
}
pub async fn put(&mut self, key: Key, value: Value) {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.put(key, value))
.await
.unwrap()
}
pub async fn put_u8(&mut self, key: Vec<u8>, value: Vec<u8>) {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.put_u8(&key, &value))
.await
.unwrap()
}
pub async fn batch_write(&mut self, entries: WriteBatchAsync<Key, Value>) {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.batch_write(entries))
.await
.unwrap()
}
pub async fn delete(&mut self, key: Key) -> Option<Value> {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.delete(key))
.await
.unwrap()
}
pub async fn flush(&mut self) {
let mut inner = self.0.clone();
task::spawn_blocking(move || inner.flush()).await.unwrap()
}
#[inline]
pub fn path(&self) -> &std::path::PathBuf {
self.0.database.path()
}
}
impl<Key, Value> NeptuneLevelDb<Key, Value>
where
Key: Serialize + DeserializeOwned + Send + Sync + 'static,
Value: Serialize + DeserializeOwned + Send + Sync + 'static,
{
pub async fn open_new_test_database(
destroy_db_on_drop: bool,
options: Option<Options>,
read_options: Option<ReadOptions>,
write_options: Option<WriteOptions>,
) -> Result<Self> {
let options_async = options.map(OptionsAsync::from);
let db = task::spawn_blocking(move || {
DB::open_new_test_database(
destroy_db_on_drop,
options_async.map(|o| o.into()),
read_options,
write_options,
)
})
.await??;
Ok(Self(NeptuneLevelDbInternal::from(db)))
}
pub async fn open_test_database(
db_path: &std::path::Path,
destroy_db_on_drop: bool,
options: Option<Options>,
read_options: Option<ReadOptions>,
write_options: Option<WriteOptions>,
) -> Result<Self> {
let path = db_path.to_path_buf();
let options_async = options.map(OptionsAsync::from);
let db = task::spawn_blocking(move || {
DB::open_test_database(
&path,
destroy_db_on_drop,
options_async.map(|o| o.into()),
read_options,
write_options,
)
})
.await??;
Ok(Self(NeptuneLevelDbInternal::from(db)))
}
pub async fn dump_database(&self) {
let inner = self.0.clone();
task::spawn_blocking(move || inner.dump_database())
.await
.unwrap()
}
}
pub(super) struct OptionsAsync {
pub create_if_missing: bool,
pub error_if_exists: bool,
pub paranoid_checks: bool,
pub write_buffer_size: Option<usize>,
pub max_open_files: Option<i32>,
pub block_size: Option<usize>,
pub block_restart_interval: Option<i32>,
pub compression: Compression,
}
impl From<&Options> for OptionsAsync {
fn from(o: &Options) -> Self {
assert!(
o.cache.is_none(),
"cache option not supported for NeptuneLevelDb"
);
Self {
create_if_missing: o.create_if_missing,
error_if_exists: o.error_if_exists,
paranoid_checks: o.paranoid_checks,
write_buffer_size: o.write_buffer_size,
max_open_files: o.max_open_files,
block_size: o.block_size,
block_restart_interval: o.block_restart_interval,
compression: o.compression,
}
}
}
impl From<Options> for OptionsAsync {
fn from(o: Options) -> Self {
Self::from(&o)
}
}
impl From<&OptionsAsync> for Options {
fn from(o: &OptionsAsync) -> Self {
Self {
create_if_missing: o.create_if_missing,
error_if_exists: o.error_if_exists,
paranoid_checks: o.paranoid_checks,
write_buffer_size: o.write_buffer_size,
max_open_files: o.max_open_files,
block_size: o.block_size,
block_restart_interval: o.block_restart_interval,
compression: o.compression,
cache: None,
}
}
}
impl From<OptionsAsync> for Options {
fn from(o: OptionsAsync) -> Self {
Self::from(&o)
}
}
#[derive(Debug, Clone)]
enum WriteBatchOpAsync<K, V> {
Write(K, V),
Delete(K),
}
#[derive(Debug, Clone)]
pub struct WriteBatchAsync<K, V>(Vec<WriteBatchOpAsync<K, V>>);
impl<K, V> WriteBatchAsync<K, V> {
pub fn new() -> Self {
Self(vec![])
}
pub fn op_write(&mut self, key: K, value: V) {
self.0.push(WriteBatchOpAsync::Write(key, value));
}
pub fn op_delete(&mut self, key: K) {
self.0.push(WriteBatchOpAsync::Delete(key));
}
}
impl<K, V> Default for WriteBatchAsync<K, V> {
fn default() -> Self {
Self::new()
}
}