|
44 | 44 | use std::convert::From; |
45 | 45 | use std::fs::File; |
46 | 46 | use std::io; |
| 47 | +use std::iter::FusedIterator; |
47 | 48 | use std::ops::{BitAnd, BitOr, Deref}; |
48 | 49 | use std::rc::Rc; |
49 | 50 | use std::sync::atomic::Ordering; |
@@ -455,8 +456,101 @@ pub trait GuestMemory { |
455 | 456 | .ok_or(Error::InvalidGuestAddress(addr)) |
456 | 457 | .and_then(|(r, addr)| r.get_slice(addr, count)) |
457 | 458 | } |
| 459 | + |
| 460 | + /// Returns an iterator over [`VolatileSlice`](struct.VolatileSlice.html)s, together covering |
| 461 | + /// `count` bytes starting at `addr`. |
| 462 | + /// |
| 463 | + /// Iterating in this way is necessary because the given address range may be fragmented across |
| 464 | + /// multiple [`GuestMemoryRegion`]s. |
| 465 | + /// |
| 466 | + /// The iterator’s items are wrapped in [`Result`], i.e. errors are reported on individual |
| 467 | + /// items. If there is no such error, the cumulative length of all items will be equal to |
| 468 | + /// `count`. If `count` is 0, an empty iterator will be returned. |
| 469 | + fn get_slices<'a>( |
| 470 | + &'a self, |
| 471 | + addr: GuestAddress, |
| 472 | + count: usize, |
| 473 | + ) -> GuestMemorySliceIterator<'a, Self> { |
| 474 | + GuestMemorySliceIterator { |
| 475 | + mem: self, |
| 476 | + addr, |
| 477 | + count, |
| 478 | + } |
| 479 | + } |
| 480 | +} |
| 481 | + |
| 482 | +/// Iterates over [`VolatileSlice`]s that together form a guest memory area. |
| 483 | +/// |
| 484 | +/// Returned by [`GuestMemory::get_slices()`]. |
| 485 | +#[derive(Debug)] |
| 486 | +pub struct GuestMemorySliceIterator<'a, M: GuestMemory + ?Sized> { |
| 487 | + /// Underlying memory |
| 488 | + mem: &'a M, |
| 489 | + /// Next address in the guest memory area |
| 490 | + addr: GuestAddress, |
| 491 | + /// Remaining bytes in the guest memory area |
| 492 | + count: usize, |
| 493 | +} |
| 494 | + |
| 495 | +impl<'a, M: GuestMemory + ?Sized> GuestMemorySliceIterator<'a, M> { |
| 496 | + /// Helper function for [`<Self as Iterator>::next()`](GuestMemorySliceIterator::next). |
| 497 | + /// |
| 498 | + /// Get the next slice (i.e. the one starting from `self.addr` with a length up to |
| 499 | + /// `self.count`) and update the internal state. |
| 500 | + /// |
| 501 | + /// # Safety |
| 502 | + /// |
| 503 | + /// This function does not reset to `self.count` to 0 in case of error, i.e. will not stop |
| 504 | + /// iterating. Actual behavior after an error is ill-defined, so the caller must check the |
| 505 | + /// return value, and in case of an error, reset `self.count` to 0. |
| 506 | + /// |
| 507 | + /// (This is why this function exists, so this resetting can be done in a single central |
| 508 | + /// location.) |
| 509 | + unsafe fn do_next(&mut self) -> Option<Result<VolatileSlice<'a, MS<'a, M>>>> { |
| 510 | + if self.count == 0 { |
| 511 | + return None; |
| 512 | + } |
| 513 | + |
| 514 | + let Some((region, start)) = self.mem.to_region_addr(self.addr) else { |
| 515 | + return Some(Err(Error::InvalidGuestAddress(self.addr))); |
| 516 | + }; |
| 517 | + |
| 518 | + let cap = region.len() - start.raw_value(); |
| 519 | + let len = std::cmp::min(cap, self.count as GuestUsize); |
| 520 | + |
| 521 | + self.count -= len as usize; |
| 522 | + self.addr = match self.addr.overflowing_add(len as GuestUsize) { |
| 523 | + (x @ GuestAddress(0), _) | (x, false) => x, |
| 524 | + (_, true) => return Some(Err(Error::GuestAddressOverflow)), |
| 525 | + }; |
| 526 | + |
| 527 | + Some(region.get_slice(start, len as usize)) |
| 528 | + } |
| 529 | +} |
| 530 | + |
| 531 | +impl<'a, M: GuestMemory + ?Sized> Iterator for GuestMemorySliceIterator<'a, M> { |
| 532 | + type Item = Result<VolatileSlice<'a, MS<'a, M>>>; |
| 533 | + |
| 534 | + fn next(&mut self) -> Option<Self::Item> { |
| 535 | + // SAFETY: |
| 536 | + // We reset `self.count` to 0 on error |
| 537 | + match unsafe { self.do_next() } { |
| 538 | + Some(Ok(slice)) => Some(Ok(slice)), |
| 539 | + other => { |
| 540 | + // On error (or end), reset to 0 so iteration remains stopped |
| 541 | + self.count = 0; |
| 542 | + other |
| 543 | + } |
| 544 | + } |
| 545 | + } |
458 | 546 | } |
459 | 547 |
|
| 548 | +/// This iterator continues to return `None` when exhausted. |
| 549 | +/// |
| 550 | +/// [`<Self as Iterator>::next()`](GuestMemorySliceIterator::next) sets `self.count` to 0 when |
| 551 | +/// returning `None`, ensuring that it will only return `None` from that point on. |
| 552 | +impl<M: GuestMemory + ?Sized> FusedIterator for GuestMemorySliceIterator<'_, M> {} |
| 553 | + |
460 | 554 | impl<T: GuestMemory + ?Sized> Bytes<GuestAddress> for T { |
461 | 555 | type E = Error; |
462 | 556 |
|
|
0 commit comments