Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,130 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
//! Adapters which allow the user to supply a write or read implementation as a value rather
//! than a trait implementation. If provided, it will override the trait implementation.
use super::{
Reader,
Writer, //
};
use crate::{
fmt,
prelude::*,
uaccess::UserSliceReader, //
};
use core::{
marker::PhantomData,
ops::Deref, //
};
/// # Safety
///
/// To implement this trait, it must be safe to cast a `&Self` to a `&Inner`.
/// It is intended for use in unstacking adapters out of `FileOps` backings.
pub(crate) unsafe trait Adapter {
type Inner;
}
/// Adapter to implement `Reader` via a callback with the same representation as `T`.
///
/// * Layer it on top of `WriterAdapter` if you want to add a custom callback for `write`.
/// * Layer it on top of `NoWriter` to pass through any support present on the underlying type.
///
/// # Invariants
///
/// If an instance for `WritableAdapter<_, W>` is constructed, `W` is inhabited.
#[repr(transparent)]
pub(crate) struct WritableAdapter<D, W> {
inner: D,
_writer: PhantomData<W>,
}
// SAFETY: Stripping off the adapter only removes constraints
unsafe impl<D, W> Adapter for WritableAdapter<D, W> {
type Inner = D;
}
impl<D: Writer, W> Writer for WritableAdapter<D, W> {
fn write(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.write(fmt)
}
}
impl<D: Deref, W> Reader for WritableAdapter<D, W>
where
W: Fn(&D::Target, &mut UserSliceReader) -> Result + Send + Sync + 'static,
{
fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result {
// SAFETY: WritableAdapter<_, W> can only be constructed if W is inhabited
let w: &W = unsafe { materialize_zst() };
w(self.inner.deref(), reader)
}
}
/// Adapter to implement `Writer` via a callback with the same representation as `T`.
///
/// # Invariants
///
/// If an instance for `FormatAdapter<_, F>` is constructed, `F` is inhabited.
#[repr(transparent)]
pub(crate) struct FormatAdapter<D, F> {
inner: D,
_formatter: PhantomData<F>,
}
impl<D, F> Deref for FormatAdapter<D, F> {
type Target = D;
fn deref(&self) -> &D {
&self.inner
}
}
impl<D, F> Writer for FormatAdapter<D, F>
where
F: Fn(&D, &mut fmt::Formatter<'_>) -> fmt::Result + 'static,
{
fn write(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
// SAFETY: FormatAdapter<_, F> can only be constructed if F is inhabited
let f: &F = unsafe { materialize_zst() };
f(&self.inner, fmt)
}
}
// SAFETY: Stripping off the adapter only removes constraints
unsafe impl<D, F> Adapter for FormatAdapter<D, F> {
type Inner = D;
}
#[repr(transparent)]
pub(crate) struct NoWriter<D> {
inner: D,
}
// SAFETY: Stripping off the adapter only removes constraints
unsafe impl<D> Adapter for NoWriter<D> {
type Inner = D;
}
impl<D> Deref for NoWriter<D> {
type Target = D;
fn deref(&self) -> &D {
&self.inner
}
}
/// For types with a unique value, produce a static reference to it.
///
/// # Safety
///
/// The caller asserts that F is inhabited
unsafe fn materialize_zst<F>() -> &'static F {
const { assert!(core::mem::size_of::<F>() == 0) };
let zst_dangle: core::ptr::NonNull<F> = core::ptr::NonNull::dangling();
// SAFETY: While the pointer is dangling, it is a dangling pointer to a ZST, based on the
// assertion above. The type is also inhabited, by the caller's assertion. This means
// we can materialize it.
unsafe { zst_dangle.as_ref() }
}
+170
View File
@@ -0,0 +1,170 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
use crate::{
debugfs::file_ops::FileOps,
prelude::*,
str::{
CStr,
CStrExt as _, //
},
sync::Arc,
};
use core::marker::PhantomData;
/// Owning handle to a DebugFS entry.
///
/// # Invariants
///
/// The wrapped pointer will always be `NULL`, an error, or an owned DebugFS `dentry`.
pub(crate) struct Entry<'a> {
entry: *mut bindings::dentry,
// If we were created with an owning parent, this is the keep-alive
_parent: Option<Arc<Entry<'static>>>,
// If we were created with a non-owning parent, this prevents us from outliving it
_phantom: PhantomData<&'a ()>,
}
// SAFETY: [`Entry`] is just a `dentry` under the hood, which the API promises can be transferred
// between threads.
unsafe impl Send for Entry<'_> {}
// SAFETY: All the C functions we call on the `dentry` pointer are threadsafe.
unsafe impl Sync for Entry<'_> {}
impl Entry<'static> {
pub(crate) fn dynamic_dir(name: &CStr, parent: Option<Arc<Self>>) -> Self {
let parent_ptr = match &parent {
Some(entry) => entry.as_ptr(),
None => core::ptr::null_mut(),
};
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
// * `name` is a valid C string by the invariants of `&CStr`.
// * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
// `dentry` by our invariant. `debugfs_create_dir` handles `NULL` pointers correctly.
let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
Entry {
entry,
_parent: parent,
_phantom: PhantomData,
}
}
/// # Safety
///
/// * `data` must outlive the returned `Entry`.
pub(crate) unsafe fn dynamic_file<T>(
name: &CStr,
parent: Arc<Self>,
data: &T,
file_ops: &'static FileOps<T>,
) -> Self {
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
// * `name` is a valid C string by the invariants of `&CStr`.
// * `parent.as_ptr()` is a pointer to a valid `dentry` by invariant.
// * The caller guarantees that `data` will outlive the returned `Entry`.
// * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
// provided.
let entry = unsafe {
bindings::debugfs_create_file_full(
name.as_char_ptr(),
file_ops.mode(),
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
&**file_ops,
)
};
Entry {
entry,
_parent: Some(parent),
_phantom: PhantomData,
}
}
}
impl<'a> Entry<'a> {
pub(crate) fn dir(name: &CStr, parent: Option<&'a Entry<'_>>) -> Self {
let parent_ptr = match &parent {
Some(entry) => entry.as_ptr(),
None => core::ptr::null_mut(),
};
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
// * `name` is a valid C string by the invariants of `&CStr`.
// * `parent_ptr` is either `NULL` (if `parent` is `None`), or a pointer to a valid
// `dentry` (because `parent` is a valid reference to an `Entry`). The lifetime `'a`
// ensures that the parent outlives this entry.
let entry = unsafe { bindings::debugfs_create_dir(name.as_char_ptr(), parent_ptr) };
Entry {
entry,
_parent: None,
_phantom: PhantomData,
}
}
pub(crate) fn file<T>(
name: &CStr,
parent: &'a Entry<'_>,
data: &'a T,
file_ops: &FileOps<T>,
) -> Self {
// SAFETY: The invariants of this function's arguments ensure the safety of this call.
// * `name` is a valid C string by the invariants of `&CStr`.
// * `parent.as_ptr()` is a pointer to a valid `dentry` because we have `&'a Entry`.
// * `data` is a valid pointer to `T` for lifetime `'a`.
// * The returned `Entry` has lifetime `'a`, so it cannot outlive `parent` or `data`.
// * The caller guarantees that `vtable` is compatible with `data`.
// * The guarantees on `FileOps` assert the vtable will be compatible with the data we have
// provided.
let entry = unsafe {
bindings::debugfs_create_file_full(
name.as_char_ptr(),
file_ops.mode(),
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
&**file_ops,
)
};
Entry {
entry,
_parent: None,
_phantom: PhantomData,
}
}
}
impl Entry<'_> {
/// Constructs a placeholder DebugFS [`Entry`].
pub(crate) fn empty() -> Self {
Self {
entry: core::ptr::null_mut(),
_parent: None,
_phantom: PhantomData,
}
}
/// Returns the pointer representation of the DebugFS directory.
///
/// # Guarantees
///
/// Due to the type invariant, the value returned from this function will always be an error
/// code, `NULL`, or a live DebugFS directory. If it is live, it will remain live at least as
/// long as this entry lives.
pub(crate) fn as_ptr(&self) -> *mut bindings::dentry {
self.entry
}
}
impl Drop for Entry<'_> {
fn drop(&mut self) {
// SAFETY: `debugfs_remove` can take `NULL`, error values, and legal DebugFS dentries.
// `as_ptr` guarantees that the pointer is of this form.
unsafe { bindings::debugfs_remove(self.as_ptr()) }
}
}
+388
View File
@@ -0,0 +1,388 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
use super::{
BinaryReader,
BinaryWriter,
Reader,
Writer, //
};
use crate::{
debugfs::callback_adapters::Adapter,
fmt,
fs::file,
prelude::*,
seq_file::SeqFile,
seq_print,
uaccess::UserSlice, //
};
use core::marker::PhantomData;
#[cfg(CONFIG_DEBUG_FS)]
use core::ops::Deref;
/// # Invariant
///
/// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
/// off an inode which has a pointer to a `T` in its private data that is safe to convert
/// into a reference.
pub(super) struct FileOps<T> {
#[cfg(CONFIG_DEBUG_FS)]
operations: bindings::file_operations,
#[cfg(CONFIG_DEBUG_FS)]
mode: u16,
_phantom: PhantomData<T>,
}
impl<T> FileOps<T> {
/// # Safety
///
/// The caller asserts that the provided `operations` is safe to use for a file whose
/// inode has a pointer to `T` in its private data that is safe to convert into a reference.
const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {
Self {
#[cfg(CONFIG_DEBUG_FS)]
operations,
#[cfg(CONFIG_DEBUG_FS)]
mode,
_phantom: PhantomData,
}
}
#[cfg(CONFIG_DEBUG_FS)]
pub(crate) const fn mode(&self) -> u16 {
self.mode
}
}
impl<T: Adapter> FileOps<T> {
pub(super) const fn adapt(&self) -> &FileOps<T::Inner> {
// SAFETY: `Adapter` asserts that `T` can be legally cast to `T::Inner`.
unsafe { core::mem::transmute(self) }
}
}
#[cfg(CONFIG_DEBUG_FS)]
impl<T> Deref for FileOps<T> {
type Target = bindings::file_operations;
fn deref(&self) -> &Self::Target {
&self.operations
}
}
struct WriterAdapter<T>(T);
impl<'a, T: Writer> fmt::Display for WriterAdapter<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.write(f)
}
}
/// Implements `open` for `file_operations` via `single_open` to fill out a `seq_file`.
///
/// # Safety
///
/// * `inode`'s private pointer must point to a value of type `T` which will outlive the `inode`
/// and will not have any unique references alias it during the call.
/// * `file` must point to a live, not-yet-initialized file object.
unsafe extern "C" fn writer_open<T: Writer + Sync>(
inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_int {
// SAFETY: The caller ensures that `inode` is a valid pointer.
let data = unsafe { (*inode).i_private };
// SAFETY:
// * `file` is acceptable by caller precondition.
// * `print_act` will be called on a `seq_file` with private data set to the third argument,
// so we meet its safety requirements.
// * The `data` pointer passed in the third argument is a valid `T` pointer that outlives
// this call by caller preconditions.
unsafe { bindings::single_open(file, Some(writer_act::<T>), data) }
}
/// Prints private data stashed in a seq_file to that seq file.
///
/// # Safety
///
/// `seq` must point to a live `seq_file` whose private data is a valid pointer to a `T` which may
/// not have any unique references alias it during the call.
unsafe extern "C" fn writer_act<T: Writer + Sync>(
seq: *mut bindings::seq_file,
_: *mut c_void,
) -> c_int {
// SAFETY: By caller precondition, this pointer is valid pointer to a `T`, and
// there are not and will not be any unique references until we are done.
let data = unsafe { &*((*seq).private.cast::<T>()) };
// SAFETY: By caller precondition, `seq_file` points to a live `seq_file`, so we can lift
// it.
let seq_file = unsafe { SeqFile::from_raw(seq) };
seq_print!(seq_file, "{}", WriterAdapter(data));
0
}
// Work around lack of generic const items.
pub(crate) trait ReadFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Writer + Sync> ReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(bindings::seq_read),
llseek: Some(bindings::seq_lseek),
release: Some(bindings::single_release),
open: Some(writer_open::<Self>),
..pin_init::zeroed()
};
// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`.
// `open`'s only requirement beyond what is provided to all open functions is that the
// inode's data pointer must point to a `T` that will outlive it, which matches the
// `FileOps` requirements.
unsafe { FileOps::new(operations, 0o400) }
};
}
fn read<T: Reader + Sync>(data: &T, buf: *const c_char, count: usize) -> isize {
let mut reader = UserSlice::new(UserPtr::from_ptr(buf as *mut c_void), count).reader();
if let Err(e) = data.read_from_slice(&mut reader) {
return e.to_errno() as isize;
}
count as isize
}
/// # Safety
///
/// `file` must be a valid pointer to a `file` struct.
/// The `private_data` of the file must contain a valid pointer to a `seq_file` whose
/// `private` data in turn points to a `T` that implements `Reader`.
/// `buf` must be a valid user-space buffer.
pub(crate) unsafe extern "C" fn write<T: Reader + Sync>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
_ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY: The file was opened with `single_open`, which sets `private_data` to a `seq_file`.
let seq = unsafe { &mut *((*file).private_data.cast::<bindings::seq_file>()) };
// SAFETY: By caller precondition, this pointer is live and points to a value of type `T`.
let data = unsafe { &*(seq.private as *const T) };
read(data, buf, count)
}
// A trait to get the file operations for a type.
pub(crate) trait ReadWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
open: Some(writer_open::<T>),
read: Some(bindings::seq_read),
write: Some(write::<T>),
llseek: Some(bindings::seq_lseek),
release: Some(bindings::single_release),
..pin_init::zeroed()
};
// SAFETY: `operations` is all stock `seq_file` implementations except for `writer_open`
// and `write`.
// `writer_open`'s only requirement beyond what is provided to all open functions is that
// the inode's data pointer must point to a `T` that will outlive it, which matches the
// `FileOps` requirements.
// `write` only requires that the file's private data pointer points to `seq_file`
// which points to a `T` that will outlive it, which matches what `writer_open`
// provides.
unsafe { FileOps::new(operations, 0o600) }
};
}
/// # Safety
///
/// `inode` must be a valid pointer to an `inode` struct.
/// `file` must be a valid pointer to a `file` struct.
unsafe extern "C" fn write_only_open(
inode: *mut bindings::inode,
file: *mut bindings::file,
) -> c_int {
// SAFETY: The caller ensures that `inode` and `file` are valid pointers.
unsafe { (*file).private_data = (*inode).i_private };
0
}
/// # Safety
///
/// * `file` must be a valid pointer to a `file` struct.
/// * The `private_data` of the file must contain a valid pointer to a `T` that implements
/// `Reader`.
/// * `buf` must be a valid user-space buffer.
pub(crate) unsafe extern "C" fn write_only_write<T: Reader + Sync>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
_ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY: The caller ensures that `file` is a valid pointer and that `private_data` holds a
// valid pointer to `T`.
let data = unsafe { &*((*file).private_data as *const T) };
read(data, buf, count)
}
pub(crate) trait WriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: Reader + Sync> WriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
open: Some(write_only_open),
write: Some(write_only_write::<T>),
llseek: Some(bindings::noop_llseek),
..pin_init::zeroed()
};
// SAFETY:
// * `write_only_open` populates the file private data with the inode private data
// * `write_only_write`'s only requirement is that the private data of the file point to
// a `T` and be legal to convert to a shared reference, which `write_only_open`
// satisfies.
unsafe { FileOps::new(operations, 0o200) }
};
}
extern "C" fn blob_read<T: BinaryWriter>(
file: *mut bindings::file,
buf: *mut c_char,
count: usize,
ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY:
// - `file` is a valid pointer to a `struct file`.
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
let this = unsafe { &*((*file).private_data.cast::<T>()) };
// SAFETY:
// - `ppos` is a valid `file::Offset` pointer.
// - We have exclusive access to `ppos`.
let pos: &mut file::Offset = unsafe { &mut *ppos };
let mut writer = UserSlice::new(UserPtr::from_ptr(buf.cast()), count).writer();
let ret = || -> Result<isize> {
let written = this.write_to_slice(&mut writer, pos)?;
Ok(written.try_into()?)
}();
match ret {
Ok(n) => n,
Err(e) => e.to_errno() as isize,
}
}
/// Representation of [`FileOps`] for read only binary files.
pub(crate) trait BinaryReadFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(blob_read::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
..pin_init::zeroed()
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o400) }
};
}
extern "C" fn blob_write<T: BinaryReader>(
file: *mut bindings::file,
buf: *const c_char,
count: usize,
ppos: *mut bindings::loff_t,
) -> isize {
// SAFETY:
// - `file` is a valid pointer to a `struct file`.
// - The type invariant of `FileOps` guarantees that `private_data` points to a valid `T`.
let this = unsafe { &*((*file).private_data.cast::<T>()) };
// SAFETY:
// - `ppos` is a valid `file::Offset` pointer.
// - We have exclusive access to `ppos`.
let pos: &mut file::Offset = unsafe { &mut *ppos };
let mut reader = UserSlice::new(UserPtr::from_ptr(buf.cast_mut().cast()), count).reader();
let ret = || -> Result<isize> {
let read = this.read_from_slice(&mut reader, pos)?;
Ok(read.try_into()?)
}();
match ret {
Ok(n) => n,
Err(e) => e.to_errno() as isize,
}
}
/// Representation of [`FileOps`] for write only binary files.
pub(crate) trait BinaryWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
..pin_init::zeroed()
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o200) }
};
}
/// Representation of [`FileOps`] for read/write binary files.
pub(crate) trait BinaryReadWriteFile<T> {
const FILE_OPS: FileOps<T>;
}
impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
let operations = bindings::file_operations {
read: Some(blob_read::<T>),
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
..pin_init::zeroed()
};
// SAFETY:
// - The private data of `struct inode` does always contain a pointer to a valid `T`.
// - `simple_open()` stores the `struct inode`'s private data in the private data of the
// corresponding `struct file`.
// - `blob_read()` re-creates a reference to `T` from the `struct file`'s private data.
// - `blob_write()` re-creates a reference to `T` from the `struct file`'s private data.
// - `default_llseek()` does not access the `struct file`'s private data.
unsafe { FileOps::new(operations, 0o600) }
};
}
+340
View File
@@ -0,0 +1,340 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2025 Google LLC.
//! Traits for rendering or updating values exported to DebugFS.
use crate::{
alloc::Allocator,
fmt,
fs::file,
prelude::*,
sync::{
atomic::{
Atomic,
AtomicBasicOps,
AtomicType,
Relaxed, //
},
Arc,
Mutex, //
},
transmute::{
AsBytes,
FromBytes, //
},
uaccess::{
UserSliceReader,
UserSliceWriter, //
},
};
use core::{
ops::{
Deref,
DerefMut, //
},
str::FromStr,
};
/// A trait for types that can be written into a string.
///
/// This works very similarly to `Debug`, and is automatically implemented if `Debug` is
/// implemented for a type. It is also implemented for any writable type inside a `Mutex`.
///
/// The derived implementation of `Debug` [may
/// change](https://doc.rust-lang.org/std/fmt/trait.Debug.html#stability)
/// between Rust versions, so if stability is key for your use case, please implement `Writer`
/// explicitly instead.
pub trait Writer {
/// Formats the value using the given formatter.
fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result;
}
impl<T: Writer> Writer for Mutex<T> {
fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.lock().write(f)
}
}
impl<T: fmt::Debug> Writer for T {
fn write(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{self:?}")
}
}
/// Trait for types that can be written out as binary.
pub trait BinaryWriter {
/// Writes the binary form of `self` into `writer`.
///
/// `offset` is the requested offset into the binary representation of `self`.
///
/// On success, returns the number of bytes written in to `writer`.
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize>;
}
// Base implementation for any `T: AsBytes`.
impl<T: AsBytes> BinaryWriter for T {
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
writer.write_slice_file(self.as_bytes(), offset)
}
}
// Delegate for `Mutex<T>`: Support a `T` with an outer mutex.
impl<T: BinaryWriter> BinaryWriter for Mutex<T> {
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
let guard = self.lock();
guard.write_to_slice(writer, offset)
}
}
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with no lock or an inner lock.
impl<T, A> BinaryWriter for Box<T, A>
where
T: BinaryWriter,
A: Allocator,
{
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().write_to_slice(writer, offset)
}
}
// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with no lock or an inner lock.
impl<T, A> BinaryWriter for Pin<Box<T, A>>
where
T: BinaryWriter,
A: Allocator,
{
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().write_to_slice(writer, offset)
}
}
// Delegate for `Arc<T>`: Support a `Arc<T>` with no lock or an inner lock.
impl<T> BinaryWriter for Arc<T>
where
T: BinaryWriter,
{
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().write_to_slice(writer, offset)
}
}
// Delegate for `Vec<T, A>`.
impl<T, A> BinaryWriter for Vec<T, A>
where
T: AsBytes,
A: Allocator,
{
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
offset: &mut file::Offset,
) -> Result<usize> {
let slice = self.as_slice();
// SAFETY: `T: AsBytes` allows us to treat `&[T]` as `&[u8]`.
let buffer = unsafe {
core::slice::from_raw_parts(slice.as_ptr().cast(), core::mem::size_of_val(slice))
};
writer.write_slice_file(buffer, offset)
}
}
/// A trait for types that can be updated from a user slice.
///
/// This works similarly to `FromStr`, but operates on a `UserSliceReader` rather than a &str.
///
/// It is automatically implemented for all atomic integers, or any type that implements `FromStr`
/// wrapped in a `Mutex`.
pub trait Reader {
/// Updates the value from the given user slice.
fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result;
}
impl<T: FromStr + Unpin> Reader for Mutex<T> {
fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result {
let mut buf = [0u8; 128];
if reader.len() > buf.len() {
return Err(EINVAL);
}
let n = reader.len();
reader.read_slice(&mut buf[..n])?;
let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?;
let val = s.trim().parse::<T>().map_err(|_| EINVAL)?;
*self.lock() = val;
Ok(())
}
}
impl<T: AtomicType + FromStr> Reader for Atomic<T>
where
T::Repr: AtomicBasicOps,
{
fn read_from_slice(&self, reader: &mut UserSliceReader) -> Result {
let mut buf = [0u8; 21]; // Enough for a 64-bit number.
if reader.len() > buf.len() {
return Err(EINVAL);
}
let n = reader.len();
reader.read_slice(&mut buf[..n])?;
let s = core::str::from_utf8(&buf[..n]).map_err(|_| EINVAL)?;
let val = s.trim().parse::<T>().map_err(|_| EINVAL)?;
self.store(val, Relaxed);
Ok(())
}
}
/// Trait for types that can be constructed from a binary representation.
///
/// See also [`BinaryReader`] for interior mutability.
pub trait BinaryReaderMut {
/// Reads the binary form of `self` from `reader`.
///
/// Same as [`BinaryReader::read_from_slice`], but takes a mutable reference.
///
/// `offset` is the requested offset into the binary representation of `self`.
///
/// On success, returns the number of bytes read from `reader`.
fn read_from_slice_mut(
&mut self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize>;
}
// Base implementation for any `T: AsBytes + FromBytes`.
impl<T: AsBytes + FromBytes> BinaryReaderMut for T {
fn read_from_slice_mut(
&mut self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
reader.read_slice_file(self.as_bytes_mut(), offset)
}
}
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an outer lock.
impl<T: ?Sized + BinaryReaderMut, A: Allocator> BinaryReaderMut for Box<T, A> {
fn read_from_slice_mut(
&mut self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref_mut().read_from_slice_mut(reader, offset)
}
}
// Delegate for `Vec<T, A>`: Support a `Vec<T, A>` with an outer lock.
impl<T, A> BinaryReaderMut for Vec<T, A>
where
T: AsBytes + FromBytes,
A: Allocator,
{
fn read_from_slice_mut(
&mut self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
let slice = self.as_mut_slice();
// SAFETY: `T: AsBytes + FromBytes` allows us to treat `&mut [T]` as `&mut [u8]`.
let buffer = unsafe {
core::slice::from_raw_parts_mut(
slice.as_mut_ptr().cast(),
core::mem::size_of_val(slice),
)
};
reader.read_slice_file(buffer, offset)
}
}
/// Trait for types that can be constructed from a binary representation.
///
/// See also [`BinaryReaderMut`] for the mutable version.
pub trait BinaryReader {
/// Reads the binary form of `self` from `reader`.
///
/// `offset` is the requested offset into the binary representation of `self`.
///
/// On success, returns the number of bytes read from `reader`.
fn read_from_slice(
&self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize>;
}
// Delegate for `Mutex<T>`: Support a `T` with an outer `Mutex`.
impl<T: BinaryReaderMut + Unpin> BinaryReader for Mutex<T> {
fn read_from_slice(
&self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
let mut this = self.lock();
this.read_from_slice_mut(reader, offset)
}
}
// Delegate for `Box<T, A>`: Support a `Box<T, A>` with an inner lock.
impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Box<T, A> {
fn read_from_slice(
&self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().read_from_slice(reader, offset)
}
}
// Delegate for `Pin<Box<T, A>>`: Support a `Pin<Box<T, A>>` with an inner lock.
impl<T: ?Sized + BinaryReader, A: Allocator> BinaryReader for Pin<Box<T, A>> {
fn read_from_slice(
&self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().read_from_slice(reader, offset)
}
}
// Delegate for `Arc<T>`: Support an `Arc<T>` with an inner lock.
impl<T: ?Sized + BinaryReader> BinaryReader for Arc<T> {
fn read_from_slice(
&self,
reader: &mut UserSliceReader,
offset: &mut file::Offset,
) -> Result<usize> {
self.deref().read_from_slice(reader, offset)
}
}