mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Modified global state format to work with new tag format
The main difference here is a change from encoding "hasorphans" and "hasmove" bits in the tag itself. This worked with the old format, but in the new format the space these bits take up must be consistent for each tag type. The tradeoff is that the new tag format allows for up to 256 different global states which may be useful in the future (for example, a global free list). The new format encodes this info in the data blob, using an additional word of storage. This word is actually formatted the same as though it was a tag, which simplified internal handling and may allow other tag types in the future. Format for global state: [---- 96 bits ----] [1|- 11 -|- 10 -|- 10 -|--- 64 ---] ^ ^ ^ ^ ^- move dir pair | | | \-------------------------- unused, must be 0s | | \--------------------------------- move id | \---------------------------------------- type, 0xfff for move \--------------------------------------------- has orphans This also included another iteration over globals (renamed to gstate) with some simplifications to how globals are handled.
This commit is contained in:
		
							
								
								
									
										397
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										397
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -331,56 +331,48 @@ struct lfs_diskoff { | ||||
|     lfs_off_t off; | ||||
| }; | ||||
|  | ||||
| // operations on set of globals | ||||
| static inline void lfs_global_xor(struct lfs_globals *a, | ||||
|         const struct lfs_globals *b) { | ||||
|     uint32_t *a32 = (uint32_t *)a; | ||||
|     const uint32_t *b32 = (const uint32_t *)b; | ||||
|     for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) { | ||||
|         a32[i] ^= b32[i]; | ||||
| // operations on global state | ||||
| static inline void lfs_gstate_xor(struct lfs_gstate *a, | ||||
|         const struct lfs_gstate *b) { | ||||
|     for (int i = 0; i < 3; i++) { | ||||
|         ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline bool lfs_global_iszero(const struct lfs_globals *a) { | ||||
|     const uint32_t *a32 = (const uint32_t *)a; | ||||
|     for (unsigned i = 0; i < sizeof(struct lfs_globals)/4; i++) { | ||||
|         if (a32[i] != 0) { | ||||
| static inline bool lfs_gstate_iszero(const struct lfs_gstate *a) { | ||||
|     for (int i = 0; i < 3; i++) { | ||||
|         if (((uint32_t*)a)[i] != 0) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
|  | ||||
| static inline void lfs_global_zero(struct lfs_globals *a) { | ||||
|     lfs_global_xor(a, a); | ||||
| static inline bool lfs_gstate_hasorphans(const struct lfs_gstate *a) { | ||||
|     return lfs_tag_size(a->tag); | ||||
| } | ||||
|  | ||||
| static inline void lfs_global_fromle32(struct lfs_globals *a) { | ||||
|     lfs_pair_fromle32(a->pair); | ||||
|     a->id = lfs_fromle16(a->id); | ||||
| static inline uint8_t lfs_gstate_getorphans(const struct lfs_gstate *a) { | ||||
|     return lfs_tag_size(a->tag); | ||||
| } | ||||
|  | ||||
| static inline void lfs_global_tole32(struct lfs_globals *a) { | ||||
|     lfs_pair_tole32(a->pair); | ||||
|     a->id = lfs_tole16(a->id); | ||||
| static inline bool lfs_gstate_hasmove(const struct lfs_gstate *a) { | ||||
|     return lfs_tag_type1(a->tag); | ||||
| } | ||||
|  | ||||
| static inline void lfs_global_move(lfs_t *lfs, | ||||
|         bool hasmove, const lfs_block_t pair[2], uint16_t id) { | ||||
|     lfs_global_fromle32(&lfs->locals); | ||||
|     lfs_global_xor(&lfs->locals, &lfs->globals); | ||||
|     lfs->globals.hasmove = hasmove; | ||||
|     lfs->globals.pair[0] = pair[0]; | ||||
|     lfs->globals.pair[1] = pair[1]; | ||||
|     lfs->globals.id      = id; | ||||
|     lfs_global_xor(&lfs->locals, &lfs->globals); | ||||
|     lfs_global_tole32(&lfs->locals); | ||||
| static inline bool lfs_gstate_hasmovehere(const struct lfs_gstate *a, | ||||
|         const lfs_block_t *pair) { | ||||
|     return lfs_tag_type1(a->tag) && lfs_pair_cmp(a->pair, pair) == 0; | ||||
| } | ||||
|  | ||||
| static inline void lfs_global_orphans(lfs_t *lfs, int8_t orphans) { | ||||
|     lfs->locals.orphans ^= (lfs->globals.orphans == 0); | ||||
|     lfs->globals.orphans += orphans; | ||||
|     lfs->locals.orphans ^= (lfs->globals.orphans == 0); | ||||
| static inline void lfs_gstate_fromle32(struct lfs_gstate *a) { | ||||
|     for (int i = 0; i < 3; i++) { | ||||
|         ((uint32_t*)a)[i] = lfs_fromle32(((uint32_t*)a)[i]); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static inline void lfs_gstate_tole32(struct lfs_gstate *a) { | ||||
|     lfs_gstate_fromle32(a); | ||||
| } | ||||
|  | ||||
| // other endianness operations | ||||
| @@ -427,6 +419,9 @@ static lfs_stag_t lfs_fs_parent(lfs_t *lfs, const lfs_block_t dir[2], | ||||
|         lfs_mdir_t *parent); | ||||
| static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         const lfs_block_t oldpair[2], lfs_block_t newpair[2]); | ||||
| static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans); | ||||
| static void lfs_fs_prepmove(lfs_t *lfs, | ||||
|         uint16_t id, const lfs_block_t pair[2]); | ||||
| static int lfs_fs_forceconsistency(lfs_t *lfs); | ||||
| static int lfs_deinit(lfs_t *lfs); | ||||
|  | ||||
| @@ -501,9 +496,8 @@ static lfs_stag_t lfs_dir_get(lfs_t *lfs, const lfs_mdir_t *dir, | ||||
|     lfs_tag_t ntag = dir->etag; | ||||
|     lfs_stag_t gdiff = 0; | ||||
|  | ||||
|     if (lfs->globals.hasmove && | ||||
|             lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0 && | ||||
|             lfs_tag_id(gtag) <= lfs->globals.id) { | ||||
|     if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair) && | ||||
|             lfs_tag_id(gtag) <= lfs_tag_id(lfs->gstate.tag)) { | ||||
|         // synthetic moves | ||||
|         gdiff -= LFS_MKTAG(0, 1, 0); | ||||
|     } | ||||
| @@ -582,13 +576,12 @@ static int lfs_dir_traverse_filter(void *p, | ||||
|  | ||||
| static int lfs_dir_traverse(lfs_t *lfs, | ||||
|         const lfs_mdir_t *dir, lfs_off_t off, lfs_tag_t ptag, | ||||
|         const struct lfs_mattr *attrs, int attrcount, | ||||
|         const struct lfs_mattr *attrs, int attrcount, bool hasseenmove, | ||||
|         lfs_tag_t tmask, lfs_tag_t ttag, | ||||
|         uint16_t begin, uint16_t end, int16_t diff, | ||||
|         int (*cb)(void *data, lfs_tag_t tag, const void *buffer), void *data) { | ||||
|     // iterate over directory and attrs, we iterate over attrs in reverse order | ||||
|     // which lets us "append" commits | ||||
|     while (off+lfs_tag_dsize(ptag) < dir->off || attrcount > 0) { | ||||
|     // iterate over directory and attrs | ||||
|     while (true) { | ||||
|         lfs_tag_t tag; | ||||
|         const void *buffer; | ||||
|         struct lfs_diskoff disk; | ||||
| @@ -606,7 +599,7 @@ static int lfs_dir_traverse(lfs_t *lfs, | ||||
|             disk.off = off+sizeof(lfs_tag_t); | ||||
|             buffer = &disk; | ||||
|             ptag = tag; | ||||
|         } else { | ||||
|         } else if (attrcount > 0) { | ||||
|             const struct lfs_mattr *a = attrs; | ||||
|             for (int j = 0; j < attrcount-1; j++) { | ||||
|                 a = a->next; | ||||
| @@ -615,6 +608,15 @@ static int lfs_dir_traverse(lfs_t *lfs, | ||||
|             tag = a->tag; | ||||
|             buffer = a->buffer; | ||||
|             attrcount -= 1; | ||||
|         } else if (!hasseenmove && | ||||
|                 lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { | ||||
|             // Wait, we have pending move? Handle this here (we need to | ||||
|             // or else we risk letting moves fall out of date) | ||||
|             tag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); | ||||
|             buffer = NULL; | ||||
|             hasseenmove = true; | ||||
|         } else { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         lfs_tag_t mask = LFS_MKTAG(0x7ff, 0, 0); | ||||
| @@ -627,7 +629,7 @@ static int lfs_dir_traverse(lfs_t *lfs, | ||||
|         if (lfs_tag_id(tmask) != 0) { | ||||
|             // scan for duplicates and update tag based on creates/deletes | ||||
|             int filter = lfs_dir_traverse(lfs, | ||||
|                     dir, off, ptag, attrs, attrcount, | ||||
|                     dir, off, ptag, attrs, attrcount, hasseenmove, | ||||
|                     0, 0, 0, 0, 0, | ||||
|                     lfs_dir_traverse_filter, &tag); | ||||
|             if (filter < 0) { | ||||
| @@ -649,7 +651,7 @@ static int lfs_dir_traverse(lfs_t *lfs, | ||||
|             uint16_t fromid = lfs_tag_size(tag); | ||||
|             uint16_t toid = lfs_tag_id(tag); | ||||
|             int err = lfs_dir_traverse(lfs, | ||||
|                     buffer, 0, 0xffffffff, NULL, 0, | ||||
|                     buffer, 0, 0xffffffff, NULL, 0, true, | ||||
|                     LFS_MKTAG(0x600, 0x3ff, 0), | ||||
|                     LFS_MKTAG(LFS_TYPE_STRUCT, 0, 0), | ||||
|                     fromid, fromid+1, toid-fromid+diff, | ||||
| @@ -672,8 +674,6 @@ static int lfs_dir_traverse(lfs_t *lfs, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
| @@ -824,10 +824,10 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|                 } | ||||
|             } else if (lfs_tag_type1(tag) == LFS_TYPE_TAIL) { | ||||
|                 tempsplit = (lfs_tag_chunk(tag) & 1); | ||||
|  | ||||
|                 err = lfs_bd_read(lfs, | ||||
|                         &lfs->pcache, &lfs->rcache, lfs->cfg->block_size, | ||||
|                         dir->pair[0], off+sizeof(tag), | ||||
|                         &temptail, sizeof(temptail)); | ||||
|                         dir->pair[0], off+sizeof(tag), &temptail, 8); | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         dir->erased = false; | ||||
| @@ -863,12 +863,11 @@ static lfs_stag_t lfs_dir_fetchmatch(lfs_t *lfs, | ||||
|         // consider what we have good enough | ||||
|         if (dir->off > 0) { | ||||
|             // synthetic move | ||||
|             if (lfs->globals.hasmove && | ||||
|                     lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { | ||||
|                 if (lfs->globals.id == lfs_tag_id(besttag)) { | ||||
|             if (lfs_gstate_hasmovehere(&lfs->gstate, dir->pair)) { | ||||
|                 if (lfs_tag_id(lfs->gstate.tag) == lfs_tag_id(besttag)) { | ||||
|                     besttag |= 0x80000000; | ||||
|                 } else if (besttag != -1 && | ||||
|                         lfs->globals.id < lfs_tag_id(besttag)) { | ||||
|                         lfs_tag_id(lfs->gstate.tag) < lfs_tag_id(besttag)) { | ||||
|                     besttag -= LFS_MKTAG(0, 1, 0); | ||||
|                 } | ||||
|             } | ||||
| @@ -904,20 +903,18 @@ static int lfs_dir_fetch(lfs_t *lfs, | ||||
|     return lfs_dir_fetchmatch(lfs, dir, pair, -1, 0, NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| static int lfs_dir_getglobals(lfs_t *lfs, const lfs_mdir_t *dir, | ||||
|         struct lfs_globals *globals) { | ||||
|     struct lfs_globals locals; | ||||
|     lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x700, 0, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_GLOBALS, 0, 10), &locals); | ||||
| static int lfs_dir_getgstate(lfs_t *lfs, const lfs_mdir_t *dir, | ||||
|         struct lfs_gstate *gstate) { | ||||
|     struct lfs_gstate temp; | ||||
|     lfs_stag_t res = lfs_dir_get(lfs, dir, LFS_MKTAG(0x7ff, 0, 0), | ||||
|             LFS_MKTAG(LFS_TYPE_MOVESTATE, 0, sizeof(temp)), &temp); | ||||
|     if (res < 0 && res != LFS_ERR_NOENT) { | ||||
|         return res; | ||||
|     } | ||||
|  | ||||
|     if (res != LFS_ERR_NOENT) { | ||||
|         locals.hasmove = (lfs_tag_type3(res) & 2); | ||||
|         locals.orphans = (lfs_tag_type3(res) & 1); | ||||
|         // xor together to find resulting globals | ||||
|         lfs_global_xor(globals, &locals); | ||||
|         // xor together to find resulting gstate | ||||
|         lfs_gstate_xor(gstate, &temp); | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| @@ -1158,13 +1155,6 @@ static int lfs_dir_commitattr(lfs_t *lfs, struct lfs_commit *commit, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_commitglobals(lfs_t *lfs, struct lfs_commit *commit, | ||||
|         struct lfs_globals *globals) { | ||||
|     return lfs_dir_commitattr(lfs, commit, | ||||
|             LFS_MKTAG(LFS_TYPE_GLOBALS + 2*globals->hasmove + globals->orphans, | ||||
|                 0x3ff, 10), globals); | ||||
| } | ||||
|  | ||||
| static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | ||||
|     // align to program units | ||||
|     lfs_off_t off = lfs_alignup(commit->off + 2*sizeof(uint32_t), | ||||
| @@ -1181,8 +1171,8 @@ static int lfs_dir_commitcrc(lfs_t *lfs, struct lfs_commit *commit) { | ||||
|  | ||||
|     // build crc tag | ||||
|     bool reset = ~lfs_frombe32(tag) >> 31; | ||||
|     tag = LFS_MKTAG(LFS_TYPE_CRC + reset, | ||||
|             0x3ff, off - (commit->off+sizeof(lfs_tag_t))); | ||||
|     tag = LFS_MKTAG(LFS_TYPE_CRC + reset, 0x3ff, | ||||
|         off - (commit->off+sizeof(lfs_tag_t))); | ||||
|  | ||||
|     // write out crc | ||||
|     uint32_t footer[2]; | ||||
| @@ -1267,23 +1257,24 @@ static int lfs_dir_alloc(lfs_t *lfs, lfs_mdir_t *dir) { | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, const lfs_mdir_t *tail) { | ||||
|     // steal tail | ||||
|     dir->tail[0] = tail->tail[0]; | ||||
|     dir->tail[1] = tail->tail[1]; | ||||
|     dir->split = tail->split; | ||||
|  | ||||
| static int lfs_dir_drop(lfs_t *lfs, lfs_mdir_t *dir, lfs_mdir_t *tail) { | ||||
|     // steal state | ||||
|     int err = lfs_dir_getglobals(lfs, tail, &lfs->locals); | ||||
|     int err = lfs_dir_getgstate(lfs, tail, &lfs->gdelta); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // update pred's tail | ||||
|     return lfs_dir_commit(lfs, dir, | ||||
|             LFS_MKATTR(LFS_TYPE_TAIL + dir->split, | ||||
|                 0x3ff, dir->tail, sizeof(dir->tail), | ||||
|     // steal tail | ||||
|     lfs_pair_tole32(tail->tail); | ||||
|     err = lfs_dir_commit(lfs, dir, | ||||
|             LFS_MKATTR(LFS_TYPE_TAIL + tail->split, 0x3ff, tail->tail, 8, | ||||
|             NULL)); | ||||
|     lfs_pair_fromle32(tail->tail); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int lfs_dir_split(lfs_t *lfs, | ||||
| @@ -1341,7 +1332,6 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|         lfs_mdir_t *source, uint16_t begin, uint16_t end) { | ||||
|     // save some state in case block is bad | ||||
|     const lfs_block_t oldpair[2] = {dir->pair[1], dir->pair[0]}; | ||||
|     struct lfs_globals globals, locals; | ||||
|     bool relocated = false; | ||||
|     bool exhausted = false; | ||||
|  | ||||
| @@ -1349,7 +1339,7 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|         // find size | ||||
|         lfs_size_t size = 0; | ||||
|         int err = lfs_dir_traverse(lfs, | ||||
|                 source, 0, 0xffffffff, attrs, attrcount, | ||||
|                 source, 0, 0xffffffff, attrs, attrcount, false, | ||||
|                 LFS_MKTAG(0x400, 0x3ff, 0), | ||||
|                 LFS_MKTAG(LFS_TYPE_NAME, 0, 0), | ||||
|                 begin, end, -begin, | ||||
| @@ -1358,10 +1348,10 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // space is complicated, we need room for tail, crc, globals, | ||||
|         // space is complicated, we need room for tail, crc, gstate, | ||||
|         // cleanup delete, and we cap at half a block to give room | ||||
|         // for metadata updates | ||||
|         if (size <= lfs_min(lfs->cfg->block_size - 38, | ||||
|         if (size <= lfs_min(lfs->cfg->block_size - 36, | ||||
|                 lfs_alignup(lfs->cfg->block_size/2, lfs->cfg->prog_size))) { | ||||
|             break; | ||||
|         } | ||||
| @@ -1376,7 +1366,7 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|             // if we fail to split, we may be able to overcompact, unless | ||||
|             // we're too big for even the full block, in which case our | ||||
|             // only option is to error | ||||
|             if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 38) { | ||||
|             if (err == LFS_ERR_NOSPC && size <= lfs->cfg->block_size - 36) { | ||||
|                 break; | ||||
|             } | ||||
|             return err; | ||||
| @@ -1424,9 +1414,7 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|         if (true) { | ||||
|             // There's nothing special about our global delta, so feed it into | ||||
|             // our local global delta | ||||
|             globals = lfs->globals; | ||||
|             locals = lfs->locals; | ||||
|             int err = lfs_dir_getglobals(lfs, dir, &locals); | ||||
|             int err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
| @@ -1465,7 +1453,7 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|  | ||||
|             // traverse the directory, this time writing out all unique tags | ||||
|             err = lfs_dir_traverse(lfs, | ||||
|                     source, 0, 0xffffffff, attrs, attrcount, | ||||
|                     source, 0, 0xffffffff, attrs, attrcount, false, | ||||
|                     LFS_MKTAG(0x400, 0x3ff, 0), | ||||
|                     LFS_MKTAG(LFS_TYPE_NAME, 0, 0), | ||||
|                     begin, end, -begin, | ||||
| @@ -1478,27 +1466,32 @@ static int lfs_dir_compact(lfs_t *lfs, | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             if (!relocated && !lfs_global_iszero(&locals)) { | ||||
|                 // commit any globals, unless we're relocating, | ||||
|                 // in which case our parent will steal our globals | ||||
|                 err = lfs_dir_commitglobals(lfs, &commit, &locals); | ||||
|             // commit tail, which may be new after last size check | ||||
|             if (!lfs_pair_isnull(dir->tail)) { | ||||
|                 lfs_pair_tole32(dir->tail); | ||||
|                 err = lfs_dir_commitattr(lfs, &commit, | ||||
|                         LFS_MKTAG(LFS_TYPE_TAIL + dir->split, 0x3ff, 8), | ||||
|                         dir->tail); | ||||
|                 lfs_pair_fromle32(dir->tail); | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         goto relocate; | ||||
|                     } | ||||
|                     return err; | ||||
|                 } | ||||
|  | ||||
|                 lfs_global_zero(&locals); | ||||
|             } | ||||
|  | ||||
|             if (!lfs_pair_isnull(dir->tail)) { | ||||
|                 // commit tail, which may be new after last size check | ||||
|                 lfs_pair_tole32(dir->tail); | ||||
|             // need to update gstate now that we've acknowledged moves | ||||
|             if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { | ||||
|                 lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||
|             } | ||||
|  | ||||
|             if (!relocated && !lfs_gstate_iszero(&lfs->gdelta)) { | ||||
|                 // commit any globals, unless we're relocating, | ||||
|                 // in which case our parent will steal our globals | ||||
|                 err = lfs_dir_commitattr(lfs, &commit, | ||||
|                         LFS_MKTAG(LFS_TYPE_TAIL + dir->split, | ||||
|                             0x3ff, sizeof(dir->tail)), dir->tail); | ||||
|                 lfs_pair_fromle32(dir->tail); | ||||
|                         LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, | ||||
|                             sizeof(lfs->gdelta)), &lfs->gdelta); | ||||
|                 if (err) { | ||||
|                     if (err == LFS_ERR_CORRUPT) { | ||||
|                         goto relocate; | ||||
| @@ -1547,11 +1540,10 @@ relocate: | ||||
|         continue; | ||||
|     } | ||||
|  | ||||
|     // successful commit, update globals | ||||
|     lfs->globals = globals; | ||||
|     lfs->locals = locals; | ||||
|  | ||||
|     if (relocated) { | ||||
|     if (!relocated) { | ||||
|         lfs->gstate = lfs->gpending; | ||||
|         lfs->gdelta = (struct lfs_gstate){0}; | ||||
|     } else { | ||||
|         // update references if we relocated | ||||
|         LFS_DEBUG("Relocating %"PRIu32" %"PRIu32" to %"PRIu32" %"PRIu32, | ||||
|                 oldpair[0], oldpair[1], dir->pair[0], dir->pair[1]); | ||||
| @@ -1566,47 +1558,35 @@ relocate: | ||||
|  | ||||
| static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|         const struct lfs_mattr *attrs) { | ||||
|     // check for globals work | ||||
|     struct lfs_mattr cancelattr; | ||||
|     struct lfs_globals cancels; | ||||
|     lfs_global_zero(&cancels); | ||||
|     if (lfs->globals.hasmove && | ||||
|             lfs_pair_cmp(dir->pair, lfs->globals.pair) == 0) { | ||||
|         // Wait, we have the move? Just cancel this out here | ||||
|         // We need to, or else the move can become outdated | ||||
|         cancelattr.tag = LFS_MKTAG(LFS_TYPE_DELETE, lfs->globals.id, 0); | ||||
|         cancelattr.next = attrs; | ||||
|         attrs = &cancelattr; | ||||
|  | ||||
|         cancels.hasmove = lfs->globals.hasmove; | ||||
|         cancels.pair[0] = lfs->globals.pair[0]; | ||||
|         cancels.pair[1] = lfs->globals.pair[1]; | ||||
|         cancels.id      = lfs->globals.id; | ||||
|         lfs_global_fromle32(&lfs->locals); | ||||
|         lfs_global_xor(&lfs->locals, &cancels); | ||||
|         lfs_global_tole32(&lfs->locals); | ||||
|     } | ||||
|  | ||||
|     struct lfs_globals globals = lfs->globals; | ||||
|     struct lfs_globals locals = lfs->locals; | ||||
|  | ||||
|     // calculate new directory size | ||||
|     // calculate changes to the directory | ||||
|     lfs_tag_t deletetag = 0xffffffff; | ||||
|     lfs_tag_t createtag = 0xffffffff; | ||||
|     int attrcount = 0; | ||||
|     for (const struct lfs_mattr *a = attrs; a; a = a->next) { | ||||
|         if (lfs_tag_type3(a->tag) == LFS_TYPE_CREATE) { | ||||
|             dir->count += 1; | ||||
|             createtag = a->tag; | ||||
|             dir->count += 1; | ||||
|         } else if (lfs_tag_type3(a->tag) == LFS_TYPE_DELETE) { | ||||
|             deletetag = a->tag; | ||||
|             LFS_ASSERT(dir->count > 0); | ||||
|             dir->count -= 1; | ||||
|             deletetag = a->tag; | ||||
|         } else if (lfs_tag_type1(a->tag) == LFS_TYPE_TAIL) { | ||||
|             dir->tail[0] = ((lfs_block_t*)a->buffer)[0]; | ||||
|             dir->tail[1] = ((lfs_block_t*)a->buffer)[1]; | ||||
|             dir->split = (lfs_tag_chunk(a->tag) & 1); | ||||
|             lfs_pair_fromle32(dir->tail); | ||||
|         } | ||||
|  | ||||
|         attrcount += 1; | ||||
|     } | ||||
|  | ||||
|     // do we have a pending move? | ||||
|     if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { | ||||
|         deletetag = lfs->gpending.tag & LFS_MKTAG(0x7ff, 0x3ff, 0); | ||||
|         LFS_ASSERT(dir->count > 0); | ||||
|         dir->count -= 1; | ||||
|     } | ||||
|  | ||||
|     // should we actually drop the directory block? | ||||
|     if (lfs_tag_isvalid(deletetag) && dir->count == 0) { | ||||
|         lfs_mdir_t pdir; | ||||
| @@ -1635,7 +1615,7 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|         // traverse attrs that need to be written out | ||||
|         lfs_pair_tole32(dir->tail); | ||||
|         int err = lfs_dir_traverse(lfs, | ||||
|                 dir, dir->off, dir->etag, attrs, attrcount, | ||||
|                 dir, dir->off, dir->etag, attrs, attrcount, false, | ||||
|                 0, 0, 0, 0, 0, | ||||
|                 lfs_dir_commit_commit, &(struct lfs_dir_commit_commit){ | ||||
|                     lfs, &commit}); | ||||
| @@ -1647,22 +1627,27 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // need to update gstate now that we've acknowledged moves | ||||
|         if (lfs_gstate_hasmovehere(&lfs->gpending, dir->pair)) { | ||||
|             lfs_fs_prepmove(lfs, 0x3ff, NULL); | ||||
|         } | ||||
|  | ||||
|         // commit any global diffs if we have any | ||||
|         if (!lfs_global_iszero(&locals)) { | ||||
|             err = lfs_dir_getglobals(lfs, dir, &locals); | ||||
|         if (!lfs_gstate_iszero(&lfs->gdelta)) { | ||||
|             err = lfs_dir_getgstate(lfs, dir, &lfs->gdelta); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             err = lfs_dir_commitglobals(lfs, &commit, &locals); | ||||
|             err = lfs_dir_commitattr(lfs, &commit, | ||||
|                     LFS_MKTAG(LFS_TYPE_MOVESTATE, 0x3ff, | ||||
|                         sizeof(lfs->gdelta)), &lfs->gdelta); | ||||
|             if (err) { | ||||
|                 if (err == LFS_ERR_NOSPC || err == LFS_ERR_CORRUPT) { | ||||
|                     goto compact; | ||||
|                 } | ||||
|                 return err; | ||||
|             } | ||||
|  | ||||
|             lfs_global_zero(&locals); | ||||
|         } | ||||
|  | ||||
|         // finalize commit with the crc | ||||
| @@ -1677,9 +1662,9 @@ static int lfs_dir_commit(lfs_t *lfs, lfs_mdir_t *dir, | ||||
|         // successful commit, update dir | ||||
|         dir->off = commit.off; | ||||
|         dir->etag = commit.ptag; | ||||
|         // successful commit, update globals | ||||
|         lfs->globals = globals; | ||||
|         lfs->locals = locals; | ||||
|         // successful commit, update gstate | ||||
|         lfs->gstate = lfs->gpending; | ||||
|         lfs->gdelta = (struct lfs_gstate){0}; | ||||
|     } else { | ||||
| compact: | ||||
|         // fall back to compaction | ||||
| @@ -1692,9 +1677,6 @@ compact: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // update globals that are affected | ||||
|     lfs_global_xor(&lfs->globals, &cancels); | ||||
|  | ||||
|     // update any directories that are affected | ||||
|     lfs_mdir_t copy = *dir; | ||||
|  | ||||
| @@ -1772,31 +1754,28 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|     } | ||||
|  | ||||
|     // setup dir | ||||
|     dir.tail[0] = pred.tail[0]; | ||||
|     dir.tail[1] = pred.tail[1]; | ||||
|     err = lfs_dir_commit(lfs, &dir, NULL); | ||||
|     lfs_pair_tole32(pred.tail); | ||||
|     err = lfs_dir_commit(lfs, &dir, | ||||
|             LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pred.tail, 8, | ||||
|             NULL)); | ||||
|     lfs_pair_fromle32(pred.tail); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
|  | ||||
|     // current block end of list? | ||||
|     if (!cwd.split) { | ||||
|         // update atomically | ||||
|         cwd.tail[0] = dir.pair[0]; | ||||
|         cwd.tail[1] = dir.pair[1]; | ||||
|     } else { | ||||
|     if (cwd.split) { | ||||
|         // update tails, this creates a desync | ||||
|         pred.tail[0] = dir.pair[0]; | ||||
|         pred.tail[1] = dir.pair[1]; | ||||
|         lfs_global_orphans(lfs, +1); | ||||
|         lfs_fs_preporphans(lfs, +1); | ||||
|         lfs_pair_tole32(dir.pair); | ||||
|         err = lfs_dir_commit(lfs, &pred, | ||||
|                 LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, | ||||
|                     pred.tail, sizeof(pred.tail), | ||||
|                 LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, | ||||
|                 NULL)); | ||||
|         lfs_pair_fromle32(dir.pair); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|         lfs_global_orphans(lfs, -1); | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|     } | ||||
|  | ||||
|     // now insert into our parent block | ||||
| @@ -1806,8 +1785,8 @@ int lfs_mkdir(lfs_t *lfs, const char *path) { | ||||
|             LFS_MKATTR(LFS_TYPE_DIR, id, path, nlen, | ||||
|             LFS_MKATTR(LFS_TYPE_CREATE, id, NULL, 0, | ||||
|             (!cwd.split) | ||||
|                 ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, | ||||
|                     cwd.tail, sizeof(cwd.tail), NULL) | ||||
|                 ? LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, dir.pair, 8, | ||||
|                   NULL) | ||||
|                 : NULL)))); | ||||
|     lfs_pair_fromle32(dir.pair); | ||||
|     if (err) { | ||||
| @@ -2867,7 +2846,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|         } | ||||
|  | ||||
|         // mark fs as orphaned | ||||
|         lfs_global_orphans(lfs, +1); | ||||
|         lfs_fs_preporphans(lfs, +1); | ||||
|     } | ||||
|  | ||||
|     // delete the entry | ||||
| @@ -2880,7 +2859,7 @@ int lfs_remove(lfs_t *lfs, const char *path) { | ||||
|  | ||||
|     if (lfs_tag_type3(tag) == LFS_TYPE_DIR) { | ||||
|         // fix orphan | ||||
|         lfs_global_orphans(lfs, -1); | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|  | ||||
|         err = lfs_fs_pred(lfs, dir.pair, &cwd); | ||||
|         if (err) { | ||||
| @@ -2948,7 +2927,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         } | ||||
|  | ||||
|         // mark fs as orphaned | ||||
|         lfs_global_orphans(lfs, +1); | ||||
|         lfs_fs_preporphans(lfs, +1); | ||||
|     } | ||||
|  | ||||
|     // create move to fix later | ||||
| @@ -2960,7 +2939,8 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|         // is a bit messy | ||||
|         newoldtagid += 1; | ||||
|     } | ||||
|     lfs_global_move(lfs, true, oldcwd.pair, newoldtagid); | ||||
|  | ||||
|     lfs_fs_prepmove(lfs, newoldtagid, oldcwd.pair); | ||||
|  | ||||
|     // move over all attributes | ||||
|     err = lfs_dir_commit(lfs, &newcwd, | ||||
| @@ -2985,7 +2965,7 @@ int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath) { | ||||
|  | ||||
|     if (prevtag != LFS_ERR_NOENT && lfs_tag_type3(prevtag) == LFS_TYPE_DIR) { | ||||
|         // fix orphan | ||||
|         lfs_global_orphans(lfs, -1); | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|  | ||||
|         err = lfs_fs_pred(lfs, prevdir.pair, &newcwd); | ||||
|         if (err) { | ||||
| @@ -3155,8 +3135,9 @@ static int lfs_init(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|     lfs->root[1] = 0xffffffff; | ||||
|     lfs->mlist = NULL; | ||||
|     lfs->seed = 0; | ||||
|     lfs_global_zero(&lfs->globals); | ||||
|     lfs_global_zero(&lfs->locals); | ||||
|     lfs->gstate = (struct lfs_gstate){0}; | ||||
|     lfs->gpending = (struct lfs_gstate){0}; | ||||
|     lfs->gdelta = (struct lfs_gstate){0}; | ||||
|  | ||||
|     return 0; | ||||
|  | ||||
| @@ -3334,8 +3315,8 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // has globals? | ||||
|         err = lfs_dir_getglobals(lfs, &dir, &lfs->locals); | ||||
|         // has gstate? | ||||
|         err = lfs_dir_getgstate(lfs, &dir, &lfs->gpending); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -3347,13 +3328,14 @@ int lfs_mount(lfs_t *lfs, const struct lfs_config *cfg) { | ||||
|         goto cleanup; | ||||
|     } | ||||
|  | ||||
|     // update littlefs with globals | ||||
|     lfs_global_fromle32(&lfs->locals); | ||||
|     lfs_global_xor(&lfs->globals, &lfs->locals); | ||||
|     lfs_global_zero(&lfs->locals); | ||||
|     if (lfs->globals.hasmove) { | ||||
|     // update littlefs with gstate | ||||
|     lfs_gstate_fromle32(&lfs->gpending); | ||||
|     lfs->gstate = lfs->gpending; | ||||
|     if (lfs_gstate_hasmove(&lfs->gstate)) { | ||||
|         LFS_DEBUG("Found move %"PRIu32" %"PRIu32" %"PRIu32, | ||||
|                 lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); | ||||
|                 lfs->gstate.pair[0], | ||||
|                 lfs->gstate.pair[1], | ||||
|                 lfs_tag_id(lfs->gstate.tag)); | ||||
|     } | ||||
|  | ||||
|     // setup free lookahead | ||||
| @@ -3531,7 +3513,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|  | ||||
|     if (tag != LFS_ERR_NOENT) { | ||||
|         // update disk, this creates a desync | ||||
|         lfs_global_orphans(lfs, +1); | ||||
|         lfs_fs_preporphans(lfs, +1); | ||||
|  | ||||
|         lfs_pair_tole32(newpair); | ||||
|         int err = lfs_dir_commit(lfs, &parent, | ||||
| @@ -3542,7 +3524,7 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|         } | ||||
|  | ||||
|         // next step, clean up orphans | ||||
|         lfs_global_orphans(lfs, -1); | ||||
|         lfs_fs_preporphans(lfs, -1); | ||||
|     } | ||||
|  | ||||
|     // find pred | ||||
| @@ -3554,12 +3536,11 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|     // if we can't find dir, it must be new | ||||
|     if (err != LFS_ERR_NOENT) { | ||||
|         // replace bad pair, either we clean up desync, or no desync occured | ||||
|         parent.tail[0] = newpair[0]; | ||||
|         parent.tail[1] = newpair[1]; | ||||
|         lfs_pair_tole32(newpair); | ||||
|         err = lfs_dir_commit(lfs, &parent, | ||||
|                 LFS_MKATTR(LFS_TYPE_TAIL + parent.split, | ||||
|                     0x3ff, parent.tail, sizeof(parent.tail), | ||||
|                 LFS_MKATTR(LFS_TYPE_TAIL + parent.split, 0x3ff, newpair, 8, | ||||
|                 NULL)); | ||||
|         lfs_pair_fromle32(newpair); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
| @@ -3568,18 +3549,52 @@ static int lfs_fs_relocate(lfs_t *lfs, | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static void lfs_fs_preporphans(lfs_t *lfs, int8_t orphans) { | ||||
|     lfs_gstate_fromle32(&lfs->gdelta); | ||||
|     lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending); | ||||
|  | ||||
|     lfs->gpending.tag += orphans; | ||||
|  | ||||
|     lfs->gdelta.tag ^= lfs_gstate_hasorphans(&lfs->gpending); | ||||
|     lfs_gstate_tole32(&lfs->gdelta); | ||||
| } | ||||
|  | ||||
| static void lfs_fs_prepmove(lfs_t *lfs, | ||||
|         uint16_t id, const lfs_block_t pair[2]) { | ||||
|     lfs_gstate_fromle32(&lfs->gdelta); | ||||
|     lfs_gstate_xor(&lfs->gdelta, &lfs->gpending); | ||||
|  | ||||
|     if (id != 0x3ff) { | ||||
|         lfs->gpending.tag = LFS_MKTAG(LFS_TYPE_DELETE, id, | ||||
|                 lfs_gstate_getorphans(&lfs->gpending)); | ||||
|         lfs->gpending.pair[0] = pair[0]; | ||||
|         lfs->gpending.pair[1] = pair[1]; | ||||
|     } else { | ||||
|         lfs->gpending.tag = LFS_MKTAG(0, 0, | ||||
|                 lfs_gstate_getorphans(&lfs->gpending)); | ||||
|         lfs->gpending.pair[0] = 0; | ||||
|         lfs->gpending.pair[1] = 0; | ||||
|     } | ||||
|  | ||||
|     lfs_gstate_xor(&lfs->gdelta, &lfs->gpending); | ||||
|     lfs_gstate_tole32(&lfs->gdelta); | ||||
| } | ||||
|  | ||||
|  | ||||
| static int lfs_fs_demove(lfs_t *lfs) { | ||||
|     if (!lfs->globals.hasmove) { | ||||
|     if (!lfs_gstate_hasmove(&lfs->gstate)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     // Fix bad moves | ||||
|     LFS_DEBUG("Fixing move %"PRIu32" %"PRIu32" %"PRIu32, | ||||
|             lfs->globals.pair[0], lfs->globals.pair[1], lfs->globals.id); | ||||
|             lfs->gstate.pair[0], | ||||
|             lfs->gstate.pair[1], | ||||
|             lfs_tag_id(lfs->gstate.tag)); | ||||
|  | ||||
|     // fetch and delete the moved entry | ||||
|     lfs_mdir_t movedir; | ||||
|     int err = lfs_dir_fetch(lfs, &movedir, lfs->globals.pair); | ||||
|     int err = lfs_dir_fetch(lfs, &movedir, lfs->gstate.pair); | ||||
|     if (err) { | ||||
|         return err; | ||||
|     } | ||||
| @@ -3594,7 +3609,7 @@ static int lfs_fs_demove(lfs_t *lfs) { | ||||
| } | ||||
|  | ||||
| static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|     if (!lfs->globals.orphans) { | ||||
|     if (!lfs_gstate_hasorphans(&lfs->gstate)) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -3644,12 +3659,11 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|                 LFS_DEBUG("Fixing half-orphan %"PRIu32" %"PRIu32, | ||||
|                         pair[0], pair[1]); | ||||
|  | ||||
|                 pdir.tail[0] = pair[0]; | ||||
|                 pdir.tail[1] = pair[1]; | ||||
|                 lfs_pair_tole32(pair); | ||||
|                 err = lfs_dir_commit(lfs, &pdir, | ||||
|                         LFS_MKATTR(LFS_TYPE_SOFTTAIL, | ||||
|                             0x3ff, pdir.tail, sizeof(pdir.tail), | ||||
|                         LFS_MKATTR(LFS_TYPE_SOFTTAIL, 0x3ff, pair, 8, | ||||
|                         NULL)); | ||||
|                 lfs_pair_fromle32(pair); | ||||
|                 if (err) { | ||||
|                     return err; | ||||
|                 } | ||||
| @@ -3662,7 +3676,8 @@ static int lfs_fs_deorphan(lfs_t *lfs) { | ||||
|     } | ||||
|  | ||||
|     // mark orphans as fixed | ||||
|     lfs_global_orphans(lfs, -lfs->globals.orphans); | ||||
|     lfs_fs_preporphans(lfs, -lfs_gstate_getorphans(&lfs->gstate)); | ||||
|     lfs->gstate = lfs->gpending; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										8
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -381,12 +381,10 @@ typedef struct lfs { | ||||
|     } *mlist; | ||||
|     uint32_t seed; | ||||
|  | ||||
|     struct lfs_globals { | ||||
|     struct lfs_gstate { | ||||
|         uint32_t tag; | ||||
|         lfs_block_t pair[2]; | ||||
|         uint16_t id; | ||||
|         bool hasmove; | ||||
|         uint8_t orphans; | ||||
|     } globals, locals; | ||||
|     } gstate, gpending, gdelta; | ||||
|  | ||||
|     struct lfs_free { | ||||
|         lfs_block_t off; | ||||
|   | ||||
							
								
								
									
										44
									
								
								lfs_util.h
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								lfs_util.h
									
									
									
									
									
								
							| @@ -164,28 +164,6 @@ static inline uint32_t lfs_tole32(uint32_t a) { | ||||
|     return lfs_fromle32(a); | ||||
| } | ||||
|  | ||||
| // Convert between 16-bit little-endian and native order | ||||
| static inline uint16_t lfs_fromle16(uint16_t a) { | ||||
| #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 !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__)) | ||||
|     return __builtin_bswap16(a); | ||||
| #else | ||||
|     return (((uint8_t*)&a)[0] <<  0) | | ||||
|            (((uint8_t*)&a)[1] <<  8); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static inline uint16_t lfs_tole16(uint16_t a) { | ||||
|     return lfs_fromle16(a); | ||||
| } | ||||
|  | ||||
| // Convert between 32-bit big-endian and native order | ||||
| static inline uint32_t lfs_frombe32(uint32_t a) { | ||||
| #if !defined(LFS_NO_INTRINSICS) && ( \ | ||||
| @@ -210,28 +188,6 @@ static inline uint32_t lfs_tobe32(uint32_t a) { | ||||
|     return lfs_frombe32(a); | ||||
| } | ||||
|  | ||||
| // Convert between 16-bit big-endian and native order | ||||
| static inline uint16_t lfs_frombe16(uint16_t a) { | ||||
| #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 __builtin_bswap16(a); | ||||
| #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__)) | ||||
|     return a; | ||||
| #else | ||||
|     return (((uint8_t*)&a)[0] <<  8) | | ||||
|            (((uint8_t*)&a)[1] <<  0); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static inline uint16_t lfs_tobe16(uint16_t a) { | ||||
|     return lfs_frombe16(a); | ||||
| } | ||||
|  | ||||
| // Calculate CRC-32 with polynomial = 0x04c11db7 | ||||
| uint32_t lfs_crc(uint32_t crc, const void *buffer, size_t size); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user