Skip to content

Commit 4aaa028

Browse files
authored
feat: impl bytes::BufMut for SmallVec (v2) (#371)
1 parent 30c30a6 commit 4aaa028

File tree

3 files changed

+165
-3
lines changed

3 files changed

+165
-3
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ may_dangle = []
1919
extract_if = []
2020

2121
[dependencies]
22+
bytes = { version = "1", optional = true, default-features = false }
2223
serde = { version = "1", optional = true, default-features = false }
2324
malloc_size_of = { version = "0.1.1", optional = true, default-features = false }
2425

src/lib.rs

+70
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ use core::ptr::copy;
9292
use core::ptr::copy_nonoverlapping;
9393
use core::ptr::NonNull;
9494

95+
#[cfg(feature = "bytes")]
96+
use bytes::{buf::UninitSlice, BufMut};
9597
#[cfg(feature = "malloc_size_of")]
9698
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
9799
#[cfg(feature = "serde")]
@@ -2220,3 +2222,71 @@ impl<const N: usize> io::Write for SmallVec<u8, N> {
22202222
Ok(())
22212223
}
22222224
}
2225+
2226+
#[cfg(feature = "bytes")]
2227+
unsafe impl<const N: usize> BufMut for SmallVec<u8, N> {
2228+
#[inline]
2229+
fn remaining_mut(&self) -> usize {
2230+
// A vector can never have more than isize::MAX bytes
2231+
isize::MAX as usize - self.len()
2232+
}
2233+
2234+
#[inline]
2235+
unsafe fn advance_mut(&mut self, cnt: usize) {
2236+
let len = self.len();
2237+
let remaining = self.capacity() - len;
2238+
2239+
if remaining < cnt {
2240+
panic!("advance out of bounds: the len is {remaining} but advancing by {cnt}");
2241+
}
2242+
2243+
// Addition will not overflow since the sum is at most the capacity.
2244+
self.set_len(len + cnt);
2245+
}
2246+
2247+
#[inline]
2248+
fn chunk_mut(&mut self) -> &mut UninitSlice {
2249+
if self.capacity() == self.len() {
2250+
self.reserve(64); // Grow the smallvec
2251+
}
2252+
2253+
let cap = self.capacity();
2254+
let len = self.len();
2255+
2256+
let ptr = self.as_mut_ptr();
2257+
// SAFETY: Since `ptr` is valid for `cap` bytes, `ptr.add(len)` must be
2258+
// valid for `cap - len` bytes. The subtraction will not underflow since
2259+
// `len <= cap`.
2260+
unsafe { UninitSlice::from_raw_parts_mut(ptr.add(len), cap - len) }
2261+
}
2262+
2263+
// Specialize these methods so they can skip checking `remaining_mut`
2264+
// and `advance_mut`.
2265+
#[inline]
2266+
fn put<T: bytes::Buf>(&mut self, mut src: T)
2267+
where
2268+
Self: Sized,
2269+
{
2270+
// In case the src isn't contiguous, reserve upfront.
2271+
self.reserve(src.remaining());
2272+
2273+
while src.has_remaining() {
2274+
let s = src.chunk();
2275+
let l = s.len();
2276+
self.extend_from_slice(s);
2277+
src.advance(l);
2278+
}
2279+
}
2280+
2281+
#[inline]
2282+
fn put_slice(&mut self, src: &[u8]) {
2283+
self.extend_from_slice(src);
2284+
}
2285+
2286+
#[inline]
2287+
fn put_bytes(&mut self, val: u8, cnt: usize) {
2288+
// If the addition overflows, then the `resize` will fail.
2289+
let new_len = self.len().saturating_add(cnt);
2290+
self.resize(new_len, val);
2291+
}
2292+
}

src/tests.rs

