#[cfg(not(redb_no_std))]
use crate::BackendError;
use crate::StorageBackend;
use crate::io;
#[cfg(not(redb_no_std))]
use crate::io::Error;
use crate::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
#[cfg(not(redb_no_std))]
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::{Debug, Formatter};
#[cfg(not(redb_no_std))]
use core::ops::Bound;
#[cfg(not(redb_no_std))]
#[derive(Debug)]
pub(crate) struct ReadOnlyBackend {
inner: Box<dyn StorageBackend>,
}
#[cfg(not(redb_no_std))]
impl ReadOnlyBackend {
pub fn new(inner: Box<dyn StorageBackend>) -> Self {
Self { inner }
}
}
#[cfg(not(redb_no_std))]
impl StorageBackend for ReadOnlyBackend {
fn try_lock_range(&self, start: Bound<u64>, end: Bound<u64>) -> Result<bool, BackendError> {
self.inner.try_lock_range(start, end)
}
fn try_lock_shared_range(
&self,
start: Bound<u64>,
end: Bound<u64>,
) -> Result<bool, BackendError> {
self.inner.try_lock_shared_range(start, end)
}
fn lock_range(&self, start: Bound<u64>, end: Bound<u64>) -> Result<(), BackendError> {
self.inner.lock_range(start, end)
}
fn lock_shared_range(&self, start: Bound<u64>, end: Bound<u64>) -> Result<(), BackendError> {
self.inner.lock_shared_range(start, end)
}
fn unlock_range(&self, start: Bound<u64>, end: Bound<u64>) -> Result<(), BackendError> {
self.inner.unlock_range(start, end)
}
fn query_lock_range(&self, start: Bound<u64>, end: Bound<u64>) -> Result<bool, BackendError> {
self.inner.query_lock_range(start, end)
}
fn len(&self) -> Result<u64, Error> {
self.inner.len()
}
fn read(&self, offset: u64, out: &mut [u8]) -> Result<(), Error> {
self.inner.read(offset, out)
}
fn set_len(&self, _len: u64) -> Result<(), Error> {
unreachable!()
}
fn sync_data(&self) -> Result<(), Error> {
unreachable!()
}
fn write(&self, _offset: u64, _data: &[u8]) -> Result<(), Error> {
unreachable!()
}
fn close(&self) -> Result<(), Error> {
self.inner.close()
}
}
#[derive(Default)]
pub struct InMemoryBackend(RwLock<Vec<u8>>);
impl Debug for InMemoryBackend {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let guard = self.0.read().unwrap_or_else(|error| error.into_inner());
f.debug_struct("InMemoryBackend")
.field("len", &guard.len())
.finish()
}
}
impl InMemoryBackend {
fn out_of_range() -> io::Error {
io::invalid_input("Index out-of-range.")
}
}
impl InMemoryBackend {
pub fn new() -> Self {
Self::default()
}
fn read(&self) -> RwLockReadGuard<'_, Vec<u8>> {
self.0.read().expect("Could not acquire read lock.")
}
fn write(&self) -> RwLockWriteGuard<'_, Vec<u8>> {
self.0.write().expect("Could not acquire write lock.")
}
}
impl StorageBackend for InMemoryBackend {
fn len(&self) -> Result<u64, io::Error> {
Ok(self.read().len() as u64)
}
fn read(&self, offset: u64, out: &mut [u8]) -> Result<(), io::Error> {
let guard = self.read();
let offset = usize::try_from(offset).map_err(|_| Self::out_of_range())?;
if offset + out.len() <= guard.len() {
out.copy_from_slice(&guard[offset..offset + out.len()]);
Ok(())
} else {
Err(Self::out_of_range())
}
}
fn set_len(&self, len: u64) -> Result<(), io::Error> {
let mut guard = self.write();
let len = usize::try_from(len).map_err(|_| Self::out_of_range())?;
guard.resize(len, 0);
Ok(())
}
fn sync_data(&self) -> Result<(), io::Error> {
Ok(())
}
fn write(&self, offset: u64, data: &[u8]) -> Result<(), io::Error> {
let mut guard = self.write();
let offset = usize::try_from(offset).map_err(|_| Self::out_of_range())?;
if offset + data.len() <= guard.len() {
guard[offset..offset + data.len()].copy_from_slice(data);
Ok(())
} else {
Err(Self::out_of_range())
}
}
}
#[cfg(test)]
mod test {
use super::InMemoryBackend;
use crate::StorageBackend;
use crate::db::FULL_RANGE;
#[test]
fn default_locks_are_unsupported() {
let backend = InMemoryBackend::new();
for result in [
backend.try_lock_range(FULL_RANGE.0, FULL_RANGE.1).err(),
backend
.try_lock_shared_range(FULL_RANGE.0, FULL_RANGE.1)
.err(),
backend.lock_range(FULL_RANGE.0, FULL_RANGE.1).err(),
backend.lock_shared_range(FULL_RANGE.0, FULL_RANGE.1).err(),
backend.unlock_range(FULL_RANGE.0, FULL_RANGE.1).err(),
backend.query_lock_range(FULL_RANGE.0, FULL_RANGE.1).err(),
] {
let err = result.expect("the locks are unsupported");
assert!(matches!(err, crate::BackendError::Unsupported));
}
backend.set_len(1024).unwrap();
assert_eq!(backend.len().unwrap(), 1024);
}
#[test]
fn debug_reports_length_not_contents() {
let backend = InMemoryBackend::new();
backend.set_len(1024).unwrap();
assert_eq!(format!("{backend:?}"), "InMemoryBackend { len: 1024 }");
}
#[test]
#[cfg(panic = "unwind")]
fn debug_reads_through_a_poisoned_lock() {
let backend = std::sync::Arc::new(InMemoryBackend::new());
backend.set_len(512).unwrap();
let poisoner = backend.clone();
let result = std::thread::spawn(move || {
let _guard = poisoner.0.write();
panic!("poison the lock");
})
.join();
assert!(result.is_err());
assert_eq!(format!("{backend:?}"), "InMemoryBackend { len: 512 }");
}
}