Skip to main content

mapping/
testing.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 crate::{DeliveryHandler, PageRequest};
6use delivery_blob::compression::{ChunkedArchiveError, DataBuffer};
7use fuchsia_sync::{Condvar, Mutex};
8use std::ops::{Deref, DerefMut, Range};
9use std::sync::Arc;
10use storage_ptr_slice::MutPtrByteSlice;
11use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
12
13#[derive(Clone, Copy, IntoBytes, FromBytes, KnownLayout, Immutable)]
14#[repr(C, align(64))]
15struct Block([u8; 64]);
16
17impl Default for Block {
18    fn default() -> Self {
19        Self([0u8; 64])
20    }
21}
22
23/// A byte buffer aligned to 64 bytes, ensuring compatibility with cryptographic and DMA operations.
24#[derive(Clone, Default)]
25pub struct AlignedBuffer {
26    blocks: Vec<Block>,
27    len: usize,
28}
29
30impl AlignedBuffer {
31    pub fn new(len: usize) -> Self {
32        let block_count = len.div_ceil(64);
33        Self { blocks: vec![Block::default(); block_count], len }
34    }
35
36    pub fn len(&self) -> usize {
37        self.len
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.len == 0
42    }
43
44    pub fn resize(&mut self, new_len: usize, val: u8) {
45        let block_count = new_len.div_ceil(64);
46        if block_count > self.blocks.len() {
47            self.blocks.resize(block_count, Block::default());
48        } else {
49            self.blocks.truncate(block_count);
50        }
51        if new_len > self.len {
52            self.blocks.as_mut_bytes()[self.len..new_len].fill(val);
53        }
54        self.len = new_len;
55    }
56
57    pub fn as_slice(&self) -> &[u8] {
58        &self.blocks.as_bytes()[..self.len]
59    }
60
61    pub fn as_mut_slice(&mut self) -> &mut [u8] {
62        &mut self.blocks.as_mut_bytes()[..self.len]
63    }
64
65    pub fn to_vec(&self) -> Vec<u8> {
66        self.as_slice().to_vec()
67    }
68}
69
70impl Deref for AlignedBuffer {
71    type Target = [u8];
72    fn deref(&self) -> &Self::Target {
73        self.as_slice()
74    }
75}
76
77impl DerefMut for AlignedBuffer {
78    fn deref_mut(&mut self) -> &mut Self::Target {
79        self.as_mut_slice()
80    }
81}
82
83#[derive(Default)]
84pub struct TestVecBufferInner {
85    pub commits: Vec<(u64, usize)>,
86    pub output: Vec<u8>,
87}
88
89#[derive(Clone)]
90pub struct TestVecBufferReceiver {
91    pub inner: Arc<Mutex<TestVecBufferInner>>,
92    on_commit: Arc<Condvar>,
93}
94
95impl Default for TestVecBufferReceiver {
96    fn default() -> Self {
97        Self {
98            inner: Arc::new(Mutex::new(TestVecBufferInner::default())),
99            on_commit: Arc::new(Condvar::new()),
100        }
101    }
102}
103
104impl TestVecBufferReceiver {
105    pub fn commits(&self) -> Vec<(u64, usize)> {
106        self.inner.lock().commits.clone()
107    }
108
109    pub fn wait_for_commits(&self, count: usize) -> Vec<(u64, usize)> {
110        let mut inner = self.inner.lock();
111        while inner.commits.len() < count {
112            self.on_commit.wait(&mut inner);
113        }
114        inner.commits.clone()
115    }
116
117    pub fn output(&self) -> Vec<u8> {
118        self.inner.lock().output.clone()
119    }
120}
121
122pub struct TestVecBuffer {
123    pub data: AlignedBuffer,
124    pub range: Range<u64>,
125    pub committed_len: usize,
126    pub offset: u64,
127    pub receiver: TestVecBufferReceiver,
128}
129
130impl TestVecBuffer {
131    pub fn new(size: usize) -> (Self, TestVecBufferReceiver) {
132        Self::new_with_offset(size, 0)
133    }
134
135    pub fn new_with_offset(size: usize, offset: u64) -> (Self, TestVecBufferReceiver) {
136        let receiver = TestVecBufferReceiver::default();
137        let range = offset..offset + size as u64;
138        let buf = Self {
139            data: AlignedBuffer::new(size),
140            range,
141            committed_len: 0,
142            offset,
143            receiver: receiver.clone(),
144        };
145        (buf, receiver)
146    }
147
148    pub fn new_with_range(range: Range<u64>) -> (Self, TestVecBufferReceiver) {
149        let size = (range.end - range.start) as usize;
150        let receiver = TestVecBufferReceiver::default();
151        let buf = Self {
152            data: AlignedBuffer::new(size),
153            offset: range.start,
154            range,
155            committed_len: 0,
156            receiver: receiver.clone(),
157        };
158        (buf, receiver)
159    }
160
161    pub fn new_unprepared() -> (Self, TestVecBufferReceiver) {
162        Self::new_unprepared_with_range(0..0)
163    }
164
165    pub fn new_unprepared_with_range(range: Range<u64>) -> (Self, TestVecBufferReceiver) {
166        let receiver = TestVecBufferReceiver::default();
167        let buf = Self {
168            data: AlignedBuffer::default(),
169            range,
170            committed_len: 0,
171            offset: 0,
172            receiver: receiver.clone(),
173        };
174        (buf, receiver)
175    }
176}
177
178impl DataBuffer for TestVecBuffer {
179    fn range(&self) -> Range<u64> {
180        self.range.clone()
181    }
182
183    fn mut_ptr_slice(&mut self) -> MutPtrByteSlice<'_> {
184        let remaining = &mut self.data[self.committed_len..];
185        MutPtrByteSlice::from(remaining)
186    }
187
188    fn commit(&mut self, size: usize) -> Result<(), ChunkedArchiveError> {
189        let mut inner = self.receiver.inner.lock();
190        inner.commits.push((self.offset, size));
191        inner.output = self.data.to_vec();
192        self.offset += size as u64;
193        self.committed_len += size;
194        self.receiver.on_commit.notify_all();
195        Ok(())
196    }
197}
198
199impl PageRequest for TestVecBuffer {
200    fn prepare(
201        &mut self,
202        read_range: Range<u64>,
203        _read_alignment: usize,
204    ) -> Result<(), ChunkedArchiveError> {
205        let size = (read_range.end - read_range.start) as usize;
206        if self.data.len() < size {
207            self.data.resize(size, 0);
208        }
209        self.range = read_range.clone();
210        self.offset = read_range.start;
211        Ok(())
212    }
213}
214
215/// A [`DeliveryHandler`] test adapter that implements `get_page_request` via a closure.
216///
217/// The closure takes `(key: u64, range: Range<u64>)` where:
218/// - `key`: The pager port key identifying the mapped file.
219/// - `range`: The byte range requested to be paged in.
220///
221/// And returns `R: PageRequest` (any type implementing [`PageRequest`]) to receive the paged data.
222///
223/// [`DeliveryHandler::register_blob`] defaults to a no-op `Ok(())`.
224pub struct TestDeliveryHandler<F>(pub F);
225
226impl<F, R: PageRequest> DeliveryHandler for TestDeliveryHandler<F>
227where
228    F: Fn(u64, Range<u64>) -> R + Send + Sync + 'static,
229{
230    type Request = R;
231
232    fn get_page_request(self: &Arc<Self>, key: u64, range: Range<u64>) -> Self::Request {
233        (self.0)(key, range)
234    }
235}