Skip to content

Commit e052671

Browse files
Use tell and seek to improve filesystem iteration efficiency
1 parent f8f5a3b commit e052671

File tree

2 files changed

+34
-33
lines changed

2 files changed

+34
-33
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ delog = "0.1.0"
4242
cbor-smol = "0.4"
4343
heapless-bytes = { version = "0.3.0", features = ["cbor"] }
4444
interchange = "0.3.0"
45-
littlefs2 = "0.4.0"
45+
littlefs2 = { version = "0.4.0", branch = "iter-optimization", git = "https://github.com/sosthene-nitrokey/littlefs2.git" }
4646
p256-cortex-m4 = { version = "0.1.0-alpha.5", features = ["prehash", "sec1-signatures"] }
4747
salty = { version = "0.2.0", features = ["cose"] }
4848
serde-indexed = "0.1.0"

src/store/filestore.rs

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ use crate::{
1010
pub struct ReadDirState {
1111
real_dir: PathBuf,
1212
location: Location,
13-
last: usize,
13+
position: DirIterationTell,
1414
}
1515

1616
#[derive(Clone)]
1717
pub struct ReadDirFilesState {
1818
real_dir: PathBuf,
19-
last: usize,
19+
position: DirIterationTell,
2020
location: Location,
2121
user_attribute: Option<UserAttribute>,
2222
}
2323

2424
use littlefs2::{
25-
fs::{DirEntry, Metadata},
25+
fs::{DirEntry, DirIterationTell, Metadata},
2626
path::{Path, PathBuf},
2727
};
2828

@@ -144,18 +144,18 @@ impl<S: Store> ClientFilestore<S> {
144144
let dir = self.actual_path(clients_dir)?;
145145

146146
Ok(fs
147-
.read_dir_and_then(&dir, |it| {
147+
.read_dir_and_then(&dir, |mut it| {
148148
// this is an iterator with Item = (usize, Result<DirEntry>)
149-
it.enumerate()
149+
(&mut it)
150150
// skip over `.` and `..`
151151
.skip(2)
152152
// todo: try ?-ing out of this (the API matches std::fs, where read/write errors
153153
// can occur during operation)
154154
//
155155
// Option<usize, Result<DirEntry>> -> ??
156-
.map(|(i, entry)| (i, entry.unwrap()))
156+
.map(Result::unwrap)
157157
// if there is a "not_before" entry, skip all entries before it.
158-
.find(|(_, entry)| {
158+
.find(|entry| {
159159
if let Some(not_before) = not_before {
160160
entry.file_name() == not_before.as_ref()
161161
} else {
@@ -165,10 +165,10 @@ impl<S: Store> ClientFilestore<S> {
165165
// if there is an entry, construct the state that needs storing out of it,
166166
// remove the prefix from the entry's path to not leak implementation details to
167167
// the client, and return both the entry and the state
168-
.map(|(i, mut entry)| {
168+
.map(|mut entry| {
169169
let read_dir_state = ReadDirState {
170170
real_dir: dir.clone(),
171-
last: i,
171+
position: it.tell().unwrap(),
172172
location,
173173
};
174174
let entry_client_path = self.client_path(entry.path());
@@ -196,24 +196,25 @@ impl<S: Store> ClientFilestore<S> {
196196
) -> Result<Option<(DirEntry, ReadDirState)>> {
197197
let ReadDirState {
198198
real_dir,
199-
last,
199+
position,
200200
location,
201201
} = state;
202202

203203
// all we want to do here is skip just past the previously found entry
204204
// in the directory iterator, then return it (plus state to continue on next call)
205205
Ok(fs
206-
.read_dir_and_then(&real_dir, |it| {
206+
.read_dir_and_then(&real_dir, |mut it| {
207207
// skip over previous
208-
it.enumerate()
209-
.nth(last + 1)
208+
it.seek(position)?;
209+
(&mut it)
210+
.next()
210211
// entry is still a Result :/ (see question in `read_dir_first`)
211-
.map(|(i, entry)| (i, entry.unwrap()))
212+
.map(Result::unwrap)
212213
// convert Option into Result, again because `read_dir_and_then` expects this
213-
.map(|(i, mut entry)| {
214+
.map(|mut entry| {
214215
let read_dir_state = ReadDirState {
215216
real_dir: real_dir.clone(),
216-
last: i,
217+
position: it.tell().unwrap(),
217218
location,
218219
};
219220

@@ -236,18 +237,18 @@ impl<S: Store> ClientFilestore<S> {
236237
let dir = self.actual_path(clients_dir)?;
237238

238239
Ok(fs
239-
.read_dir_and_then(&dir, |it| {
240+
.read_dir_and_then(&dir, |mut it| {
240241
// this is an iterator with Item = (usize, Result<DirEntry>)
241-
it.enumerate()
242+
(&mut it)
242243
// todo: try ?-ing out of this (the API matches std::fs, where read/write errors
243244
// can occur during operation)
244245
//
245246
// Option<usize, Result<DirEntry>> -> ??
246-
.map(|(i, entry)| (i, entry.unwrap()))
247+
.map(Result::unwrap)
247248
// skip over directories (including `.` and `..`)
248-
.filter(|(_, entry)| entry.file_type().is_file())
249+
.filter(|entry| entry.file_type().is_file())
249250
// take first entry that meets requirements
250-
.find(|(_, entry)| {
251+
.find(|entry| {
251252
if let Some(user_attribute) = user_attribute.as_ref() {
252253
let mut path = dir.clone();
253254
path.push(entry.file_name());
@@ -267,10 +268,10 @@ impl<S: Store> ClientFilestore<S> {
267268
// if there is an entry, construct the state that needs storing out of it,
268269
// and return the file's contents.
269270
// the client, and return both the entry and the state
270-
.map(|(i, entry)| {
271+
.map(|entry| {
271272
let read_dir_files_state = ReadDirFilesState {
272273
real_dir: dir.clone(),
273-
last: i,
274+
position: it.tell().unwrap(),
274275
location,
275276
user_attribute,
276277
};
@@ -294,24 +295,24 @@ impl<S: Store> ClientFilestore<S> {
294295
) -> Result<Option<(Option<Message>, ReadDirFilesState)>> {
295296
let ReadDirFilesState {
296297
real_dir,
297-
last,
298+
position,
298299
location,
299300
user_attribute,
300301
} = state;
301302

302303
// all we want to do here is skip just past the previously found entry
303304
// in the directory iterator, then return it (plus state to continue on next call)
304305
Ok(fs
305-
.read_dir_and_then(&real_dir, |it| {
306+
.read_dir_and_then(&real_dir, |mut it| {
306307
// skip over previous
307-
it.enumerate()
308-
.skip(last + 1)
308+
it.seek(position)?;
309+
(&mut it)
309310
// entry is still a Result :/ (see question in `read_dir_first`)
310-
.map(|(i, entry)| (i, entry.unwrap()))
311+
.map(Result::unwrap)
311312
// skip over directories (including `.` and `..`)
312-
.filter(|(_, entry)| entry.file_type().is_file())
313+
.filter(|entry| entry.file_type().is_file())
313314
// take first entry that meets requirements
314-
.find(|(_, entry)| {
315+
.find(|entry| {
315316
if let Some(user_attribute) = user_attribute.as_ref() {
316317
let mut path = real_dir.clone();
317318
path.push(entry.file_name());
@@ -327,10 +328,10 @@ impl<S: Store> ClientFilestore<S> {
327328
true
328329
}
329330
})
330-
.map(|(i, entry)| {
331+
.map(|entry| {
331332
let read_dir_files_state = ReadDirFilesState {
332333
real_dir: real_dir.clone(),
333-
last: i,
334+
position: it.tell().unwrap(),
334335
location,
335336
user_attribute,
336337
};

0 commit comments

Comments
 (0)