diff --git a/include/tapelib/filetape.h b/include/tapelib/filetape.h index b8ba77e..557cd2e 100644 --- a/include/tapelib/filetape.h +++ b/include/tapelib/filetape.h @@ -4,6 +4,7 @@ #include "tape.h" #include #include +#include using std::chrono::milliseconds; @@ -55,7 +56,7 @@ class FileTape : public Tape { std::string file_name; std::fstream file; FileTapeSettings settings; - bool temp = false; + bool tmp = false; public: /** @@ -81,24 +82,45 @@ class FileTape : public Tape { /** * Initializes a new instance of FileTape that will create and open a file - * with the specified name, and write *size* zeroes into it. + * with the specified name and fill it with *cells* amount of zeroed cells. * File must not exist and directory must be write accessible. * + * Writing operations when filling file with zeroes will not introduce any + * delay. + * * @param size Size of a newly created FileTpae * @param file_name Name of a file to be created and opened * @param settings FileTape settings */ - FileTape(size_t size, std::string file_name, + FileTape(size_t cells, std::string file_name, FileTapeSettings settings = FT_DEFAULT_SETTINGS); /** * Initializes a new instance of FileTape that will create and open a - * temporary file, and write *size* zeroes into it. + * temporary file and fill it with *cells* amount of zeroed cells. + * + * Writing operations when filling file with zeroes will not introduce any + * delay. * * @param size Size of a newly created FileTape * @param settings FileTape settings */ - FileTape(size_t size, FileTapeSettings settings = FT_DEFAULT_SETTINGS); + FileTape(size_t cells, FileTapeSettings settings = FT_DEFAULT_SETTINGS); + + /** + * Initializes a new instance of FileTape that will create and open a + * temporary file, and fill it with the data from the provided vector. + * + * Writing operations when copying vector to a FileTape will not have any + * delay. + * Use this constructor ONLY FOR TESTING. Do NOT use it to copy one + * FileTape's data to another. + * + * @param data Vector of uint32 contents of which will be copied to FileTape + * @param settings FileTape settings + */ + FileTape(const std::vector &data, + FileTapeSettings settings = FT_DEFAULT_SETTINGS); /** * Advances the underlying fstream to a new line. @@ -128,11 +150,15 @@ class FileTape : public Tape { */ void write(uint32_t data) override; + /** + * Close the undetlying fstream, and, in case the FileTape is marked as + * temporary (tmp is set to true), remove the temporary file + */ ~FileTape() override; // Define getters in header file since they are implicitly inlined - bool is_temp() const { return this->temp; } + bool is_tmp() const { return this->tmp; } const FileTapeSettings &get_settings() const { return this->settings; } diff --git a/src/filetape.cpp b/src/filetape.cpp index f50222d..f434ae1 100644 --- a/src/filetape.cpp +++ b/src/filetape.cpp @@ -1,5 +1,8 @@ #include "filetape.h" #include +#include +#include +#include #include using std::fstream; @@ -14,11 +17,11 @@ std::string generate_tmp_file_name() { return tmp_file_name.data(); } -void fill_file_with_empty_cells(size_t size, fstream &file) { - file << std::setfill('0') << std::setw(tape::FT_CELL_SIZE + 1); - for (int i = 0; i < size; i++) { +void fill_file_with_empty_cells(size_t cells, fstream &file) { + file << std::setfill('0') << std::setw(tape::FT_CELL_SIZE) << '0'; + for (size_t i = 1; i < cells; i++) { file << tape::FT_DELIMETER << std::setfill('0') - << std::setw(tape::FT_CELL_SIZE + 1); + << std::setw(tape::FT_CELL_SIZE) << '0'; } file.seekg(0, fstream::beg); } @@ -28,20 +31,37 @@ FileTape::FileTape(std::string file_name, FileTapeSettings settings) this->file = fstream(file_name); } -FileTape::FileTape(size_t size, std::string file_name, +FileTape::FileTape(size_t cells, std::string file_name, FileTapeSettings settings) : FileTape(file_name, settings) { this->file = fstream(this->file_name, fstream::in | fstream::out | fstream::trunc); - fill_file_with_empty_cells(size, this->file); + fill_file_with_empty_cells(cells, this->file); } -FileTape::FileTape(size_t size, FileTapeSettings settings) - : settings(settings), temp(true) { +FileTape::FileTape(size_t cells, FileTapeSettings settings) + : settings(settings), tmp(true) { this->file_name = generate_tmp_file_name(); this->file = fstream(this->file_name, fstream::in | fstream::out | fstream::trunc); - fill_file_with_empty_cells(size, this->file); + fill_file_with_empty_cells(cells, this->file); +} + +FileTape::FileTape(const std::vector &data, FileTapeSettings settings) + : settings(settings), tmp(true) { + this->file_name = generate_tmp_file_name(); + this->file = + fstream(this->file_name, fstream::in | fstream::out | fstream::trunc); + if (data.empty()) { + return; + } + this->file << std::setfill('0') << std::setw(FT_CELL_SIZE) + << std::to_string(data[0]); + for (size_t i = 1; i < data.size(); i++) { + this->file << FT_DELIMETER << std::setfill('0') + << std::setw(FT_CELL_SIZE) << std::to_string(data[i]); + } + this->file.seekg(0, fstream::beg); } bool FileTape::seek_backwards() { @@ -56,10 +76,10 @@ bool FileTape::seek_backwards() { bool FileTape::seek_forward() { std::this_thread::sleep_for(this->settings.seek_forward_delay); this->file.seekg(FT_SEEK_OFFSET, fstream::cur); - this->file.peek(); + this->file.peek(); // peek to check if eof is reached if (this->file.eof()) { - this->file.clear(); - this->file.seekg(-FT_SEEK_OFFSET, fstream::end); + this->file.clear(); // reset fstream + this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::end); return false; } return true; @@ -70,8 +90,8 @@ uint32_t FileTape::read() { uint32_t data = 0; this->file >> data; if (this->file.eof()) { - this->file.clear(); - this->file.seekg(-FT_SEEK_OFFSET + 2, fstream::end); + this->file.clear(); // reset fstream + this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::end); return data; } this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::cur); @@ -89,7 +109,7 @@ void FileTape::write(uint32_t data) { FileTape::~FileTape() { this->file.close(); - if (this->is_temp()) { + if (this->is_tmp()) { std::filesystem::remove(this->file_name); } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae7d9e1..7bb6c28 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,4 +5,3 @@ target_link_libraries(filetape_tests PRIVATE tapelib) target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain) add_test(filetape_tests filetape_tests) -add_compile_definitions(FILETAPE_TEST_FILE=\"${PROJECT_SOURCE_DIR}/tests/assets/filetape_test_data.txt\") diff --git a/tests/filetape_tests.cpp b/tests/filetape_tests.cpp index 4c12ba2..d842523 100644 --- a/tests/filetape_tests.cpp +++ b/tests/filetape_tests.cpp @@ -1,61 +1,54 @@ #include "filetape.h" #include +#include +#include -const static std::string TEST_DATA = "0000000001\n" - "0000012345\n" - "0000000000\n" - "2222222222\n" - "4294967295"; - -tape::FileTape prepare_test_filetape() { - tape::FileTape tape(0); - - return tape; -} +const static std::vector TEST_DATA = {1, 12345, 0, 2222222222, + 4294967295}; // NOLINTBEGIN(readability-function-cognitive-complexity) TEST_CASE("Reading data from a FileTape", "[filetape]") { - tape::FileTape tape(FILETAPE_TEST_FILE); + tape::FileTape tape(TEST_DATA); SECTION("Read all data sequentially") { - REQUIRE(tape.read() == 1); + REQUIRE(tape.read() == TEST_DATA[0]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 2); + REQUIRE(tape.read() == TEST_DATA[1]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 3); + REQUIRE(tape.read() == TEST_DATA[2]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 4); + REQUIRE(tape.read() == TEST_DATA[3]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 5); + REQUIRE(tape.read() == TEST_DATA[4]); } SECTION("Read data non-sequentially") { - REQUIRE(tape.read() == 1); + REQUIRE(tape.read() == TEST_DATA[0]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 2); + REQUIRE(tape.read() == TEST_DATA[1]); REQUIRE(tape.seek_backwards() == true); - REQUIRE(tape.read() == 1); + REQUIRE(tape.read() == TEST_DATA[0]); REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 4); + REQUIRE(tape.read() == TEST_DATA[3]); REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == true); - REQUIRE(tape.read() == 2); + REQUIRE(tape.read() == TEST_DATA[1]); } } TEST_CASE("Seeking forward and backwards", "[filetape]") { - tape::FileTape tape(FILETAPE_TEST_FILE); + tape::FileTape tape(TEST_DATA); SECTION("Rewinding at the beginning of a file tape") { REQUIRE(tape.seek_backwards() == false); - REQUIRE(tape.read() == 1); + REQUIRE(tape.read() == TEST_DATA[0]); REQUIRE(tape.seek_forward() == true); - REQUIRE(tape.read() == 2); + REQUIRE(tape.read() == TEST_DATA[1]); REQUIRE(tape.seek_backwards() == true); REQUIRE(tape.seek_backwards() == false); - REQUIRE(tape.read() == 1); + REQUIRE(tape.read() == TEST_DATA[0]); } SECTION("Seeking at the end of a file tape") { @@ -65,12 +58,12 @@ TEST_CASE("Seeking forward and backwards", "[filetape]") { REQUIRE(tape.seek_forward() == true); } REQUIRE(tape.seek_forward() == false); - REQUIRE(tape.read() == 5); + REQUIRE(tape.read() == TEST_DATA[4]); REQUIRE(tape.seek_backwards() == true); - REQUIRE(tape.read() == 4); + REQUIRE(tape.read() == TEST_DATA[3]); REQUIRE(tape.seek_forward() == true); REQUIRE(tape.seek_forward() == false); - REQUIRE(tape.read() == 5); + REQUIRE(tape.read() == TEST_DATA[4]); } }