• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Utility functions for dealing with Rust integer types.
16 
17 use crate::analyzer::ast as analyzer_ast;
18 use crate::{ast, lint};
19 use quote::{format_ident, quote};
20 
21 /// A Rust integer type such as `u8`.
22 #[derive(Copy, Clone)]
23 pub struct Integer {
24     pub width: usize,
25 }
26 
27 impl Integer {
28     /// Get the Rust integer type for the given bit width.
29     ///
30     /// This will round up the size to the nearest Rust integer size.
31     /// PDL supports integers up to 64 bit, so it is an error to call
32     /// this with a width larger than 64.
new(width: usize) -> Integer33     pub fn new(width: usize) -> Integer {
34         for integer_width in [8, 16, 32, 64] {
35             if width <= integer_width {
36                 return Integer { width: integer_width };
37             }
38         }
39         panic!("Cannot construct Integer with width: {width}")
40     }
41 }
42 
43 impl quote::ToTokens for Integer {
to_tokens(&self, tokens: &mut proc_macro2::TokenStream)44     fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
45         let t: syn::Type = syn::parse_str(&format!("u{}", self.width))
46             .expect("Could not parse integer, unsupported width?");
47         t.to_tokens(tokens);
48     }
49 }
50 
rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream51 pub fn rust_type(field: &analyzer_ast::Field) -> proc_macro2::TokenStream {
52     match &field.desc {
53         ast::FieldDesc::Scalar { width, .. } => {
54             let field_type = Integer::new(*width);
55             quote!(#field_type)
56         }
57         ast::FieldDesc::Typedef { type_id, .. } => {
58             let field_type = format_ident!("{type_id}");
59             quote!(#field_type)
60         }
61         ast::FieldDesc::Array { width: Some(width), size: Some(size), .. } => {
62             let field_type = Integer::new(*width);
63             let size = proc_macro2::Literal::usize_unsuffixed(*size);
64             quote!([#field_type; #size])
65         }
66         ast::FieldDesc::Array { width: Some(width), size: None, .. } => {
67             let field_type = Integer::new(*width);
68             quote!(Vec<#field_type>)
69         }
70         ast::FieldDesc::Array { type_id: Some(type_id), size: Some(size), .. } => {
71             let field_type = format_ident!("{type_id}");
72             let size = proc_macro2::Literal::usize_unsuffixed(*size);
73             quote!([#field_type; #size])
74         }
75         ast::FieldDesc::Array { type_id: Some(type_id), size: None, .. } => {
76             let field_type = format_ident!("{type_id}");
77             quote!(Vec<#field_type>)
78         }
79         //ast::Field::Size { .. } | ast::Field::Count { .. } => quote!(),
80         _ => todo!("{field:?}"),
81     }
82 }
83 
rust_borrow( field: &analyzer_ast::Field, scope: &lint::Scope<'_>, ) -> proc_macro2::TokenStream84 pub fn rust_borrow(
85     field: &analyzer_ast::Field,
86     scope: &lint::Scope<'_>,
87 ) -> proc_macro2::TokenStream {
88     match &field.desc {
89         ast::FieldDesc::Scalar { .. } => quote!(),
90         ast::FieldDesc::Typedef { type_id, .. } => match &scope.typedef[type_id].desc {
91             ast::DeclDesc::Enum { .. } => quote!(),
92             ast::DeclDesc::Struct { .. } => quote!(&),
93             desc => unreachable!("unexpected declaration: {desc:?}"),
94         },
95         ast::FieldDesc::Array { .. } => quote!(&),
96         _ => todo!(),
97     }
98 }
99 
100 /// Suffix for `Buf::get_*` and `BufMut::put_*` methods when reading a
101 /// value with the given `width`.
endianness_suffix(endianness: ast::EndiannessValue, width: usize) -> &'static str102 fn endianness_suffix(endianness: ast::EndiannessValue, width: usize) -> &'static str {
103     if width > 8 && endianness == ast::EndiannessValue::LittleEndian {
104         "_le"
105     } else {
106         ""
107     }
108 }
109 
110 /// Parse an unsigned integer with the given `width`.
111 ///
112 /// The generated code requires that `span` is a mutable `bytes::Buf`
113 /// value.
get_uint( endianness: ast::EndiannessValue, width: usize, span: &proc_macro2::Ident, ) -> proc_macro2::TokenStream114 pub fn get_uint(
115     endianness: ast::EndiannessValue,
116     width: usize,
117     span: &proc_macro2::Ident,
118 ) -> proc_macro2::TokenStream {
119     let suffix = endianness_suffix(endianness, width);
120     let value_type = Integer::new(width);
121     if value_type.width == width {
122         let get_u = format_ident!("get_u{}{}", value_type.width, suffix);
123         quote! {
124             #span.get_mut().#get_u()
125         }
126     } else {
127         let get_uint = format_ident!("get_uint{}", suffix);
128         let value_nbytes = proc_macro2::Literal::usize_unsuffixed(width / 8);
129         let cast = (value_type.width < 64).then(|| quote!(as #value_type));
130         quote! {
131             #span.get_mut().#get_uint(#value_nbytes) #cast
132         }
133     }
134 }
135 
136 /// Write an unsigned integer `value` to `span`.
137 ///
138 /// The generated code requires that `span` is a mutable
139 /// `bytes::BufMut` value.
put_uint( endianness: ast::EndiannessValue, value: &proc_macro2::TokenStream, width: usize, span: &proc_macro2::Ident, ) -> proc_macro2::TokenStream140 pub fn put_uint(
141     endianness: ast::EndiannessValue,
142     value: &proc_macro2::TokenStream,
143     width: usize,
144     span: &proc_macro2::Ident,
145 ) -> proc_macro2::TokenStream {
146     let suffix = endianness_suffix(endianness, width);
147     let value_type = Integer::new(width);
148     if value_type.width == width {
149         let put_u = format_ident!("put_u{}{}", width, suffix);
150         quote! {
151             #span.#put_u(#value)
152         }
153     } else {
154         let put_uint = format_ident!("put_uint{}", suffix);
155         let value_nbytes = proc_macro2::Literal::usize_unsuffixed(width / 8);
156         let cast = (value_type.width < 64).then(|| quote!(as u64));
157         quote! {
158             #span.#put_uint(#value #cast, #value_nbytes)
159         }
160     }
161 }
162 
163 #[cfg(test)]
164 mod tests {
165     use super::*;
166 
167     #[test]
test_integer_new()168     fn test_integer_new() {
169         assert_eq!(Integer::new(0).width, 8);
170         assert_eq!(Integer::new(8).width, 8);
171         assert_eq!(Integer::new(9).width, 16);
172         assert_eq!(Integer::new(64).width, 64);
173     }
174 
175     #[test]
176     #[should_panic]
test_integer_new_panics_on_large_width()177     fn test_integer_new_panics_on_large_width() {
178         Integer::new(65);
179     }
180 }
181