Skip to content

Commit

Permalink
Added more configurable utils
Browse files Browse the repository at this point in the history
Note: It's still expected to modify lfs_utils.h when porting littlefs
to a new target/system. There's just too much room for system-specific
improvements, such as taking advantage of CRC hardware.

Rather, encouraging modification of lfs_util.h and making it easy to
modify and debug should result in better integration with the consuming
systems.

This just adds a bunch of quality-of-life improvements that should help
development and integration in littlefs.

- Macros that require no side-effects are all-caps
- System includes are only brought in when needed
- Malloc/free wrappers
- LFS_NO_* checks for quickly disabling things at the command line
- At least a little-bit more docs
  • Loading branch information
geky committed Feb 19, 2018
1 parent a0a55fb commit a3fd2d4
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 43 deletions.
52 changes: 24 additions & 28 deletions lfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@
#include "lfs.h"
#include "lfs_util.h"

#include <string.h>
#include <stdlib.h>
#include <assert.h>


/// Caching block device operations ///
static int lfs_cache_read(lfs_t *lfs, lfs_cache_t *rcache,
const lfs_cache_t *pcache, lfs_block_t block,
lfs_off_t off, void *buffer, lfs_size_t size) {
uint8_t *data = buffer;
assert(block < lfs->cfg->block_count);
LFS_ASSERT(block < lfs->cfg->block_count);

while (size > 0) {
if (pcache && block == pcache->block && off >= pcache->off &&
Expand Down Expand Up @@ -153,7 +149,7 @@ static int lfs_cache_prog(lfs_t *lfs, lfs_cache_t *pcache,
lfs_cache_t *rcache, lfs_block_t block,
lfs_off_t off, const void *buffer, lfs_size_t size) {
const uint8_t *data = buffer;
assert(block < lfs->cfg->block_count);
LFS_ASSERT(block < lfs->cfg->block_count);

while (size > 0) {
if (block == pcache->block && off >= pcache->off &&
Expand All @@ -180,7 +176,7 @@ static int lfs_cache_prog(lfs_t *lfs, lfs_cache_t *pcache,

// pcache must have been flushed, either by programming and
// entire block or manually flushing the pcache
assert(pcache->block == 0xffffffff);
LFS_ASSERT(pcache->block == 0xffffffff);

if (off % lfs->cfg->prog_size == 0 &&
size >= lfs->cfg->prog_size) {
Expand Down Expand Up @@ -1130,7 +1126,7 @@ static int lfs_ctz_find(lfs_t *lfs,
return err;
}

assert(head >= 2 && head <= lfs->cfg->block_count);
LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
current -= 1 << skip;
}

Expand All @@ -1150,7 +1146,7 @@ static int lfs_ctz_extend(lfs_t *lfs,
if (err) {
return err;
}
assert(nblock >= 2 && nblock <= lfs->cfg->block_count);
LFS_ASSERT(nblock >= 2 && nblock <= lfs->cfg->block_count);

if (true) {
err = lfs_bd_erase(lfs, nblock);
Expand Down Expand Up @@ -1221,7 +1217,7 @@ static int lfs_ctz_extend(lfs_t *lfs,
}
}

assert(head >= 2 && head <= lfs->cfg->block_count);
LFS_ASSERT(head >= 2 && head <= lfs->cfg->block_count);
}

*block = nblock;
Expand Down Expand Up @@ -1347,12 +1343,12 @@ int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
if (lfs->cfg->file_buffer) {
file->cache.buffer = lfs->cfg->file_buffer;
} else if ((file->flags & 3) == LFS_O_RDONLY) {
file->cache.buffer = malloc(lfs->cfg->read_size);
file->cache.buffer = lfs_malloc(lfs->cfg->read_size);
if (!file->cache.buffer) {
return LFS_ERR_NOMEM;
}
} else {
file->cache.buffer = malloc(lfs->cfg->prog_size);
file->cache.buffer = lfs_malloc(lfs->cfg->prog_size);
if (!file->cache.buffer) {
return LFS_ERR_NOMEM;
}
Expand All @@ -1378,7 +1374,7 @@ int lfs_file_close(lfs_t *lfs, lfs_file_t *file) {

// clean up memory
if (!lfs->cfg->file_buffer) {
free(file->cache.buffer);
lfs_free(file->cache.buffer);
}

return err;
Expand Down Expand Up @@ -1527,7 +1523,7 @@ int lfs_file_sync(lfs_t *lfs, lfs_file_t *file) {
return err;
}

assert(entry.d.type == LFS_TYPE_REG);
LFS_ASSERT(entry.d.type == LFS_TYPE_REG);
entry.d.u.file.head = file->head;
entry.d.u.file.size = file->size;

Expand Down Expand Up @@ -1753,7 +1749,7 @@ int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) {

// flush+seek if not already at end
if (file->pos != oldsize) {
int err = lfs_file_seek(lfs, file, 0, SEEK_END);
int err = lfs_file_seek(lfs, file, 0, LFS_SEEK_END);
if (err < 0) {
return err;
}
Expand Down Expand Up @@ -1886,7 +1882,7 @@ int lfs_remove(lfs_t *lfs, const char *path) {
return res;
}

assert(res); // must have pred
LFS_ASSERT(res); // must have pred
cwd.d.tail[0] = dir.d.tail[0];
cwd.d.tail[1] = dir.d.tail[1];

Expand Down Expand Up @@ -2003,7 +1999,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) {
return res;
}

assert(res); // must have pred
LFS_ASSERT(res); // must have pred
newcwd.d.tail[0] = dir.d.tail[0];
newcwd.d.tail[1] = dir.d.tail[1];

Expand All @@ -2026,7 +2022,7 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
if (lfs->cfg->read_buffer) {
lfs->rcache.buffer = lfs->cfg->read_buffer;
} else {
lfs->rcache.buffer = malloc(lfs->cfg->read_size);
lfs->rcache.buffer = lfs_malloc(lfs->cfg->read_size);
if (!lfs->rcache.buffer) {
return LFS_ERR_NOMEM;
}
Expand All @@ -2037,30 +2033,30 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
if (lfs->cfg->prog_buffer) {
lfs->pcache.buffer = lfs->cfg->prog_buffer;
} else {
lfs->pcache.buffer = malloc(lfs->cfg->prog_size);
lfs->pcache.buffer = lfs_malloc(lfs->cfg->prog_size);
if (!lfs->pcache.buffer) {
return LFS_ERR_NOMEM;
}
}

// setup lookahead, round down to nearest 32-bits
assert(lfs->cfg->lookahead % 32 == 0);
assert(lfs->cfg->lookahead > 0);
LFS_ASSERT(lfs->cfg->lookahead % 32 == 0);
LFS_ASSERT(lfs->cfg->lookahead > 0);
if (lfs->cfg->lookahead_buffer) {
lfs->free.buffer = lfs->cfg->lookahead_buffer;
} else {
lfs->free.buffer = malloc(lfs->cfg->lookahead/8);
lfs->free.buffer = lfs_malloc(lfs->cfg->lookahead/8);
if (!lfs->free.buffer) {
return LFS_ERR_NOMEM;
}
}

// check that program and read sizes are multiples of the block size
assert(lfs->cfg->prog_size % lfs->cfg->read_size == 0);
assert(lfs->cfg->block_size % lfs->cfg->prog_size == 0);
LFS_ASSERT(lfs->cfg->prog_size % lfs->cfg->read_size == 0);
LFS_ASSERT(lfs->cfg->block_size % lfs->cfg->prog_size == 0);

// check that the block size is large enough to fit ctz pointers
assert(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
LFS_ASSERT(4*lfs_npw2(0xffffffff / (lfs->cfg->block_size-2*4))
<= lfs->cfg->block_size);

// setup default state
Expand All @@ -2076,15 +2072,15 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) {
static int lfs_deinit(lfs_t *lfs) {
// free allocated memory
if (!lfs->cfg->read_buffer) {
free(lfs->rcache.buffer);
lfs_free(lfs->rcache.buffer);
}

if (!lfs->cfg->prog_buffer) {
free(lfs->pcache.buffer);
lfs_free(lfs->pcache.buffer);
}

if (!lfs->cfg->lookahead_buffer) {
free(lfs->free.buffer);
lfs_free(lfs->free.buffer);
}

return 0;
Expand Down
2 changes: 1 addition & 1 deletion lfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
// Software library version
// Major (top-nibble), incremented on backwards incompatible changes
// Minor (bottom-nibble), incremented on feature additions
#define LFS_VERSION 0x00010002
#define LFS_VERSION 0x00010003
#define LFS_VERSION_MAJOR (0xffff & (LFS_VERSION >> 16))
#define LFS_VERSION_MINOR (0xffff & (LFS_VERSION >> 0))

Expand Down
92 changes: 78 additions & 14 deletions lfs_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,60 @@
#ifndef LFS_UTIL_H
#define LFS_UTIL_H

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#ifndef LFS_NO_MALLOC
#include <stdlib.h>
#endif
#ifndef LFS_NO_ASSERT
#include <assert.h>
#endif
#if !defined(LFS_NO_DEBUG) || !defined(LFS_NO_WARN) || !defined(LFS_NO_ERROR)
#include <stdio.h>
#endif


// Macros, may be replaced by system specific wrappers. Arguments to these
// macros must not have side-effects as the macros can be removed for a smaller
// code footprint

// Logging functions
#ifndef LFS_NO_DEBUG
#define LFS_DEBUG(fmt, ...) \
printf("lfs debug:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_DEBUG(fmt, ...)
#endif

#ifndef LFS_NO_WARN
#define LFS_WARN(fmt, ...) \
printf("lfs warn:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_WARN(fmt, ...)
#endif

#ifndef LFS_NO_ERROR
#define LFS_ERROR(fmt, ...) \
printf("lfs error:%d: " fmt "\n", __LINE__, __VA_ARGS__)
#else
#define LFS_ERROR(fmt, ...)
#endif

// Runtime assertions
#ifndef LFS_NO_ASSERT
#define LFS_ASSERT(test) assert(test)
#else
#define LFS_ASSERT(test)
#endif


// Builtin functions, these may be replaced by more
// efficient implementations in the system
// Builtin functions, these may be replaced by more efficient
// toolchain-specific implementations. LFS_NO_INTRINSICS falls back to a more
// expensive basic C implementation for debugging purposes

// Min/max functions for unsigned 32-bit numbers
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
return (a > b) ? a : b;
}
Expand All @@ -33,8 +80,9 @@ static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
return (a < b) ? a : b;
}

// Find the next smallest power of 2 less than or equal to a
static inline uint32_t lfs_npw2(uint32_t a) {
#if defined(__GNUC__) || defined(__CC_ARM)
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return 32 - __builtin_clz(a-1);
#else
uint32_t r = 0;
Expand All @@ -48,16 +96,19 @@ static inline uint32_t lfs_npw2(uint32_t a) {
#endif
}

// Count the number of trailing binary zeros in a
// lfs_ctz(0) may be undefined
static inline uint32_t lfs_ctz(uint32_t a) {
#if defined(__GNUC__)
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
return __builtin_ctz(a);
#else
return lfs_npw2((a & -a) + 1) - 1;
#endif
}

// Count the number of binary ones in a
static inline uint32_t lfs_popc(uint32_t a) {
#if defined(__GNUC__) || defined(__CC_ARM)
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
return __builtin_popcount(a);
#else
a = a - ((a >> 1) & 0x55555555);
Expand All @@ -66,17 +117,20 @@ static inline uint32_t lfs_popc(uint32_t a) {
#endif
}

// Find the sequence comparison of a and b, this is the distance
// between a and b ignoring overflow
static inline int lfs_scmp(uint32_t a, uint32_t b) {
return (int)(unsigned)(a - b);
}

// Convert from 32-bit little-endian to native order
static inline uint32_t lfs_fromle32(uint32_t a) {
#if ( \
#if !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
return a;
#elif ( \
#elif !defined(LFS_NO_INTRINSICS) && ( \
(defined( BYTE_ORDER ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
Expand All @@ -89,19 +143,29 @@ static inline uint32_t lfs_fromle32(uint32_t a) {
#endif
}

// Convert to 32-bit little-endian from native order
static inline uint32_t lfs_tole32(uint32_t a) {
return lfs_fromle32(a);
}

// CRC-32 with polynomial = 0x04c11db7
// Calculate CRC-32 with polynomial = 0x04c11db7
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);

// Allocate memory, only used if buffers are not provided to littlefs
static inline void *lfs_malloc(size_t size) {
#ifndef LFS_NO_MALLOC
return malloc(size);
#else
return NULL;
#endif
}

// Logging functions, these may be replaced by system-specific
// logging functions
#define LFS_DEBUG(fmt, ...) printf("lfs debug: " fmt "\n", __VA_ARGS__)
#define LFS_WARN(fmt, ...) printf("lfs warn: " fmt "\n", __VA_ARGS__)
#define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__)
// Deallocate memory, only used if buffers are not provided to littlefs
static inline void lfs_free(void *p) {
#ifndef LFS_NO_MALLOC
free(p);
#endif
}


#endif

0 comments on commit a3fd2d4

Please sign in to comment.