From 01afb289c0dd968193ec6f8abf6282a327fa4e7e Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Tue, 2 Aug 2022 16:17:37 +0300 Subject: [PATCH] [FL-2713] Buffered file streams fix (#1515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix incorrect buffered stream behaviour * Add specific tests * Make the test fail on wrong behaviour * Better names Co-authored-by: あく --- applications/unit_tests/stream/stream_test.c | 29 ++++++++++++++++++++ lib/toolbox/stream/buffered_file_stream.c | 26 ++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/applications/unit_tests/stream/stream_test.c b/applications/unit_tests/stream/stream_test.c index 36155e333..b5a2d3980 100644 --- a/applications/unit_tests/stream/stream_test.c +++ b/applications/unit_tests/stream/stream_test.c @@ -387,6 +387,34 @@ MU_TEST(stream_split_test) { furi_record_close(RECORD_STORAGE); } +MU_TEST(stream_buffered_write_after_read_test) { + const char* prefix = "I write "; + const char* substr = "Hello there"; + + const size_t substr_len = strlen(substr); + const size_t prefix_len = strlen(prefix); + const size_t buf_size = substr_len + 1; + + char buf[buf_size]; + memset(buf, 0, buf_size); + + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + mu_check(buffered_file_stream_open( + stream, EXT_PATH("filestream.str"), FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)); + mu_assert_int_eq(strlen(stream_test_data), stream_write_cstring(stream, stream_test_data)); + mu_check(stream_rewind(stream)); + mu_assert_int_eq(prefix_len, stream_read(stream, (uint8_t*)buf, prefix_len)); + mu_assert_string_eq(prefix, buf); + mu_assert_int_eq(substr_len, stream_write(stream, (uint8_t*)substr, substr_len)); + mu_check(stream_seek(stream, prefix_len, StreamOffsetFromStart)); + mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len)); + mu_assert_string_eq(substr, buf); + + stream_free(stream); + furi_record_close(RECORD_STORAGE); +} + MU_TEST(stream_buffered_large_file_test) { string_t input_data; string_t output_data; @@ -470,6 +498,7 @@ MU_TEST_SUITE(stream_suite) { MU_RUN_TEST(stream_write_read_save_load_test); MU_RUN_TEST(stream_composite_test); MU_RUN_TEST(stream_split_test); + MU_RUN_TEST(stream_buffered_write_after_read_test); MU_RUN_TEST(stream_buffered_large_file_test); } diff --git a/lib/toolbox/stream/buffered_file_stream.c b/lib/toolbox/stream/buffered_file_stream.c index 5db276d3f..2f2359a04 100644 --- a/lib/toolbox/stream/buffered_file_stream.c +++ b/lib/toolbox/stream/buffered_file_stream.c @@ -1,5 +1,6 @@ #include "buffered_file_stream.h" +#include "core/check.h" #include "stream_i.h" #include "file_stream.h" #include "stream_cache.h" @@ -38,6 +39,8 @@ const StreamVTable buffered_file_stream_vtable = { .delete_and_insert = (StreamDeleteAndInsertFn)buffered_file_stream_delete_and_insert, }; +static bool buffered_file_stream_unread(BufferedFileStream* stream); + Stream* buffered_file_stream_alloc(Storage* storage) { BufferedFileStream* stream = malloc(sizeof(BufferedFileStream)); @@ -125,8 +128,12 @@ static size_t buffered_file_stream_size(BufferedFileStream* stream) { static size_t buffered_file_stream_write(BufferedFileStream* stream, const uint8_t* data, size_t size) { - stream_cache_drop(stream->cache); - return stream_write(stream->file_stream, data, size); + size_t need_to_write = size; + do { + if(!buffered_file_stream_unread(stream)) break; + need_to_write -= stream_write(stream->file_stream, data, size); + } while(false); + return size - need_to_write; } static size_t buffered_file_stream_read(BufferedFileStream* stream, uint8_t* data, size_t size) { @@ -150,6 +157,19 @@ static bool buffered_file_stream_delete_and_insert( size_t delete_size, StreamWriteCB write_callback, const void* ctx) { + return buffered_file_stream_unread(stream) && + stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx); +} + +// Drop read cache and adjust the underlying stream seek position +static bool buffered_file_stream_unread(BufferedFileStream* stream) { + bool success = true; + const size_t cache_size = stream_cache_size(stream->cache); + const size_t cache_pos = stream_cache_pos(stream->cache); + if(cache_pos < cache_size) { + const int32_t offset = cache_size - cache_pos; + success = stream_seek(stream->file_stream, -offset, StreamOffsetFromCurrent); + } stream_cache_drop(stream->cache); - return stream_delete_and_insert(stream->file_stream, delete_size, write_callback, ctx); + return success; }