file_stream.c 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #include "stream.h"
  2. #include "stream_i.h"
  3. #include "file_stream.h"
  4. typedef struct {
  5. Stream stream_base;
  6. Storage* storage;
  7. File* file;
  8. } FileStream;
  9. static void file_stream_free(FileStream* stream);
  10. static bool file_stream_eof(FileStream* stream);
  11. static void file_stream_clean(FileStream* stream);
  12. static bool file_stream_seek(FileStream* stream, int32_t offset, StreamOffset offset_type);
  13. static size_t file_stream_tell(FileStream* stream);
  14. static size_t file_stream_size(FileStream* stream);
  15. static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size);
  16. static size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size);
  17. static bool file_stream_delete_and_insert(
  18. FileStream* stream,
  19. size_t delete_size,
  20. StreamWriteCB write_callback,
  21. const void* ctx);
  22. const StreamVTable file_stream_vtable = {
  23. .free = (StreamFreeFn)file_stream_free,
  24. .eof = (StreamEOFFn)file_stream_eof,
  25. .clean = (StreamCleanFn)file_stream_clean,
  26. .seek = (StreamSeekFn)file_stream_seek,
  27. .tell = (StreamTellFn)file_stream_tell,
  28. .size = (StreamSizeFn)file_stream_size,
  29. .write = (StreamWriteFn)file_stream_write,
  30. .read = (StreamReadFn)file_stream_read,
  31. .delete_and_insert = (StreamDeleteAndInsertFn)file_stream_delete_and_insert,
  32. };
  33. Stream* file_stream_alloc(Storage* storage) {
  34. FileStream* stream = malloc(sizeof(FileStream));
  35. stream->file = storage_file_alloc(storage);
  36. stream->storage = storage;
  37. stream->stream_base.vtable = &file_stream_vtable;
  38. return (Stream*)stream;
  39. }
  40. bool file_stream_open(
  41. Stream* _stream,
  42. const char* path,
  43. FS_AccessMode access_mode,
  44. FS_OpenMode open_mode) {
  45. furi_assert(_stream);
  46. FileStream* stream = (FileStream*)_stream;
  47. furi_check(stream->stream_base.vtable == &file_stream_vtable);
  48. return storage_file_open(stream->file, path, access_mode, open_mode);
  49. }
  50. bool file_stream_close(Stream* _stream) {
  51. furi_assert(_stream);
  52. FileStream* stream = (FileStream*)_stream;
  53. furi_check(stream->stream_base.vtable == &file_stream_vtable);
  54. return storage_file_close(stream->file);
  55. }
  56. FS_Error file_stream_get_error(Stream* _stream) {
  57. furi_assert(_stream);
  58. FileStream* stream = (FileStream*)_stream;
  59. furi_check(stream->stream_base.vtable == &file_stream_vtable);
  60. return storage_file_get_error(stream->file);
  61. }
  62. static void file_stream_free(FileStream* stream) {
  63. storage_file_free(stream->file);
  64. free(stream);
  65. }
  66. static bool file_stream_eof(FileStream* stream) {
  67. return storage_file_eof(stream->file);
  68. }
  69. static void file_stream_clean(FileStream* stream) {
  70. storage_file_seek(stream->file, 0, true);
  71. storage_file_truncate(stream->file);
  72. }
  73. static bool file_stream_seek(FileStream* stream, int32_t offset, StreamOffset offset_type) {
  74. bool result = false;
  75. size_t seek_position = 0;
  76. size_t current_position = file_stream_tell(stream);
  77. size_t size = file_stream_size(stream);
  78. // calc offset and limit to bottom
  79. switch(offset_type) {
  80. case StreamOffsetFromCurrent: {
  81. if((int32_t)(current_position + offset) >= 0) {
  82. seek_position = current_position + offset;
  83. result = true;
  84. }
  85. } break;
  86. case StreamOffsetFromStart: {
  87. if(offset >= 0) {
  88. seek_position = offset;
  89. result = true;
  90. }
  91. } break;
  92. case StreamOffsetFromEnd: {
  93. if((int32_t)(size + offset) >= 0) {
  94. seek_position = size + offset;
  95. result = true;
  96. }
  97. } break;
  98. }
  99. if(result) {
  100. // limit to top
  101. if((int32_t)(seek_position - size) > 0) {
  102. storage_file_seek(stream->file, size, true);
  103. result = false;
  104. } else {
  105. result = storage_file_seek(stream->file, seek_position, true);
  106. }
  107. } else {
  108. storage_file_seek(stream->file, 0, true);
  109. }
  110. return result;
  111. }
  112. static size_t file_stream_tell(FileStream* stream) {
  113. return storage_file_tell(stream->file);
  114. }
  115. static size_t file_stream_size(FileStream* stream) {
  116. return storage_file_size(stream->file);
  117. }
  118. static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size) {
  119. // TODO cache
  120. size_t need_to_write = size;
  121. while(need_to_write > 0) {
  122. uint16_t was_written =
  123. storage_file_write(stream->file, data + (size - need_to_write), need_to_write);
  124. need_to_write -= was_written;
  125. if(was_written == 0) break;
  126. }
  127. return size - need_to_write;
  128. }
  129. static size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size) {
  130. // TODO cache
  131. size_t need_to_read = size;
  132. while(need_to_read > 0) {
  133. uint16_t was_read =
  134. storage_file_read(stream->file, data + (size - need_to_read), need_to_read);
  135. need_to_read -= was_read;
  136. if(was_read == 0) break;
  137. }
  138. return size - need_to_read;
  139. }
  140. static bool file_stream_delete_and_insert(
  141. FileStream* _stream,
  142. size_t delete_size,
  143. StreamWriteCB write_callback,
  144. const void* ctx) {
  145. bool result = false;
  146. Stream* stream = (Stream*)_stream;
  147. // open scratchpad
  148. Stream* scratch_stream = file_stream_alloc(_stream->storage);
  149. // TODO: we need something like "storage_open_tmpfile and storage_close_tmpfile"
  150. FuriString* scratch_name;
  151. FuriString* tmp_name;
  152. tmp_name = furi_string_alloc();
  153. storage_get_next_filename(
  154. _stream->storage, STORAGE_ANY_PATH_PREFIX, ".scratch", ".pad", tmp_name, 255);
  155. scratch_name = furi_string_alloc_printf(ANY_PATH("%s.pad"), furi_string_get_cstr(tmp_name));
  156. furi_string_free(tmp_name);
  157. do {
  158. if(!file_stream_open(
  159. scratch_stream,
  160. furi_string_get_cstr(scratch_name),
  161. FSAM_READ_WRITE,
  162. FSOM_CREATE_NEW))
  163. break;
  164. size_t current_position = stream_tell(stream);
  165. size_t file_size = stream_size(stream);
  166. size_t size_to_delete = file_size - current_position;
  167. size_to_delete = MIN(delete_size, size_to_delete);
  168. size_t size_to_copy_before = current_position;
  169. size_t size_to_copy_after = file_size - current_position - size_to_delete;
  170. // copy file from 0 to insert position to scratchpad
  171. if(!stream_rewind(stream)) break;
  172. if(stream_copy(stream, scratch_stream, size_to_copy_before) != size_to_copy_before) break;
  173. if(write_callback) {
  174. if(!write_callback(scratch_stream, ctx)) break;
  175. }
  176. size_t new_position = stream_tell(scratch_stream);
  177. // copy key file after insert position + size_to_delete to scratchpad
  178. if(!stream_seek(stream, size_to_delete, StreamOffsetFromCurrent)) break;
  179. if(stream_copy(stream, scratch_stream, size_to_copy_after) != size_to_copy_after) break;
  180. size_t new_file_size = stream_size(scratch_stream);
  181. // copy whole scratchpad file to the original file
  182. if(!stream_rewind(stream)) break;
  183. if(!stream_rewind(scratch_stream)) break;
  184. if(stream_copy(scratch_stream, stream, new_file_size) != new_file_size) break;
  185. // and truncate original file
  186. if(!storage_file_truncate(_stream->file)) break;
  187. // move seek pointer at insert end
  188. if(!stream_seek(stream, new_position, StreamOffsetFromStart)) break;
  189. result = true;
  190. } while(false);
  191. stream_free(scratch_stream);
  192. storage_common_remove(_stream->storage, furi_string_get_cstr(scratch_name));
  193. furi_string_free(scratch_name);
  194. return result;
  195. }