diff --git a/.github/workflows/vtpc.yml b/.github/workflows/vtpc.yml new file mode 100644 index 0000000..979f74e --- /dev/null +++ b/.github/workflows/vtpc.yml @@ -0,0 +1,54 @@ +name: Test VT Page Cache + +on: + push: + branches: + - main + pull_request: + paths: + - 'lab/vtpc/**' + +jobs: + build: + strategy: + matrix: + cmake_build_type: + - Asan + - Release + runs-on: ubuntu-latest + container: + image: silkeh/clang:latest + defaults: + run: + working-directory: ./lab/vtpc + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Clang Format + run: | + find . -path ./build -prune -o \ + \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) \ + -exec clang-format --style=file --dry-run --verbose {} \; + + - name: Configure + run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} + + - name: Build + run: cmake --build build + + - name: Clang Tidy + if: matrix.cmake_build_type == 'Release' + run: | + find . -path ./build -prune -o \ + \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) -exec \ + clang-tidy -p ./build {} \; + + - name: Test Basic + run: ./build/test/test_basic + + - name: Test Sequential + run: ./build/test/test_seq + + - name: Test Random + run: ./build/test/test_random diff --git a/.github/workflows/vtsh.yml b/.github/workflows/vtsh.yml index c1ec7cf..2fbfefe 100644 --- a/.github/workflows/vtsh.yml +++ b/.github/workflows/vtsh.yml @@ -27,8 +27,9 @@ jobs: - name: Clang Format run: | - find . -path ./build -prune -o \( -iname '*.c' -o -iname '*.h' \) -exec \ - clang-format --style=file --dry-run --verbose {} \; + find . -path ./build -prune -o \ + \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) \ + -exec clang-format --style=file --dry-run --verbose {} \; - name: Configure run: cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} @@ -37,9 +38,11 @@ jobs: run: cmake --build build - name: Clang Tidy + if: matrix.cmake_build_type == 'Release' run: | - find . -path ./build -prune -o \( -iname '*.c' -o -iname '*.h' \) -exec \ - clang-tidy -p . {} \; + find . -path ./build -prune -o \ + \( -iname '*.c' -o -iname '*.h' -o -iname '*.cpp' -o -iname '*.hpp' \) -exec \ + clang-tidy -p ./build {} \; - name: Test working-directory: ./lab/vtsh/test diff --git a/lab/vtpc/.clang-format b/lab/vtpc/.clang-format new file mode 100644 index 0000000..dd99110 --- /dev/null +++ b/lab/vtpc/.clang-format @@ -0,0 +1,16 @@ +BasedOnStyle: Google + +AccessModifierOffset: -2 + +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false + +DerivePointerAlignment: false +BreakConstructorInitializers: BeforeComma +AlignAfterOpenBracket: BlockIndent + +BinPackArguments: false +BinPackParameters: false + +AlignArrayOfStructures: Right diff --git a/lab/vtpc/.clang-tidy b/lab/vtpc/.clang-tidy new file mode 100644 index 0000000..6cdbcfb --- /dev/null +++ b/lab/vtpc/.clang-tidy @@ -0,0 +1,22 @@ +Checks: | + -*, + bugprone-*, + -bugprone-easily-swappable-parameters, + cert-*, + clang-analyzer-*, + concurrency-*, + cppcoreguidelines-*, + -cppcoreguidelines-avoid-non-const-global-variables, + -cppcoreguidelines-special-member-functions, + google-*, + hicpp-*, + -hicpp-special-member-functions, + misc-*, + modernize-*, + performance-*, + portability-*, + -portability-avoid-pragma-once, + readability-* + -readability-identifier-length +WarningsAsErrors: '*' +FormatStyle: file diff --git a/lab/vtpc/.gitignore b/lab/vtpc/.gitignore new file mode 100644 index 0000000..fb90d94 --- /dev/null +++ b/lab/vtpc/.gitignore @@ -0,0 +1,8 @@ +# CMake +build/ + +# Clangd +.cache/ + +# Python +__pycache__/ diff --git a/lab/vtpc/.vscode/settings.json b/lab/vtpc/.vscode/settings.json new file mode 100644 index 0000000..db30e90 --- /dev/null +++ b/lab/vtpc/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "clangd.arguments": [ + "-compile-commands-dir=./build", + "-header-insertion=never", + ], + "files.eol": "\n", +} diff --git a/lab/vtpc/CMakeLists.txt b/lab/vtpc/CMakeLists.txt new file mode 100644 index 0000000..478cbae --- /dev/null +++ b/lab/vtpc/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.12) + +project( + vtpc + VERSION 0.1 + DESCRIPTION "VT Page Cache" +) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_subdirectory(lib) +add_subdirectory(test) diff --git a/lab/vtpc/README.md b/lab/vtpc/README.md new file mode 100644 index 0000000..78e4047 --- /dev/null +++ b/lab/vtpc/README.md @@ -0,0 +1,47 @@ +# Базовый трек. Лабораторная работа 2 + +## Задание + +Для оптимизации работы с блочными устройствами в ОС существует кэш страниц с данными, которыми мы производим операции чтения и записи на диск. Такой кэш позволяет избежать высоких задержек при повторном доступе к данным, так как операция будет выполнена с данными в RAM, а не на диске (вспомним пирамиду памяти). + +В данной лабораторной работе необходимо реализовать блочный кэш в пространстве пользователя в виде динамической библиотеки. Политику вытеснения страниц и другие элементы задания необходимо получить у преподавателя. + +При выполнении работы необходимо реализовать простой API для работы с файлами, предоставляющий пользователю следующие возможности: + +1. Открытие файла по заданному пути файла, доступного для чтения. Процедура возвращает некоторый хэндл на файл. Пример: `int lab2_open(const char *path)`. + +2. Закрытие файла по хэндлу. Пример: `int lab2_close(int fd)`. + +3. Чтение данных из файла. Пример: `ssize_t lab2_read(int fd, void buf[.count], size_t count)`. + +4. Запись данных в файл. Пример: `ssize_t lab2_write(int fd, const void buf[.count], size_t count)`. + +5. Перестановка позиции указателя на данные файла. Достаточно поддержать только абсолютные координаты. Пример: `​​​​​​​off_t lab2_lseek(int fd, off_t offset, int whence)`. + +6. Синхронизация данных из кэша с диском. Пример: `int lab2_fsync(int fd)`. + +Операции с диском разработанного блочного кеша должны производиться в обход page cache ОС. + +В рамках проверки работоспособности разработанного блочного кэша необходимо адаптировать указанную преподавателем программу-загрузчик из ЛР 1, добавив использование кэша. Запустите программу и убедитесь, что она корректно работает. Сравните производительность до и после. + +## Ограничения + +1. Программа (комплекс программ) должна быть реализован на языке C. + +2. Если по выданному варианту задана политика вытеснения Optimal, то необходимо предоставить пользователю возможность подсказать page cache, когда будет совершен следующий доступ к данным. Это можно сделать либо добавив параметр в процедуры `read` и `write` (например, `ssize_t lab2_read(int fd, void buf[.count], size_t count, access_hint_t hint)`), либо добавив еще одну функцию в API (например, `int lab2_advice(int fd, off_t offset, access_hint_t hint)`). `access_hint_t` в данном случае – это абсолютное время или временной интервал, по которому разработанное API будет определять время последующего доступа к данным. + +3. Запрещено использовать высокоуровневые абстракции над системными вызовами. + +## Требования к отчету и защите + +Отчет должен содержать: + +1. титульный лист с указанием номера и названия ЛР, вашего ФИО, ФИО преподавателя практики, номера вашей группы, варианта ЛР; + +2. текст задания в соответствии с вариантом; + +3. краткий обзор кода; + +4. данные о работе программы-нагрузчика до и после внедрения своего page cache; + +5. заключение с анализом результатов и выводом. diff --git a/lab/vtpc/lib/CMakeLists.txt b/lab/vtpc/lib/CMakeLists.txt new file mode 100644 index 0000000..e904b37 --- /dev/null +++ b/lab/vtpc/lib/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library( + vtpc + STATIC + vtpc.c +) + +target_include_directories( + vtpc + PUBLIC + . +) diff --git a/lab/vtpc/lib/vtpc.c b/lab/vtpc/lib/vtpc.c new file mode 100644 index 0000000..173ab1b --- /dev/null +++ b/lab/vtpc/lib/vtpc.c @@ -0,0 +1,30 @@ +#include "vtpc.h" + +#include +#include +#include +#include + +int vtpc_open(const char* path, int mode, int access) { + return open(path, mode, access); +} + +int vtpc_close(int fd) { + return close(fd); +} + +ssize_t vtpc_read(int fd, void* buf, size_t count) { + return read(fd, buf, count); +} + +ssize_t vtpc_write(int fd, const void* buf, size_t count) { + return write(fd, buf, count); +} + +off_t vtpc_lseek(int fd, off_t offset, int whence) { + return lseek(fd, offset, whence); +} + +int vtpc_fsync(int fd) { + return fsync(fd); +} diff --git a/lab/vtpc/lib/vtpc.h b/lab/vtpc/lib/vtpc.h new file mode 100644 index 0000000..2d4bd40 --- /dev/null +++ b/lab/vtpc/lib/vtpc.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +int vtpc_open(const char* path, int mode, int access); +int vtpc_close(int fd); +ssize_t vtpc_read(int fd, void* buf, size_t count); +ssize_t vtpc_write(int fd, const void* buf, size_t count); +off_t vtpc_lseek(int fd, off_t offset, int whence); +int vtpc_fsync(int fd); diff --git a/lab/vtpc/test/CMakeLists.txt b/lab/vtpc/test/CMakeLists.txt new file mode 100644 index 0000000..4c59001 --- /dev/null +++ b/lab/vtpc/test/CMakeLists.txt @@ -0,0 +1,17 @@ +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +add_subdirectory(lib) + +add_executable(test_basic test_basic.cpp) +target_include_directories(test_basic PUBLIC .) +target_link_libraries(test_basic PRIVATE vt) + +add_executable(test_seq test_seq.cpp) +target_include_directories(test_seq PUBLIC .) +target_link_libraries(test_seq PRIVATE vt) + +add_executable(test_random test_random.cpp) +target_include_directories(test_random PUBLIC .) +target_link_libraries(test_random PRIVATE vt) diff --git a/lab/vtpc/test/lib/CMakeLists.txt b/lab/vtpc/test/lib/CMakeLists.txt new file mode 100644 index 0000000..9aca5ff --- /dev/null +++ b/lab/vtpc/test/lib/CMakeLists.txt @@ -0,0 +1,11 @@ +add_library( + vt + STATIC + cmp_file.cpp + exception.cpp + file.cpp + log_file.cpp +) + +target_include_directories(vt PUBLIC .) +target_link_libraries(vt PRIVATE vtpc) diff --git a/lab/vtpc/test/lib/cmp_file.cpp b/lab/vtpc/test/lib/cmp_file.cpp new file mode 100644 index 0000000..db42e63 --- /dev/null +++ b/lab/vtpc/test/lib/cmp_file.cpp @@ -0,0 +1,82 @@ +#include "cmp_file.hpp" + +#include + +#include +#include +#include +#include +#include +#include + +#include "exception.hpp" +#include "file.hpp" + +namespace vt { + +template +void Compare(A lhs, B rhs) { + std::optional lhs_ex; + std::optional rhs_ex; + + try { + lhs(); + } catch (const vt::file_exception& e) { + lhs_ex = vt::file_exception(e.code()) << e.what(); + } + + try { + rhs(); + } catch (const vt::file_exception& e) { + rhs_ex = vt::file_exception(e.code()) << e.what(); + } + + if (lhs_ex && !rhs_ex) { + throw vt::cmp_file_exception() << "(FAIL, OK): " << lhs_ex->what(); + } + if (!lhs_ex && rhs_ex) { + throw vt::cmp_file_exception() << "(OK, FAIL): " << rhs_ex->what(); + } + if (lhs_ex && rhs_ex && lhs_ex->code() != rhs_ex->code()) { + throw vt::cmp_file_exception() + << "(FAIL, FAIL), but codes differ: " << lhs_ex->what() << ", " + << rhs_ex->what(); + } + if (lhs_ex && rhs_ex) { + const vt::file_exception& e = *lhs_ex; + throw vt::file_exception(e.code()) << e.what(); + } +} + +cmp_file::cmp_file(std::unique_ptr lhs, std::unique_ptr rhs) + : lhs_(std::move(lhs)), file_(std::move(rhs)) { +} + +auto cmp_file::read(char* buffer, size_t count) -> void { + std::string lhs(count, ' '); + std::string rhs(count, ' '); + Compare( + [&] { lhs_->read(lhs.data(), count); }, + [&] { file_->read(rhs.data(), count); } + ); + if (lhs != rhs) { + throw vt::cmp_file_exception() << "'" << lhs << "' != '" << rhs << "'"; + } + memcpy(buffer, lhs.data(), count); +} + +auto cmp_file::write(const char* buffer, size_t count) -> void { + Compare( + [&] { lhs_->write(buffer, count); }, [&] { file_->write(buffer, count); } + ); +} + +auto cmp_file::seek(off_t offset) -> void { + Compare([&] { lhs_->seek(offset); }, [&] { file_->seek(offset); }); +} + +auto cmp_file::sync() -> void { + Compare([&] { lhs_->sync(); }, [this] { file_->sync(); }); +} + +} // namespace vt diff --git a/lab/vtpc/test/lib/cmp_file.hpp b/lab/vtpc/test/lib/cmp_file.hpp new file mode 100644 index 0000000..b984ae1 --- /dev/null +++ b/lab/vtpc/test/lib/cmp_file.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include +#include + +#include "exception.hpp" +#include "file.hpp" + +namespace vt { + +class cmp_file_exception : public vt::exception {}; + +class cmp_file final : public file { +public: + using file::read; + using file::write; + + cmp_file(std::unique_ptr lhs, std::unique_ptr rhs); + ~cmp_file() override = default; + + auto read(char* buffer, size_t count) -> void override; + auto write(const char* buffer, size_t count) -> void override; + auto seek(off_t offset) -> void override; + auto sync() -> void override; + +private: + std::unique_ptr lhs_; + std::unique_ptr file_; +}; + +} // namespace vt diff --git a/lab/vtpc/test/lib/exception.cpp b/lab/vtpc/test/lib/exception.cpp new file mode 100644 index 0000000..18042a1 --- /dev/null +++ b/lab/vtpc/test/lib/exception.cpp @@ -0,0 +1 @@ +#include "exception.hpp" // NOLINT diff --git a/lab/vtpc/test/lib/exception.hpp b/lab/vtpc/test/lib/exception.hpp new file mode 100644 index 0000000..9dd8d6d --- /dev/null +++ b/lab/vtpc/test/lib/exception.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +namespace vt { + +class exception : public std::exception { +public: + exception() = default; + + [[nodiscard]] + auto what() const noexcept -> const char* override { + message_ = buffer_.str(); + return message_.c_str(); + } + + template + void Append(const T& t) { + buffer_ << t; + } + +private: + mutable std::stringstream buffer_; + mutable std::string message_; +}; + +template + requires std::is_base_of_v> +static auto operator<<(E&& e [[clang::lifetimebound]], const T& t) -> E&& { + e.Append(t); + return std::forward(e); +} + +} // namespace vt \ No newline at end of file diff --git a/lab/vtpc/test/lib/file.cpp b/lab/vtpc/test/lib/file.cpp new file mode 100644 index 0000000..dff8ed9 --- /dev/null +++ b/lab/vtpc/test/lib/file.cpp @@ -0,0 +1,133 @@ +#include "file.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exception.hpp" + +extern "C" { +#include +#include +#include + +#include "vtpc.h" +} + +namespace vt { + +constexpr auto flags = O_RDWR | O_CREAT; +constexpr auto access = 0777; + +file_exception::file_exception(ssize_t code) : code_(code) { +} + +auto file_exception::code() const -> ssize_t { + return code_; +} + +struct io { + std::function open; + std::function close; + std::function read; + std::function write; + std::function lseek; + std::function fsync; +}; + +template +void robust_do(A action, int fd, auto buf, size_t count) { + size_t total = 0; + while (total < count) { + const ssize_t local = action(fd, buf, count); + if (local < 0) { + throw vt::file_exception(local) + << "failed to read/write " << count << " bytes from file with fd " + << fd << ": " << strerror(errno); // NOLINT(concurrency-mt-unsafe); + } + if (local == 0) { + throw vt::file_exception(0) + << "failed to read/write " << count << " bytes from file with fd " + << fd << ": " << "EOF after reading " << total << " bytes"; + } + + total += local; + } +} + +class io_file final : public file { +public: + explicit io_file(std::string_view path, io io) + : fd_(io.open(path.data(), flags, access)), io_(std::move(io)) { + if (fd_ < 0) { + throw vt::file_exception(fd_) + << "failed to open file '" << path << "'" << ": " + << strerror(errno); // NOLINT(concurrency-mt-unsafe); + } + } + + ~io_file() override { + (void)io_.close(fd_); + } + + void read(char* buffer, size_t count) override { + robust_do(io_.read, fd_, buffer, count); + } + + void write(const char* buffer, size_t count) override { + robust_do(io_.write, fd_, buffer, count); + } + + void seek(off_t offset) override { + if (io_.lseek(fd_, offset, SEEK_SET) == -1) { + throw vt::file_exception(-1) + << "failed to seek to offset " << offset << "file with fd " << fd_ + << ": " << strerror(errno); // NOLINT(concurrency-mt-unsafe) + } + } + + void sync() override { + if (io_.fsync(fd_) == -1) { + throw vt::file_exception(-1) + << "failed to fsync file with fd " << fd_ << ": " + << strerror(errno); // NOLINT(concurrency-mt-unsafe) + } + } + +private: + int fd_; + io io_; +}; + +auto file::open_libc(std::string_view path) -> std::unique_ptr { + io io = { + .open = ::open, + .close = ::close, + .read = ::read, + .write = ::write, + .lseek = ::lseek, + .fsync = ::fsync, + }; + + return std::make_unique(path, std::move(io)); +} + +auto file::open_vtpc(std::string_view path) -> std::unique_ptr { + io io = { + .open = ::vtpc_open, + .close = ::vtpc_close, + .read = ::vtpc_read, + .write = ::vtpc_write, + .lseek = ::vtpc_lseek, + .fsync = ::vtpc_fsync, + }; + + return std::make_unique(path, std::move(io)); +} + +} // namespace vt diff --git a/lab/vtpc/test/lib/file.hpp b/lab/vtpc/test/lib/file.hpp new file mode 100644 index 0000000..378aedd --- /dev/null +++ b/lab/vtpc/test/lib/file.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include "exception.hpp" + +namespace vt { + +class file_exception : public vt::exception { +public: + explicit file_exception(ssize_t code); + + [[nodiscard]] auto code() const -> ssize_t; + +private: + ssize_t code_; +}; + +class file { +public: + virtual ~file() = default; + virtual auto read(char* buffer, size_t count) -> void = 0; + virtual auto write(const char* buffer, size_t count) -> void = 0; + virtual auto seek(off_t offset) -> void = 0; + virtual auto sync() -> void = 0; + + auto write(std::string_view text) -> void { + write(text.data(), text.size()); + } + + auto read(size_t size) -> std::string { + std::string text(size, 0); + read(text.data(), size); + return text; + } + + static auto open_libc(std::string_view path) -> std::unique_ptr; + static auto open_vtpc(std::string_view path) -> std::unique_ptr; +}; + +} // namespace vt diff --git a/lab/vtpc/test/lib/log_file.cpp b/lab/vtpc/test/lib/log_file.cpp new file mode 100644 index 0000000..499eca1 --- /dev/null +++ b/lab/vtpc/test/lib/log_file.cpp @@ -0,0 +1,37 @@ +#include "log_file.hpp" + +#include + +#include +#include +#include +#include + +#include "file.hpp" + +namespace vt { + +log_file::log_file(std::unique_ptr file) : file_(std::move(file)) { +} + +auto log_file::read(char* buffer, size_t count) -> void { + std::cerr << "[vt] read count " << count << "\n"; + file_->read(buffer, count); +} + +auto log_file::write(const char* buffer, size_t count) -> void { + std::cerr << "[vt] write count " << count << "\n"; + file_->write(buffer, count); +} + +auto log_file::seek(off_t offset) -> void { + std::cerr << "[vt] seek offset " << offset << "\n"; + file_->seek(offset); +} + +auto log_file::sync() -> void { + std::cerr << "[vt] sync\n"; + file_->sync(); +} + +} // namespace vt diff --git a/lab/vtpc/test/lib/log_file.hpp b/lab/vtpc/test/lib/log_file.hpp new file mode 100644 index 0000000..d5c8aa9 --- /dev/null +++ b/lab/vtpc/test/lib/log_file.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include +#include + +#include "file.hpp" + +namespace vt { + +class log_file final : public file { +public: + using file::read; + using file::write; + + explicit log_file(std::unique_ptr file); + ~log_file() override = default; + + auto read(char* buffer, size_t count) -> void override; + auto write(const char* buffer, size_t count) -> void override; + auto seek(off_t offset) -> void override; + auto sync() -> void override; + +private: + std::unique_ptr file_; +}; + +} // namespace vt diff --git a/lab/vtpc/test/test_basic.cpp b/lab/vtpc/test/test_basic.cpp new file mode 100644 index 0000000..74c0855 --- /dev/null +++ b/lab/vtpc/test/test_basic.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +#include "cmp_file.hpp" +#include "file.hpp" + +auto main() -> int try { + auto libc = vt::file::open_libc("/tmp/a"); + auto vtpc = vt::file::open_vtpc("/tmp/b"); + vt::cmp_file cmp(std::move(libc), std::move(vtpc)); + + std::string_view message = "Hello, World!"; + cmp.write(message); + cmp.sync(); + cmp.seek(0); + std::cout << cmp.read(message.size()) << '\n'; + + return 0; +} catch (const std::exception& e) { + std::cerr << "exception: " << e.what() << '\n'; + return 1; +} diff --git a/lab/vtpc/test/test_random.cpp b/lab/vtpc/test/test_random.cpp new file mode 100644 index 0000000..9d91208 --- /dev/null +++ b/lab/vtpc/test/test_random.cpp @@ -0,0 +1,76 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmp_file.hpp" +#include "file.hpp" +#include "log_file.hpp" + +auto main() -> int try { + constexpr size_t seed = 1; + constexpr size_t steps = (1U << 16U); + constexpr size_t size = (1U << 12U); + constexpr size_t interval = 100; + + std::unique_ptr file = [] { + auto libc = vt::file::open_libc("/tmp/a"); + auto vtpc = vt::file::open_vtpc("/tmp/b"); + auto cmp = std::make_unique(std::move(libc), std::move(vtpc)); + auto log = std::make_unique(std::move(cmp)); + return log; + }(); + + std::default_random_engine random(seed); // NOLINT + + std::uniform_int_distribution action_dist(0, 100); // NOLINT + std::uniform_int_distribution offset_dist(0, size); + std::uniform_int_distribution batch_dist(0, size / 4); + std::uniform_int_distribution char_dist(0); + + const auto random_string = [&](size_t size) { + std::string string(size, ' '); + for (char& c : string) { + c = static_cast(char_dist(random)); + } + return string; + }; + + file->seek(0); + file->write(std::string(size, ' ')); + + file->seek(0); + for (size_t i = 0; i < steps; ++i) { + if (i % interval == 0) { + std::cerr << "i = " << i << '\n'; + } + + try { + size_t point = action_dist(random); + if (point < 40) { // NOLINT + size_t batch = batch_dist(random); + file->read(batch); + } else if (point < 75) { // NOLINT + size_t batch = batch_dist(random); + file->write(random_string(batch)); + } else if (point < 95) { // NOLINT + file->seek(offset_dist(random)); + } else { + file->sync(); + } + } catch (vt::file_exception& e) { // NOLINT + // Do nothing + } + } + + return 0; +} catch (const std::exception& e) { + std::cerr << "exception: " << e.what() << '\n'; + return 1; +} diff --git a/lab/vtpc/test/test_seq.cpp b/lab/vtpc/test/test_seq.cpp new file mode 100644 index 0000000..3aa417a --- /dev/null +++ b/lab/vtpc/test/test_seq.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include +#include +#include + +#include "cmp_file.hpp" +#include "file.hpp" + +auto main() -> int try { + constexpr size_t count = 1024; + + auto libc = vt::file::open_libc("/tmp/a"); + auto vtpc = vt::file::open_vtpc("/tmp/b"); + vt::cmp_file cmp(std::move(libc), std::move(vtpc)); + + cmp.seek(0); + for (size_t i = 0; i < count; ++i) { + std::string text = std::to_string(i); + cmp.write(text); + } + + cmp.seek(0); + for (size_t i = 0; i < count; ++i) { + std::string expected = std::to_string(i); + std::string actual = cmp.read(expected.size()); + if (expected != actual) { + throw vt::exception() << "'" << expected << "' != '" << actual << "'"; + } + } + + cmp.sync(); + + return 0; +} catch (const std::exception& e) { + std::cerr << "exception: " << e.what() << '\n'; + return 1; +}