Skip to content

Commit 9f40a8b

Browse files
experiment: go back to unsafe
1 parent 6d61968 commit 9f40a8b

File tree

2 files changed

+45
-117
lines changed

2 files changed

+45
-117
lines changed

src/decoding/ringbuffer.rs

Lines changed: 45 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use alloc::collections::VecDeque;
2-
use core::cmp;
2+
use core::{cmp, mem::MaybeUninit};
33

44
pub struct RingBuffer {
55
buf: VecDeque<u8>,
@@ -78,7 +78,7 @@ impl RingBuffer {
7878

7979
/// Copies elements from the provided range to the end of the buffer.
8080
#[allow(dead_code)]
81-
pub fn extend_from_within(&mut self, mut start: usize, len: usize) {
81+
pub fn extend_from_within(&mut self, start: usize, mut len: usize) {
8282
if start + len > self.len() {
8383
panic!(
8484
"Calls to this functions must respect start ({}) + len ({}) <= self.len() ({})!",
@@ -88,126 +88,55 @@ impl RingBuffer {
8888
);
8989
}
9090

91-
// Naive and cheaper implementation (for small lengths)
92-
if len <= 12 {
93-
self.reserve(len);
94-
for i in 0..len {
95-
let byte = self.get(start + i).unwrap();
96-
self.push_back(byte);
91+
self.reserve(len);
92+
93+
let mut buf = [MaybeUninit::<u8>::uninit(); 2048];
94+
let mut start = start;
95+
while len > 0 {
96+
let round_len = cmp::min(len, buf.len());
97+
let mut remaining_len = round_len;
98+
99+
let (a, b) = self.buf.as_slices();
100+
let b = if start < a.len() {
101+
let a = &a[start..];
102+
let end = cmp::min(a.len(), remaining_len);
103+
unsafe {
104+
buf.as_mut_ptr()
105+
.cast::<u8>()
106+
.copy_from_nonoverlapping(a.as_ptr(), end);
107+
}
108+
remaining_len -= end;
109+
b
110+
} else {
111+
unsafe { b.get_unchecked(start - a.len()..) }
112+
};
113+
114+
if remaining_len > 0 {
115+
unsafe {
116+
buf.as_mut_ptr()
117+
.cast::<u8>()
118+
.add(round_len - remaining_len)
119+
.copy_from_nonoverlapping(b.as_ptr(), remaining_len);
120+
}
97121
}
98122

99-
return;
123+
/*
124+
let mut i = 0;
125+
self.buf.iter().skip(start).take(len).for_each(|&b| unsafe {
126+
*buf.get_unchecked_mut(i) = MaybeUninit::new(b);
127+
i += 1;
128+
});
129+
*/
130+
131+
self.buf.extend(unsafe {
132+
std::slice::from_raw_parts(buf.as_ptr().cast::<u8>(), round_len)
133+
});
134+
len -= round_len;
135+
start += round_len;
100136
}
101-
102-
let original_len = self.len();
103-
let mut intermediate = {
104-
IntermediateRingBuffer {
105-
this: self,
106-
original_len,
107-
}
108-
};
109-
110-
intermediate.this.buf.extend((0..len).map(|_| 0));
111-
debug_assert_eq!(intermediate.this.buf.len(), original_len + len);
112-
113-
let (a, b, a_spare, b_spare) = intermediate.as_slices_spare_mut();
114-
debug_assert_eq!(a_spare.len() + b_spare.len(), len);
115-
116-
let skip = cmp::min(a.len(), start);
117-
start -= skip;
118-
let a = &a[skip..];
119-
let b = &b[start..];
120-
121-
let mut remaining_copy_len = len;
122-
123-
// A -> A Spare
124-
let copy_at_least = cmp::min(cmp::min(a.len(), a_spare.len()), remaining_copy_len);
125-
copy_bytes_overshooting(a, a_spare, copy_at_least);
126-
remaining_copy_len -= copy_at_least;
127-
128-
if remaining_copy_len == 0 {
129-
return;
130-
}
131-
132-
let a = &a[copy_at_least..];
133-
let a_spare = &mut a_spare[copy_at_least..];
134-
135-
// A -> B Spare
136-
let copy_at_least = cmp::min(cmp::min(a.len(), b_spare.len()), remaining_copy_len);
137-
copy_bytes_overshooting(a, b_spare, copy_at_least);
138-
remaining_copy_len -= copy_at_least;
139-
140-
if remaining_copy_len == 0 {
141-
return;
142-
}
143-
144-
let b_spare = &mut b_spare[copy_at_least..];
145-
146-
// B -> A Spare
147-
let copy_at_least = cmp::min(cmp::min(b.len(), a_spare.len()), remaining_copy_len);
148-
copy_bytes_overshooting(b, a_spare, copy_at_least);
149-
remaining_copy_len -= copy_at_least;
150-
151-
if remaining_copy_len == 0 {
152-
return;
153-
}
154-
155-
let b = &b[copy_at_least..];
156-
157-
// B -> B Spare
158-
let copy_at_least = cmp::min(cmp::min(b.len(), b_spare.len()), remaining_copy_len);
159-
copy_bytes_overshooting(b, b_spare, copy_at_least);
160-
remaining_copy_len -= copy_at_least;
161-
162-
debug_assert_eq!(remaining_copy_len, 0);
163137
}
164138
}
165139

166-
struct IntermediateRingBuffer<'a> {
167-
this: &'a mut RingBuffer,
168-
original_len: usize,
169-
}
170-
171-
impl<'a> IntermediateRingBuffer<'a> {
172-
// inspired by `Vec::split_at_spare_mut`
173-
fn as_slices_spare_mut(&mut self) -> (&[u8], &[u8], &mut [u8], &mut [u8]) {
174-
let (a, b) = self.this.buf.as_mut_slices();
175-
debug_assert!(a.len() + b.len() >= self.original_len);
176-
177-
let mut remaining_init_len = self.original_len;
178-
let a_mid = cmp::min(a.len(), remaining_init_len);
179-
remaining_init_len -= a_mid;
180-
let b_mid = remaining_init_len;
181-
debug_assert!(b.len() >= b_mid);
182-
183-
let (a, a_spare) = a.split_at_mut(a_mid);
184-
let (b, b_spare) = b.split_at_mut(b_mid);
185-
debug_assert!(a_spare.is_empty() || b.is_empty());
186-
187-
(a, b, a_spare, b_spare)
188-
}
189-
}
190-
191-
/// Similar to ptr::copy_nonoverlapping
192-
///
193-
/// But it might overshoot the desired copy length if deemed useful
194-
///
195-
/// src and dst specify the entire length they are eligible for reading/writing respectively
196-
/// in addition to the desired copy length.
197-
///
198-
/// This function will then copy in chunks and might copy up to chunk size - 1 more bytes from src to dst
199-
/// if that operation does not read/write memory that does not belong to src/dst.
200-
///
201-
/// The chunk size is not part of the contract and may change depending on the target platform.
202-
///
203-
/// If that isn't possible we just fall back to ptr::copy_nonoverlapping
204-
fn copy_bytes_overshooting(src: &[u8], dst: &mut [u8], copy_at_least: usize) {
205-
let src = &src[..copy_at_least];
206-
let dst = &mut dst[..copy_at_least];
207-
208-
dst.copy_from_slice(src);
209-
}
210-
211140
#[cfg(test)]
212141
mod tests {
213142
use std::vec::Vec;

src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
//! than the original implementation.
1515
#![no_std]
1616
#![deny(trivial_casts, trivial_numeric_casts, rust_2018_idioms)]
17-
#![forbid(unsafe_code)]
1817

1918
#[cfg(feature = "std")]
2019
extern crate std;

0 commit comments

Comments
 (0)