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 DepthLimit {
70 pub const fn new(depth: usize) -> Option<Self> {
72 if depth <= u16::MAX as usize - 2 {
73 Some(Self(std::num::NonZeroU16::new((depth + 1) as u16).unwrap()))
74 } else {
75 None
76 }
77 }
78 pub const fn unlimited() -> Self {
80 Self(std::num::NonZeroU16::MAX)
81 }
82 pub const fn get(self) -> usize {
84 if self.is_unlimited() {
85 usize::MAX
86 } else {
87 self.0.get() as usize - 1
88 }
89 }
90 pub const fn is_unlimited(self) -> bool {
91 self.0.get() == u16::MAX
92 }
93}
94
95pub const DEFAULT_READV_MAX_TOTAL_BYTES: usize = DEFAULT_READ_MAX_BYTES;
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct FsReadResult {
101 pub offset: u64,
102 pub data: Vec<u8>,
103 pub eof: bool,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108pub struct ReadIntoResult {
109 pub offset: u64,
110 pub bytes_read: usize,
111 pub eof: bool,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
118pub struct WriteResult {
119 pub offset: u64,
120 pub bytes_written: usize,
121 pub stable: bool,
122}
123
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
126pub enum StreamCompletion {
127 Complete,
128 Stopped { next_offset: u64 },
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133pub enum TraversalCompletion {
134 Complete,
135 Stopped,
136}
137
138#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141#[must_use = "configuration and requests have no effect until used"]
142pub struct ResourceLimits {
143 max_read_bytes: usize,
147 stream_chunk_bytes: std::num::NonZeroUsize,
149 max_directory_entries: usize,
151 max_directory_path_bytes: usize,
153 max_walk_depth: DepthLimit,
155}
156
157impl Default for ResourceLimits {
158 fn default() -> Self {
159 Self {
160 max_read_bytes: DEFAULT_READ_MAX_BYTES,
161 stream_chunk_bytes: std::num::NonZeroUsize::new(DEFAULT_READ_STREAM_CHUNK_BYTES)
162 .unwrap(),
163 max_directory_entries: crate::api::DEFAULT_DIRECTORY_MAX_ENTRIES,
164 max_directory_path_bytes: crate::api::DEFAULT_DIRECTORY_MAX_PATH_BYTES,
165 max_walk_depth: DepthLimit::new(crate::api::DEFAULT_WALK_MAX_DEPTH).unwrap(),
166 }
167 }
168}
169
170impl ResourceLimits {
171 pub fn new() -> Self {
172 Self::default()
173 }
174 pub const fn with_max_read_bytes(mut self, value: usize) -> Self {
176 self.max_read_bytes = value;
177 self
178 }
179 pub const fn max_read_bytes(self) -> usize {
181 self.max_read_bytes
182 }
183 pub const fn with_stream_chunk_size(mut self, value: std::num::NonZeroUsize) -> Self {
185 self.stream_chunk_bytes = value;
186 self
187 }
188 pub const fn stream_chunk_size(self) -> usize {
190 self.stream_chunk_bytes.get()
191 }
192 pub const fn with_max_directory_entries(mut self, value: usize) -> Self {
194 self.max_directory_entries = value;
195 self
196 }
197 pub const fn max_directory_entries(self) -> usize {
199 self.max_directory_entries
200 }
201 pub const fn with_max_directory_path_bytes(mut self, value: usize) -> Self {
203 self.max_directory_path_bytes = value;
204 self
205 }
206 pub const fn max_directory_path_bytes(self) -> usize {
208 self.max_directory_path_bytes
209 }
210 pub const fn with_max_walk_depth(mut self, value: DepthLimit) -> Self {
212 self.max_walk_depth = value;
213 self
214 }
215 pub const fn max_walk_depth(self) -> DepthLimit {
217 self.max_walk_depth
218 }
219
220 pub fn directory_options(self) -> ListDirOptions {
221 ListDirOptions::new()
222 .with_max_entries(self.max_directory_entries)
223 .with_max_path_bytes(self.max_directory_path_bytes)
224 }
225
226 pub fn walk_options(self) -> crate::api::ListDirOptions {
227 crate::api::ListDirOptions::new()
228 .with_recursive(true)
229 .with_max_entries(self.max_directory_entries)
230 .with_max_path_bytes(self.max_directory_path_bytes)
231 .with_max_depth(self.max_walk_depth)
232 }
233}
234
235#[derive(Clone, Debug, PartialEq, Eq)]
237pub struct DirectoryListing {
238 pub path: PathBuf,
239 pub entries: Vec<DirEntry>,
240}
241
242#[cfg(test)]
243mod depth_limit_tests {
244 use super::*;
245 #[test]
246 fn checked_depths_preserve_finite_limits_and_inheritance() {
247 for depth in [0, 1, 128, 200, 201, 500, 65533] {
248 let limit = DepthLimit::new(depth).unwrap();
249 assert!(!limit.is_unlimited());
250 let limits = ResourceLimits::new().with_max_walk_depth(limit);
251 assert_eq!(ListDirOptions::new().resolve(limits).max_depth(), limit);
252 }
253 for invalid in [65534, 65535, usize::MAX] {
254 assert!(DepthLimit::new(invalid).is_none());
255 }
256 assert!(
257 ListDirOptions::new()
258 .resolve(ResourceLimits::new().with_max_walk_depth(DepthLimit::unlimited()))
259 .max_depth()
260 .is_unlimited()
261 );
262 }
263}