b3ab83d5b5
The main change is adding LFS3_M_REVPERTURB, which will be necessary for
preerase allocations, but I got distracted and ended up giving the
revision count subsystem a bit of a refactor.
Main changes:
- Added LFS3_M_REVPERTURB, which ensures the leading bit in the
revision count changes after each allocation/relocation/compaction.
This is generally optional, but will be required for preerase
allocations. Our ecksum system is only reliable if we ensure at least
one bit changes, otherwise the chance of ecksum collision is very
high.
The downside of LFS3_M_REVPERTURB is that we need to read the contents
of the new block to figure out what the bit should change to. Probably
a minimal cost in the system, but still a good reason to make the
behavior optional.
Does LFS3_M_REVPERTURB have any use outside of preerased allocation?
I'm not sure. Maybe it has some niche use reducing the chance of bd
ECC collisions?
- Dropped LFS3_M_REVDBG, but adding low-effort debug bits that are
always enabled.
Making LFS3_M_REVDBG conditional was probably overkill. The flag
checks probably cost more than the actual debug bits when enabled.
Instead, replaced with a simpler, low-effort debug bit system, where
we only set the debug bits during mdir allocation/relocation. These
bits shouldn't change during normal compaction, but we _don't_
introduce debug bits if mounting a filesystem from a driver without
these debug bits.
- Restricted recycle counter to at most 20-bits to make space for
things. This ensures perturb/debug bits don't get overwritten (though
we really only care about perturb bits).
2^20 (~1M) recycles is probably enough for any device littlefs will
run on, especially considering the recycle_count should probably be
several orders of magnitude smaller than the device's expected erase
cycles.
Worst case this can always be increased in the future without
backwards incompatible changes. The only hard requirement for revision
counts is that the full 32-bits are comparable.
- Simplified lfs3_rev_inc and friends, and moved most of the
disk-dependent revision count stuff down into lfs3_rbyd appendrev.
This deduplicates the messy revision count handling in
lfs3_btree_commit_.
Though note the implicit lfs3_rbyd_appendrev now defaults to writing
the btree debug bits ('b'). A bit of a hack, but works for littlefs.
Here's the resulting encoding:
vvvv---- -------- -------- -ddddddd
vvvvrrrr rrrrrr-- -------- -ddddddd
vvvvrrrr rrrrrrnn nnnnnnnn pddddddd
'-.''----.----''----.----' ^'--.--'
'------|----------|------|---|---- 4-bit relocation revision
'----------|------|---|---- recycle-bits recycle counter
'------|---|---- pseudorandom noise (if revnoise)
'---|---- perturb bit (if revperturb)
'---- low-effort debug bits
11-1--- - h = mroot anchor
11-11-1 - m = mdir
11---1- - b = btree node
Note we store revision counts as le32s, so the perturb bit should end up
as the leading bit in the first byte.
Costs a bit more code (mostly because the debug bits are now
unconditional, even if low-effort), but simplifies the codebase:
code stack ctx
before: 35124 2136 660
after: 35144 (+0.1%) 2136 (+0.0%) 660 (+0.0%)
after+yesrevperturb: 35192 (+0.2%) 2136 (+0.0%) 660 (+0.0%)
code stack ctx
gbmap+np before: 38252 2144 776
gbmap+np after: 38272 (+0.1%) 2144 (+0.0%) 776 (+0.0%)
gbmap+np after+yrp: 38328 (+0.2%) 2144 (+0.0%) 776 (+0.0%)
code stack ctx
gbmap+yp before: 38832 2168 796
gbmap+yp after: 38852 (+0.1%) 2168 (+0.0%) 796 (+0.0%)
gbmap+yp after+yrp: 38908 (+0.2%) 2168 (+0.0%) 796 (+0.0%)
801 lines
18 KiB
C
801 lines
18 KiB
C
/*
|
|
* lfs3 utility functions
|
|
*
|
|
* Copyright (c) 2022, The littlefs authors.
|
|
* Copyright (c) 2017, Arm Limited. All rights reserved.
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
#ifndef LFS3_UTIL_H
|
|
#define LFS3_UTIL_H
|
|
|
|
// Users can override lfs3_util.h with their own configuration by defining
|
|
// LFS3_CFG as a header file to include (-DLFS3_CFG=my_cfg.h).
|
|
//
|
|
// If LFS3_CFG is used, none of the default utils will be emitted and must be
|
|
// provided by the config file. To start, I would suggest copying lfs3_util.h
|
|
// and modifying as needed.
|
|
#ifdef LFS3_CFG
|
|
#define LFS3_STRINGIZE(x) LFS3_STRINGIZE2(x)
|
|
#define LFS3_STRINGIZE2(x) #x
|
|
#include LFS3_STRINGIZE(LFS3_CFG)
|
|
#else
|
|
|
|
|
|
// Some convenient macro aliases
|
|
// TODO move these to something like lfs3_cfg.h?
|
|
|
|
// LFS3_BIGGEST enables all opt-in features
|
|
#ifdef LFS3_BIGGEST
|
|
#ifndef LFS3_REVPERTURB
|
|
#define LFS3_REVPERTURB
|
|
#endif
|
|
#ifndef LFS3_REVNOISE
|
|
#define LFS3_REVNOISE
|
|
#endif
|
|
#ifndef LFS3_CKPROGS
|
|
#define LFS3_CKPROGS
|
|
#endif
|
|
#ifndef LFS3_CKFETCHES
|
|
#define LFS3_CKFETCHES
|
|
#endif
|
|
#ifndef LFS3_CKMETAPARITY
|
|
#define LFS3_CKMETAPARITY
|
|
#endif
|
|
#ifndef LFS3_CKDATACKSUMS
|
|
#define LFS3_CKDATACKSUMS
|
|
#endif
|
|
#ifndef LFS3_GC
|
|
#define LFS3_GC
|
|
#endif
|
|
#ifndef LFS3_GBMAP
|
|
#define LFS3_GBMAP
|
|
#endif
|
|
#ifndef LFS3_BLEAFCACHE
|
|
#define LFS3_BLEAFCACHE
|
|
#endif
|
|
#endif
|
|
|
|
// LFS3_YES_* variants imply the relevant LFS3_* macro
|
|
#ifdef LFS3_YES_RDONLY
|
|
#define LFS3_RDONLY
|
|
#endif
|
|
#ifdef LFS3_YES_REVPERTURB
|
|
#define LFS3_REVPERTURB
|
|
#endif
|
|
#ifdef LFS3_YES_REVNOISE
|
|
#define LFS3_REVNOISE
|
|
#endif
|
|
#ifdef LFS3_YES_CKPROGS
|
|
#define LFS3_CKPROGS
|
|
#endif
|
|
#ifdef LFS3_YES_CKFETCHES
|
|
#define LFS3_CKFETCHES
|
|
#endif
|
|
#ifdef LFS3_YES_CKMETAPARITY
|
|
#define LFS3_CKMETAPARITY
|
|
#endif
|
|
#ifdef LFS3_YES_CKDATACKSUMS
|
|
#define LFS3_CKDATACKSUMS
|
|
#endif
|
|
#ifdef LFS3_YES_GC
|
|
#define LFS3_GC
|
|
#endif
|
|
#ifdef LFS3_YES_GBMAP
|
|
#define LFS3_GBMAP
|
|
#endif
|
|
#ifdef LFS3_YES_BLEAFCACHE
|
|
#define LFS3_BLEAFCACHE
|
|
#endif
|
|
|
|
// LFS3_NO_LOG disables all logging macros
|
|
#ifdef LFS3_NO_LOG
|
|
#ifndef LFS3_NO_DEBUG
|
|
#define LFS3_NO_DEBUG
|
|
#endif
|
|
#ifndef LFS3_NO_INFO
|
|
#define LFS3_NO_INFO
|
|
#endif
|
|
#ifndef LFS3_NO_WARN
|
|
#define LFS3_NO_WARN
|
|
#endif
|
|
#ifndef LFS3_NO_ERROR
|
|
#define LFS3_NO_ERROR
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// System includes
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <sys/types.h>
|
|
#include <inttypes.h>
|
|
#ifndef LFS3_NO_STRINGH
|
|
#include <string.h>
|
|
#endif
|
|
#ifndef LFS3_NO_MALLOC
|
|
#include <stdlib.h>
|
|
#endif
|
|
#ifndef LFS3_NO_ASSERT
|
|
#include <assert.h>
|
|
#endif
|
|
#if !defined(LFS3_NO_DEBUG) \
|
|
|| !defined(LFS3_NO_INFO) \
|
|
|| !defined(LFS3_NO_WARN) \
|
|
|| !defined(LFS3_NO_ERROR) \
|
|
|| defined(LFS3_YES_TRACE)
|
|
#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 LFS3_TRACE
|
|
#ifdef LFS3_YES_TRACE
|
|
#define LFS3_TRACE_(fmt, ...) \
|
|
printf("%s:%d:trace: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
#define LFS3_TRACE(...) LFS3_TRACE_(__VA_ARGS__, "")
|
|
#else
|
|
#define LFS3_TRACE(...)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef LFS3_DEBUG
|
|
#ifndef LFS3_NO_DEBUG
|
|
#define LFS3_DEBUG_(fmt, ...) \
|
|
printf("%s:%d:debug: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
#define LFS3_DEBUG(...) LFS3_DEBUG_(__VA_ARGS__, "")
|
|
#else
|
|
#define LFS3_DEBUG(...)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef LFS3_INFO
|
|
#ifndef LFS3_NO_INFO
|
|
#define LFS3_INFO_(fmt, ...) \
|
|
printf("%s:%d:info: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
#define LFS3_INFO(...) LFS3_INFO_(__VA_ARGS__, "")
|
|
#else
|
|
#define LFS3_INFO(...)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef LFS3_WARN
|
|
#ifndef LFS3_NO_WARN
|
|
#define LFS3_WARN_(fmt, ...) \
|
|
printf("%s:%d:warn: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
#define LFS3_WARN(...) LFS3_WARN_(__VA_ARGS__, "")
|
|
#else
|
|
#define LFS3_WARN(...)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef LFS3_ERROR
|
|
#ifndef LFS3_NO_ERROR
|
|
#define LFS3_ERROR_(fmt, ...) \
|
|
printf("%s:%d:error: " fmt "%s\n", __FILE__, __LINE__, __VA_ARGS__)
|
|
#define LFS3_ERROR(...) LFS3_ERROR_(__VA_ARGS__, "")
|
|
#else
|
|
#define LFS3_ERROR(...)
|
|
#endif
|
|
#endif
|
|
|
|
// Runtime assertions
|
|
#ifndef LFS3_ASSERT
|
|
#ifndef LFS3_NO_ASSERT
|
|
#define LFS3_ASSERT(test) assert(test)
|
|
#else
|
|
#define LFS3_ASSERT(test)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef LFS3_UNREACHABLE
|
|
#ifndef LFS3_NO_ASSERT
|
|
#define LFS3_UNREACHABLE() LFS3_ASSERT(false)
|
|
#elif !defined(LFS3_NO_BUILTINS)
|
|
#define LFS3_UNREACHABLE() __builtin_unreachable()
|
|
#else
|
|
#define LFS3_UNREACHABLE()
|
|
#endif
|
|
#endif
|
|
|
|
|
|
// Some ifdef conveniences
|
|
#ifdef LFS3_RDONLY
|
|
#define LFS3_IFDEF_RDONLY(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_RDONLY(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_REVPERTURB
|
|
#define LFS3_IFDEF_REVPERTURB(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_REVPERTURB(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_REVPERTURB) && defined(LFS3_YES_REVPERTURB)
|
|
#define LFS3_IFYES_REVPERTURB(a, b, c) (a)
|
|
#elif defined(LFS3_REVPERTURB)
|
|
#define LFS3_IFYES_REVPERTURB(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_REVPERTURB(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_REVNOISE
|
|
#define LFS3_IFDEF_REVNOISE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_REVNOISE(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_REVNOISE) && defined(LFS3_YES_REVNOISE)
|
|
#define LFS3_IFYES_REVNOISE(a, b, c) (a)
|
|
#elif defined(LFS3_REVNOISE)
|
|
#define LFS3_IFYES_REVNOISE(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_REVNOISE(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKPROGS
|
|
#define LFS3_IFDEF_CKPROGS(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKPROGS(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_CKPROGS) && defined(LFS3_YES_CKPROGS)
|
|
#define LFS3_IFYES_CKPROGS(a, b, c) (a)
|
|
#elif defined(LFS3_CKPROGS)
|
|
#define LFS3_IFYES_CKPROGS(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_CKPROGS(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKFETCHES
|
|
#define LFS3_IFDEF_CKFETCHES(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKFETCHES(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_CKFETCHES) && defined(LFS3_YES_CKFETCHES)
|
|
#define LFS3_IFYES_CKFETCHES(a, b, c) (a)
|
|
#elif defined(LFS3_CKFETCHES)
|
|
#define LFS3_IFYES_CKFETCHES(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_CKFETCHES(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKMETAPARITY
|
|
#define LFS3_IFDEF_CKMETAPARITY(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKMETAPARITY(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_CKMETAPARITY) && defined(LFS3_YES_CKMETAPARITY)
|
|
#define LFS3_IFYES_CKMETAPARITY(a, b, c) (a)
|
|
#elif defined(LFS3_CKMETAPARITY)
|
|
#define LFS3_IFYES_CKMETAPARITY(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_CKMETAPARITY(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
#define LFS3_IFDEF_CKDATACKSUMS(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKDATACKSUMS(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_CKDATACKSUMS) && defined(LFS3_YES_CKDATACKSUMS)
|
|
#define LFS3_IFYES_CKDATACKSUMS(a, b, c) (a)
|
|
#elif defined(LFS3_CKDATACKSUMS)
|
|
#define LFS3_IFYES_CKDATACKSUMS(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_CKDATACKSUMS(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_GC
|
|
#define LFS3_IFDEF_GC(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_GC(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_GBMAP
|
|
#define LFS3_IFDEF_GBMAP(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_GBMAP(a, b) (b)
|
|
#endif
|
|
|
|
#if defined(LFS3_GBMAP) && defined(LFS3_YES_GBMAP)
|
|
#define LFS3_IFYES_GBMAP(a, b, c) (a)
|
|
#elif defined(LFS3_GBMAP)
|
|
#define LFS3_IFYES_GBMAP(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_GBMAP(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifndef LFS3_NO_PREERASE
|
|
#define LFS3_IFDEF_PREERASE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_PREERASE(a, b) (b)
|
|
#endif
|
|
|
|
#if !defined(LFS3_NO_PREERASE) && defined(LFS3_YES_PREERASE)
|
|
#define LFS3_IFYES_PREERASE(a, b, c) (a)
|
|
#elif !defined(LFS3_NO_PREERASE)
|
|
#define LFS3_IFYES_PREERASE(a, b, c) (b)
|
|
#else
|
|
#define LFS3_IFYES_PREERASE(a, b, c) (c)
|
|
#endif
|
|
|
|
#ifdef LFS3_BLEAFCACHE
|
|
#define LFS3_IFDEF_BLEAFCACHE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_BLEAFCACHE(a, b) (b)
|
|
#endif
|
|
|
|
|
|
// Some function attributes, no way around these
|
|
|
|
// Force a function to be inlined
|
|
#if !defined(LFS3_NO_BUILTINS) && defined(__GNUC__)
|
|
#define LFS3_FORCEINLINE __attribute__((always_inline))
|
|
#else
|
|
#define LFS3_FORCEINLINE
|
|
#endif
|
|
|
|
// Force a function to _not_ be inlined
|
|
#if !defined(LFS3_NO_BUILTINS) && defined(__GNUC__)
|
|
#define LFS3_NOINLINE __attribute__((noinline))
|
|
#else
|
|
#define LFS3_NOINLINE
|
|
#endif
|
|
|
|
|
|
// Builtin functions, these may be replaced by more efficient
|
|
// toolchain-specific implementations. LFS3_NO_BUILTINS falls back to a more
|
|
// expensive basic C implementation for debugging purposes
|
|
//
|
|
// Most of the backup implementations are based on the infamous Bit
|
|
// Twiddling Hacks compiled by Sean Eron Anderson:
|
|
// https://graphics.stanford.edu/~seander/bithacks.html
|
|
//
|
|
|
|
// Compile time min/max
|
|
#define LFS3_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
#define LFS3_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
|
|
|
// Min/max functions for unsigned 32-bit numbers
|
|
static inline uint32_t lfs3_min(uint32_t a, uint32_t b) {
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
static inline uint32_t lfs3_max(uint32_t a, uint32_t b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
static inline int32_t lfs3_smin(int32_t a, int32_t b) {
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
static inline int32_t lfs3_smax(int32_t a, int32_t b) {
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
// Absolute value of signed numbers
|
|
static inline int32_t lfs3_abs(int32_t a) {
|
|
return (a < 0) ? -a : a;
|
|
}
|
|
|
|
// Swap two variables
|
|
#define LFS3_SWAP(_t, _a, _b) \
|
|
do { \
|
|
_t *a = _a; \
|
|
_t *b = _b; \
|
|
_t t = *a; \
|
|
*a = *b; \
|
|
*b = t; \
|
|
} while (0)
|
|
|
|
// Align to nearest multiple of a size
|
|
static inline uint32_t lfs3_aligndown(uint32_t a, uint32_t alignment) {
|
|
return a - (a % alignment);
|
|
}
|
|
|
|
static inline uint32_t lfs3_alignup(uint32_t a, uint32_t alignment) {
|
|
return lfs3_aligndown(a + alignment-1, alignment);
|
|
}
|
|
|
|
// Find the smallest power of 2 greater than or equal to a
|
|
static inline uint32_t lfs3_nlog2(uint32_t a) {
|
|
// __builtin_clz of zero is undefined, so treat both 0 and 1 specially
|
|
if (a <= 1) {
|
|
return a;
|
|
}
|
|
|
|
#if !defined(LFS3_NO_BUILTINS) && (defined(__GNUC__) || defined(__CC_ARM))
|
|
return 32 - __builtin_clz(a-1);
|
|
#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
|
|
}
|
|
|
|
// Count the number of trailing binary zeros in a
|
|
// lfs3_ctz(0) may be undefined
|
|
static inline uint32_t lfs3_ctz(uint32_t a) {
|
|
#if !defined(LFS3_NO_BUILTINS) && defined(__GNUC__)
|
|
return __builtin_ctz(a);
|
|
#else
|
|
return lfs3_nlog2((a & -a) + 1) - 1;
|
|
#endif
|
|
}
|
|
|
|
// Count the number of binary ones in a
|
|
static inline uint32_t lfs3_popc(uint32_t a) {
|
|
#if !defined(LFS3_NO_BUILTINS) && (defined(__GNUC__) || defined(__CC_ARM))
|
|
return __builtin_popcount(a);
|
|
#else
|
|
a = a - ((a >> 1) & 0x55555555);
|
|
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
|
a = (a + (a >> 4)) & 0x0f0f0f0f;
|
|
return (a * 0x1010101) >> 24;
|
|
#endif
|
|
}
|
|
|
|
// Returns true if there is an odd number of binary ones in a
|
|
static inline bool lfs3_parity(uint32_t a) {
|
|
#if !defined(LFS3_NO_BUILTINS) && (defined(__GNUC__) || defined(__CC_ARM))
|
|
return __builtin_parity(a);
|
|
#else
|
|
a ^= a >> 16;
|
|
a ^= a >> 8;
|
|
a ^= a >> 4;
|
|
return (0x6996 >> (a & 0xf)) & 1;
|
|
#endif
|
|
}
|
|
|
|
// Find the sequence comparison of a and b, this is the distance
|
|
// between a and b ignoring overflow
|
|
static inline int lfs3_scmp(uint32_t a, uint32_t b) {
|
|
return (int)(unsigned)(a - b);
|
|
}
|
|
|
|
// Perform polynomial/carry-less multiplication
|
|
//
|
|
// This is a multiply where all adds are replaced with xors. If we view
|
|
// a and b as binary polynomials, xor is polynomial addition and pmul is
|
|
// polynomial multiplication.
|
|
static inline uint64_t lfs3_pmul(uint32_t a, uint32_t b) {
|
|
uint64_t r = 0;
|
|
uint64_t a_ = a;
|
|
while (b) {
|
|
if (b & 1) {
|
|
r ^= a_;
|
|
}
|
|
a_ <<= 1;
|
|
b >>= 1;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
|
|
// Convert to/from 32-bit little-endian
|
|
static inline void lfs3_tole32(uint32_t word, void *buffer) {
|
|
((uint8_t*)buffer)[0] = word >> 0;
|
|
((uint8_t*)buffer)[1] = word >> 8;
|
|
((uint8_t*)buffer)[2] = word >> 16;
|
|
((uint8_t*)buffer)[3] = word >> 24;
|
|
}
|
|
|
|
static inline uint32_t lfs3_fromle32(const void *buffer) {
|
|
return (((uint8_t*)buffer)[0] << 0)
|
|
| (((uint8_t*)buffer)[1] << 8)
|
|
| (((uint8_t*)buffer)[2] << 16)
|
|
| (((uint8_t*)buffer)[3] << 24);
|
|
}
|
|
|
|
// Convert to/from leb128 encoding
|
|
// TODO should we really be using ssize_t here and not lfs3_ssize_t?
|
|
ssize_t lfs3_toleb128(uint32_t word, void *buffer, size_t size);
|
|
|
|
ssize_t lfs3_fromleb128(uint32_t *word, const void *buffer, size_t size);
|
|
|
|
|
|
// Compare n bytes of memory
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_memcmp memcmp
|
|
#elif !defined(LFS3_NO_BUILTINS)
|
|
#define lfs3_memcmp __builtin_memcmp
|
|
#else
|
|
static inline int lfs3_memcmp(const void *a, const void *b, size_t size) {
|
|
const uint8_t *a_ = a;
|
|
const uint8_t *b_ = b;
|
|
for (size_t i = 0; i < size; i++) {
|
|
if (a_[i] != b_[i]) {
|
|
return (int)a_[i] - (int)b_[i];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// Copy n bytes from src to dst, src and dst must not overlap
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_memcpy memcpy
|
|
#elif !defined(LFS3_NO_BUILTINS)
|
|
#define lfs3_memcpy __builtin_memcpy
|
|
#else
|
|
static inline void *lfs3_memcpy(
|
|
void *restrict dst, const void *restrict src, size_t size) {
|
|
uint8_t *dst_ = dst;
|
|
const uint8_t *src_ = src;
|
|
for (size_t i = 0; i < size; i++) {
|
|
dst_[i] = src_[i];
|
|
}
|
|
|
|
return dst_;
|
|
}
|
|
#endif
|
|
|
|
// Copy n bytes from src to dst, src and dst may overlap
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_memmove memmove
|
|
#elif !defined(LFS3_NO_BUILTINS)
|
|
#define lfs3_memmove __builtin_memmove
|
|
#else
|
|
static inline void *lfs3_memmove(void *dst, const void *src, size_t size) {
|
|
uint8_t *dst_ = dst;
|
|
const uint8_t *src_ = src;
|
|
if (dst_ < src_) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
dst_[i] = src_[i];
|
|
}
|
|
} else if (dst_ > src_) {
|
|
for (size_t i = 0; i < size; i++) {
|
|
dst_[(size-1)-i] = src_[(size-1)-i];
|
|
}
|
|
}
|
|
|
|
return dst_;
|
|
}
|
|
#endif
|
|
|
|
// Set n bytes to c
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_memset memset
|
|
#elif !defined(LFS3_NO_BUILTINS)
|
|
#define lfs3_memset __builtin_memset
|
|
#else
|
|
static inline void *lfs3_memset(void *dst, int c, size_t size) {
|
|
uint8_t *dst_ = dst;
|
|
for (size_t i = 0; i < size; i++) {
|
|
dst_[i] = c;
|
|
}
|
|
|
|
return dst_;
|
|
}
|
|
#endif
|
|
|
|
// Find the first occurrence of c or NULL
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_memchr memchr
|
|
#else
|
|
static inline void *lfs3_memchr(const void *a, int c, size_t size) {
|
|
const uint8_t *a_ = a;
|
|
for (size_t i = 0; i < size; i++) {
|
|
if (a_[i] == c) {
|
|
return (void*)&a_[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
// Find the first occurrence of anything not c or NULL
|
|
static inline void *lfs3_memcchr(const void *a, int c, size_t size) {
|
|
const uint8_t *a_ = a;
|
|
for (size_t i = 0; i < size; i++) {
|
|
if (a_[i] != c) {
|
|
return (void*)&a_[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Find the minimum length that includes all non-zero bytes
|
|
static inline size_t lfs3_memlen(const void *a, size_t size) {
|
|
const uint8_t *a_ = a;
|
|
while (size > 0 && a_[size-1] == 0) {
|
|
size -= 1;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
// Xor n bytes from b into a
|
|
static inline void *lfs3_memxor(
|
|
void *restrict a, const void *restrict b, size_t size) {
|
|
uint8_t *a_ = a;
|
|
const uint8_t *b_ = b;
|
|
for (size_t i = 0; i < size; i++) {
|
|
a_[i] ^= b_[i];
|
|
}
|
|
|
|
return a_;
|
|
}
|
|
|
|
|
|
// Find the length of a null-terminated string
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_strlen strlen
|
|
#else
|
|
static inline size_t lfs3_strlen(const char *a) {
|
|
const char *a_ = a;
|
|
while (*a_) {
|
|
a_++;
|
|
}
|
|
|
|
return a_ - a;
|
|
}
|
|
#endif
|
|
|
|
// Compare two null-terminated strings
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_strcmp strcmp
|
|
#else
|
|
static inline int lfs3_strcmp(const char *a, const char *b) {
|
|
while (*a && *a == *b) {
|
|
a++;
|
|
b++;
|
|
}
|
|
|
|
return (int)*a - (int)*b;
|
|
}
|
|
#endif
|
|
|
|
// Copy a null-terminated string from src to dst
|
|
#if !defined(LFS3_NO_STRINGH)
|
|
#define lfs3_strcpy strcpy
|
|
#else
|
|
static inline char *lfs3_strcpy(
|
|
char *restrict dst, const char *restrict src) {
|
|
char *dst_ = dst;
|
|
while (*src) {
|
|
*dst_ = *src;
|
|
dst_++;
|
|
src++;
|
|
}
|
|
|
|
*dst_ = '\0';
|
|
return dst;
|
|
}
|
|
#endif
|
|
|
|
// Find first occurrence of c or NULL
|
|
#ifndef LFS3_NO_STRINGH
|
|
#define lfs3_strchr strchr
|
|
#else
|
|
static inline char *lfs3_strchr(const char *a, int c) {
|
|
while (*a) {
|
|
if (*a == c) {
|
|
return (char*)a;
|
|
}
|
|
|
|
a++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
// Find first occurrence of anything not c or NULL
|
|
static inline char *lfs3_strcchr(const char *a, int c) {
|
|
while (*a) {
|
|
if (*a != c) {
|
|
return (char*)a;
|
|
}
|
|
|
|
a++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Find length of a that does not contain any char in cs
|
|
#ifndef LFS3_NO_STRINGH
|
|
#define lfs3_strspn strspn
|
|
#else
|
|
static inline size_t lfs3_strspn(const char *a, const char *cs) {
|
|
const char *a_ = a;
|
|
while (*a_) {
|
|
const char *cs_ = cs;
|
|
while (*cs_) {
|
|
if (*a_ != *cs_) {
|
|
return a_ - a;
|
|
}
|
|
cs_++;
|
|
}
|
|
|
|
a_++;
|
|
}
|
|
|
|
return a_ - a;
|
|
}
|
|
#endif
|
|
|
|
// Find length of a that only contains chars in cs
|
|
#ifndef LFS3_NO_STRINGH
|
|
#define lfs3_strcspn strcspn
|
|
#else
|
|
static inline size_t lfs3_strcspn(const char *a, const char *cs) {
|
|
const char *a_ = a;
|
|
while (*a_) {
|
|
const char *cs_ = cs;
|
|
while (*cs_) {
|
|
if (*a_ == *cs_) {
|
|
return a_ - a;
|
|
}
|
|
cs_++;
|
|
}
|
|
|
|
a_++;
|
|
}
|
|
|
|
return a_ - a;
|
|
}
|
|
#endif
|
|
|
|
|
|
// Odd-parity and even-parity zeros in our crc32c ring
|
|
#define LFS3_CRC32C_ODDZERO 0xfca42daf
|
|
#define LFS3_CRC32C_EVENZERO 0x00000000
|
|
|
|
// Calculate crc32c incrementally
|
|
//
|
|
// polynomial = 0x11edc6f41
|
|
// init = 0xffffffff
|
|
// fini = 0xffffffff
|
|
//
|
|
uint32_t lfs3_crc32c(uint32_t crc, const void *buffer, size_t size);
|
|
|
|
// Multiply two crc32cs in the crc32c ring
|
|
uint32_t lfs3_crc32c_mul(uint32_t a, uint32_t b);
|
|
|
|
// Find the cube of a crc32c in the crc32c ring
|
|
static inline uint32_t lfs3_crc32c_cube(uint32_t a) {
|
|
return lfs3_crc32c_mul(lfs3_crc32c_mul(a, a), a);
|
|
}
|
|
|
|
|
|
// Allocate memory, only used if buffers are not provided to littlefs
|
|
#ifndef LFS3_NO_MALLOC
|
|
#define lfs3_malloc malloc
|
|
#else
|
|
static inline void *lfs3_malloc(size_t size) {
|
|
(void)size;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
// Deallocate memory, only used if buffers are not provided to littlefs
|
|
#ifndef LFS3_NO_MALLOC
|
|
#define lfs3_free free
|
|
#else
|
|
static inline void lfs3_free(void *p) {
|
|
(void)p;
|
|
}
|
|
#endif
|
|
|
|
|
|
#endif
|
|
#endif
|