Add new fs abstraction
parent
1cb35b6b36
commit
0573e80992
@ -0,0 +1,568 @@
|
|||||||
|
/*
|
||||||
|
* Created on Thu Feb 29 2024
|
||||||
|
*
|
||||||
|
* This file is a part of Skytable
|
||||||
|
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
|
||||||
|
* NoSQL database written by Sayan Nandan ("the Author") with the
|
||||||
|
* vision to provide flexibility in data modelling without compromising
|
||||||
|
* on performance, queryability or scalability.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
/*
|
||||||
|
file system
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::io::BufWriter;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use super::vfs::{VFileDescriptor, VirtualFS};
|
||||||
|
use {
|
||||||
|
crate::IoResult,
|
||||||
|
std::{
|
||||||
|
fs as std_fs,
|
||||||
|
io::{BufReader, Error, ErrorKind, Read, Seek, SeekFrom, Write},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct FileSystem {}
|
||||||
|
|
||||||
|
impl Default for FileSystem {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::instance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||||
|
pub enum FSContext {
|
||||||
|
Local,
|
||||||
|
Virtual,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileSystem {
|
||||||
|
pub fn instance() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
fn context() -> FSContext {
|
||||||
|
local! { static CTX: FSContext = FSContext::Virtual; }
|
||||||
|
local_ref!(CTX, |ctx| *ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileSystem {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read(path: &str) -> IoResult<Vec<u8>> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().read().get_data(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::read(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn create_dir(path: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_create_dir(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::create_dir(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn create_dir_all(path: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_create_dir_all(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::create_dir_all(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn remove_dir(path: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_delete_dir(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::remove_dir(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn remove_dir_all(path: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_delete_dir_all(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::remove_dir_all(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn remove_file(path: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_remove_file(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::remove_file(path)
|
||||||
|
}
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn rename(from: &str, to: &str) -> IoResult<()> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match Self::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => return VirtualFS::instance().write().fs_rename(from, to),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std_fs::rename(from, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file traits
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub trait FileRead {
|
||||||
|
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()>;
|
||||||
|
fn fread_exact_block<const N: usize>(&mut self) -> IoResult<[u8; N]> {
|
||||||
|
let mut blk = [0; N];
|
||||||
|
self.fread_exact(&mut blk).map(|_| blk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FileWrite {
|
||||||
|
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64>;
|
||||||
|
fn fwrite_all(&mut self, buf: &[u8]) -> IoResult<()> {
|
||||||
|
self.fwrite_all_count(buf).1
|
||||||
|
}
|
||||||
|
fn fwrite_all_count(&mut self, buf: &[u8]) -> (u64, IoResult<()>) {
|
||||||
|
let len = buf.len() as u64;
|
||||||
|
let mut written = 0;
|
||||||
|
while written != len {
|
||||||
|
match self.fwrite(buf) {
|
||||||
|
Ok(0) => {
|
||||||
|
return (
|
||||||
|
written,
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::WriteZero,
|
||||||
|
format!("could only write {} of {} bytes", written, buf.len()),
|
||||||
|
)
|
||||||
|
.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Ok(n) => written += n,
|
||||||
|
Err(e) => return (written, Err(e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(written, Ok(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FileWriteExt {
|
||||||
|
fn fsync_all(&mut self) -> IoResult<()>;
|
||||||
|
fn fsync_data(&mut self) -> IoResult<()>;
|
||||||
|
fn f_truncate(&mut self, new_size: u64) -> IoResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FileExt {
|
||||||
|
fn f_len(&self) -> IoResult<u64>;
|
||||||
|
fn f_cursor(&mut self) -> IoResult<u64>;
|
||||||
|
fn f_seek_start(&mut self, offset: u64) -> IoResult<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file impls
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl FileWrite for File {
|
||||||
|
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
|
||||||
|
self.f.fwrite(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileRead for File {
|
||||||
|
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
|
||||||
|
self.f.fread_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileWriteExt for File {
|
||||||
|
fn fsync_all(&mut self) -> IoResult<()> {
|
||||||
|
self.f.fsync_all()
|
||||||
|
}
|
||||||
|
fn fsync_data(&mut self) -> IoResult<()> {
|
||||||
|
self.f.fsync_data()
|
||||||
|
}
|
||||||
|
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
|
||||||
|
self.f.f_truncate(new_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileExt for File {
|
||||||
|
fn f_len(&self) -> IoResult<u64> {
|
||||||
|
self.f.f_len()
|
||||||
|
}
|
||||||
|
fn f_cursor(&mut self) -> IoResult<u64> {
|
||||||
|
self.f.f_cursor()
|
||||||
|
}
|
||||||
|
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
|
||||||
|
self.f.f_seek_start(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impls for local file
|
||||||
|
*/
|
||||||
|
|
||||||
|
trait LocalFile {
|
||||||
|
fn _mut(&mut self) -> &mut std_fs::File;
|
||||||
|
fn _ref(&self) -> &std_fs::File;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalFile for BufReader<std_fs::File> {
|
||||||
|
fn _mut(&mut self) -> &mut std_fs::File {
|
||||||
|
self.get_mut()
|
||||||
|
}
|
||||||
|
fn _ref(&self) -> &std_fs::File {
|
||||||
|
self.get_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LocalFile for std_fs::File {
|
||||||
|
fn _mut(&mut self) -> &mut std_fs::File {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
fn _ref(&self) -> &std_fs::File {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Write> FileWrite for W {
|
||||||
|
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
|
||||||
|
self.write(buf).map(|x| x as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Read> FileRead for R {
|
||||||
|
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
|
||||||
|
self.read_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Lf: LocalFile> FileWriteExt for Lf {
|
||||||
|
fn fsync_all(&mut self) -> IoResult<()> {
|
||||||
|
self._mut().sync_all()
|
||||||
|
}
|
||||||
|
fn fsync_data(&mut self) -> IoResult<()> {
|
||||||
|
self._mut().sync_data()
|
||||||
|
}
|
||||||
|
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
|
||||||
|
self._mut().set_len(new_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Lf: LocalFile> FileExt for Lf {
|
||||||
|
fn f_len(&self) -> IoResult<u64> {
|
||||||
|
self._ref().metadata().map(|md| md.len())
|
||||||
|
}
|
||||||
|
fn f_cursor(&mut self) -> IoResult<u64> {
|
||||||
|
self._mut().stream_position()
|
||||||
|
}
|
||||||
|
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
|
||||||
|
self._mut().seek(SeekFrom::Start(offset)).map(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
impls for vfile
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<Lf: FileWrite> FileWrite for AnyFile<Lf> {
|
||||||
|
fn fwrite(&mut self, buf: &[u8]) -> IoResult<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.fwrite(buf),
|
||||||
|
Self::Virtual(vf) => vf.get_mut(&mut VirtualFS::instance().write()).fwrite(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<Lf: FileRead> FileRead for AnyFile<Lf> {
|
||||||
|
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.fread_exact(buf),
|
||||||
|
Self::Virtual(vf) => vf
|
||||||
|
.get_mut(&mut VirtualFS::instance().write())
|
||||||
|
.fread_exact(buf),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<Lf: FileWriteExt> FileWriteExt for AnyFile<Lf> {
|
||||||
|
fn fsync_all(&mut self) -> IoResult<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.fsync_all(),
|
||||||
|
Self::Virtual(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn fsync_data(&mut self) -> IoResult<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.fsync_data(),
|
||||||
|
Self::Virtual(_) => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn f_truncate(&mut self, new_size: u64) -> IoResult<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.f_truncate(new_size),
|
||||||
|
Self::Virtual(vf) => vf
|
||||||
|
.get_mut(&mut VirtualFS::instance().write())
|
||||||
|
.truncate(new_size),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
impl<Lf: FileExt> FileExt for AnyFile<Lf> {
|
||||||
|
fn f_len(&self) -> IoResult<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.f_len(),
|
||||||
|
Self::Virtual(vf) => vf.get_ref(&VirtualFS::instance().read()).length(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn f_cursor(&mut self) -> IoResult<u64> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.f_cursor(),
|
||||||
|
Self::Virtual(vf) => vf.get_ref(&VirtualFS::instance().read()).cursor(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
|
||||||
|
match self {
|
||||||
|
Self::Local(lf) => lf.f_seek_start(offset),
|
||||||
|
Self::Virtual(vf) => vf
|
||||||
|
.get_mut(&mut VirtualFS::instance().write())
|
||||||
|
.seek_from_start(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
file abstraction
|
||||||
|
*/
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
enum AnyFile<Lf = std_fs::File> {
|
||||||
|
Local(Lf),
|
||||||
|
Virtual(VFileDescriptor),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct File {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile,
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: std_fs::File,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl File {
|
||||||
|
pub fn open(path: &str) -> IoResult<Self> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match FileSystem::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => {
|
||||||
|
return VirtualFS::instance()
|
||||||
|
.write()
|
||||||
|
.fs_fopen_rw(path)
|
||||||
|
.map(|f| Self {
|
||||||
|
f: AnyFile::Virtual(f),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file = std_fs::File::options().read(true).write(true).open(path)?;
|
||||||
|
Ok(Self {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile::Local(file),
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: file,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn create(path: &str) -> IoResult<Self> {
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match FileSystem::context() {
|
||||||
|
FSContext::Local => {}
|
||||||
|
FSContext::Virtual => {
|
||||||
|
return VirtualFS::instance()
|
||||||
|
.write()
|
||||||
|
.fs_fcreate_rw(path)
|
||||||
|
.map(|f| Self {
|
||||||
|
f: AnyFile::Virtual(f),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file = std_fs::File::options()
|
||||||
|
.create_new(true)
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)?;
|
||||||
|
Ok(Self {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile::Local(file),
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: file,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn into_buffered_reader(self) -> BufferedReader {
|
||||||
|
BufferedReader::new(self.f)
|
||||||
|
}
|
||||||
|
pub fn into_buffered_writer(self) -> BufferedWriter {
|
||||||
|
BufferedWriter::new(self.f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
buffered readers and writers
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub struct BufferedReader {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile<BufReader<std_fs::File>>,
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: BufReader<std_fs::File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferedReader {
|
||||||
|
fn new(#[cfg(test)] f: AnyFile<std_fs::File>, #[cfg(not(test))] f: std_fs::File) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: match f {
|
||||||
|
AnyFile::Local(lf) => AnyFile::Local(BufReader::new(lf)),
|
||||||
|
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
|
||||||
|
},
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: BufReader::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn into_inner(self) -> File {
|
||||||
|
File {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: match self.f {
|
||||||
|
AnyFile::Local(lf) => AnyFile::Local(lf.into_inner()),
|
||||||
|
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
|
||||||
|
},
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: self.f.into_inner(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileRead for BufferedReader {
|
||||||
|
fn fread_exact(&mut self, buf: &mut [u8]) -> IoResult<()> {
|
||||||
|
self.f.fread_exact(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileExt for BufferedReader {
|
||||||
|
fn f_len(&self) -> IoResult<u64> {
|
||||||
|
self.f.f_len()
|
||||||
|
}
|
||||||
|
fn f_cursor(&mut self) -> IoResult<u64> {
|
||||||
|
self.f.f_cursor()
|
||||||
|
}
|
||||||
|
fn f_seek_start(&mut self, offset: u64) -> IoResult<()> {
|
||||||
|
self.f.f_seek_start(offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BufferedWriter {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile<BufWriter<std_fs::File>>,
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: BufWriter<std_fs::File>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferedWriter {
|
||||||
|
pub fn into_inner(self) -> IoResult<File> {
|
||||||
|
let mut local;
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match self.f {
|
||||||
|
AnyFile::Local(lf) => local = lf,
|
||||||
|
AnyFile::Virtual(vf) => {
|
||||||
|
return Ok(File {
|
||||||
|
f: AnyFile::Virtual(vf),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
local = self.f;
|
||||||
|
}
|
||||||
|
local.flush()?;
|
||||||
|
let local = local.into_inner().unwrap();
|
||||||
|
Ok(File {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: AnyFile::Local(local),
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: local,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn flush(&mut self) -> IoResult<()> {
|
||||||
|
let local;
|
||||||
|
#[cfg(test)]
|
||||||
|
{
|
||||||
|
match self.f {
|
||||||
|
AnyFile::Local(ref mut l) => local = l,
|
||||||
|
AnyFile::Virtual(_) => return Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(test))]
|
||||||
|
{
|
||||||
|
local = &mut self.f;
|
||||||
|
}
|
||||||
|
local.flush()
|
||||||
|
}
|
||||||
|
fn new(#[cfg(test)] f: AnyFile<std_fs::File>, #[cfg(not(test))] f: std_fs::File) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(test)]
|
||||||
|
f: match f {
|
||||||
|
AnyFile::Local(lf) => AnyFile::Local(BufWriter::new(lf)),
|
||||||
|
AnyFile::Virtual(vf) => AnyFile::Virtual(vf),
|
||||||
|
},
|
||||||
|
#[cfg(not(test))]
|
||||||
|
f: BufWriter::new(f),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,205 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created on Sun Jan 07 2024
|
|
||||||
*
|
|
||||||
* This file is a part of Skytable
|
|
||||||
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
|
|
||||||
* NoSQL database written by Sayan Nandan ("the Author") with the
|
|
||||||
* vision to provide flexibility in data modelling without compromising
|
|
||||||
* on performance, queryability or scalability.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
use {
|
|
||||||
super::fs_traits::{
|
|
||||||
FSInterface, FileInterface, FileInterfaceBufWrite, FileInterfaceExt, FileInterfaceRead,
|
|
||||||
FileInterfaceWrite, FileInterfaceWriteExt, FileOpen,
|
|
||||||
},
|
|
||||||
crate::engine::RuntimeResult,
|
|
||||||
std::{
|
|
||||||
fs::{self, File},
|
|
||||||
io::{BufReader, BufWriter, Read, Seek, SeekFrom, Write},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
local fs impls
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// A type representing the host's local filesystem (or atleast where our data directory is)
|
|
||||||
pub struct LocalFS;
|
|
||||||
|
|
||||||
fn cvt<T, E1, E2: From<E1>>(r: Result<T, E1>) -> Result<T, E2> {
|
|
||||||
r.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FSInterface for LocalFS {
|
|
||||||
type File = File;
|
|
||||||
fn fs_remove_file(fpath: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::remove_file(fpath))
|
|
||||||
}
|
|
||||||
fn fs_rename(from: &str, to: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::rename(from, to))
|
|
||||||
}
|
|
||||||
fn fs_create_dir(fpath: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::create_dir(fpath))
|
|
||||||
}
|
|
||||||
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::create_dir_all(fpath))
|
|
||||||
}
|
|
||||||
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::remove_dir(fpath))
|
|
||||||
}
|
|
||||||
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()> {
|
|
||||||
cvt(fs::remove_dir_all(fpath))
|
|
||||||
}
|
|
||||||
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<super::fs_traits::FileOpen<Self::File>> {
|
|
||||||
let r = || -> Result<_, std::io::Error> {
|
|
||||||
let f = File::options()
|
|
||||||
.create(true)
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.open(fpath)?;
|
|
||||||
let md = f.metadata()?;
|
|
||||||
if md.len() == 0 {
|
|
||||||
Ok(FileOpen::Created(f))
|
|
||||||
} else {
|
|
||||||
Ok(FileOpen::Existing(f))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
cvt(r())
|
|
||||||
}
|
|
||||||
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File> {
|
|
||||||
let f = File::options().read(true).write(true).open(fpath)?;
|
|
||||||
Ok(f)
|
|
||||||
}
|
|
||||||
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File> {
|
|
||||||
let f = File::options()
|
|
||||||
.create_new(true)
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.open(fpath)?;
|
|
||||||
Ok(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
common impls for files
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl<R: Read> FileInterfaceRead for R {
|
|
||||||
fn fread_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()> {
|
|
||||||
cvt(self.read_exact(buf))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<W: Write> FileInterfaceWrite for W {
|
|
||||||
fn fwrite(&mut self, buf: &[u8]) -> RuntimeResult<u64> {
|
|
||||||
cvt(self.write(buf).map(|v| v as _))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
local file impls
|
|
||||||
*/
|
|
||||||
|
|
||||||
impl FileInterface for File {
|
|
||||||
type BufReader = BufReader<Self>;
|
|
||||||
type BufWriter = BufWriter<Self>;
|
|
||||||
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader> {
|
|
||||||
Ok(BufReader::new(self))
|
|
||||||
}
|
|
||||||
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter> {
|
|
||||||
Ok(BufWriter::new(self))
|
|
||||||
}
|
|
||||||
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self> {
|
|
||||||
Ok(r.into_inner())
|
|
||||||
}
|
|
||||||
fn downgrade_writer(mut r: Self::BufWriter) -> RuntimeResult<Self> {
|
|
||||||
// TODO(@ohsayan): maybe we'll want to explicitly handle not syncing this?
|
|
||||||
r.flush()?;
|
|
||||||
let (me, err) = r.into_parts();
|
|
||||||
match err {
|
|
||||||
Ok(x) if x.is_empty() => Ok(me),
|
|
||||||
Ok(_) | Err(_) => {
|
|
||||||
return Err(std::io::Error::new(
|
|
||||||
std::io::ErrorKind::Other,
|
|
||||||
"failed to flush data from buffer into sink",
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait for handling wrappers of [`std::fs::File`]
|
|
||||||
trait AsLocalFile {
|
|
||||||
fn file(&self) -> &File;
|
|
||||||
fn file_mut(&mut self) -> &mut File;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsLocalFile for File {
|
|
||||||
fn file(&self) -> &File {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
fn file_mut(&mut self) -> &mut File {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsLocalFile for BufReader<File> {
|
|
||||||
fn file(&self) -> &File {
|
|
||||||
self.get_ref()
|
|
||||||
}
|
|
||||||
fn file_mut(&mut self) -> &mut File {
|
|
||||||
self.get_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsLocalFile for BufWriter<File> {
|
|
||||||
fn file(&self) -> &File {
|
|
||||||
self.get_ref()
|
|
||||||
}
|
|
||||||
fn file_mut(&mut self) -> &mut File {
|
|
||||||
self.get_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileInterfaceBufWrite for BufWriter<File> {
|
|
||||||
fn sync_write_cache(&mut self) -> RuntimeResult<()> {
|
|
||||||
// TODO(@ohsayan): maybe we'll want to explicitly handle not syncing this?
|
|
||||||
cvt(self.flush())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<F: AsLocalFile> FileInterfaceExt for F {
|
|
||||||
fn fext_length(&self) -> RuntimeResult<u64> {
|
|
||||||
Ok(self.file().metadata()?.len())
|
|
||||||
}
|
|
||||||
fn fext_cursor(&mut self) -> RuntimeResult<u64> {
|
|
||||||
cvt(self.file_mut().stream_position())
|
|
||||||
}
|
|
||||||
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()> {
|
|
||||||
cvt(self.file_mut().seek(SeekFrom::Start(by)).map(|_| ()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileInterfaceWriteExt for File {
|
|
||||||
fn fwext_truncate_to(&mut self, to: u64) -> RuntimeResult<()> {
|
|
||||||
cvt(self.set_len(to))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created on Sun Jan 07 2024
|
|
||||||
*
|
|
||||||
* This file is a part of Skytable
|
|
||||||
* Skytable (formerly known as TerrabaseDB or Skybase) is a free and open-source
|
|
||||||
* NoSQL database written by Sayan Nandan ("the Author") with the
|
|
||||||
* vision to provide flexibility in data modelling without compromising
|
|
||||||
* on performance, queryability or scalability.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2024, Sayan Nandan <nandansayan@outlook.com>
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::engine::RuntimeResult,
|
|
||||||
std::io::{Error as IoError, ErrorKind as IoErrorKind},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
|
||||||
/// Result of opening a file
|
|
||||||
/// - Created: newly created file
|
|
||||||
/// - Existing: existing file that was reopened
|
|
||||||
pub enum FileOpen<CF, EF = CF> {
|
|
||||||
/// new file
|
|
||||||
Created(CF),
|
|
||||||
/// existing file
|
|
||||||
Existing(EF),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FSInterface {
|
|
||||||
// settings
|
|
||||||
/// set to false if the file system is a special device like `/dev/null`
|
|
||||||
const NOT_NULL: bool = true;
|
|
||||||
// types
|
|
||||||
/// the file type that is returned by this file system
|
|
||||||
type File: FileInterface;
|
|
||||||
// functions
|
|
||||||
/// Remove a file
|
|
||||||
fn fs_remove_file(fpath: &str) -> RuntimeResult<()>;
|
|
||||||
/// Rename a file
|
|
||||||
fn fs_rename(from: &str, to: &str) -> RuntimeResult<()>;
|
|
||||||
/// Create a directory
|
|
||||||
fn fs_create_dir(fpath: &str) -> RuntimeResult<()>;
|
|
||||||
/// Create a directory and all corresponding path components
|
|
||||||
fn fs_create_dir_all(fpath: &str) -> RuntimeResult<()>;
|
|
||||||
/// Delete a directory
|
|
||||||
fn fs_delete_dir(fpath: &str) -> RuntimeResult<()>;
|
|
||||||
/// Delete a directory and recursively remove all (if any) children
|
|
||||||
fn fs_delete_dir_all(fpath: &str) -> RuntimeResult<()>;
|
|
||||||
/// Open or create a file in R/W mode
|
|
||||||
///
|
|
||||||
/// This will:
|
|
||||||
/// - Create a file if it doesn't exist
|
|
||||||
/// - Open a file it it does exist
|
|
||||||
fn fs_fopen_or_create_rw(fpath: &str) -> RuntimeResult<FileOpen<Self::File>>;
|
|
||||||
/// Open an existing file
|
|
||||||
fn fs_fopen_rw(fpath: &str) -> RuntimeResult<Self::File>;
|
|
||||||
/// Create a new file
|
|
||||||
fn fs_fcreate_rw(fpath: &str) -> RuntimeResult<Self::File>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// File interface definition
|
|
||||||
pub trait FileInterface:
|
|
||||||
FileInterfaceRead + FileInterfaceWrite + FileInterfaceWriteExt + FileInterfaceExt + Sized
|
|
||||||
{
|
|
||||||
/// A buffered reader implementation
|
|
||||||
type BufReader: FileInterfaceRead + FileInterfaceExt;
|
|
||||||
/// A buffered writer implementation
|
|
||||||
type BufWriter: FileInterfaceBufWrite;
|
|
||||||
/// Get a buffered reader for this file
|
|
||||||
fn upgrade_to_buffered_reader(self) -> RuntimeResult<Self::BufReader>;
|
|
||||||
/// Get a buffered writer for this file
|
|
||||||
fn upgrade_to_buffered_writer(self) -> RuntimeResult<Self::BufWriter>;
|
|
||||||
/// Get the file back from the buffered reader
|
|
||||||
fn downgrade_reader(r: Self::BufReader) -> RuntimeResult<Self>;
|
|
||||||
/// Get the file back from the buffered writer
|
|
||||||
fn downgrade_writer(r: Self::BufWriter) -> RuntimeResult<Self>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait FileInterfaceBufWrite: FileInterfaceWrite + FileInterfaceExt {
|
|
||||||
fn sync_write_cache(&mut self) -> RuntimeResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Readable object
|
|
||||||
pub trait FileInterfaceRead {
|
|
||||||
/// Read in a block of the exact given length
|
|
||||||
fn fread_exact_block<const N: usize>(&mut self) -> RuntimeResult<[u8; N]> {
|
|
||||||
let mut ret = [0u8; N];
|
|
||||||
self.fread_exact(&mut ret)?;
|
|
||||||
Ok(ret)
|
|
||||||
}
|
|
||||||
/// Read in `n` bytes to fill the given buffer
|
|
||||||
fn fread_exact(&mut self, buf: &mut [u8]) -> RuntimeResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writable object
|
|
||||||
pub trait FileInterfaceWrite {
|
|
||||||
/// Attempt to write the buffer into this object, returning the number of bytes that were
|
|
||||||
/// written. It is **NOT GUARANTEED THAT ALL DATA WILL BE WRITTEN**
|
|
||||||
fn fwrite(&mut self, buf: &[u8]) -> RuntimeResult<u64>;
|
|
||||||
/// Attempt to write the entire buffer into this object, returning the number of bytes written
|
|
||||||
///
|
|
||||||
/// It is guaranteed that if the [`Result`] returned is [`Ok(())`], then the entire buffer was
|
|
||||||
/// written to disk.
|
|
||||||
fn fwrite_all_count(&mut self, buf: &[u8]) -> (u64, RuntimeResult<()>) {
|
|
||||||
let len = buf.len() as u64;
|
|
||||||
let mut written = 0;
|
|
||||||
while written != len {
|
|
||||||
match self.fwrite(buf) {
|
|
||||||
Ok(0) => {
|
|
||||||
return (
|
|
||||||
written,
|
|
||||||
Err(IoError::new(
|
|
||||||
IoErrorKind::WriteZero,
|
|
||||||
format!("could only write {} of {} bytes", written, buf.len()),
|
|
||||||
)
|
|
||||||
.into()),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Ok(n) => written += n,
|
|
||||||
Err(e) => return (written, Err(e)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(written, Ok(()))
|
|
||||||
}
|
|
||||||
/// Attempt to write the entire buffer into this object
|
|
||||||
///
|
|
||||||
/// If this return [`Ok(())`] then it is guaranteed that all bytes have been written
|
|
||||||
fn fw_write_all(&mut self, buf: &[u8]) -> RuntimeResult<()> {
|
|
||||||
self.fwrite_all_count(buf).1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advanced write traits
|
|
||||||
pub trait FileInterfaceWriteExt {
|
|
||||||
/// Sync data and metadata for this file
|
|
||||||
fn fwext_sync_all(&mut self) -> RuntimeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Sync data for this file
|
|
||||||
fn fwext_sync_data(&mut self) -> RuntimeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Sync meta for this file
|
|
||||||
fn fwext_sync_meta(&mut self) -> RuntimeResult<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
/// Truncate the size of the file to the given size
|
|
||||||
///
|
|
||||||
/// - If `to` > actual file length: the file is zero padded to fill `to - len`
|
|
||||||
/// - If `to` < actual file length: the file is trimmed to the size `to`
|
|
||||||
fn fwext_truncate_to(&mut self, to: u64) -> RuntimeResult<()>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advanced file access
|
|
||||||
pub trait FileInterfaceExt {
|
|
||||||
/// Returns the length of the file
|
|
||||||
fn fext_length(&self) -> RuntimeResult<u64>;
|
|
||||||
/// Returns the current cursor position of the file
|
|
||||||
fn fext_cursor(&mut self) -> RuntimeResult<u64>;
|
|
||||||
/// Seek by `from` bytes from the start of the file
|
|
||||||
fn fext_seek_ahead_from_start_by(&mut self, by: u64) -> RuntimeResult<()>;
|
|
||||||
}
|
|
Loading…
Reference in New Issue