From f3aaa26df8e8a29b3d0500f3014b85387a476d4c Mon Sep 17 00:00:00 2001 From: erius Date: Fri, 25 Oct 2024 06:56:52 +0300 Subject: [PATCH] Chaged FileSettings delays type to std::chrono:milliseconds Changed file format for a FileTape - added and example in the class comment Removed prev_line_pos and at_first_line fields from FieldTape Changed Tape data type from int32_t to uint32_t Cleand up includes Implemented FileTape methods Unit tests are in working state --- include/tapelib/filetape.h | 55 ++++++++++++-------- include/tapelib/tape.h | 6 +-- src/filetape.cpp | 78 ++++++++++++++++++++++------- src/tape_config.cpp | 2 - tests/CMakeLists.txt | 1 + tests/assets/filetape_test_data.txt | 10 ++-- tests/filetape_tests.cpp | 26 ++++++---- 7 files changed, 118 insertions(+), 60 deletions(-) diff --git a/include/tapelib/filetape.h b/include/tapelib/filetape.h index 804b078..27e1a9f 100644 --- a/include/tapelib/filetape.h +++ b/include/tapelib/filetape.h @@ -2,10 +2,10 @@ #define FILE_TAPE_H #include "tape.h" -#include -#include +#include #include -#include + +using std::chrono::milliseconds; namespace tape { @@ -14,35 +14,46 @@ namespace tape { * such as read dealy, write delay etc. */ struct FileTapeSettings { - unsigned int read_delay_ms; - unsigned int write_delay_ms; - unsigned int seek_forward_delay_ms; - unsigned int seek_backwards_delay_ms; + milliseconds read_delay; + milliseconds write_delay; + milliseconds seek_forward_delay; + milliseconds seek_backwards_delay; }; -const static struct FileTapeSettings DEFAULT_FT_SETTINGS = - FileTapeSettings{.read_delay_ms = 0, - .write_delay_ms = 0, - .seek_forward_delay_ms = 0, - .seek_backwards_delay_ms = 0}; +const static struct FileTapeSettings FT_DEFAULT_SETTINGS = + FileTapeSettings{.read_delay = milliseconds(0), + .write_delay = milliseconds(0), + .seek_forward_delay = milliseconds(0), + .seek_backwards_delay = milliseconds(0)}; const static char FT_DELIMETER = '\n'; +// uint32_t digits max count is 10 +const static int FT_CELL_SIZE = 10; +const static int FT_SEEK_OFFSET = FT_CELL_SIZE + 1; + /** * Mock tape implementation that uses text files as data source. * * Simulates tape operations by artificially pausing the execution of some * methods using time intervals, specified in command line arguments or a - * configuration file. Each cell is represented as a plain text 32-bit int - * separated by FT_DELIMETER. End of the file tape is indicated either by EOF - * or some arbitrary data that cannot be parsed to int32_t + * configuration file. Each cell is represented as a plain text 32-bit unsigned + * int, padded to FT_CELL_SIZE with leading zeroes, separated by FT_DELIMETER. + * Each cell MUST be FT_CELL_SIZE long to correctly implement write operation + * without re-writing the entire file. FT_DELIMETER + * + * Example of a valid file for a FileTape + * - example.txt + * 000000001 + * 000000034 + * 000001234 + * 123456789 + * 000000000 */ class FileTape : public Tape { private: std::fstream file; FileTapeSettings settings; - size_t prev_line_pos = 0; - bool at_first_line = false; public: /** @@ -54,7 +65,7 @@ class FileTape : public Tape { * @param settings FileTape settings */ explicit FileTape(const std::string &file_name, - FileTapeSettings settings = DEFAULT_FT_SETTINGS); + FileTapeSettings settings = FT_DEFAULT_SETTINGS); /** * Initializes a new instance of FileTape that will create and open a file @@ -66,7 +77,7 @@ class FileTape : public Tape { * @param settings FileTape settings */ explicit FileTape(size_t size, const std::string &file_name, - FileTapeSettings settings = DEFAULT_FT_SETTINGS); + FileTapeSettings settings = FT_DEFAULT_SETTINGS); /** * Initializes a new instance of FileTape that will create and open a @@ -76,7 +87,7 @@ class FileTape : public Tape { * @param settings FileTape settings */ explicit FileTape(size_t size, - FileTapeSettings settings = DEFAULT_FT_SETTINGS); + FileTapeSettings settings = FT_DEFAULT_SETTINGS); /** * Advances the underlying fstream to a new line. @@ -97,14 +108,14 @@ class FileTape : public Tape { * * @return number from the line, or 0 if data can not be parsed. */ - int32_t read() override; + uint32_t read() override; /** * Writes data to the current line. * * @param data Number, that should be written to the current line. */ - void write(int32_t data) override; + void write(uint32_t data) override; }; } // namespace tape diff --git a/include/tapelib/tape.h b/include/tapelib/tape.h index 7179eef..5c7667a 100644 --- a/include/tapelib/tape.h +++ b/include/tapelib/tape.h @@ -9,7 +9,7 @@ namespace tape { * Abstract class representing a magnetic tape. * * Tape is a storage medium consisting of N cells, where each cell contains a - * 32-bit signed integer. Data can be read or written into one cell at a time. + * 32-bit unsigned integer. Data can be read or written into one cell at a time. * Tape can be moved one cell forward or backwards in order to change the * targeted cell. Tape performs every operation sequentially. The amount of data * stored on a single tape could exceed the amount of available RAM. Moving tape @@ -45,14 +45,14 @@ class Tape { * * @return number, that is stored on the current cell. */ - virtual int32_t read() = 0; + virtual uint32_t read() = 0; /** * Writes data to the current cell. * * @param data Number, that should be written to the current cell. */ - virtual void write(int32_t data) = 0; + virtual void write(uint32_t data) = 0; }; } // namespace tape diff --git a/src/filetape.cpp b/src/filetape.cpp index fa802ff..8138e95 100644 --- a/src/filetape.cpp +++ b/src/filetape.cpp @@ -1,41 +1,81 @@ #include "filetape.h" -#include -#include -#include -#include -#include +#include +using std::fstream; using tape::FileTape; FileTape::FileTape(const std::string &file_name, FileTapeSettings settings) : settings(settings) { - this->file = std::fstream(file_name); + this->file = fstream(file_name); +} + +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++) { + file << tape::FT_DELIMETER << std::setfill('0') + << std::setw(tape::FT_CELL_SIZE + 1); + } + file.seekg(0, fstream::beg); } FileTape::FileTape(size_t size, const std::string &file_name, FileTapeSettings settings) : FileTape(file_name, settings) { - this->file << "0"; - for (int i = 0; i < size; i++) { - this->file << FT_DELIMETER << "0"; - } + this->file = + fstream(file_name, fstream::in | fstream::out | fstream::trunc); + fill_file_with_empty_cells(size, file); } FileTape::FileTape(size_t size, FileTapeSettings settings) : settings(settings) { std::array file_name{}; + // tmpnam is unsafe, but its cross-platform + // create a platform dependant tmp file handler later std::tmpnam(file_name.data()); - this->file = std::fstream(file_name.data()); - this->file << "0"; - for (int i = 0; i < size; i++) { - this->file << FT_DELIMETER << "0"; - } + this->file = + fstream(file_name.data(), fstream::in | fstream::out | fstream::trunc); + fill_file_with_empty_cells(size, file); } -bool FileTape::seek_forward() { return true; } +bool FileTape::seek_backwards() { + std::this_thread::sleep_for(this->settings.seek_backwards_delay); + if (this->file.tellg() == 0) { + return false; + } + this->file.seekg(-FT_SEEK_OFFSET, fstream::cur); + return true; +} -bool FileTape::seek_backwards() { return true; } +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(); + if (this->file.eof()) { + this->file.clear(); + this->file.seekg(-FT_SEEK_OFFSET, fstream::end); + return false; + } + return true; +} -int32_t FileTape::read() { return 0; } +uint32_t FileTape::read() { + std::this_thread::sleep_for(this->settings.read_delay); + uint32_t data = 0; + this->file >> data; + if (this->file.eof()) { + this->file.clear(); + this->file.seekg(-FT_SEEK_OFFSET + 2, fstream::end); + return data; + } + this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::cur); + return data; +} -void FileTape::write(int32_t data) {} +void FileTape::write(uint32_t data) { + std::this_thread::sleep_for(this->settings.write_delay); + std::stringstream cell; + cell << std::setfill('0') << std::setw(FT_CELL_SIZE) + << std::to_string(data); + this->file.write(cell.str().data(), FT_CELL_SIZE); + this->file.seekg(-FT_SEEK_OFFSET + 1, fstream::cur); +} diff --git a/src/tape_config.cpp b/src/tape_config.cpp index 629b92f..7014b79 100644 --- a/src/tape_config.cpp +++ b/src/tape_config.cpp @@ -1,6 +1,4 @@ #include "tape_config.h" -#include "filetape.h" -#include struct CommandLineArgs { std::string config_file_path; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7bb6c28..ae7d9e1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,3 +5,4 @@ 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/assets/filetape_test_data.txt b/tests/assets/filetape_test_data.txt index 8a1218a..176a180 100644 --- a/tests/assets/filetape_test_data.txt +++ b/tests/assets/filetape_test_data.txt @@ -1,5 +1,5 @@ -1 -2 -3 -4 -5 +0000000001 +0000000002 +0000000003 +0000000004 +0000000005 diff --git a/tests/filetape_tests.cpp b/tests/filetape_tests.cpp index 17c408e..3f7cf25 100644 --- a/tests/filetape_tests.cpp +++ b/tests/filetape_tests.cpp @@ -1,10 +1,9 @@ #include "filetape.h" #include -#include // NOLINTBEGIN(readability-function-cognitive-complexity) TEST_CASE("Reading data from a FileTape", "[filetape]") { - tape::FileTape tape("assets/filetape_test_data.txt"); + tape::FileTape tape(FILETAPE_TEST_FILE); SECTION("Read all data sequentially") { REQUIRE(tape.read() == 1); @@ -35,7 +34,7 @@ TEST_CASE("Reading data from a FileTape", "[filetape]") { } TEST_CASE("Seeking forward and backwards", "[filetape]") { - tape::FileTape tape("assets/filetape_test_data.txt"); + tape::FileTape tape(FILETAPE_TEST_FILE); SECTION("Rewinding at the beginning of a file tape") { REQUIRE(tape.seek_backwards() == false); @@ -64,11 +63,20 @@ TEST_CASE("Seeking forward and backwards", "[filetape]") { } TEST_CASE("Writing to a file tape", "[filetape]") { - // init a temp file to modify - // std::string tmp_file_name = "/tmp/" - // std::unique_ptr tmp_file(std::tmpfile()); - // std::fstream tmp(tmp_file); - // std::fstream tmp(tmp_file); - // tape::FileTape tape("assets/filetape_test_data.txt"); + tape::FileTape tape(3); + + tape.write(0); + REQUIRE(tape.read() == 0); + tape.seek_forward(); + tape.write(1); + REQUIRE(tape.read() == 1); + tape.seek_forward(); + tape.write(2); + REQUIRE(tape.read() == 2); + tape.write(3); + REQUIRE(tape.read() == 3); + tape.seek_backwards(); + tape.write(4); + REQUIRE(tape.read() == 4); } // NOLINTEND(readability-function-cognitive-complexity)