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