From a4e9132d7fd3022972bc15d18d6d07c5a40422ba Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sat, 22 Apr 2017 19:48:31 -0500 Subject: [PATCH] Removed a layer of indirection for index-list lookup Files are now stored directly in the index-list, instead of being referenced by pointers that used to live there. This somewhat reduces the complexity around handling files, while still keeping the O(logn) lookup cost. --- lfs.c | 369 ++++++++++++++++++++++++---------------------------------- lfs.h | 7 +- 2 files changed, 156 insertions(+), 220 deletions(-) diff --git a/lfs.c b/lfs.c index 7880d63..d0c21e6 100644 --- a/lfs.c +++ b/lfs.c @@ -246,154 +246,6 @@ static int lfs_alloc(lfs_t *lfs, lfs_block_t *block) { } -/// Index list operations /// - -// Next index offset -static lfs_off_t lfs_indexnext(lfs_t *lfs, lfs_off_t ioff) { - ioff += 1; - while (ioff % lfs->words == 0) { - ioff += lfs_min(lfs_ctz(ioff/lfs->words + 1), lfs->words-1) + 1; - } - - return ioff; -} - -static lfs_off_t lfs_indexfrom(lfs_t *lfs, lfs_off_t off) { - lfs_off_t i = 0; - while (off > lfs->cfg->block_size) { - i = lfs_indexnext(lfs, i); - off -= lfs->cfg->block_size; - } - - return i; -} - -// Find index in index chain given its index offset -static int lfs_index_find(lfs_t *lfs, lfs_block_t head, - lfs_size_t icount, lfs_off_t ioff, lfs_block_t *block) { - lfs_off_t iitarget = ioff / lfs->words; - lfs_off_t iicurrent = (icount-1) / lfs->words; - - while (iitarget != iicurrent) { - lfs_size_t skip = lfs_min( - lfs_min(lfs_ctz(iicurrent+1), lfs->words-1), - lfs_npw2((iitarget ^ iicurrent)+1)-1); - - int err = lfs_bd_read(lfs, head, 4*skip, 4, &head); - if (err) { - return err; - } - - iicurrent -= 1 << skip; - } - - return lfs_bd_read(lfs, head, 4*(ioff % lfs->words), 4, block); -} - -// Append index to index chain, updates head and icount -static int lfs_index_append(lfs_t *lfs, lfs_block_t *headp, - lfs_size_t *icountp, lfs_block_t block) { - lfs_block_t head = *headp; - lfs_size_t ioff = *icountp - 1; - - ioff += 1; - - while (ioff % lfs->words == 0) { - lfs_block_t nhead; - int err = lfs_alloc(lfs, &nhead); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, nhead); - if (err) { - return err; - } - - lfs_off_t skips = lfs_min( - lfs_ctz(ioff/lfs->words + 1), lfs->words-2) + 1; - for (lfs_off_t i = 0; i < skips; i++) { - err = lfs_bd_prog(lfs, nhead, 4*i, 4, &head); - if (err) { - return err; - } - - if (head && i != skips-1) { - err = lfs_bd_read(lfs, head, 4*i, 4, &head); - if (err) { - return err; - } - } - } - - ioff += skips; - head = nhead; - } - - int err = lfs_bd_prog(lfs, head, 4*(ioff % lfs->words), 4, &block); - if (err) { - return err; - } - - *headp = head; - *icountp = ioff + 1; - return 0; -} - -static int lfs_index_traverse(lfs_t *lfs, lfs_block_t head, - lfs_size_t icount, int (*cb)(void*, lfs_block_t), void *data) { - lfs_off_t iicurrent = (icount-1) / lfs->words; - - while (iicurrent > 0) { - int err = cb(data, head); - if (err) { - return err; - } - - lfs_size_t skip = lfs_min(lfs_ctz(iicurrent+1), lfs->words-1); - for (lfs_off_t i = skip; i < lfs->words; i++) { - lfs_block_t block; - int err = lfs_bd_read(lfs, head, 4*i, 4, &block); - if (err) { - return err; - } - - err = cb(data, block); - if (err) { - return err; - } - } - - err = lfs_bd_read(lfs, head, 0, 4, &head); - if (err) { - return err; - } - - iicurrent -= 1; - } - - int err = cb(data, head); - if (err) { - return err; - } - - for (lfs_off_t i = 0; i < lfs->words; i++) { - lfs_block_t block; - int err = lfs_bd_read(lfs, head, 4*i, 4, &block); - if (err) { - return err; - } - - err = cb(data, block); - if (err) { - return err; - } - } - - return 0; -} - - /// Metadata pair and directory operations /// static inline void lfs_pairswap(lfs_block_t pair[2]) { lfs_block_t t = pair[0]; @@ -934,9 +786,123 @@ int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info) { } +/// Index list operations /// +static int lfs_index(lfs_t *lfs, lfs_off_t *off) { + lfs_off_t i = 0; + + while (*off >= lfs->cfg->block_size) { + i += 1; + *off -= lfs->cfg->block_size; + *off += 4*lfs_min(lfs_ctz(i)+1, lfs->words-1); + } + + return i; +} + +static int lfs_index_find(lfs_t *lfs, lfs_block_t head, lfs_size_t size, + lfs_size_t pos, lfs_block_t *block, lfs_off_t *off) { + if (size == 0) { + *block = 0; + *off = 0; + return 0; + } + + lfs_off_t current = lfs_index(lfs, &(lfs_off_t){size-1}); + lfs_off_t target = lfs_index(lfs, &pos); + + while (current != target) { + lfs_size_t skip = lfs_min( + lfs_npw2(current-target+1) - 1, + lfs_min(lfs_ctz(current)+1, lfs->words-1) - 1); + + int err = lfs_bd_read(lfs, head, 4*skip, 4, &head); + if (err) { + return err; + } + + current -= 1 << skip; + } + + *block = head; + *off = pos; + return 0; +} + +static int lfs_index_extend(lfs_t *lfs, + lfs_block_t head, lfs_size_t size, + lfs_off_t *block, lfs_block_t *off) { + int err = lfs_alloc(lfs, block); + if (err) { + return err; + } + + err = lfs_bd_erase(lfs, *block); + if (err) { + return err; + } + + if (size == 0) { + *off = 0; + return 0; + } + + lfs_off_t index = lfs_index(lfs, &(lfs_off_t){size-1}) + 1; + lfs_size_t skips = lfs_min(lfs_ctz(index)+1, lfs->words-1); + + for (lfs_off_t i = 0; i < skips; i++) { + err = lfs_bd_prog(lfs, *block, 4*i, 4, &head); + if (err) { + return err; + } + + if (i != skips-1) { + err = lfs_bd_read(lfs, head, 4*i, 4, &head); + if (err) { + return err; + } + } + } + + *off = 4*skips; + return 0; +} + +static int lfs_index_traverse(lfs_t *lfs, + lfs_block_t head, lfs_size_t size, + int (*cb)(void*, lfs_block_t), void *data) { + if (size == 0) { + return 0; + } + + lfs_off_t index = lfs_index(lfs, &(lfs_off_t){size-1}); + + while (true) { + int err = cb(data, head); + if (err) { + return err; + } + + if (index == 0) { + return 0; + } + + err = lfs_bd_read(lfs, head, 0, 4, &head); + if (err) { + return err; + } + + index -= 1; + } + + return 0; +} + + /// Top level file operations /// int lfs_file_open(lfs_t *lfs, lfs_file_t *file, const char *path, int flags) { + file->flags = flags; + // Allocate entry for file if it doesn't exist // TODO check open files lfs_dir_t cwd; @@ -946,43 +912,36 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file, } err = lfs_dir_find(lfs, &cwd, &file->entry, &path); - if (err && !((flags & LFS_O_CREAT) && err == LFS_ERROR_NO_ENTRY)) { + if (err && err != LFS_ERROR_NO_ENTRY) { return err; - } else if (err != LFS_ERROR_NO_ENTRY && - file->entry.d.type == LFS_TYPE_DIR) { - return LFS_ERROR_IS_DIR; } - if ((flags & LFS_O_CREAT) && err == LFS_ERROR_NO_ENTRY) { + if (err == LFS_ERROR_NO_ENTRY) { + if (!(flags & LFS_O_CREAT)) { + return LFS_ERROR_NO_ENTRY; + } + // create entry to remember name - file->entry.d.type = 1; + file->entry.d.type = LFS_TYPE_REG; file->entry.d.len = sizeof(file->entry.d) + strlen(path); file->entry.d.u.file.head = 0; file->entry.d.u.file.size = 0; - int err = lfs_dir_append(lfs, &cwd, &file->entry, path); + err = lfs_dir_append(lfs, &cwd, &file->entry, path); if (err) { return err; } + } else if (file->entry.d.type == LFS_TYPE_DIR) { + return LFS_ERROR_IS_DIR; + } else if (flags & LFS_O_EXCL) { + return LFS_ERROR_EXISTS; } file->head = file->entry.d.u.file.head; file->size = file->entry.d.u.file.size; - file->windex = lfs_indexfrom(lfs, file->size); + file->wpos = file->entry.d.u.file.size; + file->wblock = 0; + file->rpos = 0; file->rblock = 0; - file->rindex = 0; - file->roff = 0; - - // TODO do this lazily in write? - // TODO cow the head i/d block - if (file->size < lfs->cfg->block_size) { - file->wblock = file->head; - } else { - int err = lfs_index_find(lfs, file->head, file->windex, - file->windex, &file->wblock); - if (err) { - return err; - } - } return 0; } @@ -1007,48 +966,29 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, lfs_size_t nsize = size; while (nsize > 0) { - lfs_off_t woff = file->size % lfs->cfg->block_size; - - if (file->size == 0) { - int err = lfs_alloc(lfs, &file->wblock); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, file->wblock); - if (err) { - return err; - } - - file->head = file->wblock; - file->windex = 0; - } else if (woff == 0) { - int err = lfs_alloc(lfs, &file->wblock); - if (err) { - return err; - } - - err = lfs_bd_erase(lfs, file->wblock); - if (err) { - return err; - } - - err = lfs_index_append(lfs, &file->head, - &file->windex, file->wblock); + if (!file->wblock || file->woff == lfs->cfg->block_size) { + int err = lfs_index_extend(lfs, file->head, file->wpos, + &file->wblock, &file->woff); if (err) { return err; } } - lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - woff); - int err = lfs_bd_prog(lfs, file->wblock, woff, diff, data); + lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->woff); + int err = lfs_bd_prog(lfs, file->wblock, file->woff, diff, data); if (err) { return err; } - file->size += diff; + file->wpos += diff; + file->woff += diff; data += diff; nsize -= diff; + + if (file->wpos > file->size) { + file->size = file->wpos; + file->head = file->wblock; + } } return size; @@ -1057,38 +997,31 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file, void *buffer, lfs_size_t size) { uint8_t *data = buffer; + size = lfs_min(size, file->size - file->rpos); lfs_size_t nsize = size; - while (nsize > 0 && file->roff < file->size) { - lfs_off_t roff = file->roff % lfs->cfg->block_size; - - // TODO cache index blocks - if (file->size < lfs->cfg->block_size) { - file->rblock = file->head; - } else if (roff == 0) { - int err = lfs_index_find(lfs, file->head, file->windex, - file->rindex, &file->rblock); + while (nsize > 0) { + if (!file->rblock || file->roff == lfs->cfg->block_size) { + int err = lfs_index_find(lfs, file->head, file->size, file->rpos, + &file->rblock, &file->roff); if (err) { return err; } - - file->rindex = lfs_indexnext(lfs, file->rindex); } - lfs_size_t diff = lfs_min( - lfs_min(nsize, file->size-file->roff), - lfs->cfg->block_size - roff); - int err = lfs_bd_read(lfs, file->rblock, roff, diff, data); + lfs_size_t diff = lfs_min(nsize, lfs->cfg->block_size - file->roff); + int err = lfs_bd_read(lfs, file->rblock, file->roff, diff, data); if (err) { return err; } + file->rpos += diff; file->roff += diff; data += diff; nsize -= diff; } - return size - nsize; + return size; } @@ -1292,7 +1225,7 @@ int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data) { } else { int err = lfs_index_traverse(lfs, file.entry.d.u.file.head, - lfs_indexfrom(lfs, file.entry.d.u.file.size), + file.entry.d.u.file.size, cb, data); if (err) { return err; diff --git a/lfs.h b/lfs.h index f1ca8df..1138057 100644 --- a/lfs.h +++ b/lfs.h @@ -125,13 +125,16 @@ typedef struct lfs_file { lfs_block_t head; lfs_size_t size; + lfs_off_t wpos; lfs_block_t wblock; - uint32_t windex; + lfs_off_t woff; + lfs_off_t rpos; lfs_block_t rblock; - uint32_t rindex; lfs_off_t roff; + int flags; + struct lfs_entry entry; } lfs_file_t;