2017-03-25 16:20:31 -05:00
|
|
|
/*
|
|
|
|
* lfs utility functions
|
|
|
|
*
|
2023-06-05 19:01:07 +08:00
|
|
|
* Copyright (c) 2022, The littlefs authors.
|
2018-06-21 11:35:57 -05:00
|
|
|
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
2017-03-25 16:20:31 -05:00
|
|
|
*/
|
|
|
|
#ifndef LFS_UTIL_H
|
|
|
|
#define LFS_UTIL_H
|
|
|
|
|
2018-02-22 13:34:10 -06:00
|
|
|
// Users can override lfs_util.h with their own configuration by defining
|
|
|
|
// LFS_CONFIG as a header file to include (-DLFS_CONFIG=lfs_config.h).
|
|
|
|
//
|
|
|
|
// If LFS_CONFIG is used, none of the default utils will be emitted and must be
|
2018-08-08 09:37:43 -05:00
|
|
|
// provided by the config file. To start, I would suggest copying lfs_util.h
|
|
|
|
// and modifying as needed.
|
2018-02-22 13:34:10 -06:00
|
|
|
#ifdef LFS_CONFIG
|
|
|
|
#define LFS_STRINGIZE(x) LFS_STRINGIZE2(x)
|
|
|
|
#define LFS_STRINGIZE2(x) #x
|
|
|
|
#include LFS_STRINGIZE(LFS_CONFIG)
|
|
|
|
#else
|
|
|
|
|
|
|
|
// System includes
|
2017-04-23 21:40:03 -05:00
|
|
|
#include <stdint.h>
|
2018-01-29 15:20:12 -06:00
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
2018-08-04 20:33:09 -05:00
|
|
|
#include <inttypes.h>
|
2018-01-29 15:20:12 -06:00
|
|
|
|
|
|
|
#ifndef LFS_NO_MALLOC
|
|
|
|
#include <stdlib.h>
|
|
|
|
#endif
|
|
|
|
#ifndef LFS_NO_ASSERT
|
|
|
|
#include <assert.h>
|
|
|
|
#endif
|
2019-05-31 04:40:19 -05:00
|
|
|
#if !defined(LFS_NO_DEBUG) || \
|
|
|
|
!defined(LFS_NO_WARN) || \
|
|
|
|
!defined(LFS_NO_ERROR) || \
|
|
|
|
defined(LFS_YES_TRACE)
|
2017-04-23 21:40:03 -05:00
|
|
|
#include <stdio.h>
|
2018-01-29 15:20:12 -06:00
|
|
|
#endif
|
|
|
|
|
2018-07-13 09:34:49 +02:00
|
|
|
#ifdef __cplusplus
|
|
|
|
extern "C"
|
|
|
|
{
|
|
|
|
#endif
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
|
|
|
|
// 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
|
2021-01-18 14:01:53 -06:00
|
|
|
#ifndef LFS_TRACE
|
2019-05-31 04:40:19 -05:00
|
|
|
#ifdef LFS_YES_TRACE
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_TRACE_(fmt, ...) \
|
2020-03-31 18:24:54 -05:00
|
|
|
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_TRACE(...) LFS_TRACE_(__VA_ARGS__, "")
|
2019-05-31 04:40:19 -05:00
|
|
|
#else
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_TRACE(...)
|
2019-05-31 04:40:19 -05:00
|
|
|
#endif
|
2021-01-18 14:01:53 -06:00
|
|
|
#endif
|
2019-05-31 04:40:19 -05:00
|
|
|
|
2021-01-18 14:01:53 -06:00
|
|
|
#ifndef LFS_DEBUG
|
2018-01-29 15:20:12 -06:00
|
|
|
#ifndef LFS_NO_DEBUG
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_DEBUG_(fmt, ...) \
|
2020-03-31 18:24:54 -05:00
|
|
|
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_DEBUG(...) LFS_DEBUG_(__VA_ARGS__, "")
|
2018-01-29 15:20:12 -06:00
|
|
|
#else
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_DEBUG(...)
|
2018-01-29 15:20:12 -06:00
|
|
|
#endif
|
2021-01-18 14:01:53 -06:00
|
|
|
#endif
|
2018-01-29 15:20:12 -06:00
|
|
|
|
2021-01-18 14:01:53 -06:00
|
|
|
#ifndef LFS_WARN
|
2018-01-29 15:20:12 -06:00
|
|
|
#ifndef LFS_NO_WARN
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_WARN_(fmt, ...) \
|
2020-03-31 18:24:54 -05:00
|
|
|
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_WARN(...) LFS_WARN_(__VA_ARGS__, "")
|
2018-01-29 15:20:12 -06:00
|
|
|
#else
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_WARN(...)
|
2018-01-29 15:20:12 -06:00
|
|
|
#endif
|
2021-01-18 14:01:53 -06:00
|
|
|
#endif
|
2018-01-29 15:20:12 -06:00
|
|
|
|
2021-01-18 14:01:53 -06:00
|
|
|
#ifndef LFS_ERROR
|
2018-01-29 15:20:12 -06:00
|
|
|
#ifndef LFS_NO_ERROR
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_ERROR_(fmt, ...) \
|
2020-03-31 18:24:54 -05:00
|
|
|
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_ERROR(...) LFS_ERROR_(__VA_ARGS__, "")
|
2018-01-29 15:20:12 -06:00
|
|
|
#else
|
2019-11-05 14:43:02 -06:00
|
|
|
#define LFS_ERROR(...)
|
2018-01-29 15:20:12 -06:00
|
|
|
#endif
|
2021-01-18 14:01:53 -06:00
|
|
|
#endif
|
2018-01-29 15:20:12 -06:00
|
|
|
|
|
|
|
// Runtime assertions
|
2021-01-18 14:01:53 -06:00
|
|
|
#ifndef LFS_ASSERT
|
2018-01-29 15:20:12 -06:00
|
|
|
#ifndef LFS_NO_ASSERT
|
|
|
|
#define LFS_ASSERT(test) assert(test)
|
|
|
|
#else
|
|
|
|
#define LFS_ASSERT(test)
|
|
|
|
#endif
|
2021-01-18 14:01:53 -06:00
|
|
|
#endif
|
2017-03-25 16:20:31 -05:00
|
|
|
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// 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
|
2017-03-25 16:20:31 -05:00
|
|
|
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
|
|
|
return (a > b) ? a : b;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
|
|
|
return (a < b) ? a : b;
|
|
|
|
}
|
|
|
|
|
2018-08-08 09:37:43 -05:00
|
|
|
// Align to nearest multiple of a size
|
|
|
|
static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) {
|
|
|
|
return a - (a % alignment);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t lfs_alignup(uint32_t a, uint32_t alignment) {
|
|
|
|
return lfs_aligndown(a + alignment-1, alignment);
|
|
|
|
}
|
|
|
|
|
2020-01-02 13:46:07 -08:00
|
|
|
// Find the smallest power of 2 greater than or equal to a
|
2017-03-25 16:20:31 -05:00
|
|
|
static inline uint32_t lfs_npw2(uint32_t a) {
|
2018-01-29 15:20:12 -06:00
|
|
|
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
2017-03-25 16:20:31 -05:00
|
|
|
return 32 - __builtin_clz(a-1);
|
2018-01-28 09:56:06 -06:00
|
|
|
#else
|
|
|
|
uint32_t r = 0;
|
|
|
|
uint32_t s;
|
|
|
|
a -= 1;
|
|
|
|
s = (a > 0xffff) << 4; a >>= s; r |= s;
|
|
|
|
s = (a > 0xff ) << 3; a >>= s; r |= s;
|
|
|
|
s = (a > 0xf ) << 2; a >>= s; r |= s;
|
|
|
|
s = (a > 0x3 ) << 1; a >>= s; r |= s;
|
|
|
|
return (r | (a >> 1)) + 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Count the number of trailing binary zeros in a
|
|
|
|
// lfs_ctz(0) may be undefined
|
2018-01-28 09:56:06 -06:00
|
|
|
static inline uint32_t lfs_ctz(uint32_t a) {
|
2018-01-29 15:20:12 -06:00
|
|
|
#if !defined(LFS_NO_INTRINSICS) && defined(__GNUC__)
|
2018-01-28 09:56:06 -06:00
|
|
|
return __builtin_ctz(a);
|
|
|
|
#else
|
|
|
|
return lfs_npw2((a & -a) + 1) - 1;
|
|
|
|
#endif
|
2017-03-25 16:20:31 -05:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Count the number of binary ones in a
|
2017-10-16 19:08:47 -05:00
|
|
|
static inline uint32_t lfs_popc(uint32_t a) {
|
2018-01-29 15:20:12 -06:00
|
|
|
#if !defined(LFS_NO_INTRINSICS) && (defined(__GNUC__) || defined(__CC_ARM))
|
2017-10-16 19:08:47 -05:00
|
|
|
return __builtin_popcount(a);
|
2018-01-28 09:56:06 -06:00
|
|
|
#else
|
|
|
|
a = a - ((a >> 1) & 0x55555555);
|
|
|
|
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
|
|
|
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
|
|
|
#endif
|
2017-10-16 19:08:47 -05:00
|
|
|
}
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Find the sequence comparison of a and b, this is the distance
|
|
|
|
// between a and b ignoring overflow
|
2017-03-25 16:20:31 -05:00
|
|
|
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
|
|
|
return (int)(unsigned)(a - b);
|
|
|
|
}
|
|
|
|
|
2018-08-01 18:10:24 -05:00
|
|
|
// Convert between 32-bit little-endian and native order
|
2018-02-02 05:58:43 -06:00
|
|
|
static inline uint32_t lfs_fromle32(uint32_t a) {
|
2024-04-17 19:05:36 +08:00
|
|
|
#if (defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
2019-05-14 18:18:29 +01:00
|
|
|
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
2024-04-17 19:05:36 +08:00
|
|
|
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
2018-02-02 05:58:43 -06:00
|
|
|
return a;
|
2018-01-29 15:20:12 -06:00
|
|
|
#elif !defined(LFS_NO_INTRINSICS) && ( \
|
2019-05-14 18:18:29 +01:00
|
|
|
(defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
|
|
|
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
|
|
|
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
|
2018-02-02 05:58:43 -06:00
|
|
|
return __builtin_bswap32(a);
|
|
|
|
#else
|
|
|
|
return (((uint8_t*)&a)[0] << 0) |
|
|
|
|
(((uint8_t*)&a)[1] << 8) |
|
|
|
|
(((uint8_t*)&a)[2] << 16) |
|
|
|
|
(((uint8_t*)&a)[3] << 24);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t lfs_tole32(uint32_t a) {
|
|
|
|
return lfs_fromle32(a);
|
|
|
|
}
|
|
|
|
|
Tweaked tag endianness to catch power-loss after <1 word is written
There was an interesting subtlety with the existing layout of tags that
could become a problem in the future. Basically, littlefs avoids writing to
any region of storage it is not absolutely sure has been erased
beforehand. This is a part of limiting the number of assumptions about
storage. It's possible a storage technology can't support writes without
erases in a way that is undetectable at write time (Maybe changing a bit
without an erase decreases the longevity of the information stored on
the bit).
But the existing layout had a very tiny corner case where this wasn't
true. Consider the location of the valid bit in the tag struct:
[1|--- 31 ---]
^--- valid bit
The responsibility of this bit is to indicate if an attempt has been
made to write the following commit. If it is not set (the specific value
is dependent on a previous read and identified by the preceeding commit),
the assumption is that it is safe to write to the next region because it
has been erased previously. If it is set, we check if the next commit is
valid, if it isn't (because of CRC failure, likely due to power-loss), we
discard the commit. But because an attempt has been made to write to
that storage, we must then do a compaction to move to the other block in
the metadata-pair.
This plan looks good on paper, but what does it look like on storage?
The problem is that words in littlefs are in little-endian. So on
storage the tag actually looks like this:
[- 8 -|- 8 -|- 8 -|1|- 7 -]
^-- valid bit
This means that we don't actually set the valid bit before writing the
tag! We write the lower bytes first. If we lose power, we may have
written 3 bytes without this fact being detectable.
We could restructure the tag structure to store the valid bit lower,
however because none of the fields are 7 bits, this would make the
extraction more costly, and we then lose the ability to check this
valid bit with a sign comparison.
The simple solution is to just store the tag in big-endian. A small
benefit is that this will actually have a negative code cost on
big-endian machines.
This mixture of endiannesses is frustrating, however it is a pragmatic
solution with only a 20-byte code size cost.
2018-10-22 16:42:30 -05:00
|
|
|
// Convert between 32-bit big-endian and native order
|
|
|
|
static inline uint32_t lfs_frombe32(uint32_t a) {
|
|
|
|
#if !defined(LFS_NO_INTRINSICS) && ( \
|
2019-05-14 18:18:29 +01:00
|
|
|
(defined( BYTE_ORDER ) && defined( ORDER_LITTLE_ENDIAN ) && BYTE_ORDER == ORDER_LITTLE_ENDIAN ) || \
|
|
|
|
(defined(__BYTE_ORDER ) && defined(__ORDER_LITTLE_ENDIAN ) && __BYTE_ORDER == __ORDER_LITTLE_ENDIAN ) || \
|
|
|
|
(defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
|
Tweaked tag endianness to catch power-loss after <1 word is written
There was an interesting subtlety with the existing layout of tags that
could become a problem in the future. Basically, littlefs avoids writing to
any region of storage it is not absolutely sure has been erased
beforehand. This is a part of limiting the number of assumptions about
storage. It's possible a storage technology can't support writes without
erases in a way that is undetectable at write time (Maybe changing a bit
without an erase decreases the longevity of the information stored on
the bit).
But the existing layout had a very tiny corner case where this wasn't
true. Consider the location of the valid bit in the tag struct:
[1|--- 31 ---]
^--- valid bit
The responsibility of this bit is to indicate if an attempt has been
made to write the following commit. If it is not set (the specific value
is dependent on a previous read and identified by the preceeding commit),
the assumption is that it is safe to write to the next region because it
has been erased previously. If it is set, we check if the next commit is
valid, if it isn't (because of CRC failure, likely due to power-loss), we
discard the commit. But because an attempt has been made to write to
that storage, we must then do a compaction to move to the other block in
the metadata-pair.
This plan looks good on paper, but what does it look like on storage?
The problem is that words in littlefs are in little-endian. So on
storage the tag actually looks like this:
[- 8 -|- 8 -|- 8 -|1|- 7 -]
^-- valid bit
This means that we don't actually set the valid bit before writing the
tag! We write the lower bytes first. If we lose power, we may have
written 3 bytes without this fact being detectable.
We could restructure the tag structure to store the valid bit lower,
however because none of the fields are 7 bits, this would make the
extraction more costly, and we then lose the ability to check this
valid bit with a sign comparison.
The simple solution is to just store the tag in big-endian. A small
benefit is that this will actually have a negative code cost on
big-endian machines.
This mixture of endiannesses is frustrating, however it is a pragmatic
solution with only a 20-byte code size cost.
2018-10-22 16:42:30 -05:00
|
|
|
return __builtin_bswap32(a);
|
2024-04-17 19:05:36 +08:00
|
|
|
#elif (defined( BYTE_ORDER ) && defined( ORDER_BIG_ENDIAN ) && BYTE_ORDER == ORDER_BIG_ENDIAN ) || \
|
2019-05-14 18:18:29 +01:00
|
|
|
(defined(__BYTE_ORDER ) && defined(__ORDER_BIG_ENDIAN ) && __BYTE_ORDER == __ORDER_BIG_ENDIAN ) || \
|
2024-04-17 19:05:36 +08:00
|
|
|
(defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
Tweaked tag endianness to catch power-loss after <1 word is written
There was an interesting subtlety with the existing layout of tags that
could become a problem in the future. Basically, littlefs avoids writing to
any region of storage it is not absolutely sure has been erased
beforehand. This is a part of limiting the number of assumptions about
storage. It's possible a storage technology can't support writes without
erases in a way that is undetectable at write time (Maybe changing a bit
without an erase decreases the longevity of the information stored on
the bit).
But the existing layout had a very tiny corner case where this wasn't
true. Consider the location of the valid bit in the tag struct:
[1|--- 31 ---]
^--- valid bit
The responsibility of this bit is to indicate if an attempt has been
made to write the following commit. If it is not set (the specific value
is dependent on a previous read and identified by the preceeding commit),
the assumption is that it is safe to write to the next region because it
has been erased previously. If it is set, we check if the next commit is
valid, if it isn't (because of CRC failure, likely due to power-loss), we
discard the commit. But because an attempt has been made to write to
that storage, we must then do a compaction to move to the other block in
the metadata-pair.
This plan looks good on paper, but what does it look like on storage?
The problem is that words in littlefs are in little-endian. So on
storage the tag actually looks like this:
[- 8 -|- 8 -|- 8 -|1|- 7 -]
^-- valid bit
This means that we don't actually set the valid bit before writing the
tag! We write the lower bytes first. If we lose power, we may have
written 3 bytes without this fact being detectable.
We could restructure the tag structure to store the valid bit lower,
however because none of the fields are 7 bits, this would make the
extraction more costly, and we then lose the ability to check this
valid bit with a sign comparison.
The simple solution is to just store the tag in big-endian. A small
benefit is that this will actually have a negative code cost on
big-endian machines.
This mixture of endiannesses is frustrating, however it is a pragmatic
solution with only a 20-byte code size cost.
2018-10-22 16:42:30 -05:00
|
|
|
return a;
|
|
|
|
#else
|
|
|
|
return (((uint8_t*)&a)[0] << 24) |
|
|
|
|
(((uint8_t*)&a)[1] << 16) |
|
|
|
|
(((uint8_t*)&a)[2] << 8) |
|
|
|
|
(((uint8_t*)&a)[3] << 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline uint32_t lfs_tobe32(uint32_t a) {
|
|
|
|
return lfs_frombe32(a);
|
|
|
|
}
|
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Calculate CRC-32 with polynomial = 0x04c11db7
|
2018-08-07 14:59:44 -05:00
|
|
|
uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size);
|
2017-04-23 21:40:03 -05:00
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Allocate memory, only used if buffers are not provided to littlefs
|
2019-04-09 18:07:44 -05:00
|
|
|
// Note, memory must be 64-bit aligned
|
2018-01-29 15:20:12 -06:00
|
|
|
static inline void *lfs_malloc(size_t size) {
|
|
|
|
#ifndef LFS_NO_MALLOC
|
|
|
|
return malloc(size);
|
|
|
|
#else
|
2018-06-08 11:24:27 +10:00
|
|
|
(void)size;
|
2018-01-29 15:20:12 -06:00
|
|
|
return NULL;
|
|
|
|
#endif
|
|
|
|
}
|
2017-04-23 21:40:03 -05:00
|
|
|
|
2018-01-29 15:20:12 -06:00
|
|
|
// Deallocate memory, only used if buffers are not provided to littlefs
|
|
|
|
static inline void lfs_free(void *p) {
|
|
|
|
#ifndef LFS_NO_MALLOC
|
|
|
|
free(p);
|
2018-06-08 11:24:27 +10:00
|
|
|
#else
|
|
|
|
(void)p;
|
2018-01-29 15:20:12 -06:00
|
|
|
#endif
|
|
|
|
}
|
2017-03-25 16:20:31 -05:00
|
|
|
|
|
|
|
|
2018-07-13 09:34:49 +02:00
|
|
|
#ifdef __cplusplus
|
|
|
|
} /* extern "C" */
|
|
|
|
#endif
|
|
|
|
|
2017-03-25 16:20:31 -05:00
|
|
|
#endif
|
2018-02-22 13:34:10 -06:00
|
|
|
#endif
|