482 lines
12 KiB
Rust
482 lines
12 KiB
Rust
//! Various helper node implementations for convenience
|
|
use core::{marker::PhantomData, str::FromStr};
|
|
|
|
use alloc::{
|
|
string::{String, ToString},
|
|
sync::Arc,
|
|
vec::Vec,
|
|
};
|
|
|
|
use kernel_util::sync::IrqSafeSpinlock;
|
|
use yggdrasil_abi::{error::Error, io::OpenOptions};
|
|
|
|
use crate::{DirectoryOpenPosition, InstanceData};
|
|
|
|
use super::{CommonImpl, DirectoryImpl, Node, NodeFlags, NodeRef, RegularImpl, SymlinkImpl};
|
|
|
|
trait SliceRead {
|
|
fn read_slice(&self, offset: usize, buf: &mut [u8]) -> usize;
|
|
}
|
|
|
|
trait SliceWrite {
|
|
fn write_slice(&mut self, offset: usize, buf: &[u8]) -> usize;
|
|
}
|
|
|
|
trait AsInstanceData {
|
|
fn as_instance_data(&self) -> Vec<u8>;
|
|
}
|
|
|
|
/// Closure interface for reading a single value
|
|
pub trait ValueReadFn<T> = Fn() -> Result<T, Error> + Send + Sync;
|
|
/// Closure interface for writing a single value
|
|
pub trait ValueWriteFn<T> = Fn(T) -> Result<(), Error> + Send + Sync;
|
|
/// Closure interface for reading bytes
|
|
pub trait ReadFn = Fn(u64, &mut [u8]) -> Result<usize, Error> + Send + Sync;
|
|
/// Closure interface for writing bytes
|
|
pub trait WriteFn = Fn(u64, &[u8]) -> Result<usize, Error> + Send + Sync;
|
|
|
|
impl<T: AsRef<[u8]>> SliceRead for T {
|
|
fn read_slice(&self, pos: usize, buf: &mut [u8]) -> usize {
|
|
let value = self.as_ref();
|
|
if pos >= value.len() {
|
|
return 0;
|
|
}
|
|
let count = core::cmp::min(value.len() - pos, buf.len());
|
|
buf[..count].copy_from_slice(&value[pos..pos + count]);
|
|
count
|
|
}
|
|
}
|
|
|
|
impl SliceWrite for Vec<u8> {
|
|
fn write_slice(&mut self, offset: usize, buf: &[u8]) -> usize {
|
|
if offset + buf.len() > self.len() {
|
|
self.resize(offset + buf.len(), 0);
|
|
}
|
|
self[offset..offset + buf.len()].copy_from_slice(buf);
|
|
buf.len()
|
|
}
|
|
}
|
|
|
|
impl<T: ToString> AsInstanceData for T {
|
|
fn as_instance_data(&self) -> Vec<u8> {
|
|
self.to_string().as_bytes().to_vec()
|
|
}
|
|
}
|
|
|
|
enum FnNodeData {
|
|
Read(Vec<u8>),
|
|
Write(IrqSafeSpinlock<Vec<u8>>),
|
|
}
|
|
|
|
/// Allows read-only access to a value. The value is converted to a string representation when it's
|
|
/// read.
|
|
pub struct ReadOnlyFnValueNode<T: ToString, R: ValueReadFn<T>> {
|
|
read: R,
|
|
_pd: PhantomData<T>,
|
|
}
|
|
|
|
/// Allows read-write access to a value (but not both at the same time). The value is converted
|
|
/// to/from a string representation when it's read/written.
|
|
pub struct FnValueNode<T: ToString + FromStr, R: ValueReadFn<T>, W: ValueWriteFn<T>> {
|
|
read: R,
|
|
write: W,
|
|
_pd: PhantomData<T>,
|
|
}
|
|
|
|
/// Allows read-only access to a "functional file"
|
|
pub struct ReadOnlyFnNode<R: ReadFn> {
|
|
read: R,
|
|
}
|
|
|
|
/// In-memory directory using tree cache
|
|
pub struct MemoryDirectory;
|
|
/// In-memory symlink pointing to a fixed [Node]
|
|
pub struct FixedSymlink {
|
|
target: NodeRef,
|
|
}
|
|
|
|
impl<T, R> ReadOnlyFnValueNode<T, R>
|
|
where
|
|
T: ToString + Send + Sync + 'static,
|
|
R: ValueReadFn<T> + 'static,
|
|
{
|
|
/// Creates a new [ReadOnlyFnValueNode] with given read function
|
|
pub fn new(read: R) -> NodeRef {
|
|
Node::regular(
|
|
Self::new_impl(read),
|
|
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
|
)
|
|
}
|
|
|
|
const fn new_impl(read: R) -> Self {
|
|
Self {
|
|
read,
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, R> CommonImpl for ReadOnlyFnValueNode<T, R>
|
|
where
|
|
T: ToString + Send + Sync,
|
|
R: ValueReadFn<T>,
|
|
{
|
|
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
impl<T, R> RegularImpl for ReadOnlyFnValueNode<T, R>
|
|
where
|
|
T: ToString + Send + Sync,
|
|
R: ValueReadFn<T>,
|
|
{
|
|
fn open(
|
|
&self,
|
|
_node: &NodeRef,
|
|
opts: OpenOptions,
|
|
) -> Result<(u64, Option<InstanceData>), Error> {
|
|
if opts.contains(OpenOptions::WRITE) {
|
|
return Err(Error::ReadOnly);
|
|
}
|
|
let t = (self.read)()?;
|
|
Ok((0, Some(Arc::new(t.as_instance_data()))))
|
|
}
|
|
|
|
fn read(
|
|
&self,
|
|
_node: &NodeRef,
|
|
instance: Option<&InstanceData>,
|
|
pos: u64,
|
|
buf: &mut [u8],
|
|
) -> Result<usize, Error> {
|
|
let value = instance.unwrap().downcast_ref::<Vec<u8>>().unwrap();
|
|
Ok(value.read_slice(pos as usize, buf))
|
|
}
|
|
|
|
fn write(
|
|
&self,
|
|
_node: &NodeRef,
|
|
_instance: Option<&InstanceData>,
|
|
_pos: u64,
|
|
_buf: &[u8],
|
|
) -> Result<usize, Error> {
|
|
Err(Error::ReadOnly)
|
|
}
|
|
|
|
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
|
|
Err(Error::ReadOnly)
|
|
}
|
|
}
|
|
|
|
// Read-write FnNode
|
|
|
|
impl FnNodeData {
|
|
fn write() -> Self {
|
|
Self::Write(IrqSafeSpinlock::new(Vec::new()))
|
|
}
|
|
|
|
fn read<T: AsInstanceData>(value: T) -> Self {
|
|
Self::Read(value.as_instance_data())
|
|
}
|
|
|
|
fn as_read(&self) -> Result<&Vec<u8>, Error> {
|
|
match self {
|
|
Self::Read(r) => Ok(r),
|
|
Self::Write(_) => Err(Error::InvalidOperation),
|
|
}
|
|
}
|
|
|
|
fn as_write(&self) -> Result<&IrqSafeSpinlock<Vec<u8>>, Error> {
|
|
match self {
|
|
Self::Write(w) => Ok(w),
|
|
Self::Read(_) => Err(Error::InvalidOperation),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, R, W> FnValueNode<T, R, W>
|
|
where
|
|
T: ToString + FromStr + Send + Sync + 'static,
|
|
R: ValueReadFn<T> + 'static,
|
|
W: ValueWriteFn<T> + 'static,
|
|
{
|
|
/// Creates a new [FnValueNode] with given read and write functions
|
|
pub fn new(read: R, write: W) -> NodeRef {
|
|
Node::regular(
|
|
Self::new_impl(read, write),
|
|
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
|
)
|
|
}
|
|
|
|
const fn new_impl(read: R, write: W) -> Self {
|
|
Self {
|
|
read,
|
|
write,
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, R, W> CommonImpl for FnValueNode<T, R, W>
|
|
where
|
|
T: ToString + FromStr + Send + Sync,
|
|
R: ValueReadFn<T>,
|
|
W: ValueWriteFn<T>,
|
|
{
|
|
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
impl<T, R, W> RegularImpl for FnValueNode<T, R, W>
|
|
where
|
|
T: ToString + FromStr + Send + Sync,
|
|
R: ValueReadFn<T>,
|
|
W: ValueWriteFn<T>,
|
|
{
|
|
fn open(
|
|
&self,
|
|
_node: &NodeRef,
|
|
opts: OpenOptions,
|
|
) -> Result<(u64, Option<InstanceData>), Error> {
|
|
if opts.contains(OpenOptions::READ | OpenOptions::WRITE) {
|
|
Err(Error::InvalidOperation)
|
|
} else if opts.contains(OpenOptions::WRITE) {
|
|
Ok((0, Some(Arc::new(FnNodeData::write()))))
|
|
} else if opts.contains(OpenOptions::READ) {
|
|
let t = (self.read)()?;
|
|
Ok((0, Some(Arc::new(FnNodeData::read(t)))))
|
|
} else {
|
|
Err(Error::InvalidOperation)
|
|
}
|
|
}
|
|
|
|
fn close(&self, _node: &NodeRef, instance: Option<&InstanceData>) -> Result<(), Error> {
|
|
if let Ok(write) = instance
|
|
.unwrap()
|
|
.downcast_ref::<FnNodeData>()
|
|
.unwrap()
|
|
.as_write()
|
|
{
|
|
let write = write.lock();
|
|
// Flush write
|
|
let str = core::str::from_utf8(write.as_ref())
|
|
.map_err(|_| Error::InvalidArgument)?
|
|
.trim();
|
|
let t = T::from_str(str).map_err(|_| Error::InvalidArgument)?;
|
|
|
|
(self.write)(t)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn read(
|
|
&self,
|
|
_node: &NodeRef,
|
|
instance: Option<&InstanceData>,
|
|
pos: u64,
|
|
buf: &mut [u8],
|
|
) -> Result<usize, Error> {
|
|
let instance = instance.unwrap().downcast_ref::<FnNodeData>().unwrap();
|
|
Ok(instance.as_read()?.read_slice(pos as usize, buf))
|
|
}
|
|
|
|
fn write(
|
|
&self,
|
|
_node: &NodeRef,
|
|
instance: Option<&InstanceData>,
|
|
pos: u64,
|
|
buf: &[u8],
|
|
) -> Result<usize, Error> {
|
|
let instance = instance.unwrap().downcast_ref::<FnNodeData>().unwrap();
|
|
Ok(instance.as_write()?.lock().write_slice(pos as _, buf))
|
|
}
|
|
|
|
fn truncate(&self, _node: &NodeRef, _new_size: u64) -> Result<(), Error> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
// Byte read-only node
|
|
|
|
impl<R> ReadOnlyFnNode<R>
|
|
where
|
|
R: ReadFn + 'static,
|
|
{
|
|
/// Creates a new [ReadOnlyFnNode] with given read function
|
|
pub fn new(read: R) -> NodeRef {
|
|
Node::regular(Self { read }, NodeFlags::IN_MEMORY_PROPS)
|
|
}
|
|
}
|
|
|
|
impl<R> CommonImpl for ReadOnlyFnNode<R>
|
|
where
|
|
R: ReadFn,
|
|
{
|
|
fn size(&self, _node: &NodeRef) -> Result<u64, Error> {
|
|
Ok(0)
|
|
}
|
|
}
|
|
|
|
impl<R> RegularImpl for ReadOnlyFnNode<R>
|
|
where
|
|
R: ReadFn,
|
|
{
|
|
fn open(
|
|
&self,
|
|
_node: &NodeRef,
|
|
opts: OpenOptions,
|
|
) -> Result<(u64, Option<InstanceData>), Error> {
|
|
if opts.contains(OpenOptions::WRITE) {
|
|
return Err(Error::ReadOnly);
|
|
}
|
|
Ok((0, None))
|
|
}
|
|
|
|
fn read(
|
|
&self,
|
|
_node: &NodeRef,
|
|
_instance: Option<&InstanceData>,
|
|
pos: u64,
|
|
buf: &mut [u8],
|
|
) -> Result<usize, Error> {
|
|
(self.read)(pos, buf)
|
|
}
|
|
}
|
|
|
|
// In-memory directory
|
|
|
|
impl MemoryDirectory {
|
|
/// Creates a [MemoryDirectory] with no children
|
|
pub fn empty() -> NodeRef {
|
|
Node::directory(
|
|
MemoryDirectory,
|
|
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
|
)
|
|
}
|
|
}
|
|
|
|
impl CommonImpl for MemoryDirectory {}
|
|
impl DirectoryImpl for MemoryDirectory {
|
|
fn open(&self, _node: &NodeRef) -> Result<DirectoryOpenPosition, Error> {
|
|
Ok(DirectoryOpenPosition::FromCache)
|
|
}
|
|
}
|
|
|
|
// In-memory fixed symlink
|
|
|
|
impl CommonImpl for FixedSymlink {}
|
|
impl SymlinkImpl for FixedSymlink {
|
|
fn target(&self, _node: &NodeRef) -> Result<NodeRef, Error> {
|
|
Ok(self.target.clone())
|
|
}
|
|
}
|
|
|
|
/// Creates a read-only value node with given `value`
|
|
pub fn const_value_node<T>(value: T) -> NodeRef
|
|
where
|
|
T: ToString + Clone + Send + Sync + 'static,
|
|
{
|
|
ReadOnlyFnValueNode::new(move || Ok(value.clone()))
|
|
}
|
|
|
|
/// Creates a read-write value node with given `value`
|
|
pub fn value_node<T>(value: T) -> NodeRef
|
|
where
|
|
T: ToString + FromStr + Clone + Send + Sync + 'static,
|
|
{
|
|
let rd_state = Arc::new(IrqSafeSpinlock::new(value));
|
|
let wr_state = rd_state.clone();
|
|
|
|
FnValueNode::new(
|
|
move || Ok(rd_state.lock().clone()),
|
|
move |t| {
|
|
*wr_state.lock() = t;
|
|
Ok(())
|
|
},
|
|
)
|
|
}
|
|
|
|
/// Creates a read-only node with given read function
|
|
pub fn read_fn_node<R: ReadFn + 'static>(read: R) -> NodeRef {
|
|
ReadOnlyFnNode::new(read)
|
|
}
|
|
|
|
/// Creates an in-memory directory from the iterator
|
|
pub fn mdir<S: Into<String>, I: IntoIterator<Item = (S, NodeRef)>>(it: I) -> NodeRef {
|
|
let dir = Node::directory(
|
|
MemoryDirectory,
|
|
NodeFlags::IN_MEMORY_PROPS | NodeFlags::IN_MEMORY_SIZE,
|
|
);
|
|
for (name, node) in it {
|
|
dir.add_child(name, node).unwrap();
|
|
}
|
|
dir
|
|
}
|
|
|
|
/// Creates a static symlink pointing to given node
|
|
pub fn f_symlink(target: NodeRef) -> NodeRef {
|
|
Node::symlink(FixedSymlink { target }, NodeFlags::IN_MEMORY_PROPS)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use yggdrasil_abi::io::OpenOptions;
|
|
|
|
use crate::{
|
|
node::{
|
|
impls::{const_value_node, value_node},
|
|
AccessToken,
|
|
},
|
|
traits::{Read, Seek, Write},
|
|
};
|
|
|
|
#[test]
|
|
fn read_only_fn_node() {
|
|
let node = const_value_node("abcdef");
|
|
let file = node
|
|
.open(OpenOptions::READ, AccessToken::test_authorized())
|
|
.unwrap();
|
|
let mut buf = [0; 512];
|
|
|
|
assert_eq!(file.tell().unwrap(), 0);
|
|
|
|
assert_eq!(file.read(&mut buf[..3]).unwrap(), 3);
|
|
assert_eq!(&buf[..3], b"abc");
|
|
|
|
assert_eq!(file.read(&mut buf).unwrap(), 3);
|
|
assert_eq!(&buf[..3], b"def");
|
|
}
|
|
|
|
#[test]
|
|
fn fn_node() {
|
|
let node = value_node(1234);
|
|
let mut buf = [0; 512];
|
|
|
|
// Try read the value
|
|
let file = node
|
|
.open(OpenOptions::READ, AccessToken::test_authorized())
|
|
.unwrap();
|
|
assert_eq!(file.tell().unwrap(), 0);
|
|
assert_eq!(file.read(&mut buf).unwrap(), 4);
|
|
assert_eq!(&buf[..4], b"1234");
|
|
|
|
// Try write the value
|
|
let file = node
|
|
.open(OpenOptions::WRITE, AccessToken::test_authorized())
|
|
.unwrap();
|
|
assert_eq!(file.tell().unwrap(), 0);
|
|
assert_eq!(file.write(b"654321").unwrap(), 6);
|
|
drop(file);
|
|
|
|
// Try read the value again
|
|
let file = node
|
|
.open(OpenOptions::READ, AccessToken::test_authorized())
|
|
.unwrap();
|
|
assert_eq!(file.tell().unwrap(), 0);
|
|
assert_eq!(file.read(&mut buf).unwrap(), 6);
|
|
assert_eq!(&buf[..6], b"654321");
|
|
}
|
|
}
|