Переглянути джерело

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.
Christopher Haster 8 роки тому
батько
коміт
a4e9132d7f
2 змінених файлів з 156 додано та 220 видалено
  1. 151 218
      lfs.c
  2. 5 2
      lfs.h

+ 151 - 218
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;

+ 5 - 2
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;