Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lifetimed indexing for array accessors #44

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ repository = "https://github.com/toku-sa-n/accessor"
readme = "README.md"
categories = ["no-std", "os"]
keywords = ["no_std", "OS"]

[dependencies]
accessor_macros = { path = "./accessor_macros" }
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,23 @@
![Crates.io](https://img.shields.io/crates/l/accessor)
[![docs.rs](https://docs.rs/accessor/badge.svg)](https://docs.rs/accessor)

Accessors to access physical memory.
Accessors to read and write physical memory volatilely, such as performing memory-mapped I/O.

This crate provides accessors to values at a specific memory address. When an accessor is
created, physical memory is mapped to virtual memory. The methods of the accessors can access
a value at the specified physical address. Once an accessor is dropped, the mapped memory is unmapped.
This crate provides accessors to the value at a specific physical memory address.
The accessed type doesn't have to implement [`Copy`], but be aware that reading and writing the value creates a bitwise copy of it.

This crate is intended to access memory-mapped I/O. Reading and writing are done volatilely.
Accessors are similar to pointers with volatile read/writes
(or for those who are familiar with crate [volatile(~v0.3.0)](https://docs.rs/volatile/0.3.0/volatile/index.html), pointers of volatile wrappers)
but also designed to refer correct physical addresses even in virtual memory mode,
once an appropriate physical-to-virtual memory mapper is specified.

The accessed type must implement [`Copy`] because reading and writing values need to copy it.
When an accessor is created, the physical memory is mapped into virtual memory, with the help of the
mapper implemented by the crate user. The methods of accessors allow access to the value at the
specified physical address. Once an accessor is dropped, the mapped memory is unmapped.

If one has full control of physical memory addresses(e.g. developing their own kernel),
a 'virtual' address may be equal to the physical one, in which case the mapper should map any address into itself.
The built-in mapper `mapper::Identity` can be used for such cases.

This crate is `#[no_std]` compatible.

Expand Down
12 changes: 12 additions & 0 deletions accessor_macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "accessor_macros"
version = "0.1.0"
edition = "2018"

[lib]
proc-macro = true

[dependencies]
proc-macro2 = "1.0.67"
quote = "1.0.33"
syn = "2.0.37"
144 changes: 144 additions & 0 deletions accessor_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//! A crate which defines [`BoundedStructuralOf`] proc-macro.

#![feature(offset_of)]

use proc_macro2::{Ident, Span};
use quote::quote;
use syn::DeriveInput;

/// Use this derivation to field structs so that an accessor of the struct type can be accessed/indexed into a struct of fields.
///
/// See `accessor::single::{BoundedStructural, BoundedStructuralMut}` and `accessor::array::{BoundedStructural, BoundededStructuralMut}` traits for more details.
#[proc_macro_derive(BoundedStructuralOf)]
pub fn derive_bounded_structural_of(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let DeriveInput {
vis,
ident: orig_ident,
data,
..
} = syn::parse(input).unwrap();

let bounded_ident = Ident::new(&format!("BoundedStructuralOf{}", orig_ident), Span::call_site());

let fields = match data {
syn::Data::Struct(ref s) => &s.fields,
_ => panic!("`BoundedStructuralOf` can be derived only for field structs."),
};
if let syn::Fields::Named(_) = fields {}
else {
// todo: support for tuple structs
panic!("`BoundedStructuralOf` can be derived only for field structs.");
}

let field_var = fields.iter()
.map(|field| {
let vis = field.vis.clone();
let ident = field.ident.as_ref().unwrap().clone();
let ty = field.ty.clone();
quote! {
#vis #ident: accessor::single::Generic<#ty, accessor::mapper::Identity, A>,
}
});

let field_convert = fields.iter()
.map(|field| {
let ident = field.ident.as_ref().unwrap().clone();
quote! {
#ident: accessor::single::Generic::new(addr + core::mem::offset_of!(#orig_ident, #ident), accessor::mapper::Identity),
}
});
let field_convert_mut = field_convert.clone();
let field_convert_2 = field_convert.clone();
let field_convert_2_mut = field_convert.clone();

let tokens = quote! {
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
#vis struct #bounded_ident<'a, ACC, A>
where
A: accessor::marker::AccessorTypeSpecifier,
{
#(#field_var)*
_lifetime: core::marker::PhantomData<&'a ACC>
}

impl<M, A> accessor::single::BoundedStructural<#orig_ident, M, A> for accessor::single::Generic<#orig_ident, M, A>
where
M: accessor::mapper::Mapper,
A: accessor::marker::Readable,
{
type BoundedStructuralType<'a> = #bounded_ident<'a, Self, accessor::marker::ReadOnly>
where Self: 'a;

fn structural<'a>(&'a self) -> #bounded_ident<'a, Self, accessor::marker::ReadOnly> {
unsafe {
let addr = self.addr();
#bounded_ident {
#(#field_convert)*
_lifetime: core::marker::PhantomData
}
}
}
}

impl<M, A> accessor::single::BoundedStructuralMut<#orig_ident, M, A> for accessor::single::Generic<#orig_ident, M, A>
where
M: accessor::mapper::Mapper,
A: accessor::marker::Writable,
{
type BoundedStructuralType<'a> = #bounded_ident<'a, Self, A>
where Self: 'a;

fn structural_mut<'a>(&'a mut self) -> #bounded_ident<'a, Self, A> {
unsafe {
let addr = self.addr();
#bounded_ident {
#(#field_convert_mut)*
_lifetime: core::marker::PhantomData
}
}
}
}

impl<M, A> accessor::array::BoundedStructural<#orig_ident, M, A> for accessor::array::Generic<#orig_ident, M, A>
where
M: accessor::mapper::Mapper,
A: accessor::marker::Readable,
{
type BoundedStructuralType<'a> = #bounded_ident<'a, Self, accessor::marker::ReadOnly>
where Self: 'a;

fn structural_at<'a>(&'a self, i: usize) -> #bounded_ident<'a, Self, accessor::marker::ReadOnly> {
assert!(i < self.len());
unsafe {
let addr = self.addr(i);
#bounded_ident {
#(#field_convert_2)*
_lifetime: core::marker::PhantomData
}
}
}
}

impl<M, A> accessor::array::BoundedStructuralMut<#orig_ident, M, A> for accessor::array::Generic<#orig_ident, M, A>
where
M: accessor::mapper::Mapper,
A: accessor::marker::Writable,
{
type BoundedStructuralType<'a> = #bounded_ident<'a, Self, A>
where Self: 'a;

fn structural_at_mut<'a>(&'a mut self, i: usize) -> #bounded_ident<'a, Self, A> {
assert!(i < self.len());
unsafe {
let addr = self.addr(i);
#bounded_ident {
#(#field_convert_2_mut)*
_lifetime: core::marker::PhantomData
}
}
}
}
};
tokens.into()
}
Loading
Loading