Take more advantage of the isize::MAX limit in Layout · qinheping/verify-rust-std@cc10a76 (original) (raw)

`@@ -5,8 +5,10 @@

`

5

5

`// Your performance intuition is useless. Run perf.

`

6

6

``

7

7

`use crate::error::Error;

`

``

8

`+

use crate::intrinsics::{unchecked_add, unchecked_mul, unchecked_sub};

`

``

9

`+

use crate::mem::SizedTypeProperties;

`

8

10

`use crate::ptr::{Alignment, NonNull};

`

9

``

`-

use crate::{assert_unsafe_precondition, cmp, fmt, mem};

`

``

11

`+

use crate::{assert_unsafe_precondition, fmt, mem};

`

10

12

``

11

13

`// While this function is used in one place and its implementation

`

12

14

`// could be inlined, the previous attempts to do so made rustc

`

`@@ -98,7 +100,10 @@ impl Layout {

`

98

100

`//

`

99

101

`// Above implies that checking for summation overflow is both

`

100

102

`// necessary and sufficient.

`

101

``

`-

isize::MAX as usize - (align.as_usize() - 1)

`

``

103

+

``

104

`` +

// SAFETY: the maximum possible alignment is isize::MAX + 1,

``

``

105

`+

// so the subtraction cannot overflow.

`

``

106

`+

unsafe { unchecked_sub(isize::MAX as usize + 1, align.as_usize()) }

`

102

107

`}

`

103

108

``

104

109

`/// Internal helper constructor to skip revalidating alignment validity.

`

`@@ -252,9 +257,14 @@ impl Layout {

`

252

257

`` /// Returns an error if the combination of self.size() and the given

``

253

258

`` /// align violates the conditions listed in [Layout::from_size_align].

``

254

259

`#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]

`

``

260

`+

#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]

`

255

261

`#[inline]

`

256

``

`-

pub fn align_to(&self, align: usize) -> Result<Self, LayoutError> {

`

257

``

`-

Layout::from_size_align(self.size(), cmp::max(self.align(), align))

`

``

262

`+

pub const fn align_to(&self, align: usize) -> Result<Self, LayoutError> {

`

``

263

`+

if let Some(align) = Alignment::new(align) {

`

``

264

`+

Layout::from_size_alignment(self.size, Alignment::max(self.align, align))

`

``

265

`+

} else {

`

``

266

`+

Err(LayoutError)

`

``

267

`+

}

`

258

268

`}

`

259

269

``

260

270

`` /// Returns the amount of padding we must insert after self

``

`@@ -279,29 +289,42 @@ impl Layout {

`

279

289

`` without modifying the Layout"]

``

280

290

`#[inline]

`

281

291

`pub const fn padding_needed_for(&self, align: usize) -> usize {

`

282

``

`-

let len = self.size();

`

``

292

`` +

// FIXME: Can we just change the type on this to Alignment?

``

``

293

`+

let Some(align) = Alignment::new(align) else { return usize::MAX };

`

``

294

`+

let len_rounded_up = self.size_rounded_up_to_custom_align(align);

`

``

295

`+

// SAFETY: Cannot overflow because the rounded-up value is never less

`

``

296

`+

unsafe { unchecked_sub(len_rounded_up, self.size) }

`

``

297

`+

}

`

283

298

``

``

299

`` +

/// Returns the smallest multiple of align greater than or equal to self.size().

``

``

300

`+

///

`

``

301

`` +

/// This can return at most Alignment::MAX (aka isize::MAX + 1)

``

``

302

`` +

/// because the original size is at most isize::MAX.

``

``

303

`+

#[inline]

`

``

304

`+

const fn size_rounded_up_to_custom_align(&self, align: Alignment) -> usize {

`

``

305

`+

// SAFETY:

`

284

306

`// Rounded up value is:

`

285

``

`-

// len_rounded_up = (len + align - 1) & !(align - 1);

`

