843412cc79
Allocating pre-erased blocks gets quite complicated due to our
restricted flash model, but at least the actual pre-erasing is
relatively straightforward:
- We keep track of known preerased state in lfs3->gbmap.preeraser.
- If LFS3_GC_PREERASE is provided during gc work, we increment the
preeraser's known window by scanning the gbmap.
- Any BMFREE ranges we find, we erase a block at a time, and store the
resulting ecksum in a BMERASED range in the gbmap.
- We keep track of how many blocks we erased, and stop early if this
exceeds cfg.gc_preerase_count. This just lets users tune how many
blocks to preerase in case something (?) prevents preerased blocks
from being used.
Some notes:
- We don't really do anything with ranges in lfs3_alloc_preerase. In
theory we could bulk in erase to minimize the number of commits to the
gbmap, but we expect erase to dominate, so this probably isn't worth
it.
And if erase doesn't dominate, why would you bother pre-erasing
blocks?
- Preerasing isn't really a traversal operation, and is managed by a
sort of secondary state machine in lfs3_fs_gc_.
This also means lfs3_trv_read with LFS3_T_PREERASE does nothing, but I
guess that is ok? It's tempting to try to make lfs3_trv_read also
preerase, but it's unclear what block it should return -- it's
probably the wrong API.
- Introducing ecksums actually went quite a bit smoother than I
expected. Though it helps ecksums are the only optional payload, no
type punning or anything.
Ecksums do muddy the gbmap's design a bit, unfortunately. The main
issue being that we can only merge BMERASED ranges with equal ecksums.
This makes BMERASED ranges less compressable than the others, and may
be one reason to limit cfg.gc_preerase_count.
However:
1. This is where I think it's useful to emphasize that the gbmap's
responsibility is to track _free_ blocks, in-use blocks are
secondary.
When allocating, we're going to stop at the first BMFREE/BMERASED,
but may need to skip over an unbounded number of BMINUSE/BMBAD
blocks. So the compressability of BMFREE/BMERASED ranges should
have less of an impact on block allocation.
2. In practice, most flash uses consistent erase values, so the
resulting ecksums will probably be compressable. The exceptions are
noop-erases (SD/eMMC, RAM, NVRAM, etc), and encryption with block
address permutation?
Though noop-erases are a pretty big exception.
Code changes:
code stack ctx
before: 35116 2136 660
after: 35116 (+0.0%) 2136 (+0.0%) 660 (+0.0%)
code stack ctx
gbmap+np before: 38040 2136 776
gbmap+np after: 38188 (+0.4%) 2144 (+0.4%) 776 (+0.0%)
code stack ctx
gbmap+yp before: 38040 2136 776
gbmap+yp after: 38608 (+1.5%) 2144 (+0.4%) 796 (+2.6%)
756 lines
17 KiB
C
756 lines
17 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_REVDBG
|
|
#define LFS3_REVDBG
|
|
#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_REVDBG
|
|
#define LFS3_REVDBG
|
|
#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_REVDBG
|
|
#define LFS3_IFDEF_REVDBG(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_REVDBG(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_YES_REVDBG
|
|
#define LFS3_IFDEF_YES_REVDBG(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_YES_REVDBG(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_REVNOISE
|
|
#define LFS3_IFDEF_REVNOISE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_REVNOISE(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_YES_REVNOISE
|
|
#define LFS3_IFDEF_YES_REVNOISE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_YES_REVNOISE(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKPROGS
|
|
#define LFS3_IFDEF_CKPROGS(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKPROGS(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKFETCHES
|
|
#define LFS3_IFDEF_CKFETCHES(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKFETCHES(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKMETAPARITY
|
|
#define LFS3_IFDEF_CKMETAPARITY(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKMETAPARITY(a, b) (b)
|
|
#endif
|
|
|
|
#ifdef LFS3_CKDATACKSUMS
|
|
#define LFS3_IFDEF_CKDATACKSUMS(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_CKDATACKSUMS(a, b) (b)
|
|
#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
|
|
|
|
// TODO other LFS3_IFDEF_YES_* macros?
|
|
#ifdef LFS3_YES_GBMAP
|
|
#define LFS3_IFDEF_YES_GBMAP(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_YES_GBMAP(a, b) (b)
|
|
#endif
|
|
|
|
#ifndef LFS3_NO_PREERASE
|
|
#define LFS3_IFDEF_PREERASE(a, b) (a)
|
|
#else
|
|
#define LFS3_IFDEF_PREERASE(a, b) (b)
|
|
#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
|