diff --git a/benchmarks/criterion/Cargo.toml b/benchmarks/criterion/Cargo.toml index 48e136f59..62d5e0efb 100644 --- a/benchmarks/criterion/Cargo.toml +++ b/benchmarks/criterion/Cargo.toml @@ -14,4 +14,6 @@ harness = false [dependencies] criterion = "0.3.0" sled = { path = "../.." } + +[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies] jemallocator = "0.3.2" diff --git a/benchmarks/criterion/benches/sled.rs b/benchmarks/criterion/benches/sled.rs index b5e3f5826..3e6331b38 100644 --- a/benchmarks/criterion/benches/sled.rs +++ b/benchmarks/criterion/benches/sled.rs @@ -1,15 +1,11 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use jemallocator::Jemalloc; - use sled::Config; -#[cfg_attr( - // only enable jemalloc on linux and macos by default - any(target_os = "linux", target_os = "macos"), - global_allocator -)] -static ALLOC: Jemalloc = Jemalloc; +// only enable jemalloc on linux and macos by default +#[cfg(any(target_os = "linux", target_os = "macos"))] +#[global_allocator] +static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc; fn counter() -> usize { use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; diff --git a/src/fastcmp.rs b/src/fastcmp.rs deleted file mode 100644 index 1a48042af..000000000 --- a/src/fastcmp.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::cmp::Ordering; - -#[cfg(any(unix, windows))] -#[allow(unsafe_code)] -pub(crate) fn fastcmp(l: &[u8], r: &[u8]) -> Ordering { - let len = std::cmp::min(l.len(), r.len()); - let cmp = unsafe { libc::memcmp(l.as_ptr() as _, r.as_ptr() as _, len) }; - match cmp { - a if a > 0 => Ordering::Greater, - a if a < 0 => Ordering::Less, - _ => l.len().cmp(&r.len()), - } -} - -#[cfg(not(any(unix, windows)))] -#[allow(unsafe_code)] -pub(crate) fn fastcmp(l: &[u8], r: &[u8]) -> Ordering { - l.cmp(r) -} - -#[cfg(test)] -mod qc { - use super::fastcmp; - - fn prop_cmp_matches(l: &[u8], r: &[u8]) -> bool { - assert_eq!(fastcmp(l, r), l.cmp(r)); - assert_eq!(fastcmp(r, l), r.cmp(l)); - assert_eq!(fastcmp(l, l), l.cmp(l)); - assert_eq!(fastcmp(r, r), r.cmp(r)); - true - } - - #[test] - fn basic_functionality() { - let cases: [&[u8]; 8] = [ - &[], - &[0], - &[1], - &[1], - &[255], - &[1, 2, 3], - &[1, 2, 3, 0], - &[1, 2, 3, 55], - ]; - for pair in cases.windows(2) { - prop_cmp_matches(pair[0], pair[1]); - } - } - - quickcheck::quickcheck! { - #[cfg_attr(miri, ignore)] - fn qc_fastcmp(l: Vec, r: Vec) -> bool { - prop_cmp_matches(&l, &r) - } - } -} diff --git a/src/lib.rs b/src/lib.rs index 534a533f1..1d9c761f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -192,7 +192,6 @@ mod context; mod db; mod dll; mod ebr; -mod fastcmp; mod fastlock; mod fnv; mod histogram; @@ -302,7 +301,6 @@ use { pin as crossbeam_pin, Atomic, Guard as CrossbeamGuard, Owned, Shared, }, - fastcmp::fastcmp, lru::Lru, meta::Meta, node::Node, diff --git a/src/lru.rs b/src/lru.rs index f69eaa5b8..8d687d536 100644 --- a/src/lru.rs +++ b/src/lru.rs @@ -4,7 +4,6 @@ use std::{ borrow::{Borrow, BorrowMut}, convert::TryFrom, hash::{Hash, Hasher}, - mem::MaybeUninit, sync::atomic::{AtomicPtr, AtomicUsize, Ordering}, }; @@ -38,7 +37,7 @@ impl Default for AccessBlock { fn default() -> AccessBlock { AccessBlock { len: AtomicUsize::new(0), - block: unsafe { MaybeUninit::zeroed().assume_init() }, + block: [(); MAX_QUEUE_ITEMS].map(|_| AtomicU64::default() ), next: AtomicPtr::default(), } } @@ -53,7 +52,7 @@ impl AccessBlock { fn new(item: CacheAccess) -> AccessBlock { let mut ret = AccessBlock { len: AtomicUsize::new(1), - block: unsafe { MaybeUninit::zeroed().assume_init() }, + block: [(); MAX_QUEUE_ITEMS].map(|_| AtomicU64::default() ), next: AtomicPtr::default(), }; ret.block[0] = AtomicU64::from(u64::from(item)); diff --git a/src/node.rs b/src/node.rs index 2cfee072f..9a066bf27 100644 --- a/src/node.rs +++ b/src/node.rs @@ -41,7 +41,8 @@ fn uninitialized_node(len: usize) -> Inner { #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct Header { - // NB always lay out fields from largest to smallest to properly pack the struct + // NB always lay out fields from largest to smallest to properly pack the + // struct pub next: Option, pub merging_child: Option, lo_len: u64, @@ -96,16 +97,12 @@ fn apply_computed_distance(mut buf: &mut [u8], mut distance: usize) { // TODO change to u64 or u128 output // This function has several responsibilities: -// * `find` will call this when looking for the -// proper child pid on an index, with slice -// lengths that may or may not match -// * `KeyRef::Ord` and `KeyRef::distance` call -// this while performing node iteration, -// again with possibly mismatching slice -// lengths. Merging nodes together, or -// merging overlays into inner nodes -// will rely on this functionality, and -// it's possible for the lengths to vary. +// * `find` will call this when looking for the proper child pid on an index, +// with slice lengths that may or may not match +// * `KeyRef::Ord` and `KeyRef::distance` call this while performing node +// iteration, again with possibly mismatching slice lengths. Merging nodes +// together, or merging overlays into inner nodes will rely on this +// functionality, and it's possible for the lengths to vary. // // This is not a general-purpose function. It // is not possible to determine distances when @@ -412,9 +409,10 @@ impl<'a> Iterator for Iter<'a> { (Some((_, Some(_))), None) => { log::trace!("src/node.rs:114"); log::trace!("iterator returning {:?}", self.next_a); - return self.next_a.take().map(|(k, v)| { - (KeyRef::Slice(k), v.unwrap().as_ref()) - }); + return self + .next_a + .take() + .map(|(k, v)| (KeyRef::Slice(k), v.unwrap().as_ref())); } (Some((k_a, v_a_opt)), Some((k_b, _))) => { let cmp = KeyRef::Slice(k_a).cmp(&k_b); @@ -511,9 +509,10 @@ impl<'a> DoubleEndedIterator for Iter<'a> { (Some((_, Some(_))), None) => { log::trace!("src/node.rs:483"); log::trace!("iterator returning {:?}", self.next_back_a); - return self.next_back_a.take().map(|(k, v)| { - (KeyRef::Slice(k), v.unwrap().as_ref()) - }); + return self + .next_back_a + .take() + .map(|(k, v)| (KeyRef::Slice(k), v.unwrap().as_ref())); } (Some((k_a, Some(_))), Some((k_b, _))) if k_b > *k_a => { log::trace!("src/node.rs:508"); @@ -522,18 +521,20 @@ impl<'a> DoubleEndedIterator for Iter<'a> { } (Some((k_a, Some(_))), Some((k_b, _))) if k_b < *k_a => { log::trace!("iterator returning {:?}", self.next_back_a); - return self.next_back_a.take().map(|(k, v)| { - (KeyRef::Slice(k), v.unwrap().as_ref()) - }); + return self + .next_back_a + .take() + .map(|(k, v)| (KeyRef::Slice(k), v.unwrap().as_ref())); } (Some((k_a, Some(_))), Some((k_b, _))) if k_b == *k_a => { // prefer overlay, discard node value self.next_back_b.take(); log::trace!("src/node.rs:520"); log::trace!("iterator returning {:?}", self.next_back_a); - return self.next_back_a.take().map(|(k, v)| { - (KeyRef::Slice(k), v.unwrap().as_ref()) - }); + return self + .next_back_a + .take() + .map(|(k, v)| (KeyRef::Slice(k), v.unwrap().as_ref())); } _ => unreachable!( "did not expect combination a: {:?} b: {:?}", @@ -905,8 +906,9 @@ impl Node { Some(Node { overlay: Default::default(), inner: new_inner }) } - /// `node_kv_pair` returns either the existing (node/key, value, current offset) tuple or - /// (node/key, none, future offset) where a node/key is node level encoded key. + /// `node_kv_pair` returns either the existing (node/key, value, current + /// offset) tuple or (node/key, none, future offset) where a node/key is + /// node level encoded key. pub(crate) fn node_kv_pair<'a>( &'a self, key: &'a [u8], @@ -949,7 +951,7 @@ impl Node { return Some(( self.prefix_decode(self.inner.index_key(idx)), self.inner.index_value(idx).into(), - )) + )); } Err(idx) => idx, }; @@ -1018,7 +1020,7 @@ impl Node { return Some(( self.prefix_decode(self.inner.index_key(idx)), self.inner.index_value(idx).into(), - )) + )); } Err(idx) => idx, }; @@ -1088,7 +1090,12 @@ impl Node { let pid_bytes = self.index_value(idx); let pid = u64::from_le_bytes(pid_bytes.try_into().unwrap()); - log::trace!("index_next_node for key {:?} returning pid {} after searching node {:?}", key, pid, self); + log::trace!( + "index_next_node for key {:?} returning pid {} after searching node {:?}", + key, + pid, + self + ); (is_leftmost, pid) } @@ -1741,29 +1748,10 @@ impl Inner { * (tf!(size_of::(), u32) - u32::from(self.offset_bytes))); - let mut tmp = std::mem::MaybeUninit::::uninit(); let len = size_of::(); - // we use unsafe code here because it cuts a significant number of - // CPU cycles on a simple insertion workload compared to using the - // more idiomatic approach of copying the correct number of bytes into - // a buffer initialized with zeroes. the seemingly "less" unsafe - // approach of using ptr::copy_nonoverlapping did not improve matters. - // using a match statement on offset_bytes and performing simpler - // casting for one or two bytes slowed things down due to increasing - // code size. this approach is branch-free and cut CPU usage of this - // function from 7-11% down to 0.5-2% in a monotonic insertion workload. - #[allow(unsafe_code)] - unsafe { - let ptr: *const u8 = self.ptr().add(start); - std::ptr::copy_nonoverlapping( - ptr, - tmp.as_mut_ptr() as *mut u8, - len, - ); - *tmp.as_mut_ptr() &= mask; - tmp.assume_init() - } + usize::from_ne_bytes(self.buf()[start..start + len].try_into().unwrap()) + & mask } fn set_offset(&mut self, index: usize, offset: usize) { @@ -2217,10 +2205,17 @@ impl Inner { { // search key does not evenly fit based on // our fixed stride length - log::trace!("failed to find, search: {:?} lo: {:?} \ + log::trace!( + "failed to find, search: {:?} lo: {:?} \ prefix_len: {} distance: {} stride: {} offset: {} children: {}, node: {:?}", - key, self.lo(), self.prefix_len, distance, - stride.get(), offset, self.children, self + key, + self.lo(), + self.prefix_len, + distance, + stride.get(), + offset, + self.children, + self ); return Err((offset + 1).min(self.children())); } @@ -2239,7 +2234,7 @@ impl Inner { let mid = left + size / 2; let l = self.index_key(mid); - let cmp = crate::fastcmp(l.unwrap_slice(), key); + let cmp = l.unwrap_slice().cmp(key); if cmp == Less { left = mid + 1; @@ -2263,19 +2258,19 @@ impl Inner { fn iter_keys( &self, ) -> impl Iterator> - + ExactSizeIterator - + DoubleEndedIterator - + Clone { + + ExactSizeIterator + + DoubleEndedIterator + + Clone { (0..self.children()).map(move |idx| self.index_key(idx)) } fn iter_index_pids( &self, ) -> impl '_ - + Iterator - + ExactSizeIterator - + DoubleEndedIterator - + Clone { + + Iterator + + ExactSizeIterator + + DoubleEndedIterator + + Clone { assert!(self.is_index); self.iter_values().map(move |pid_bytes| { u64::from_le_bytes(pid_bytes.try_into().unwrap()) @@ -2308,21 +2303,13 @@ impl Inner { pub(crate) fn hi(&self) -> Option<&[u8]> { let start = tf!(self.lo_len) + size_of::
(); let end = start + tf!(self.hi_len); - if start == end { - None - } else { - Some(&self.as_ref()[start..end]) - } + if start == end { None } else { Some(&self.as_ref()[start..end]) } } fn hi_mut(&mut self) -> Option<&mut [u8]> { let start = tf!(self.lo_len) + size_of::
(); let end = start + tf!(self.hi_len); - if start == end { - None - } else { - Some(&mut self.as_mut()[start..end]) - } + if start == end { None } else { Some(&mut self.as_mut()[start..end]) } } fn index_key(&self, idx: usize) -> KeyRef<'_> { @@ -3000,7 +2987,8 @@ mod test { #[test] fn node_bug_02() { - // postmortem: the test code had some issues with handling invalid keys for nodes + // postmortem: the test code had some issues with handling invalid keys + // for nodes let node = Inner::new( &[47, 97][..], None, @@ -3057,7 +3045,8 @@ mod test { #[test] fn node_bug_05() { // postmortem: `prop_indexable` did not account for the requirement - // of feeding sorted items that are >= the lo key to the Node::new method. + // of feeding sorted items that are >= the lo key to the Node::new + // method. assert!(prop_indexable( vec![1], vec![], diff --git a/src/pagecache/iobuf.rs b/src/pagecache/iobuf.rs index bd89cf1d7..bd415a360 100644 --- a/src/pagecache/iobuf.rs +++ b/src/pagecache/iobuf.rs @@ -1,3 +1,5 @@ +use std::ops::DerefMut; +use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut}; use std::{ alloc::{alloc, dealloc, Layout}, cell::UnsafeCell, @@ -32,6 +34,26 @@ impl AlignedBuf { } } +impl Deref for AlignedBuf { + type Target = [u8]; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + unsafe { + slice_from_raw_parts(self.0, self.1).as_ref().unwrap_unchecked() + } + } +} + +impl DerefMut for AlignedBuf { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + unsafe { + slice_from_raw_parts_mut(self.0, self.1).as_mut().unwrap_unchecked() + } + } +} + impl Drop for AlignedBuf { fn drop(&mut self) { let layout = Layout::from_size_align(self.1, 8192).unwrap(); @@ -65,8 +87,8 @@ impl IoBuf { /// uninitialized memory. For this to be correct, we must /// ensure that: /// 1. overlapping mutable slices are never created. - /// 2. a read to any subslice of this slice only happens - /// after a write has initialized that memory + /// 2. a read to any subslice of this slice only happens after a write has + /// initialized that memory /// /// It is intended that the log reservation code guarantees /// that no two `Reservation` objects will hold overlapping @@ -81,19 +103,38 @@ impl IoBuf { /// to meet this requirement. /// /// The safety of this method was discussed in #1044. - pub(crate) fn get_mut_range( - &self, - at: usize, - len: usize, - ) -> &'static mut [u8] { - let buf_ptr = self.buf.get(); + #[inline(always)] + pub(crate) fn get_mut_range(&self, at: usize, len: usize) -> &mut [u8] { + unsafe { &mut self.get_mut()[at..at + len] } + } + /// # Safety + /// + /// This operation provides access to a mutable buffer of + /// uninitialized memory. For this to be correct, we must + /// ensure that: + /// 1. overlapping mutable slices are never created. + /// 2. a read to any subslice of this slice only happens after a write has + /// initialized that memory + /// + /// It is intended that the log reservation code guarantees + /// that no two `Reservation` objects will hold overlapping + /// mutable slices to our io buffer. + /// + /// It is intended that the `write_to_log` function only + /// tries to write initialized bytes to the underlying storage. + /// + /// It is intended that the `write_to_log` function will + /// initialize any yet-to-be-initialized bytes before writing + /// the buffer to storage. #1040 added logic that was intended + /// to meet this requirement. + /// + /// The safety of this method was discussed in #1044. + #[inline(always)] + pub(crate) fn get_mut(&self) -> &mut [u8] { unsafe { - assert!((*buf_ptr).1 >= at + len); - std::slice::from_raw_parts_mut( - (*buf_ptr).0.add(self.base + at), - len, - ) + let buf_ptr = self.buf.get().as_mut().unwrap_unchecked(); + &mut buf_ptr[self.base..] } } @@ -119,11 +160,8 @@ impl IoBuf { #[allow(unsafe_code)] unsafe { - std::ptr::copy_nonoverlapping( - header_bytes.as_ptr(), - (*self.buf.get()).0, - SEG_HEADER_LEN, - ); + let buf = self.buf.get().as_mut().unwrap(); + buf[..SEG_HEADER_LEN].copy_from_slice(&header_bytes); } // ensure writes to the buffer land after our header. @@ -687,48 +725,20 @@ impl IoBufs { unused_space - header_bytes.len() ]; - #[allow(unsafe_code)] - unsafe { - std::ptr::copy_nonoverlapping( - header_bytes.as_ptr(), - data.as_mut_ptr(), - header_bytes.len(), - ); - std::ptr::copy_nonoverlapping( - padding_bytes.as_ptr(), - data.as_mut_ptr().add(header_bytes.len()), - padding_bytes.len(), - ); - } - + let (hdat, rem) = data.split_at_mut(header_bytes.len()); + hdat.copy_from_slice(&header_bytes); + let (pdat, _) = rem.split_at_mut(padding_bytes.len()); + pdat.copy_from_slice(&padding_bytes); // this as to stay aligned with the hashing let crc32_arr = u32_to_arr(calculate_message_crc32( &header_bytes, &padding_bytes[..pad_len], )); - - #[allow(unsafe_code)] - unsafe { - std::ptr::copy_nonoverlapping( - crc32_arr.as_ptr(), - // the crc32 is the first part of the buffer - data.as_mut_ptr(), - std::mem::size_of::(), - ); - } + data[0..4].copy_from_slice(&crc32_arr) } else if maxed { // initialize the remainder of this buffer's red zone let data = iobuf.get_mut_range(bytes_to_write, unused_space); - - #[allow(unsafe_code)] - unsafe { - // note: this could use slice::fill() if it stabilizes - std::ptr::write_bytes( - data.as_mut_ptr(), - MessageKind::Corrupted.into(), - unused_space, - ); - } + data.fill(MessageKind::Corrupted.into()); } let total_len = if maxed { capacity } else { bytes_to_write }; @@ -953,7 +963,10 @@ pub(in crate::pagecache) fn make_stable_inner( while stable < lsn { if let Err(e) = iobufs.config.global_error() { - error!("bailing out of stabilization code due to detected IO error: {:?}", e); + error!( + "bailing out of stabilization code due to detected IO error: {:?}", + e + ); let intervals = iobufs.intervals.lock(); // having held the mutex makes this linearized diff --git a/src/pagecache/logger.rs b/src/pagecache/logger.rs index 9fbf137a8..cabcc4c14 100644 --- a/src/pagecache/logger.rs +++ b/src/pagecache/logger.rs @@ -1,3 +1,4 @@ +#![forbid(unsafe_code)] use std::fs::File; use super::{ @@ -421,7 +422,7 @@ impl Log { return Ok(Reservation { iobuf, log: self, - buf: destination, + buf_range: buf_offset..buf_offset + inline_buf_len, flushed: false, lsn: reservation_lsn, pointer, @@ -571,31 +572,27 @@ impl LogRead { impl From<[u8; SEG_HEADER_LEN]> for SegmentHeader { fn from(buf: [u8; SEG_HEADER_LEN]) -> Self { - #[allow(unsafe_code)] - unsafe { - let crc32_header = - arr_to_u32(buf.get_unchecked(0..4)) ^ 0xFFFF_FFFF; + let crc32_header = arr_to_u32(&buf[0..4]) ^ 0xFFFF_FFFF; - let xor_lsn = arr_to_lsn(buf.get_unchecked(4..12)); - let lsn = xor_lsn ^ 0x7FFF_FFFF_FFFF_FFFF; + let xor_lsn = arr_to_lsn(&buf[4..12]); + let lsn = xor_lsn ^ 0x7FFF_FFFF_FFFF_FFFF; - let xor_max_stable_lsn = arr_to_lsn(buf.get_unchecked(12..20)); - let max_stable_lsn = xor_max_stable_lsn ^ 0x7FFF_FFFF_FFFF_FFFF; + let xor_max_stable_lsn = arr_to_lsn(&buf[12..20]); + let max_stable_lsn = xor_max_stable_lsn ^ 0x7FFF_FFFF_FFFF_FFFF; - let crc32_tested = crc32(&buf[4..20]); + let crc32_tested = crc32(&buf[4..20]); - let ok = crc32_tested == crc32_header; + let ok = crc32_tested == crc32_header; - if !ok { - debug!( - "segment with lsn {} had computed crc {}, \ + if !ok { + debug!( + "segment with lsn {} had computed crc {}, \ but stored crc {}", - lsn, crc32_tested, crc32_header - ); - } - - Self { lsn, max_stable_lsn, ok } + lsn, crc32_tested, crc32_header + ); } + + Self { lsn, max_stable_lsn, ok } } } @@ -609,30 +606,11 @@ impl From for [u8; SEG_HEADER_LEN] { let xor_max_stable_lsn = header.max_stable_lsn ^ 0x7FFF_FFFF_FFFF_FFFF; let highest_stable_lsn_arr = lsn_to_arr(xor_max_stable_lsn); - #[allow(unsafe_code)] - unsafe { - std::ptr::copy_nonoverlapping( - lsn_arr.as_ptr(), - buf.as_mut_ptr().add(4), - std::mem::size_of::(), - ); - std::ptr::copy_nonoverlapping( - highest_stable_lsn_arr.as_ptr(), - buf.as_mut_ptr().add(12), - std::mem::size_of::(), - ); - } + buf[4..12].copy_from_slice(&lsn_arr); + buf[12..20].copy_from_slice(&highest_stable_lsn_arr); let crc32 = u32_to_arr(crc32(&buf[4..20]) ^ 0xFFFF_FFFF); - - #[allow(unsafe_code)] - unsafe { - std::ptr::copy_nonoverlapping( - crc32.as_ptr(), - buf.as_mut_ptr(), - std::mem::size_of::(), - ); - } + buf[0..4].copy_from_slice(&crc32); buf } diff --git a/src/pagecache/reservation.rs b/src/pagecache/reservation.rs index 1b6280b2f..97e3fb4d3 100644 --- a/src/pagecache/reservation.rs +++ b/src/pagecache/reservation.rs @@ -1,4 +1,7 @@ +#![forbid(unsafe_code)] + use crate::{pagecache::*, *}; +use std::ops::Range; /// A pending log reservation which can be aborted or completed. /// NB the holder should quickly call `complete` or `abort` as @@ -8,7 +11,7 @@ use crate::{pagecache::*, *}; pub struct Reservation<'a> { pub(super) log: &'a Log, pub(super) iobuf: Arc, - pub(super) buf: &'a mut [u8], + pub(super) buf_range: Range, pub(super) flushed: bool, pub pointer: DiskPtr, pub lsn: Lsn, @@ -28,6 +31,10 @@ impl<'a> Drop for Reservation<'a> { } impl<'a> Reservation<'a> { + #[inline] + fn buf(&self) -> &mut [u8] { + &mut self.iobuf.get_mut()[self.buf_range.clone()] + } /// Cancel the reservation, placing a failed flush on disk, returning /// the (cancelled) log sequence number and file offset. pub fn abort(mut self) -> Result<(Lsn, DiskPtr)> { @@ -72,17 +79,16 @@ impl<'a> Reservation<'a> { self.lsn ); + let this_buf = self.buf(); if self.lsn == peg_lsn { // this can happen because high-level tree updates // may result in no work happening. self.abort() } else { - self.buf[4] = MessageKind::BatchManifest.into(); + this_buf[4] = MessageKind::BatchManifest.into(); + let dst = &mut this_buf[self.header_len..]; let buf = lsn_to_arr(peg_lsn); - - let dst = &mut self.buf[self.header_len..]; - dst.copy_from_slice(&buf); let mut intervals = self.log.iobufs.intervals.lock(); @@ -99,45 +105,25 @@ impl<'a> Reservation<'a> { } self.flushed = true; + let buf = self.buf(); if !valid { - self.buf[4] = MessageKind::Canceled.into(); + buf[4] = MessageKind::Canceled.into(); // zero the message contents to prevent UB - #[allow(unsafe_code)] - unsafe { - std::ptr::write_bytes( - self.buf[self.header_len..].as_mut_ptr(), - 0, - self.buf.len() - self.header_len, - ) - } + buf[self.header_len..].fill(0); } // zero the crc bytes to prevent UB - #[allow(unsafe_code)] - unsafe { - std::ptr::write_bytes( - self.buf[..].as_mut_ptr(), - 0, - std::mem::size_of::(), - ) - } + buf[0..4].fill(0); let crc32 = calculate_message_crc32( - self.buf[..self.header_len].as_ref(), - &self.buf[self.header_len..], + &buf[..self.header_len], + &buf[self.header_len..], ); let crc32_arr = u32_to_arr(crc32); - #[allow(unsafe_code)] - unsafe { - std::ptr::copy_nonoverlapping( - crc32_arr.as_ptr(), - self.buf.as_mut_ptr(), - std::mem::size_of::(), - ); - } + buf[0..4].copy_from_slice(&crc32_arr); self.log.exit_reservation(&self.iobuf)?; Ok((self.lsn, self.pointer))