hun_offsetof/
lib.rs

1//! Provides C-like macros: offset_of and container_of
2//!
3//! 和memoffset crate的区别
4//! Differences between crate memoffset and crate self
5//! 1. 基于指针操作,不占用栈空间,支持超大数据结构,无栈溢出风险
6//! 1. Pointer-based opertions do not occupy stack space, support super-large data structures, and avoid stack overflow risks.
7//! 2. 提供container_of操作
8//! 2. Provides the container_of operation.
9//!
10//! 基于指针的操作属于unsafe范围,如果使用声明宏方式,可能出现嵌套unsafe的编译告警,而过程宏可以消除这类告警。
11//! Pointer-based operations are unsafe, If the declaration macro mode is used, nested unsafe compilation warnings may be generated, which can be eliminated by procedure macro.
12//!
13//! # Examples
14//!
15//! ```rust
16//!
17//!	extern crate hun_offsetof as hun;
18//!
19//! #[repr(C)]
20//! struct Bar {
21//! 	key:	i32,
22//!		value:	i32,
23//! }
24//!
25//! #[repr(C)]
26//! struct Foo {
27//!		key:	i32,
28//!		value:	[Bar; 2],
29//! }
30//!
31//!	assert_eq!(hun::offset_of!(Bar, value), 4);
32//! assert_eq!(hun::offset_of!(Foo, value[1].key), 12);
33//!
34//! let foo = Foo {
35//!		key: 1,
36//!		value: [ Bar { key: 2, value: 2}, Bar { key: 3, value: 3 }],
37//!	};
38//!	let value = &foo.value[1].value;
39//!
40//! let obj = unsafe { hun::container_of!(value, Foo, value[1].value) };
41//!	assert_eq!(obj as *const _, &foo as *const _);
42//! ```
43//!
44
45extern crate proc_macro;
46use std::time::{ SystemTime };
47use proc_macro::{ TokenStream };
48use proc_macro2::{ Span };
49use quote::quote;
50use syn::{
51	Expr, Token, Path, parse_macro_input,
52	spanned::{ Spanned },
53	parse::{self, Parse, ParseStream },
54	punctuated::{ Punctuated },
55};
56
57#[proc_macro]
58pub fn offset_of(input: TokenStream) -> TokenStream {
59	let input = parse_macro_input!(input as OffsetOfInput);
60	let parent = &input.parent;
61	let member = &input.member;
62	let base = make_ident("base", parent.span());
63	quote! {
64		unsafe {
65			let #base = ::core::ptr::null::<#parent>();
66			::core::ptr::addr_of!((*#base).#member) as *const u8 as usize
67		}
68	}.into()
69}
70
71#[proc_macro]
72pub fn container_of(input: TokenStream) -> TokenStream {
73	container_of_impl(input, false)
74}
75
76#[proc_macro]
77pub fn container_of_mut(input: TokenStream) -> TokenStream {
78	container_of_impl(input, true)
79}
80
81struct OffsetOfInput {
82	parent: Path,
83	_comma: Option<Token![,]>,
84	member:	Punctuated<Expr, Token![.]>,
85}
86
87impl Parse for OffsetOfInput {
88	fn parse(input: ParseStream) -> parse::Result<Self> {
89		Ok(OffsetOfInput {
90			parent: input.parse()?,
91			_comma: input.parse()?,
92			member: Punctuated::parse_terminated(input)?,
93		})
94	}
95}
96
97fn container_of_impl(input: TokenStream, is_mut: bool) -> TokenStream {
98	let input = parse_macro_input!(input as ContainerOfInput);
99	let tokens = container_of_tokens(&input);
100	if is_mut {
101		quote!({ &mut *(#tokens) }).into()
102	} else {
103		quote!({ &*(#tokens) }).into()
104	}
105}
106
107fn container_of_tokens(input: &ContainerOfInput) -> proc_macro2::TokenStream {
108	let obj = &input.obj;
109	let parent = &input.offset.parent;
110	let member = &input.offset.member;
111	let base = make_ident("base", parent.span());
112	let offset = make_ident("offset", member.span());
113	quote!({
114		let #base = ::core::ptr::null::<#parent>();
115		let #offset = ::core::ptr::addr_of!((*#base).#member) as *const u8 as usize;
116		if ::core::mem::size_of_val(&((*#base).#member)) ==
117			::core::mem::size_of_val(#obj) {
118			let #base = (#obj) as *const _ as *const u8 as usize;
119			(#base - #offset) as *mut u8 as *mut #parent
120		} else {
121			core::panic!("container_of: wrong arguments!");
122		}
123	})
124}
125
126struct ContainerOfInput {
127	obj:	Expr,
128	_comma:	Option<Token![,]>,
129	offset:	OffsetOfInput,
130}
131
132impl Parse for ContainerOfInput {
133	fn parse(input: ParseStream) -> syn::Result<Self> {
134		Ok(ContainerOfInput {
135			obj:	input.parse()?,
136			_comma:	input.parse()?,
137			offset:	OffsetOfInput {
138				parent:	input.parse()?,
139				_comma:	input.parse()?,
140				member:	Punctuated::parse_terminated(input)?,
141			},
142		})
143	}
144}
145
146fn make_ident(name: &str, span: Span) -> syn::Ident {
147	let name = format!("{}_{}", name, SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs());
148	syn::Ident::new(&name, span)
149}