1extern 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}