Skip to content

Commit 3807bf2

Browse files
Add ReadDir(Files)Nth API call, which allows skipping files (useful for resuming iteration)
1 parent e052671 commit 3807bf2

File tree

3 files changed

+140
-8
lines changed

3 files changed

+140
-8
lines changed

src/api.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ generate_enums! {
4141
Hash: 12
4242
// TODO: add ReadDir{First,Next}, not loading data, if needed for efficiency
4343
ReadDirFilesFirst: 13
44+
ReadDirFilesNth: 31
4445
ReadDirFilesNext: 14
4546
ReadFile: 15
4647
Metadata: 26
@@ -63,6 +64,7 @@ generate_enums! {
6364

6465
// // CreateDir, <-- implied by WriteFile
6566
ReadDirFirst: 31 // <-- gets Option<FileType> to restrict to just dir/file DirEntries,
67+
ReadDirNth: 31
6668
ReadDirNext: 32 // <-- gets Option<FileType> to restrict to just dir/file DirEntries,
6769
// // returns simplified Metadata
6870
// // ReadDirFilesFirst: 23 // <-- returns contents
@@ -229,13 +231,24 @@ pub mod request {
229231
- dir: PathBuf
230232
- user_attribute: Option<UserAttribute>
231233

234+
ReadDirFilesNth:
235+
- location: Location
236+
- dir: PathBuf
237+
- start_at: usize
238+
- user_attribute: Option<UserAttribute>
239+
232240
ReadDirFilesNext:
233241

234242
ReadDirFirst:
235243
- location: Location
236244
- dir: PathBuf
237245
- not_before_filename: Option<PathBuf>
238246

247+
ReadDirNth:
248+
- location: Location
249+
- dir: PathBuf
250+
- start_at: usize
251+
239252
ReadDirNext:
240253

241254
ReadFile:
@@ -419,12 +432,18 @@ pub mod reply {
419432
ReadDirFilesFirst:
420433
- data: Option<Message>
421434

435+
ReadDirFilesNth:
436+
- data: Option<Message>
437+
422438
ReadDirFilesNext:
423439
- data: Option<Message>
424440

425441
ReadDirFirst:
426442
- entry: Option<DirEntry>
427443

444+
ReadDirNth:
445+
- data: Option<Message>
446+
428447
ReadDirNext:
429448
- entry: Option<DirEntry>
430449

src/service.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,20 @@ impl<P: Platform> ServiceResources<P> {
353353
};
354354
Ok(Reply::ReadDirFirst(reply::ReadDirFirst { entry: maybe_entry } ))
355355
}
356+
Request::ReadDirNth(request) => {
357+
let maybe_entry = match filestore.read_dir_nth(&request.dir, request.location, request.start_at)? {
358+
Some((entry, read_dir_state)) => {
359+
ctx.read_dir_state = Some(read_dir_state);
360+
Some(entry)
361+
}
362+
None => {
363+
ctx.read_dir_state = None;
364+
None
365+
366+
}
367+
};
368+
Ok(Reply::ReadDirFirst(reply::ReadDirFirst { entry: maybe_entry } ))
369+
}
356370

357371
Request::ReadDirNext(_request) => {
358372
// ensure next call has nothing to work with, unless we store state again
@@ -391,6 +405,20 @@ impl<P: Platform> ServiceResources<P> {
391405
Ok(Reply::ReadDirFilesFirst(reply::ReadDirFilesFirst { data: maybe_data } ))
392406
}
393407

408+
Request::ReadDirFilesNth(request) => {
409+
let maybe_data = match filestore.read_dir_files_nth(&request.dir, request.location, request.start_at, request.user_attribute.clone())? {
410+
Some((data, state)) => {
411+
ctx.read_dir_files_state = Some(state);
412+
data
413+
}
414+
None => {
415+
ctx.read_dir_files_state = None;
416+
None
417+
}
418+
};
419+
Ok(Reply::ReadDirFilesFirst(reply::ReadDirFilesFirst { data: maybe_data } ))
420+
}
421+
394422
Request::ReadDirFilesNext(_request) => {
395423
let read_dir_files_state = ctx.read_dir_files_state.take();
396424

src/store/filestore.rs

Lines changed: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,19 @@ pub trait Filestore {
8989
filename: PathBuf,
9090
) -> Result<Option<PathBuf>>;
9191

92+
/// Iterate over entries of a directory (both file and directory entries).
93+
///
94+
/// This function is modeled after `std::fs::read_dir`, within the limitations of our setup.
95+
///
96+
/// In case an entry was found, the returned option also contains state, so the expected
97+
/// call to `read_dir_next` can resume operation.
98+
fn read_dir_nth(
99+
&mut self,
100+
dir: &PathBuf,
101+
location: Location,
102+
start_at: usize,
103+
) -> Result<Option<(DirEntry, ReadDirState)>>;
104+
92105
/// Iterate over entries of a directory (both file and directory entries).
93106
///
94107
/// This function is modeled after `std::fs::read_dir`, within the limitations of our setup.
@@ -125,6 +138,21 @@ pub trait Filestore {
125138
user_attribute: Option<UserAttribute>,
126139
) -> Result<Option<(Option<Message>, ReadDirFilesState)>>;
127140

141+
/// Iterate over contents of files inside a directory.
142+
///
143+
/// This has no equivalent in `std::fs`, it is an optimization to avoid duplicate
144+
/// calls and a more complicated state machine (interspersing read_dir_first/next calls
145+
/// with some sort of "fetch data").
146+
///
147+
/// Additionally, files may optionally be filtered via attributes.
148+
fn read_dir_files_nth(
149+
&mut self,
150+
clients_dir: &PathBuf,
151+
location: Location,
152+
start_at: usize,
153+
user_attribute: Option<UserAttribute>,
154+
) -> Result<Option<(Option<Message>, ReadDirFilesState)>>;
155+
128156
/// Continuation of `read_dir_files_first`.
129157
fn read_dir_files_next(
130158
&mut self,
@@ -139,6 +167,7 @@ impl<S: Store> ClientFilestore<S> {
139167
clients_dir: &PathBuf,
140168
location: Location,
141169
not_before: Option<&PathBuf>,
170+
start_at: usize,
142171
fs: &'static Fs<F>,
143172
) -> Result<Option<(DirEntry, ReadDirState)>> {
144173
let dir = self.actual_path(clients_dir)?;
@@ -148,7 +177,7 @@ impl<S: Store> ClientFilestore<S> {
148177
// this is an iterator with Item = (usize, Result<DirEntry>)
149178
(&mut it)
150179
// skip over `.` and `..`
151-
.skip(2)
180+
.skip(2 + start_at)
152181
// todo: try ?-ing out of this (the API matches std::fs, where read/write errors
153182
// can occur during operation)
154183
//
@@ -232,6 +261,7 @@ impl<S: Store> ClientFilestore<S> {
232261
clients_dir: &PathBuf,
233262
location: Location,
234263
user_attribute: Option<UserAttribute>,
264+
start_at: usize,
235265
fs: &'static Fs<F>,
236266
) -> Result<Option<(Option<Message>, ReadDirFilesState)>> {
237267
let dir = self.actual_path(clients_dir)?;
@@ -247,8 +277,7 @@ impl<S: Store> ClientFilestore<S> {
247277
.map(Result::unwrap)
248278
// skip over directories (including `.` and `..`)
249279
.filter(|entry| entry.file_type().is_file())
250-
// take first entry that meets requirements
251-
.find(|entry| {
280+
.filter(|entry| {
252281
if let Some(user_attribute) = user_attribute.as_ref() {
253282
let mut path = dir.clone();
254283
path.push(entry.file_name());
@@ -257,14 +286,16 @@ impl<S: Store> ClientFilestore<S> {
257286
.unwrap();
258287

259288
if let Some(attribute) = attribute {
260-
user_attribute == attribute.data()
289+
user_attribute != attribute.data()
261290
} else {
262-
false
291+
true
263292
}
264293
} else {
265294
true
266295
}
267296
})
297+
// take nth entry that meets requirements
298+
.nth(start_at)
268299
// if there is an entry, construct the state that needs storing out of it,
269300
// and return the file's contents.
270301
// the client, and return both the entry and the state
@@ -403,13 +434,32 @@ impl<S: Store> Filestore for ClientFilestore<S> {
403434
) -> Result<Option<(DirEntry, ReadDirState)>> {
404435
match location {
405436
Location::Internal => {
406-
self.read_dir_first_impl(clients_dir, location, not_before, self.store.ifs())
437+
self.read_dir_first_impl(clients_dir, location, not_before, 0, self.store.ifs())
438+
}
439+
Location::External => {
440+
self.read_dir_first_impl(clients_dir, location, not_before, 0, self.store.efs())
441+
}
442+
Location::Volatile => {
443+
self.read_dir_first_impl(clients_dir, location, not_before, 0, self.store.vfs())
444+
}
445+
}
446+
}
447+
448+
fn read_dir_nth(
449+
&mut self,
450+
clients_dir: &PathBuf,
451+
location: Location,
452+
start_at: usize,
453+
) -> Result<Option<(DirEntry, ReadDirState)>> {
454+
match location {
455+
Location::Internal => {
456+
self.read_dir_first_impl(clients_dir, location, None, start_at, self.store.ifs())
407457
}
408458
Location::External => {
409-
self.read_dir_first_impl(clients_dir, location, not_before, self.store.efs())
459+
self.read_dir_first_impl(clients_dir, location, None, start_at, self.store.efs())
410460
}
411461
Location::Volatile => {
412-
self.read_dir_first_impl(clients_dir, location, not_before, self.store.vfs())
462+
self.read_dir_first_impl(clients_dir, location, None, start_at, self.store.vfs())
413463
}
414464
}
415465
}
@@ -433,18 +483,53 @@ impl<S: Store> Filestore for ClientFilestore<S> {
433483
clients_dir,
434484
location,
435485
user_attribute,
486+
0,
487+
self.store.ifs(),
488+
),
489+
Location::External => self.read_dir_files_first_impl(
490+
clients_dir,
491+
location,
492+
user_attribute,
493+
0,
494+
self.store.efs(),
495+
),
496+
Location::Volatile => self.read_dir_files_first_impl(
497+
clients_dir,
498+
location,
499+
user_attribute,
500+
0,
501+
self.store.vfs(),
502+
),
503+
}
504+
}
505+
506+
fn read_dir_files_nth(
507+
&mut self,
508+
clients_dir: &PathBuf,
509+
location: Location,
510+
start_at: usize,
511+
user_attribute: Option<UserAttribute>,
512+
) -> Result<Option<(Option<Message>, ReadDirFilesState)>> {
513+
match location {
514+
Location::Internal => self.read_dir_files_first_impl(
515+
clients_dir,
516+
location,
517+
user_attribute,
518+
start_at,
436519
self.store.ifs(),
437520
),
438521
Location::External => self.read_dir_files_first_impl(
439522
clients_dir,
440523
location,
441524
user_attribute,
525+
start_at,
442526
self.store.efs(),
443527
),
444528
Location::Volatile => self.read_dir_files_first_impl(
445529
clients_dir,
446530
location,
447531
user_attribute,
532+
start_at,
448533
self.store.vfs(),
449534
),
450535
}

0 commit comments

Comments
 (0)