Skip to content

Commit 35305b5

Browse files
Drop decimal and octal support from bytes macros (#44)
1 parent 3f72178 commit 35305b5

File tree

3 files changed

+337
-143
lines changed

3 files changed

+337
-143
lines changed

src/bytes.rs

Lines changed: 159 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,6 @@ pub fn bytes(input: TokenStream2) -> TokenStream2 {
1111
Err(e) => return e.to_compile_error(),
1212
};
1313

14-
// Convert the integer literal into a base10 string, and into a slice of
15-
// bytes, via a big integer. The conversion should never fail because
16-
// syn::LitInt already validated the integer, unless the value is negative.
17-
// Any leading zeroes are discarded.
18-
let int = match BigUint::from_str(lit.base10_digits()) {
19-
Ok(int) => int,
20-
Err(_) => return Error::new(lit.span(), "negative values unsupported").to_compile_error(),
21-
};
22-
2314
// Get the raw integer literal as it appears in the token stream.
2415
let raw = lit.to_string();
2516

@@ -28,37 +19,31 @@ pub fn bytes(input: TokenStream2) -> TokenStream2 {
2819

2920
// Remove any leading prefix that indicates the base, and use the base to
3021
// determine how many bits per leading zero needs to be prefilled into the
31-
// bytes generated. If bits_per_digit is None, leading zero digits are
32-
// unsupported.
33-
let (form, bits_per_zero_digit, remainder) = match normalized.as_bytes() {
34-
[b'0', b'x', r @ ..] => ("hex", Some(4), r),
35-
[b'0', b'b', r @ ..] => ("binary", Some(1), r),
36-
[b'0', b'o', r @ ..] => ("octal", None, r),
37-
[r @ ..] => ("decimal", None, r),
22+
// bytes generated.
23+
let (bits_per_zero_digit, remainder) = match normalized.as_bytes() {
24+
[b'0', b'x', r @ ..] => (4, r),
25+
[b'0', b'b', r @ ..] => (1, r),
26+
_ => {
27+
return Error::new(
28+
lit.span(),
29+
"only positive hex (0x) and binary (0b) integer literals are supported",
30+
)
31+
.to_compile_error();
32+
}
3833
};
3934

35+
// Convert the integer literal into a base10 string, and into a slice of
36+
// bytes, via a big integer. The conversion should never fail because
37+
// syn::LitInt already validated the integer, and the form check above
38+
// ensures only non-negative hex/binary literals reach here.
39+
let int = BigUint::from_str(lit.base10_digits()).expect("valid hex or binary literal");
40+
4041
// Count the leading zero bits by counting the number of leading zeros and
4142
// multiplying by the bits per digit.
4243
let leading_zero_count = remainder.iter().take_while(|d| **d == b'0').count();
43-
let leading_zero_bits = if let Some(bits_per_digit) = bits_per_zero_digit {
44-
leading_zero_count
45-
.checked_mul(bits_per_digit)
46-
.expect("overflow")
47-
} else if leading_zero_count > 0 {
48-
// If there are leading zeros without a bits per digit error, since a
49-
// caller may expect the zeros to be preserved, and so it is better for
50-
// us to error. They can proceed by removing the zeros.
51-
return Error::new(
52-
lit.span(),
53-
format!(
54-
"leading zeros are not preserved or supported on integer literals in {} form",
55-
form,
56-
),
57-
)
58-
.to_compile_error();
59-
} else {
60-
0
61-
};
44+
let leading_zero_bits = leading_zero_count
45+
.checked_mul(bits_per_zero_digit)
46+
.expect("overflow");
6247

6348
// Create the final byte slice, which has length of the leading zero bytes,
6449
// followed by the big integer bytes.
@@ -84,9 +69,12 @@ mod test {
8469
#[test]
8570
fn neg() {
8671
let tokens = bytes(quote! {-0x1});
87-
let expect = Error::new(Span::call_site(), "negative values unsupported")
88-
.to_compile_error()
89-
.to_string();
72+
let expect = Error::new(
73+
Span::call_site(),
74+
"only positive hex (0x) and binary (0b) integer literals are supported",
75+
)
76+
.to_compile_error()
77+
.to_string();
9078
assert_eq!(tokens.to_string(), expect);
9179
}
9280

@@ -114,21 +102,38 @@ mod test {
114102
}
115103

116104
#[test]
117-
fn base10() {
118-
let tokens = bytes(quote! {340_282_366_920_938_463_463_374_607_431_768_211_455u128});
119-
let parsed = syn::parse2::<ExprArray>(tokens).unwrap();
120-
let expect = syn::parse_quote!([
121-
255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8,
122-
255u8, 255u8, 255u8, 255u8
123-
]);
124-
assert_eq!(parsed, expect);
125-
126-
let tokens = bytes(quote! {340_282_366_920_938_463_463_374_607_431_768_211_456});
127-
let parsed = syn::parse2::<ExprArray>(tokens).unwrap();
128-
let expect = syn::parse_quote!([
129-
1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8
130-
]);
131-
assert_eq!(parsed, expect);
105+
fn decimal_and_octal_unsupported() {
106+
let table: &[_] = &[
107+
// Decimal.
108+
quote!(0),
109+
quote!(1),
110+
quote!(9),
111+
quote!(255),
112+
quote!(256),
113+
quote!(0255),
114+
quote!(00255),
115+
quote!(00),
116+
quote!(340_282_366_920_938_463_463_374_607_431_768_211_455u128),
117+
quote!(340_282_366_920_938_463_463_374_607_431_768_211_456),
118+
// Octal.
119+
quote!(0o0),
120+
quote!(0o1),
121+
quote!(0o377),
122+
quote!(0o0377),
123+
quote!(0o00377),
124+
quote!(0o00),
125+
quote!(0o400),
126+
];
127+
let expect = Error::new(
128+
Span::call_site(),
129+
"only positive hex (0x) and binary (0b) integer literals are supported",
130+
)
131+
.to_compile_error()
132+
.to_string();
133+
for (i, input) in table.iter().enumerate() {
134+
let tokens = bytes(input.clone());
135+
assert_eq!(tokens.to_string(), expect, "table entry: {}", i);
136+
}
132137
}
133138

134139
#[test]
@@ -172,35 +177,108 @@ mod test {
172177
}
173178

174179
#[test]
175-
fn leading_zeros_prohibited() {
176-
let table: &[(_, Result<ExprArray, Error>)] = &[
177-
// Base 8.
178-
(quote!(0o377), Ok(parse_quote!([255u8]))),
179-
(quote!(0o0377), Err(Error::new(Span::call_site(), "leading zeros are not preserved or supported on integer literals in octal form"))),
180-
(quote!(0o00377), Err(Error::new(Span::call_site(), "leading zeros are not preserved or supported on integer literals in octal form"))),
181-
(quote!(0o400), Ok(parse_quote!([1u8, 0u8]))),
182-
// Base 10.
183-
(quote!(255), Ok(parse_quote!([255u8]))),
184-
(quote!(0255), Err(Error::new(Span::call_site(), "leading zeros are not preserved or supported on integer literals in decimal form"))),
185-
(quote!(00255), Err(Error::new(Span::call_site(), "leading zeros are not preserved or supported on integer literals in decimal form"))),
186-
(quote!(256), Ok(parse_quote!([1u8, 0u8]))),
180+
fn zero() {
181+
let table: &[(_, ExprArray)] = &[
182+
(quote!(0x0), parse_quote!([0u8])),
183+
(quote!(0x00), parse_quote!([0u8])),
184+
(quote!(0b0), parse_quote!([0u8])),
185+
(quote!(0b00000000), parse_quote!([0u8])),
187186
];
188-
for (i, t) in table.iter().enumerate() {
189-
let tokens = bytes(t.0.clone());
190-
match t.1.clone() {
191-
Ok(expect) => {
192-
let parsed = syn::parse2::<ExprArray>(tokens);
193-
assert_eq!(parsed.unwrap(), expect, "table entry: {}", i);
194-
}
195-
Err(e) => {
196-
assert_eq!(
197-
tokens.to_string(),
198-
e.to_compile_error().to_string(),
199-
"table entry: {}",
200-
i
201-
);
202-
}
203-
};
187+
for (i, t) in table.iter().cloned().enumerate() {
188+
let tokens = bytes(t.0);
189+
let parsed = syn::parse2::<ExprArray>(tokens).unwrap();
190+
let expect = t.1;
191+
assert_eq!(parsed, expect, "table entry: {}", i);
204192
}
205193
}
194+
195+
#[test]
196+
fn byte_boundaries() {
197+
let table: &[(_, ExprArray)] = &[
198+
// u8 max
199+
(quote!(0xff), parse_quote!([255u8])),
200+
(quote!(0b11111111), parse_quote!([255u8])),
201+
// u8 max + 1
202+
(quote!(0x100), parse_quote!([1u8, 0u8])),
203+
(quote!(0b100000000), parse_quote!([1u8, 0u8])),
204+
// u16 max
205+
(quote!(0xffff), parse_quote!([255u8, 255u8])),
206+
// u16 max + 1
207+
(quote!(0x10000), parse_quote!([1u8, 0u8, 0u8])),
208+
// u32 max
209+
(
210+
quote!(0xffffffff),
211+
parse_quote!([255u8, 255u8, 255u8, 255u8]),
212+
),
213+
// u32 max + 1
214+
(quote!(0x100000000), parse_quote!([1u8, 0u8, 0u8, 0u8, 0u8])),
215+
// u64 max
216+
(
217+
quote!(0xffffffffffffffff),
218+
parse_quote!([255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8]),
219+
),
220+
// u64 max + 1
221+
(
222+
quote!(0x10000000000000000),
223+
parse_quote!([1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8]),
224+
),
225+
// u128 max
226+
(
227+
quote!(0xffffffffffffffffffffffffffffffff),
228+
parse_quote!([
229+
255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8,
230+
255u8, 255u8, 255u8, 255u8, 255u8
231+
]),
232+
),
233+
// u128 max + 1
234+
(
235+
quote!(0x100000000000000000000000000000000),
236+
parse_quote!([
237+
1u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8, 0u8,
238+
0u8
239+
]),
240+
),
241+
];
242+
for (i, t) in table.iter().cloned().enumerate() {
243+
let tokens = bytes(t.0);
244+
let parsed = syn::parse2::<ExprArray>(tokens).unwrap();
245+
let expect = t.1;
246+
assert_eq!(parsed, expect, "table entry: {}", i);
247+
}
248+
}
249+
250+
#[test]
251+
fn one() {
252+
let table: &[(_, ExprArray)] = &[
253+
(quote!(0x1), parse_quote!([1u8])),
254+
(quote!(0b1), parse_quote!([1u8])),
255+
];
256+
for (i, t) in table.iter().cloned().enumerate() {
257+
let tokens = bytes(t.0);
258+
let parsed = syn::parse2::<ExprArray>(tokens).unwrap();
259+
let expect = t.1;
260+
assert_eq!(parsed, expect, "table entry: {}", i);
261+
}
262+
}
263+
264+
#[test]
265+
fn empty_input() {
266+
let tokens = bytes(quote! {});
267+
let expect = Error::new(
268+
Span::call_site(),
269+
"unexpected end of input, expected integer literal",
270+
)
271+
.to_compile_error()
272+
.to_string();
273+
assert_eq!(tokens.to_string(), expect);
274+
}
275+
276+
#[test]
277+
fn non_integer_input() {
278+
let tokens = bytes(quote! {"hello"});
279+
let expect = Error::new(Span::call_site(), "expected integer literal")
280+
.to_compile_error()
281+
.to_string();
282+
assert_eq!(tokens.to_string(), expect);
283+
}
206284
}

0 commit comments

Comments
 (0)