Skip to content

Commit 63cccd9

Browse files
committed
Replace get_split with split_first_chunk builtin
[`split_first_chunk`][0] is a nightly only builtin. It looks like it will be [stabillized soon][1], so I decided to use it and copy over the implementation in the meantime. Amusingly, benchmark show a consistent 3% improvement to throughput compared to using `get_split`. [0]: https://doc.rust-lang.org/std/primitive.slice.html#method.split_first_chunk [1]: rust-lang/rust#117561
1 parent 65bd6f1 commit 63cccd9

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

src/binary/de.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::{tokens::*, Rgb};
22
use crate::{
33
binary::{BinaryFlavor, FailedResolveStrategy, TokenResolver},
44
de::ColorSequence,
5-
util::get_split,
5+
util::split_first_chunk,
66
BinaryTape, BinaryToken, DeserializeError, DeserializeErrorKind, Error, ErrorKind,
77
};
88
use serde::de::{self, Deserialize, DeserializeSeed, MapAccess, SeqAccess, Visitor};
@@ -24,8 +24,8 @@ impl<'data> OndemandParser<'data> {
2424

2525
#[inline]
2626
pub fn next(&mut self) -> Option<u16> {
27-
let (data, token) =
28-
get_split::<2>(self.data).map(|(head, rest)| (rest, u16::from_le_bytes(head)))?;
27+
let (data, token) = split_first_chunk::<2>(self.data)
28+
.map(|(head, rest)| (rest, u16::from_le_bytes(*head)))?;
2929
self.data = data;
3030
Some(token)
3131
}
@@ -37,8 +37,8 @@ impl<'data> OndemandParser<'data> {
3737

3838
#[inline]
3939
pub fn read_string(&mut self) -> Result<&'data [u8], Error> {
40-
let (head, rest) = get_split::<2>(self.data).ok_or_else(Error::eof)?;
41-
let text_len = usize::from(u16::from_le_bytes(head));
40+
let (head, rest) = split_first_chunk::<2>(self.data).ok_or_else(Error::eof)?;
41+
let text_len = usize::from(u16::from_le_bytes(*head));
4242
if text_len <= rest.len() {
4343
let (text, rest) = rest.split_at(text_len);
4444
self.data = rest;
@@ -57,44 +57,44 @@ impl<'data> OndemandParser<'data> {
5757

5858
#[inline]
5959
fn read_u32(&mut self) -> Result<u32, Error> {
60-
let (head, rest) = get_split::<4>(self.data).ok_or_else(Error::eof)?;
60+
let (head, rest) = split_first_chunk::<4>(self.data).ok_or_else(Error::eof)?;
6161
self.data = rest;
62-
Ok(u32::from_le_bytes(head))
62+
Ok(u32::from_le_bytes(*head))
6363
}
6464

6565
#[inline]
6666
fn read_u64(&mut self) -> Result<u64, Error> {
67-
let (head, rest) = get_split::<8>(self.data).ok_or_else(Error::eof)?;
67+
let (head, rest) = split_first_chunk::<8>(self.data).ok_or_else(Error::eof)?;
6868
self.data = rest;
69-
Ok(u64::from_le_bytes(head))
69+
Ok(u64::from_le_bytes(*head))
7070
}
7171

7272
#[inline]
7373
fn read_i64(&mut self) -> Result<i64, Error> {
74-
let (head, rest) = get_split::<8>(self.data).ok_or_else(Error::eof)?;
74+
let (head, rest) = split_first_chunk::<8>(self.data).ok_or_else(Error::eof)?;
7575
self.data = rest;
76-
Ok(i64::from_le_bytes(head))
76+
Ok(i64::from_le_bytes(*head))
7777
}
7878

7979
#[inline]
8080
fn read_i32(&mut self) -> Result<i32, Error> {
81-
let (head, rest) = get_split::<4>(self.data).ok_or_else(Error::eof)?;
81+
let (head, rest) = split_first_chunk::<4>(self.data).ok_or_else(Error::eof)?;
8282
self.data = rest;
83-
Ok(i32::from_le_bytes(head))
83+
Ok(i32::from_le_bytes(*head))
8484
}
8585

8686
#[inline]
8787
fn read_f32(&mut self) -> Result<[u8; 4], Error> {
88-
let (head, rest) = get_split::<4>(self.data).ok_or_else(Error::eof)?;
88+
let (head, rest) = split_first_chunk::<4>(self.data).ok_or_else(Error::eof)?;
8989
self.data = rest;
90-
Ok(head)
90+
Ok(*head)
9191
}
9292

9393
#[inline]
9494
fn read_f64(&mut self) -> Result<[u8; 8], Error> {
95-
let (head, rest) = get_split::<8>(self.data).ok_or_else(Error::eof)?;
95+
let (head, rest) = split_first_chunk::<8>(self.data).ok_or_else(Error::eof)?;
9696
self.data = rest;
97-
Ok(head)
97+
Ok(*head)
9898
}
9999

100100
#[inline]

src/util.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,32 @@ fn take<const N: usize>(data: &[u8]) -> [u8; N] {
55
unsafe { *(data.as_ptr() as *const [u8; N]) }
66
}
77

8+
#[inline]
9+
pub const fn split_first_chunk<const N: usize>(data: &[u8]) -> Option<(&[u8; N], &[u8])> {
10+
#[inline]
11+
const unsafe fn split_at_unchecked(data: &[u8], mid: usize) -> (&[u8], &[u8]) {
12+
let len = data.len();
13+
let ptr = data.as_ptr();
14+
unsafe {
15+
(
16+
core::slice::from_raw_parts(ptr, mid),
17+
core::slice::from_raw_parts(ptr.add(mid), len - mid),
18+
)
19+
}
20+
}
21+
22+
if data.len() < N {
23+
None
24+
} else {
25+
// SAFETY: We manually verified the bounds of the split.
26+
let (first, tail) = unsafe { split_at_unchecked(data, N) };
27+
28+
// SAFETY: We explicitly check for the correct number of elements,
29+
// and do not let the references outlive the slice.
30+
Some((unsafe { &*(first.as_ptr().cast::<[u8; N]>()) }, tail))
31+
}
32+
}
33+
834
#[inline]
935
pub(crate) fn get_split<const N: usize>(data: &[u8]) -> Option<([u8; N], &[u8])> {
1036
data.get(N..).map(|d| (take::<N>(data), d))

0 commit comments

Comments
 (0)