Skip to content

Commit

Permalink
Add Payload::Vec variant to aid write perf of Vec & Strings
Browse files Browse the repository at this point in the history
  • Loading branch information
alexheretic authored and daniel-abramov committed Dec 14, 2024
1 parent 7c5c3d2 commit 491d825
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 25 deletions.
14 changes: 2 additions & 12 deletions benches/write.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! Benchmarks for write performance.
use bytes::{BufMut, BytesMut};
use criterion::Criterion;
use std::{
fmt::Write as _,
hint, io,
time::{Duration, Instant},
};
Expand Down Expand Up @@ -55,19 +53,11 @@ fn benchmark(c: &mut Criterion) {
let mut ws =
WebSocket::from_raw_socket(MockWrite(Vec::with_capacity(MOCK_WRITE_LEN)), role, None);

let mut buf = BytesMut::with_capacity(128 * 1024);

b.iter(|| {
for i in 0_u64..100_000 {
let msg = match i {
_ if i % 3 == 0 => {
buf.put_slice(&i.to_le_bytes());
Message::binary(buf.split())
}
_ => {
buf.write_fmt(format_args!("{{\"id\":{i}}}")).unwrap();
Message::Text(buf.split().try_into().unwrap())
}
_ if i % 3 == 0 => Message::binary(i.to_le_bytes().to_vec()),
_ => Message::text(format!("{{\"id\":{i}}}")),
};
ws.write(msg).unwrap();
}
Expand Down
2 changes: 1 addition & 1 deletion src/protocol/frame/frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ impl Frame {
0 => Ok(None),
1 => Err(Error::Protocol(ProtocolError::InvalidCloseSequence)),
_ => {
let mut data = self.payload.into_data();
let mut data = self.payload.as_slice();
let code = u16::from_be_bytes([data[0], data[1]]).into();
data.advance(2);
let text = String::from_utf8(data.to_vec())?;
Expand Down
4 changes: 2 additions & 2 deletions src/protocol/frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ impl FrameCodec {
pub(super) fn new() -> Self {
Self {
in_buffer: BytesMut::with_capacity(READ_BUFFER_CAP),
out_buffer: Vec::new(),
out_buffer: <_>::default(),
max_out_buffer_len: usize::MAX,
out_buffer_write_len: 0,
header: None,
Expand All @@ -134,7 +134,7 @@ impl FrameCodec {
in_buffer.reserve(READ_BUFFER_CAP.saturating_sub(in_buffer.len()));
Self {
in_buffer,
out_buffer: Vec::new(),
out_buffer: <_>::default(),
max_out_buffer_len: usize::MAX,
out_buffer_write_len: 0,
header: None,
Expand Down
29 changes: 19 additions & 10 deletions src/protocol/frame/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,15 @@ impl TryFrom<BytesMut> for Utf8Payload {
}
}

impl TryFrom<Vec<u8>> for Utf8Payload {
type Error = str::Utf8Error;

#[inline]
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
Payload::from(bytes).try_into()
}
}

impl From<String> for Utf8Payload {
#[inline]
fn from(s: String) -> Self {
Expand Down Expand Up @@ -104,6 +113,8 @@ pub enum Payload {
Owned(BytesMut),
/// Shared data with shared ownership.
Shared(Bytes),
/// Owned vec data.
Vec(Vec<u8>),
}

impl Payload {
Expand Down Expand Up @@ -135,6 +146,7 @@ impl Payload {
match self {
Payload::Owned(v) => v,
Payload::Shared(v) => v,
Payload::Vec(v) => v,
}
}

Expand All @@ -148,33 +160,30 @@ impl Payload {
pub fn as_mut_slice(&mut self) -> &mut [u8] {
match self {
Payload::Owned(v) => &mut *v,
Payload::Vec(v) => &mut *v,
Payload::Shared(v) => {
// Using `Bytes::to_vec()` or `Vec::from(bytes.as_ref())` would mean making a copy.
// `Bytes::into()` would not make a copy if our `Bytes` instance is the only one.
let data = mem::take(v).into();
*self = Payload::Owned(data);
match self {
Payload::Owned(v) => v,
Payload::Shared(_) => unreachable!(),
_ => unreachable!(),
}
}
}
}

/// Returns the length of the payload.
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.as_slice().len()
}

/// Consumes the payload and returns the underlying data as a vector.
/// Returns true if the payload has a length of 0.
#[inline]
pub fn into_data(self) -> BytesMut {
match self {
Payload::Owned(v) => v,
Payload::Shared(v) => v.into(),
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}

/// Consumes the payload and returns the underlying data as a string.
Expand All @@ -194,14 +203,14 @@ impl Default for Payload {
impl From<Vec<u8>> for Payload {
#[inline]
fn from(v: Vec<u8>) -> Self {
Payload::Owned(BytesMut::from_iter(v))
Payload::Vec(v)
}
}

impl From<String> for Payload {
#[inline]
fn from(v: String) -> Self {
Vec::from(v).into()
v.into_bytes().into()
}
}

Expand Down

0 comments on commit 491d825

Please sign in to comment.