Auto merge of #98324 - conradludgate:write-vectored-vec, r=Mark-Simul… · oli-obk/rust@64eb9ab (original) (raw)
`@@ -396,38 +396,99 @@ fn slice_write_vectored(
`
396
396
`Ok(nwritten)
`
397
397
`}
`
398
398
``
399
``
`-
// Resizing write implementation
`
400
``
`-
fn vec_write(pos_mut: &mut u64, vec: &mut Vec<u8, A>, buf: &[u8]) -> io::Result
`
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 {
`
404
405
`let pos: usize = (*pos_mut).try_into().map_err(|_| {
`
405
406
` io::const_io_error!(
`
406
407
`ErrorKind::InvalidInput,
`
407
408
`"cursor position exceeds maximum possible vector length",
`
408
409
`)
`
409
410
`})?;
`
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
`+
}
`
424
436
`}
`
425
437
``
``
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(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(pos_mut: &mut u64, vec: &mut Vec<u8, A>, buf: &[u8]) -> io::Result
`
``
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
+
426
478
`// 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)
`
429
481
`}
`
430
482
``
``
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
`
431
492
`fn vec_write_vectored(
`
432
493
`pos_mut: &mut u64,
`
433
494
`vec: &mut Vec<u8, A>,
`
`@@ -436,11 +497,26 @@ fn vec_write_vectored(
`
436
497
`where
`
437
498
`A: Allocator,
`
438
499
`{
`
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
`+
}
`
442
515
`}
`
443
``
`-
Ok(nwritten)
`
``
516
+
``
517
`+
// Bump us forward
`
``
518
`+
*pos_mut += buf_len as u64;
`
``
519
`+
Ok(buf_len)
`
444
520
`}
`
445
521
``
446
522
`#[stable(feature = "rust1", since = "1.0.0")]
`