286

``

`` -

// and then we return the padding difference: len_rounded_up - len.

``

``

307

`+

// size_rounded_up = (size + align - 1) & !(align - 1);

`

287

308

`//

`

288

``

`-

// We use modular arithmetic throughout:

`

``

309

`+

// The arithmetic we do here can never overflow:

`

289

310

`//

`

290

311

`// 1. align is guaranteed to be > 0, so align - 1 is always

`

291

312

`// valid.

`

292

313

`//

`

293

``

`` -

// 2. len + align - 1 can overflow by at most align - 1,

``

294

``

`` -

// so the &-mask with !(align - 1) will ensure that in the

``

295

``

`` -

// case of overflow, len_rounded_up will itself be 0.

``

296

``

`` -

// Thus the returned padding, when added to len, yields 0,

``

297

``

`` -

// which trivially satisfies the alignment align.

``

``

314

`` +

// 2. size is at most isize::MAX, so adding align - 1 (which is at

``

``

315

`` +

// most isize::MAX) can never overflow a usize.

``

298

316

`//

`

299

``

`-

// (Of course, attempts to allocate blocks of memory whose

`

300

``

`-

// size and padding overflow in the above manner should cause

`

301

``

`-

// the allocator to yield an error anyway.)

`

302

``

-

303

``

`-

let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);

`

304

``

`-

len_rounded_up.wrapping_sub(len)

`

``

317

`` +

// 3. masking by the alignment can remove at most align - 1,

``

``

318

`+

// which is what we just added, thus the value we return is never

`

``

319

`` +

// less than the original size.

``

``

320

`+

//

`

``

321

`+

// (Size 0 Align MAX is already aligned, so stays the same, but things like

`

``

322

`` +

// Size 1 Align MAX or Size isize::MAX Align 2 round up to isize::MAX + 1.)

``

``

323

`+

unsafe {

`

``

324

`+

let align_m1 = unchecked_sub(align.as_usize(), 1);

`

``

325

`+

let size_rounded_up = unchecked_add(self.size, align_m1) & !align_m1;

`

``

326

`+

size_rounded_up

`

``

327

`+

}

`

305

328

`}

`

306

329

``

307

330

`/// Creates a layout by rounding the size of this layout up to a multiple

`

`@@ -315,12 +338,11 @@ impl Layout {

`

315

338

` without modifying the original"]

`

316

339

`#[inline]

`

317

340

`pub const fn pad_to_align(&self) -> Layout {

`

318

``

`-

let pad = self.padding_needed_for(self.align());

`

319

341

`// This cannot overflow. Quoting from the invariant of Layout:

`

320

342

`` // > size, when rounded up to the nearest multiple of align,

``

321

343

`// > must not overflow isize (i.e., the rounded value must be

`

322

344

`` // > less than or equal to isize::MAX)

``

323

``

`-

let new_size = self.size() + pad;

`

``

345

`+

let new_size = self.size_rounded_up_to_custom_align(self.align);

`

324

346

``

325

347

`` // SAFETY: padded size is guaranteed to not exceed isize::MAX.

``

326

348

`unsafe { Layout::from_size_align_unchecked(new_size, self.align()) }

`

`@@ -333,20 +355,36 @@ impl Layout {

`

333

355

`` /// layout of the array and offs is the distance between the start

``

334

356

`/// of each element in the array.

`

335

357

`///

`

``

358

`+

/// (That distance between elements is sometimes known as "stride".)

`

``

359

`+

///

`

336

360

`` /// On arithmetic overflow, returns LayoutError.

``

``

361

`+

///

`

``

362

`+

/// # Examples

`

``

363

`+

///

`

``

364


/// ```

``

365

`+

/// #![feature(alloc_layout_extra)]

`

``

366

`+

/// use std::alloc::Layout;

`

``

367

`+

///

`

``

368

`+

/// // All rust types have a size that's a multiple of their alignment.

`

``

