random_access_memory/
lib.rs1#![forbid(unsafe_code, missing_docs)]
2#![cfg_attr(test, deny(warnings))]
3#![doc(test(attr(deny(warnings))))]
4pub use intmap::IntMap;
67
68use random_access_storage::{RandomAccess, RandomAccessError};
69use std::cmp;
70
71#[derive(Debug)]
73pub struct RandomAccessMemory {
74 page_size: usize,
76
77 buffers: IntMap<Vec<u8>>,
79
80 length: u64,
82}
83
84impl Default for RandomAccessMemory {
85 fn default() -> Self {
87 RandomAccessMemory::new(1024 * 1024)
88 }
89}
90
91#[allow(clippy::needless_range_loop)]
92impl RandomAccessMemory {
93 pub fn new(page_size: usize) -> Self {
95 RandomAccessMemory::with_buffers(page_size, IntMap::new())
96 }
97
98 pub fn with_buffers(page_size: usize, buffers: IntMap<Vec<u8>>) -> Self {
100 RandomAccessMemory {
101 page_size,
102 buffers,
103 length: 0,
104 }
105 }
106
107 fn page_num_and_index(
111 &self,
112 offset: u64,
113 exclusive_end: bool,
114 ) -> (usize, usize) {
115 let page_num = (offset / (self.page_size as u64)) as usize;
116 let page_index = (offset % (self.page_size as u64)) as usize;
117 if page_index == 0 && exclusive_end {
118 (if page_num > 0 { page_num - 1 } else { 0 }, self.page_size)
119 } else {
120 (page_num, page_index)
121 }
122 }
123
124 fn zero(&mut self, offset: u64, length: u64) {
126 let (first_page_num, first_page_start) =
127 self.page_num_and_index(offset, false);
128 let (last_page_num, last_page_end) =
129 self.page_num_and_index(offset + length, true);
130
131 if first_page_start > 0
133 || (first_page_num == last_page_num && last_page_end > 0)
134 {
135 if let Some(page) = self.buffers.get_mut(first_page_num as u64) {
136 let begin_page_end = first_page_start
138 + cmp::min(length as usize, self.page_size - first_page_start);
139 for index in first_page_start..begin_page_end {
140 page[index] = 0;
141 }
142 }
143 }
144
145 if last_page_num > first_page_num + 1
147 || (first_page_start == 0 && last_page_num == first_page_num + 1)
148 {
149 let first_page_to_drop = if first_page_start == 0 {
150 first_page_num
151 } else {
152 first_page_num + 1
153 };
154
155 for index in first_page_to_drop..last_page_num {
156 self.buffers.remove(index as u64);
157 }
158 }
159
160 if last_page_num > first_page_num && last_page_end > 0 {
162 if let Some(page) = self.buffers.get_mut(last_page_num as u64) {
163 for index in 0..last_page_end {
165 page[index] = 0;
166 }
167 }
168 }
169 }
170}
171
172#[async_trait::async_trait]
173impl RandomAccess for RandomAccessMemory {
174 async fn write(
175 &mut self,
176 offset: u64,
177 data: &[u8],
178 ) -> Result<(), RandomAccessError> {
179 let new_len = offset + data.len() as u64;
180 if new_len > self.length {
181 self.length = new_len;
182 }
183
184 let mut page_num = (offset / self.page_size as u64) as usize;
185 let mut page_cursor =
186 (offset - (page_num * self.page_size) as u64) as usize;
187 let mut data_cursor = 0;
188
189 while data_cursor < data.len() {
192 let data_bound = data.len() - data_cursor;
193 let upper_bound = cmp::min(self.page_size, page_cursor + data_bound);
194 let range = page_cursor..upper_bound;
195 let range_len = (page_cursor..upper_bound).len();
196
197 if self.buffers.get(page_num as u64).is_none() {
200 let buf = vec![0; self.page_size];
201 self.buffers.insert(page_num as u64, buf);
202 }
203
204 let buffer = &mut self.buffers.get_mut(page_num as u64).unwrap();
208 for (index, buf_index) in range.enumerate() {
209 buffer[buf_index] = data[data_cursor + index];
210 }
211
212 page_num += 1;
213 page_cursor = 0;
214 data_cursor += range_len;
215 }
216
217 Ok(())
218 }
219
220 async fn sync_all(&mut self) -> Result<(), RandomAccessError> {
221 Ok(())
222 }
223
224 async fn read(
225 &mut self,
226 offset: u64,
227 length: u64,
228 ) -> Result<Vec<u8>, RandomAccessError> {
229 if (offset + length) > self.length {
230 return Err(RandomAccessError::OutOfBounds {
231 offset,
232 end: Some(offset + length),
233 length: self.length,
234 });
235 };
236
237 let mut page_num = (offset / self.page_size as u64) as usize;
238 let mut page_cursor =
239 (offset - (page_num * self.page_size) as u64) as usize;
240
241 let mut res_buf = vec![0; length as usize];
242 let mut res_cursor = 0; let res_capacity = length;
244
245 while res_cursor < res_capacity {
246 let res_bound = res_capacity - res_cursor;
247 let page_bound = self.page_size - page_cursor;
248 let relative_bound = cmp::min(res_bound, page_bound as u64);
249 let upper_bound = page_cursor + relative_bound as usize;
250 let range = page_cursor..upper_bound;
251
252 match self.buffers.get(page_num as u64) {
255 Some(buf) => {
256 for (index, buf_index) in range.enumerate() {
257 res_buf[res_cursor as usize + index] = buf[buf_index];
258 }
259 }
260 None => {
261 for (index, _) in range.enumerate() {
262 res_buf[res_cursor as usize + index] = 0;
263 }
264 }
265 }
266
267 res_cursor += relative_bound;
268 page_num += 1;
269 page_cursor = 0;
270 }
271
272 Ok(res_buf)
273 }
274
275 async fn del(
276 &mut self,
277 offset: u64,
278 length: u64,
279 ) -> Result<(), RandomAccessError> {
280 if offset > self.length {
281 return Err(RandomAccessError::OutOfBounds {
282 offset,
283 end: None,
284 length: self.length,
285 });
286 };
287
288 if length == 0 {
289 return Ok(());
291 }
292
293 if offset + length >= self.length {
295 return self.truncate(offset).await;
296 }
297
298 self.zero(offset, length);
300 Ok(())
301 }
302
303 #[allow(clippy::comparison_chain)]
304 async fn truncate(&mut self, length: u64) -> Result<(), RandomAccessError> {
305 let (current_last_page_num, _) = self.page_num_and_index(self.length, true);
306
307 if self.length < length {
308 let truncate_page_num = (length / self.page_size as u64) as usize;
309 for index in current_last_page_num + 1..truncate_page_num + 1 {
312 self.buffers.remove(index as u64);
313 }
314 } else if self.length > length {
315 let delete_length =
316 ((current_last_page_num + 1) * self.page_size) - length as usize;
317 self.zero(length, delete_length as u64);
320 }
321
322 self.length = length;
324
325 Ok(())
326 }
327
328 async fn len(&mut self) -> Result<u64, RandomAccessError> {
329 Ok(self.length)
330 }
331
332 async fn is_empty(&mut self) -> Result<bool, RandomAccessError> {
333 Ok(self.length == 0)
334 }
335}