1use super::*;
2use std::path::PathBuf;
3
4pub const DEFAULT_READ_MAX_BYTES: usize = 16 * 1024 * 1024;
6
7pub const DEFAULT_READ_STREAM_CHUNK_BYTES: usize = 1024 * 1024;
9
10pub const DEFAULT_READ_ALLV_MAX_TOTAL_BYTES: usize = DEFAULT_READ_MAX_BYTES;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15#[must_use = "configuration and requests have no effect until used"]
16pub struct StreamOptions {
17 chunk_size: std::num::NonZeroUsize,
18}
19
20impl StreamOptions {
21 pub const fn new() -> Self {
22 Self {
23 chunk_size: std::num::NonZeroUsize::new(DEFAULT_READ_STREAM_CHUNK_BYTES).unwrap(),
24 }
25 }
26
27 pub const fn with_chunk_size(mut self, bytes: std::num::NonZeroUsize) -> Self {
37 self.chunk_size = bytes;
38 self
39 }
40
41 pub const fn chunk_size(self) -> usize {
42 self.chunk_size.get()
43 }
44}
45
46impl Default for StreamOptions {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52pub const DEFAULT_DIRECTORY_MAX_ENTRIES: usize = 100_000;
54
55pub const DEFAULT_DIRECTORY_MAX_PATH_BYTES: usize = 16 * 1024 * 1024;
57
58pub const DEFAULT_WALK_MAX_DEPTH: usize = 128;
60
61#[repr(transparent)]
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub struct DepthLimit(std::num::NonZeroU16);
68
69impl Default for DepthLimit {
70 fn default() -> Self {
71 Self::new(DEFAULT_WALK_MAX_DEPTH).unwrap()
72 }
73}
74
75impl DepthLimit {
76 pub const fn new(depth: usize) -> Option<Self> {
78 if depth <= u16::MAX as usize - 2 {
79 Some(Self(std::num::NonZeroU16::new((depth + 1) as u16).unwrap()))
80 } else {
81 None
82 }
83 }
84 pub const fn unlimited() -> Self {
86 Self(std::num::NonZeroU16::MAX)
87 }
88 pub const fn get(self) -> usize {
90 if self.is_unlimited() {
91 usize::MAX
92 } else {
93 self.0.get() as usize - 1
94 }
95 }
96 pub const fn is_unlimited(self) -> bool {
97 self.0.get() == u16::MAX
98 }
99}
100
101pub const DEFAULT_READV_MAX_TOTAL_BYTES: usize = DEFAULT_READ_MAX_BYTES;
103
104#[derive(Debug, Clone, PartialEq, Eq)]
106pub struct FsReadResult {
107 pub offset: u64,
108 pub data: Vec<u8>,
109 pub eof: bool,
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114pub struct ReadIntoResult {
115 pub offset: u64,
116 pub bytes_read: usize,
117 pub eof: bool,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub struct WriteResult {
125 pub offset: u64,
126 pub bytes_written: usize,
127 pub stable: bool,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum StreamCompletion {
133 Complete,
134 Stopped { next_offset: u64 },
135}
136
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
139pub enum TraversalCompletion {
140 Complete,
141 Stopped,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147#[must_use = "configuration and requests have no effect until used"]
148pub struct ResourceLimits {
149 max_read_bytes: usize,
153 stream_chunk_bytes: std::num::NonZeroUsize,
155 max_directory_entries: usize,
157 max_directory_path_bytes: usize,
159 max_walk_depth: DepthLimit,
161}
162
163impl Default for ResourceLimits {
164 fn default() -> Self {
165 Self {
166 max_read_bytes: DEFAULT_READ_MAX_BYTES,
167 stream_chunk_bytes: std::num::NonZeroUsize::new(DEFAULT_READ_STREAM_CHUNK_BYTES)
168 .unwrap(),
169 max_directory_entries: crate::api::DEFAULT_DIRECTORY_MAX_ENTRIES,
170 max_directory_path_bytes: crate::api::DEFAULT_DIRECTORY_MAX_PATH_BYTES,
171 max_walk_depth: DepthLimit::new(crate::api::DEFAULT_WALK_MAX_DEPTH).unwrap(),
172 }
173 }
174}
175
176impl ResourceLimits {
177 pub fn new() -> Self {
178 Self::default()
179 }
180 pub const fn with_max_read_bytes(mut self, value: usize) -> Self {
182 self.max_read_bytes = value;
183 self
184 }
185 pub const fn max_read_bytes(self) -> usize {
187 self.max_read_bytes
188 }
189 pub const fn with_stream_chunk_size(mut self, value: std::num::NonZeroUsize) -> Self {
191 self.stream_chunk_bytes = value;
192 self
193 }
194 pub const fn stream_chunk_size(self) -> usize {
196 self.stream_chunk_bytes.get()
197 }
198 pub const fn with_max_directory_entries(mut self, value: usize) -> Self {
200 self.max_directory_entries = value;
201 self
202 }
203 pub const fn max_directory_entries(self) -> usize {
205 self.max_directory_entries
206 }
207 pub const fn with_max_directory_path_bytes(mut self, value: usize) -> Self {
209 self.max_directory_path_bytes = value;
210 self
211 }
212 pub const fn max_directory_path_bytes(self) -> usize {
214 self.max_directory_path_bytes
215 }
216 pub const fn with_max_walk_depth(mut self, value: DepthLimit) -> Self {
218 self.max_walk_depth = value;
219 self
220 }
221 pub const fn max_walk_depth(self) -> DepthLimit {
223 self.max_walk_depth
224 }
225
226 pub fn directory_options(self) -> ListDirOptions {
227 ListDirOptions::new()
228 .with_max_entries(self.max_directory_entries)
229 .with_max_path_bytes(self.max_directory_path_bytes)
230 }
231
232 pub fn walk_options(self) -> crate::api::ListDirOptions {
233 crate::api::ListDirOptions::new()
234 .with_recursive(true)
235 .with_max_entries(self.max_directory_entries)
236 .with_max_path_bytes(self.max_directory_path_bytes)
237 .with_max_depth(self.max_walk_depth)
238 }
239}
240
241#[derive(Clone, Debug, PartialEq, Eq)]
243pub struct DirectoryListing {
244 pub path: PathBuf,
245 pub entries: Vec<DirEntry>,
246}
247
248#[cfg(test)]
249mod depth_limit_tests {
250 use super::*;
251 #[test]
252 fn checked_depths_preserve_finite_limits_and_inheritance() {
253 for depth in [0, 1, 128, 200, 201, 500, 65533] {
254 let limit = DepthLimit::new(depth).unwrap();
255 assert!(!limit.is_unlimited());
256 let limits = ResourceLimits::new().with_max_walk_depth(limit);
257 assert_eq!(
258 ListDirOptions::new()
259 .resolve(limits)
260 .max_depth_or(Default::default()),
261 limit
262 );
263 }
264 for invalid in [65534, 65535, usize::MAX] {
265 assert!(DepthLimit::new(invalid).is_none());
266 }
267 assert!(
268 ListDirOptions::new()
269 .resolve(ResourceLimits::new().with_max_walk_depth(DepthLimit::unlimited()))
270 .max_depth_or(Default::default())
271 .is_unlimited()
272 );
273 }
274}