Dropped bmoss/inline file write strategy
This is the first step towards dropping bmoss/bsprout support
completely: Changing our write strategy to no longer emit bmosses.
Now, small files are converted directly to inlined bshrubs, which are
not _that_ much more overhead.
The biggest savings are in lfsr_file_truncate/fruncate, where we no
longer have to worry about the edge cases around converting from
bshrub/btree -> bmoss. We can just rely on btrees naturally folding into
bshrubs when they get small enough.
This saves a nice chunk of code, but keeping in mind we're still lugging
most of the bmoss circuitry around in order to support reading bmosses:
code stack ctx
before: 38284 2624 640
after: 37652 (-1.7%) 2616 (-0.3%) 640 (+0.0%)
This commit is contained in:
@@ -11163,25 +11163,6 @@ static int lfsr_file_fetch(lfs_t *lfs, lfsr_file_t *file, bool trunc) {
|
|||||||
file->o.o.flags &= ~LFS_o_UNSYNC;
|
file->o.o.flags &= ~LFS_o_UNSYNC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if our file is small, try to keep the whole thing in our buffer
|
|
||||||
//
|
|
||||||
// if this fails we may end up with corrupt data, but that's ok, we
|
|
||||||
// just can't end up with corrupt metadata
|
|
||||||
lfs_size_t size = lfsr_bshrub_size(&file->o.bshrub);
|
|
||||||
if (size <= lfsr_file_inlinesize(lfs, file)) {
|
|
||||||
lfs_ssize_t d = lfsr_file_read_(lfs, file,
|
|
||||||
0, file->buffer.buffer, size);
|
|
||||||
if (d < 0) {
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
// small files remain perpetually unflushed
|
|
||||||
file->o.o.flags |= LFS_o_UNFLUSH;
|
|
||||||
lfsr_bshrub_init(&file->o.bshrub);
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to fetch any custom attributes
|
// try to fetch any custom attributes
|
||||||
for (lfs_size_t i = 0; i < file->cfg->attr_count; i++) {
|
for (lfs_size_t i = 0; i < file->cfg->attr_count; i++) {
|
||||||
// skip writeonly attrs
|
// skip writeonly attrs
|
||||||
@@ -12528,17 +12509,6 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
pos = lfsr_file_size_(file);
|
pos = lfsr_file_size_(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we're a small file, we may need to append zeros
|
|
||||||
if (pos > lfsr_file_size_(file)
|
|
||||||
&& pos <= lfsr_file_inlinesize(lfs, file)) {
|
|
||||||
LFS_ASSERT(lfsr_o_isunflush(file->o.o.flags));
|
|
||||||
LFS_ASSERT(lfsr_file_size_(file) == file->buffer.size);
|
|
||||||
lfs_memset(&file->buffer.buffer[file->buffer.size],
|
|
||||||
0,
|
|
||||||
pos - file->buffer.size);
|
|
||||||
file->buffer.size = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t *buffer_ = buffer;
|
const uint8_t *buffer_ = buffer;
|
||||||
lfs_size_t written = 0;
|
lfs_size_t written = 0;
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
@@ -12631,9 +12601,6 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
|
|||||||
file->pos = pos;
|
file->pos = pos;
|
||||||
|
|
||||||
// flush if requested
|
// flush if requested
|
||||||
//
|
|
||||||
// this seems unreachable, but it's possible if we transition from
|
|
||||||
// a small file to a non-small file
|
|
||||||
if (lfsr_o_isflush(file->o.o.flags)) {
|
if (lfsr_o_isflush(file->o.o.flags)) {
|
||||||
err = lfsr_file_flush(lfs, file);
|
err = lfsr_file_flush(lfs, file);
|
||||||
if (err) {
|
if (err) {
|
||||||
@@ -12667,30 +12634,16 @@ int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// do nothing if our file is small
|
|
||||||
//
|
|
||||||
// note this means small files remain perpetually unflushed
|
|
||||||
if (lfsr_file_size_(file) <= lfsr_file_inlinesize(lfs, file)) {
|
|
||||||
// our file must reside entirely in our buffer
|
|
||||||
LFS_ASSERT(file->buffer.pos == 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// clobber entangled traversals
|
// clobber entangled traversals
|
||||||
lfsr_omdir_mkdirty(lfs, &file->o.o);
|
lfsr_omdir_mkdirty(lfs, &file->o.o);
|
||||||
// checkpoint the allocator
|
// checkpoint the allocator
|
||||||
lfs_alloc_ckpoint(lfs);
|
lfs_alloc_ckpoint(lfs);
|
||||||
|
|
||||||
// flush our buffer if it contains any unwritten data
|
// flush our buffer
|
||||||
int err;
|
int err = lfsr_file_flush_(lfs, file,
|
||||||
if (lfsr_o_isunflush(file->o.o.flags)
|
file->buffer.pos, file->buffer.buffer, file->buffer.size);
|
||||||
&& file->buffer.size != 0) {
|
if (err) {
|
||||||
// flush
|
goto failed;
|
||||||
err = lfsr_file_flush_(lfs, file,
|
|
||||||
file->buffer.pos, file->buffer.buffer, file->buffer.size);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark as flushed
|
// mark as flushed
|
||||||
@@ -12735,15 +12688,6 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
|||||||
// this is convenient because bptrs are a bit annoying to commit
|
// this is convenient because bptrs are a bit annoying to commit
|
||||||
LFS_ASSERT(!lfsr_bshrub_isbmoss(&file->o.o.mdir, &file->o.bshrub));
|
LFS_ASSERT(!lfsr_bshrub_isbmoss(&file->o.o.mdir, &file->o.bshrub));
|
||||||
LFS_ASSERT(!lfsr_bshrub_isbptr(&file->o.o.mdir, &file->o.bshrub));
|
LFS_ASSERT(!lfsr_bshrub_isbptr(&file->o.o.mdir, &file->o.bshrub));
|
||||||
// small files should start as zero, const prop should optimize this out
|
|
||||||
LFS_ASSERT(!lfsr_o_isunflush(file->o.o.flags)
|
|
||||||
|| file->buffer.pos == 0);
|
|
||||||
// small files/btree should be exclusive here
|
|
||||||
LFS_ASSERT(!lfsr_o_isunflush(file->o.o.flags)
|
|
||||||
|| lfsr_bshrub_size(&file->o.bshrub) == 0);
|
|
||||||
// small files must be inlined entirely in our buffer
|
|
||||||
LFS_ASSERT(!lfsr_o_isunflush(file->o.o.flags)
|
|
||||||
|| file->buffer.size <= lfsr_file_inlinesize(lfs, file));
|
|
||||||
// uncreated files must be unsynced
|
// uncreated files must be unsynced
|
||||||
LFS_ASSERT(!lfsr_o_isuncreat(file->o.o.flags)
|
LFS_ASSERT(!lfsr_o_isuncreat(file->o.o.flags)
|
||||||
|| lfsr_o_isunsync(file->o.o.flags));
|
|| lfsr_o_isunsync(file->o.o.flags));
|
||||||
@@ -12778,15 +12722,10 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// null? no rat?
|
// null? no rat?
|
||||||
if (lfsr_o_isunflush(file->o.o.flags) && file->buffer.size == 0) {
|
if (lfsr_file_size_(file) == 0) {
|
||||||
rats[rat_count++] = LFSR_RAT(
|
rats[rat_count++] = LFSR_RAT(
|
||||||
LFSR_TAG_RM | LFSR_TAG_SUB | LFSR_TAG_STRUCT, 0,
|
LFSR_TAG_RM | LFSR_TAG_SUB | LFSR_TAG_STRUCT, 0,
|
||||||
LFSR_DATA_NULL());
|
LFSR_DATA_NULL());
|
||||||
// small file inlined in mdir?
|
|
||||||
} else if (lfsr_o_isunflush(file->o.o.flags)) {
|
|
||||||
rats[rat_count++] = LFSR_RAT_CAT_(
|
|
||||||
LFSR_TAG_SUB | LFSR_TAG_DATA, 0,
|
|
||||||
(const lfsr_data_t*)&file->buffer, 1);
|
|
||||||
// bshrub?
|
// bshrub?
|
||||||
} else if (lfsr_bshrub_isbshrub(&file->o.o.mdir, &file->o.bshrub)) {
|
} else if (lfsr_bshrub_isbshrub(&file->o.o.mdir, &file->o.bshrub)) {
|
||||||
rats[rat_count++] = LFSR_RAT_SHRUBTRUNK(
|
rats[rat_count++] = LFSR_RAT_SHRUBTRUNK(
|
||||||
@@ -12876,12 +12815,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
|
|||||||
|
|
||||||
// update synced files
|
// update synced files
|
||||||
} else {
|
} else {
|
||||||
file_->o.o.flags &= ~LFS_o_UNSYNC;
|
file_->o.o.flags &= ~(LFS_o_UNSYNC | LFS_o_UNFLUSH);
|
||||||
if (lfsr_o_isunflush(file->o.o.flags)) {
|
|
||||||
file_->o.o.flags |= LFS_o_UNFLUSH;
|
|
||||||
} else {
|
|
||||||
file_->o.o.flags &= ~LFS_o_UNFLUSH;
|
|
||||||
}
|
|
||||||
file_->o.bshrub = file->o.bshrub;
|
file_->o.bshrub = file->o.bshrub;
|
||||||
file_->buffer.pos = file->buffer.pos;
|
file_->buffer.pos = file->buffer.pos;
|
||||||
LFS_ASSERT(file->buffer.size
|
LFS_ASSERT(file->buffer.size
|
||||||
@@ -13054,73 +12988,21 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
|
|||||||
// mark as unsynced in case we fail
|
// mark as unsynced in case we fail
|
||||||
file->o.o.flags |= LFS_o_UNSYNC;
|
file->o.o.flags |= LFS_o_UNSYNC;
|
||||||
|
|
||||||
// does our file become small?
|
// truncate our btree
|
||||||
if (size_ <= lfsr_file_inlinesize(lfs, file)) {
|
err = lfsr_file_carve(lfs, file,
|
||||||
// if our data is not already in our buffer we unfortunately
|
lfs_min(size, size_), size - lfs_min(size, size_),
|
||||||
// need to flush so our buffer is available to hold everything
|
LFSR_RAT(
|
||||||
if (file->buffer.pos > 0
|
LFSR_TAG_DATA, +size_ - size,
|
||||||
|| file->buffer.size < lfs_min(
|
LFSR_DATA_NULL()));
|
||||||
size_,
|
if (err) {
|
||||||
lfsr_bshrub_size(&file->o.bshrub))) {
|
goto failed;
|
||||||
err = lfsr_file_flush(lfs, file);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = 0;
|
|
||||||
|
|
||||||
lfs_ssize_t d = lfsr_file_read_(lfs, file,
|
|
||||||
0, file->buffer.buffer, size_);
|
|
||||||
if (d < 0) {
|
|
||||||
err = d;
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we may need to zero some of our buffer
|
|
||||||
if (size_ > file->buffer.size) {
|
|
||||||
lfs_memset(&file->buffer.buffer[file->buffer.size],
|
|
||||||
0,
|
|
||||||
size_ - file->buffer.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// small files remain perpetually unflushed
|
|
||||||
file->o.o.flags |= LFS_o_UNFLUSH;
|
|
||||||
lfsr_bshrub_init(&file->o.bshrub);
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = size_;
|
|
||||||
|
|
||||||
// truncate our file normally
|
|
||||||
} else {
|
|
||||||
// truncate our btree
|
|
||||||
err = lfsr_file_carve(lfs, file,
|
|
||||||
lfs_min(size, size_), size - lfs_min(size, size_),
|
|
||||||
LFSR_RAT(
|
|
||||||
LFSR_TAG_DATA, +size_ - size,
|
|
||||||
LFSR_DATA_NULL()));
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// truncate our buffer
|
|
||||||
file->buffer.pos = lfs_min(file->buffer.pos, size_);
|
|
||||||
file->buffer.size = lfs_min(
|
|
||||||
file->buffer.size,
|
|
||||||
size_ - lfs_min(file->buffer.pos, size_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush if requested
|
// truncate our buffer
|
||||||
//
|
file->buffer.pos = lfs_min(file->buffer.pos, size_);
|
||||||
// this seems unreachable, but it's possible if we transition from
|
file->buffer.size = lfs_min(
|
||||||
// a small file to a non-small file
|
file->buffer.size,
|
||||||
if (lfsr_o_isflush(file->o.o.flags)) {
|
size_ - lfs_min(file->buffer.pos, size_));
|
||||||
err = lfsr_file_flush(lfs, file);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync if requested
|
// sync if requested
|
||||||
if (lfsr_o_issync(file->o.o.flags)) {
|
if (lfsr_o_issync(file->o.o.flags)) {
|
||||||
@@ -13163,101 +13045,36 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size_) {
|
|||||||
// mark as unsynced in case we fail
|
// mark as unsynced in case we fail
|
||||||
file->o.o.flags |= LFS_o_UNSYNC;
|
file->o.o.flags |= LFS_o_UNSYNC;
|
||||||
|
|
||||||
// does our file become small?
|
// fruncate our btree
|
||||||
if (size_ <= lfsr_file_inlinesize(lfs, file)) {
|
err = lfsr_file_carve(lfs, file,
|
||||||
// if our data is not already in our buffer we unfortunately
|
0, lfs_smax(size - size_, 0),
|
||||||
// need to flush so our buffer is available to hold everything
|
LFSR_RAT(
|
||||||
if (file->buffer.pos + file->buffer.size
|
LFSR_TAG_DATA, +size_ - size,
|
||||||
< lfsr_bshrub_size(&file->o.bshrub)
|
LFSR_DATA_NULL()));
|
||||||
|| file->buffer.size < lfs_min(
|
if (err) {
|
||||||
size_,
|
goto failed;
|
||||||
lfsr_bshrub_size(&file->o.bshrub))) {
|
}
|
||||||
err = lfsr_file_flush(lfs, file);
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = 0;
|
|
||||||
|
|
||||||
lfs_ssize_t d = lfsr_file_read_(lfs, file,
|
// fruncate our buffer
|
||||||
lfsr_bshrub_size(&file->o.bshrub) - lfs_min(
|
lfs_memmove(file->buffer.buffer,
|
||||||
size_,
|
&file->buffer.buffer[lfs_min(
|
||||||
lfsr_bshrub_size(&file->o.bshrub)),
|
|
||||||
file->buffer.buffer, size_);
|
|
||||||
if (d < 0) {
|
|
||||||
err = d;
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = size_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we may need to move the data in our buffer
|
|
||||||
if (file->buffer.size > size_) {
|
|
||||||
lfs_memmove(file->buffer.buffer,
|
|
||||||
&file->buffer.buffer[file->buffer.size - size_],
|
|
||||||
file->buffer.size);
|
|
||||||
}
|
|
||||||
// we may need to zero some of our buffer
|
|
||||||
if (size_ > file->buffer.size) {
|
|
||||||
lfs_memmove(&file->buffer.buffer[size_ - file->buffer.size],
|
|
||||||
file->buffer.buffer,
|
|
||||||
file->buffer.size);
|
|
||||||
lfs_memset(file->buffer.buffer,
|
|
||||||
0,
|
|
||||||
size_ - file->buffer.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// small files remain perpetually unflushed
|
|
||||||
file->o.o.flags |= LFS_o_UNFLUSH;
|
|
||||||
lfsr_bshrub_init(&file->o.bshrub);
|
|
||||||
file->buffer.pos = 0;
|
|
||||||
file->buffer.size = size_;
|
|
||||||
|
|
||||||
// fruncate our file normally
|
|
||||||
} else {
|
|
||||||
// fruncate our btree
|
|
||||||
err = lfsr_file_carve(lfs, file,
|
|
||||||
0, lfs_smax(size - size_, 0),
|
|
||||||
LFSR_RAT(
|
|
||||||
LFSR_TAG_DATA, +size_ - size,
|
|
||||||
LFSR_DATA_NULL()));
|
|
||||||
if (err) {
|
|
||||||
goto failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fruncate our buffer
|
|
||||||
lfs_memmove(file->buffer.buffer,
|
|
||||||
&file->buffer.buffer[lfs_min(
|
|
||||||
lfs_smax(
|
|
||||||
size - size_ - file->buffer.pos,
|
|
||||||
0),
|
|
||||||
file->buffer.size)],
|
|
||||||
file->buffer.size - lfs_min(
|
|
||||||
lfs_smax(
|
|
||||||
size - size_ - file->buffer.pos,
|
|
||||||
0),
|
|
||||||
file->buffer.size));
|
|
||||||
file->buffer.size -= lfs_min(
|
|
||||||
lfs_smax(
|
lfs_smax(
|
||||||
size - size_ - file->buffer.pos,
|
size - size_ - file->buffer.pos,
|
||||||
0),
|
0),
|
||||||
file->buffer.size);
|
file->buffer.size)],
|
||||||
file->buffer.pos -= lfs_smin(
|
file->buffer.size - lfs_min(
|
||||||
size - size_,
|
lfs_smax(
|
||||||
file->buffer.pos);
|
size - size_ - file->buffer.pos,
|
||||||
}
|
0),
|
||||||
|
file->buffer.size));
|
||||||
// flush if requested
|
file->buffer.size -= lfs_min(
|
||||||
//
|
lfs_smax(
|
||||||
// this seems unreachable, but it's possible if we transition from
|
size - size_ - file->buffer.pos,
|
||||||
// a small file to a non-small file
|
0),
|
||||||
if (lfsr_o_isflush(file->o.o.flags)) {
|
file->buffer.size);
|
||||||
err = lfsr_file_flush(lfs, file);
|
file->buffer.pos -= lfs_smin(
|
||||||
if (err) {
|
size - size_,
|
||||||
goto failed;
|
file->buffer.pos);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync if requested
|
// sync if requested
|
||||||
if (lfsr_o_issync(file->o.o.flags)) {
|
if (lfsr_o_issync(file->o.o.flags)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user