mirror of
				https://github.com/eledio-devices/thirdparty-littlefs.git
				synced 2025-10-31 00:32:38 +01:00 
			
		
		
		
	Added lfs_file_truncate
As a copy-on-write filesystem, the truncate function is a very nice function to have, as it can take advantage of reusing the data already written out to disk.
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -33,7 +33,7 @@ size: $(OBJ) | ||||
| 	$(SIZE) -t $^ | ||||
|  | ||||
| .SUFFIXES: | ||||
| test: test_format test_dirs test_files test_seek test_parallel \ | ||||
| test: test_format test_dirs test_files test_seek test_truncate test_parallel \ | ||||
| 	test_alloc test_paths test_orphan test_move test_corrupt | ||||
| test_%: tests/test_%.sh | ||||
| ifdef QUIET | ||||
|   | ||||
							
								
								
									
										57
									
								
								lfs.c
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								lfs.c
									
									
									
									
									
								
							| @@ -1664,6 +1664,57 @@ lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|     return file->pos; | ||||
| } | ||||
|  | ||||
| int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size) { | ||||
|     if ((file->flags & 3) == LFS_O_RDONLY) { | ||||
|         return LFS_ERR_INVAL; | ||||
|     } | ||||
|  | ||||
|     if (size < lfs_file_size(lfs, file)) { | ||||
|         // need to flush since directly changing metadata | ||||
|         int err = lfs_file_flush(lfs, file); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         // lookup new head in ctz skip list | ||||
|         err = lfs_ctz_find(lfs, &file->cache, NULL, | ||||
|                 file->head, file->size, | ||||
|                 size, &file->head, &(lfs_off_t){0}); | ||||
|         if (err) { | ||||
|             return err; | ||||
|         } | ||||
|  | ||||
|         file->size = size; | ||||
|         file->flags |= LFS_F_DIRTY; | ||||
|     } else if (size > lfs_file_size(lfs, file)) { | ||||
|         lfs_off_t pos = file->pos; | ||||
|  | ||||
|         // flush+seek if not already at end | ||||
|         if (file->pos != lfs_file_size(lfs, file)) { | ||||
|             int err = lfs_file_seek(lfs, file, 0, SEEK_END); | ||||
|             if (err) { | ||||
|                 return err; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // fill with zeros | ||||
|         while (file->pos < size) { | ||||
|             lfs_ssize_t res = lfs_file_write(lfs, file, &(uint8_t){0}, 1); | ||||
|             if (res < 0) { | ||||
|                 return res; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // restore pos | ||||
|         int err = lfs_file_seek(lfs, file, pos, LFS_SEEK_SET); | ||||
|         if (err < 0) { | ||||
|             return err; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return file->pos; | ||||
| } | ||||
| @@ -1678,7 +1729,11 @@ int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file) { | ||||
| } | ||||
|  | ||||
| lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file) { | ||||
|     return lfs_max(file->pos, file->size); | ||||
|     if (file->flags & LFS_F_WRITING) { | ||||
|         return lfs_max(file->pos, file->size); | ||||
|     } else { | ||||
|         return file->size; | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										5
									
								
								lfs.h
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								lfs.h
									
									
									
									
									
								
							| @@ -364,6 +364,11 @@ lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, | ||||
| lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file, | ||||
|         lfs_soff_t off, int whence); | ||||
|  | ||||
| // Truncates the size of the file to the specified size | ||||
| // | ||||
| // Returns a negative error code on failure. | ||||
| int lfs_file_truncate(lfs_t *lfs, lfs_file_t *file, lfs_off_t size); | ||||
|  | ||||
| // Return the position of the file | ||||
| // | ||||
| // Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR) | ||||
|   | ||||
							
								
								
									
										133
									
								
								tests/test_truncate.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										133
									
								
								tests/test_truncate.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| #!/bin/bash | ||||
| set -eu | ||||
|  | ||||
| SMALLSIZE=32 | ||||
| MEDIUMSIZE=2048 | ||||
| LARGESIZE=8192 | ||||
|  | ||||
| echo "=== Truncate tests ===" | ||||
| rm -rf blocks | ||||
| tests/test.py << TEST | ||||
|     lfs_format(&lfs, &cfg) => 0; | ||||
| TEST | ||||
|  | ||||
| truncate_test() { | ||||
| STARTSIZES="$1" | ||||
| HOTSIZES="$2" | ||||
| COLDSIZES="$3" | ||||
| tests/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, | ||||
|                 LFS_O_WRONLY | LFS_O_CREAT | LFS_O_TRUNC) => 0; | ||||
|  | ||||
|         strcpy((char*)buffer, "hair"); | ||||
|         size = strlen((char*)buffer); | ||||
|         for (int j = 0; j < startsizes[i]; j += size) { | ||||
|             lfs_file_write(&lfs, &file[0], buffer, size) => size; | ||||
|         } | ||||
|         lfs_file_size(&lfs, &file[0]) => startsizes[i]; | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file[0], hotsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => hotsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|     static const lfs_off_t coldsizes[]  = {$COLDSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDWR) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => hotsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < hotsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_truncate(&lfs, &file[0], coldsizes[i]) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => coldsizes[i]; | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| tests/test.py << TEST | ||||
|     static const lfs_off_t startsizes[] = {$STARTSIZES}; | ||||
|     static const lfs_off_t hotsizes[]   = {$HOTSIZES}; | ||||
|     static const lfs_off_t coldsizes[]  = {$COLDSIZES}; | ||||
|  | ||||
|     lfs_mount(&lfs, &cfg) => 0; | ||||
|  | ||||
|     for (int i = 0; i < sizeof(startsizes)/sizeof(startsizes[0]); i++) { | ||||
|         sprintf((char*)buffer, "hairyhead%d", i); | ||||
|         lfs_file_open(&lfs, &file[0], (const char*)buffer, LFS_O_RDONLY) => 0; | ||||
|         lfs_file_size(&lfs, &file[0]) => coldsizes[i]; | ||||
|  | ||||
|         size = strlen("hair"); | ||||
|         int j = 0; | ||||
|         for (; j < startsizes[i] && j < hotsizes[i] && j < coldsizes[i]; | ||||
|                 j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             memcmp(buffer, "hair", size) => 0; | ||||
|         } | ||||
|  | ||||
|         for (; j < coldsizes[i]; j += size) { | ||||
|             lfs_file_read(&lfs, &file[0], buffer, size) => size; | ||||
|             memcmp(buffer, "\0\0\0\0", size) => 0; | ||||
|         } | ||||
|  | ||||
|         lfs_file_close(&lfs, &file[0]) => 0; | ||||
|     } | ||||
|  | ||||
|     lfs_unmount(&lfs) => 0; | ||||
| TEST | ||||
| } | ||||
|  | ||||
| echo "--- Cold shrinking truncate ---" | ||||
| truncate_test \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" | ||||
|  | ||||
| echo "--- Cold expanding truncate ---" | ||||
| truncate_test \ | ||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" | ||||
|  | ||||
| echo "--- Warm shrinking truncate ---" | ||||
| truncate_test \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "           0,            0,            0,            0,            0" | ||||
|  | ||||
| echo "--- Warm expanding truncate ---" | ||||
| truncate_test \ | ||||
|     "           0,   $SMALLSIZE,  $MEDIUMSIZE,   $LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" \ | ||||
|     "2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE, 2*$LARGESIZE" | ||||
|  | ||||
| echo "--- Results ---" | ||||
| tests/stats.py | ||||
		Reference in New Issue
	
	Block a user