#![allow(
clippy::indexing_slicing,
clippy::expect_used,
reason = "bench harness over hand-built buffers: a panic on a malformed \
fixture is the report, and adding error handling would put \
branches in the code being measured"
)]
use crate::{
Error, Result,
resp::{
BufferDecoder, Command, CommandEncoder, RespBuf, RespFrameParser, RespResponse, RespTapeMut,
},
};
use bytes::{Bytes, BytesMut};
use serde::de::DeserializeOwned;
use tokio_util::codec::{Decoder, Encoder as _};
#[inline]
pub fn bench_decode_to<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
let mut tape = RespTapeMut::default();
let (frame, frame_len) = RespFrameParser::new(bytes, &mut tape).parse()?;
let buf = RespBuf::from(Bytes::copy_from_slice(&bytes[..frame_len]));
RespResponse::new(buf, frame).to()
}
#[inline]
pub fn bench_decode_chunked<T: DeserializeOwned>(chunks: &[&[u8]]) -> Result<T> {
let mut decoder = BufferDecoder::new();
let mut buf = BytesMut::new();
for chunk in chunks {
buf.extend_from_slice(chunk);
if let Some(resp) = decoder.decode(&mut buf)? {
return resp.to();
}
}
match decoder.decode(&mut buf)? {
Some(resp) => resp.to(),
None => Err(Error::EOF),
}
}
#[inline(never)]
pub fn bench_decode_stream_grow(data: &[u8], chunk: usize) -> Result<usize> {
drive_stream(data, chunk, false)
}
#[inline(never)]
pub fn bench_decode_stream_prereserve(data: &[u8], chunk: usize) -> Result<usize> {
drive_stream(data, chunk, true)
}
#[inline(always)]
fn drive_stream(data: &[u8], chunk: usize, prereserve: bool) -> Result<usize> {
use crate::client::BufferConfig;
let mut decoder = BufferDecoder::new();
let mut src = BytesMut::with_capacity(BufferConfig::DEFAULT.read_capacity);
let mut pos = 0usize;
let mut reserved = false;
loop {
if let Some(resp) = decoder.decode(&mut src)? {
let len = match std::hint::black_box(&resp) {
RespResponse::Frame { buf, .. } => buf.as_ref().len(),
_ => 0,
};
return Ok(len);
}
if pos >= data.len() {
return Err(Error::EOF);
}
if prereserve && !reserved && !src.is_empty() {
src.reserve(data.len().saturating_sub(src.len()));
reserved = true;
}
src.reserve(1);
let spare = src.capacity() - src.len();
let take = spare.min(chunk).min(data.len() - pos);
src.extend_from_slice(&data[pos..pos + take]);
pos += take;
}
}
#[derive(Default)]
pub struct BenchTape(RespTapeMut);
impl BenchTape {
pub fn new() -> Self {
Self::default()
}
}
#[inline(never)]
pub fn bench_parse_only(bytes: &[u8], tape: &mut BenchTape) {
let tape = &mut tape.0;
let (frame, frame_len) = RespFrameParser::new(bytes, tape)
.parse()
.expect("bench_parse_only fed a valid frame");
std::hint::black_box((&frame, frame_len));
}
#[inline(never)]
pub fn bench_encode_command(command: &Command, buf: &mut BytesMut) {
buf.clear();
CommandEncoder
.encode(command, buf)
.expect("bench_encode_command fed a valid command");
std::hint::black_box(&buf);
}