1use super::support::FsReadResult as OwnedReadResult;
2use super::*;
3use std::path::Path;
4
5pub struct ReadOp<'a, H: crate::api::FileHandle + 'a> {
8 source: OpSource<'a, H>,
9}
10enum OpSource<'a, H: crate::api::FileHandle + 'a> {
11 Whole(&'a Path),
12 Range(H::ReadRequest<'a>, usize),
13 Into(H::ReadIntoRequest<'a>, usize),
14}
15impl<H: crate::api::FileHandle> std::fmt::Debug for ReadOp<'_, H> {
16 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 match &self.source {
18 OpSource::Whole(path) => formatter.debug_tuple("ReadOp::whole").field(path).finish(),
19 OpSource::Range(_, length) => formatter
20 .debug_struct("ReadOp::range")
21 .field("length", length)
22 .finish_non_exhaustive(),
23 OpSource::Into(_, length) => formatter
24 .debug_struct("ReadOp::into")
25 .field("length", length)
26 .finish_non_exhaustive(),
27 }
28 }
29}
30impl<'a, H: crate::api::FileHandle + 'a> From<&'a str> for ReadOp<'a, H> {
31 fn from(path: &'a str) -> Self {
32 Self::whole(path)
33 }
34}
35impl<'a, H: crate::api::FileHandle + 'a> From<&'a Path> for ReadOp<'a, H> {
36 fn from(path: &'a Path) -> Self {
37 Self::whole(path)
38 }
39}
40impl<'a, H: crate::api::FileHandle + 'a> ReadOp<'a, H> {
41 pub fn whole<P: AsRef<Path> + ?Sized>(path: &'a P) -> Self {
43 Self {
44 source: OpSource::Whole(path.as_ref()),
45 }
46 }
47 pub fn range(file: &'a H, offset: u64, length: usize) -> Self {
49 Self {
50 source: OpSource::Range(file.read_request_at(offset, length), length),
51 }
52 }
53 pub fn into(file: &'a H, offset: u64, buffer: &'a mut [u8]) -> Self {
56 let length = buffer.len();
57 Self {
58 source: OpSource::Into(file.read_request_at_into(offset, buffer), length),
59 }
60 }
61 pub fn whole_file_path(&self) -> Option<&'a Path> {
63 match &self.source {
64 OpSource::Whole(path) => Some(*path),
65 _ => None,
66 }
67 }
68 pub fn range_ref(&self) -> Option<&H::ReadRequest<'a>> {
70 match &self.source {
71 OpSource::Range(request, _) => Some(request),
72 _ => None,
73 }
74 }
75 pub fn buffer_request_mut(&mut self) -> Option<&mut H::ReadIntoRequest<'a>> {
77 match &mut self.source {
78 OpSource::Into(request, _) => Some(request),
79 _ => None,
80 }
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
86pub struct ReadResult {
87 pub(crate) offset: u64,
89 pub(crate) read: usize,
91 pub(crate) eof: bool,
93 pub(crate) data: Option<Vec<u8>>,
95}
96
97impl ReadResult {
98 pub fn owned(offset: u64, data: Vec<u8>, eof: bool) -> Self {
100 Self {
101 offset,
102 read: data.len(),
103 eof,
104 data: Some(data),
105 }
106 }
107 pub const fn buffered(offset: u64, read: usize, eof: bool) -> Self {
110 Self {
111 offset,
112 read,
113 eof,
114 data: None,
115 }
116 }
117 pub const fn offset(&self) -> u64 {
118 self.offset
119 }
120 pub const fn read(&self) -> usize {
121 self.read
122 }
123 pub const fn eof(&self) -> bool {
124 self.eof
125 }
126 pub fn data(&self) -> Option<&[u8]> {
127 self.data.as_deref()
128 }
129 pub fn into_data(self) -> Option<Vec<u8>> {
130 self.data
131 }
132 pub const fn is_buffered(&self) -> bool {
133 self.data.is_none()
134 }
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141pub struct ReadOptions {
142 max_total_bytes: Option<std::num::NonZeroUsize>,
143}
144impl ReadOptions {
145 pub const fn new() -> Self {
147 Self {
148 max_total_bytes: None,
149 }
150 }
151 pub const fn max_total_bytes(mut self, bytes: Option<std::num::NonZeroUsize>) -> Self {
155 self.max_total_bytes = bytes;
156 self
157 }
158 pub const fn total_byte_limit(self) -> Option<std::num::NonZeroUsize> {
160 self.max_total_bytes
161 }
162 pub fn limit_or(self, default: usize) -> usize {
164 self.max_total_bytes
165 .map_or(default, std::num::NonZeroUsize::get)
166 }
167}
168
169impl Default for ReadOptions {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174pub fn consume_ops<'a, H: crate::api::FileHandle + 'a>(
175 ops: impl IntoIterator<Item = ReadOp<'a, H>>,
176 budget: usize,
177 owned: impl FnOnce(&[ReadRequest<'a, H::ReadRequest<'a>>], usize) -> Result<Vec<OwnedReadResult>>,
178 buffers: impl FnOnce(
179 &mut [H::ReadIntoRequest<'a>],
180 usize,
181 ) -> Result<Vec<crate::api::ReadIntoResult>>,
182) -> Result<Vec<ReadResult>> {
183 let mut owned_ops = Vec::new();
184 let mut owned_indices = Vec::new();
185 let mut buffer_ops = Vec::new();
186 let mut buffer_indices = Vec::new();
187 let mut requested = 0usize;
188 let mut count = 0usize;
189 for (index, op) in ops.into_iter().enumerate() {
190 count += 1;
191 let length = match &op.source {
192 OpSource::Whole(_) => 0,
193 OpSource::Range(_, length) | OpSource::Into(_, length) => *length,
194 };
195 requested = requested
196 .checked_add(length)
197 .filter(|bytes| *bytes <= budget)
198 .ok_or_else(|| Error::client(index, libc::EFBIG as u32))?;
199 match op.source {
200 OpSource::Whole(path) => {
201 owned_ops.push(ReadRequest::whole_file(path));
202 owned_indices.push(index);
203 }
204 OpSource::Range(request, _) => {
205 owned_ops.push(ReadRequest::range(request));
206 owned_indices.push(index);
207 }
208 OpSource::Into(request, _) => {
209 buffer_ops.push(request);
210 buffer_indices.push(index);
211 }
212 }
213 }
214 let remap = |error: Error, indices: &[usize]| match error.index() {
215 Some(index) if index < indices.len() => error.with_index(indices[index]),
216 Some(_) => Error::transport(None, "invalid readv backend error index"),
217 None => error,
218 };
219 let buffered = if buffer_ops.is_empty() {
220 Vec::new()
221 } else {
222 buffers(&mut buffer_ops, budget).map_err(|e| remap(e, &buffer_indices))?
223 };
224 drop(buffer_ops);
225 if buffered.len() != buffer_indices.len() {
226 return Err(Error::transport(None, "invalid readv buffer result count"));
227 }
228 let mut remaining = budget;
229 for result in &buffered {
230 remaining = remaining
231 .checked_sub(result.read)
232 .ok_or_else(|| Error::transport(None, "readv backend exceeded byte budget"))?;
233 }
234 let allocated = if owned_ops.is_empty() {
235 Vec::new()
236 } else {
237 owned(&owned_ops, remaining).map_err(|e| remap(e, &owned_indices))?
238 };
239 if allocated.len() != owned_indices.len() {
240 return Err(Error::transport(None, "invalid readv owned result count"));
241 }
242 let mut output: Vec<Option<ReadResult>> = (0..count).map(|_| None).collect();
243 for (index, result) in buffer_indices.into_iter().zip(buffered) {
244 output[index] = Some(ReadResult {
245 offset: result.offset,
246 read: result.read,
247 eof: result.eof,
248 data: None,
249 });
250 }
251 for (index, result) in owned_indices.into_iter().zip(allocated) {
252 remaining = remaining
253 .checked_sub(result.data.len())
254 .ok_or_else(|| Error::transport(None, "readv backend exceeded byte budget"))?;
255 output[index] = Some(ReadResult {
256 offset: result.offset,
257 read: result.data.len(),
258 eof: result.eof,
259 data: Some(result.data),
260 });
261 }
262 output
263 .into_iter()
264 .map(|result| result.ok_or_else(|| Error::transport(None, "readv omitted a result")))
265 .collect()
266}
267pub struct ReadRequest<'a, R> {
269 pub(crate) source: Source<'a, R>,
270}
271pub(crate) enum Source<'a, R> {
272 Whole(&'a Path),
273 Range(R),
274}
275impl<'a, R> ReadRequest<'a, R> {
276 pub fn whole_file<P: AsRef<Path> + ?Sized>(path: &'a P) -> Self {
278 Self {
279 source: Source::Whole(path.as_ref()),
280 }
281 }
282 pub fn range(request: R) -> Self {
284 Self {
285 source: Source::Range(request),
286 }
287 }
288 pub fn range_ref(&self) -> Option<&R> {
292 match &self.source {
293 Source::Range(request) => Some(request),
294 _ => None,
295 }
296 }
297}
298impl<'a, R> From<R> for ReadRequest<'a, R> {
299 fn from(request: R) -> Self {
300 Self::range(request)
301 }
302}
303pub fn read_batch<R>(
304 requests: &[ReadRequest<'_, R>],
305 budget: usize,
306 read_ranges: impl FnOnce(&[&R], usize) -> Result<Vec<OwnedReadResult>>,
307 read_paths: impl FnOnce(&[&Path], usize) -> Result<Vec<Vec<u8>>>,
308) -> Result<Vec<OwnedReadResult>> {
309 if requests.is_empty() {
310 return Ok(Vec::new());
311 }
312 let (mut paths, mut path_indices, mut ranges, mut range_indices) =
313 (Vec::new(), Vec::new(), Vec::new(), Vec::new());
314 for (i, r) in requests.iter().enumerate() {
315 match &r.source {
316 Source::Whole(path) => {
317 paths.push(*path);
318 path_indices.push(i);
319 }
320 Source::Range(range) => {
321 ranges.push(range);
322 range_indices.push(i);
323 }
324 }
325 }
326 let remap = |e: Error, indices: &[usize]| match e.index() {
327 Some(i) if i < indices.len() => e.with_index(indices[i]),
328 Some(_) => Error::transport(None, "invalid readv backend error index"),
329 None => e,
330 };
331 let range_results = if ranges.is_empty() {
333 Vec::new()
334 } else {
335 read_ranges(&ranges, budget).map_err(|e| remap(e, &range_indices))?
336 };
337 if range_results.len() != ranges.len() {
338 return Err(Error::transport(None, "invalid readv range result count"));
339 }
340 let mut remaining = budget;
341 for r in &range_results {
342 remaining = remaining
343 .checked_sub(r.data.len())
344 .ok_or_else(|| Error::transport(None, "readv backend exceeded byte budget"))?;
345 }
346 let path_results = if paths.is_empty() {
347 Vec::new()
348 } else {
349 read_paths(&paths, remaining).map_err(|e| remap(e, &path_indices))?
350 };
351 if path_results.len() != paths.len() {
352 return Err(Error::transport(None, "invalid readv path result count"));
353 }
354 for r in &path_results {
355 remaining = remaining
356 .checked_sub(r.len())
357 .ok_or_else(|| Error::transport(None, "readv backend exceeded byte budget"))?;
358 }
359 let mut output: Vec<Option<OwnedReadResult>> = (0..requests.len()).map(|_| None).collect();
360 for (i, r) in range_indices.into_iter().zip(range_results) {
361 output[i] = Some(r);
362 }
363 for (i, data) in path_indices.into_iter().zip(path_results) {
364 output[i] = Some(OwnedReadResult {
365 offset: 0,
366 data,
367 eof: true,
368 });
369 }
370 output
371 .into_iter()
372 .map(|r| r.ok_or_else(|| Error::transport(None, "readv omitted a result")))
373 .collect()
374}
375
376#[cfg(test)]
377mod option_layout_tests {
378 use super::ReadOptions;
379 use std::num::NonZeroUsize;
380 #[test]
381 fn read_options_are_one_word_and_keep_inheritance_distinct() {
382 assert_eq!(
383 std::mem::size_of::<ReadOptions>(),
384 std::mem::size_of::<usize>()
385 );
386 let mut options = ReadOptions::new();
387 assert_eq!(options.limit_or(7), 7);
388 for bytes in [1, 42, usize::MAX] {
389 let limit = NonZeroUsize::new(bytes);
390 options = options.max_total_bytes(limit);
391 assert_eq!(options.total_byte_limit(), limit);
392 assert_eq!(options.limit_or(7), bytes);
393 }
394 options = options.max_total_bytes(None);
395 assert_eq!(options.total_byte_limit(), None);
396 assert_eq!(options.limit_or(7), 7);
397 assert_eq!(
398 options.limit_or(0),
399 0,
400 "an inherited zero policy must not become unlimited"
401 );
402 }
403}