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