369

`+

/// let normal = Layout::from_size_align(12, 4).unwrap();

`

``

370

`+

/// let repeated = normal.repeat(3).unwrap();

`

``

371

`+

/// assert_eq!(repeated, (Layout::from_size_align(36, 4).unwrap(), 12));

`

``

372

`+

///

`

``

373

`+

/// // But you can manually make layouts which don't meet that rule.

`

``

374

`+

/// let padding_needed = Layout::from_size_align(6, 4).unwrap();

`

``

375

`+

/// let repeated = padding_needed.repeat(3).unwrap();

`

``

376

`+

/// assert_eq!(repeated, (Layout::from_size_align(24, 4).unwrap(), 8));

`

``

377


/// ```

337

378

`#[unstable(feature = "alloc_layout_extra", issue = "55724")]

`

``

379

`+

#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]

`

338

380

`#[inline]

`

339

``

`-

pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {

`

340

``

`-

// This cannot overflow. Quoting from the invariant of Layout:

`

341

``

`` -

// > size, when rounded up to the nearest multiple of align,

``

342

``

`-

// > must not overflow isize (i.e., the rounded value must be

`

343

``

`` -

// > less than or equal to isize::MAX)

``

344

``

`-

let padded_size = self.size() + self.padding_needed_for(self.align());

`

345

``

`-

let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?;

`

346

``

-

347

``

`-

// The safe constructor is called here to enforce the isize size limit.

`

348

``

`-

let layout = Layout::from_size_alignment(alloc_size, self.align)?;

`

349

``

`-

Ok((layout, padded_size))

`

``

381

`+

pub const fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {

`

``

382

`+

let padded = self.pad_to_align();

`

``

383

`+

if let Ok(repeated) = padded.repeat_packed(n) {

`

``

384

`+

Ok((repeated, padded.size()))

`

``

385

`+

} else {

`

``

386

`+

Err(LayoutError)

`

``

387

`+

}

`

350

388

`}

`

351

389

``

352

390

`` /// Creates a layout describing the record for self followed by

``

`@@ -395,17 +433,23 @@ impl Layout {

`

395

433

`/// # assert_eq!(repr_c(&[u64, u32, u16, u32]), Ok((s, vec![0, 8, 12, 16])));

`

396

434

```` /// ```

````

397

435

`#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]

`

``

436

`+

#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]

`

398

437

`#[inline]

`

399

``

`-

pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {

`

400

``

`-

let new_align = cmp::max(self.align, next.align);

`

401

``

`-

let pad = self.padding_needed_for(next.align());

`

402

``

-

403

``

`-

let offset = self.size().checked_add(pad).ok_or(LayoutError)?;

`

404

``

`-

let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?;

`

405

``

-

406

``

`-

// The safe constructor is called here to enforce the isize size limit.

`

407

``

`-

let layout = Layout::from_size_alignment(new_size, new_align)?;

`

408

``

`-

Ok((layout, offset))

`

``

438

`+

pub const fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {

`

``

439

`+

let new_align = Alignment::max(self.align, next.align);

`

``

440

`+

let offset = self.size_rounded_up_to_custom_align(next.align);

`

``

441

+

``

442

`` +

// SAFETY: offset is at most isize::MAX + 1 (such as from aligning

``

``

443

`` +

// to Alignment::MAX) and next.size is at most isize::MAX (from the

``

``

444

`` +

// Layout type invariant). Thus the largest possible new_size is

``

``

445

`` +

// isize::MAX + 1 + isize::MAX, which is usize::MAX, and cannot overflow.

``

``

446

`+

let new_size = unsafe { unchecked_add(offset, next.size) };

`

``

447

+

``

448

`+

if let Ok(layout) = Layout::from_size_alignment(new_size, new_align) {

`

``

449

`+

Ok((layout, offset))

`

``

450

`+

} else {

`

``

451

`+

Err(LayoutError)

`

``

452

