file_stream.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. static void file_stream_free(FileStream* stream) {
  57. storage_file_free(stream->file);
  58. free(stream);
  59. }
  60. static bool file_stream_eof(FileStream* stream) {
  61. return storage_file_eof(stream->file);
  62. }
  63. static void file_stream_clean(FileStream* stream) {
  64. storage_file_seek(stream->file, 0, true);
  65. storage_file_truncate(stream->file);
  66. }
  67. static bool file_stream_seek(FileStream* stream, int32_t offset, StreamOffset offset_type) {
  68. bool result = false;
  69. size_t seek_position = 0;
  70. size_t current_position = file_stream_tell(stream);
  71. size_t size = file_stream_size(stream);
  72. // calc offset and limit to bottom
  73. switch(offset_type) {
  74. case StreamOffsetFromCurrent: {
  75. if((int32_t)(current_position + offset) >= 0) {
  76. seek_position = current_position + offset;
  77. result = true;
  78. }
  79. } break;
  80. case StreamOffsetFromStart: {
  81. if(offset >= 0) {
  82. seek_position = offset;
  83. result = true;
  84. }
  85. } break;
  86. case StreamOffsetFromEnd: {
  87. if((int32_t)(size + offset) >= 0) {
  88. seek_position = size + offset;
  89. result = true;
  90. }
  91. } break;
  92. }
  93. if(result) {
  94. // limit to top
  95. if((int32_t)(seek_position - size) > 0) {
  96. storage_file_seek(stream->file, size, true);
  97. result = false;
  98. } else {
  99. result = storage_file_seek(stream->file, seek_position, true);
  100. }
  101. } else {
  102. storage_file_seek(stream->file, 0, true);
  103. }
  104. return result;
  105. }
  106. static size_t file_stream_tell(FileStream* stream) {
  107. return storage_file_tell(stream->file);
  108. }
  109. static size_t file_stream_size(FileStream* stream) {
  110. return storage_file_size(stream->file);
  111. }
  112. static size_t file_stream_write(FileStream* stream, const uint8_t* data, size_t size) {
  113. // TODO cache
  114. size_t need_to_write = size;
  115. while(need_to_write > 0) {
  116. uint16_t was_written =
  117. storage_file_write(stream->file, data + (size - need_to_write), need_to_write);
  118. need_to_write -= was_written;
  119. if(was_written == 0) break;
  120. }
  121. return size - need_to_write;
  122. }
  123. static size_t file_stream_read(FileStream* stream, uint8_t* data, size_t size) {
  124. // TODO cache
  125. size_t need_to_read = size;
  126. while(need_to_read > 0) {
  127. uint16_t was_read =
  128. storage_file_read(stream->file, data + (size - need_to_read), need_to_read);
  129. need_to_read -= was_read;
  130. if(was_read == 0) break;
  131. }
  132. return size - need_to_read;
  133. }
  134. static bool file_stream_delete_and_insert(
  135. FileStream* _stream,
  136. size_t delete_size,
  137. StreamWriteCB write_callback,
  138. const void* ctx) {
  139. bool result = false;
  140. Stream* stream = (Stream*)_stream;
  141. // open scratchpad
  142. Stream* scratch_stream = file_stream_alloc(_stream->storage);
  143. // TODO: we need something like "storage_open_tmpfile and storage_close_tmpfile"
  144. string_t scratch_name;
  145. string_t tmp_name;
  146. string_init(tmp_name);
  147. storage_get_next_filename(_stream->storage, "/any", ".scratch", ".pad", tmp_name);
  148. string_init_printf(scratch_name, "/any/%s.pad", string_get_cstr(tmp_name));
  149. string_clear(tmp_name);
  150. do {
  151. if(!file_stream_open(
  152. scratch_stream, string_get_cstr(scratch_name), FSAM_READ_WRITE, FSOM_CREATE_NEW))
  153. break;
  154. size_t current_position = stream_tell(stream);
  155. size_t file_size = stream_size(stream);
  156. size_t size_to_delete = file_size - current_position;
  157. size_to_delete = MIN(delete_size, size_to_delete);
  158. size_t size_to_copy_before = current_position;
  159. size_t size_to_copy_after = file_size - current_position - size_to_delete;
  160. // copy file from 0 to insert position to scratchpad
  161. if(!stream_rewind(stream)) break;
  162. if(stream_copy(stream, scratch_stream, size_to_copy_before) != size_to_copy_before) break;
  163. if(write_callback) {
  164. if(!write_callback(scratch_stream, ctx)) break;
  165. }
  166. size_t new_position = stream_tell(scratch_stream);
  167. // copy key file after insert position + size_to_delete to scratchpad
  168. if(!stream_seek(stream, size_to_delete, StreamOffsetFromCurrent)) break;
  169. if(stream_copy(stream, scratch_stream, size_to_copy_after) != size_to_copy_after) break;
  170. size_t new_file_size = stream_size(scratch_stream);
  171. // copy whole scratchpad file to the original file
  172. if(!stream_rewind(stream)) break;
  173. if(!stream_rewind(scratch_stream)) break;
  174. if(stream_copy(scratch_stream, stream, new_file_size) != new_file_size) break;
  175. // and truncate original file
  176. if(!storage_file_truncate(_stream->file)) break;
  177. // move seek pointer at insert end
  178. if(!stream_seek(stream, new_position, StreamOffsetFromStart)) break;
  179. result = true;
  180. } while(false);
  181. stream_free(scratch_stream);
  182. storage_common_remove(_stream->storage, string_get_cstr(scratch_name));
  183. string_clear(scratch_name);
  184. return result;
  185. }