diff --git a/protobuf/src/coded_output_stream/mod.rs b/protobuf/src/coded_output_stream/mod.rs index 574d6af1c..991984ddd 100644 --- a/protobuf/src/coded_output_stream/mod.rs +++ b/protobuf/src/coded_output_stream/mod.rs @@ -265,6 +265,7 @@ impl<'a> CodedOutputStream<'a> { pub fn write_raw_varint32(&mut self, value: u32) -> crate::Result<()> { if self.buffer.unfilled_len() >= 5 { // fast path + // SAFETY: we've just checked that there's enough space in the buffer. unsafe { let len = encode_varint32(value, self.buffer.unfilled()); self.buffer.advance(len); @@ -284,6 +285,7 @@ impl<'a> CodedOutputStream<'a> { pub fn write_raw_varint64(&mut self, value: u64) -> crate::Result<()> { if self.buffer.unfilled_len() >= MAX_VARINT_ENCODED_LEN { // fast path + // SAFETY: we've just checked that there's enough space in the buffer. unsafe { let len = encode_varint64(value, self.buffer.unfilled()); self.buffer.advance(len); @@ -301,12 +303,38 @@ impl<'a> CodedOutputStream<'a> { /// Write 32-bit integer little endian pub fn write_raw_little_endian32(&mut self, value: u32) -> crate::Result<()> { - self.write_raw_bytes(&value.to_le_bytes()) + if self.buffer.unfilled_len() >= 4 { + // fast path + // SAFETY: we've just checked that there's enough space in the buffer. + unsafe { + let buf = self.buffer.unfilled(); + let bytes = value.to_le_bytes(); + ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr() as *mut u8, 4); + self.buffer.advance(4); + }; + Ok(()) + } else { + // slow path + self.write_raw_bytes(&value.to_le_bytes()) + } } /// Write 64-bit integer little endian pub fn write_raw_little_endian64(&mut self, value: u64) -> crate::Result<()> { - self.write_raw_bytes(&value.to_le_bytes()) + if self.buffer.unfilled_len() >= 8 { + // fast path + // SAFETY: we've just checked that there's enough space in the buffer. + unsafe { + let buf = self.buffer.unfilled(); + let bytes = value.to_le_bytes(); + ptr::copy_nonoverlapping(bytes.as_ptr(), buf.as_mut_ptr() as *mut u8, 8); + self.buffer.advance(8); + }; + Ok(()) + } else { + // slow path + self.write_raw_bytes(&value.to_le_bytes()) + } } /// Write `float` diff --git a/test-crates/perftest/misc/benches/coded_output_stream.rs b/test-crates/perftest/misc/benches/coded_output_stream.rs index 8aed4d820..a6fcabaf9 100644 --- a/test-crates/perftest/misc/benches/coded_output_stream.rs +++ b/test-crates/perftest/misc/benches/coded_output_stream.rs @@ -63,3 +63,33 @@ fn bench_write_raw_varint_32(b: &mut Bencher) { v.len() }) } + +#[bench] +fn bench_write_raw_fixed32(b: &mut Bencher) { + let mut v = Vec::with_capacity(10_000); + b.iter(|| { + v.clear(); + { + let mut os = CodedOutputStream::new(&mut v); + for i in 0..1000 { + os.write_fixed32_no_tag(i * 139 % 1000).unwrap(); + } + } + v.len() + }) +} + +#[bench] +fn bench_write_raw_fixed64(b: &mut Bencher) { + let mut v = Vec::with_capacity(10_000); + b.iter(|| { + v.clear(); + { + let mut os = CodedOutputStream::new(&mut v); + for i in 0..1000 { + os.write_fixed64_no_tag(i * 12345678 % 100000000).unwrap(); + } + } + v.len() + }) +}