@@ -396,38 +396,99 @@ fn slice_write_vectored(
396396 Ok ( nwritten)
397397}
398398
399- // Resizing write implementation
400- fn vec_write < A > ( pos_mut : & mut u64 , vec : & mut Vec < u8 , A > , buf : & [ u8 ] ) -> io:: Result < usize >
401- where
402- A : Allocator ,
403- {
399+ /// Reserves the required space, and pads the vec with 0s if necessary.
400+ fn reserve_and_pad < A : Allocator > (
401+ pos_mut : & mut u64 ,
402+ vec : & mut Vec < u8 , A > ,
403+ buf_len : usize ,
404+ ) -> io:: Result < usize > {
404405 let pos: usize = ( * pos_mut) . try_into ( ) . map_err ( |_| {
405406 io:: const_io_error!(
406407 ErrorKind :: InvalidInput ,
407408 "cursor position exceeds maximum possible vector length" ,
408409 )
409410 } ) ?;
410- // Make sure the internal buffer is as least as big as where we
411- // currently are
412- let len = vec. len ( ) ;
413- if len < pos {
414- // use `resize` so that the zero filling is as efficient as possible
415- vec. resize ( pos, 0 ) ;
416- }
417- // Figure out what bytes will be used to overwrite what's currently
418- // there (left), and what will be appended on the end (right)
419- {
420- let space = vec. len ( ) - pos;
421- let ( left, right) = buf. split_at ( cmp:: min ( space, buf. len ( ) ) ) ;
422- vec[ pos..pos + left. len ( ) ] . copy_from_slice ( left) ;
423- vec. extend_from_slice ( right) ;
411+
412+ // For safety reasons, we don't want these numbers to overflow
413+ // otherwise our allocation won't be enough
414+ let desired_cap = pos. saturating_add ( buf_len) ;
415+ if desired_cap > vec. capacity ( ) {
416+ // We want our vec's total capacity
417+ // to have room for (pos+buf_len) bytes. Reserve allocates
418+ // based on additional elements from the length, so we need to
419+ // reserve the difference
420+ vec. reserve ( desired_cap - vec. len ( ) ) ;
421+ }
422+ // Pad if pos is above the current len.
423+ if pos > vec. len ( ) {
424+ let diff = pos - vec. len ( ) ;
425+ // Unfortunately, `resize()` would suffice but the optimiser does not
426+ // realise the `reserve` it does can be eliminated. So we do it manually
427+ // to eliminate that extra branch
428+ let spare = vec. spare_capacity_mut ( ) ;
429+ debug_assert ! ( spare. len( ) >= diff) ;
430+ // Safety: we have allocated enough capacity for this.
431+ // And we are only writing, not reading
432+ unsafe {
433+ spare. get_unchecked_mut ( ..diff) . fill ( core:: mem:: MaybeUninit :: new ( 0 ) ) ;
434+ vec. set_len ( pos) ;
435+ }
424436 }
425437
438+ Ok ( pos)
439+ }
440+
441+ /// Writes the slice to the vec without allocating
442+ /// # Safety: vec must have buf.len() spare capacity
443+ unsafe fn vec_write_unchecked < A > ( pos : usize , vec : & mut Vec < u8 , A > , buf : & [ u8 ] ) -> usize
444+ where
445+ A : Allocator ,
446+ {
447+ debug_assert ! ( vec. capacity( ) >= pos + buf. len( ) ) ;
448+ vec. as_mut_ptr ( ) . add ( pos) . copy_from ( buf. as_ptr ( ) , buf. len ( ) ) ;
449+ pos + buf. len ( )
450+ }
451+
452+ /// Resizing write implementation for [`Cursor`]
453+ ///
454+ /// Cursor is allowed to have a pre-allocated and initialised
455+ /// vector body, but with a position of 0. This means the [`Write`]
456+ /// will overwrite the contents of the vec.
457+ ///
458+ /// This also allows for the vec body to be empty, but with a position of N.
459+ /// This means that [`Write`] will pad the vec with 0 initially,
460+ /// before writing anything from that point
461+ fn vec_write < A > ( pos_mut : & mut u64 , vec : & mut Vec < u8 , A > , buf : & [ u8 ] ) -> io:: Result < usize >
462+ where
463+ A : Allocator ,
464+ {
465+ let buf_len = buf. len ( ) ;
466+ let mut pos = reserve_and_pad ( pos_mut, vec, buf_len) ?;
467+
468+ // Write the buf then progress the vec forward if necessary
469+ // Safety: we have ensured that the capacity is available
470+ // and that all bytes get written up to pos
471+ unsafe {
472+ pos = vec_write_unchecked ( pos, vec, buf) ;
473+ if pos > vec. len ( ) {
474+ vec. set_len ( pos) ;
475+ }
476+ } ;
477+
426478 // Bump us forward
427- * pos_mut = ( pos + buf . len ( ) ) as u64 ;
428- Ok ( buf . len ( ) )
479+ * pos_mut += buf_len as u64 ;
480+ Ok ( buf_len )
429481}
430482
483+ /// Resizing write_vectored implementation for [`Cursor`]
484+ ///
485+ /// Cursor is allowed to have a pre-allocated and initialised
486+ /// vector body, but with a position of 0. This means the [`Write`]
487+ /// will overwrite the contents of the vec.
488+ ///
489+ /// This also allows for the vec body to be empty, but with a position of N.
490+ /// This means that [`Write`] will pad the vec with 0 initially,
491+ /// before writing anything from that point
431492fn vec_write_vectored < A > (
432493 pos_mut : & mut u64 ,
433494 vec : & mut Vec < u8 , A > ,
@@ -436,11 +497,26 @@ fn vec_write_vectored<A>(
436497where
437498 A : Allocator ,
438499{
439- let mut nwritten = 0 ;
440- for buf in bufs {
441- nwritten += vec_write ( pos_mut, vec, buf) ?;
500+ // For safety reasons, we don't want this sum to overflow ever.
501+ // If this saturates, the reserve should panic to avoid any unsound writing.
502+ let buf_len = bufs. iter ( ) . fold ( 0usize , |a, b| a. saturating_add ( b. len ( ) ) ) ;
503+ let mut pos = reserve_and_pad ( pos_mut, vec, buf_len) ?;
504+
505+ // Write the buf then progress the vec forward if necessary
506+ // Safety: we have ensured that the capacity is available
507+ // and that all bytes get written up to the last pos
508+ unsafe {
509+ for buf in bufs {
510+ pos = vec_write_unchecked ( pos, vec, buf) ;
511+ }
512+ if pos > vec. len ( ) {
513+ vec. set_len ( pos) ;
514+ }
442515 }
443- Ok ( nwritten)
516+
517+ // Bump us forward
518+ * pos_mut += buf_len as u64 ;
519+ Ok ( buf_len)
444520}
445521
446522#[ stable( feature = "rust1" , since = "1.0.0" ) ]
0 commit comments