Skip to main content

bitmap/
storage.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zx_status::Status;
6
7/// Trait for the backing storage of a raw bitmap.
8pub trait Storage {
9    /// True if this storage supports growing.
10    const SUPPORTS_GROW: bool = false;
11
12    /// Allocates at least `size` bytes of storage.
13    fn allocate(&mut self, size: usize) -> Result<(), Status>;
14
15    /// Returns a read-only slice of `usize` words to the underlying storage.
16    fn get_data(&self) -> &[usize];
17
18    /// Returns a mutable slice of `usize` words to the underlying storage.
19    fn get_data_mut(&mut self) -> &mut [usize];
20
21    /// Optionally grows the storage to at least `size` bytes.
22    fn grow(&mut self, _size: usize) -> Result<(), Status> {
23        Err(Status::NO_RESOURCES)
24    }
25}
26
27pub struct DefaultStorage {
28    storage: kalloc::Box<[usize]>,
29}
30
31impl core::fmt::Debug for DefaultStorage {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        f.debug_struct("DefaultStorage").field("storage", &self.storage.as_ref()).finish()
34    }
35}
36
37impl Default for DefaultStorage {
38    fn default() -> Self {
39        Self::new()
40    }
41}
42
43impl DefaultStorage {
44    /// Create a new, empty default storage.
45    pub const fn new() -> Self {
46        Self { storage: kalloc::Box::<[usize]>::empty_slice() }
47    }
48}
49
50impl Storage for DefaultStorage {
51    fn allocate(&mut self, size: usize) -> Result<(), Status> {
52        let usize_size = core::mem::size_of::<usize>();
53        let num_elements = (size + usize_size - 1) / usize_size;
54        let new_storage = kalloc::Box::<[usize]>::try_new_zeroed_slice(num_elements)
55            .map_err(|_| Status::NO_MEMORY)?;
56        self.storage = new_storage;
57        Ok(())
58    }
59
60    fn get_data(&self) -> &[usize] {
61        &self.storage
62    }
63
64    fn get_data_mut(&mut self) -> &mut [usize] {
65        &mut self.storage
66    }
67}
68
69/// A fixed-size static bitmap storage.
70///
71/// `N` is the number of `usize` elements in the array.
72#[derive(Debug)]
73pub struct FixedStorage<const N: usize> {
74    storage: [usize; N],
75}
76
77impl<const N: usize> Default for FixedStorage<N> {
78    fn default() -> Self {
79        Self::new()
80    }
81}
82
83impl<const N: usize> FixedStorage<N> {
84    /// Create a new, zero-initialized fixed storage.
85    pub const fn new() -> Self {
86        Self { storage: [0; N] }
87    }
88}
89
90impl<const N: usize> Storage for FixedStorage<N> {
91    fn allocate(&mut self, size: usize) -> Result<(), Status> {
92        let usize_size = core::mem::size_of::<usize>();
93        let required_elements = (size + usize_size - 1) / usize_size;
94        if required_elements > N {
95            return Err(Status::INVALID_ARGS);
96        }
97        Ok(())
98    }
99
100    fn get_data(&self) -> &[usize] {
101        &self.storage
102    }
103
104    fn get_data_mut(&mut self) -> &mut [usize] {
105        &mut self.storage
106    }
107}
108
109#[cfg(all(not(is_kernel), target_os = "fuchsia"))]
110mod userspace {
111    use super::*;
112    use fuchsia_runtime;
113
114    // Helper to map a VMO with default read/write permissions at offset 0.
115    fn map_vmo(vmar: &zx::Vmar, vmo: &zx::Vmo, size: usize) -> Result<usize, Status> {
116        vmar.map(0, vmo, 0, size, zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE)
117    }
118
119    /// Userspace-only storage backed by a VMO (Virtual Memory Object).
120    #[derive(Default, Debug)]
121    pub struct VmoStorage {
122        vmo: Option<zx::Vmo>,
123        mapped_addr: usize,
124        size: usize,
125    }
126
127    impl VmoStorage {
128        /// Create a new, unallocated VmoStorage.
129        pub const fn new() -> Self {
130            Self { vmo: None, mapped_addr: 0, size: 0 }
131        }
132
133        fn release(&mut self) {
134            if self.mapped_addr != 0 {
135                let vmar = fuchsia_runtime::vmar_root_self();
136                // SAFETY: We mapped this memory in `allocate` or `grow` and we own it.
137                unsafe {
138                    let _ = vmar.unmap(self.mapped_addr, self.size);
139                }
140            }
141            self.mapped_addr = 0;
142            self.size = 0;
143            self.vmo = None;
144        }
145
146        /// Access the underlying VMO if allocated.
147        pub fn get_vmo(&self) -> Option<&zx::Vmo> {
148            self.vmo.as_ref()
149        }
150    }
151
152    impl Drop for VmoStorage {
153        fn drop(&mut self) {
154            self.release();
155        }
156    }
157
158    impl Storage for VmoStorage {
159        const SUPPORTS_GROW: bool = true;
160
161        fn allocate(&mut self, size: usize) -> Result<(), Status> {
162            self.release();
163            let page_size = zx::system_get_page_size() as usize;
164            let rounded_size = (size + page_size - 1) & !(page_size - 1);
165            let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, rounded_size as u64)?;
166            let _ = vmo.set_name(&zx::Name::new_lossy("vmo-backed-bitmap"));
167
168            let vmar = fuchsia_runtime::vmar_root_self();
169            let mapped_addr = map_vmo(&vmar, &vmo, rounded_size)?;
170
171            self.vmo = Some(vmo);
172            self.mapped_addr = mapped_addr;
173            self.size = rounded_size;
174            Ok(())
175        }
176
177        fn get_data(&self) -> &[usize] {
178            if self.mapped_addr == 0 {
179                &[]
180            } else {
181                // SAFETY: `mapped_addr` is a valid address mapped from a VMO with `self.size` bytes.
182                // VMO mappings are page-aligned (and thus aligned to `usize`). The size of the
183                // slice in words is `self.size / size_of::<usize>()`.
184                // We use `with_exposed_provenance` to construct a pointer with exposed provenance,
185                // and then `from_raw_parts` to construct a slice of `usize`.
186                unsafe {
187                    let ptr = core::ptr::with_exposed_provenance::<usize>(self.mapped_addr);
188                    core::slice::from_raw_parts(ptr, self.size / core::mem::size_of::<usize>())
189                }
190            }
191        }
192
193        fn get_data_mut(&mut self) -> &mut [usize] {
194            if self.mapped_addr == 0 {
195                &mut []
196            } else {
197                // SAFETY: `mapped_addr` is a valid address mapped from a VMO with `self.size` bytes.
198                // VMO mappings are page-aligned (and thus aligned to `usize`). The size of the
199                // slice in words is `self.size / size_of::<usize>()`.
200                // We use `with_exposed_provenance_mut` to construct a mutable pointer with exposed provenance,
201                // and then `from_raw_parts_mut` to construct a mutable slice of `usize`.
202                unsafe {
203                    let ptr = core::ptr::with_exposed_provenance_mut::<usize>(self.mapped_addr);
204                    core::slice::from_raw_parts_mut(ptr, self.size / core::mem::size_of::<usize>())
205                }
206            }
207        }
208
209        fn grow(&mut self, size: usize) -> Result<(), Status> {
210            if size <= self.size {
211                return Ok(());
212            }
213            let page_size = zx::system_get_page_size() as usize;
214            let rounded_size = (size + page_size - 1) & !(page_size - 1);
215            let vmo = self.vmo.as_ref().ok_or(Status::BAD_STATE)?;
216            vmo.set_size(rounded_size as u64)?;
217
218            let vmar = fuchsia_runtime::vmar_root_self();
219            let vmar_info = vmar.info()?;
220            let extend_offset = self.mapped_addr + self.size - vmar_info.base;
221            let extend_size = rounded_size - self.size;
222            let map_result = vmar.map(
223                extend_offset,
224                vmo,
225                self.size as u64,
226                extend_size,
227                zx::VmarFlags::PERM_READ | zx::VmarFlags::PERM_WRITE | zx::VmarFlags::SPECIFIC,
228            );
229            if map_result.is_ok() {
230                self.size = rounded_size;
231                return Ok(());
232            }
233
234            let new_addr = map_vmo(&vmar, vmo, rounded_size)?;
235            // SAFETY: We mapped the old memory at `mapped_addr` and we are unmapping it.
236            unsafe {
237                let _ = vmar.unmap(self.mapped_addr, self.size);
238            }
239            self.mapped_addr = new_addr;
240            self.size = rounded_size;
241            Ok(())
242        }
243    }
244}
245
246#[cfg(all(not(is_kernel), target_os = "fuchsia"))]
247pub use userspace::VmoStorage;