[FL-2655, FL-2650] Buffered file streams (#1424)
* Initial buffered file stream implementation * Fix logical errors * Fix more logical errors * Minimally working implementation * Adapt infrared unit tests for buffered streams * Increase read buffer size from 512 to 1K * Correct naming and formatting * More code improvements * Allow passing access and open modes for buffered streams * Implement tests for buffered streams * Better file and method names * Add comments and correct formatting * Use buffered streams in Infrared * Fix compilation error
This commit is contained in:
@@ -22,7 +22,7 @@ static void infrared_test_alloc() {
|
||||
test = malloc(sizeof(InfraredTest));
|
||||
test->decoder_handler = infrared_alloc_decoder();
|
||||
test->encoder_handler = infrared_alloc_encoder();
|
||||
test->ff = flipper_format_file_alloc(storage);
|
||||
test->ff = flipper_format_buffered_file_alloc(storage);
|
||||
string_init(test->file_path);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,8 @@ static bool infrared_test_prepare_file(const char* protocol_name) {
|
||||
|
||||
do {
|
||||
uint32_t format_version;
|
||||
if(!flipper_format_file_open_existing(test->ff, string_get_cstr(test->file_path))) break;
|
||||
if(!flipper_format_buffered_file_open_existing(test->ff, string_get_cstr(test->file_path)))
|
||||
break;
|
||||
if(!flipper_format_read_header(test->ff, file_type, &format_version)) break;
|
||||
if(string_cmp_str(file_type, "IR tests file") || format_version != 1) break;
|
||||
success = true;
|
||||
@@ -230,7 +231,7 @@ static void infrared_test_run_encoder(InfraredProtocol protocol, uint32_t test_i
|
||||
test->ff, string_get_cstr(buf), &expected_timings, &expected_timings_count),
|
||||
"Failed to load raw signal from file");
|
||||
|
||||
flipper_format_file_close(test->ff);
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
uint32_t j = 0;
|
||||
@@ -280,7 +281,7 @@ static void infrared_test_run_encoder_decoder(InfraredProtocol protocol, uint32_
|
||||
test->ff, string_get_cstr(buf), &input_messages, &input_messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_file_close(test->ff);
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
for(uint32_t message_counter = 0; message_counter < input_messages_count; ++message_counter) {
|
||||
@@ -343,7 +344,7 @@ static void infrared_test_run_decoder(InfraredProtocol protocol, uint32_t test_i
|
||||
infrared_test_load_messages(test->ff, string_get_cstr(buf), &messages, &messages_count),
|
||||
"Failed to load messages from file");
|
||||
|
||||
flipper_format_file_close(test->ff);
|
||||
flipper_format_buffered_file_close(test->ff);
|
||||
string_clear(buf);
|
||||
|
||||
InfraredMessage message_decoded_check_local;
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include <toolbox/stream/stream.h>
|
||||
#include <toolbox/stream/string_stream.h>
|
||||
#include <toolbox/stream/file_stream.h>
|
||||
#include <toolbox/stream/buffered_file_stream.h>
|
||||
#include <storage/storage.h>
|
||||
#include "../minunit.h"
|
||||
|
||||
@@ -282,6 +283,13 @@ MU_TEST(stream_composite_test) {
|
||||
mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_composite_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test buffered file stream
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_composite_subtest, stream);
|
||||
stream_free(stream);
|
||||
furi_record_close("storage");
|
||||
}
|
||||
|
||||
@@ -366,13 +374,101 @@ MU_TEST(stream_split_test) {
|
||||
mu_check(file_stream_open(stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_split_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
// test buffered stream
|
||||
stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
MU_RUN_TEST_1(stream_split_subtest, stream);
|
||||
stream_free(stream);
|
||||
|
||||
furi_record_close("storage");
|
||||
}
|
||||
|
||||
MU_TEST(stream_buffered_large_file_test) {
|
||||
string_t input_data;
|
||||
string_t output_data;
|
||||
string_init(input_data);
|
||||
string_init(output_data);
|
||||
|
||||
Storage* storage = furi_record_open("storage");
|
||||
|
||||
// generate test data consisting of several identical lines
|
||||
const size_t data_size = 4096;
|
||||
const size_t line_size = strlen(stream_test_data);
|
||||
const size_t rep_count = data_size / line_size + 1;
|
||||
|
||||
for(size_t i = 0; i < rep_count; ++i) {
|
||||
string_cat_printf(input_data, "%s\n", stream_test_data);
|
||||
}
|
||||
|
||||
// write test data to file
|
||||
Stream* stream = buffered_file_stream_alloc(storage);
|
||||
mu_check(buffered_file_stream_open(
|
||||
stream, "/ext/filestream.str", FSAM_READ_WRITE, FSOM_CREATE_ALWAYS));
|
||||
mu_assert_int_eq(0, stream_size(stream));
|
||||
mu_assert_int_eq(string_size(input_data), stream_write_string(stream, input_data));
|
||||
mu_assert_int_eq(string_size(input_data), stream_size(stream));
|
||||
|
||||
const size_t substr_start = 8;
|
||||
const size_t substr_len = 11;
|
||||
|
||||
mu_check(stream_seek(stream, substr_start, StreamOffsetFromStart));
|
||||
mu_assert_int_eq(substr_start, stream_tell(stream));
|
||||
|
||||
// copy one substring from test data
|
||||
char test_substr[substr_len + 1];
|
||||
memset(test_substr, 0, substr_len + 1);
|
||||
memcpy(test_substr, stream_test_data + substr_start, substr_len);
|
||||
|
||||
char buf[substr_len + 1];
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// read substring
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// forward seek to cause a cache miss
|
||||
mu_check(stream_seek(
|
||||
stream, (line_size + 1) * (rep_count - 1) - substr_len, StreamOffsetFromCurrent));
|
||||
// read same substring from a different line
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
memset(buf, 0, substr_len + 1);
|
||||
|
||||
// backward seek to cause a cache miss
|
||||
mu_check(stream_seek(
|
||||
stream, -((line_size + 1) * (rep_count - 1) + substr_len), StreamOffsetFromCurrent));
|
||||
mu_assert_int_eq(substr_len, stream_read(stream, (uint8_t*)buf, substr_len));
|
||||
mu_assert_string_eq(test_substr, buf);
|
||||
|
||||
// read the whole file
|
||||
mu_check(stream_rewind(stream));
|
||||
string_t tmp;
|
||||
string_init(tmp);
|
||||
while(stream_read_line(stream, tmp)) {
|
||||
string_cat(output_data, tmp);
|
||||
}
|
||||
string_clear(tmp);
|
||||
|
||||
// check against generated data
|
||||
mu_assert_int_eq(string_size(input_data), string_size(output_data));
|
||||
mu_check(string_equal_p(input_data, output_data));
|
||||
mu_check(stream_eof(stream));
|
||||
|
||||
stream_free(stream);
|
||||
|
||||
furi_record_close("storage");
|
||||
string_clear(input_data);
|
||||
string_clear(output_data);
|
||||
}
|
||||
|
||||
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_large_file_test);
|
||||
}
|
||||
|
||||
int run_minunit_test_stream() {
|
||||
|
Reference in New Issue
Block a user