Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace fstream with cstdio #56

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
274 changes: 173 additions & 101 deletions ev3dev.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@

#include <iostream>
#include <sstream>
#include <fstream>
#include <list>
#include <map>
#include <array>
Expand All @@ -38,7 +37,8 @@
#include <thread>
#include <stdexcept>
#include <string.h>
#include <math.h>
#include <cmath>
#include <cstdio>

#include <dirent.h>
#include <sys/mman.h>
Expand Down Expand Up @@ -66,6 +66,160 @@ static const int bits_per_long = sizeof(long) * 8;
namespace ev3dev {
namespace {

//-----------------------------------------------------------------------------
class file_reader {
public:
file_reader() : f(0) {}

file_reader(file_reader &&other)
: fname(std::move(other.fname)), f(other.f)
{
other.f = 0;
}

~file_reader() {
if (f) fclose(f);
}

bool is_open() const {
return f != 0;
}

void open(const std::string &_fname) {
fname = _fname;
f = fopen(fname.c_str(), "r");
setbuf(f, NULL);

if (!f) {
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)),
std::string("Failed to open \"") + fname + "\"");
}
}

std::string get_string() {
char buf[256];
try_read([this, &buf](){ return 1 == fscanf(f, "%255s", buf); });
return buf;
}

std::string get_line() {
char buf[256];
try_read([this, &buf](){
if (!fgets(buf, 255, f)) return false;
char *pos;
if ((pos=strchr(buf, '\n')) != NULL) *pos = '\0';
return true;
});
return buf;
}

int get_int() {
int v;
try_read([this, &v](){return 1 == fscanf(f, "%d", &v); });
return v;
}

void get_data(char *data, size_t count) {
try_read([this, &data, count](){ return count == fread(data, count, count, f); });
}
private:
std::string fname;
FILE *f;

void reopen() {
if (f) fclose(f);
f = fopen(fname.c_str(), "r");
setbuf(f, NULL);
}

template <class Callable>
void try_read(Callable w) {
for(int attempt = 0; attempt < 2; ++attempt) {
fseek(f, 0, SEEK_SET);

if (w()) return;

// Failed to read the value.
// This could mean the sysfs attribute was recreated and the
// corresponding file handle got stale. Lets close the file and try
// again (once):
if (attempt != 0) {
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)), fname);
}

reopen();
}
}
};

//-----------------------------------------------------------------------------
class file_writer {
public:
file_writer() : f(0) {}

file_writer(file_writer &&other)
: fname(std::move(other.fname)), f(other.f)
{
other.f = 0;
}

~file_writer() {
if (f) fclose(f);
}

bool is_open() const {
return f != 0;
}

void open(const std::string &_fname) {
fname = _fname;
f = fopen(fname.c_str(), "w");
setbuf(f, NULL);

if (!f) {
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)),
std::string("Failed to open \"") + fname + "\"");
}
}

void put_string(const std::string &v) {
try_write([this, &v](){ return EOF != fputs(v.c_str(), f); });
}

void put_int(int v) {
try_write([this, v](){ return fprintf(f, "%d", v) >= 0; });
}

private:
std::string fname;
FILE *f;

void reopen() {
if (f) fclose(f);
f = fopen(fname.c_str(), "w");
setbuf(f, NULL);
}

template <class Callable>
void try_write(Callable w) {
for(int attempt = 0; attempt < 2; ++attempt) {
fseek(f, 0, SEEK_SET);

if (w()) return;

// Failed to write the value.
// This could mean the sysfs attribute was recreated and the
// corresponding file handle got stale. Lets close the file and try
// again (once):
if (attempt != 0) {
throw std::system_error(std::make_error_code(static_cast<std::errc>(errno)), fname);
}

reopen();
}
}
};

// This class implements a small LRU cache. It assumes the number of elements
// is small, and so uses a simple linear search.
template <typename K, typename V>
Expand Down Expand Up @@ -118,46 +272,24 @@ class lru_cache {
};

