1- #![ allow( unsafe_op_in_unsafe_fn) ] // FIXME
2-
31use core:: fmt;
42use core:: mem:: MaybeUninit ;
53use core:: ops:: Range ;
@@ -16,6 +14,18 @@ impl<'a> Writer<'a> {
1614 /// Creates a new `Writer` from a fully initialized buffer.
1715 #[ inline]
1816 pub fn new ( buf : & ' a mut [ u8 ] ) -> Writer < ' a > {
17+ // SAFETY: Because buf is a slice, most of the preconditions for
18+ // core::slice::from_raw_parts_mut are satisfied:
19+ // * buf.as_mut_ptr() is non-null.
20+ // * The memory range is within a single allocated object.
21+ // * buf is mutable, so the range is valid for both reads
22+ // and writes of up to buf.len() * size_of::<u8>() bytes.
23+ // * The contents of the slice are initialized.
24+ // * buf.as_mut_ptr() + buf.len() does not wrap around
25+ // the end of the address space.
26+ // The remaining precondition is enforced by the borrow checker when this function is called:
27+ // * The memory range cannot be accessed through any other pointer for the
28+ // duration of lifetime 'a.
1929 unsafe { Self :: new_uninit ( buf. as_mut_ptr ( ) , buf. len ( ) ) }
2030 }
2131
@@ -26,6 +36,9 @@ impl<'a> Writer<'a> {
2636 /// The arguments must satisfy the requirements of [`core::slice::from_raw_parts_mut`].
2737 #[ inline]
2838 pub unsafe fn new_uninit ( ptr : * mut u8 , len : usize ) -> Writer < ' a > {
39+ // SAFETY: The preconditions for WeakSliceMut::from_raw_parts_mut are the same
40+ // as for core::slice::from_raw_parts_mut, and the caller is responsible for
41+ // ensuring the latter.
2942 let buf = unsafe { WeakSliceMut :: from_raw_parts_mut ( ptr as * mut MaybeUninit < u8 > , len) } ;
3043 Writer { buf, filled : 0 }
3144 }
@@ -272,27 +285,47 @@ impl<'a> Writer<'a> {
272285 /// # Safety
273286 ///
274287 /// `src` must be safe to perform unaligned reads in `core::mem::size_of::<C>()` chunks until
275- /// `end` is reached. `dst` must be safe to (unalingned ) write that number of chunks.
288+ /// `end` is reached. `dst` must be safe to (unaligned ) write that number of chunks.
276289 #[ inline( always) ]
277290 unsafe fn copy_chunk_unchecked < const N : usize > (
278291 mut src : * const MaybeUninit < u8 > ,
279292 mut dst : * mut MaybeUninit < u8 > ,
280293 length : usize ,
281294 ) {
282- let end = src. add ( length) ;
295+ if length == 0 {
296+ return ;
297+ }
283298
284- let chunk = load_chunk :: < N > ( src) ;
285- store_chunk :: < N > ( dst, chunk) ;
299+ // SAFETY: The caller ensured that src + length is within (or just at the end of)
300+ // a readable range of bytes.
301+ // FIXME: The other safety precondition for `add` is that the amount being added
302+ // must fit in an `isize`. Should that be part of the documented safety preconditions
303+ // for this function, so that our caller is responsible for ensuring it?
304+ let end = unsafe { src. add ( length) } ;
286305
287- src = src. add ( N ) ;
288- dst = dst. add ( N ) ;
306+ // SAFETY: We checked above that length != 0, so there is at least one chunk remaining.
307+ let chunk = unsafe { load_chunk :: < N > ( src) } ;
308+ unsafe { store_chunk :: < N > ( dst, chunk) } ;
289309
290- while src < end {
291- let chunk = load_chunk :: < N > ( src) ;
292- store_chunk :: < N > ( dst, chunk) ;
310+ // SAFETY: src and dest haven't been modified yet, and we checked above that
311+ // length != 0, so adding one chunk (N bytes) to both src and dst will result in
312+ // a pointer in (or just at the end of) each of the underlying buffers.
313+ src = unsafe { src. add ( N ) } ;
314+ dst = unsafe { dst. add ( N ) } ;
293315
294- src = src. add ( N ) ;
295- dst = dst. add ( N ) ;
316+ while src < end {
317+ // SAFETY: The caller ensured that src and dst contain enough bytes to support
318+ // reads (from src) or writes (to dst) up to and including the chunk that contains
319+ // end. Note that, if length is not a multiple of N, we will copy up to N-1 bytes
320+ // past end.
321+ let chunk = unsafe { load_chunk :: < N > ( src) } ;
322+ unsafe { store_chunk :: < N > ( dst, chunk) } ;
323+
324+ // SAFETY: Because src is currently < end, we have at least one more chunk available
325+ // to copy, so there is room to advance the pointers by N within both the src and
326+ // dst bufs.
327+ src = unsafe { src. add ( N ) } ;
328+ dst = unsafe { dst. add ( N ) } ;
296329 }
297330 }
298331}
@@ -302,15 +335,17 @@ impl<'a> Writer<'a> {
302335/// Must be valid to read a `[u8; N]` value from `from` with an unaligned read.
303336#[ inline( always) ]
304337unsafe fn load_chunk < const N : usize > ( from : * const MaybeUninit < u8 > ) -> [ MaybeUninit < u8 > ; N ] {
305- core:: ptr:: read_unaligned ( from. cast :: < [ MaybeUninit < u8 > ; N ] > ( ) )
338+ // SAFETY: Checked by the caller.
339+ unsafe { core:: ptr:: read_unaligned ( from. cast :: < [ MaybeUninit < u8 > ; N ] > ( ) ) }
306340}
307341
308342/// # Safety
309343///
310344/// Must be valid to write a `[u8; N]` value to `out` with an unaligned write.
311345#[ inline( always) ]
312346unsafe fn store_chunk < const N : usize > ( out : * mut MaybeUninit < u8 > , chunk : [ MaybeUninit < u8 > ; N ] ) {
313- core:: ptr:: write_unaligned ( out. cast ( ) , chunk)
347+ // SAFETY: checked by the caller.
348+ unsafe { core:: ptr:: write_unaligned ( out. cast ( ) , chunk) }
314349}
315350
316351impl fmt:: Debug for Writer < ' _ > {
0 commit comments