file.cpp
Go to the documentation of this file.
1 // Copyright (C) 2020-2023 Jonathan Müller and lexy contributors
2 // SPDX-License-Identifier: BSL-1.0
3 
4 #include <lexy/input/file.hpp>
5 
6 #include <cerrno>
7 #include <cstdio>
9 
10 #if defined(__unix__) || defined(__APPLE__)
11 
12 # include <fcntl.h>
13 # include <sys/mman.h>
14 # include <unistd.h>
15 
16 namespace
17 {
18 class raii_fd
19 {
20 public:
21  explicit raii_fd(int file) noexcept : _file(file) {}
22 
23  raii_fd(const raii_fd&) = delete;
24  raii_fd& operator=(const raii_fd&) = delete;
25 
26  ~raii_fd() noexcept
27  {
28  if (_file >= 0)
29  ::close(_file);
30  }
31 
32  operator int() const noexcept
33  {
34  return _file;
35  }
36 
37 private:
38  int _file;
39 };
40 
41 lexy::file_error get_file_error() noexcept
42 {
43  switch (errno)
44  {
45  case ENOENT:
46  case ENOTDIR:
47  case ELOOP:
49 
50  case EACCES:
51  case EPERM:
53 
54  default:
56  }
57 }
58 
59 constexpr std::size_t small_file_size = 4 * 1024;
60 constexpr std::size_t medium_file_size = 32 * 1024;
61 } // namespace
62 
63 lexy::file_error lexy::_detail::read_file(const char* path, file_callback cb, void* user_data)
64 {
65  raii_fd fd(::open(path, O_RDONLY));
66  if (fd < 0)
67  return get_file_error();
68 
69  auto off = ::lseek(fd, 0, SEEK_END);
70  if (off == static_cast<::off_t>(-1))
72  auto size = static_cast<std::size_t>(off);
73 
74  if (size <= small_file_size)
75  {
76  if (::lseek(fd, 0, SEEK_SET) != 0)
78 
79  char buffer[small_file_size]; // Don't initialize.
80  if (::read(fd, buffer, size) != static_cast<::ssize_t>(size))
82 
83  cb(user_data, buffer, size);
84  }
85  else if (size <= medium_file_size)
86  {
87  if (::lseek(fd, 0, SEEK_SET) != 0)
89 
91  if (::read(fd, builder.data(), builder.size()) != static_cast<::ssize_t>(size))
93 
94  cb(user_data, builder.data(), builder.size());
95  }
96  else
97  {
98  auto memory = ::mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0);
99  if (memory == MAP_FAILED) // NOLINT: int-to-ptr conversion happens in header
101 
102  cb(user_data, reinterpret_cast<const char*>(memory), size);
103 
104  ::munmap(memory, size);
105  }
106 
108 }
109 
110 #else // portable read_file() using C I/O
111 
112 namespace
113 {
114 class raii_file
115 {
116 public:
117  explicit raii_file(std::FILE* file) noexcept : _file(file) {}
118 
119  raii_file(const raii_file&) = delete;
120  raii_file& operator=(const raii_file&) = delete;
121 
122  ~raii_file() noexcept
123  {
124  if (_file)
125  std::fclose(_file);
126  }
127 
128  operator std::FILE*() const noexcept
129  {
130  return _file;
131  }
132 
133 private:
134  std::FILE* _file;
135 };
136 
137 lexy::file_error get_file_error() noexcept
138 {
139  switch (errno)
140  {
141  case ENOENT:
142  case ENOTDIR:
143  case ELOOP:
145 
146  case EACCES:
147  case EPERM:
149 
150  default:
152  }
153 }
154 } // namespace
155 
156 lexy::file_error lexy::_detail::read_file(const char* path, file_callback cb, void* user_data)
157 {
158  // Open file.
159  raii_file file(std::fopen(path, "rb"));
160  if (!file)
161  return get_file_error();
162 
163  // Determine correct file size.
164  if (std::fseek(file, 0, SEEK_END) != 0)
166 
167  auto size = std::ftell(file);
168  if (size == -1)
170 
171  if (std::fseek(file, 0, SEEK_SET) != 0)
173 
174  // Now read the entire file into a buffer.
175  lexy::buffer<>::builder builder{std::size_t(size)};
176  if (std::fread(builder.data(), sizeof(char), builder.size(), file) != builder.size())
178  LEXY_ASSERT(std::fgetc(file) == EOF, "we haven't read everything?!");
179 
180  // Pass to callback.
181  // Note that this isn't ideal, as we're having one unnecessary copy plus allocation.
182  cb(user_data, builder.data(), builder.size());
183  return file_error::_success;
184 }
185 
186 #endif
187 
188 // When reading from stdin, performance doesn't really matter.
189 // As such, we use the simple portable way of the C I/O routines.
191 {
192  // We can't use ftell() to get file size
193  // So instead use a conservative loop.
195  while (true)
196  {
197  const auto buffer_size = builder.write_size();
198  LEXY_ASSERT(buffer_size > 0, "buffer empty?!");
199 
200  // Read into the entire write area of the buffer from stdin,
201  // commiting what we've just read.
202  const auto read = std::fread(builder.write_data(), sizeof(char), buffer_size, stdin);
203  builder.commit(read);
204 
205  // Check whether we have exhausted the file.
206  if (read < buffer_size)
207  {
208  if (std::ferror(stdin) != 0)
209  // We have a read error.
211 
212  // We should have reached the end.
213  LEXY_ASSERT(std::feof(stdin), "why did fread() not read enough?");
214  break;
215  }
216 
217  // We've filled the entire buffer and need more space.
218  // This grow might be unnecessary if we're just so happen to reach EOF with the next
219  // input, but checking this requires reading more input.
220  builder.grow();
221  }
222 
223  // Pass final buffer to callback.
224  cb(user_data, builder.read_data(), builder.read_size());
225  return file_error::_success;
226 }
227 
lexy::_detail::read_stdin
file_error read_stdin(file_callback cb, void *user_data)
Definition: file.cpp:190
cx::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: wildcards.hpp:636
wildcards::detail::is_set_state::open
@ open
lexy::file_error::permission_denied
@ permission_denied
The file cannot be opened.
lexy::_detail::buffer_builder::read_data
const T * read_data() const noexcept
Definition: buffer_builder.hpp:51
lexy::_detail::buffer_builder
Definition: buffer_builder.hpp:19
lexy::_detail::buffer_builder::write_data
T * write_data() noexcept
Definition: buffer_builder.hpp:61
file.hpp
buffer_builder.hpp
lexy::file_error::_success
@ _success
lexy::buffer
buffer(const CharT *, const CharT *) -> buffer< deduce_encoding< CharT >>
lexy::file_error
file_error
Errors that might occur while reading the file.
Definition: file.hpp:19
lexy::_detail::buffer_builder::grow
void grow()
Definition: buffer_builder.hpp:86
lexy::_detail::buffer_builder::read_size
std::size_t read_size() const noexcept
Definition: buffer_builder.hpp:55
lexy::file_error::os_error
@ os_error
An internal OS error, such as failure to read from the file.
lexy::_detail::buffer_builder::commit
void commit(std::size_t n) noexcept
Definition: buffer_builder.hpp:78
lexy::buffer::builder
Allows the creation of an uninitialized buffer that is then filled by the user.
Definition: buffer.hpp:93
lexy::_detail::file_callback
void(*)(void *user_data, const char *memory, std::size_t size) file_callback
Definition: file.hpp:33
lexy::_detail::buffer_builder::write_size
std::size_t write_size() const noexcept
Definition: buffer_builder.hpp:65
lexy::file_error::file_not_found
@ file_not_found
The file was not found.
LEXY_ASSERT
#define LEXY_ASSERT(Expr, Msg)
Definition: assert.hpp:37
lexy::_detail::read_file
file_error read_file(const char *path, file_callback cb, void *user_data)
Definition: file.cpp:156


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Jun 28 2024 02:20:07