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