+94-3
Original file line numberDiff line numberDiff line change
@@ -1154,9 +1154,7 @@ fn collect_from_iter() {
11541154
#[test]
11551155
fn test_collect_with_spill() {
11561156
let input = "0123456";
1157-
let collected: SmallVec<char, 4> = input
1158-
.chars()
1159-
.collect();
1157+
let collected: SmallVec<char, 4> = input.chars().collect();
11601158
assert_eq!(collected, &['0', '1', '2', '3', '4', '5', '6']);
11611159
}
11621160

@@ -1186,3 +1184,96 @@ fn test_spare_capacity_mut() {
11861184
assert!(spare.len() >= 1);
11871185
assert_eq!(spare.as_ptr().cast::<u8>(), unsafe { v.as_ptr().add(3) });
11881186
}
1187+
1188+
// Adopted from `tests/test_buf_mut.rs` in the `bytes` crate.
1189+
#[cfg(feature = "bytes")]
1190+
mod buf_mut {
1191+
use bytes::BufMut as _;
1192+
1193+
type SmallVec = crate::SmallVec<u8, 8>;
1194+
1195+
#[test]
1196+
fn test_smallvec_as_mut_buf() {
1197+
let mut buf = SmallVec::with_capacity(64);
1198+
1199+
assert_eq!(buf.remaining_mut(), isize::MAX as usize);
1200+
1201+
assert!(buf.chunk_mut().len() >= 64);
1202+
1203+
buf.put(&b"zomg"[..]);
1204+
1205+
assert_eq!(&buf, b"zomg");
1206+
1207+
assert_eq!(buf.remaining_mut(), isize::MAX as usize - 4);
1208+
assert_eq!(buf.capacity(), 64);
1209+
1210+
for _ in 0..16 {
1211+
buf.put(&b"zomg"[..]);
1212+
}
1213+
1214+
assert_eq!(buf.len(), 68);
1215+
}
1216+
1217+
#[test]
1218+
fn test_smallvec_put_bytes() {
1219+
let mut buf = SmallVec::new();
1220+
buf.push(17);
1221+
buf.put_bytes(19, 2);
1222+
assert_eq!([17, 19, 19], &buf[..]);
1223+
}
1224+
1225+
#[test]
1226+
fn test_put_u8() {
1227+
let mut buf = SmallVec::with_capacity(8);
1228+
buf.put_u8(33);
1229+
assert_eq!(b"\x21", &buf[..]);
1230+
}
1231+
1232+
#[test]
1233+
fn test_put_u16() {
1234+
let mut buf = SmallVec::with_capacity(8);
1235+
buf.put_u16(8532);
1236+
assert_eq!(b"\x21\x54", &buf[..]);
1237+
1238+
buf.clear();
1239+
buf.put_u16_le(8532);
1240+
assert_eq!(b"\x54\x21", &buf[..]);
1241+
}
1242+
1243+
#[test]
1244+
fn test_put_int() {
1245+
let mut buf = SmallVec::with_capacity(8);
1246+
buf.put_int(0x1020304050607080, 3);
1247+
assert_eq!(b"\x60\x70\x80", &buf[..]);
1248+
}
1249+
1250+
#[test]
1251+
#[should_panic]
1252+
fn test_put_int_nbytes_overflow() {
1253+
let mut buf = SmallVec::with_capacity(8);
1254+
buf.put_int(0x1020304050607080, 9);
1255+
}
1256+
1257+
#[test]
1258+
fn test_put_int_le() {
1259+
let mut buf = SmallVec::with_capacity(8);
1260+
buf.put_int_le(0x1020304050607080, 3);
1261+
assert_eq!(b"\x80\x70\x60", &buf[..]);
1262+
}
1263+
1264+
#[test]
1265+
#[should_panic]
1266+
fn test_put_int_le_nbytes_overflow() {
1267+
let mut buf = SmallVec::with_capacity(8);
1268+
buf.put_int_le(0x1020304050607080, 9);
1269+
}
1270+
1271+
#[test]
1272+
#[should_panic(expected = "advance out of bounds: the len is 8 but advancing by 12")]
1273+
fn test_smallvec_advance_mut() {
1274+
let mut buf = SmallVec::with_capacity(8);
1275+
unsafe {
1276+
buf.advance_mut(12);
1277+
}
1278+
}
1279+
}

0 commit comments

Comments
 (0)