Skip to content

Commit

Permalink
first version
Browse files Browse the repository at this point in the history
  • Loading branch information
andrew-grechkin committed Aug 19, 2019
1 parent 48e3ad6 commit 4724e17
Show file tree
Hide file tree
Showing 11 changed files with 249 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
build/
14 changes: 14 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 3.0.0)
project(sparse VERSION 1.0.0)

add_executable(sparse src/main.cpp src/config.cpp src/file.cpp src/memory.cpp src/stats.cpp)

target_include_directories(sparse PRIVATE src/include)

include(CTest)
enable_testing()

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
52 changes: 52 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#include "config.hpp"

#include <argp.h>
#include <cstdlib>
#include <unistd.h>

const char *argp_program_version = "v1.0.0";
const char *argp_program_bug_address = "Andrew Grechkin <[email protected]>";

auto args_doc = "FILE";
auto doc = "sparse -- a simple tool to pipe standard input into a sparse file";

const argp_option options[] = {
{"input", 'i', "FILE", 0, "Input from FILE instead of standard input" },
{"verbose", 'v', 0, 0, "Produce verbose output" },
{ 0 },
};

error_t parse_opt(int key, char *arg, struct argp_state *state) {
auto args = static_cast<Config*>(state->input);
switch (key) {
case 'v':
args->verbose = 1;
break;
case 'i':
args->input_file = arg;
break;
case ARGP_KEY_NO_ARGS:
argp_usage(state);
case ARGP_KEY_ARG:
args->file = arg;
state->next = state->argc;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}

const argp argp = { options, parse_opt, args_doc, doc, 0, 0, 0 };

int Config::parse(int argc, char ** argv)
{
buf_size = getpagesize();
return argp_parse(&argp, argc, argv, 0, nullptr, this);
}

Config& Config::get()
{
static Config inst;
return inst;
}
69 changes: 69 additions & 0 deletions src/file.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "config.hpp"
#include "file.hpp"
#include "stats.hpp"
#include "memory.hpp"

#include <cstdlib>
#include <cstdio>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>

File::~File()
{
if (close(fd))
warn("Unable to properly close destination file");
}

File::File(const char* path)
{
auto mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);

if (fd == -1)
err(EXIT_FAILURE, "Unable to create file '%s'", path);
}

void File::save_sparce_file(int src)
{
auto& config = Config::get();

auto buf = malloc(config.buf_size);
if (!buf)
err(EXIT_FAILURE, "Unable to create buffer");

auto stats = Stats();
while (true) {
auto bytes_read = read(src, buf, config.buf_size);

if (bytes_read == 0) {
break;
} else if (bytes_read < 0) {
err(EXIT_FAILURE, "Unable to read data from source");
}

stats.total_read += bytes_read;
if (is_memory_dirty(buf, bytes_read)) {
auto bytes_written = write(fd, buf, bytes_read);
if (bytes_written != bytes_read) {
err(EXIT_FAILURE, "Unable to write into destination file");
}
stats.total_written += bytes_written;
} else {
lseek(fd, bytes_read, SEEK_CUR);
ftruncate(fd, current_offset());
stats.total_saved += bytes_read;
}
}

if (config.verbose) {
printf("Buffer used: %ld bytes\n", config.buf_size);
stats.print();
}
}

size_t File::current_offset() const
{
return lseek(fd, 0, SEEK_CUR);
}
24 changes: 24 additions & 0 deletions src/include/config.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef __SPARSE__CONFIG_HPP__
#define __SPARSE__CONFIG_HPP__

#include <cstddef>

struct Config
{
static Config& get();

size_t verbose = 0;
size_t buf_size = 0;
char *input_file = nullptr;
char *file = nullptr;

int parse(int argc, char ** argv);

Config(const Config&) = delete;
void operator= (const Config&) = delete;

private:
Config() {}
};

#endif
19 changes: 19 additions & 0 deletions src/include/file.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#ifndef __SPARSE__FILE_HPP__
#define __SPARSE__FILE_HPP__

#include <cstdlib>

class File
{
public:
~File();
File(const char* path);
void save_sparce_file(int src);
void save_sparce_file(int src, int buf_size);
size_t current_offset() const;

private:
int fd;
};

#endif
8 changes: 8 additions & 0 deletions src/include/memory.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef __SPARSE__MEMORY_HPP__
#define __SPARSE__MEMORY_HPP__

#include <cstdlib>

bool is_memory_dirty(const void* buf, size_t size);

#endif
14 changes: 14 additions & 0 deletions src/include/stats.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef __SPARSE__STATS_HPP__
#define __SPARSE__STATS_HPP__

#include <cstdlib>

struct Stats
{
size_t total_read = 0;
size_t total_written = 0;
size_t total_saved = 0;
void print() const;
};

#endif
29 changes: 29 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "config.hpp"
#include "file.hpp"

#include <cstdio>
#include <error.h>
#include <unistd.h>
#include <err.h>

int main(int argc, char **argv)
{
auto& config = Config::get();

auto err = config.parse(argc, argv);
if (!err) {
auto src = 0;
if (config.input_file) {
}

auto dst = File(config.file);
dst.save_sparce_file(src);

if (src && close(src))
::err(EXIT_FAILURE, "Unable to close source");

return EXIT_SUCCESS;
}

return err;
}
9 changes: 9 additions & 0 deletions src/memory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include "memory.hpp"

#include <cstring>

bool is_memory_dirty(const void* buf, size_t size)
{
auto ptr = static_cast<const char*>(buf);
return *ptr || std::memcmp(ptr, ptr + 1, size - 1);
}
10 changes: 10 additions & 0 deletions src/stats.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "stats.hpp"

#include <cstdio>

void Stats::print() const
{
std::printf("Read total: %lu bytes\n", total_read);
std::printf("Saved total: %lu bytes\n", total_saved);
std::printf("Written total: %lu bytes\n", total_written);
}

0 comments on commit 4724e17

Please sign in to comment.