Skip to content

Commit 2040ac1

Browse files
authored
Merge pull request #2556 from subspace/improvements-around-unsafe
Improvements around unsafe
2 parents 2f15c08 + ecd8814 commit 2040ac1

File tree

2 files changed

+34
-36
lines changed

2 files changed

+34
-36
lines changed

crates/subspace-farmer-components/src/reading.rs

+10-12
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,10 @@ where
118118
S: ReadAtSync,
119119
A: ReadAtAsync,
120120
{
121-
let mut record_chunks = vec![None; Record::NUM_S_BUCKETS];
121+
// TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
122+
// SAFETY: Data structure filled with zeroes is a valid invariant
123+
let mut record_chunks =
124+
unsafe { Box::<[Option<Scalar>; Record::NUM_S_BUCKETS]>::new_zeroed().assume_init() };
122125

123126
let read_chunks_inputs = record_chunks
124127
.par_iter_mut()
@@ -244,13 +247,6 @@ where
244247
}
245248
}
246249

247-
let mut record_chunks = ManuallyDrop::new(record_chunks);
248-
249-
// SAFETY: Original memory is not dropped, layout is exactly what we need here
250-
let record_chunks = unsafe {
251-
Box::from_raw(record_chunks.as_mut_ptr() as *mut [Option<Scalar>; Record::NUM_S_BUCKETS])
252-
};
253-
254250
Ok(record_chunks)
255251
}
256252

@@ -261,6 +257,8 @@ pub fn recover_extended_record_chunks(
261257
erasure_coding: &ErasureCoding,
262258
) -> Result<Box<[Scalar; Record::NUM_S_BUCKETS]>, ReadingError> {
263259
// Restore source record scalars
260+
// TODO: Recover into `Box<[Scalar; Record::NUM_S_BUCKETS]>` or else conversion into `Box` below
261+
// might leak memory
264262
let record_chunks = erasure_coding
265263
.recover(sector_record_chunks)
266264
.map_err(|error| ReadingError::FailedToErasureDecodeRecord {
@@ -276,12 +274,12 @@ pub fn recover_extended_record_chunks(
276274
});
277275
}
278276

277+
// Allocation in vector can be larger than contents, we need to make sure allocation is the same
278+
// as the contents, this should also contain fast path if allocation matches contents
279+
let record_chunks = record_chunks.into_iter().collect::<Box<_>>();
279280
let mut record_chunks = ManuallyDrop::new(record_chunks);
280-
281281
// SAFETY: Original memory is not dropped, size of the data checked above
282-
let record_chunks = unsafe {
283-
Box::from_raw(record_chunks.as_mut_ptr() as *mut [Scalar; Record::NUM_S_BUCKETS])
284-
};
282+
let record_chunks = unsafe { Box::from_raw(record_chunks.as_mut_ptr() as *mut _) };
285283

286284
Ok(record_chunks)
287285
}

crates/subspace-farmer-components/src/sector.rs

+24-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use bitvec::prelude::*;
22
use parity_scale_codec::{Decode, Encode};
33
use rayon::prelude::*;
4+
use std::mem::ManuallyDrop;
45
use std::ops::{Deref, DerefMut};
56
use std::{mem, slice};
67
use subspace_core_primitives::checksum::Blake3Checksummed;
@@ -59,24 +60,24 @@ pub struct SectorMetadata {
5960
impl SectorMetadata {
6061
/// Returns offsets of each s-bucket relatively to the beginning of the sector (in chunks)
6162
pub fn s_bucket_offsets(&self) -> Box<[u32; Record::NUM_S_BUCKETS]> {
62-
// TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
63-
// SAFETY: Data structure filled with zeroes is a valid invariant
64-
let mut s_bucket_offsets =
65-
unsafe { Box::<[u32; Record::NUM_S_BUCKETS]>::new_zeroed().assume_init() };
66-
67-
self.s_bucket_sizes
63+
let s_bucket_offsets = self
64+
.s_bucket_sizes
6865
.iter()
69-
.zip(s_bucket_offsets.iter_mut())
70-
.for_each({
66+
.map({
7167
let mut base_offset = 0;
7268

73-
move |(s_bucket_size, s_bucket_offset)| {
74-
*s_bucket_offset = base_offset;
69+
move |s_bucket_size| {
70+
let offset = base_offset;
7571
base_offset += u32::from(*s_bucket_size);
72+
offset
7673
}
77-
});
74+
})
75+
.collect::<Box<_>>();
7876

79-
s_bucket_offsets
77+
assert_eq!(s_bucket_offsets.len(), Record::NUM_S_BUCKETS);
78+
let mut s_bucket_offsets = ManuallyDrop::new(s_bucket_offsets);
79+
// SAFETY: Original memory is not dropped, number of elements checked above
80+
unsafe { Box::from_raw(s_bucket_offsets.as_mut_ptr() as *mut [u32; Record::NUM_S_BUCKETS]) }
8081
}
8182
}
8283

@@ -164,6 +165,7 @@ impl RawSector {
164165

165166
// Bit array containing space for bits equal to the number of s-buckets in a record
166167
type SingleRecordBitArray = BitArray<[u8; Record::NUM_S_BUCKETS / u8::BITS as usize]>;
168+
167169
const SINGLE_RECORD_BIT_ARRAY_SIZE: usize = mem::size_of::<SingleRecordBitArray>();
168170

169171
// TODO: I really tried to avoid `count_ones()`, but wasn't able to with safe Rust due to lifetimes
@@ -400,23 +402,21 @@ impl SectorContentsMap {
400402

401403
/// Returns sizes of each s-bucket
402404
pub fn s_bucket_sizes(&self) -> Box<[u16; Record::NUM_S_BUCKETS]> {
403-
// TODO: Should have been just `::new()`, but https://github.com/rust-lang/rust/issues/53827
404-
// SAFETY: Data structure filled with zeroes is a valid invariant
405-
let mut s_bucket_sizes =
406-
unsafe { Box::<[u16; Record::NUM_S_BUCKETS]>::new_zeroed().assume_init() };
407405
// Rayon doesn't support iteration over custom types yet
408-
(u16::from(SBucket::ZERO)..=u16::from(SBucket::MAX))
406+
let s_bucket_sizes = (u16::from(SBucket::ZERO)..=u16::from(SBucket::MAX))
409407
.into_par_iter()
410408
.map(SBucket::from)
411-
.zip(s_bucket_sizes.par_iter_mut())
412-
.for_each(|(s_bucket, s_bucket_size)| {
413-
*s_bucket_size = self
414-
.iter_s_bucket_records(s_bucket)
409+
.map(|s_bucket| {
410+
self.iter_s_bucket_records(s_bucket)
415411
.expect("S-bucket guaranteed to be in range; qed")
416-
.count() as u16;
417-
});
412+
.count() as u16
413+
})
414+
.collect::<Box<_>>();
418415

419-
s_bucket_sizes
416+
assert_eq!(s_bucket_sizes.len(), Record::NUM_S_BUCKETS);
417+
let mut s_bucket_sizes = ManuallyDrop::new(s_bucket_sizes);
418+
// SAFETY: Original memory is not dropped, number of elements checked above
419+
unsafe { Box::from_raw(s_bucket_sizes.as_mut_ptr() as *mut [u16; Record::NUM_S_BUCKETS]) }
420420
}
421421

422422
/// Creates an iterator of `(s_bucket, encoded_chunk_used, chunk_location)`, where `s_bucket` is

0 commit comments

Comments
 (0)