Skip to main content

cuneiform/
lib.rs

1//! # Cuneiform: In memory optimizations for Rust, revived from the slabs of Sumer.
2//!
3//! This crate provides proc macro attributes to improve in memory data access times.
4//!
5//! Cuneiform's main macro can take various arguments at attribute position:
6//! * `hermetic = true|false` (default is `true` when `#[cuneiform]`)
7//! * Hermetic enables cuneiform to detect cache sizes from OSes which have API to fetch.
8//! * Currently, hermetic argument works only Linux kernel 2.6.32 and above.
9//! * If system is different than supported systems it falls back to `slab`s.
10//! * `slab = "board_or_architecture_name` (e.g. `#[cuneiform(slab = "powerpc_mpc8xx")]`)
11//!     * Slabs are either embedded system boards or other specific architecture.
12//!     * Slab checking have two stages:
13//!         * First, it checks the given board/architecture if exist.
14//!         * If not slabs fall back to Rust supported architectures.
15//!         * Still architecture is not detected, it will fall back to default values.
16//! * `force = u8` (e.g. `#[cuneiform(force = 16)]`)
17//!     * Forces a given cache alignment. Overrides all other systems mentioned above.
18//!
19//! ```toml
20//! [dependencies]
21//! cuneiform = "0.1"
22//! ```
23//!
24//! ## Examples
25//! Basic usage can be:
26//! ```rust
27//! use cuneiform::*;
28//!
29//! // Defaults to `hermetic = true`
30//! #[cuneiform]
31//! pub struct Varying {
32//!     data: u8,
33//!     data_2: u16,
34//! }
35//! ```
36//!
37//! Targeting specific architecture:
38//! ```rust
39//! use cuneiform::*;
40//!
41//! #[cuneiform(slab = "krait")]
42//! pub struct SlabBased {
43//!     data: u8,
44//!     data_2: u16,
45//! }
46//! ```
47//!
48//! Overriding the default cache alignment:
49//! ```rust
50//! use cuneiform::*;
51//!
52//! #[cuneiform(force = 16)]
53//! pub struct Forced {
54//!     data: u8,
55//!     data_2: u16,
56//! }
57//! ```
58
59#![doc(
60    html_logo_url = "https://github.com/vertexclique/cuneiform/raw/master/img/cuneiform-logo.png"
61)]
62// Force missing implementations
63#![warn(missing_docs)]
64#![warn(missing_debug_implementations)]
65#![forbid(unsafe_code)]
66
67extern crate proc_macro;
68use self::proc_macro::TokenStream;
69
70use quote::*;
71use syn::parse::{Parse, ParseStream, Result};
72use syn::{parse_macro_input, DeriveInput, Index, LitBool, LitInt, LitStr, Token};
73
74mod detection;
75mod slabs;
76
77///
78/// Inference arguments
79pub(crate) struct CuneiformArgs {
80    /// Inherit coherency size targeting from the current machine that the compiler runs. (Default)
81    pub(crate) hermetic: bool,
82    /// Force the slab that is going to be used.
83    pub(crate) slab: String,
84    /// Force size to a specific amount. Overrides any other parameter.
85    pub(crate) force: isize,
86}
87
88impl CuneiformArgs {
89    fn new() -> Self {
90        CuneiformArgs {
91            hermetic: true,
92            slab: String::from(""),
93            force: !0,
94        }
95    }
96
97    fn with_hermetic(&mut self, hermetic: bool) {
98        self.hermetic = hermetic;
99    }
100
101    fn with_slab(&mut self, slab: String) {
102        self.slab = slab;
103    }
104
105    fn with_force(&mut self, force: isize) {
106        self.force = force;
107    }
108}
109
110mod cunei_keywords {
111    syn::custom_keyword!(hermetic);
112    syn::custom_keyword!(slab);
113    syn::custom_keyword!(force);
114}
115
116///
117/// Syn parse for Cuneiform
118impl Parse for CuneiformArgs {
119    fn parse(input: ParseStream) -> Result<Self> {
120        let mut cargs = CuneiformArgs::new();
121
122        if input.parse::<cunei_keywords::hermetic>().is_ok() {
123            input.parse::<Token![=]>()?;
124            let hermetic: LitBool = input.parse()?;
125            cargs.with_hermetic(hermetic.value);
126        }
127
128        if input.parse::<cunei_keywords::slab>().is_ok() {
129            input.parse::<Token![=]>()?;
130            let slab: LitStr = input.parse()?;
131            cargs.with_slab(String::from(slab.value().as_str()));
132        }
133
134        if input.parse::<cunei_keywords::force>().is_ok() {
135            input.parse::<Token![=]>()?;
136            let force_lit: LitInt = input.parse()?;
137            let force = force_lit.base10_parse::<isize>()?;
138            cargs.with_force(force);
139        }
140
141        Ok(cargs)
142    }
143}
144
145///
146/// Entry point for cuneiform proc macro attribute.
147///
148/// Cuneiform allows structs to be optimized for the specific cache line sizes.
149///
150/// # Example
151///
152/// ```rust
153/// use cuneiform::*;
154///
155/// #[cuneiform]
156/// pub struct Varying {
157///     data: u8,
158///     data_2: u16,
159/// }
160/// ```
161#[proc_macro_attribute]
162pub fn cuneiform(args: TokenStream, input: TokenStream) -> TokenStream {
163    let pargs = parse_macro_input!(args as CuneiformArgs);
164    let input = parse_macro_input!(input as DeriveInput);
165
166    let frep = crate::slabs::fetch(pargs);
167    let frep = Index::from(frep as usize);
168    TokenStream::from(quote! {
169        // Preserve the input struct unchanged in the output.
170        #[repr(align(#frep))]
171        #input
172    })
173}
174
175///
176/// Boundary size alignment for RAM data.
177///
178/// This macro allows fetching feasible local data size boundary alignment,
179/// and embeds it into your code as constant with the same name.
180/// When acquired it can be used to have single-shot aligned contiguous memory blocks.
181///
182/// # Example
183///
184/// ```rust
185/// # use std::alloc::Layout;
186/// use cuneiform::*;
187///
188/// #[boundary_size]
189/// type BS = ();
190///
191/// let layout = unsafe {
192///    Layout::from_size_align_unchecked(1 << 4, BOUNDARY_SIZE as usize)
193/// };
194/// assert_eq!(layout.align(), BOUNDARY_SIZE as usize);
195/// ```
196#[proc_macro_attribute]
197pub fn boundary_size(_attr: TokenStream, _input: TokenStream) -> TokenStream {
198    let boundary = crate::slabs::fallbackless_slab_fetch();
199    TokenStream::from(quote! {
200        pub const BOUNDARY_SIZE: u8 = #boundary;
201    })
202}