pub mod drivers;
pub mod ext;
use crate::io_buffers::{IoVector, IoVectorMut};
use drivers::CommonStorageHelper;
use std::any::Any;
use std::fmt::{Debug, Display};
use std::future::Future;
use std::io;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
#[derive(Clone, Debug, Default)]
pub struct StorageOpenOptions {
pub(crate) filename: Option<PathBuf>,
pub(crate) writable: bool,
pub(crate) direct: bool,
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "musl")))]
pub(crate) write_dontcache: bool,
#[cfg(target_os = "macos")]
pub(crate) relaxed_sync: bool,
}
#[derive(Clone, Debug)]
pub struct StorageCreateOptions {
pub(crate) open_opts: StorageOpenOptions,
pub(crate) size: u64,
pub(crate) prealloc_mode: PreallocateMode,
pub(crate) overwrite: bool,
}
pub trait Storage: Debug + Display + Send + Sized + Sync {
#[allow(async_fn_in_trait)] async fn open(_opts: StorageOpenOptions) -> io::Result<Self> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
format!(
"Cannot open storage objects of type {}",
std::any::type_name::<Self>()
),
))
}
#[cfg(feature = "sync-wrappers")]
fn open_sync(opts: StorageOpenOptions) -> io::Result<Self> {
tokio::runtime::Builder::new_current_thread()
.build()?
.block_on(Self::open(opts))
}
#[allow(async_fn_in_trait)] async fn create_open(_opts: StorageCreateOptions) -> io::Result<Self> {
Err(io::Error::new(
io::ErrorKind::Unsupported,
format!(
"Cannot create storage objects of type {}",
std::any::type_name::<Self>()
),
))
}
#[allow(async_fn_in_trait)] async fn create(opts: StorageCreateOptions) -> io::Result<()> {
Self::create_open(opts).await?;
Ok(())
}
fn mem_align(&self) -> usize {
1
}
fn req_align(&self) -> usize {
1
}
fn zero_align(&self) -> usize {
1
}
fn discard_align(&self) -> usize {
1
}
fn size(&self) -> io::Result<u64>;
fn resolve_relative_path<P: AsRef<Path>>(&self, _relative: P) -> io::Result<PathBuf> {
Err(io::ErrorKind::Unsupported.into())
}
fn get_filename(&self) -> Option<PathBuf> {
None
}
#[allow(async_fn_in_trait)] async unsafe fn pure_readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()>;
#[allow(async_fn_in_trait)] async unsafe fn pure_writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()>;
#[allow(async_fn_in_trait)] async unsafe fn pure_write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
ext::write_full_zeroes(self, offset, length).await
}
#[allow(async_fn_in_trait)] async unsafe fn pure_write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
ext::write_full_zeroes(self, offset, length).await
}
#[allow(async_fn_in_trait)] async unsafe fn pure_discard(&self, _offset: u64, _length: u64) -> io::Result<()> {
Ok(())
}
#[allow(async_fn_in_trait)] async fn flush(&self) -> io::Result<()>;
#[allow(async_fn_in_trait)] async fn sync(&self) -> io::Result<()>;
#[allow(async_fn_in_trait)] async unsafe fn invalidate_cache(&self) -> io::Result<()>;
fn get_storage_helper(&self) -> &CommonStorageHelper;
#[allow(async_fn_in_trait)] async fn resize(&self, _new_size: u64, _prealloc_mode: PreallocateMode) -> io::Result<()> {
Err(io::ErrorKind::Unsupported.into())
}
}
pub trait DynStorage: Any + Debug + Display + Send + Sync {
fn dyn_mem_align(&self) -> usize;
fn dyn_req_align(&self) -> usize;
fn dyn_zero_align(&self) -> usize;
fn dyn_discard_align(&self) -> usize;
fn dyn_size(&self) -> io::Result<u64>;
fn dyn_resolve_relative_path(&self, relative: &Path) -> io::Result<PathBuf>;
fn dyn_get_filename(&self) -> Option<PathBuf>;
unsafe fn dyn_pure_readv<'a>(
&'a self,
bufv: IoVectorMut<'a>,
offset: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + 'a>>;
unsafe fn dyn_pure_writev<'a>(
&'a self,
bufv: IoVector<'a>,
offset: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + 'a>>;
unsafe fn dyn_pure_write_zeroes(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
unsafe fn dyn_pure_write_allocated_zeroes(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
unsafe fn dyn_pure_discard(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
fn dyn_flush(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
fn dyn_sync(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
unsafe fn dyn_invalidate_cache(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
fn dyn_get_storage_helper(&self) -> &CommonStorageHelper;
fn dyn_resize(
&self,
new_size: u64,
prealloc_mode: PreallocateMode,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>>;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum PreallocateMode {
None,
Zero,
Allocate,
WriteData,
}
impl<S: Storage> Storage for &S {
fn mem_align(&self) -> usize {
(*self).mem_align()
}
fn req_align(&self) -> usize {
(*self).req_align()
}
fn zero_align(&self) -> usize {
(*self).zero_align()
}
fn discard_align(&self) -> usize {
(*self).discard_align()
}
fn size(&self) -> io::Result<u64> {
(*self).size()
}
fn resolve_relative_path<P: AsRef<Path>>(&self, relative: P) -> io::Result<PathBuf> {
(*self).resolve_relative_path(relative)
}
fn get_filename(&self) -> Option<PathBuf> {
(*self).get_filename()
}
async unsafe fn pure_readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()> {
unsafe { (*self).pure_readv(bufv, offset).await }
}
async unsafe fn pure_writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()> {
unsafe { (*self).pure_writev(bufv, offset).await }
}
async unsafe fn pure_write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { (*self).pure_write_zeroes(offset, length).await }
}
async unsafe fn pure_write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { (*self).pure_write_allocated_zeroes(offset, length).await }
}
async unsafe fn pure_discard(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { (*self).pure_discard(offset, length).await }
}
async fn flush(&self) -> io::Result<()> {
(*self).flush().await
}
async fn sync(&self) -> io::Result<()> {
(*self).sync().await
}
async unsafe fn invalidate_cache(&self) -> io::Result<()> {
unsafe { (*self).invalidate_cache().await }
}
fn get_storage_helper(&self) -> &CommonStorageHelper {
(*self).get_storage_helper()
}
async fn resize(&self, new_size: u64, prealloc_mode: PreallocateMode) -> io::Result<()> {
(*self).resize(new_size, prealloc_mode).await
}
}
impl<S: Storage + 'static> DynStorage for S {
fn dyn_mem_align(&self) -> usize {
<S as Storage>::mem_align(self)
}
fn dyn_req_align(&self) -> usize {
<S as Storage>::req_align(self)
}
fn dyn_zero_align(&self) -> usize {
<S as Storage>::zero_align(self)
}
fn dyn_discard_align(&self) -> usize {
<S as Storage>::discard_align(self)
}
fn dyn_size(&self) -> io::Result<u64> {
<S as Storage>::size(self)
}
fn dyn_resolve_relative_path(&self, relative: &Path) -> io::Result<PathBuf> {
<S as Storage>::resolve_relative_path(self, relative)
}
fn dyn_get_filename(&self) -> Option<PathBuf> {
<S as Storage>::get_filename(self)
}
unsafe fn dyn_pure_readv<'a>(
&'a self,
bufv: IoVectorMut<'a>,
offset: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + 'a>> {
Box::pin(unsafe { <S as Storage>::pure_readv(self, bufv, offset) })
}
unsafe fn dyn_pure_writev<'a>(
&'a self,
bufv: IoVector<'a>,
offset: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + 'a>> {
Box::pin(unsafe { <S as Storage>::pure_writev(self, bufv, offset) })
}
unsafe fn dyn_pure_write_zeroes(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(unsafe { <S as Storage>::pure_write_zeroes(self, offset, length) })
}
unsafe fn dyn_pure_write_allocated_zeroes(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(unsafe { <S as Storage>::pure_write_allocated_zeroes(self, offset, length) })
}
unsafe fn dyn_pure_discard(
&self,
offset: u64,
length: u64,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(unsafe { <S as Storage>::pure_discard(self, offset, length) })
}
fn dyn_flush(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(<S as Storage>::flush(self))
}
fn dyn_sync(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(<S as Storage>::sync(self))
}
unsafe fn dyn_invalidate_cache(&self) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(unsafe { <S as Storage>::invalidate_cache(self) })
}
fn dyn_get_storage_helper(&self) -> &CommonStorageHelper {
<S as Storage>::get_storage_helper(self)
}
fn dyn_resize(
&self,
new_size: u64,
prealloc_mode: PreallocateMode,
) -> Pin<Box<dyn Future<Output = io::Result<()>> + '_>> {
Box::pin(<S as Storage>::resize(self, new_size, prealloc_mode))
}
}
impl Storage for Box<dyn DynStorage> {
async fn open(opts: StorageOpenOptions) -> io::Result<Self> {
Ok(Box::new(crate::file::File::open(opts).await?))
}
async fn create_open(opts: StorageCreateOptions) -> io::Result<Self> {
Ok(Box::new(crate::file::File::create_open(opts).await?))
}
fn mem_align(&self) -> usize {
self.as_ref().dyn_mem_align()
}
fn req_align(&self) -> usize {
self.as_ref().dyn_req_align()
}
fn zero_align(&self) -> usize {
self.as_ref().dyn_zero_align()
}
fn discard_align(&self) -> usize {
self.as_ref().dyn_discard_align()
}
fn size(&self) -> io::Result<u64> {
self.as_ref().dyn_size()
}
fn resolve_relative_path<P: AsRef<Path>>(&self, relative: P) -> io::Result<PathBuf> {
self.as_ref().dyn_resolve_relative_path(relative.as_ref())
}
fn get_filename(&self) -> Option<PathBuf> {
self.as_ref().dyn_get_filename()
}
async unsafe fn pure_readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_readv(bufv, offset).await }
}
async unsafe fn pure_writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_writev(bufv, offset).await }
}
async unsafe fn pure_write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_write_zeroes(offset, length).await }
}
async unsafe fn pure_write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe {
self.as_ref()
.dyn_pure_write_allocated_zeroes(offset, length)
.await
}
}
async unsafe fn pure_discard(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_discard(offset, length).await }
}
async fn flush(&self) -> io::Result<()> {
self.as_ref().dyn_flush().await
}
async fn sync(&self) -> io::Result<()> {
self.as_ref().dyn_sync().await
}
async unsafe fn invalidate_cache(&self) -> io::Result<()> {
unsafe { self.as_ref().dyn_invalidate_cache().await }
}
fn get_storage_helper(&self) -> &CommonStorageHelper {
self.as_ref().dyn_get_storage_helper()
}
async fn resize(&self, new_size: u64, prealloc_mode: PreallocateMode) -> io::Result<()> {
self.as_ref().dyn_resize(new_size, prealloc_mode).await
}
}
impl Storage for Arc<dyn DynStorage> {
async fn open(opts: StorageOpenOptions) -> io::Result<Self> {
Box::<dyn DynStorage>::open(opts).await.map(Into::into)
}
async fn create_open(opts: StorageCreateOptions) -> io::Result<Self> {
Box::<dyn DynStorage>::create_open(opts)
.await
.map(Into::into)
}
fn mem_align(&self) -> usize {
self.as_ref().dyn_mem_align()
}
fn req_align(&self) -> usize {
self.as_ref().dyn_req_align()
}
fn zero_align(&self) -> usize {
self.as_ref().dyn_zero_align()
}
fn discard_align(&self) -> usize {
self.as_ref().dyn_discard_align()
}
fn size(&self) -> io::Result<u64> {
self.as_ref().dyn_size()
}
fn resolve_relative_path<P: AsRef<Path>>(&self, relative: P) -> io::Result<PathBuf> {
self.as_ref().dyn_resolve_relative_path(relative.as_ref())
}
fn get_filename(&self) -> Option<PathBuf> {
self.as_ref().dyn_get_filename()
}
async unsafe fn pure_readv(&self, bufv: IoVectorMut<'_>, offset: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_readv(bufv, offset) }.await
}
async unsafe fn pure_writev(&self, bufv: IoVector<'_>, offset: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_writev(bufv, offset) }.await
}
async unsafe fn pure_write_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_write_zeroes(offset, length) }.await
}
async unsafe fn pure_write_allocated_zeroes(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe {
self.as_ref()
.dyn_pure_write_allocated_zeroes(offset, length)
}
.await
}
async unsafe fn pure_discard(&self, offset: u64, length: u64) -> io::Result<()> {
unsafe { self.as_ref().dyn_pure_discard(offset, length) }.await
}
async fn flush(&self) -> io::Result<()> {
self.as_ref().dyn_flush().await
}
async fn sync(&self) -> io::Result<()> {
self.as_ref().dyn_sync().await
}
async unsafe fn invalidate_cache(&self) -> io::Result<()> {
unsafe { self.as_ref().dyn_invalidate_cache().await }
}
fn get_storage_helper(&self) -> &CommonStorageHelper {
self.as_ref().dyn_get_storage_helper()
}
async fn resize(&self, new_size: u64, prealloc_mode: PreallocateMode) -> io::Result<()> {
self.as_ref().dyn_resize(new_size, prealloc_mode).await
}
}
impl StorageOpenOptions {
pub fn new() -> Self {
StorageOpenOptions::default()
}
pub fn filename<P: AsRef<Path>>(mut self, filename: P) -> Self {
self.filename = Some(filename.as_ref().to_owned());
self
}
pub fn write(mut self, write: bool) -> Self {
self.writable = write;
self
}
pub fn direct(mut self, direct: bool) -> Self {
self.direct = direct;
self
}
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "musl")))]
pub fn write_dontcache(mut self, write_dontcache: bool) -> Self {
self.write_dontcache = write_dontcache;
self
}
#[cfg(target_os = "macos")]
pub fn relaxed_sync(mut self, relaxed_sync: bool) -> Self {
self.relaxed_sync = relaxed_sync;
self
}
pub fn get_filename(&self) -> Option<&Path> {
self.filename.as_deref()
}
pub fn get_writable(&self) -> bool {
self.writable
}
pub fn get_direct(&self) -> bool {
self.direct
}
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "musl")))]
pub fn get_write_dontcache(&self) -> bool {
self.write_dontcache
}
#[cfg(target_os = "macos")]
pub fn get_relaxed_sync(&self) -> bool {
self.relaxed_sync
}
}
impl StorageCreateOptions {
pub fn new() -> Self {
StorageCreateOptions::default()
}
pub fn filename<P: AsRef<Path>>(self, filename: P) -> Self {
self.modify_open_opts(|o| o.filename(filename))
}
pub fn size(mut self, size: u64) -> Self {
self.size = size;
self
}
pub fn preallocate(mut self, prealloc_mode: PreallocateMode) -> Self {
self.prealloc_mode = prealloc_mode;
self
}
pub fn overwrite(mut self, overwrite: bool) -> Self {
self.overwrite = overwrite;
self
}
pub fn modify_open_opts<F: FnOnce(StorageOpenOptions) -> StorageOpenOptions>(
mut self,
f: F,
) -> Self {
self.open_opts = f(self.open_opts);
self
}
pub fn get_filename(&self) -> Option<&Path> {
self.open_opts.filename.as_deref()
}
pub fn get_size(&self) -> u64 {
self.size
}
pub fn get_preallocate(&self) -> PreallocateMode {
self.prealloc_mode
}
pub fn get_overwrite(&self) -> bool {
self.overwrite
}
pub fn get_open_options(self) -> StorageOpenOptions {
self.open_opts
}
}
impl Default for StorageCreateOptions {
fn default() -> Self {
StorageCreateOptions {
open_opts: Default::default(),
size: 0,
prealloc_mode: PreallocateMode::None,
overwrite: false,
}
}
}