|
| 1 | +/* |
| 2 | +Copyright 2025 The Hyperlight Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | +*/ |
| 16 | + |
| 17 | +use std::collections::HashMap; |
| 18 | + |
| 19 | +use hyperlight_common::mem::PAGE_SIZE_USIZE; |
| 20 | + |
| 21 | +/// A compact snapshot representation that stores pages in a contiguous buffer |
| 22 | +/// with an index for efficient lookup. |
| 23 | +/// |
| 24 | +/// This struct is designed to efficiently store and retrieve memory snapshots |
| 25 | +/// by using a contiguous buffer for all page data combined with a HashMap index |
| 26 | +/// for page lookups. This approach reduces memory overhead |
| 27 | +/// compared to storing pages individually. |
| 28 | +/// |
| 29 | +/// # Clone Derivation |
| 30 | +/// |
| 31 | +/// This struct derives `Clone` because it's stored in `Vec<PageSnapshot>` within |
| 32 | +/// `SharedMemorySnapshotManager`, which itself derives `Clone`. |
| 33 | +#[derive(Clone)] |
| 34 | +pub(super) struct PageSnapshot { |
| 35 | + /// Maps page numbers to their offset within the buffer (in page units) |
| 36 | + page_index: HashMap<usize, usize>, // page_number -> buffer_offset_in_pages |
| 37 | + /// Contiguous buffer containing all the page data |
| 38 | + buffer: Vec<u8>, |
| 39 | + /// How many non-main-RAM regions were mapped when this snapshot was taken? |
| 40 | + mapped_rgns: u64, |
| 41 | +} |
| 42 | + |
| 43 | +impl PageSnapshot { |
| 44 | + /// Create a snapshot from a list of page numbers with pre-allocated buffer |
| 45 | + pub(super) fn with_pages_and_buffer( |
| 46 | + page_numbers: Vec<usize>, |
| 47 | + buffer: Vec<u8>, |
| 48 | + mapped_rgns: u64, |
| 49 | + ) -> Self { |
| 50 | + let page_count = page_numbers.len(); |
| 51 | + let mut page_index = HashMap::with_capacity(page_count); |
| 52 | + |
| 53 | + // Map each page number to its offset in the buffer |
| 54 | + for (buffer_offset, page_num) in page_numbers.into_iter().enumerate() { |
| 55 | + page_index.insert(page_num, buffer_offset); |
| 56 | + } |
| 57 | + |
| 58 | + Self { |
| 59 | + page_index, |
| 60 | + buffer, |
| 61 | + mapped_rgns, |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + /// Get page data by page number, returns None if page is not in snapshot |
| 66 | + pub(super) fn get_page(&self, page_num: usize) -> Option<&[u8]> { |
| 67 | + self.page_index.get(&page_num).map(|&buffer_offset| { |
| 68 | + let start = buffer_offset * PAGE_SIZE_USIZE; |
| 69 | + let end = start + PAGE_SIZE_USIZE; |
| 70 | + &self.buffer[start..end] |
| 71 | + }) |
| 72 | + } |
| 73 | + |
| 74 | + /// Get an iterator over all page numbers in this snapshot |
| 75 | + pub(super) fn page_numbers(&self) -> impl Iterator<Item = usize> + '_ { |
| 76 | + self.page_index.keys().copied() |
| 77 | + } |
| 78 | + |
| 79 | + /// Get the maximum page number in this snapshot, or None if empty |
| 80 | + pub(super) fn max_page(&self) -> Option<usize> { |
| 81 | + self.page_index.keys().max().copied() |
| 82 | + } |
| 83 | + |
| 84 | + /// Get the number of mapped regions when this snapshot was taken |
| 85 | + pub(super) fn mapped_rgns(&self) -> u64 { |
| 86 | + self.mapped_rgns |
| 87 | + } |
| 88 | +} |
0 commit comments