summaryrefslogtreecommitdiffstats
path: root/third_party/rust/rkv/src/backend/impl_safe
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/rkv/src/backend/impl_safe')
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/cursor.rs98
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/database.rs41
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/environment.rs351
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/error.rs79
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/flags.rs124
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/info.rs35
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/iter.rs24
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/snapshot.rs141
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/stat.rs39
-rw-r--r--third_party/rust/rkv/src/backend/impl_safe/transaction.rs208
10 files changed, 1140 insertions, 0 deletions
diff --git a/third_party/rust/rkv/src/backend/impl_safe/cursor.rs b/third_party/rust/rkv/src/backend/impl_safe/cursor.rs
new file mode 100644
index 0000000000..c3bfefad94
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/cursor.rs
@@ -0,0 +1,98 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use super::{snapshot::Snapshot, IterImpl};
+use crate::backend::traits::BackendRoCursor;
+
+#[derive(Debug)]
+pub struct RoCursorImpl<'c>(pub(crate) &'c Snapshot);
+
+#[cfg(not(feature = "db-dup-sort"))]
+impl<'c> BackendRoCursor<'c> for RoCursorImpl<'c> {
+ type Iter = IterImpl<'c>;
+
+ fn into_iter(self) -> Self::Iter {
+ IterImpl(Box::new(self.0.iter()))
+ }
+
+ fn into_iter_from<K>(self, key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ IterImpl(Box::new(
+ self.0.iter().skip_while(move |&(k, _)| k < key.as_ref()),
+ ))
+ }
+
+ fn into_iter_dup_of<K>(self, key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ IterImpl(Box::new(
+ self.0.iter().filter(move |&(k, _)| k == key.as_ref()),
+ ))
+ }
+}
+
+#[cfg(feature = "db-dup-sort")]
+impl<'c> BackendRoCursor<'c> for RoCursorImpl<'c> {
+ type Iter = IterImpl<'c>;
+
+ fn into_iter(self) -> Self::Iter {
+ let flattened = self
+ .0
+ .iter()
+ .flat_map(|(key, values)| values.map(move |value| (key, value)));
+ IterImpl(Box::new(flattened))
+ }
+
+ fn into_iter_from<K>(self, key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ let skipped = self.0.iter().skip_while(move |&(k, _)| k < key.as_ref());
+ let flattened = skipped.flat_map(|(key, values)| values.map(move |value| (key, value)));
+ IterImpl(Box::new(flattened))
+ }
+
+ fn into_iter_dup_of<K>(self, key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ let filtered = self.0.iter().filter(move |&(k, _)| k == key.as_ref());
+ let flattened = filtered.flat_map(|(key, values)| values.map(move |value| (key, value)));
+ IterImpl(Box::new(flattened))
+ }
+}
+
+#[derive(Debug)]
+pub struct RwCursorImpl<'c>(&'c mut Snapshot);
+
+impl<'c> BackendRoCursor<'c> for RwCursorImpl<'c> {
+ type Iter = IterImpl<'c>;
+
+ fn into_iter(self) -> Self::Iter {
+ unimplemented!()
+ }
+
+ fn into_iter_from<K>(self, _key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ unimplemented!()
+ }
+
+ fn into_iter_dup_of<K>(self, _key: K) -> Self::Iter
+ where
+ K: AsRef<[u8]> + 'c,
+ {
+ unimplemented!()
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/database.rs b/third_party/rust/rkv/src/backend/impl_safe/database.rs
new file mode 100644
index 0000000000..f8eb4aef76
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/database.rs
@@ -0,0 +1,41 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use id_arena::Id;
+use serde_derive::{Deserialize, Serialize};
+
+use super::{snapshot::Snapshot, DatabaseFlagsImpl};
+use crate::backend::traits::BackendDatabase;
+
+#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
+pub struct DatabaseImpl(pub(crate) Id<Database>);
+
+impl BackendDatabase for DatabaseImpl {}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Database {
+ snapshot: Snapshot,
+}
+
+impl Database {
+ pub(crate) fn new(flags: Option<DatabaseFlagsImpl>, snapshot: Option<Snapshot>) -> Database {
+ Database {
+ snapshot: snapshot.unwrap_or_else(|| Snapshot::new(flags)),
+ }
+ }
+
+ pub(crate) fn snapshot(&self) -> Snapshot {
+ self.snapshot.clone()
+ }
+
+ pub(crate) fn replace(&mut self, snapshot: Snapshot) -> Snapshot {
+ std::mem::replace(&mut self.snapshot, snapshot)
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/environment.rs b/third_party/rust/rkv/src/backend/impl_safe/environment.rs
new file mode 100644
index 0000000000..f9c44cf664
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/environment.rs
@@ -0,0 +1,351 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use std::{
+ borrow::Cow,
+ collections::HashMap,
+ fs,
+ ops::DerefMut,
+ path::{Path, PathBuf},
+ sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard},
+};
+
+use id_arena::Arena;
+use log::warn;
+
+use super::{
+ database::Database, DatabaseFlagsImpl, DatabaseImpl, EnvironmentFlagsImpl, ErrorImpl, InfoImpl,
+ RoTransactionImpl, RwTransactionImpl, StatImpl,
+};
+use crate::backend::common::RecoveryStrategy;
+use crate::backend::traits::{BackendEnvironment, BackendEnvironmentBuilder};
+
+const DEFAULT_DB_FILENAME: &str = "data.safe.bin";
+const DEFAULT_CORRUPT_DB_EXTENSION: &str = "bin.corrupt";
+
+type DatabaseArena = Arena<Database>;
+type DatabaseNameMap = HashMap<Option<String>, DatabaseImpl>;
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+pub struct EnvironmentBuilderImpl {
+ flags: EnvironmentFlagsImpl,
+ max_readers: Option<usize>,
+ max_dbs: Option<usize>,
+ map_size: Option<usize>,
+ make_dir_if_needed: bool,
+ corruption_recovery_strategy: RecoveryStrategy,
+}
+
+impl<'b> BackendEnvironmentBuilder<'b> for EnvironmentBuilderImpl {
+ type Environment = EnvironmentImpl;
+ type Error = ErrorImpl;
+ type Flags = EnvironmentFlagsImpl;
+
+ fn new() -> EnvironmentBuilderImpl {
+ EnvironmentBuilderImpl {
+ flags: EnvironmentFlagsImpl::empty(),
+ max_readers: None,
+ max_dbs: None,
+ map_size: None,
+ make_dir_if_needed: false,
+ corruption_recovery_strategy: RecoveryStrategy::Error,
+ }
+ }
+
+ fn set_flags<T>(&mut self, flags: T) -> &mut Self
+ where
+ T: Into<Self::Flags>,
+ {
+ self.flags = flags.into();
+ self
+ }
+
+ fn set_max_readers(&mut self, max_readers: u32) -> &mut Self {
+ self.max_readers = Some(max_readers as usize);
+ self
+ }
+
+ fn set_max_dbs(&mut self, max_dbs: u32) -> &mut Self {
+ self.max_dbs = Some(max_dbs as usize);
+ self
+ }
+
+ fn set_map_size(&mut self, map_size: usize) -> &mut Self {
+ self.map_size = Some(map_size);
+ self
+ }
+
+ fn set_make_dir_if_needed(&mut self, make_dir_if_needed: bool) -> &mut Self {
+ self.make_dir_if_needed = make_dir_if_needed;
+ self
+ }
+
+ fn set_corruption_recovery_strategy(&mut self, strategy: RecoveryStrategy) -> &mut Self {
+ self.corruption_recovery_strategy = strategy;
+ self
+ }
+
+ fn open(&self, path: &Path) -> Result<Self::Environment, Self::Error> {
+ // Technically NO_SUB_DIR should change these checks here, but they're both currently
+ // unimplemented with this storage backend.
+ if !path.is_dir() {
+ if !self.make_dir_if_needed {
+ return Err(ErrorImpl::UnsuitableEnvironmentPath(path.into()));
+ }
+ fs::create_dir_all(path)?;
+ }
+ let mut env = EnvironmentImpl::new(
+ path,
+ self.flags,
+ self.max_readers,
+ self.max_dbs,
+ self.map_size,
+ )?;
+ env.read_from_disk(self.corruption_recovery_strategy)?;
+ Ok(env)
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct EnvironmentDbs {
+ pub(crate) arena: DatabaseArena,
+ pub(crate) name_map: DatabaseNameMap,
+}
+
+#[derive(Debug)]
+pub(crate) struct EnvironmentDbsRefMut<'a> {
+ pub(crate) arena: &'a mut DatabaseArena,
+ pub(crate) name_map: &'a mut DatabaseNameMap,
+}
+
+impl<'a> From<&'a mut EnvironmentDbs> for EnvironmentDbsRefMut<'a> {
+ fn from(dbs: &mut EnvironmentDbs) -> EnvironmentDbsRefMut {
+ EnvironmentDbsRefMut {
+ arena: &mut dbs.arena,
+ name_map: &mut dbs.name_map,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct EnvironmentImpl {
+ path: PathBuf,
+ max_dbs: usize,
+ dbs: RwLock<EnvironmentDbs>,
+ ro_txns: Arc<()>,
+ rw_txns: Arc<()>,
+}
+
+impl EnvironmentImpl {
+ fn serialize(&self) -> Result<Vec<u8>, ErrorImpl> {
+ let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?;
+ let data: HashMap<_, _> = dbs
+ .name_map
+ .iter()
+ .map(|(name, id)| (name, &dbs.arena[id.0]))
+ .collect();
+ Ok(bincode::serialize(&data)?)
+ }
+
+ fn load(
+ path: &Path,
+ strategy: RecoveryStrategy,
+ ) -> Result<(DatabaseArena, DatabaseNameMap), ErrorImpl> {
+ let bytes = fs::read(path)?;
+
+ match Self::deserialize(&bytes) {
+ Ok((arena, name_map)) => Ok((arena, name_map)),
+ Err(err) => match strategy {
+ RecoveryStrategy::Error => Err(err),
+ RecoveryStrategy::Discard => Ok((DatabaseArena::new(), HashMap::new())),
+ RecoveryStrategy::Rename => {
+ let corrupted_path = path.with_extension(DEFAULT_CORRUPT_DB_EXTENSION);
+ fs::rename(path, corrupted_path)?;
+
+ Ok((DatabaseArena::new(), HashMap::new()))
+ }
+ },
+ }
+ }
+
+ fn deserialize(bytes: &[u8]) -> Result<(DatabaseArena, DatabaseNameMap), ErrorImpl> {
+ let mut arena = DatabaseArena::new();
+ let mut name_map = HashMap::new();
+ let data: HashMap<_, _> = bincode::deserialize(bytes)?;
+
+ for (name, db) in data {
+ name_map.insert(name, DatabaseImpl(arena.alloc(db)));
+ }
+ Ok((arena, name_map))
+ }
+}
+
+impl EnvironmentImpl {
+ pub(crate) fn new(
+ path: &Path,
+ flags: EnvironmentFlagsImpl,
+ max_readers: Option<usize>,
+ max_dbs: Option<usize>,
+ map_size: Option<usize>,
+ ) -> Result<EnvironmentImpl, ErrorImpl> {
+ if !flags.is_empty() {
+ warn!("Ignoring `flags={:?}`", flags);
+ }
+ if let Some(max_readers) = max_readers {
+ warn!("Ignoring `max_readers={}`", max_readers);
+ }
+ if let Some(map_size) = map_size {
+ warn!("Ignoring `map_size={}`", map_size);
+ }
+
+ Ok(EnvironmentImpl {
+ path: path.to_path_buf(),
+ max_dbs: max_dbs.unwrap_or(std::usize::MAX),
+ dbs: RwLock::new(EnvironmentDbs {
+ arena: DatabaseArena::new(),
+ name_map: HashMap::new(),
+ }),
+ ro_txns: Arc::new(()),
+ rw_txns: Arc::new(()),
+ })
+ }
+
+ pub(crate) fn read_from_disk(&mut self, strategy: RecoveryStrategy) -> Result<(), ErrorImpl> {
+ let mut path = Cow::from(&self.path);
+ if fs::metadata(&path)?.is_dir() {
+ path.to_mut().push(DEFAULT_DB_FILENAME);
+ };
+ if fs::metadata(&path).is_err() {
+ return Ok(());
+ };
+ let (arena, name_map) = Self::load(&path, strategy)?;
+ self.dbs = RwLock::new(EnvironmentDbs { arena, name_map });
+ Ok(())
+ }
+
+ pub(crate) fn write_to_disk(&self) -> Result<(), ErrorImpl> {
+ let mut path = Cow::from(&self.path);
+ if fs::metadata(&path)?.is_dir() {
+ path.to_mut().push(DEFAULT_DB_FILENAME);
+ };
+
+ // Write to a temp file first.
+ let tmp_path = path.with_extension("tmp");
+ fs::write(&tmp_path, self.serialize()?)?;
+
+ // Atomically move that file to the database file.
+ fs::rename(tmp_path, path)?;
+ Ok(())
+ }
+
+ pub(crate) fn dbs(&self) -> Result<RwLockReadGuard<EnvironmentDbs>, ErrorImpl> {
+ self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)
+ }
+
+ pub(crate) fn dbs_mut(&self) -> Result<RwLockWriteGuard<EnvironmentDbs>, ErrorImpl> {
+ self.dbs.write().map_err(|_| ErrorImpl::EnvPoisonError)
+ }
+}
+
+impl<'e> BackendEnvironment<'e> for EnvironmentImpl {
+ type Database = DatabaseImpl;
+ type Error = ErrorImpl;
+ type Flags = DatabaseFlagsImpl;
+ type Info = InfoImpl;
+ type RoTransaction = RoTransactionImpl<'e>;
+ type RwTransaction = RwTransactionImpl<'e>;
+ type Stat = StatImpl;
+
+ fn get_dbs(&self) -> Result<Vec<Option<String>>, Self::Error> {
+ let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?;
+ Ok(dbs.name_map.keys().map(|key| key.to_owned()).collect())
+ }
+
+ fn open_db(&self, name: Option<&str>) -> Result<Self::Database, Self::Error> {
+ if Arc::strong_count(&self.ro_txns) > 1 {
+ return Err(ErrorImpl::DbsIllegalOpen);
+ }
+ // TOOD: don't reallocate `name`.
+ let key = name.map(String::from);
+ let dbs = self.dbs.read().map_err(|_| ErrorImpl::EnvPoisonError)?;
+ let db = dbs.name_map.get(&key).ok_or(ErrorImpl::DbNotFoundError)?;
+ Ok(*db)
+ }
+
+ fn create_db(
+ &self,
+ name: Option<&str>,
+ flags: Self::Flags,
+ ) -> Result<Self::Database, Self::Error> {
+ if Arc::strong_count(&self.ro_txns) > 1 {
+ return Err(ErrorImpl::DbsIllegalOpen);
+ }
+ // TOOD: don't reallocate `name`.
+ let key = name.map(String::from);
+ let mut dbs = self.dbs.write().map_err(|_| ErrorImpl::EnvPoisonError)?;
+ if dbs.name_map.keys().filter_map(|k| k.as_ref()).count() >= self.max_dbs && name.is_some()
+ {
+ return Err(ErrorImpl::DbsFull);
+ }
+ let parts = EnvironmentDbsRefMut::from(dbs.deref_mut());
+ let arena = parts.arena;
+ let name_map = parts.name_map;
+ let id = name_map
+ .entry(key)
+ .or_insert_with(|| DatabaseImpl(arena.alloc(Database::new(Some(flags), None))));
+ Ok(*id)
+ }
+
+ fn begin_ro_txn(&'e self) -> Result<Self::RoTransaction, Self::Error> {
+ RoTransactionImpl::new(self, self.ro_txns.clone())
+ }
+
+ fn begin_rw_txn(&'e self) -> Result<Self::RwTransaction, Self::Error> {
+ RwTransactionImpl::new(self, self.rw_txns.clone())
+ }
+
+ fn sync(&self, force: bool) -> Result<(), Self::Error> {
+ warn!("Ignoring `force={}`", force);
+ self.write_to_disk()
+ }
+
+ fn stat(&self) -> Result<Self::Stat, Self::Error> {
+ Ok(StatImpl)
+ }
+
+ fn info(&self) -> Result<Self::Info, Self::Error> {
+ Ok(InfoImpl)
+ }
+
+ fn freelist(&self) -> Result<usize, Self::Error> {
+ unimplemented!()
+ }
+
+ fn load_ratio(&self) -> Result<Option<f32>, Self::Error> {
+ warn!("`load_ratio()` is irrelevant for this storage backend.");
+ Ok(None)
+ }
+
+ fn set_map_size(&self, size: usize) -> Result<(), Self::Error> {
+ warn!(
+ "`set_map_size({})` is ignored by this storage backend.",
+ size
+ );
+ Ok(())
+ }
+
+ fn get_files_on_disk(&self) -> Vec<PathBuf> {
+ // Technically NO_SUB_DIR and NO_LOCK should change this output, but
+ // they're both currently unimplemented with this storage backend.
+ let mut db_filename = self.path.clone();
+ db_filename.push(DEFAULT_DB_FILENAME);
+ vec![db_filename]
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/error.rs b/third_party/rust/rkv/src/backend/impl_safe/error.rs
new file mode 100644
index 0000000000..f086cdc954
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/error.rs
@@ -0,0 +1,79 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use std::{fmt, io, path::PathBuf};
+
+use bincode::Error as BincodeError;
+
+use crate::{backend::traits::BackendError, error::StoreError};
+
+#[derive(Debug)]
+pub enum ErrorImpl {
+ KeyValuePairNotFound,
+ EnvPoisonError,
+ DbsFull,
+ DbsIllegalOpen,
+ DbNotFoundError,
+ DbIsForeignError,
+ UnsuitableEnvironmentPath(PathBuf),
+ IoError(io::Error),
+ BincodeError(BincodeError),
+}
+
+impl BackendError for ErrorImpl {}
+
+impl fmt::Display for ErrorImpl {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ErrorImpl::KeyValuePairNotFound => write!(fmt, "KeyValuePairNotFound (safe mode)"),
+ ErrorImpl::EnvPoisonError => write!(fmt, "EnvPoisonError (safe mode)"),
+ ErrorImpl::DbsFull => write!(fmt, "DbsFull (safe mode)"),
+ ErrorImpl::DbsIllegalOpen => write!(fmt, "DbIllegalOpen (safe mode)"),
+ ErrorImpl::DbNotFoundError => write!(fmt, "DbNotFoundError (safe mode)"),
+ ErrorImpl::DbIsForeignError => write!(fmt, "DbIsForeignError (safe mode)"),
+ ErrorImpl::UnsuitableEnvironmentPath(_) => {
+ write!(fmt, "UnsuitableEnvironmentPath (safe mode)")
+ }
+ ErrorImpl::IoError(e) => e.fmt(fmt),
+ ErrorImpl::BincodeError(e) => e.fmt(fmt),
+ }
+ }
+}
+
+impl Into<StoreError> for ErrorImpl {
+ fn into(self) -> StoreError {
+ // The `StoreError::KeyValuePairBadSize` error is unused, because this
+ // backend supports keys and values of arbitrary sizes.
+ // The `StoreError::MapFull` and `StoreError::ReadersFull` are
+ // unimplemented yet, but they should be in the future.
+ match self {
+ ErrorImpl::KeyValuePairNotFound => StoreError::KeyValuePairNotFound,
+ ErrorImpl::BincodeError(_) => StoreError::FileInvalid,
+ ErrorImpl::DbsFull => StoreError::DbsFull,
+ ErrorImpl::UnsuitableEnvironmentPath(path) => {
+ StoreError::UnsuitableEnvironmentPath(path)
+ }
+ ErrorImpl::IoError(error) => StoreError::IoError(error),
+ _ => StoreError::SafeModeError(self),
+ }
+ }
+}
+
+impl From<io::Error> for ErrorImpl {
+ fn from(e: io::Error) -> ErrorImpl {
+ ErrorImpl::IoError(e)
+ }
+}
+
+impl From<BincodeError> for ErrorImpl {
+ fn from(e: BincodeError) -> ErrorImpl {
+ ErrorImpl::BincodeError(e)
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/flags.rs b/third_party/rust/rkv/src/backend/impl_safe/flags.rs
new file mode 100644
index 0000000000..5469b44a25
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/flags.rs
@@ -0,0 +1,124 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use bitflags::bitflags;
+use serde_derive::{Deserialize, Serialize};
+
+use crate::backend::{
+ common::{DatabaseFlags, EnvironmentFlags, WriteFlags},
+ traits::{BackendDatabaseFlags, BackendEnvironmentFlags, BackendFlags, BackendWriteFlags},
+};
+
+bitflags! {
+ #[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
+ pub struct EnvironmentFlagsImpl: u32 {
+ const NIL = 0b0000_0000;
+ }
+}
+
+impl BackendFlags for EnvironmentFlagsImpl {
+ fn empty() -> EnvironmentFlagsImpl {
+ EnvironmentFlagsImpl::empty()
+ }
+}
+
+impl BackendEnvironmentFlags for EnvironmentFlagsImpl {
+ fn set(&mut self, flag: EnvironmentFlags, value: bool) {
+ self.set(flag.into(), value)
+ }
+}
+
+impl Into<EnvironmentFlagsImpl> for EnvironmentFlags {
+ fn into(self) -> EnvironmentFlagsImpl {
+ match self {
+ EnvironmentFlags::FIXED_MAP => unimplemented!(),
+ EnvironmentFlags::NO_SUB_DIR => unimplemented!(),
+ EnvironmentFlags::WRITE_MAP => unimplemented!(),
+ EnvironmentFlags::READ_ONLY => unimplemented!(),
+ EnvironmentFlags::NO_META_SYNC => unimplemented!(),
+ EnvironmentFlags::NO_SYNC => unimplemented!(),
+ EnvironmentFlags::MAP_ASYNC => unimplemented!(),
+ EnvironmentFlags::NO_TLS => unimplemented!(),
+ EnvironmentFlags::NO_LOCK => unimplemented!(),
+ EnvironmentFlags::NO_READAHEAD => unimplemented!(),
+ EnvironmentFlags::NO_MEM_INIT => unimplemented!(),
+ }
+ }
+}
+
+bitflags! {
+ #[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
+ pub struct DatabaseFlagsImpl: u32 {
+ const NIL = 0b0000_0000;
+ #[cfg(feature = "db-dup-sort")]
+ const DUP_SORT = 0b0000_0001;
+ #[cfg(feature = "db-int-key")]
+ const INTEGER_KEY = 0b0000_0010;
+ }
+}
+
+impl BackendFlags for DatabaseFlagsImpl {
+ fn empty() -> DatabaseFlagsImpl {
+ DatabaseFlagsImpl::empty()
+ }
+}
+
+impl BackendDatabaseFlags for DatabaseFlagsImpl {
+ fn set(&mut self, flag: DatabaseFlags, value: bool) {
+ self.set(flag.into(), value)
+ }
+}
+
+impl Into<DatabaseFlagsImpl> for DatabaseFlags {
+ fn into(self) -> DatabaseFlagsImpl {
+ match self {
+ DatabaseFlags::REVERSE_KEY => unimplemented!(),
+ #[cfg(feature = "db-dup-sort")]
+ DatabaseFlags::DUP_SORT => DatabaseFlagsImpl::DUP_SORT,
+ #[cfg(feature = "db-dup-sort")]
+ DatabaseFlags::DUP_FIXED => unimplemented!(),
+ #[cfg(feature = "db-int-key")]
+ DatabaseFlags::INTEGER_KEY => DatabaseFlagsImpl::INTEGER_KEY,
+ DatabaseFlags::INTEGER_DUP => unimplemented!(),
+ DatabaseFlags::REVERSE_DUP => unimplemented!(),
+ }
+ }
+}
+
+bitflags! {
+ #[derive(Default, Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy)]
+ pub struct WriteFlagsImpl: u32 {
+ const NIL = 0b0000_0000;
+ }
+}
+
+impl BackendFlags for WriteFlagsImpl {
+ fn empty() -> WriteFlagsImpl {
+ WriteFlagsImpl::empty()
+ }
+}
+
+impl BackendWriteFlags for WriteFlagsImpl {
+ fn set(&mut self, flag: WriteFlags, value: bool) {
+ self.set(flag.into(), value)
+ }
+}
+
+impl Into<WriteFlagsImpl> for WriteFlags {
+ fn into(self) -> WriteFlagsImpl {
+ match self {
+ WriteFlags::NO_OVERWRITE => unimplemented!(),
+ WriteFlags::NO_DUP_DATA => unimplemented!(),
+ WriteFlags::CURRENT => unimplemented!(),
+ WriteFlags::APPEND => unimplemented!(),
+ WriteFlags::APPEND_DUP => unimplemented!(),
+ }
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/info.rs b/third_party/rust/rkv/src/backend/impl_safe/info.rs
new file mode 100644
index 0000000000..18f0f51da3
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/info.rs
@@ -0,0 +1,35 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use crate::backend::traits::BackendInfo;
+
+pub struct InfoImpl;
+
+impl BackendInfo for InfoImpl {
+ fn map_size(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn last_pgno(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn last_txnid(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn max_readers(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn num_readers(&self) -> usize {
+ unimplemented!()
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/iter.rs b/third_party/rust/rkv/src/backend/impl_safe/iter.rs
new file mode 100644
index 0000000000..a784c00a5b
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/iter.rs
@@ -0,0 +1,24 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use super::ErrorImpl;
+use crate::backend::traits::BackendIter;
+
+// FIXME: Use generics instead.
+pub struct IterImpl<'i>(pub(crate) Box<dyn Iterator<Item = (&'i [u8], &'i [u8])> + 'i>);
+
+impl<'i> BackendIter<'i> for IterImpl<'i> {
+ type Error = ErrorImpl;
+
+ #[allow(clippy::type_complexity)]
+ fn next(&mut self) -> Option<Result<(&'i [u8], &'i [u8]), Self::Error>> {
+ self.0.next().map(Ok)
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/snapshot.rs b/third_party/rust/rkv/src/backend/impl_safe/snapshot.rs
new file mode 100644
index 0000000000..95f5690d34
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/snapshot.rs
@@ -0,0 +1,141 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use std::{
+ collections::{BTreeMap, BTreeSet},
+ sync::Arc,
+};
+
+use serde_derive::{Deserialize, Serialize};
+
+use super::DatabaseFlagsImpl;
+
+type Key = Box<[u8]>;
+type Value = Box<[u8]>;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Snapshot {
+ flags: DatabaseFlagsImpl,
+ #[cfg(not(feature = "db-dup-sort"))]
+ map: Arc<BTreeMap<Key, Value>>,
+ #[cfg(feature = "db-dup-sort")]
+ map: Arc<BTreeMap<Key, BTreeSet<Value>>>,
+}
+
+impl Snapshot {
+ pub(crate) fn new(flags: Option<DatabaseFlagsImpl>) -> Snapshot {
+ Snapshot {
+ flags: flags.unwrap_or_default(),
+ map: Default::default(),
+ }
+ }
+
+ pub(crate) fn flags(&self) -> &DatabaseFlagsImpl {
+ &self.flags
+ }
+
+ pub(crate) fn clear(&mut self) {
+ self.map = Default::default();
+ }
+}
+
+#[cfg(not(feature = "db-dup-sort"))]
+impl Snapshot {
+ pub(crate) fn get(&self, key: &[u8]) -> Option<&[u8]> {
+ self.map.get(key).map(|value| value.as_ref())
+ }
+
+ pub(crate) fn put(&mut self, key: &[u8], value: &[u8]) {
+ let map = Arc::make_mut(&mut self.map);
+ map.insert(Box::from(key), Box::from(value));
+ }
+
+ pub(crate) fn del(&mut self, key: &[u8]) -> Option<()> {
+ let map = Arc::make_mut(&mut self.map);
+ map.remove(key).map(|_| ())
+ }
+
+ pub(crate) fn iter(&self) -> impl Iterator<Item = (&[u8], &[u8])> {
+ self.map
+ .iter()
+ .map(|(key, value)| (key.as_ref(), value.as_ref()))
+ }
+}
+
+#[cfg(feature = "db-dup-sort")]
+impl Snapshot {
+ pub(crate) fn get(&self, key: &[u8]) -> Option<&[u8]> {
+ self.map
+ .get(key)
+ .and_then(|v| v.iter().next())
+ .map(|v| v.as_ref())
+ }
+
+ pub(crate) fn put(&mut self, key: &[u8], value: &[u8]) {
+ let map = Arc::make_mut(&mut self.map);
+ match map.get_mut(key) {
+ None => {
+ let mut values = BTreeSet::new();
+ values.insert(Box::from(value));
+ map.insert(Box::from(key), values);
+ }
+ Some(values) => {
+ values.clear();
+ values.insert(Box::from(value));
+ }
+ }
+ }
+
+ pub(crate) fn del(&mut self, key: &[u8]) -> Option<()> {
+ let map = Arc::make_mut(&mut self.map);
+ match map.get_mut(key) {
+ None => None,
+ Some(values) => {
+ let was_empty = values.is_empty();
+ values.clear();
+ Some(()).filter(|_| !was_empty)
+ }
+ }
+ }
+
+ pub(crate) fn iter(&self) -> impl Iterator<Item = (&[u8], impl Iterator<Item = &[u8]>)> {
+ self.map
+ .iter()
+ .map(|(key, values)| (key.as_ref(), values.iter().map(|value| value.as_ref())))
+ }
+}
+
+#[cfg(feature = "db-dup-sort")]
+impl Snapshot {
+ pub(crate) fn put_dup(&mut self, key: &[u8], value: &[u8]) {
+ let map = Arc::make_mut(&mut self.map);
+ match map.get_mut(key) {
+ None => {
+ let mut values = BTreeSet::new();
+ values.insert(Box::from(value));
+ map.insert(Box::from(key), values);
+ }
+ Some(values) => {
+ values.insert(Box::from(value));
+ }
+ }
+ }
+
+ pub(crate) fn del_exact(&mut self, key: &[u8], value: &[u8]) -> Option<()> {
+ let map = Arc::make_mut(&mut self.map);
+ match map.get_mut(key) {
+ None => None,
+ Some(values) => {
+ let was_removed = values.remove(value);
+ Some(()).filter(|_| was_removed)
+ }
+ }
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/stat.rs b/third_party/rust/rkv/src/backend/impl_safe/stat.rs
new file mode 100644
index 0000000000..c117b56833
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/stat.rs
@@ -0,0 +1,39 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+
+use crate::backend::traits::BackendStat;
+
+pub struct StatImpl;
+
+impl BackendStat for StatImpl {
+ fn page_size(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn depth(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn branch_pages(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn leaf_pages(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn overflow_pages(&self) -> usize {
+ unimplemented!()
+ }
+
+ fn entries(&self) -> usize {
+ unimplemented!()
+ }
+}
diff --git a/third_party/rust/rkv/src/backend/impl_safe/transaction.rs b/third_party/rust/rkv/src/backend/impl_safe/transaction.rs
new file mode 100644
index 0000000000..d37352a42d
--- /dev/null
+++ b/third_party/rust/rkv/src/backend/impl_safe/transaction.rs
@@ -0,0 +1,208 @@
+// Copyright 2018-2019 Mozilla
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+// this file except in compliance with the License. You may obtain a copy of the
+// License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software distributed
+// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+// CONDITIONS OF ANY KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations under the License.
+#![allow(dead_code)] // TODO: Get rid of unused struct members
+
+use std::{collections::HashMap, sync::Arc};
+
+use super::{
+ snapshot::Snapshot, DatabaseImpl, EnvironmentImpl, ErrorImpl, RoCursorImpl, WriteFlagsImpl,
+};
+use crate::backend::traits::{
+ BackendRoCursorTransaction, BackendRoTransaction, BackendRwCursorTransaction,
+ BackendRwTransaction,
+};
+
+#[derive(Debug)]
+pub struct RoTransactionImpl<'t> {
+ env: &'t EnvironmentImpl,
+ snapshots: HashMap<DatabaseImpl, Snapshot>,
+ idx: Arc<()>,
+}
+
+impl<'t> RoTransactionImpl<'t> {
+ pub(crate) fn new(
+ env: &'t EnvironmentImpl,
+ idx: Arc<()>,
+ ) -> Result<RoTransactionImpl<'t>, ErrorImpl> {
+ let snapshots = env
+ .dbs()?
+ .arena
+ .iter()
+ .map(|(id, db)| (DatabaseImpl(id), db.snapshot()))
+ .collect();
+ Ok(RoTransactionImpl {
+ env,
+ snapshots,
+ idx,
+ })
+ }
+}
+
+impl<'t> BackendRoTransaction for RoTransactionImpl<'t> {
+ type Database = DatabaseImpl;
+ type Error = ErrorImpl;
+
+ fn get(&self, db: &Self::Database, key: &[u8]) -> Result<&[u8], Self::Error> {
+ let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
+ snapshot.get(key).ok_or(ErrorImpl::KeyValuePairNotFound)
+ }
+
+ fn abort(self) {
+ // noop
+ }
+}
+
+impl<'t> BackendRoCursorTransaction<'t> for RoTransactionImpl<'t> {
+ type RoCursor = RoCursorImpl<'t>;
+
+ fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
+ let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
+ Ok(RoCursorImpl(snapshot))
+ }
+}
+
+#[derive(Debug)]
+pub struct RwTransactionImpl<'t> {
+ env: &'t EnvironmentImpl,
+ snapshots: HashMap<DatabaseImpl, Snapshot>,
+ idx: Arc<()>,
+}
+
+impl<'t> RwTransactionImpl<'t> {
+ pub(crate) fn new(
+ env: &'t EnvironmentImpl,
+ idx: Arc<()>,
+ ) -> Result<RwTransactionImpl<'t>, ErrorImpl> {
+ let snapshots = env
+ .dbs()?
+ .arena
+ .iter()
+ .map(|(id, db)| (DatabaseImpl(id), db.snapshot()))
+ .collect();
+ Ok(RwTransactionImpl {
+ env,
+ snapshots,
+ idx,
+ })
+ }
+}
+
+impl<'t> BackendRwTransaction for RwTransactionImpl<'t> {
+ type Database = DatabaseImpl;
+ type Error = ErrorImpl;
+ type Flags = WriteFlagsImpl;
+
+ fn get(&self, db: &Self::Database, key: &[u8]) -> Result<&[u8], Self::Error> {
+ let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
+ snapshot.get(key).ok_or(ErrorImpl::KeyValuePairNotFound)
+ }
+
+ #[cfg(not(feature = "db-dup-sort"))]
+ fn put(
+ &mut self,
+ db: &Self::Database,
+ key: &[u8],
+ value: &[u8],
+ _flags: Self::Flags,
+ ) -> Result<(), Self::Error> {
+ let snapshot = self
+ .snapshots
+ .get_mut(db)
+ .ok_or_else(|| ErrorImpl::DbIsForeignError)?;
+ snapshot.put(key, value);
+ Ok(())
+ }
+
+ #[cfg(feature = "db-dup-sort")]
+ fn put(
+ &mut self,
+ db: &Self::Database,
+ key: &[u8],
+ value: &[u8],
+ _flags: Self::Flags,
+ ) -> Result<(), Self::Error> {
+ use super::DatabaseFlagsImpl;
+ let snapshot = self
+ .snapshots
+ .get_mut(db)
+ .ok_or(ErrorImpl::DbIsForeignError)?;
+ if snapshot.flags().contains(DatabaseFlagsImpl::DUP_SORT) {
+ snapshot.put_dup(key, value);
+ } else {
+ snapshot.put(key, value);
+ }
+ Ok(())
+ }
+
+ #[cfg(not(feature = "db-dup-sort"))]
+ fn del(&mut self, db: &Self::Database, key: &[u8]) -> Result<(), Self::Error> {
+ let snapshot = self
+ .snapshots
+ .get_mut(db)
+ .ok_or_else(|| ErrorImpl::DbIsForeignError)?;
+ let deleted = snapshot.del(key);
+ Ok(deleted.ok_or_else(|| ErrorImpl::KeyValuePairNotFound)?)
+ }
+
+ #[cfg(feature = "db-dup-sort")]
+ fn del(
+ &mut self,
+ db: &Self::Database,
+ key: &[u8],
+ value: Option<&[u8]>,
+ ) -> Result<(), Self::Error> {
+ use super::DatabaseFlagsImpl;
+ let snapshot = self
+ .snapshots
+ .get_mut(db)
+ .ok_or(ErrorImpl::DbIsForeignError)?;
+ let deleted = match (value, snapshot.flags()) {
+ (Some(value), flags) if flags.contains(DatabaseFlagsImpl::DUP_SORT) => {
+ snapshot.del_exact(key, value)
+ }
+ _ => snapshot.del(key),
+ };
+ deleted.ok_or(ErrorImpl::KeyValuePairNotFound)
+ }
+
+ fn clear_db(&mut self, db: &Self::Database) -> Result<(), Self::Error> {
+ let snapshot = self
+ .snapshots
+ .get_mut(db)
+ .ok_or(ErrorImpl::DbIsForeignError)?;
+ snapshot.clear();
+ Ok(())
+ }
+
+ fn commit(self) -> Result<(), Self::Error> {
+ let mut dbs = self.env.dbs_mut()?;
+
+ for (id, snapshot) in self.snapshots {
+ let db = dbs.arena.get_mut(id.0).ok_or(ErrorImpl::DbIsForeignError)?;
+ db.replace(snapshot);
+ }
+
+ drop(dbs);
+ self.env.write_to_disk()
+ }
+
+ fn abort(self) {
+ // noop
+ }
+}
+
+impl<'t> BackendRwCursorTransaction<'t> for RwTransactionImpl<'t> {
+ type RoCursor = RoCursorImpl<'t>;
+
+ fn open_ro_cursor(&'t self, db: &Self::Database) -> Result<Self::RoCursor, Self::Error> {
+ let snapshot = self.snapshots.get(db).ok_or(ErrorImpl::DbIsForeignError)?;
+ Ok(RoCursorImpl(snapshot))
+ }
+}