hioff/
lib.rs

1//! Provides C-like macros: offset_of and container_of
2//!
3//! # Examples
4//!
5//! ```rust
6//!
7//!	extern crate hioff as hun;
8//!
9//! #[repr(C)]
10//! struct Bar {
11//! 	key:	i32,
12//!		value:	i32,
13//! }
14//!
15//! #[repr(C)]
16//! struct Foo {
17//!		key:	i32,
18//!		value:	[Bar; 2],
19//! }
20//!
21//!	assert_eq!(hun::offset_of!(Bar, value), 4);
22//! assert_eq!(hun::offset_of!(Foo, value[1].key), 12);
23//!
24//! let foo = Foo {
25//!		key: 1,
26//!		value: [ Bar { key: 2, value: 2}, Bar { key: 3, value: 3 }],
27//!	};
28//!	let value = &foo.value[1].value;
29//!
30//! let obj = unsafe { hun::container_of!(value, Foo, value[1].value) };
31//!	assert_eq!(obj as *const _, &foo as *const _);
32//! ```
33//!
34
35extern crate proc_macro;
36use std::time::{ SystemTime };
37use proc_macro::{ TokenStream };
38use proc_macro2::{ Span };
39use quote::quote;
40use syn::{
41	Expr, Token, Path, parse_macro_input,
42	spanned::{ Spanned },
43	parse::{self, Parse, ParseStream },
44	punctuated::{ Punctuated },
45};
46
47#[proc_macro]
48pub fn offset_of(input: TokenStream) -> TokenStream {
49	let input = parse_macro_input!(input as OffsetOfInput);
50	let parent = &input.parent;
51	let member = &input.member;
52	let base = make_ident("base", parent.span());
53	quote! {
54		unsafe {
55			let #base = ::core::ptr::null::<#parent>();
56			::core::ptr::addr_of!((*#base).#member) as *const u8 as usize
57		}
58	}.into()
59}
60
61#[proc_macro]
62pub fn container_of(input: TokenStream) -> TokenStream {
63	container_of_impl(input, false)
64}
65
66#[proc_macro]
67pub fn container_of_mut(input: TokenStream) -> TokenStream {
68	container_of_impl(input, true)
69}
70
71struct OffsetOfInput {
72	parent: Path,
73	_comma: Option<Token![,]>,
74	member:	Punctuated<Expr, Token![.]>,
75}
76
77impl Parse for OffsetOfInput {
78	fn parse(input: ParseStream) -> parse::Result<Self> {
79		Ok(OffsetOfInput {
80			parent: input.parse()?,
81			_comma: input.parse()?,
82			member: Punctuated::parse_terminated(input)?,
83		})
84	}
85}
86
87fn container_of_impl(input: TokenStream, is_mut: bool) -> TokenStream {
88	let input = parse_macro_input!(input as ContainerOfInput);
89	let tokens = container_of_tokens(&input);
90	if is_mut {
91		quote!({ &mut *(#tokens) }).into()
92	} else {
93		quote!({ &*(#tokens) }).into()
94	}
95}
96
97fn container_of_tokens(input: &ContainerOfInput) -> proc_macro2::TokenStream {
98	let obj = &input.obj;
99	let parent = &input.offset.parent;
100	let member = &input.offset.member;
101	let base = make_ident("base", parent.span());
102	let offset = make_ident("offset", member.span());
103	quote!({
104		let #base = ::core::ptr::null::<#parent>();
105		let #offset = ::core::ptr::addr_of!((*#base).#member) as *const u8 as usize;
106		if ::core::mem::size_of_val(&((*#base).#member)) ==
107			::core::mem::size_of_val(#obj) {
108			let #base = (#obj) as *const _ as *const u8 as usize;
109			(#base - #offset) as *mut u8 as *mut #parent
110		} else {
111			core::panic!("container_of: wrong arguments!");
112		}
113	})
114}
115
116struct ContainerOfInput {
117	obj:	Expr,
118	_comma:	Option<Token![,]>,
119	offset:	OffsetOfInput,
120}
121
122impl Parse for ContainerOfInput {
123	fn parse(input: ParseStream) -> syn::Result<Self> {
124		Ok(ContainerOfInput {
125			obj:	input.parse()?,
126			_comma:	input.parse()?,
127			offset:	OffsetOfInput {
128				parent:	input.parse()?,
129				_comma:	input.parse()?,
130				member:	Punctuated::parse_terminated(input)?,
131			},
132		})
133	}
134}
135
136fn make_ident(name: &str, span: Span) -> syn::Ident {
137	let name = format!("{}_{}", name, SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs());
138	syn::Ident::new(&name, span)
139}