rustc_serialize: fix incorrect signed LEB128 decoding
The signed LEB128 decoding function used a hardcoded constant of 64 instead of the number of bits in the type of integer being decoded, which resulted in incorrect results for some inputs. Fix this, make the decoding more consistent with the unsigned version, and increase the LEB128 encoding and decoding test coverage.
This commit is contained in:
parent
52f21791fb
commit
f15fae822e
4 changed files with 88 additions and 57 deletions
|
@ -119,28 +119,38 @@ impl_write_signed_leb128!(write_i64_leb128, i64);
|
||||||
impl_write_signed_leb128!(write_i128_leb128, i128);
|
impl_write_signed_leb128!(write_i128_leb128, i128);
|
||||||
impl_write_signed_leb128!(write_isize_leb128, isize);
|
impl_write_signed_leb128!(write_isize_leb128, isize);
|
||||||
|
|
||||||
#[inline]
|
macro_rules! impl_read_signed_leb128 {
|
||||||
pub fn read_signed_leb128(data: &[u8], start_position: usize) -> (i128, usize) {
|
($fn_name:ident, $int_ty:ty) => {
|
||||||
let mut result = 0;
|
#[inline]
|
||||||
let mut shift = 0;
|
pub fn $fn_name(slice: &[u8]) -> ($int_ty, usize) {
|
||||||
let mut position = start_position;
|
let mut result = 0;
|
||||||
let mut byte;
|
let mut shift = 0;
|
||||||
|
let mut position = 0;
|
||||||
|
let mut byte;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
byte = data[position];
|
byte = slice[position];
|
||||||
position += 1;
|
position += 1;
|
||||||
result |= i128::from(byte & 0x7F) << shift;
|
result |= <$int_ty>::from(byte & 0x7F) << shift;
|
||||||
shift += 7;
|
shift += 7;
|
||||||
|
|
||||||
if (byte & 0x80) == 0 {
|
if (byte & 0x80) == 0 {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shift < <$int_ty>::BITS) && ((byte & 0x40) != 0) {
|
||||||
|
// sign extend
|
||||||
|
result |= (!0 << shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
(result, position)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
if (shift < 64) && ((byte & 0x40) != 0) {
|
|
||||||
// sign extend
|
|
||||||
result |= -(1 << shift);
|
|
||||||
}
|
|
||||||
|
|
||||||
(result, position - start_position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_read_signed_leb128!(read_i16_leb128, i16);
|
||||||
|
impl_read_signed_leb128!(read_i32_leb128, i32);
|
||||||
|
impl_read_signed_leb128!(read_i64_leb128, i64);
|
||||||
|
impl_read_signed_leb128!(read_i128_leb128, i128);
|
||||||
|
impl_read_signed_leb128!(read_isize_leb128, isize);
|
||||||
|
|
|
@ -17,6 +17,7 @@ Core encoding and decoding interfaces.
|
||||||
#![feature(min_specialization)]
|
#![feature(min_specialization)]
|
||||||
#![feature(vec_spare_capacity)]
|
#![feature(vec_spare_capacity)]
|
||||||
#![feature(core_intrinsics)]
|
#![feature(core_intrinsics)]
|
||||||
|
#![feature(int_bits_const)]
|
||||||
#![feature(maybe_uninit_slice)]
|
#![feature(maybe_uninit_slice)]
|
||||||
#![feature(new_uninit)]
|
#![feature(new_uninit)]
|
||||||
#![cfg_attr(test, feature(test))]
|
#![cfg_attr(test, feature(test))]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::leb128::{self, max_leb128_len, read_signed_leb128};
|
use crate::leb128::{self, max_leb128_len};
|
||||||
use crate::serialize;
|
use crate::serialize;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -561,7 +561,7 @@ impl<'a> Decoder<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! read_uleb128 {
|
macro_rules! read_leb128 {
|
||||||
($dec:expr, $fun:ident) => {{
|
($dec:expr, $fun:ident) => {{
|
||||||
let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]);
|
let (value, bytes_read) = leb128::$fun(&$dec.data[$dec.position..]);
|
||||||
$dec.position += bytes_read;
|
$dec.position += bytes_read;
|
||||||
|
@ -569,14 +569,6 @@ macro_rules! read_uleb128 {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! read_sleb128 {
|
|
||||||
($dec:expr, $t:ty) => {{
|
|
||||||
let (value, bytes_read) = read_signed_leb128($dec.data, $dec.position);
|
|
||||||
$dec.position += bytes_read;
|
|
||||||
Ok(value as $t)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> serialize::Decoder for Decoder<'a> {
|
impl<'a> serialize::Decoder for Decoder<'a> {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
|
@ -587,22 +579,22 @@ impl<'a> serialize::Decoder for Decoder<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u128(&mut self) -> Result<u128, Self::Error> {
|
fn read_u128(&mut self) -> Result<u128, Self::Error> {
|
||||||
read_uleb128!(self, read_u128_leb128)
|
read_leb128!(self, read_u128_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u64(&mut self) -> Result<u64, Self::Error> {
|
fn read_u64(&mut self) -> Result<u64, Self::Error> {
|
||||||
read_uleb128!(self, read_u64_leb128)
|
read_leb128!(self, read_u64_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u32(&mut self) -> Result<u32, Self::Error> {
|
fn read_u32(&mut self) -> Result<u32, Self::Error> {
|
||||||
read_uleb128!(self, read_u32_leb128)
|
read_leb128!(self, read_u32_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_u16(&mut self) -> Result<u16, Self::Error> {
|
fn read_u16(&mut self) -> Result<u16, Self::Error> {
|
||||||
read_uleb128!(self, read_u16_leb128)
|
read_leb128!(self, read_u16_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -614,27 +606,27 @@ impl<'a> serialize::Decoder for Decoder<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_usize(&mut self) -> Result<usize, Self::Error> {
|
fn read_usize(&mut self) -> Result<usize, Self::Error> {
|
||||||
read_uleb128!(self, read_usize_leb128)
|
read_leb128!(self, read_usize_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i128(&mut self) -> Result<i128, Self::Error> {
|
fn read_i128(&mut self) -> Result<i128, Self::Error> {
|
||||||
read_sleb128!(self, i128)
|
read_leb128!(self, read_i128_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i64(&mut self) -> Result<i64, Self::Error> {
|
fn read_i64(&mut self) -> Result<i64, Self::Error> {
|
||||||
read_sleb128!(self, i64)
|
read_leb128!(self, read_i64_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i32(&mut self) -> Result<i32, Self::Error> {
|
fn read_i32(&mut self) -> Result<i32, Self::Error> {
|
||||||
read_sleb128!(self, i32)
|
read_leb128!(self, read_i32_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_i16(&mut self) -> Result<i16, Self::Error> {
|
fn read_i16(&mut self) -> Result<i16, Self::Error> {
|
||||||
read_sleb128!(self, i16)
|
read_leb128!(self, read_i16_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -646,7 +638,7 @@ impl<'a> serialize::Decoder for Decoder<'a> {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read_isize(&mut self) -> Result<isize, Self::Error> {
|
fn read_isize(&mut self) -> Result<isize, Self::Error> {
|
||||||
read_sleb128!(self, isize)
|
read_leb128!(self, read_isize_leb128)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![feature(int_bits_const)]
|
||||||
#![feature(maybe_uninit_slice)]
|
#![feature(maybe_uninit_slice)]
|
||||||
#![feature(maybe_uninit_uninit_array)]
|
#![feature(maybe_uninit_uninit_array)]
|
||||||
|
|
||||||
|
@ -8,16 +9,28 @@ macro_rules! impl_test_unsigned_leb128 {
|
||||||
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
|
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
|
// Test 256 evenly spaced values of integer range,
|
||||||
|
// integer max value, and some "random" numbers.
|
||||||
|
let mut values = Vec::new();
|
||||||
|
|
||||||
|
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
|
||||||
|
values.extend((0..256).map(|i| $int_ty::MIN + i * increment));
|
||||||
|
|
||||||
|
values.push($int_ty::MAX);
|
||||||
|
|
||||||
|
values.extend(
|
||||||
|
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFu64 as $int_ty)),
|
||||||
|
);
|
||||||
|
|
||||||
let mut stream = Vec::new();
|
let mut stream = Vec::new();
|
||||||
|
|
||||||
for x in 0..62 {
|
for &x in &values {
|
||||||
let mut buf = MaybeUninit::uninit_array();
|
let mut buf = MaybeUninit::uninit_array();
|
||||||
stream.extend($write_fn_name(&mut buf, (3u64 << x) as $int_ty));
|
stream.extend($write_fn_name(&mut buf, x));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut position = 0;
|
let mut position = 0;
|
||||||
for x in 0..62 {
|
for &expected in &values {
|
||||||
let expected = (3u64 << x) as $int_ty;
|
|
||||||
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
|
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
position += bytes_read;
|
position += bytes_read;
|
||||||
|
@ -34,12 +47,28 @@ impl_test_unsigned_leb128!(test_u128_leb128, write_u128_leb128, read_u128_leb128
|
||||||
impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize);
|
impl_test_unsigned_leb128!(test_usize_leb128, write_usize_leb128, read_usize_leb128, usize);
|
||||||
|
|
||||||
macro_rules! impl_test_signed_leb128 {
|
macro_rules! impl_test_signed_leb128 {
|
||||||
($test_name:ident, $write_fn_name:ident, $int_ty:ident) => {
|
($test_name:ident, $write_fn_name:ident, $read_fn_name:ident, $int_ty:ident) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $test_name() {
|
fn $test_name() {
|
||||||
let values: Vec<_> = (-500..500)
|
// Test 256 evenly spaced values of integer range,
|
||||||
.map(|i| ((i as $int_ty).wrapping_mul(0x12345789ABCDEF_i64 as $int_ty)))
|
// integer max value, and some "random" numbers.
|
||||||
.collect();
|
let mut values = Vec::new();
|
||||||
|
|
||||||
|
let mut value = $int_ty::MIN;
|
||||||
|
let increment = (1 as $int_ty) << ($int_ty::BITS - 8);
|
||||||
|
|
||||||
|
for _ in 0..256 {
|
||||||
|
values.push(value);
|
||||||
|
// The addition in the last loop iteration overflows.
|
||||||
|
value = value.wrapping_add(increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
values.push($int_ty::MAX);
|
||||||
|
|
||||||
|
values.extend(
|
||||||
|
(-500..500).map(|i| (i as $int_ty).wrapping_mul(0x12345789ABCDEFi64 as $int_ty)),
|
||||||
|
);
|
||||||
|
|
||||||
let mut stream = Vec::new();
|
let mut stream = Vec::new();
|
||||||
|
|
||||||
for &x in &values {
|
for &x in &values {
|
||||||
|
@ -48,9 +77,8 @@ macro_rules! impl_test_signed_leb128 {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut position = 0;
|
let mut position = 0;
|
||||||
for &x in &values {
|
for &expected in &values {
|
||||||
let expected = x as i128;
|
let (actual, bytes_read) = $read_fn_name(&stream[position..]);
|
||||||
let (actual, bytes_read) = read_signed_leb128(&mut stream, position);
|
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
position += bytes_read;
|
position += bytes_read;
|
||||||
}
|
}
|
||||||
|
@ -59,8 +87,8 @@ macro_rules! impl_test_signed_leb128 {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, i16);
|
impl_test_signed_leb128!(test_i16_leb128, write_i16_leb128, read_i16_leb128, i16);
|
||||||
impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, i32);
|
impl_test_signed_leb128!(test_i32_leb128, write_i32_leb128, read_i32_leb128, i32);
|
||||||
impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, i64);
|
impl_test_signed_leb128!(test_i64_leb128, write_i64_leb128, read_i64_leb128, i64);
|
||||||
impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, i128);
|
impl_test_signed_leb128!(test_i128_leb128, write_i128_leb128, read_i128_leb128, i128);
|
||||||
impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, isize);
|
impl_test_signed_leb128!(test_isize_leb128, write_isize_leb128, read_isize_leb128, isize);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue