Skip to content

Commit e73b238

Browse files
authored
refactor: Bytes inner arithmetic (#138)
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 cdabb70 commit e73b238

File tree

1 file changed

+85
-38
lines changed

1 file changed

+85
-38
lines changed

src/iter.rs

Lines changed: 85 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,155 @@
1-
use core::slice;
21
use core::convert::TryInto;
32
use core::convert::TryFrom;
43

54
#[allow(missing_docs)]
65
pub struct Bytes<'a> {
7-
slice: &'a [u8],
8-
pos: usize
6+
start: *const u8,
7+
end: *const u8,
8+
cursor: *const u8,
9+
phantom: core::marker::PhantomData<&'a ()>,
910
}
1011

1112
#[allow(missing_docs)]
1213
impl<'a> Bytes<'a> {
1314
#[inline]
1415
pub fn new(slice: &'a [u8]) -> Bytes<'a> {
16+
let start = slice.as_ptr();
17+
let end = unsafe { start.add(slice.len()) };
18+
let cursor = start;
1519
Bytes {
16-
slice,
17-
pos: 0
20+
start,
21+
end,
22+
cursor,
23+
phantom: core::marker::PhantomData,
1824
}
1925
}
2026

2127
#[inline]
2228
pub fn pos(&self) -> usize {
23-
self.pos
29+
self.cursor as usize - self.start as usize
2430
}
2531

2632
#[inline]
2733
pub fn peek(&self) -> Option<u8> {
28-
self.peek_ahead(0)
34+
if self.cursor < self.end {
35+
// SAFETY: bounds checked
36+
Some(unsafe { *self.cursor })
37+
} else {
38+
None
39+
}
2940
}
3041

3142
#[inline]
3243
pub fn peek_ahead(&self, n: usize) -> Option<u8> {
33-
self.slice.get(self.pos + n).copied()
44+
let ptr = unsafe { self.cursor.add(n) };
45+
if ptr < self.end {
46+
// SAFETY: bounds checked
47+
Some(unsafe { *ptr })
48+
} else {
49+
None
50+
}
3451
}
35-
52+
3653
#[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()
54+
pub fn peek_n<'b: 'a, U: TryFrom<&'a [u8]>>(&'b self, n: usize) -> Option<U> {
55+
// TODO: once we bump MSRC, use const generics to allow only [u8; N] reads
56+
// TODO: drop `n` arg in favour of const
57+
// let n = core::mem::size_of::<U>();
58+
self.as_ref().get(..n)?.try_into().ok()
3959
}
4060

4161
#[inline]
4262
pub unsafe fn bump(&mut self) {
43-
debug_assert!(self.pos < self.slice.len(), "overflow");
44-
self.pos += 1;
63+
self.advance(1)
4564
}
4665

47-
#[allow(unused)]
4866
#[inline]
4967
pub unsafe fn advance(&mut self, n: usize) {
50-
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
51-
self.pos += n;
68+
self.cursor = self.cursor.add(n);
69+
debug_assert!(self.cursor <= self.end, "overflow");
5270
}
5371

5472
#[inline]
5573
pub fn len(&self) -> usize {
56-
self.slice.len()
74+
self.end as usize - self.cursor as usize
5775
}
5876

5977
#[inline]
6078
pub fn slice(&mut self) -> &'a [u8] {
6179
// not moving position at all, so it's safe
62-
unsafe {
63-
self.slice_skip(0)
64-
}
80+
let slice = unsafe { slice_from_ptr_range(self.start, self.cursor) };
81+
self.commit();
82+
slice
6583
}
6684

85+
// TODO: this is an anti-pattern, should be removed
6786
#[inline]
6887
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;
88+
debug_assert!(self.cursor.sub(skip) >= self.start);
89+
let head = slice_from_ptr_range(self.start, self.cursor.sub(skip));
90+
self.commit();
7691
head
7792
}
93+
94+
#[inline]
95+
pub fn commit(&mut self) {
96+
self.start = self.cursor
97+
}
7898

7999
#[inline]
80100
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;
101+
self.advance(n);
102+
self.commit();
103+
}
104+
105+
#[inline]
106+
pub fn as_ptr(&self) -> *const u8 {
107+
self.cursor
108+
}
109+
110+
#[inline]
111+
pub fn start(&self) -> *const u8 {
112+
self.start
113+
}
114+
115+
#[inline]
116+
pub fn end(&self) -> *const u8 {
117+
self.end
118+
}
119+
120+
#[inline]
121+
pub unsafe fn set_cursor(&mut self, ptr: *const u8) {
122+
debug_assert!(ptr >= self.start);
123+
debug_assert!(ptr <= self.end);
124+
self.cursor = ptr;
87125
}
88126
}
89127

90128
impl<'a> AsRef<[u8]> for Bytes<'a> {
91129
#[inline]
92130
fn as_ref(&self) -> &[u8] {
93-
&self.slice[self.pos..]
131+
unsafe { slice_from_ptr_range(self.cursor, self.end) }
94132
}
95133
}
96134

135+
#[inline]
136+
unsafe fn slice_from_ptr_range<'a>(start: *const u8, end: *const u8) -> &'a [u8] {
137+
debug_assert!(start <= end);
138+
core::slice::from_raw_parts(start, end as usize - start as usize)
139+
}
140+
97141
impl<'a> Iterator for Bytes<'a> {
98142
type Item = u8;
99143

100144
#[inline]
101145
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)
146+
if self.cursor < self.end {
147+
// SAFETY: bounds checked
148+
unsafe {
149+
let b = *self.cursor;
150+
self.bump();
151+
Some(b)
152+
}
106153
} else {
107154
None
108155
}

0 commit comments

Comments
 (0)