1use arrayref::{array_refs, mut_array_refs};
9use fuchsia_hash::Hash;
10use std::borrow::Borrow;
11use std::marker::PhantomData;
12use std::mem::size_of;
13use storage_ptr_slice::{MutPtrByteSlice, PtrByteSlice};
14
15pub const BOOT_PROFILE_NAME: &str = ".boot";
17
18pub const FILE_OPEN_MARKER: u64 = u64::MAX;
20
21pub const MAX_PROFILE_VMO_SIZE: u64 = 64 * 1024 * 1024;
23
24struct VmoWriter {
27 vmo: zx::Vmo,
28 vmo_offset: u64,
29 block_size: usize,
30 buffer: Vec<u8>,
31}
32
33impl VmoWriter {
34 fn new(block_size: usize) -> Result<Self, zx::Status> {
35 let vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 0)?;
36 Ok(Self { vmo, vmo_offset: 0, block_size, buffer: Vec::with_capacity(block_size) })
37 }
38
39 fn write<M: Message>(&mut self, msg: &M) -> Result<(), zx::Status> {
40 let msg_size = size_of::<M>();
41 if msg_size == 0 || self.block_size < msg_size {
42 return Ok(());
43 }
44 if self.vmo_offset + self.block_size as u64 > MAX_PROFILE_VMO_SIZE {
48 return Ok(());
49 }
50 let start = self.buffer.len();
51 self.buffer.resize(start + msg_size, 0);
52 msg.encode_to(MutPtrByteSlice::from(&mut self.buffer[start..]));
53 if self.buffer.len() + msg_size > self.block_size {
55 self.flush()?;
56 }
57 Ok(())
58 }
59
60 fn flush(&mut self) -> Result<(), zx::Status> {
61 let new_size = self.vmo_offset + self.block_size as u64;
62 let res = self.vmo.set_size(new_size).and_then(|()| {
63 self.vmo.write(&self.buffer, self.vmo_offset)?;
64 self.vmo_offset = new_size;
65 Ok(())
66 });
67 self.buffer.clear();
68 res
69 }
70
71 fn finish(mut self) -> Result<zx::Vmo, zx::Status> {
72 if !self.buffer.is_empty() {
73 self.flush()?;
74 }
75 Ok(self.vmo)
76 }
77}
78
79#[derive(Debug)]
81pub struct DecodedMessageIter<M> {
82 vmo: zx::Vmo,
83 vmo_size: u64,
84 vmo_offset: u64,
85 block_size: usize,
86 block_buf: Vec<u8>,
87 block_offset: usize,
88 _marker: PhantomData<M>,
89}
90
91impl<M: Message> DecodedMessageIter<M> {
92 fn new(vmo: zx::Vmo, block_size: usize) -> Result<Self, zx::Status> {
93 let vmo_size = vmo.get_size()?;
94 if vmo_size > MAX_PROFILE_VMO_SIZE {
95 return Err(zx::Status::OUT_OF_RANGE);
96 }
97 Ok(Self {
98 vmo,
99 vmo_size,
100 vmo_offset: 0,
101 block_size,
102 block_buf: Vec::with_capacity(block_size),
103 block_offset: 0,
104 _marker: PhantomData,
105 })
106 }
107}
108
109impl<M: Message> Iterator for DecodedMessageIter<M> {
110 type Item = M;
111
112 fn next(&mut self) -> Option<Self::Item> {
113 let msg_size = size_of::<M>();
114 if msg_size == 0 || self.block_size < msg_size {
115 return None;
116 }
117 if self.block_offset + msg_size > self.block_buf.len() {
118 if self.vmo_offset + self.block_size as u64 > self.vmo_size {
119 return None;
120 }
121 self.block_buf.resize(self.block_size, 0);
122 if self.vmo.read(&mut self.block_buf, self.vmo_offset).is_err() {
123 return None;
124 }
125 self.vmo_offset += self.block_size as u64;
126 self.block_offset = 0;
127 }
128 let entry_bytes = &self.block_buf[self.block_offset..self.block_offset + msg_size];
129 self.block_offset += msg_size;
130 let msg = M::decode_from(PtrByteSlice::from(entry_bytes));
131 if msg.is_zeroes() {
133 self.vmo_offset = self.vmo_size;
134 return None;
135 }
136 Some(msg)
137 }
138}
139
140pub trait Message:
142 Eq + PartialEq + Sized + Send + Sync + std::hash::Hash + Copy + Clone + 'static
143{
144 type IdType: std::fmt::Display + Ord + Send + Sized + Copy + Clone;
145
146 fn id(&self) -> Self::IdType;
147 fn offset(&self) -> u64;
148 fn encode_to(&self, dest: MutPtrByteSlice<'_>);
149 fn decode_from(src: PtrByteSlice<'_>) -> Self;
150 fn is_zeroes(&self) -> bool;
151
152 fn is_open_marker(&self) -> bool {
153 self.offset() == FILE_OPEN_MARKER
154 }
155
156 fn encode_to_vmo(
160 messages: impl IntoIterator<Item = impl Borrow<Self>>,
161 block_size: usize,
162 ) -> Result<zx::Vmo, zx::Status> {
163 let mut writer = VmoWriter::new(block_size)?;
164 for msg in messages {
165 writer.write(msg.borrow())?;
166 }
167 writer.finish()
168 }
169
170 fn decode_from_vmo(
173 vmo: zx::Vmo,
174 block_size: usize,
175 ) -> Result<DecodedMessageIter<Self>, zx::Status> {
176 DecodedMessageIter::new(vmo, block_size)
177 }
178}
179
180#[derive(Copy, Clone, Debug, Eq, std::hash::Hash, PartialEq)]
183pub struct BlobMessage {
184 pub id: Hash,
185 pub offset: u64,
189}
190
191impl Message for BlobMessage {
192 type IdType = Hash;
193
194 fn id(&self) -> Self::IdType {
195 self.id
196 }
197
198 fn offset(&self) -> u64 {
199 self.offset
200 }
201
202 fn encode_to(&self, mut dest: MutPtrByteSlice<'_>) {
203 let mut buf = [0u8; size_of::<Self>()];
204 let (first, second) = mut_array_refs![&mut buf, size_of::<Hash>(), size_of::<u64>()];
205 *first = self.id.into();
206 *second = self.offset.to_le_bytes();
207 dest.copy_from_slice(&buf);
208 }
209
210 fn decode_from(src: PtrByteSlice<'_>) -> Self {
211 let mut buf = [0u8; size_of::<Self>()];
212 src.copy_to_slice(&mut buf);
213 let (first, second) = array_refs!(&buf, size_of::<Hash>(), size_of::<u64>());
214 Self { id: Hash::from_array(*first), offset: u64::from_le_bytes(*second) }
215 }
216
217 fn is_zeroes(&self) -> bool {
218 self.id == Hash::from_array([0u8; size_of::<Hash>()]) && self.offset == 0
219 }
220}
221
222#[derive(Copy, Clone, Debug, Eq, std::hash::Hash, PartialEq)]
225pub struct FileMessage {
226 pub id: u64,
227 pub offset: u64,
231}
232
233impl Message for FileMessage {
234 type IdType = u64;
235
236 fn id(&self) -> Self::IdType {
237 self.id
238 }
239
240 fn offset(&self) -> u64 {
241 self.offset
242 }
243
244 fn encode_to(&self, mut dest: MutPtrByteSlice<'_>) {
245 let mut buf = [0u8; size_of::<Self>()];
246 let (first, second) = mut_array_refs![&mut buf, size_of::<u64>(), size_of::<u64>()];
247 *first = self.id.to_le_bytes();
248 *second = self.offset.to_le_bytes();
249 dest.copy_from_slice(&buf);
250 }
251
252 fn decode_from(src: PtrByteSlice<'_>) -> Self {
253 let mut buf = [0u8; size_of::<Self>()];
254 src.copy_to_slice(&mut buf);
255 let (first, second) = array_refs!(&buf, size_of::<u64>(), size_of::<u64>());
256 Self { id: u64::from_le_bytes(*first), offset: u64::from_le_bytes(*second) }
257 }
258
259 fn is_zeroes(&self) -> bool {
260 self.id == 0 && self.offset == 0
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 const BLOCK_SIZE: usize = 4096;
269
270 #[fuchsia::test]
271 fn test_encode_decode_blob() {
272 let mut buf = [0u8; size_of::<BlobMessage>()];
273 let m = BlobMessage { id: [88u8; 32].into(), offset: 77 };
274 m.encode_to(MutPtrByteSlice::from(&mut buf[..]));
275 let m2 = BlobMessage::decode_from(PtrByteSlice::from(&buf[..]));
276 assert!(!m2.is_zeroes());
277 assert_eq!(m, m2);
278 }
279
280 #[fuchsia::test]
281 fn test_encode_decode_file() {
282 let mut buf = [0u8; size_of::<FileMessage>()];
283 let m = FileMessage { id: 88, offset: 77 };
284 m.encode_to(MutPtrByteSlice::from(&mut buf[..]));
285 let m2 = FileMessage::decode_from(PtrByteSlice::from(&buf[..]));
286 assert!(!m2.is_zeroes());
287 assert_eq!(m, m2);
288 }
289
290 #[fuchsia::test]
291 fn test_file_message_vmo_round_trip() {
292 let empty_vmo = FileMessage::encode_to_vmo(&[] as &[FileMessage], BLOCK_SIZE)
293 .expect("encode_to_vmo failed");
294 assert_eq!(
295 FileMessage::decode_from_vmo(empty_vmo, BLOCK_SIZE)
296 .expect("decode_from_vmo failed")
297 .collect::<Vec<_>>(),
298 Vec::<FileMessage>::new()
299 );
300
301 let exact_block: Vec<FileMessage> =
303 (0..256).map(|i| FileMessage { id: (i % 7) + 1, offset: i * 4096 }).collect();
304 let exact_vmo =
305 FileMessage::encode_to_vmo(&exact_block, BLOCK_SIZE).expect("encode_to_vmo failed");
306 assert_eq!(
307 FileMessage::decode_from_vmo(exact_vmo, BLOCK_SIZE)
308 .expect("decode_from_vmo failed")
309 .collect::<Vec<_>>(),
310 exact_block
311 );
312
313 let messages: Vec<FileMessage> =
315 (0..260).map(|i| FileMessage { id: (i % 7) + 1, offset: i * 4096 }).collect();
316 let vmo = FileMessage::encode_to_vmo(&messages, BLOCK_SIZE).expect("encode_to_vmo failed");
317 let decoded: Vec<_> = FileMessage::decode_from_vmo(vmo, BLOCK_SIZE)
318 .expect("decode_from_vmo failed")
319 .collect();
320 assert_eq!(decoded, messages);
321 }
322
323 #[fuchsia::test]
324 fn test_blob_message_vmo_round_trip_across_blocks() {
325 let messages: Vec<BlobMessage> = (0..105)
330 .map(|i| BlobMessage { id: [(i as u8) + 1; 32].into(), offset: (i as u64) * 131072 })
331 .collect();
332 let vmo = BlobMessage::encode_to_vmo(&messages, BLOCK_SIZE).expect("encode_to_vmo failed");
333 assert_eq!(vmo.get_size().expect("get_size failed"), (2 * BLOCK_SIZE) as u64);
334
335 let decoded: Vec<_> = BlobMessage::decode_from_vmo(vmo, BLOCK_SIZE)
336 .expect("decode_from_vmo failed")
337 .collect();
338 assert_eq!(decoded, messages);
339 }
340}