Skip to content

Commit

Permalink
Merge branch 'master' into fix-attributes-regressions
Browse files Browse the repository at this point in the history
  • Loading branch information
LucioFranco authored Sep 4, 2021
2 parents c3798f5 + a4be973 commit b9867f2
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 42 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ The recommended way to add `.proto` compilation to a Cargo project is to use the
`prost-build` library. See the [`prost-build` documentation](prost-build) for
more details and examples.

See the [snazzy repository](https://github.com/danburkert/snazzy) for a simple
start-to-finish example.

## Generated Code

`prost` generates Rust code from source `.proto` files using the `proto2` or
Expand Down
68 changes: 26 additions & 42 deletions src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,19 @@ pub fn encode_varint<B>(mut value: u64, buf: &mut B)
where
B: BufMut,
{
// Safety notes:
//
// - ptr::write is an unsafe raw pointer write. The use here is safe since the length of the
// uninit slice is checked.
// - advance_mut is unsafe because it could cause uninitialized memory to be advanced over. The
// use here is safe since each byte which is advanced over has been written to in the
// previous loop iteration.
unsafe {
let mut i;
'outer: loop {
i = 0;

let uninit_slice = buf.chunk_mut();
for offset in 0..uninit_slice.len() {
i += 1;
let ptr = uninit_slice.as_mut_ptr().add(offset);
if value < 0x80 {
ptr.write(value as u8);
break 'outer;
} else {
ptr.write(((value & 0x7F) | 0x80) as u8);
value >>= 7;
}
}

buf.advance_mut(i);
debug_assert!(buf.has_remaining_mut());
loop {
if value < 0x80 {
buf.put_u8(value as u8);
break;
} else {
buf.put_u8(((value & 0x7F) | 0x80) as u8);
value >>= 7;
}

buf.advance_mut(i);
}
}

/// Decodes a LEB128-encoded variable length integer from the buffer.
#[inline]
pub fn decode_varint<B>(buf: &mut B) -> Result<u64, DecodeError>
where
B: Buf,
Expand All @@ -71,12 +50,12 @@ where
return Err(DecodeError::new("invalid varint"));
}

let byte = unsafe { *bytes.get_unchecked(0) };
let byte = bytes[0];
if byte < 0x80 {
buf.advance(1);
Ok(u64::from(byte))
} else if len > 10 || bytes[len - 1] < 0x80 {
let (value, advance) = unsafe { decode_varint_slice(bytes) }?;
let (value, advance) = decode_varint_slice(bytes)?;
buf.advance(advance);
Ok(value)
} else {
Expand All @@ -96,30 +75,34 @@ where
///
/// [1]: https://github.com/google/protobuf/blob/3.3.x/src/google/protobuf/io/coded_stream.cc#L365-L406
#[inline]
unsafe fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError> {
fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError> {
// Fully unrolled varint decoding loop. Splitting into 32-bit pieces gives better performance.

// Use assertions to ensure memory safety, but it should always be optimized after inline.
assert!(!bytes.is_empty());
assert!(bytes.len() > 10 || bytes[bytes.len() - 1] < 0x80);

let mut b: u8;
let mut part0: u32;
b = *bytes.get_unchecked(0);
b = unsafe { *bytes.get_unchecked(0) };
part0 = u32::from(b);
if b < 0x80 {
return Ok((u64::from(part0), 1));
};
part0 -= 0x80;
b = *bytes.get_unchecked(1);
b = unsafe { *bytes.get_unchecked(1) };
part0 += u32::from(b) << 7;
if b < 0x80 {
return Ok((u64::from(part0), 2));
};
part0 -= 0x80 << 7;
b = *bytes.get_unchecked(2);
b = unsafe { *bytes.get_unchecked(2) };
part0 += u32::from(b) << 14;
if b < 0x80 {
return Ok((u64::from(part0), 3));
};
part0 -= 0x80 << 14;
b = *bytes.get_unchecked(3);
b = unsafe { *bytes.get_unchecked(3) };
part0 += u32::from(b) << 21;
if b < 0x80 {
return Ok((u64::from(part0), 4));
Expand All @@ -128,25 +111,25 @@ unsafe fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError>
let value = u64::from(part0);

let mut part1: u32;
b = *bytes.get_unchecked(4);
b = unsafe { *bytes.get_unchecked(4) };
part1 = u32::from(b);
if b < 0x80 {
return Ok((value + (u64::from(part1) << 28), 5));
};
part1 -= 0x80;
b = *bytes.get_unchecked(5);
b = unsafe { *bytes.get_unchecked(5) };
part1 += u32::from(b) << 7;
if b < 0x80 {
return Ok((value + (u64::from(part1) << 28), 6));
};
part1 -= 0x80 << 7;
b = *bytes.get_unchecked(6);
b = unsafe { *bytes.get_unchecked(6) };
part1 += u32::from(b) << 14;
if b < 0x80 {
return Ok((value + (u64::from(part1) << 28), 7));
};
part1 -= 0x80 << 14;
b = *bytes.get_unchecked(7);
b = unsafe { *bytes.get_unchecked(7) };
part1 += u32::from(b) << 21;
if b < 0x80 {
return Ok((value + (u64::from(part1) << 28), 8));
Expand All @@ -155,13 +138,13 @@ unsafe fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError>
let value = value + ((u64::from(part1)) << 28);

let mut part2: u32;
b = *bytes.get_unchecked(8);
b = unsafe { *bytes.get_unchecked(8) };
part2 = u32::from(b);
if b < 0x80 {
return Ok((value + (u64::from(part2) << 56), 9));
};
part2 -= 0x80;
b = *bytes.get_unchecked(9);
b = unsafe { *bytes.get_unchecked(9) };
part2 += u32::from(b) << 7;
if b < 0x80 {
return Ok((value + (u64::from(part2) << 56), 10));
Expand All @@ -174,6 +157,7 @@ unsafe fn decode_varint_slice(bytes: &[u8]) -> Result<(u64, usize), DecodeError>
/// Decodes a LEB128-encoded variable length integer from the buffer, advancing the buffer as
/// necessary.
#[inline(never)]
#[cold]
fn decode_varint_slow<B>(buf: &mut B) -> Result<u64, DecodeError>
where
B: Buf,
Expand Down

0 comments on commit b9867f2

Please sign in to comment.