Consolidated find/parent scanning functions

An interesting observation about the find and parent scanning functions
is that at their core, they're both actually doing the same operation.
They search a metadata-pair during fetch for an entry uses the entry data
instead of the entry tag. This means we can combine these functions and
get a decent code savings.

It's a little bit trickier because pair ordering isn't guaranteed. But
to work around that we can simply search for both pair orderings. It's a
bit more expensive but may be worth the code savings. A fancier
implementation in the future can avoid the 2x lfs_parent scans.
This commit is contained in:
Christopher Haster
2018-07-09 17:29:40 -05:00
parent fd121dc2e2
commit fe31f79b5f

294
lfs.c
View File

@@ -1415,64 +1415,85 @@ static int lfs_dir_getinfo(lfs_t *lfs, lfs_mdir_t *dir,
} }
struct lfs_dir_find { struct lfs_dir_find {
const char *name; uint32_t mask;
uint16_t len; lfs_tag_t tag;
int16_t id; const void *buffer;
int16_t tempid; lfs_tag_t foundtag;
uint8_t findtype; lfs_tag_t temptag;
uint8_t tempfindtype;
}; };
static int lfs_dir_findscan(lfs_t *lfs, void *p, lfs_mattr_t attr) { static int lfs_dir_findscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
struct lfs_dir_find *find = p; struct lfs_dir_find *find = p;
if (lfs_tag_subtype(attr.tag) == LFS_TYPE_NAME && if ((attr.tag & find->mask) == (find->tag & find->mask)) {
lfs_tag_size(attr.tag) == find->len) {
int res = lfs_bd_cmp(lfs, attr.u.d.block, attr.u.d.off, int res = lfs_bd_cmp(lfs, attr.u.d.block, attr.u.d.off,
find->name, find->len); find->buffer, lfs_tag_size(attr.tag));
if (res < 0) { if (res < 0) {
return res; return res;
} }
if (res) { if (res) {
// found a match // found a match
find->tempid = lfs_tag_id(attr.tag); find->temptag = attr.tag;
find->tempfindtype = lfs_tag_type(attr.tag);
} }
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_DELETE) { } else if (lfs_tag_type(attr.tag) == LFS_TYPE_DELETE) {
if (lfs_tag_id(attr.tag) == find->tempid) { if (lfs_tag_id(attr.tag) == lfs_tag_id(find->temptag)) {
find->tempid = -1; find->temptag = 0xffffffff;
} else if (lfs_tag_id(attr.tag) < find->tempid) { } else if (lfs_tag_id(find->temptag) < 0x3ff &&
find->tempid -= 1; lfs_tag_id(attr.tag) < lfs_tag_id(find->temptag)) {
find->temptag -= lfs_mktag(0, 1, 0);
} }
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_CRC) { } else if (lfs_tag_type(attr.tag) == LFS_TYPE_CRC) {
find->id = find->tempid; find->foundtag = find->temptag;
find->findtype = find->tempfindtype;
} }
return 0; return 0;
} }
static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, const lfs_block_t *pair,
uint32_t mask, lfs_tag_t tag,
const void *buffer, lfs_tag_t *foundtag) {
struct lfs_dir_find find = {
.mask = mask,
.tag = tag,
.buffer = buffer,
.foundtag = 0xffffffff,
.temptag = 0xffffffff,
};
int err = lfs_dir_fetchwith(lfs, dir, pair, lfs_dir_findscan, &find);
if (err) {
return err;
}
if (find.foundtag == 0xffffffff) {
return LFS_ERR_NOENT;
}
*foundtag = find.foundtag;
return 0;
}
// TODO drop others, make this only return id, also make get take in only entry to populate (with embedded tag) // TODO drop others, make this only return id, also make get take in only entry to populate (with embedded tag)
static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir, static int lfs_dir_lookup(lfs_t *lfs, lfs_mdir_t *dir,
const char **path, uint16_t *id, uint8_t *type) { const char **path, uint16_t *id, uint8_t *type) {
lfs_mattr_t attr = { lfs_mattr_t attr = {
.u.pair[0] = lfs->root[0], .u.pair[0] = lfs->root[0],
.u.pair[1] = lfs->root[1], .u.pair[1] = lfs->root[1],
}; };
struct lfs_dir_find find = { const char *name = *path;
.name = *path, lfs_size_t namelen;
};
while (true) { while (true) {
nextname: nextname:
// skip slashes // skip slashes
find.name += strspn(find.name, "/"); name += strspn(name, "/");
find.len = strcspn(find.name, "/"); namelen = strcspn(name, "/");
// special case for root dir // special case for root dir
if (find.name[0] == '\0') { if (name[0] == '\0') {
// Return ISDIR when we hit root // Return ISDIR when we hit root
// TODO change this to -1 or 0x3ff? // TODO change this to -1 or 0x3ff?
*type = LFS_TYPE_DIR; *type = LFS_TYPE_DIR;
@@ -1480,14 +1501,14 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
} }
// skip '.' and root '..' // skip '.' and root '..'
if ((find.len == 1 && memcmp(find.name, ".", 1) == 0) || if ((namelen == 1 && memcmp(name, ".", 1) == 0) ||
(find.len == 2 && memcmp(find.name, "..", 2) == 0)) { (namelen == 2 && memcmp(name, "..", 2) == 0)) {
find.name += find.len; name += namelen;
goto nextname; goto nextname;
} }
// skip if matched by '..' in name // skip if matched by '..' in name
const char *suffix = find.name + find.len; const char *suffix = name + namelen;
lfs_size_t sufflen; lfs_size_t sufflen;
int depth = 1; int depth = 1;
while (true) { while (true) {
@@ -1500,7 +1521,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) {
depth -= 1; depth -= 1;
if (depth == 0) { if (depth == 0) {
find.name = suffix + sufflen; name = suffix + sufflen;
goto nextname; goto nextname;
} }
} else { } else {
@@ -1511,21 +1532,22 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
} }
// update what we've found // update what we've found
*path = find.name; *path = name;
// find path // find path
while (true) { while (true) {
//printf("checking %d %d for %s\n", attr.u.pair[0], attr.u.pair[1], *path); lfs_tag_t foundtag = -1;
find.id = -1; int err = lfs_dir_find(lfs, dir, attr.u.pair,
find.tempid = -1; 0x7c000fff, lfs_mktag(LFS_TYPE_NAME, 0, namelen),
int err = lfs_dir_fetchwith(lfs, dir, attr.u.pair, name, &foundtag);
lfs_dir_findscan, &find); if (err && err != LFS_ERR_NOENT) {
if (err) {
return err; return err;
} }
if (find.id >= 0) { if (err != LFS_ERR_NOENT) {
// found it // found it
*id = lfs_tag_id(foundtag);
*type = lfs_tag_type(foundtag);
break; break;
} }
@@ -1537,17 +1559,15 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
attr.u.pair[1] = dir->tail[1]; attr.u.pair[1] = dir->tail[1];
} }
*id = find.id; name += namelen;
*type = find.findtype; name += strspn(name, "/");
find.name += find.len; if (name[0] == '\0') {
find.name += strspn(find.name, "/");
if (find.name[0] == '\0') {
return 0; return 0;
} }
// don't continue on if we didn't hit a directory // don't continue on if we didn't hit a directory
// TODO update with what's on master? // TODO update with what's on master?
if (find.findtype != LFS_TYPE_DIR) { if (*type != LFS_TYPE_DIR) {
return LFS_ERR_NOTDIR; return LFS_ERR_NOTDIR;
} }
@@ -1555,7 +1575,7 @@ static int lfs_dir_find(lfs_t *lfs, lfs_mdir_t *dir,
// TODO would this mean more code? // TODO would this mean more code?
// grab the entry data // grab the entry data
int err = lfs_dir_getentry(lfs, dir, 0x7c3ff000, int err = lfs_dir_getentry(lfs, dir, 0x7c3ff000,
lfs_mktag(LFS_TYPE_STRUCT, find.id, 0), &attr); lfs_mktag(LFS_TYPE_STRUCT, *id, 0), &attr);
if (err) { if (err) {
return err; return err;
} }
@@ -1573,7 +1593,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
} }
lfs_mdir_t cwd; lfs_mdir_t cwd;
int err = lfs_dir_find(lfs, &cwd, &path, &(uint16_t){0}, &(uint8_t){0}); int err = lfs_dir_lookup(lfs, &cwd, &path, &(uint16_t){0}, &(uint8_t){0});
if (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) { if (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) {
if (!err || err == LFS_ERR_ISDIR) { if (!err || err == LFS_ERR_ISDIR) {
return LFS_ERR_EXIST; return LFS_ERR_EXIST;
@@ -1626,7 +1646,7 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) { int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path) {
uint16_t id; uint16_t id;
uint8_t type; uint8_t type;
int err = lfs_dir_find(lfs, &dir->m, &path, &id, &type); int err = lfs_dir_lookup(lfs, &dir->m, &path, &id, &type);
if (err && err != LFS_ERR_ISDIR) { if (err && err != LFS_ERR_ISDIR) {
return err; return err;
} }
@@ -1979,7 +1999,7 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
lfs_mdir_t cwd; lfs_mdir_t cwd;
uint16_t id; uint16_t id;
uint8_t type; uint8_t type;
int err = lfs_dir_find(lfs, &cwd, &path, &id, &type); int err = lfs_dir_lookup(lfs, &cwd, &path, &id, &type);
if (err && (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) && if (err && (err != LFS_ERR_NOENT || strchr(path, '/') != NULL) &&
err != LFS_ERR_ISDIR) { err != LFS_ERR_ISDIR) {
return err; return err;
@@ -2655,7 +2675,7 @@ int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info) {
lfs_mdir_t cwd; lfs_mdir_t cwd;
uint16_t id; uint16_t id;
// TODO pass to getinfo? // TODO pass to getinfo?
int err = lfs_dir_find(lfs, &cwd, &path, &id, &(uint8_t){0}); int err = lfs_dir_lookup(lfs, &cwd, &path, &id, &(uint8_t){0});
if (err && err != LFS_ERR_ISDIR) { if (err && err != LFS_ERR_ISDIR) {
return err; return err;
} }
@@ -2687,7 +2707,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
uint16_t id; uint16_t id;
uint8_t type; uint8_t type;
err = lfs_dir_find(lfs, &cwd, &path, &id, &type); err = lfs_dir_lookup(lfs, &cwd, &path, &id, &type);
if (err) { if (err) {
return err; return err;
} }
@@ -2749,7 +2769,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_mdir_t oldcwd; lfs_mdir_t oldcwd;
uint16_t oldid; uint16_t oldid;
uint8_t oldtype; uint8_t oldtype;
int err = lfs_dir_find(lfs, &oldcwd, &oldpath, &oldid, &oldtype); int err = lfs_dir_lookup(lfs, &oldcwd, &oldpath, &oldid, &oldtype);
if (err) { if (err) {
return err; return err;
} }
@@ -2758,7 +2778,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
lfs_mdir_t newcwd; lfs_mdir_t newcwd;
uint16_t newid; uint16_t newid;
uint8_t prevtype; uint8_t prevtype;
err = lfs_dir_find(lfs, &newcwd, &newpath, &newid, &prevtype); err = lfs_dir_lookup(lfs, &newcwd, &newpath, &newid, &prevtype);
if (err && err != LFS_ERR_NOENT) { if (err && err != LFS_ERR_NOENT) {
return err; return err;
} }
@@ -2883,7 +2903,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// } // }
// //
// lfs_mattr_t entry; // lfs_mattr_t entry;
// err = lfs_dir_find(lfs, &cwd, &entry, &path); // err = lfs_dir_lookup(lfs, &cwd, &entry, &path);
// if (err) { // if (err) {
// return err; // return err;
// } // }
@@ -2900,7 +2920,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
// } // }
// //
// lfs_mattr_t entry; // lfs_mattr_t entry;
// err = lfs_dir_find(lfs, &cwd, &entry, &path); // err = lfs_dir_lookup(lfs, &cwd, &entry, &path);
// if (err) { // if (err) {
// return err; // return err;
// } // }
@@ -3331,168 +3351,42 @@ static int lfs_pred(lfs_t *lfs, const lfs_block_t pair[2], lfs_mdir_t *pdir) {
return false; return false;
} }
/*
static int lfs_pred(lfs_t *lfs, const lfs_block_t dir[2], lfs_mdir_t *pdir) {
if (lfs_pairisnull(lfs->root)) {
return 0;
}
// iterate directories
int err = lfs_dir_fetch(lfs, pdir, (const lfs_block_t[2]){0, 1});
if (err) {
return err;
}
while (!lfs_pairisnull(pdir->d.tail)) {
if (lfs_paircmp(pdir->d.tail, dir) == 0) {
return true;
}
err = lfs_dir_fetch(lfs, pdir, pdir->d.tail);
if (err) {
return err;
}
}
return false;
}
*/
// TODO combine parentscan and findscan?
struct lfs_dir_parentscan {
lfs_block_t pair[2];
int16_t id;
int16_t tempid;
};
static int lfs_parentscan(lfs_t *lfs, void *p, lfs_mattr_t attr) {
struct lfs_dir_parentscan *parentscan = p;
if (lfs_tag_type(attr.tag) == LFS_STRUCT_DIR) {
int err = lfs_bd_read(lfs, attr.u.d.block, attr.u.d.off,
&attr.u, sizeof(attr.u));
if (err) {
return err;
}
if (lfs_paircmp(attr.u.pair, parentscan->pair) == 0) {
// found a match
parentscan->tempid = lfs_tag_id(attr.tag);
}
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_DELETE) {
if (lfs_tag_id(attr.tag) == parentscan->tempid) {
parentscan->tempid = -1;
} else if (lfs_tag_id(attr.tag) < parentscan->tempid) {
parentscan->tempid -= 1;
}
} else if (lfs_tag_type(attr.tag) == LFS_TYPE_CRC) {
parentscan->id = parentscan->tempid;
}
return 0;
}
static int lfs_parent(lfs_t *lfs, const lfs_block_t pair[2], static int lfs_parent(lfs_t *lfs, const lfs_block_t pair[2],
lfs_mdir_t *parent, lfs_mattr_t *attr) { lfs_mdir_t *parent, lfs_mattr_t *attr) {
// iterate over all directory directory entries // search for both orderings so we can reuse the find function
parent->tail[0] = 0; lfs_block_t child[2] = {pair[0], pair[1]};
parent->tail[1] = 1;
while (!lfs_pairisnull(parent->tail)) {
struct lfs_dir_parentscan parentscan = {
.pair[0] = pair[0],
.pair[1] = pair[1],
.id = -1,
.tempid = -1,
};
int err = lfs_dir_fetchwith(lfs, parent, parent->tail, for (int i = 0; i < 2; i++) {
lfs_parentscan, &parentscan); // iterate over all directory directory entries
if (err) { parent->tail[0] = 0;
return err; parent->tail[1] = 1;
} while (!lfs_pairisnull(parent->tail)) {
lfs_tag_t foundtag = -1;
if (parentscan.id != -1) { int err = lfs_dir_find(lfs, parent, parent->tail,
int err = lfs_dir_getentry(lfs, parent, 0x7ffff000, 0x7fc00fff, lfs_mktag(LFS_STRUCT_DIR, 0, sizeof(child)),
lfs_mktag(LFS_STRUCT_DIR, parentscan.id, 0), attr); child, &foundtag);
if (err) {
return err;
}
return true;
}
}
return false;
}
//
//static int lfs_parent(lfs_t *lfs, const lfs_block_t pair[2],
// lfs_mdir_t *parent, lfs_mattr_t *attr) {
// // iterate over all directory directory entries
// parent->tail[0] = 0;
// parent->tail[1] = 1;
// while (!lfs_pairisnull(parent->tail)) {
// int err = lfs_dir_fetch(lfs, parent, parent->tail);
// if (err) {
// return err;
// }
//
// // TODO make this O(n) by using fetchwith to match the pointers
// for (uint16_t id = 0; id < parent->count; id++) {
// int err = lfs_dir_getentry(lfs, parent, 0x43dff000,
// lfs_mktag(LFS_STRUCT_DIR, id, 0), attr);
// if (err) {
// if (err == LFS_ERR_NOENT) {
// continue;
// }
// return err;
// }
//
// if (lfs_paircmp(attr->u.pair, pair) == 0) {
// return true;
// }
// }
// }
//
// return false;
//}
/*
static int lfs_parent(lfs_t *lfs, const lfs_block_t dir[2],
lfs_mdir_t *parent, lfs_mattr_t *attr) {
if (lfs_pairisnull(lfs->root)) {
return 0;
}
parent->d.tail[0] = 0;
parent->d.tail[1] = 1;
// iterate over all directory directory entries
while (!lfs_pairisnull(parent->d.tail)) {
int err = lfs_dir_fetch(lfs, parent, parent->d.tail);
if (err) {
return err;
}
while (true) {
err = lfs_dir_next(lfs, parent, attr);
if (err && err != LFS_ERR_NOENT) { if (err && err != LFS_ERR_NOENT) {
return err; return err;
} }
if (err == LFS_ERR_NOENT) { if (err != LFS_ERR_NOENT) {
break; // found our parent
} int err = lfs_dir_getentry(lfs, parent,
0x7ffff000, foundtag, attr);
if (err) {
return err;
}
if (((0x70 & attr->d.type) == LFS_STRUCT_DIR) &&
lfs_paircmp(attr->d.u.dir, dir) == 0) {
return true; return true;
} }
} }
lfs_pairswap(child);
} }
return false; return false;
} }
*/
// TODO rename to lfs_dir_relocate? // TODO rename to lfs_dir_relocate?
static int lfs_relocate(lfs_t *lfs, static int lfs_relocate(lfs_t *lfs,
@@ -3725,7 +3619,7 @@ int lfs_deorphan(lfs_t *lfs) {
// } // }
// //
// lfs_mattr_t entry; // lfs_mattr_t entry;
// err = lfs_dir_find(lfs, &cwd, &entry, &path); // err = lfs_dir_lookup(lfs, &cwd, &entry, &path);
// if (err) { // if (err) {
// return err; // return err;
// } // }