scnlib  0.2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
file.h
Go to the documentation of this file.
1 // Copyright 2017-2019 Elias Kosunen
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 // This file is a part of scnlib:
16 // https://github.com/eliaskosunen/scnlib
17 
18 #ifndef SCN_DETAIL_FILE_H
19 #define SCN_DETAIL_FILE_H
20 
21 #include "range.h"
22 
23 #include <cstdio>
24 #include <string>
25 
26 namespace scn {
28 
29  namespace detail {
30  struct file_handle {
31 #if SCN_WINDOWS
32  using handle_type = void*;
33 #else
34  using handle_type = int;
35 #endif
36 
37  static constexpr file_handle invalid()
38  {
39 #if SCN_WINDOWS
40  return {nullptr};
41 #else
42  return {-1};
43 #endif
44  }
45 
47  };
48 
50  public:
51  using iterator = const char*;
52  using sentinel = const char*;
53 
54  byte_mapped_file() = default;
55  byte_mapped_file(const char* filename);
56 
57  byte_mapped_file(const byte_mapped_file&) = delete;
59 
61  : m_file(o.m_file), m_begin(o.m_begin), m_end(o.m_end)
62  {
63  o.m_file = file_handle::invalid();
64  o.m_begin = nullptr;
65  o.m_end = nullptr;
66 
67  SCN_ENSURE(!o.valid());
68  SCN_ENSURE(valid());
69  }
71  {
72  if (valid()) {
73  _destruct();
74  }
75  m_file = o.m_file;
76  m_begin = o.m_begin;
77  m_end = o.m_end;
78 
79  o.m_file = file_handle::invalid();
80  o.m_begin = nullptr;
81  o.m_end = nullptr;
82 
83  SCN_ENSURE(!o.valid());
84  SCN_ENSURE(valid());
85  return *this;
86  }
87 
89  {
90  if (valid()) {
91  _destruct();
92  }
93  }
94 
95  bool valid() const
96  {
97  return m_file.handle != file_handle::invalid().handle;
98  }
99 
100  iterator begin() const
101  {
102  return m_begin;
103  }
104  sentinel end() const
105  {
106  return m_end;
107  }
108 
109  private:
110  void _destruct();
111 
113  char* m_begin{nullptr};
114  char* m_end{nullptr};
115  };
116  } // namespace detail
117 
118  template <typename CharT>
120  public:
121  using iterator = const CharT*;
122  using sentinel = const CharT*;
123 
124  using byte_mapped_file::byte_mapped_file;
125 
126  // embrace the UB
127  iterator begin() const
128  {
129  return reinterpret_cast<iterator>(byte_mapped_file::begin());
130  }
131  sentinel end() const
132  {
133  return reinterpret_cast<sentinel>(byte_mapped_file::end());
134  }
135  };
136 
139 
140  template <typename CharT>
141  class basic_file;
142 
143  namespace detail {
144  template <typename CharT>
146  public:
147  using char_type = CharT;
150  using pointer = value_type*;
151  using difference_type = std::ptrdiff_t;
152  using iterator_category = std::input_iterator_tag;
153 
154  cfile_iterator() = default;
155  cfile_iterator(const basic_file<CharT>* f) : m_file(f) {}
156 
157  expected<CharT> operator*() const;
158  const cfile_iterator& operator++() const
159  {
160  return *this;
161  }
162 
163  bool valid() const
164  {
165  return m_file != nullptr;
166  }
167 
168  bool operator==(const cfile_iterator& o) const
169  {
170  return m_file == o.m_file;
171  }
172  bool operator!=(const cfile_iterator& o) const
173  {
174  return !operator==(o);
175  }
176 
177  const basic_file<CharT>& file() const
178  {
179  return *m_file;
180  }
181 
182  private:
183  const basic_file<CharT>* m_file{nullptr};
184  };
185 
186  template <typename CharT>
188  using char_type = CharT;
189  using traits = std::char_traits<CharT>;
190  using int_type = typename traits::int_type;
191 
192  template <typename F>
193  bool sync(F sync_fn)
194  {
195  if (n == 0) {
196  return true;
197  }
198  auto s = span<char_type>(std::addressof(*(buffer.end() - n)),
199  &buffer[0] + buffer.size());
200  if (sync_fn(s)) {
201  buffer.clear();
202  n = 0;
203  return true;
204  }
205  return false;
206  }
207 
208  std::basic_string<char_type> buffer{};
209  std::ptrdiff_t n{0};
210  int_type latest{traits::eof()};
212  };
213 
214  template <typename CharT>
216  public:
217  using char_type = CharT;
220  using traits = std::char_traits<char_type>;
221 
224  using pointer = value_type*;
225  using difference_type =
227  using iterator_category = std::bidirectional_iterator_tag;
228 
229  caching_cfile_iterator() = default;
231  : m_it(std::move(it)), m_cache(std::addressof(c))
232  {
233  }
234 
236  {
237  return m_it;
238  }
240  {
241  return m_cache;
242  }
243 
245  {
246  SCN_EXPECT(m_cache != nullptr);
247  if (m_cache->n > 0) {
248  return {*(m_cache->buffer.end() - m_cache->n)};
249  }
250  if (m_cache->err) {
251  if (m_cache->latest == traits::eof()) {
252  return _read_next();
253  }
254  return traits::to_char_type(m_cache->latest);
255  }
256  return m_cache->err;
257  }
259  {
260  SCN_EXPECT(m_cache != nullptr);
261  if (m_cache->n > 0) {
262  --m_cache->n;
263  }
264  else {
265  _read_next();
266  }
267  return *this;
268  }
270  {
271  SCN_EXPECT(m_cache != nullptr);
272  ++m_cache->n;
273  return *this;
274  }
275 
276  bool operator==(const caching_cfile_iterator& o) const
277  {
278  if (m_it == o.m_it) {
279  if (!m_cache) {
280  return true;
281  }
282  return m_cache->n == o.m_cache->n;
283  }
284  return false;
285  }
286  bool operator!=(const caching_cfile_iterator& o) const
287  {
288  return !operator==(o);
289  }
290 
291  bool operator==(const cfile_iterator<CharT>& o) const
292  {
293  if (!m_cache || m_cache->n == 0) {
294  return m_it == o;
295  }
296  return false;
297  }
298  bool operator!=(const cfile_iterator<CharT>& o) const
299  {
300  return !operator==(o);
301  }
302 
303  private:
304  expected<char_type> _read_next()
305  {
306  SCN_EXPECT(m_cache != nullptr);
307  if (m_cache->err && m_cache->latest != traits::eof()) {
308  m_cache->buffer.push_back(
309  traits::to_char_type(m_cache->latest));
310  }
311  if (!m_cache->err) {
312  return m_cache->err;
313  }
314  ++m_it;
316  auto next = wrap_deref(*m_it);
318  if (next) {
319  m_cache->latest = traits::to_int_type(next.value());
320  }
321  else {
322  m_cache->err = next.error();
323  }
324  return next;
325  }
326 
327  underlying_iterator m_it{};
328  cache_type* m_cache{nullptr};
329  };
330  } // namespace detail
331 
332  template <typename CharT>
334 
335  template <typename CharT>
336  class basic_file {
337  public:
342 
343  basic_file(FILE* f) : m_file(f), m_cache{} {}
344 
345  basic_file(const basic_file&) = delete;
346  basic_file& operator=(const basic_file&) = delete;
347 
348  basic_file(basic_file&&) = default;
349  basic_file& operator=(basic_file&&) = default;
350 
352  {
354  sync();
356  }
357 
358  iterator begin() const noexcept
359  {
360  auto uit = underlying_iterator{this};
361  return {std::move(uit), m_cache};
362  }
363 
364  sentinel end() const noexcept
365  {
366  return {};
367  }
368 
369  FILE* file() const noexcept
370  {
371  return m_file;
372  }
373 
374  cache_type& cache() const noexcept
375  {
376  return m_cache;
377  }
378 
379  bool sync() const;
380 
381  basic_file_view<CharT> make_view() const;
383  {
384  return {make_view()};
385  }
386 
387  private:
388  FILE* m_file;
389  mutable cache_type m_cache;
390  };
391 
392  namespace detail {
393  template <typename CharT>
394  struct is_caching_range_impl<basic_file<CharT>> : std::true_type {
395  };
396  } // namespace detail
397 
400 
401  template <typename CharT>
403  public:
405  using iterator = typename file_type::iterator;
406  using sentinel = typename file_type::sentinel;
407 
408  basic_file_view() = default;
409  basic_file_view(const file_type& f) : m_file(std::addressof(f)) {}
411  : m_file(std::addressof(i.base().file()))
412  {
413  }
414 
415  iterator begin() const noexcept
416  {
417  SCN_EXPECT(*this);
418  return {m_file->begin()};
419  }
420  sentinel end() const noexcept
421  {
422  SCN_EXPECT(*this);
423  return m_file->end();
424  }
425 
426  bool sync() const
427  {
428  SCN_EXPECT(*this);
429  return begin().base().base().file().sync();
430  }
431 
432  FILE* file() const noexcept
433  {
434  SCN_EXPECT(*this);
435  return m_file->file();
436  }
437 
438  const file_type& get() const
439  {
440  SCN_EXPECT(*this);
441  return *m_file;
442  }
443 
444  explicit operator bool() const
445  {
446  return m_file != nullptr;
447  }
448 
449  private:
450  const file_type* m_file{nullptr};
451  };
452 
453  namespace detail {
454  template <typename CharT>
455  struct is_caching_range_impl<basic_file_view<CharT>> : std::true_type {
456  };
457  } // namespace detail
458 
461 
462  template <typename CharT>
464  {
465  return {*this};
466  }
467 
469  SCN_CLANG_IGNORE("-Wexit-time-destructors")
470  template <typename CharT>
472  {
473  static auto f = basic_file<CharT>{stdin};
474  static auto view = basic_file_view<CharT>(f);
475  return view;
476  }
477  inline file_view& cstdin()
478  {
479  return stdin_range<char>();
480  }
481  inline wfile_view& wcstdin()
482  {
483  return stdin_range<wchar_t>();
484  }
486 
488 } // namespace scn
489 
490 #if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY && !defined(SCN_FILE_CPP)
491 #include "file.cpp"
492 #endif
493 
494 #endif // SCN_DETAIL_FILE_H
caching_cfile_iterator(underlying_iterator it, cache_type &c)
Definition: file.h:230
bool operator==(const caching_cfile_iterator &o) const
Definition: file.h:276
FILE * file() const noexcept
Definition: file.h:369
expected< CharT > wrap_deref(CharT ch)
Definition: range.h:268
std::basic_string< char_type > buffer
Definition: file.h:208
typename traits::int_type int_type
Definition: file.h:190
static constexpr file_handle invalid()
Definition: file.h:37
ranges::iter_reference_t< underlying_iterator > reference
Definition: file.h:223
#define SCN_END_NAMESPACE
Definition: config.h:401
const char * sentinel
Definition: file.h:52
decltype(*std::declval< T & >()) iter_reference_t
Definition: ranges.h:65
typename file_type::sentinel sentinel
Definition: file.h:406
expected< CharT > operator*() const
ranges::iter_difference_t< underlying_iterator > difference_type
Definition: file.h:226
bool operator==(const cfile_iterator &o) const
Definition: file.h:168
caching_cfile_iterator & operator++()
Definition: file.h:258
sentinel end() const noexcept
Definition: file.h:420
FILE * file() const noexcept
Definition: file.h:432
ranges::iter_value_t< underlying_iterator > value_type
Definition: file.h:222
std::bidirectional_iterator_tag iterator_category
Definition: file.h:227
typename file_type::iterator iterator
Definition: file.h:405
#define SCN_CLANG_IGNORE(x)
Definition: config.h:144
bool sync() const
Definition: file.h:426
file_view & cstdin()
Definition: file.h:477
typename readable_traits< T >::value_type iter_value_t
Definition: ranges.h:227
cfile_iterator(const basic_file< CharT > *f)
Definition: file.h:155
iterator begin() const noexcept
Definition: file.h:415
bool operator!=(const cfile_iterator &o) const
Definition: file.h:172
expected-like type.
Definition: result.h:180
cfile_iterator< CharT > underlying_iterator
Definition: file.h:218
bool operator!=(const cfile_iterator< CharT > &o) const
Definition: file.h:298
std::char_traits< char_type > traits
Definition: file.h:220
#define SCN_CLANG_POP
Definition: config.h:143
byte_mapped_file(byte_mapped_file &&o) noexcept
Definition: file.h:60
#define SCN_CLANG_POP_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:146
basic_file_view< CharT > make_view() const
Definition: file.h:463
handle_type handle
Definition: file.h:46
underlying_iterator base()
Definition: file.h:235
constexpr error() noexcept=default
cfile_iterator_cache< CharT > cache_type
Definition: file.h:219
iterator begin() const
Definition: file.h:100
std::char_traits< CharT > traits
Definition: file.h:189
sentinel end() const
Definition: file.h:104
Error class.
Definition: result.h:32
bool operator==(const cfile_iterator< CharT > &o) const
Definition: file.h:291
#define SCN_BEGIN_NAMESPACE
Definition: config.h:400
#define SCN_ENSURE(cond)
Definition: config.h:397
basic_file(FILE *f)
Definition: file.h:343
bool valid() const
Definition: file.h:95
basic_file_view(iterator i, sentinel)
Definition: file.h:410
const basic_file< CharT > & file() const
Definition: file.h:177
iterator begin() const
Definition: file.h:127
iterator begin() const noexcept
Definition: file.h:358
sentinel end() const
Definition: file.h:131
detail::range_wrapper< basic_file_view< CharT > > wrap() const
Definition: file.h:382
caching_cfile_iterator & operator--() noexcept
Definition: file.h:269
bool valid() const
Definition: file.h:163
byte_mapped_file & operator=(const byte_mapped_file &)=delete
typename incrementable_traits< T >::difference_type iter_difference_t
Definition: ranges.h:130
byte_mapped_file & operator=(byte_mapped_file &&o) noexcept
Definition: file.h:70
#define SCN_CLANG_PUSH_IGNORE_UNDEFINED_TEMPLATE
Definition: config.h:145
void end(T &&)=delete
sentinel end() const noexcept
Definition: file.h:364
expected< char_type > operator*()
Definition: file.h:244
SCN_CLANG_PUSH basic_file_view< CharT > & stdin_range()
Definition: file.h:471
const cfile_iterator & operator++() const
Definition: file.h:158
const char * iterator
Definition: file.h:51
wfile_view & wcstdin()
Definition: file.h:481
void begin(T &&)=delete
bool operator!=(const caching_cfile_iterator &o) const
Definition: file.h:286
std::input_iterator_tag iterator_category
Definition: file.h:152
std::ptrdiff_t difference_type
Definition: file.h:151
basic_file_view(const file_type &f)
Definition: file.h:409
cache_type & cache() const noexcept
Definition: file.h:374
#define SCN_EXPECT(cond)
Definition: config.h:396
#define SCN_CLANG_PUSH
Definition: config.h:142
A view over a contiguous range.
Definition: span.h:29