// A global cache of files.
std::ifstream& ifstream_cache(const std::string &path) {
static lru_cache<std::string, std::ifstream> cache(FSTREAM_CACHE_SIZE);
file_reader& reader_cache(const std::string &path) {
static lru_cache<std::string, file_reader> cache(FSTREAM_CACHE_SIZE);
static std::mutex mx;

std::lock_guard<std::mutex> lock(mx);
return cache[path];
file_reader &f = cache[path];
if (!f.is_open()) f.open(path);
return f;
}

std::ofstream& ofstream_cache(const std::string &path) {
static lru_cache<std::string, std::ofstream> cache(FSTREAM_CACHE_SIZE);
file_writer& writer_cache(const std::string &path) {
static lru_cache<std::string, file_writer> cache(FSTREAM_CACHE_SIZE);
static std::mutex mx;

std::lock_guard<std::mutex> lock(mx);
return cache[path];
}

//-----------------------------------------------------------------------------
std::ofstream &ofstream_open(const std::string &path) {
std::ofstream &file = ofstream_cache(path);
if (!file.is_open()) {
// Don't buffer writes to avoid latency. Also saves a bit of memory.
file.rdbuf()->pubsetbuf(NULL, 0);
file.open(path);
} else {
// Clear the error bits in case something happened.
file.clear();
}
return file;
}

std::ifstream &ifstream_open(const std::string &path) {
std::ifstream &file = ifstream_cache(path);
if (!file.is_open()) {
file.open(path);
} else {
// Clear the flags bits in case something happened (like reaching EOF).
file.clear();
file.seekg(0, std::ios::beg);
}
return file;
file_writer &f = cache[path];
if (!f.is_open()) f.open(path);
return f;
}

} // namespace
Expand Down Expand Up @@ -243,25 +375,7 @@ int device::get_attr_int(const std::string &name) const {
if (_path.empty())
throw system_error(make_error_code(errc::function_not_supported), "no device connected");

for(int attempt = 0; attempt < 2; ++attempt) {
ifstream &is = ifstream_open(_path + name);
if (is.is_open()) {
int result = 0;
try {
is >> result;
return result;
} catch(...) {
// This could mean the sysfs attribute was recreated and the
// corresponding file handle got stale. Lets close the file and try
// again (once):
if (attempt != 0) throw;

is.close();
is.clear();
}
} else break;
}
throw system_error(make_error_code(errc::no_such_device), _path+name);
return reader_cache(_path + name).get_int();
}

//-----------------------------------------------------------------------------
Expand All @@ -271,23 +385,7 @@ void device::set_attr_int(const std::string &name, int value) {
if (_path.empty())
throw system_error(make_error_code(errc::function_not_supported), "no device connected");

for(int attempt = 0; attempt < 2; ++attempt) {
ofstream &os = ofstream_open(_path + name);
if (os.is_open()) {
if (os << value) return;

// An error could mean that sysfs attribute was recreated and the cached
// file handle is stale. Lets close the file and try again (once):
if (attempt == 0 && errno == ENODEV) {
os.close();
os.clear();
} else {
throw system_error(std::error_code(errno, std::system_category()));
}
} else {
throw system_error(make_error_code(errc::no_such_device), _path + name);
}
}
writer_cache(_path + name).put_int(value);
}

//-----------------------------------------------------------------------------
Expand All @@ -297,14 +395,7 @@ std::string device::get_attr_string(const std::string &name) const {
if (_path.empty())
throw system_error(make_error_code(errc::function_not_supported), "no device connected");

ifstream &is = ifstream_open(_path + name);
if (is.is_open()) {
string result;
is >> result;
return result;
}

throw system_error(make_error_code(errc::no_such_device), _path+name);
return reader_cache(_path + name).get_string();
}

//-----------------------------------------------------------------------------
Expand All @@ -314,13 +405,7 @@ void device::set_attr_string(const std::string &name, const std::string &value)
if (_path.empty())
throw system_error(make_error_code(errc::function_not_supported), "no device connected");

ofstream &os = ofstream_open(_path + name);
if (os.is_open()) {
if (!(os << value)) throw system_error(std::error_code(errno, std::system_category()));
return;
}

throw system_error(make_error_code(errc::no_such_device), _path+name);
writer_cache(_path + name).put_string(value);
}

//-----------------------------------------------------------------------------
Expand All @@ -330,14 +415,7 @@ std::string device::get_attr_line(const std::string &name) const {
if (_path.empty())
throw system_error(make_error_code(errc::function_not_supported), "no device connected");

ifstream &is = ifstream_open(_path + name);
if (is.is_open()) {
string result;
getline(is, result);
return result;
}

throw system_error(make_error_code(errc::no_such_device), _path+name);
return reader_cache(_path + name).get_line();
}

//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -513,14 +591,8 @@ const std::vector<char>& sensor::bin_data() const {
_bin_data.resize(num_values() * value_size);
}

const string fname = _path + "bin_data";
ifstream &is = ifstream_open(fname);
if (is.is_open()) {
is.read(_bin_data.data(), _bin_data.size());
return _bin_data;
}

throw system_error(make_error_code(errc::no_such_device), fname);
reader_cache(_path + "bin_data").get_data(_bin_data.data(), _bin_data.size());
return _bin_data;
}

//-----------------------------------------------------------------------------
Expand Down