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
This commit is contained in:
parent
0e49b35f12
commit
f3aaa26df8
7 changed files with 118 additions and 60 deletions
|
@ -2,10 +2,10 @@
|
||||||
#define FILE_TAPE_H
|
#define FILE_TAPE_H
|
||||||
|
|
||||||
#include "tape.h"
|
#include "tape.h"
|
||||||
#include <cstddef>
|
#include <chrono>
|
||||||
#include <cstdint>
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string>
|
|
||||||
|
using std::chrono::milliseconds;
|
||||||
|
|
||||||
namespace tape {
|
namespace tape {
|
||||||
|
|
||||||
|
@ -14,35 +14,46 @@ namespace tape {
|
||||||
* such as read dealy, write delay etc.
|
* such as read dealy, write delay etc.
|
||||||
*/
|
*/
|
||||||
struct FileTapeSettings {
|
struct FileTapeSettings {
|
||||||
unsigned int read_delay_ms;
|
milliseconds read_delay;
|
||||||
unsigned int write_delay_ms;
|
milliseconds write_delay;
|
||||||
unsigned int seek_forward_delay_ms;
|
milliseconds seek_forward_delay;
|
||||||
unsigned int seek_backwards_delay_ms;
|
milliseconds seek_backwards_delay;
|
||||||
};
|
};
|
||||||
|
|
||||||
const static struct FileTapeSettings DEFAULT_FT_SETTINGS =
|
const static struct FileTapeSettings FT_DEFAULT_SETTINGS =
|
||||||
FileTapeSettings{.read_delay_ms = 0,
|
FileTapeSettings{.read_delay = milliseconds(0),
|
||||||
.write_delay_ms = 0,
|
.write_delay = milliseconds(0),
|
||||||
.seek_forward_delay_ms = 0,
|
.seek_forward_delay = milliseconds(0),
|
||||||
.seek_backwards_delay_ms = 0};
|
.seek_backwards_delay = milliseconds(0)};
|
||||||
|
|
||||||
const static char FT_DELIMETER = '\n';
|
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.
|
* Mock tape implementation that uses text files as data source.
|
||||||
*
|
*
|
||||||
* Simulates tape operations by artificially pausing the execution of some
|
* Simulates tape operations by artificially pausing the execution of some
|
||||||
* methods using time intervals, specified in command line arguments or a
|
* methods using time intervals, specified in command line arguments or a
|
||||||
* configuration file. Each cell is represented as a plain text 32-bit int
|
* configuration file. Each cell is represented as a plain text 32-bit unsigned
|
||||||
* separated by FT_DELIMETER. End of the file tape is indicated either by EOF
|
* int, padded to FT_CELL_SIZE with leading zeroes, separated by FT_DELIMETER.
|
||||||
* or some arbitrary data that cannot be parsed to int32_t
|
* 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 {
|
class FileTape : public Tape {
|
||||||
private:
|
private:
|
||||||
std::fstream file;
|
std::fstream file;
|
||||||
FileTapeSettings settings;
|
FileTapeSettings settings;
|
||||||
size_t prev_line_pos = 0;
|
|
||||||
bool at_first_line = false;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +65,7 @@ class FileTape : public Tape {
|
||||||
* @param settings FileTape settings
|
* @param settings FileTape settings
|
||||||
*/
|
*/
|
||||||
explicit FileTape(const std::string &file_name,
|
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
|
* 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
|
* @param settings FileTape settings
|
||||||
*/
|
*/
|
||||||
explicit FileTape(size_t size, const std::string &file_name,
|
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
|
* Initializes a new instance of FileTape that will create and open a
|
||||||
|
@ -76,7 +87,7 @@ class FileTape : public Tape {
|
||||||
* @param settings FileTape settings
|
* @param settings FileTape settings
|
||||||
*/
|
*/
|
||||||
explicit FileTape(size_t size,
|
explicit FileTape(size_t size,
|
||||||
FileTapeSettings settings = DEFAULT_FT_SETTINGS);
|
FileTapeSettings settings = FT_DEFAULT_SETTINGS);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances the underlying fstream to a new line.
|
* 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.
|
* @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.
|
* Writes data to the current line.
|
||||||
*
|
*
|
||||||
* @param data Number, that should be written 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
|
} // namespace tape
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace tape {
|
||||||
* Abstract class representing a magnetic tape.
|
* Abstract class representing a magnetic tape.
|
||||||
*
|
*
|
||||||
* Tape is a storage medium consisting of N cells, where each cell contains a
|
* 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
|
* 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
|
* 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
|
* 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.
|
* @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.
|
* Writes data to the current cell.
|
||||||
*
|
*
|
||||||
* @param data Number, that should be written 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
|
} // namespace tape
|
||||||
|
|
|
@ -1,41 +1,81 @@
|
||||||
#include "filetape.h"
|
#include "filetape.h"
|
||||||
#include <array>
|
#include <thread>
|
||||||
#include <cstdint>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
|
using std::fstream;
|
||||||
using tape::FileTape;
|
using tape::FileTape;
|
||||||
|
|
||||||
FileTape::FileTape(const std::string &file_name, FileTapeSettings settings)
|
FileTape::FileTape(const std::string &file_name, FileTapeSettings settings)
|
||||||
: settings(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,
|
FileTape::FileTape(size_t size, const std::string &file_name,
|
||||||
FileTapeSettings settings)
|
FileTapeSettings settings)
|
||||||
: FileTape(file_name, settings) {
|
: FileTape(file_name, settings) {
|
||||||
this->file << "0";
|
this->file =
|
||||||
for (int i = 0; i < size; i++) {
|
fstream(file_name, fstream::in | fstream::out | fstream::trunc);
|
||||||
this->file << FT_DELIMETER << "0";
|
fill_file_with_empty_cells(size, file);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FileTape::FileTape(size_t size, FileTapeSettings settings)
|
FileTape::FileTape(size_t size, FileTapeSettings settings)
|
||||||
: settings(settings) {
|
: settings(settings) {
|
||||||
std::array<char, L_tmpnam> file_name{};
|
std::array<char, L_tmpnam> file_name{};
|
||||||
|
// tmpnam is unsafe, but its cross-platform
|
||||||
|
// create a platform dependant tmp file handler later
|
||||||
std::tmpnam(file_name.data());
|
std::tmpnam(file_name.data());
|
||||||
this->file = std::fstream(file_name.data());
|
this->file =
|
||||||
this->file << "0";
|
fstream(file_name.data(), fstream::in | fstream::out | fstream::trunc);
|
||||||
for (int i = 0; i < size; i++) {
|
fill_file_with_empty_cells(size, file);
|
||||||
this->file << FT_DELIMETER << "0";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#include "tape_config.h"
|
#include "tape_config.h"
|
||||||
#include "filetape.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
struct CommandLineArgs {
|
struct CommandLineArgs {
|
||||||
std::string config_file_path;
|
std::string config_file_path;
|
||||||
|
|
|
@ -5,3 +5,4 @@ target_link_libraries(filetape_tests PRIVATE tapelib)
|
||||||
target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain)
|
target_link_libraries(filetape_tests PRIVATE Catch2::Catch2WithMain)
|
||||||
|
|
||||||
add_test(filetape_tests filetape_tests)
|
add_test(filetape_tests filetape_tests)
|
||||||
|
add_compile_definitions(FILETAPE_TEST_FILE=\"${PROJECT_SOURCE_DIR}/tests/assets/filetape_test_data.txt\")
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
1
|
0000000001
|
||||||
2
|
0000000002
|
||||||
3
|
0000000003
|
||||||
4
|
0000000004
|
||||||
5
|
0000000005
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
#include "filetape.h"
|
#include "filetape.h"
|
||||||
#include <catch2/catch_test_macros.hpp>
|
#include <catch2/catch_test_macros.hpp>
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
// NOLINTBEGIN(readability-function-cognitive-complexity)
|
// NOLINTBEGIN(readability-function-cognitive-complexity)
|
||||||
TEST_CASE("Reading data from a FileTape", "[filetape]") {
|
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") {
|
SECTION("Read all data sequentially") {
|
||||||
REQUIRE(tape.read() == 1);
|
REQUIRE(tape.read() == 1);
|
||||||
|
@ -35,7 +34,7 @@ TEST_CASE("Reading data from a FileTape", "[filetape]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Seeking forward and backwards", "[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") {
|
SECTION("Rewinding at the beginning of a file tape") {
|
||||||
REQUIRE(tape.seek_backwards() == false);
|
REQUIRE(tape.seek_backwards() == false);
|
||||||
|
@ -64,11 +63,20 @@ TEST_CASE("Seeking forward and backwards", "[filetape]") {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Writing to a file tape", "[filetape]") {
|
TEST_CASE("Writing to a file tape", "[filetape]") {
|
||||||
// init a temp file to modify
|
tape::FileTape tape(3);
|
||||||
// std::string tmp_file_name = "/tmp/"
|
|
||||||
// std::unique_ptr<std::FILE> tmp_file(std::tmpfile());
|
tape.write(0);
|
||||||
// std::fstream tmp(tmp_file);
|
REQUIRE(tape.read() == 0);
|
||||||
// std::fstream tmp(tmp_file);
|
tape.seek_forward();
|
||||||
// tape::FileTape tape("assets/filetape_test_data.txt");
|
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)
|
// NOLINTEND(readability-function-cognitive-complexity)
|
||||||
|
|
Loading…
Reference in a new issue