mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Generated v2 prefixes
This commit is contained in:
		
							
								
								
									
										8
									
								
								SPEC.md
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								SPEC.md
									
									
									
									
									
								
							| @@ -233,19 +233,19 @@ Metadata tag fields: | |||||||
|    into a 3-bit abstract type and an 8-bit chunk field. Note that the value |    into a 3-bit abstract type and an 8-bit chunk field. Note that the value | ||||||
|    `0x000` is invalid and not assigned a type. |    `0x000` is invalid and not assigned a type. | ||||||
|  |  | ||||||
| 3. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into |     1. **Type1 (3-bits)** - Abstract type of the tag. Groups the tags into | ||||||
|        8 categories that facilitate bitmasked lookups. |        8 categories that facilitate bitmasked lookups. | ||||||
|  |  | ||||||
| 4. **Chunk (8-bits)** - Chunk field used for various purposes by the different |     2. **Chunk (8-bits)** - Chunk field used for various purposes by the different | ||||||
|        abstract types.  type1+chunk+id form a unique identifier for each tag in the |        abstract types.  type1+chunk+id form a unique identifier for each tag in the | ||||||
|        metadata block. |        metadata block. | ||||||
|  |  | ||||||
| 5. **Id (10-bits)** - File id associated with the tag. Each file in a metadata | 3. **Id (10-bits)** - File id associated with the tag. Each file in a metadata | ||||||
|    block gets a unique id which is used to associate tags with that file. The |    block gets a unique id which is used to associate tags with that file. The | ||||||
|    special value `0x3ff` is used for any tags that are not associated with a |    special value `0x3ff` is used for any tags that are not associated with a | ||||||
|    file, such as directory and global metadata. |    file, such as directory and global metadata. | ||||||
|  |  | ||||||
| 6. **Length (10-bits)** - Length of the data in bytes. The special value | 4. **Length (10-bits)** - Length of the data in bytes. The special value | ||||||
|    `0x3ff` indicates that this tag has been deleted. |    `0x3ff` indicates that this tag has been deleted. | ||||||
|  |  | ||||||
| ## Metadata types | ## Metadata types | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ int lfs2_filebd_read(const struct lfs2_config *cfg, lfs2_block_t block, | |||||||
|     LFS2_ASSERT(size % cfg->read_size == 0); |     LFS2_ASSERT(size % cfg->read_size == 0); | ||||||
|     LFS2_ASSERT(block < cfg->block_count); |     LFS2_ASSERT(block < cfg->block_count); | ||||||
|  |  | ||||||
|     // zero for reproducability (in case file is truncated) |     // zero for reproducibility (in case file is truncated) | ||||||
|     if (bd->cfg->erase_value != -1) { |     if (bd->cfg->erase_value != -1) { | ||||||
|         memset(buffer, bd->cfg->erase_value, size); |         memset(buffer, bd->cfg->erase_value, size); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -32,10 +32,12 @@ int lfs2_rambd_createcfg(const struct lfs2_config *cfg, | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // zero for reproducability? |     // zero for reproducibility? | ||||||
|     if (bd->cfg->erase_value != -1) { |     if (bd->cfg->erase_value != -1) { | ||||||
|         memset(bd->buffer, bd->cfg->erase_value, |         memset(bd->buffer, bd->cfg->erase_value, | ||||||
|                 cfg->block_size * cfg->block_count); |                 cfg->block_size * cfg->block_count); | ||||||
|  |     } else { | ||||||
|  |         memset(bd->buffer, 0, cfg->block_size * cfg->block_count); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", 0); |     LFS2_RAMBD_TRACE("lfs2_rambd_createcfg -> %d", 0); | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								lfs2.c
									
									
									
									
									
								
							
							
						
						
									
										22
									
								
								lfs2.c
									
									
									
									
									
								
							| @@ -11,6 +11,7 @@ | |||||||
| #define LFS2_BLOCK_INLINE ((lfs2_block_t)-2) | #define LFS2_BLOCK_INLINE ((lfs2_block_t)-2) | ||||||
|  |  | ||||||
| /// Caching block device operations /// | /// Caching block device operations /// | ||||||
|  |  | ||||||
| static inline void lfs2_cache_drop(lfs2_t *lfs2, lfs2_cache_t *rcache) { | static inline void lfs2_cache_drop(lfs2_t *lfs2, lfs2_cache_t *rcache) { | ||||||
|     // do not zero, cheaper if cache is readonly or only going to be |     // do not zero, cheaper if cache is readonly or only going to be | ||||||
|     // written with identical data (during relocates) |     // written with identical data (during relocates) | ||||||
| @@ -268,22 +269,26 @@ static inline int lfs2_pair_cmp( | |||||||
|              paira[0] == pairb[1] || paira[1] == pairb[0]); |              paira[0] == pairb[1] || paira[1] == pairb[0]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
| static inline bool lfs2_pair_sync( | static inline bool lfs2_pair_sync( | ||||||
|         const lfs2_block_t paira[2], |         const lfs2_block_t paira[2], | ||||||
|         const lfs2_block_t pairb[2]) { |         const lfs2_block_t pairb[2]) { | ||||||
|     return (paira[0] == pairb[0] && paira[1] == pairb[1]) || |     return (paira[0] == pairb[0] && paira[1] == pairb[1]) || | ||||||
|            (paira[0] == pairb[1] && paira[1] == pairb[0]); |            (paira[0] == pairb[1] && paira[1] == pairb[0]); | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static inline void lfs2_pair_fromle32(lfs2_block_t pair[2]) { | static inline void lfs2_pair_fromle32(lfs2_block_t pair[2]) { | ||||||
|     pair[0] = lfs2_fromle32(pair[0]); |     pair[0] = lfs2_fromle32(pair[0]); | ||||||
|     pair[1] = lfs2_fromle32(pair[1]); |     pair[1] = lfs2_fromle32(pair[1]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
| static inline void lfs2_pair_tole32(lfs2_block_t pair[2]) { | static inline void lfs2_pair_tole32(lfs2_block_t pair[2]) { | ||||||
|     pair[0] = lfs2_tole32(pair[0]); |     pair[0] = lfs2_tole32(pair[0]); | ||||||
|     pair[1] = lfs2_tole32(pair[1]); |     pair[1] = lfs2_tole32(pair[1]); | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| // operations on 32-bit entry tags | // operations on 32-bit entry tags | ||||||
| typedef uint32_t lfs2_tag_t; | typedef uint32_t lfs2_tag_t; | ||||||
| @@ -365,6 +370,7 @@ static inline bool lfs2_gstate_iszero(const lfs2_gstate_t *a) { | |||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
| static inline bool lfs2_gstate_hasorphans(const lfs2_gstate_t *a) { | static inline bool lfs2_gstate_hasorphans(const lfs2_gstate_t *a) { | ||||||
|     return lfs2_tag_size(a->tag); |     return lfs2_tag_size(a->tag); | ||||||
| } | } | ||||||
| @@ -376,6 +382,7 @@ static inline uint8_t lfs2_gstate_getorphans(const lfs2_gstate_t *a) { | |||||||
| static inline bool lfs2_gstate_hasmove(const lfs2_gstate_t *a) { | static inline bool lfs2_gstate_hasmove(const lfs2_gstate_t *a) { | ||||||
|     return lfs2_tag_type1(a->tag); |     return lfs2_tag_type1(a->tag); | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| static inline bool lfs2_gstate_hasmovehere(const lfs2_gstate_t *a, | static inline bool lfs2_gstate_hasmovehere(const lfs2_gstate_t *a, | ||||||
|         const lfs2_block_t *pair) { |         const lfs2_block_t *pair) { | ||||||
| @@ -388,11 +395,13 @@ static inline void lfs2_gstate_fromle32(lfs2_gstate_t *a) { | |||||||
|     a->pair[1] = lfs2_fromle32(a->pair[1]); |     a->pair[1] = lfs2_fromle32(a->pair[1]); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
| static inline void lfs2_gstate_tole32(lfs2_gstate_t *a) { | static inline void lfs2_gstate_tole32(lfs2_gstate_t *a) { | ||||||
|     a->tag     = lfs2_tole32(a->tag); |     a->tag     = lfs2_tole32(a->tag); | ||||||
|     a->pair[0] = lfs2_tole32(a->pair[0]); |     a->pair[0] = lfs2_tole32(a->pair[0]); | ||||||
|     a->pair[1] = lfs2_tole32(a->pair[1]); |     a->pair[1] = lfs2_tole32(a->pair[1]); | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| // other endianness operations | // other endianness operations | ||||||
| static void lfs2_ctz_fromle32(struct lfs2_ctz *ctz) { | static void lfs2_ctz_fromle32(struct lfs2_ctz *ctz) { | ||||||
| @@ -416,6 +425,7 @@ static inline void lfs2_superblock_fromle32(lfs2_superblock_t *superblock) { | |||||||
|     superblock->attr_max    = lfs2_fromle32(superblock->attr_max); |     superblock->attr_max    = lfs2_fromle32(superblock->attr_max); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
| static inline void lfs2_superblock_tole32(lfs2_superblock_t *superblock) { | static inline void lfs2_superblock_tole32(lfs2_superblock_t *superblock) { | ||||||
|     superblock->version     = lfs2_tole32(superblock->version); |     superblock->version     = lfs2_tole32(superblock->version); | ||||||
|     superblock->block_size  = lfs2_tole32(superblock->block_size); |     superblock->block_size  = lfs2_tole32(superblock->block_size); | ||||||
| @@ -424,6 +434,7 @@ static inline void lfs2_superblock_tole32(lfs2_superblock_t *superblock) { | |||||||
|     superblock->file_max    = lfs2_tole32(superblock->file_max); |     superblock->file_max    = lfs2_tole32(superblock->file_max); | ||||||
|     superblock->attr_max    = lfs2_tole32(superblock->attr_max); |     superblock->attr_max    = lfs2_tole32(superblock->attr_max); | ||||||
| } | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
| #ifndef LFS2_NO_ASSERT | #ifndef LFS2_NO_ASSERT | ||||||
| static bool lfs2_mlist_isopen(struct lfs2_mlist *head, | static bool lfs2_mlist_isopen(struct lfs2_mlist *head, | ||||||
| @@ -1449,7 +1460,7 @@ static int lfs2_dir_alloc(lfs2_t *lfs2, lfs2_mdir_t *dir) { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // zero for reproducability in case initial block is unreadable |     // zero for reproducibility in case initial block is unreadable | ||||||
|     dir->rev = 0; |     dir->rev = 0; | ||||||
|  |  | ||||||
|     // rather than clobbering one of the blocks we just pretend |     // rather than clobbering one of the blocks we just pretend | ||||||
| @@ -1509,7 +1520,6 @@ static int lfs2_dir_split(lfs2_t *lfs2, | |||||||
|         lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, |         lfs2_mdir_t *dir, const struct lfs2_mattr *attrs, int attrcount, | ||||||
|         lfs2_mdir_t *source, uint16_t split, uint16_t end) { |         lfs2_mdir_t *source, uint16_t split, uint16_t end) { | ||||||
|     // create tail directory |     // create tail directory | ||||||
|     lfs2_alloc_ack(lfs2); |  | ||||||
|     lfs2_mdir_t tail; |     lfs2_mdir_t tail; | ||||||
|     int err = lfs2_dir_alloc(lfs2, &tail); |     int err = lfs2_dir_alloc(lfs2, &tail); | ||||||
|     if (err) { |     if (err) { | ||||||
| @@ -2730,7 +2740,6 @@ static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) { | |||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| #ifndef LFS2_READONLY |  | ||||||
| static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { | static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { | ||||||
|     if (file->flags & LFS2_F_READING) { |     if (file->flags & LFS2_F_READING) { | ||||||
|         if (!(file->flags & LFS2_F_INLINE)) { |         if (!(file->flags & LFS2_F_INLINE)) { | ||||||
| @@ -2739,6 +2748,7 @@ static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { | |||||||
|         file->flags &= ~LFS2_F_READING; |         file->flags &= ~LFS2_F_READING; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | #ifndef LFS2_READONLY | ||||||
|     if (file->flags & LFS2_F_WRITING) { |     if (file->flags & LFS2_F_WRITING) { | ||||||
|         lfs2_off_t pos = file->pos; |         lfs2_off_t pos = file->pos; | ||||||
|  |  | ||||||
| @@ -2805,10 +2815,10 @@ relocate: | |||||||
|  |  | ||||||
|         file->pos = pos; |         file->pos = pos; | ||||||
|     } |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #ifndef LFS2_READONLY | #ifndef LFS2_READONLY | ||||||
| static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { | static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { | ||||||
| @@ -3081,13 +3091,11 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, | |||||||
|         return npos; |         return npos; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| #ifndef LFS2_READONLY |  | ||||||
|     // write out everything beforehand, may be noop if rdonly |     // write out everything beforehand, may be noop if rdonly | ||||||
|     int err = lfs2_file_flush(lfs2, file); |     int err = lfs2_file_flush(lfs2, file); | ||||||
|     if (err) { |     if (err) { | ||||||
|         return err; |         return err; | ||||||
|     } |     } | ||||||
| #endif |  | ||||||
|  |  | ||||||
|     // update pos |     // update pos | ||||||
|     file->pos = npos; |     file->pos = npos; | ||||||
| @@ -4074,7 +4082,7 @@ static int lfs2_fs_relocate(lfs2_t *lfs2, | |||||||
|             lfs2_fs_prepmove(lfs2, 0x3ff, NULL); |             lfs2_fs_prepmove(lfs2, 0x3ff, NULL); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // replace bad pair, either we clean up desync, or no desync occured |         // replace bad pair, either we clean up desync, or no desync occurred | ||||||
|         lfs2_pair_tole32(newpair); |         lfs2_pair_tole32(newpair); | ||||||
|         err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS( |         err = lfs2_dir_commit(lfs2, &parent, LFS2_MKATTRS( | ||||||
|                 {LFS2_MKTAG_IF(moveid != 0x3ff, |                 {LFS2_MKTAG_IF(moveid != 0x3ff, | ||||||
|   | |||||||
							
								
								
									
										36
									
								
								lfs2.h
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								lfs2.h
									
									
									
									
									
								
							| @@ -159,49 +159,49 @@ struct lfs2_config { | |||||||
|     // information to the block device operations |     // information to the block device operations | ||||||
|     void *context; |     void *context; | ||||||
|  |  | ||||||
|     // Read a region in a block. Negative error codes are propogated |     // Read a region in a block. Negative error codes are propagated | ||||||
|     // to the user. |     // to the user. | ||||||
|     int (*read)(const struct lfs2_config *c, lfs2_block_t block, |     int (*read)(const struct lfs2_config *c, lfs2_block_t block, | ||||||
|             lfs2_off_t off, void *buffer, lfs2_size_t size); |             lfs2_off_t off, void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|     // Program a region in a block. The block must have previously |     // Program a region in a block. The block must have previously | ||||||
|     // been erased. Negative error codes are propogated to the user. |     // been erased. Negative error codes are propagated to the user. | ||||||
|     // May return LFS2_ERR_CORRUPT if the block should be considered bad. |     // May return LFS2_ERR_CORRUPT if the block should be considered bad. | ||||||
|     int (*prog)(const struct lfs2_config *c, lfs2_block_t block, |     int (*prog)(const struct lfs2_config *c, lfs2_block_t block, | ||||||
|             lfs2_off_t off, const void *buffer, lfs2_size_t size); |             lfs2_off_t off, const void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|     // Erase a block. A block must be erased before being programmed. |     // Erase a block. A block must be erased before being programmed. | ||||||
|     // The state of an erased block is undefined. Negative error codes |     // The state of an erased block is undefined. Negative error codes | ||||||
|     // are propogated to the user. |     // are propagated to the user. | ||||||
|     // May return LFS2_ERR_CORRUPT if the block should be considered bad. |     // May return LFS2_ERR_CORRUPT if the block should be considered bad. | ||||||
|     int (*erase)(const struct lfs2_config *c, lfs2_block_t block); |     int (*erase)(const struct lfs2_config *c, lfs2_block_t block); | ||||||
|  |  | ||||||
|     // Sync the state of the underlying block device. Negative error codes |     // Sync the state of the underlying block device. Negative error codes | ||||||
|     // are propogated to the user. |     // are propagated to the user. | ||||||
|     int (*sync)(const struct lfs2_config *c); |     int (*sync)(const struct lfs2_config *c); | ||||||
|  |  | ||||||
| #ifdef LFS2_THREADSAFE | #ifdef LFS2_THREADSAFE | ||||||
|     // Lock the underlying block device. Negative error codes |     // Lock the underlying block device. Negative error codes | ||||||
|     // are propogated to the user. |     // are propagated to the user. | ||||||
|     int (*lock)(const struct lfs2_config *c); |     int (*lock)(const struct lfs2_config *c); | ||||||
|  |  | ||||||
|     // Unlock the underlying block device. Negative error codes |     // Unlock the underlying block device. Negative error codes | ||||||
|     // are propogated to the user. |     // are propagated to the user. | ||||||
|     int (*unlock)(const struct lfs2_config *c); |     int (*unlock)(const struct lfs2_config *c); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|     // Minimum size of a block read. All read operations will be a |     // Minimum size of a block read in bytes. All read operations will be a | ||||||
|     // multiple of this value. |     // multiple of this value. | ||||||
|     lfs2_size_t read_size; |     lfs2_size_t read_size; | ||||||
|  |  | ||||||
|     // Minimum size of a block program. All program operations will be a |     // Minimum size of a block program in bytes. All program operations will be | ||||||
|     // multiple of this value. |     // a multiple of this value. | ||||||
|     lfs2_size_t prog_size; |     lfs2_size_t prog_size; | ||||||
|  |  | ||||||
|     // Size of an erasable block. This does not impact ram consumption and |     // Size of an erasable block in bytes. This does not impact ram consumption | ||||||
|     // may be larger than the physical erase size. However, non-inlined files |     // and may be larger than the physical erase size. However, non-inlined | ||||||
|     // take up at minimum one block. Must be a multiple of the read |     // files take up at minimum one block. Must be a multiple of the read and | ||||||
|     // and program sizes. |     // program sizes. | ||||||
|     lfs2_size_t block_size; |     lfs2_size_t block_size; | ||||||
|  |  | ||||||
|     // Number of erasable blocks on the device. |     // Number of erasable blocks on the device. | ||||||
| @@ -215,11 +215,11 @@ struct lfs2_config { | |||||||
|     // Set to -1 to disable block-level wear-leveling. |     // Set to -1 to disable block-level wear-leveling. | ||||||
|     int32_t block_cycles; |     int32_t block_cycles; | ||||||
|  |  | ||||||
|     // Size of block caches. Each cache buffers a portion of a block in RAM. |     // Size of block caches in bytes. Each cache buffers a portion of a block in | ||||||
|     // The littlefs needs a read cache, a program cache, and one additional |     // RAM. The littlefs needs a read cache, a program cache, and one additional | ||||||
|     // cache per file. Larger caches can improve performance by storing more |     // cache per file. Larger caches can improve performance by storing more | ||||||
|     // data and reducing the number of disk accesses. Must be a multiple of |     // data and reducing the number of disk accesses. Must be a multiple of the | ||||||
|     // the read and program sizes, and a factor of the block size. |     // read and program sizes, and a factor of the block size. | ||||||
|     lfs2_size_t cache_size; |     lfs2_size_t cache_size; | ||||||
|  |  | ||||||
|     // Size of the lookahead buffer in bytes. A larger lookahead buffer |     // Size of the lookahead buffer in bytes. A larger lookahead buffer | ||||||
| @@ -485,7 +485,7 @@ int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info); | |||||||
| // Returns the size of the attribute, or a negative error code on failure. | // Returns the size of the attribute, or a negative error code on failure. | ||||||
| // Note, the returned size is the size of the attribute on disk, irrespective | // Note, the returned size is the size of the attribute on disk, irrespective | ||||||
| // of the size of the buffer. This can be used to dynamically allocate a buffer | // of the size of the buffer. This can be used to dynamically allocate a buffer | ||||||
| // or check for existance. | // or check for existence. | ||||||
| lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, | lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, | ||||||
|         uint8_t type, void *buffer, lfs2_size_t size); |         uint8_t type, void *buffer, lfs2_size_t size); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -565,7 +565,7 @@ class TestSuite: | |||||||
|                     path=self.path)) |                     path=self.path)) | ||||||
|                 mk.write('\n') |                 mk.write('\n') | ||||||
|  |  | ||||||
|             # add truely global defines globally |             # add truly global defines globally | ||||||
|             for k, v in sorted(self.defines.items()): |             for k, v in sorted(self.defines.items()): | ||||||
|                 mk.write('%s.test: override CFLAGS += -D%s=%r\n' |                 mk.write('%s.test: override CFLAGS += -D%s=%r\n' | ||||||
|                     % (self.path, k, v)) |                     % (self.path, k, v)) | ||||||
| @@ -656,7 +656,7 @@ def main(**args): | |||||||
|         for path in glob.glob(testpath): |         for path in glob.glob(testpath): | ||||||
|             suites.append(TestSuite(path, classes, defines, filter, **args)) |             suites.append(TestSuite(path, classes, defines, filter, **args)) | ||||||
|  |  | ||||||
|     # sort for reproducability |     # sort for reproducibility | ||||||
|     suites = sorted(suites) |     suites = sorted(suites) | ||||||
|  |  | ||||||
|     # generate permutations |     # generate permutations | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user