`+

}

`

409

453

`}

`

410

454

``

411

455

`` /// Creates a layout describing the record for n instances of

``

`@@ -421,11 +465,15 @@ impl Layout {

`

421

465

`///

`

422

466

`` /// On arithmetic overflow, returns LayoutError.

``

423

467

`#[unstable(feature = "alloc_layout_extra", issue = "55724")]

`

``

468

`+

#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]

`

424

469

`#[inline]

`

425

``

`-

pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {

`

426

``

`-

let size = self.size().checked_mul(n).ok_or(LayoutError)?;

`

427

``

`-

// The safe constructor is called here to enforce the isize size limit.

`

428

``

`-

Layout::from_size_alignment(size, self.align)

`

``

470

`+

pub const fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {

`

``

471

`+

if let Some(size) = self.size.checked_mul(n) {

`

``

472

`+

// The safe constructor is called here to enforce the isize size limit.

`

``

473

`+

Layout::from_size_alignment(size, self.align)

`

``

474

`+

} else {

`

``

475

`+

Err(LayoutError)

`

``

476

`+

}

`

429

477

`}

`

430

478

``

431

479

`` /// Creates a layout describing the record for self followed by

``

`@@ -435,10 +483,13 @@ impl Layout {

`

435

483

`///

`

436

484

`` /// On arithmetic overflow, returns LayoutError.

``

437

485

`#[unstable(feature = "alloc_layout_extra", issue = "55724")]

`

``

486

`+

#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]

`

438

487

`#[inline]

`

439

``

`-

pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {

`

440

``

`-

let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?;

`

441

``

`-

// The safe constructor is called here to enforce the isize size limit.

`

``

488

`+

pub const fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {

`

``

489

`` +

// SAFETY: each size is at most isize::MAX == usize::MAX/2, so the

``

``

490

`` +

// sum is at most usize::MAX/2*2 == usize::MAX - 1, and cannot overflow.

``

``

491

`+

let new_size = unsafe { unchecked_add(self.size, next.size) };

`

``

492

`+

// The safe constructor enforces that the new size isn't too big for the alignment

`

442

493

`Layout::from_size_alignment(new_size, self.align)

`

443

494

`}

`

444

495

``

`@@ -451,14 +502,12 @@ impl Layout {

`

451

502

`#[inline]

`

452

503

`pub const fn array(n: usize) -> Result<Self, LayoutError> {

`

453

504

`` // Reduce the amount of code we need to monomorphize per T.

``

454

``

`-

return inner(mem::size_of::(), Alignment::of::(), n);

`

``

505

`+

return inner(T::LAYOUT, n);

`

455

506

``

456

507

`#[inline]

`

457

``

`-

const fn inner(

`

458

``

`-

element_size: usize,

`

459

``

`-

align: Alignment,

`

460

``

`-

n: usize,

`

461

``

`-

) -> Result<Layout, LayoutError> {

`

``

508

`+

const fn inner(element_layout: Layout, n: usize) -> Result<Layout, LayoutError> {

`

``

509

`+

let Layout { size: element_size, align } = element_layout;

`

``

510

+

462

511

`// We need to check two things about the size:

`

463

512

`` // - That the total size won't overflow a usize, and

``

464

513

`` // - That the total size still fits in an isize.

``

`@@ -473,7 +522,7 @@ impl Layout {

`

473

522

`// This is a useless hint inside this function, but after inlining this helps

`

474

523

`// deduplicate checks for whether the overall capacity is zero (e.g., in RawVec's

`

475

524

`// allocation path) before/after this multiplication.

`

476

``

`-

let array_size = unsafe { element_size.unchecked_mul(n) };

`

``

525

`+

let array_size = unsafe { unchecked_mul(element_size, n) };

`

477

526

``

478

527

`` // SAFETY: We just checked above that the array_size will not

``

479

528

`` // exceed isize::MAX even when rounded up to the alignment.

``