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


behaviortree_cpp_v4
Author(s): Davide Faconti
autogenerated on Fri Dec 13 2024 03:19:16