Traded enum-based DSL for full callback-based DSL

Now, instead of passing an enum for mem/disk commits, we pass a function
pointer that can specify any behaviour.

This has the benefit of opening up the possibility to pass any sort of
commit logic to the committers, and unused logic can be garbage-collected
by the compiler if unused. The downside is that unfortunately compilers have
a harder time optimizing around functions pointers than enums, and
fitting the state into structs for the callbacks may be costly.
This commit is contained in:
Christopher Haster
2018-03-11 11:28:13 -05:00
parent ca3d6a52d2
commit f30ab677a4

274
lfs.c
View File

@@ -484,30 +484,85 @@ static int lfs_dir_fetch(lfs_t *lfs,
return 0; return 0;
} }
struct lfs_region { struct lfs_commit {
enum { uint32_t crc;
LFS_FROM_MEM, lfs_block_t block;
LFS_FROM_DISK, lfs_off_t off;
} source; };
static int lfs_commit(lfs_t *lfs, struct lfs_commit *c, const void *data, lfs_size_t size) {
lfs_crc(&c->crc, data, size);
int err = lfs_bd_prog(lfs, c->block, c->off, data, size);
c->off += size;
return err;
}
struct lfs_region {
lfs_off_t off; lfs_off_t off;
lfs_ssize_t diff; lfs_ssize_t diff;
union { int (*commit)(lfs_t *lfs, struct lfs_commit *c, const void *p);
struct { const void *data;
const void *data;
} m;
struct {
lfs_block_t block;
lfs_off_t off;
} d;
} u;
lfs_size_t len;
struct lfs_region *next; struct lfs_region *next;
}; };
struct lfs_commit_mem {
const void *data;
lfs_size_t size;
};
static int lfs_commit_mem(lfs_t *lfs, struct lfs_commit *c, const void *p) {
const struct lfs_commit_mem *m = p;
return lfs_commit(lfs, c, m->data, m->size);
}
struct lfs_commit_disk {
lfs_block_t block;
lfs_off_t off;
lfs_size_t size;
struct lfs_region *regions;
};
static int lfs_commit_disk(lfs_t *lfs, struct lfs_commit *c, const void *p) {
const struct lfs_commit_disk *u = p;
struct lfs_region *r = u->regions;
lfs_off_t off = 0;
while (true) {
if (r && r->off == off) {
lfs_off_t orig = c->off;
int err = r->commit(lfs, c, r->data);
if (err) {
return err;
}
off += (c->off - orig) - r->diff;
r = r->next;
} else if (off < u->size) {
uint8_t data;
int err = lfs_bd_read(lfs, u->block, u->off + off, &data, 1);
if (err) {
return err;
}
err = lfs_commit(lfs, c, &data, 1);
if (err) {
return err;
}
off += 1;
} else {
return 0;
}
}
}
static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
struct lfs_region *regions) { struct lfs_region *regions) {
// state for copying over
const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]};
lfs_size_t oldsize = (0x7fffffff & dir->d.size) - 4;
bool relocated = false;
// increment revision count // increment revision count
dir->d.rev += 1; dir->d.rev += 1;
@@ -517,9 +572,6 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
dir->d.size += r->diff; dir->d.size += r->diff;
} }
const lfs_block_t oldpair[2] = {dir->pair[0], dir->pair[1]};
bool relocated = false;
while (true) { while (true) {
if (true) { if (true) {
int err = lfs_bd_erase(lfs, dir->pair[0]); int err = lfs_bd_erase(lfs, dir->pair[0]);
@@ -530,10 +582,20 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
uint32_t crc = 0xffffffff; struct lfs_commit c = {
.crc = 0xffffffff,
.block = dir->pair[0],
.off = 0,
};
lfs_dir_tole32(&dir->d); lfs_dir_tole32(&dir->d);
lfs_crc(&crc, &dir->d, sizeof(dir->d)); err = lfs_commit_disk(lfs, &c, &(struct lfs_commit_disk){
err = lfs_bd_prog(lfs, dir->pair[0], 0, &dir->d, sizeof(dir->d)); oldpair[1], 0, oldsize,
&(struct lfs_region){
0, 0,
lfs_commit_mem, &(struct lfs_commit_mem){
&dir->d, sizeof(dir->d)},
regions}});
lfs_dir_fromle32(&dir->d); lfs_dir_fromle32(&dir->d);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
@@ -542,54 +604,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
struct lfs_region *r = regions; c.crc = lfs_tole32(c.crc);
int j = 0; err = lfs_bd_prog(lfs, dir->pair[0], c.off, &c.crc, 4);
lfs_off_t oldoff = sizeof(dir->d); c.crc = lfs_fromle32(c.crc);
lfs_off_t newoff = sizeof(dir->d);
while (newoff < (0x7fffffff & dir->d.size)-4) {
while (r && r->off == oldoff && r->len == j) {
oldoff += r->len - r->diff;
r = r->next;
j = 0;
}
uint8_t data;
if (r && r->off == oldoff) {
if (r->source == LFS_FROM_DISK) {
err = lfs_bd_read(lfs, r->u.d.block,
r->u.d.off + j, &data, 1);
if (err) {
return err;
}
} else {
data = ((const uint8_t *)r->u.m.data)[j];
}
j += 1;
} else {
err = lfs_bd_read(lfs, oldpair[1], oldoff, &data, 1);
if (err) {
return err;
}
oldoff += 1;
}
lfs_crc(&crc, &data, 1);
err = lfs_bd_prog(lfs, dir->pair[0], newoff, &data, 1);
if (err) {
if (err == LFS_ERR_CORRUPT) {
goto relocate;
}
return err;
}
newoff += 1;
}
crc = lfs_tole32(crc);
err = lfs_bd_prog(lfs, dir->pair[0], newoff, &crc, 4);
crc = lfs_fromle32(crc);
if (err) { if (err) {
if (err == LFS_ERR_CORRUPT) { if (err == LFS_ERR_CORRUPT) {
goto relocate; goto relocate;
@@ -613,12 +630,13 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_dir_t *dir,
return err; return err;
} }
if (ncrc != crc) { if (ncrc != c.crc) {
goto relocate; goto relocate;
} }
} }
break; break;
relocate: relocate:
//commit was corrupted //commit was corrupted
LFS_DEBUG("Bad block at %d", dir->pair[0]); LFS_DEBUG("Bad block at %d", dir->pair[0]);
@@ -709,14 +727,10 @@ static int lfs_dir_append(lfs_t *lfs, lfs_dir_t *dir,
static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir, static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
lfs_entry_t *entry, struct lfs_region *regions) { lfs_entry_t *entry, struct lfs_region *regions) {
lfs_off_t oldoff = entry->off; lfs_off_t oldoff = entry->off; // <- TODO rm me?
lfs_ssize_t diff = 0; lfs_ssize_t diff = 0;
lfs_size_t len = 0;
struct lfs_region **tail = &regions;
for (struct lfs_region *r = regions; r; r = r->next) { for (struct lfs_region *r = regions; r; r = r->next) {
diff += r->diff; diff += r->diff;
len += r->len;
tail = &r->next;
} }
// do we still fit? // do we still fit?
@@ -731,45 +745,44 @@ static int lfs_dir_update(lfs_t *lfs, lfs_dir_t *dir,
} }
} else { } else {
lfs_dir_t olddir = *dir; lfs_dir_t olddir = *dir;
lfs_off_t oldoff = entry->off;
lfs_size_t oldsize = lfs_entry_size(entry) - diff;
// mark as moving // mark as moving
entry->d.type |= LFS_STRUCT_MOVED; entry->d.type |= LFS_STRUCT_MOVED;
int err = lfs_dir_commit(lfs, &olddir, int err = lfs_dir_commit(lfs, &olddir,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, oldoff, 0, oldoff, 0,
{.m.data = &entry->d.type}, 1}); lfs_commit_mem, &(struct lfs_commit_mem){
&entry->d.type, 1}});
if (err) { if (err) {
return err; return err;
} }
// append updated entry // append updated entry
entry->d.type &= LFS_STRUCT_MOVED; entry->d.type &= LFS_STRUCT_MOVED;
lfs_size_t prefix = regions->off; err = lfs_dir_append(lfs, dir, entry,
lfs_size_t suffix = lfs_entry_size(entry) - len - 4; &(struct lfs_region){
for (struct lfs_region *r = regions; r; r = r->next) { 0, +lfs_entry_size(entry),
r->off = 0; lfs_commit_disk, &(struct lfs_commit_disk){
r->diff = r->len; olddir.pair[0], entry->off, oldsize, regions}});
if (err) {
return err;
} }
regions = &(struct lfs_region){
LFS_FROM_DISK, 0, +prefix,
{.d.block = olddir.pair[0], .d.off = entry->off},
prefix, regions};
*tail = &(struct lfs_region){
LFS_FROM_DISK, 0, +suffix,
{.d.block = olddir.pair[0], .d.off = entry->off+prefix+len},
suffix};
// remove old entry // remove old entry
err = lfs_dir_commit(lfs, &olddir, err = lfs_dir_commit(lfs, &olddir,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, oldoff, -(lfs_entry_size(entry) - diff), oldoff, -oldsize,
{.m.data = NULL}, 0}); lfs_commit_mem, &(struct lfs_commit_mem){
NULL, 0}});
if (err) { if (err) {
return err; return err;
} }
} }
// TODO move to dir_commit?
// TODO this doesn't work...
// shift over any files/directories that are affected // shift over any files/directories that are affected
for (lfs_file_t *f = lfs->files; f; f = f->next) { for (lfs_file_t *f = lfs->files; f; f = f->next) {
if (lfs_paircmp(f->pair, dir->pair) == 0) { if (lfs_paircmp(f->pair, dir->pair) == 0) {
@@ -812,9 +825,11 @@ static int lfs_dir_remove(lfs_t *lfs, lfs_dir_t *dir, lfs_entry_t *entry) {
} }
// shift out the entry // shift out the entry
int err = lfs_dir_commit(lfs, dir, &(struct lfs_region){ int err = lfs_dir_commit(lfs, dir,
LFS_FROM_MEM, entry->off, -lfs_entry_size(entry), &(struct lfs_region){
{.m.data = NULL}, 0}); entry->off, -lfs_entry_size(entry),
lfs_commit_mem, &(struct lfs_commit_mem){
NULL, 0}});
if (err) { if (err) {
return err; return err;
} }
@@ -1036,11 +1051,13 @@ int lfs_mkdir(lfs_t *lfs, const char *path) {
err = lfs_dir_append(lfs, &cwd, &entry, err = lfs_dir_append(lfs, &cwd, &entry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +sizeof(entry.d), 0, +sizeof(entry.d),
{.m.data = &entry.d}, sizeof(entry.d), lfs_commit_mem, &(struct lfs_commit_mem){
&entry.d, sizeof(entry.d)},
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +entry.d.nlen, 0, +entry.d.nlen,
{.m.data = path}, entry.d.nlen}}); lfs_commit_mem, &(struct lfs_commit_mem){
path, entry.d.nlen}}});
if (err) { if (err) {
return err; return err;
} }
@@ -1426,11 +1443,13 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
entry.d.u.file.size = 0; entry.d.u.file.size = 0;
err = lfs_dir_append(lfs, &cwd, &entry, err = lfs_dir_append(lfs, &cwd, &entry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +sizeof(entry.d), 0, +sizeof(entry.d),
{.m.data = &entry.d}, sizeof(entry.d), lfs_commit_mem, &(struct lfs_commit_mem){
&entry.d, sizeof(entry.d)},
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +entry.d.nlen, 0, +entry.d.nlen,
{.m.data = path}, entry.d.nlen}}); lfs_commit_mem, &(struct lfs_commit_mem){
path, entry.d.nlen}}});
if (err) { if (err) {
return err; return err;
} }
@@ -1648,8 +1667,9 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
err = lfs_dir_update(lfs, &cwd, &entry, err = lfs_dir_update(lfs, &cwd, &entry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, 0, 0, 0,
{.m.data = &entry.d}, sizeof(entry.d)}); lfs_commit_mem, &(struct lfs_commit_mem){
&entry.d, sizeof(entry.d)}});
if (err) { if (err) {
return err; return err;
} }
@@ -2073,8 +2093,9 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
oldentry.d.type |= LFS_STRUCT_MOVED; oldentry.d.type |= LFS_STRUCT_MOVED;
err = lfs_dir_update(lfs, &oldcwd, &oldentry, err = lfs_dir_update(lfs, &oldcwd, &oldentry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, 0, 0, 0,
{.m.data = &oldentry.d}, sizeof(oldentry.d)}); lfs_commit_mem, &(struct lfs_commit_mem){
&oldentry.d, sizeof(oldentry.d)}});
if (err) { if (err) {
return err; return err;
} }
@@ -2093,22 +2114,26 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
if (prevexists) { if (prevexists) {
err = lfs_dir_update(lfs, &newcwd, &newentry, err = lfs_dir_update(lfs, &newcwd, &newentry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, 0, 0, 0,
{.m.data = &newentry.d}, sizeof(newentry.d), lfs_commit_mem, &(struct lfs_commit_mem){
&newentry.d, sizeof(newentry.d)},
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, sizeof(newentry.d), 0, sizeof(newentry.d), 0,
{.m.data = newpath}, newentry.d.nlen}}); lfs_commit_mem, &(struct lfs_commit_mem){
newpath, newentry.d.nlen}}});
if (err) { if (err) {
return err; return err;
} }
} else { } else {
err = lfs_dir_append(lfs, &newcwd, &newentry, err = lfs_dir_append(lfs, &newcwd, &newentry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +sizeof(newentry.d), 0, +sizeof(newentry.d),
{.m.data = &newentry.d}, sizeof(newentry.d), lfs_commit_mem, &(struct lfs_commit_mem){
&newentry.d, sizeof(newentry.d)},
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, +newentry.d.nlen, 0, +newentry.d.nlen,
{.m.data = newpath}, newentry.d.nlen}}); lfs_commit_mem, &(struct lfs_commit_mem){
newpath, newentry.d.nlen}}});
if (err) { if (err) {
return err; return err;
} }
@@ -2275,8 +2300,9 @@ int lfs_format(lfs_t *lfs, const struct lfs_config *cfg) {
bool valid = false; bool valid = false;
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
err = lfs_dir_commit(lfs, &superdir, &(struct lfs_region){ err = lfs_dir_commit(lfs, &superdir, &(struct lfs_region){
LFS_FROM_MEM, sizeof(superdir.d), 0, sizeof(superdir.d), 0,
{.m.data = &superblock.d}, sizeof(superblock.d)}); lfs_commit_mem, &(struct lfs_commit_mem){
&superblock.d, sizeof(superblock.d)}});
if (err && err != LFS_ERR_CORRUPT) { if (err && err != LFS_ERR_CORRUPT) {
return err; return err;
} }
@@ -2542,8 +2568,9 @@ static int lfs_relocate(lfs_t *lfs,
int err = lfs_dir_update(lfs, &parent, &entry, int err = lfs_dir_update(lfs, &parent, &entry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, 0, 0, 0,
{.m.data = &entry.d}, sizeof(entry.d)}); lfs_commit_mem, &(struct lfs_commit_mem){
&entry.d, sizeof(entry.d)}});
if (err) { if (err) {
return err; return err;
} }
@@ -2669,8 +2696,9 @@ int lfs_deorphan(lfs_t *lfs) {
entry.d.type &= ~LFS_STRUCT_MOVED; entry.d.type &= ~LFS_STRUCT_MOVED;
err = lfs_dir_update(lfs, &cwd, &entry, err = lfs_dir_update(lfs, &cwd, &entry,
&(struct lfs_region){ &(struct lfs_region){
LFS_FROM_MEM, 0, 0, 0, 0,
{.m.data = &entry.d}, sizeof(entry.d)}); lfs_commit_mem, &(struct lfs_commit_mem){
&entry.d, sizeof(entry.d)}});
if (err) { if (err) {
return err; return err;
} }