Skip to content

Commit 5352d27

Browse files
committed
refactor: Bytes inner arithmetic
IMO cleaner pointer arithmetic avoiding unnecessary intermediate slices. No perf gains in isolation but facilitates some (upcoming PRs) This is conceptually easier to reason about, end is fixed, "start" and "cursor" advance monotonically
1 parent fbb0bdd commit 5352d27

File tree

1 file changed

+92
-42
lines changed

1 file changed

+92
-42
lines changed

src/iter.rs

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,158 @@
1-
use core::slice;
2-
use core::convert::TryInto;
3-
use core::convert::TryFrom;
4-
51
#[allow(missing_docs)]
62
pub struct Bytes<'a> {
7-
slice: &'a [u8],
8-
pos: usize
3+
start: *const u8,
4+
end: *const u8,
5+
cursor: *const u8,
6+
phantom: core::marker::PhantomData<&'a ()>,
97
}
108

119
#[allow(missing_docs)]
1210
impl<'a> Bytes<'a> {
1311
#[inline]
1412
pub fn new(slice: &'a [u8]) -> Bytes<'a> {
13+
let start = slice.as_ptr();
14+
let end = unsafe { start.add(slice.len()) };
15+
let cursor = start;
1516
Bytes {
16-
slice,
17-
pos: 0
17+
start,
18+
end,
19+
cursor,
20+
phantom: core::marker::PhantomData,
1821
}
1922
}
2023

2124
#[inline]
2225
pub fn pos(&self) -> usize {
23-
self.pos
26+
self.cursor as usize - self.start as usize
2427
}
2528

2629
#[inline]
2730
pub fn peek(&self) -> Option<u8> {
28-
self.peek_ahead(0)
31+
if self.cursor < self.end {
32+
// SAFETY: bounds checked
33+
Some(unsafe { *self.cursor })
34+
} else {
35+
None
36+
}
2937
}
3038

3139
#[inline]
3240
pub fn peek_ahead(&self, n: usize) -> Option<u8> {
33-
self.slice.get(self.pos + n).copied()
41+
let ptr = unsafe { self.cursor.add(n) };
42+
if ptr < self.end {
43+
// SAFETY: bounds checked
44+
Some(unsafe { *ptr })
45+
} else {
46+
None
47+
}
3448
}
35-
49+
3650
#[inline]
37-
pub fn peek_n<U: TryFrom<&'a[u8]>>(&self, n: usize) -> Option<U> {
38-
self.slice.get(self.pos..self.pos + n)?.try_into().ok()
51+
pub fn peek_n<U>(&self, n: usize) -> Option<U> {
52+
// TODO: drop `n` arg in favour of const
53+
// let n = core::mem::size_of::<U>();
54+
// Boundary check then read array from ptr
55+
if self.len() >= n {
56+
let ptr = self.cursor as *const U;
57+
let x = unsafe { core::ptr::read_unaligned(ptr) };
58+
Some(x)
59+
} else {
60+
None
61+
}
3962
}
4063

4164
#[inline]
4265
pub unsafe fn bump(&mut self) {
43-
debug_assert!(self.pos < self.slice.len(), "overflow");
44-
self.pos += 1;
66+
self.advance(1)
4567
}
4668

47-
#[allow(unused)]
4869
#[inline]
4970
pub unsafe fn advance(&mut self, n: usize) {
50-
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
51-
self.pos += n;
71+
self.cursor = self.cursor.add(n);
72+
debug_assert!(self.cursor <= self.end, "overflow");
5273
}
5374

5475
#[inline]
5576
pub fn len(&self) -> usize {
56-
self.slice.len()
77+
self.end as usize - self.cursor as usize
5778
}
5879

5980
#[inline]
6081
pub fn slice(&mut self) -> &'a [u8] {
6182
// not moving position at all, so it's safe
62-
unsafe {
63-
self.slice_skip(0)
64-
}
83+
let slice = unsafe { slice_from_ptr_range(self.start, self.cursor) };
84+
self.commit();
85+
slice
6586
}
6687

88+
// TODO: this is an anti-pattern, should be removed
6789
#[inline]
6890
pub unsafe fn slice_skip(&mut self, skip: usize) -> &'a [u8] {
69-
debug_assert!(self.pos >= skip);
70-
let head_pos = self.pos - skip;
71-
let ptr = self.slice.as_ptr();
72-
let head = slice::from_raw_parts(ptr, head_pos);
73-
let tail = slice::from_raw_parts(ptr.add(self.pos), self.slice.len() - self.pos);
74-
self.pos = 0;
75-
self.slice = tail;
91+
debug_assert!(self.cursor.sub(skip) >= self.start);
92+
let head = slice_from_ptr_range(self.start, self.cursor.sub(skip));
93+
self.commit();
7694
head
7795
}
96+
97+
#[inline]
98+
pub fn commit(&mut self) {
99+
self.start = self.cursor
100+
}
78101

79102
#[inline]
80103
pub unsafe fn advance_and_commit(&mut self, n: usize) {
81-
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
82-
self.pos += n;
83-
let ptr = self.slice.as_ptr();
84-
let tail = slice::from_raw_parts(ptr.add(n), self.slice.len() - n);
85-
self.pos = 0;
86-
self.slice = tail;
104+
self.advance(n);
105+
self.commit();
106+
}
107+
108+
#[inline]
109+
pub fn as_ptr(&self) -> *const u8 {
110+
self.cursor
111+
}
112+
113+
#[inline]
114+
pub fn start(&self) -> *const u8 {
115+
self.start
116+
}
117+
118+
#[inline]
119+
pub fn end(&self) -> *const u8 {
120+
self.end
121+
}
122+
123+
#[inline]
124+
pub unsafe fn set_cursor(&mut self, ptr: *const u8) {
125+
debug_assert!(ptr >= self.start);
126+
debug_assert!(ptr <= self.end);
127+
self.cursor = ptr;
87128
}
88129
}
89130

90131
impl<'a> AsRef<[u8]> for Bytes<'a> {
91132
#[inline]
92-
fn as_ref(&self) -> &[u8] {
93-
&self.slice[self.pos..]
133+
fn as_ref(&self) -> &'a [u8] {
134+
unsafe { slice_from_ptr_range(self.cursor, self.end) }
94135
}
95136
}
96137

138+
#[inline]
139+
unsafe fn slice_from_ptr_range<'a>(start: *const u8, end: *const u8) -> &'a [u8] {
140+
debug_assert!(start <= end);
141+
core::slice::from_raw_parts(start, end as usize - start as usize)
142+
}
143+
97144
impl<'a> Iterator for Bytes<'a> {
98145
type Item = u8;
99146

100147
#[inline]
101148
fn next(&mut self) -> Option<u8> {
102-
if self.slice.len() > self.pos {
103-
let b = unsafe { *self.slice.get_unchecked(self.pos) };
104-
self.pos += 1;
105-
Some(b)
149+
if self.cursor < self.end {
150+
// SAFETY: bounds checked
151+
unsafe {
152+
let b = *self.cursor;
153+
self.bump();
154+
Some(b)
155+
}
106156
} else {
107157
None
108158
}

0 commit comments

Comments
 (0)