2727
2828#include < iostream>
2929#include < sstream>
30- #include < fstream>
3130#include < list>
3231#include < map>
3332#include < array>
3837#include < thread>
3938#include < stdexcept>
4039#include < string.h>
41- #include < math.h>
40+ #include < cmath>
41+ #include < cstdio>
4242
4343#include < dirent.h>
4444#include < sys/mman.h>
@@ -66,6 +66,154 @@ static const int bits_per_long = sizeof(long) * 8;
6666namespace ev3dev {
6767namespace {
6868
69+ // -----------------------------------------------------------------------------
70+ class file_reader {
71+ public:
72+ file_reader () : f(0 ) {}
73+
74+ file_reader (file_reader &&other)
75+ : fname(std::move(other.fname)), f(other.f)
76+ {
77+ other.f = 0 ;
78+ }
79+
80+ ~file_reader () {
81+ if (f) fclose (f);
82+ }
83+
84+ bool is_open () const {
85+ return f != 0 ;
86+ }
87+
88+ void open (const std::string &_fname) {
89+ fname = _fname;
90+ f = fopen (fname.c_str (), " r" );
91+
92+ if (!f) {
93+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)),
94+ std::string (" Failed to open \" " ) + fname + " \" " );
95+ }
96+ }
97+
98+ std::string get_string () {
99+ char buf[256 ];
100+ try_read ([this , &buf](){ return 1 == fscanf (f, " %255s" , buf); });
101+ return buf;
102+ }
103+
104+ std::string get_line () {
105+ char buf[256 ];
106+ try_read ([this , &buf](){
107+ if (!fgets (buf, 255 , f)) return false ;
108+ char *pos;
109+ if ((pos=strchr (buf, ' \n ' )) != NULL ) *pos = ' \0 ' ;
110+ return true ;
111+ });
112+ return buf;
113+ }
114+
115+ int get_int () {
116+ int v;
117+ try_read ([this , &v](){return 1 == fscanf (f, " %d" , &v); });
118+ return v;
119+ }
120+
121+ void get_data (char *data, size_t count) {
122+ try_read ([this , &data, count](){ return count == fread (data, count, count, f); });
123+ }
124+ private:
125+ std::string fname;
126+ FILE *f;
127+
128+ void reopen () {
129+ if (f) fclose (f);
130+ f = fopen (fname.c_str (), " r" );
131+ }
132+
133+ template <class Callable >
134+ void try_read (Callable w) {
135+ for (int attempt = 0 ; attempt < 2 ; ++attempt) {
136+ fseek (f, 0 , SEEK_SET);
137+
138+ if (w ()) return ;
139+
140+ // Failed to read the value.
141+ // This could mean the sysfs attribute was recreated and the
142+ // corresponding file handle got stale. Lets close the file and try
143+ // again (once):
144+ if (attempt != 0 ) {
145+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)), fname);
146+ }
147+
148+ reopen ();
149+ }
150+ }
151+ };
152+
153+ // -----------------------------------------------------------------------------
154+ class file_writer {
155+ public:
156+ file_writer () : f(0 ) {}
157+
158+ file_writer (file_writer &&other)
159+ : fname(std::move(other.fname)), f(other.f)
160+ {
161+ other.f = 0 ;
162+ }
163+
164+ ~file_writer () {
165+ if (f) fclose (f);
166+ }
167+
168+ bool is_open () const {
169+ return f != 0 ;
170+ }
171+
172+ void open (const std::string &_fname) {
173+ fname = _fname;
174+ f = fopen (fname.c_str (), " w" );
175+
176+ if (!f) {
177+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)),
178+ std::string (" Failed to open \" " ) + fname + " \" " );
179+ }
180+ }
181+
182+ void put_string (const std::string &v) {
183+ try_write ([this , &v](){ return EOF != fputs (v.c_str (), f); });
184+ }
185+
186+ void put_int (int v) {
187+ try_write ([this , v](){ return fprintf (f, " %d" , v) >= 0 ; });
188+ }
189+
190+ private:
191+ std::string fname;
192+ FILE *f;
193+
194+ void reopen () {
195+ if (f) fclose (f);
196+ f = fopen (fname.c_str (), " w" );
197+ }
198+
199+ template <class Callable >
200+ void try_write (Callable w) {
201+ for (int attempt = 0 ; attempt < 2 ; ++attempt) {
202+ if (w ()) return ;
203+
204+ // Failed to write the value.
205+ // This could mean the sysfs attribute was recreated and the
206+ // corresponding file handle got stale. Lets close the file and try
207+ // again (once):
208+ if (attempt != 0 ) {
209+ throw std::system_error (std::make_error_code (static_cast <std::errc>(errno)), fname);
210+ }
211+
212+ reopen ();
213+ }
214+ }
215+ };
216+
69217// This class implements a small LRU cache. It assumes the number of elements
70218// is small, and so uses a simple linear search.
71219template <typename K, typename V>
@@ -118,46 +266,24 @@ class lru_cache {
118266};
119267
120268// A global cache of files.
121- std::ifstream& ifstream_cache (const std::string &path) {
122- static lru_cache<std::string, std::ifstream > cache (FSTREAM_CACHE_SIZE);
269+ file_reader& reader_cache (const std::string &path) {
270+ static lru_cache<std::string, file_reader > cache (FSTREAM_CACHE_SIZE);
123271 static std::mutex mx;
124272
125273 std::lock_guard<std::mutex> lock (mx);
126- return cache[path];
274+ file_reader &f = cache[path];
275+ if (!f.is_open ()) f.open (path);
276+ return f;
127277}
128278
129- std::ofstream& ofstream_cache (const std::string &path) {
130- static lru_cache<std::string, std::ofstream > cache (FSTREAM_CACHE_SIZE);
279+ file_writer& writer_cache (const std::string &path) {
280+ static lru_cache<std::string, file_writer > cache (FSTREAM_CACHE_SIZE);
131281 static std::mutex mx;
132282
133283 std::lock_guard<std::mutex> lock (mx);
134- return cache[path];
135- }
136-
137- // -----------------------------------------------------------------------------
138- std::ofstream &ofstream_open (const std::string &path) {
139- std::ofstream &file = ofstream_cache (path);
140- if (!file.is_open ()) {
141- // Don't buffer writes to avoid latency. Also saves a bit of memory.
142- file.rdbuf ()->pubsetbuf (NULL , 0 );
143- file.open (path);
144- } else {
145- // Clear the error bits in case something happened.
146- file.clear ();
147- }
148- return file;
149- }
150-
151- std::ifstream &ifstream_open (const std::string &path) {
152- std::ifstream &file = ifstream_cache (path);
153- if (!file.is_open ()) {
154- file.open (path);
155- } else {
156- // Clear the flags bits in case something happened (like reaching EOF).
157- file.clear ();
158- file.seekg (0 , std::ios::beg);
159- }
160- return file;
284+ file_writer &f = cache[path];
285+ if (!f.is_open ()) f.open (path);
286+ return f;
161287}
162288
163289} // namespace
@@ -243,25 +369,7 @@ int device::get_attr_int(const std::string &name) const {
243369 if (_path.empty ())
244370 throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
245371
246- for (int attempt = 0 ; attempt < 2 ; ++attempt) {
247- ifstream &is = ifstream_open (_path + name);
248- if (is.is_open ()) {
249- int result = 0 ;
250- try {
251- is >> result;
252- return result;
253- } catch (...) {
254- // This could mean the sysfs attribute was recreated and the
255- // corresponding file handle got stale. Lets close the file and try
256- // again (once):
257- if (attempt != 0 ) throw ;
258-
259- is.close ();
260- is.clear ();
261- }
262- } else break ;
263- }
264- throw system_error (make_error_code (errc::no_such_device), _path+name);
372+ return reader_cache (_path + name).get_int ();
265373}
266374
267375// -----------------------------------------------------------------------------
@@ -271,23 +379,7 @@ void device::set_attr_int(const std::string &name, int value) {
271379 if (_path.empty ())
272380 throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
273381
274- for (int attempt = 0 ; attempt < 2 ; ++attempt) {
275- ofstream &os = ofstream_open (_path + name);
276- if (os.is_open ()) {
277- if (os << value) return ;
278-
279- // An error could mean that sysfs attribute was recreated and the cached
280- // file handle is stale. Lets close the file and try again (once):
281- if (attempt == 0 && errno == ENODEV) {
282- os.close ();
283- os.clear ();
284- } else {
285- throw system_error (std::error_code (errno, std::system_category ()));
286- }
287- } else {
288- throw system_error (make_error_code (errc::no_such_device), _path + name);
289- }
290- }
382+ writer_cache (_path + name).put_int (value);
291383}
292384
293385// -----------------------------------------------------------------------------
@@ -297,14 +389,7 @@ std::string device::get_attr_string(const std::string &name) const {
297389 if (_path.empty ())
298390 throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
299391
300- ifstream &is = ifstream_open (_path + name);
301- if (is.is_open ()) {
302- string result;
303- is >> result;
304- return result;
305- }
306-
307- throw system_error (make_error_code (errc::no_such_device), _path+name);
392+ return reader_cache (_path + name).get_string ();
308393}
309394
310395// -----------------------------------------------------------------------------
@@ -314,13 +399,7 @@ void device::set_attr_string(const std::string &name, const std::string &value)
314399 if (_path.empty ())
315400 throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
316401
317- ofstream &os = ofstream_open (_path + name);
318- if (os.is_open ()) {
319- if (!(os << value)) throw system_error (std::error_code (errno, std::system_category ()));
320- return ;
321- }
322-
323- throw system_error (make_error_code (errc::no_such_device), _path+name);
402+ writer_cache (_path + name).put_string (value);
324403}
325404
326405// -----------------------------------------------------------------------------
@@ -330,14 +409,7 @@ std::string device::get_attr_line(const std::string &name) const {
330409 if (_path.empty ())
331410 throw system_error (make_error_code (errc::function_not_supported), " no device connected" );
332411
333- ifstream &is = ifstream_open (_path + name);
334- if (is.is_open ()) {
335- string result;
336- getline (is, result);
337- return result;
338- }
339-
340- throw system_error (make_error_code (errc::no_such_device), _path+name);
412+ return reader_cache (_path + name).get_line ();
341413}
342414
343415// -----------------------------------------------------------------------------
@@ -513,14 +585,8 @@ const std::vector<char>& sensor::bin_data() const {
513585 _bin_data.resize (num_values () * value_size);
514586 }
515587
516- const string fname = _path + " bin_data" ;
517- ifstream &is = ifstream_open (fname);
518- if (is.is_open ()) {
519- is.read (_bin_data.data (), _bin_data.size ());
520- return _bin_data;
521- }
522-
523- throw system_error (make_error_code (errc::no_such_device), fname);
588+ reader_cache (_path + " bin_data" ).get_data (_bin_data.data (), _bin_data.size ());
589+ return _bin_data;
524590}
525591
526592// -----------------------------------------------------------------------------
0 commit comments