From 8f3036f1e58b11513c36dc41f9fb88bfca549f66 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Sun, 5 May 2024 11:50:59 -0500 Subject: [PATCH] Unified attr-list context into little attr arenas The idea is for cases where we need to incrementally allocate attrs + context, to allocate from both sides of a statically allocated attr array. This keeps all of the attr-list state in one place, simplifying state allocation: .---+---+---+---. | attr | +---+---+---+---+ | attr ----------. +---+---+---+---+ | | attr --------. | +---+---+---+---+ | | | | | | | | v | | | | | | | | ^ | | | | | | | | +---+---+---+---+ | | | data | <-' | +---+---+---+---+ | | encoded bptr | <---' '---+---+---+---' This is especially useful for the non-terminating tail-recursive lfsr_btree_commit_, which needs to pass this state through a function call. Unfortunately, to make this work we needed to implement more-or-less a full arena allocator, complete with annoying alignment handling. alignof isn't even available in C99, so we needed a few more intrinsics: - LFS_ALIGNOF(t) - Alignment of type t - LFS_ALIGNEDSIZEOF(t) - Necessary size to force alignment for t - LFS_MIN(a, b) - Compile-time min - LFS_MAX(a, b) - Compile-time max Technically only LFS_ALIGNOF was required, but the others are nice to have. LFS_MIN/LFS_MAX is also useful anywhere you need to calculate complicated compile-time sizes. At least in C11 we get alignof, so we won't need compiler extensions/ hacks for this in the future... --- Unfortunately this ended up a net-negative. Pushing up the code/stack cost to near pre-cat levels: code stack before cat: 33856 2824 before scratch: 33812 (-0.1%) 2800 (-0.8%) after: 33844 (-0.0%) 2824 (+0.0%) I think the two main culprits are 1. the extra logic needed to calculate alignment, and 2. wasted stack due to aligning scratch space up to the nearest lfsr_attr_t. --- lfs.c | 178 ++++++++++++++++++++++++++++++----------------------- lfs_util.h | 30 ++++++--- 2 files changed, 122 insertions(+), 86 deletions(-) diff --git a/lfs.c b/lfs.c index 5676c532..0d70b7eb 100644 --- a/lfs.c +++ b/lfs.c @@ -1631,6 +1631,29 @@ static inline lfsr_shrub_t *lfsr_attr_shrubtrunk(const lfsr_attr_t *attr) { return (lfsr_shrub_t*)attr->cat.u.buf.buffer; } +// sometimes we want some extra scratch space associated with an +// attribute list +#define LFSR_ATTR_SCRATCH(_size) \ + (((_size)+sizeof(lfsr_attr_t)-1) / sizeof(lfsr_attr_t)) + +static inline void *lfsr_attr_scratch( + lfsr_attr_t *attr, lfs_size_t *attr_scratch, + lfs_size_t size) { + lfs_size_t attr_scratch_ = *attr_scratch - size; + *attr_scratch = attr_scratch_; + return &((uint8_t*)attr)[attr_scratch_]; +} + +static inline lfsr_data_t *lfsr_attr_scratchdata( + lfsr_attr_t *attr, lfs_size_t *attr_scratch) { + lfs_size_t attr_scratch_ = *attr_scratch - sizeof(lfsr_data_t); + // align down + attr_scratch_ -= (uintptr_t)&((uint8_t*)attr)[attr_scratch_] + % LFS_ALIGNOF(lfsr_data_t); + *attr_scratch = attr_scratch_; + return (lfsr_data_t*)&((uint8_t*)attr)[attr_scratch_]; +} + // generalized info returned by traveral functions @@ -4374,6 +4397,11 @@ static int lfsr_btree_parent(lfs_t *lfs, const lfsr_btree_t *btree, } +// attrs needed for lfsr_btree_commit_ +#define LFSR_BTREE_COMMIT_ATTRS \ + (4 + LFSR_ATTR_SCRATCH( \ + LFS_ALIGNEDSIZEOF(lfsr_data_t) + 2*LFSR_BRANCH_DSIZE)) + // core btree algorithm // // this commits up to the root, but stops if: @@ -4382,10 +4410,7 @@ static int lfsr_btree_parent(lfs_t *lfs, const lfsr_btree_t *btree, // static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, lfsr_bid_t *bid_, const lfsr_attr_t **attrs_, lfs_size_t *attr_count_, - lfsr_attr_t attrs__[static 4], - uint8_t buf__[static 2*LFSR_BRANCH_DSIZE], - // TODO can we carve this out of buf__? - lfsr_data_t split_data__[static 1]) { + lfsr_attr_t attrs__[static LFSR_BTREE_COMMIT_ATTRS]) { lfsr_bid_t bid = *bid_; LFS_ASSERT(bid <= (lfsr_bid_t)btree->weight); const lfsr_attr_t *attrs = *attrs_; @@ -4506,16 +4531,17 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, // note that since we defer merges to compaction time, we can // end up removing an rbyd here attr_count = 0; - lfs_size_t buf_size = 0; + lfs_size_t attr_scratch = LFSR_BTREE_COMMIT_ATTRS*sizeof(lfsr_attr_t); bid -= pid - (rbyd.weight-1); if (rbyd_.weight == 0) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_RM, -rbyd.weight, LFSR_CAT_NULL()); } else { + uint8_t *buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, 0, - lfsr_cat_frombranch(&rbyd_, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&rbyd_, buf)); if (rbyd_.weight != rbyd.weight) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight, @@ -4762,58 +4788,65 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, goto finalize; } + // prepare commit to parent, tail recursing upwards + LFS_ASSERT(rbyd_.weight > 0); + LFS_ASSERT(sibling.weight > 0); + attr_count = 0; + attr_scratch = LFSR_BTREE_COMMIT_ATTRS*sizeof(lfsr_attr_t); + // lookup first name in sibling to use as the split name // // note we need to do this after playing out pending attrs in case // they introduce a new name! lfsr_tag_t split_tag; + lfsr_data_t *split_data = lfsr_attr_scratchdata( + attrs__, &attr_scratch); err = lfsr_rbyd_lookupnext(lfs, &sibling, 0, LFSR_TAG_NAME, - NULL, &split_tag, NULL, &split_data__[0]); + NULL, &split_tag, NULL, split_data); if (err) { LFS_ASSERT(err != LFS_ERR_NOENT); return err; } - // prepare commit to parent, tail recursing upwards - LFS_ASSERT(rbyd_.weight > 0); - LFS_ASSERT(sibling.weight > 0); - attr_count = 0; - buf_size = 0; // new root? if (!lfsr_rbyd_trunk(&parent)) { + uint8_t *buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, +rbyd_.weight, - lfsr_cat_frombranch(&rbyd_, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&rbyd_, buf)); + buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, +sibling.weight, - lfsr_cat_frombranch(&sibling, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&sibling, buf)); if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_NAME, 0, - lfsr_cat_fromdatas(&split_data__[0], 1)); + LFSR_CAT_DATA(split_data)); } // split root? } else { bid -= pid - (rbyd.weight-1); + uint8_t *buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, 0, - lfsr_cat_frombranch(&rbyd_, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&rbyd_, buf)); if (rbyd_.weight != rbyd.weight) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight, LFSR_CAT_NULL()); } + buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, +sibling.weight, - lfsr_cat_frombranch(&sibling, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&sibling, buf)); if (lfsr_tag_suptype(split_tag) == LFSR_TAG_NAME) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_NAME, 0, - lfsr_cat_fromdatas(&split_data__[0], 1)); + LFSR_CAT_DATA(split_data)); } } attrs = attrs__; @@ -4876,14 +4909,15 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, // prepare commit to parent, tail recursing upwards LFS_ASSERT(rbyd_.weight > 0); attr_count = 0; - buf_size = 0; + attr_scratch = LFSR_BTREE_COMMIT_ATTRS*sizeof(lfsr_attr_t); bid -= pid - (rbyd.weight-1); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_RM, -sibling.weight, LFSR_CAT_NULL()); + uint8_t *buf = lfsr_attr_scratch(attrs__, &attr_scratch, + LFSR_BRANCH_DSIZE); attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_BRANCH, 0, - lfsr_cat_frombranch(&rbyd_, &buf__[buf_size])); - buf_size += LFSR_BRANCH_DSIZE; + lfsr_cat_frombranch(&rbyd_, buf)); if (rbyd_.weight != rbyd.weight) { attrs__[attr_count++] = LFSR_ATTR( LFSR_TAG_GROW, -rbyd.weight + rbyd_.weight, @@ -4900,15 +4934,11 @@ static int lfsr_btree_commit_(lfs_t *lfs, lfsr_btree_t *btree, // this is atomic static int lfsr_btree_commit(lfs_t *lfs, lfsr_btree_t *btree, lfsr_bid_t bid, const lfsr_attr_t *attrs, lfs_size_t attr_count) { - // we need some scratch space for tail-recursive attrs - lfsr_attr_t attrs__[4]; - uint8_t buffer[2*LFSR_BRANCH_DSIZE]; - lfsr_data_t split_data__[1]; - // try to commit to the btree + lfsr_attr_t attrs__[LFSR_BTREE_COMMIT_ATTRS]; int err = lfsr_btree_commit_(lfs, btree, &bid, &attrs, &attr_count, - attrs__, buffer, split_data__); + attrs__); if (err && err != LFS_ERR_RANGE) { return err; } @@ -5687,10 +5717,7 @@ static bool lfsr_mid_isopen(lfs_t *lfs, lfsr_smid_t mid) { .u.mptr.weight=(LFSR_MTREE_ISMPTR | (_weight)), \ .u.mptr.mptr=_mptr}) -#define LFSR_MTREE_DSIZE ( \ - (LFSR_MPTR_DSIZE > LFSR_BTREE_DSIZE) \ - ? LFSR_MPTR_DSIZE \ - : LFSR_BTREE_DSIZE) +#define LFSR_MTREE_DSIZE LFS_MAX(LFSR_MPTR_DSIZE, LFSR_BTREE_DSIZE) static inline bool lfsr_mtree_isnull(const lfsr_mtree_t *mtree) { return mtree->u.weight == (LFSR_MTREE_ISMPTR | 0); @@ -10153,15 +10180,11 @@ static int lfsr_bshrub_commit(lfs_t *lfs, lfsr_file_t *file, } } - // we need some scratch space for tail-recursive attrs - lfsr_attr_t attrs__[4]; - uint8_t buffer[2*LFSR_BRANCH_DSIZE]; - lfsr_data_t split_data__[1]; - // try to commit to the btree + lfsr_attr_t attrs__[LFSR_BTREE_COMMIT_ATTRS]; int err = lfsr_btree_commit_(lfs, &file->bshrub.u.btree, &bid, &attrs, &attr_count, - attrs__, buffer, split_data__); + attrs__); if (err && err != LFS_ERR_RANGE) { return err; } @@ -10304,10 +10327,10 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, // try to merge commits where possible lfsr_bid_t bid = lfsr_bshrub_size(&file->bshrub); - lfsr_attr_t attrs[5]; + lfsr_attr_t attrs[5 + LFSR_ATTR_SCRATCH( + 2*LFS_MAX(LFS_ALIGNEDSIZEOF(lfsr_data_t), LFSR_BPTR_DSIZE))]; lfs_size_t attr_count = 0; - uint8_t buf[2*LFSR_BPTR_DSIZE]; - lfs_size_t buf_size = 0; + lfs_size_t attr_scratch = sizeof(attrs); // always convert to bshrub/btree when this function is called if (!lfsr_bshrub_isbshruborbtree(&file->bshrub)) { @@ -10319,10 +10342,11 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, LFSR_TAG_DATA, +lfsr_bshrub_size(&file->bshrub), LFSR_CAT_DATA(&file->bshrub.u.bsprout)); } else if (lfsr_bshrub_isbptr(&file->m.mdir, &file->bshrub)) { + uint8_t *buf = lfsr_attr_scratch(attrs, &attr_scratch, + LFSR_BPTR_DSIZE); attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_BLOCK, +lfsr_bshrub_size(&file->bshrub), - lfsr_cat_frombptr(&file->bshrub.u.bptr, &buf[buf_size])); - buf_size += LFSR_BPTR_DSIZE; + lfsr_cat_frombptr(&file->bshrub.u.bptr, buf)); } file->bshrub.u.bshrub.blocks[0] = file->m.mdir.rbyd.blocks[0]; @@ -10332,8 +10356,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, file->bshrub.u.bshrub.estimate = -1; if (attr_count > 0) { - LFS_ASSERT(attr_count <= sizeof(attrs)/sizeof(lfsr_attr_t)); - LFS_ASSERT(buf_size <= sizeof(buf)); + LFS_ASSERT(attr_count*sizeof(lfsr_attr_t) <= attr_scratch); int err = lfsr_bshrub_commit(lfs, file, 0, attrs, attr_count); if (err) { @@ -10342,7 +10365,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, } attr_count = 0; - buf_size = 0; + attr_scratch = sizeof(attrs); } // need a hole? @@ -10367,9 +10390,6 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, lfsr_tag_t right_tag_ = 0; lfsr_bid_t right_weight_; lfsr_bptr_t right_bptr_; - // TODO should we handle left/right slice/bptr differently? allocate - // from buf maybe? - lfsr_data_t left_slice_; while (pos < lfsr_bshrub_size(&file->bshrub)) { lfsr_tag_t tag_; lfsr_bid_t weight_; @@ -10382,7 +10402,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, } // note, an entry can be both a left and right sibling - left_slice_ = lfsr_data_slice(bptr_.data, + lfsr_data_t left_slice_ = lfsr_data_slice(bptr_.data, -1, pos - (bid-(weight_-1))); lfsr_data_t right_slice_ = lfsr_data_slice(bptr_.data, @@ -10458,13 +10478,18 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, // carve fragment? } else if (tag_ == LFSR_TAG_DATA) { + lfsr_data_t *data = lfsr_attr_scratchdata( + attrs, &attr_scratch); + *data = left_slice_; attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_GROW | LFSR_TAG_SUB | LFSR_TAG_DATA, -(bid+1 - pos), - LFSR_CAT_DATA(&left_slice_)); + LFSR_CAT_DATA(data)); // carve bptr? } else if (tag_ == LFSR_TAG_BLOCK) { + uint8_t *buf = lfsr_attr_scratch(attrs, &attr_scratch, + LFSR_BPTR_DSIZE); attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_GROW | LFSR_TAG_SUB | LFSR_TAG_BLOCK, -(bid+1 - pos), @@ -10473,8 +10498,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, .data = left_slice_, .cksize = bptr_.cksize, .cksum = bptr_.cksum}, - &buf[buf_size])); - buf_size += LFSR_BPTR_DSIZE; + buf)); } else { LFS_UNREACHABLE(); @@ -10490,8 +10514,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, // so commit what we have and move on to next entry if (pos+weight > bid+1) { LFS_ASSERT(lfsr_data_size(right_slice_) == 0); - LFS_ASSERT(attr_count <= sizeof(attrs)/sizeof(lfsr_attr_t)); - LFS_ASSERT(buf_size <= sizeof(buf)); + LFS_ASSERT(attr_count*sizeof(lfsr_attr_t) <= attr_scratch); err = lfsr_bshrub_commit(lfs, file, bid, attrs, attr_count); @@ -10502,7 +10525,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, delta += lfs_min32(weight, bid+1 - pos); weight -= lfs_min32(weight, bid+1 - pos); attr_count = 0; - buf_size = 0; + attr_scratch = sizeof(attrs); continue; } @@ -10564,16 +10587,19 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, if (right_tag_) { // right fragment? if (right_tag_ == LFSR_TAG_DATA) { + lfsr_data_t *data = lfsr_attr_scratchdata(attrs, &attr_scratch); + *data = right_bptr_.data; attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_DATA, +right_weight_, - LFSR_CAT_DATA(&right_bptr_.data)); + LFSR_CAT_DATA(data)); // right bptr? } else if (right_tag_ == LFSR_TAG_BLOCK) { + uint8_t *buf = lfsr_attr_scratch(attrs, &attr_scratch, + LFSR_BPTR_DSIZE); attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_BLOCK, +right_weight_, - lfsr_cat_frombptr(&right_bptr_, &buf[buf_size])); - buf_size += LFSR_BPTR_DSIZE; + lfsr_cat_frombptr(&right_bptr_, buf)); } else { LFS_UNREACHABLE(); @@ -10582,8 +10608,7 @@ static int lfsr_file_carve(lfs_t *lfs, lfsr_file_t *file, // commit pending attrs if (attr_count > 0) { - LFS_ASSERT(attr_count <= sizeof(attrs)/sizeof(lfsr_attr_t)); - LFS_ASSERT(buf_size <= sizeof(buf)); + LFS_ASSERT(attr_count*sizeof(lfsr_attr_t) <= attr_scratch); int err = lfsr_bshrub_commit(lfs, file, bid, attrs, attr_count); @@ -11399,17 +11424,16 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { lfs_alloc_ckpoint(lfs); // commit our file's metadata - lfsr_attr_t attrs[2]; + lfsr_attr_t attrs[2 + LFSR_ATTR_SCRATCH( + LFS_ALIGNEDSIZEOF(lfsr_data_t) + LFSR_BTREE_DSIZE)]; lfs_size_t attr_count = 0; - uint8_t buf[LFSR_BTREE_DSIZE]; - lfs_size_t buf_size = 0; - // TODO should we carve this out of buf? - lfsr_data_t data; + lfs_size_t attr_scratch = sizeof(attrs); // not created yet? need to convert orphan to normal file if (lfsr_f_isorphan(file->m.flags)) { + lfsr_data_t *data = lfsr_attr_scratchdata(attrs, &attr_scratch); err = lfsr_mdir_lookup(lfs, &file->m.mdir, LFSR_TAG_ORPHAN, - &data); + data); if (err) { // we must have an orphan at this point LFS_ASSERT(err != LFS_ERR_NOENT); @@ -11418,7 +11442,7 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_SUB | LFSR_TAG_REG, 0, - LFSR_CAT_DATA(&data)); + LFSR_CAT_DATA(data)); } // commit the file state @@ -11440,18 +11464,16 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) { &file->bshrub_.u.bshrub); // btree? } else if (lfsr_bshrub_isbtree(&file->m.mdir, &file->bshrub)) { + uint8_t *buf = lfsr_attr_scratch(attrs, &attr_scratch, + LFSR_BTREE_DSIZE); attrs[attr_count++] = LFSR_ATTR( LFSR_TAG_SUB | LFSR_TAG_BTREE, 0, - lfsr_cat_frombtree( - &file->bshrub.u.btree, - &buf[buf_size])); - buf_size += LFSR_BTREE_DSIZE; + lfsr_cat_frombtree(&file->bshrub.u.btree, buf)); } else { LFS_UNREACHABLE(); } - LFS_ASSERT(attr_count <= sizeof(attrs)/sizeof(lfsr_attr_t)); - LFS_ASSERT(buf_size <= sizeof(buf)); + LFS_ASSERT(attr_count*sizeof(lfsr_attr_t) <= attr_scratch); err = lfsr_mdir_commit(lfs, &file->m.mdir, attrs, attr_count); diff --git a/lfs_util.h b/lfs_util.h index eee7276e..e9999115 100644 --- a/lfs_util.h +++ b/lfs_util.h @@ -142,16 +142,16 @@ extern "C" // 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; -} +// Compile time min/max +#define LFS_MIN(a, b) ((a < b) ? a : b) +#define LFS_MAX(a, b) ((a > b) ? a : b) +// Min/max functions for unsigned 32-bit numbers static inline uint32_t lfs_min(uint32_t a, uint32_t b) { return (a < b) ? a : b; } -static inline uint32_t lfs_max32(uint32_t a, uint32_t b) { +static inline uint32_t lfs_max(uint32_t a, uint32_t b) { return (a > b) ? a : b; } @@ -159,7 +159,7 @@ static inline uint32_t lfs_min32(uint32_t a, uint32_t b) { return (a < b) ? a : b; } -static inline int32_t lfs_smax32(int32_t a, int32_t b) { +static inline uint32_t lfs_max32(uint32_t a, uint32_t b) { return (a > b) ? a : b; } @@ -167,15 +167,19 @@ static inline int32_t lfs_smin32(int32_t a, int32_t b) { return (a < b) ? a : b; } -// TODO other 16-bit ops? -static inline uint16_t lfs_max16(uint16_t a, uint16_t b) { +static inline int32_t lfs_smax32(int32_t a, int32_t b) { return (a > b) ? a : b; } +// TODO other 16-bit ops? static inline uint16_t lfs_min16(uint16_t a, uint16_t b) { return (a < b) ? a : b; } +static inline uint16_t lfs_max16(uint16_t a, uint16_t b) { + return (a > b) ? a : b; +} + // Clamp is useful as the logic for min/max when clamping can become confusing static inline uint32_t lfs_clamp32(uint32_t a, uint32_t min, uint32_t max) { return lfs_min32(lfs_max32(a, min), max); @@ -217,6 +221,16 @@ static inline void lfs_sswap32(int32_t *a, int32_t *b) { *b = t; } +// Find alignment of a type at compile time +#if !defined(LFS_NO_INTRINSICS) +#define LFS_ALIGNOF(t) __alignof__(t) +#else +#define LFS_ALIGNOF(t) ((size_t)&((struct {char a; t b;}*)0)->b) +#endif + +// Find size necessary to align type at compile time +#define LFS_ALIGNEDSIZEOF(t) (sizeof(t) + LFS_ALIGNOF(t)-1) + // Align to nearest multiple of a size static inline uint32_t lfs_aligndown(uint32_t a, uint32_t alignment) { return a - (a % alignment);