mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-25 13:23:08 +00:00
1dab58a2cf
This search/replace updates all copyright notices to drop the "All rights reserved", Use "ChromiumOS" instead of "Chromium OS" and drops the trailing dots. This fulfills the request from legal and unifies our notices. ./tools/health-check has been updated to only accept this style. BUG=b:246579983 TEST=./tools/health-check Change-Id: I87a80701dc651f1baf4820e5cc42469d7c5f5bf7 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3894243 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Dennis Kempin <denniskempin@google.com>
180 lines
6.3 KiB
Rust
180 lines
6.3 KiB
Rust
// Copyright 2018 The ChromiumOS Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#![recursion_limit = "128"]
|
|
|
|
extern crate proc_macro;
|
|
|
|
use proc_macro2::Ident;
|
|
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use syn::parse_macro_input;
|
|
use syn::Data;
|
|
use syn::DeriveInput;
|
|
use syn::Field;
|
|
use syn::Fields;
|
|
use syn::Index;
|
|
use syn::Member;
|
|
use syn::Variant;
|
|
|
|
#[cfg(test)]
|
|
mod tests;
|
|
|
|
// The method for packing an enum into a u64 is as follows:
|
|
// 1) Reserve the lowest "ceil(log_2(x))" bits where x is the number of enum variants.
|
|
// 2) Store the enum variant's index (0-based index based on order in the enum definition) in
|
|
// reserved bits.
|
|
// 3) If there is data in the enum variant, store the data in remaining bits.
|
|
// The method for unpacking is as follows
|
|
// 1) Mask the raw token to just the reserved bits
|
|
// 2) Match the reserved bits to the enum variant token.
|
|
// 3) If the indicated enum variant had data, extract it from the unreserved bits.
|
|
|
|
// Calculates the number of bits needed to store the variant index. Essentially the log base 2
|
|
// of the number of variants, rounded up.
|
|
fn variant_bits(variants: &[Variant]) -> u32 {
|
|
if variants.is_empty() {
|
|
// The degenerate case of no variants.
|
|
0
|
|
} else {
|
|
variants.len().next_power_of_two().trailing_zeros()
|
|
}
|
|
}
|
|
|
|
// Name of the field if it has one, otherwise 0 assuming this is the zeroth
|
|
// field of a tuple variant.
|
|
fn field_member(field: &Field) -> Member {
|
|
match &field.ident {
|
|
Some(name) => Member::Named(name.clone()),
|
|
None => Member::Unnamed(Index::from(0)),
|
|
}
|
|
}
|
|
|
|
// Generates the function body for `as_raw_token`.
|
|
fn generate_as_raw_token(enum_name: &Ident, variants: &[Variant]) -> TokenStream {
|
|
let variant_bits = variant_bits(variants);
|
|
|
|
// Each iteration corresponds to one variant's match arm.
|
|
let cases = variants.iter().enumerate().map(|(index, variant)| {
|
|
let variant_name = &variant.ident;
|
|
let index = index as u64;
|
|
|
|
// The capture string is for everything between the variant identifier and the `=>` in
|
|
// the match arm: the variant's data capture.
|
|
let capture = variant.fields.iter().next().map(|field| {
|
|
let member = field_member(field);
|
|
quote!({ #member: data })
|
|
});
|
|
|
|
// The modifier string ORs the variant index with extra bits from the variant data
|
|
// field.
|
|
let modifier = match variant.fields {
|
|
Fields::Named(_) | Fields::Unnamed(_) => Some(quote! {
|
|
| ((data as u64) << #variant_bits)
|
|
}),
|
|
Fields::Unit => None,
|
|
};
|
|
|
|
// Assembly of the match arm.
|
|
quote! {
|
|
#enum_name::#variant_name #capture => #index #modifier
|
|
}
|
|
});
|
|
|
|
quote! {
|
|
match *self {
|
|
#(
|
|
#cases,
|
|
)*
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generates the function body for `from_raw_token`.
|
|
fn generate_from_raw_token(enum_name: &Ident, variants: &[Variant]) -> TokenStream {
|
|
let variant_bits = variant_bits(variants);
|
|
let variant_mask = ((1 << variant_bits) - 1) as u64;
|
|
|
|
// Each iteration corresponds to one variant's match arm.
|
|
let cases = variants.iter().enumerate().map(|(index, variant)| {
|
|
let variant_name = &variant.ident;
|
|
let index = index as u64;
|
|
|
|
// The data string is for extracting the enum variant's data bits out of the raw token
|
|
// data, which includes both variant index and data bits.
|
|
let data = variant.fields.iter().next().map(|field| {
|
|
let member = field_member(field);
|
|
let ty = &field.ty;
|
|
quote!({ #member: (data >> #variant_bits) as #ty })
|
|
});
|
|
|
|
// Assembly of the match arm.
|
|
quote! {
|
|
#index => #enum_name::#variant_name #data
|
|
}
|
|
});
|
|
|
|
quote! {
|
|
// The match expression only matches the bits for the variant index.
|
|
match data & #variant_mask {
|
|
#(
|
|
#cases,
|
|
)*
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// The proc_macro::TokenStream type can only be constructed from within a
|
|
// procedural macro, meaning that unit tests are not able to invoke `fn
|
|
// event_token` below as an ordinary Rust function. We factor out the logic into
|
|
// a signature that deals with Syn and proc-macro2 types only which are not
|
|
// restricted to a procedural macro invocation.
|
|
fn event_token_inner(input: DeriveInput) -> TokenStream {
|
|
let variants: Vec<Variant> = match input.data {
|
|
Data::Enum(data) => data.variants.into_iter().collect(),
|
|
Data::Struct(_) | Data::Union(_) => panic!("input must be an enum"),
|
|
};
|
|
|
|
for variant in &variants {
|
|
assert!(variant.fields.iter().count() <= 1);
|
|
}
|
|
|
|
// Given our basic model of a user given enum that is suitable as a token, we generate the
|
|
// implementation. The implementation is NOT always well formed, such as when a variant's data
|
|
// type is not bit shiftable or castable to u64, but we let Rust generate such errors as it
|
|
// would be difficult to detect every kind of error. Importantly, every implementation that we
|
|
// generate here and goes on to compile succesfully is sound.
|
|
|
|
let enum_name = input.ident;
|
|
let as_raw_token = generate_as_raw_token(&enum_name, &variants);
|
|
let from_raw_token = generate_from_raw_token(&enum_name, &variants);
|
|
|
|
quote! {
|
|
impl EventToken for #enum_name {
|
|
fn as_raw_token(&self) -> u64 {
|
|
#as_raw_token
|
|
}
|
|
|
|
fn from_raw_token(data: u64) -> Self {
|
|
#from_raw_token
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Implements the EventToken trait for a given `enum`.
|
|
///
|
|
/// There are limitations on what `enum`s this custom derive will work on:
|
|
///
|
|
/// * Each variant must be a unit variant (no data), or have a single (un)named data field.
|
|
/// * If a variant has data, it must be a primitive type castable to and from a `u64`.
|
|
/// * If a variant data has size greater than or equal to a `u64`, its most significant bits must be
|
|
/// zero. The number of bits truncated is equal to the number of bits used to store the variant
|
|
/// index plus the number of bits above 64.
|
|
#[proc_macro_derive(EventToken)]
|
|
pub fn event_token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
event_token_inner(input).into()
|
|
}
|