GCC Code Coverage Report


Directory: ./
File: libs/capy/src/detail/file_posix.cpp
Date: 2025-12-22 08:41:20
Exec Total Coverage
Lines: 126 151 83.4%
Functions: 12 12 100.0%
Branches: 40 55 72.7%

Line Branch Exec Source
1 //
2 // Copyright (c) 2022 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/capy
8 //
9
10 #include <boost/capy/detail/file_posix.hpp>
11
12 #if BOOST_CAPY_USE_POSIX_FILE
13
14 #include <boost/core/exchange.hpp>
15 #include <limits>
16 #include <fcntl.h>
17 #include <sys/types.h>
18 #include <sys/uio.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <limits.h>
22
23 #if ! defined(BOOST_CAPY_NO_POSIX_FADVISE)
24 # if defined(__APPLE__) || (defined(__ANDROID__) && (__ANDROID_API__ < 21))
25 # define BOOST_CAPY_NO_POSIX_FADVISE
26 # endif
27 #endif
28
29 #if ! defined(BOOST_CAPY_USE_POSIX_FADVISE)
30 # if ! defined(BOOST_CAPY_NO_POSIX_FADVISE)
31 # define BOOST_CAPY_USE_POSIX_FADVISE 1
32 # else
33 # define BOOST_CAPY_USE_POSIX_FADVISE 0
34 # endif
35 #endif
36
37 namespace boost {
38 namespace capy {
39 namespace detail {
40
41 int
42 106 file_posix::
43 native_close(native_handle_type& fd)
44 {
45 /* https://github.com/boostorg/beast/issues/1445
46
47 This function is tuned for Linux / Mac OS:
48
49 * only calls close() once
50 * returns the error directly to the caller
51 * does not loop on EINTR
52
53 If this is incorrect for the platform, then the
54 caller will need to implement their own type
55 meeting the File requirements and use the correct
56 behavior.
57
58 See:
59 http://man7.org/linux/man-pages/man2/close.2.html
60 */
61 106 int ev = 0;
62
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 70 times.
106 if(fd != -1)
63 {
64
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
36 if(::close(fd) != 0)
65 ev = errno;
66 36 fd = -1;
67 }
68 106 return ev;
69 }
70
71 48 file_posix::
72 ~file_posix()
73 {
74 48 native_close(fd_);
75 48 }
76
77 2 file_posix::
78 file_posix(
79 2 file_posix&& other) noexcept
80 2 : fd_(boost::exchange(other.fd_, -1))
81 {
82 2 }
83
84 file_posix&
85 6 file_posix::
86 operator=(
87 file_posix&& other) noexcept
88 {
89
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if(&other == this)
90 2 return *this;
91 4 native_close(fd_);
92 4 fd_ = other.fd_;
93 4 other.fd_ = -1;
94 4 return *this;
95 }
96
97 void
98 2 file_posix::
99 native_handle(native_handle_type fd)
100 {
101 2 native_close(fd_);
102 2 fd_ = fd;
103 2 }
104
105 void
106 8 file_posix::
107 close(
108 system::error_code& ec)
109 {
110 8 auto const ev = native_close(fd_);
111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(ev)
112 ec.assign(ev,
113 system::system_category());
114 else
115 8 ec = {};
116 8 }
117
118 void
119 44 file_posix::
120 open(char const* path, file_mode mode, system::error_code& ec)
121 {
122 44 auto const ev = native_close(fd_);
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if(ev)
124 ec.assign(ev,
125 system::system_category());
126 else
127 44 ec = {};
128
129 44 int f = 0;
130 #if BOOST_CAPY_USE_POSIX_FADVISE
131 44 int advise = 0;
132 #endif
133
7/7
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 4 times.
44 switch(mode)
134 {
135 4 default:
136 case file_mode::read:
137 4 f = O_RDONLY;
138 #if BOOST_CAPY_USE_POSIX_FADVISE
139 4 advise = POSIX_FADV_RANDOM;
140 #endif
141 4 break;
142 4 case file_mode::scan:
143 4 f = O_RDONLY;
144 #if BOOST_CAPY_USE_POSIX_FADVISE
145 4 advise = POSIX_FADV_SEQUENTIAL;
146 #endif
147 4 break;
148
149 20 case file_mode::write:
150 20 f = O_RDWR | O_CREAT | O_TRUNC;
151 #if BOOST_CAPY_USE_POSIX_FADVISE
152 20 advise = POSIX_FADV_RANDOM;
153 #endif
154 20 break;
155
156 4 case file_mode::write_new:
157 4 f = O_RDWR | O_CREAT | O_EXCL;
158 #if BOOST_CAPY_USE_POSIX_FADVISE
159 4 advise = POSIX_FADV_RANDOM;
160 #endif
161 4 break;
162
163 4 case file_mode::write_existing:
164 4 f = O_RDWR | O_EXCL;
165 #if BOOST_CAPY_USE_POSIX_FADVISE
166 4 advise = POSIX_FADV_RANDOM;
167 #endif
168 4 break;
169
170 4 case file_mode::append:
171 4 f = O_WRONLY | O_CREAT | O_APPEND;
172 #if BOOST_CAPY_USE_POSIX_FADVISE
173 4 advise = POSIX_FADV_SEQUENTIAL;
174 #endif
175 4 break;
176
177 4 case file_mode::append_existing:
178 4 f = O_WRONLY | O_APPEND;
179 #if BOOST_CAPY_USE_POSIX_FADVISE
180 4 advise = POSIX_FADV_SEQUENTIAL;
181 #endif
182 4 break;
183 }
184 for(;;)
185 {
186 44 fd_ = ::open(path, f, 0644);
187
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8 times.
44 if(fd_ != -1)
188 36 break;
189 8 auto const ev = errno;
190
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if(ev != EINTR)
191 {
192 8 ec.assign(ev,
193 system::system_category());
194 8 return;
195 }
196 }
197 #if BOOST_CAPY_USE_POSIX_FADVISE
198
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 36 times.
36 if(::posix_fadvise(fd_, 0, 0, advise))
199 {
200 auto const ev = errno;
201 native_close(fd_);
202 ec.assign(ev,
203 system::system_category());
204 return;
205 }
206 #endif
207 36 ec = {};
208 }
209
210 std::uint64_t
211 5 file_posix::
212 size(
213 system::error_code& ec) const
214 {
215
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if(fd_ == -1)
216 {
217 3 ec = make_error_code(
218 system::errc::bad_file_descriptor);
219 3 return 0;
220 }
221 struct stat st;
222
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if(::fstat(fd_, &st) != 0)
223 {
224 ec.assign(errno,
225 system::system_category());
226 return 0;
227 }
228 2 ec = {};
229 2 return st.st_size;
230 }
231
232 std::uint64_t
233 7 file_posix::
234 pos(
235 system::error_code& ec) const
236 {
237
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if(fd_ == -1)
238 {
239 3 ec = make_error_code(
240 system::errc::bad_file_descriptor);
241 3 return 0;
242 }
243 4 auto const result = ::lseek(fd_, 0, SEEK_CUR);
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(result == (::off_t)-1)
245 {
246 ec.assign(errno,
247 system::system_category());
248 return 0;
249 }
250 4 ec = {};
251 4 return result;
252 }
253
254 void
255 5 file_posix::
256 seek(std::uint64_t offset,
257 system::error_code& ec)
258 {
259
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if(fd_ == -1)
260 {
261 3 ec = make_error_code(
262 system::errc::bad_file_descriptor);
263 3 return;
264 }
265 2 auto const result = ::lseek(fd_, offset, SEEK_SET);
266
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if(result == static_cast<::off_t>(-1))
267 {
268 ec.assign(errno,
269 system::system_category());
270 return;
271 }
272 2 ec = {};
273 }
274
275 std::size_t
276 7 file_posix::
277 read(void* buffer, std::size_t n,
278 system::error_code& ec)
279 {
280
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 if(fd_ == -1)
281 {
282 3 ec = make_error_code(
283 system::errc::bad_file_descriptor);
284 3 return 0;
285 }
286 4 std::size_t nread = 0;
287
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 while(n > 0)
288 {
289 // <limits> not required to define SSIZE_MAX so we avoid it
290 4 constexpr auto ssmax =
291 static_cast<std::size_t>((std::numeric_limits<
292 decltype(::read(fd_, buffer, n))>::max)());
293 4 auto const amount = (std::min)(
294 4 n, ssmax);
295
1/1
✓ Branch 1 taken 4 times.
4 auto const result = ::read(fd_, buffer, amount);
296
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(result == -1)
297 {
298 auto const ev = errno;
299 if(ev == EINTR)
300 continue;
301 ec.assign(ev,
302 system::system_category());
303 return nread;
304 }
305
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(result == 0)
306 {
307 // short read
308 return nread;
309 }
310 4 n -= result;
311 4 nread += result;
312 4 buffer = static_cast<char*>(buffer) + result;
313 }
314 4 return nread;
315 }
316
317 std::size_t
318 11 file_posix::
319 write(void const* buffer, std::size_t n,
320 system::error_code& ec)
321 {
322
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 8 times.
11 if(fd_ == -1)
323 {
324 3 ec = make_error_code(
325 system::errc::bad_file_descriptor);
326 3 return 0;
327 }
328 8 std::size_t nwritten = 0;
329
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 while(n > 0)
330 {
331 // <limits> not required to define SSIZE_MAX so we avoid it
332 8 constexpr auto ssmax =
333 static_cast<std::size_t>((std::numeric_limits<
334 decltype(::write(fd_, buffer, n))>::max)());
335 8 auto const amount = (std::min)(
336 8 n, ssmax);
337
1/1
✓ Branch 1 taken 8 times.
8 auto const result = ::write(fd_, buffer, amount);
338
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if(result == -1)
339 {
340 auto const ev = errno;
341 if(ev == EINTR)
342 continue;
343 ec.assign(ev,
344 system::system_category());
345 return nwritten;
346 }
347 8 n -= result;
348 8 nwritten += result;
349 8 buffer = static_cast<char const*>(buffer) + result;
350 }
351 8 return nwritten;
352 }
353
354 } // detail
355 } // capy
356 } // boost
357
358 #endif
359