include/boost/corosio/stream_file.hpp

100.0% Lines (13 / 13) 100.0% Functions (6 / 6)
stream_file.hpp
f(x) Functions (6)
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Michael Vandeberg
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/corosio
8 //
9
10 #ifndef BOOST_COROSIO_STREAM_FILE_HPP
11 #define BOOST_COROSIO_STREAM_FILE_HPP
12
13 #include <boost/corosio/detail/config.hpp>
14 #include <boost/corosio/detail/platform.hpp>
15 #include <boost/corosio/detail/except.hpp>
16 #include <boost/corosio/detail/native_handle.hpp>
17 #include <boost/corosio/file_base.hpp>
18 #include <boost/corosio/io/io_stream.hpp>
19 #include <boost/capy/ex/execution_context.hpp>
20 #include <boost/capy/concept/executor.hpp>
21 #include <boost/capy/io_result.hpp>
22
23 #include <concepts>
24 #include <cstdint>
25 #include <filesystem>
26 #include <system_error>
27
28 namespace boost::corosio {
29
30 /** Reads and writes a file sequentially, from a coroutine.
31
32 Provides asynchronous read and write operations on a regular
33 file with an implicit position that advances after each
34 operation.
35
36 Inherits from @ref io_stream, so `read_some` and `write_some`
37 are available and work with any algorithm that accepts an
38 `io_stream&`.
39
40 On POSIX platforms, file I/O is dispatched to a thread pool
41 (blocking `preadv`/`pwritev`) with completion posted back to
42 the scheduler. On Windows, true overlapped I/O is used via IOCP.
43
44 @par Thread Safety
45 Distinct objects: Safe.@n
46 Shared objects: Unsafe. Only one asynchronous operation
47 may be in flight at a time.
48
49 @par Example
50 @par !example stream_file
51 */
52 class BOOST_COROSIO_DECL stream_file : public io_stream
53 {
54 public:
55 /** Defines the file operations a platform backend implements.
56
57 Backends derive from this to provide file I/O.
58 `read_some` and `write_some` are inherited from
59 @ref io_stream::implementation.
60 */
61 struct implementation : io_stream::implementation
62 {
63 /// Return the platform file descriptor or handle.
64 virtual native_handle_type native_handle() const noexcept = 0;
65
66 /// Cancel pending asynchronous operations.
67 virtual void cancel() noexcept = 0;
68
69 /** Return the file size in bytes.
70
71 @return The current size of the file, in bytes.
72
73 @throws std::system_error if the underlying size query fails.
74 */
75 virtual std::uint64_t size() const = 0;
76
77 /** Resize the file to @p new_size bytes.
78
79 @param new_size The requested size in bytes.
80
81 @return The error code, empty on success.
82 */
83 virtual std::error_code resize(std::uint64_t new_size) noexcept = 0;
84
85 /** Synchronize file data to stable storage.
86
87 @return The error code, empty on success.
88 */
89 virtual std::error_code sync_data() noexcept = 0;
90
91 /** Synchronize file data and metadata to stable storage.
92
93 @return The error code, empty on success.
94 */
95 virtual std::error_code sync_all() noexcept = 0;
96
97 /** Release ownership of the native handle.
98
99 @return The native handle, which the caller now owns.
100
101 @throws std::system_error if the file is not open.
102 */
103 virtual native_handle_type release() = 0;
104
105 /** Adopt an existing native handle.
106
107 @param handle The native handle to adopt. The implementation takes
108 ownership and closes it.
109
110 @return The error code, empty on success.
111 */
112 virtual std::error_code assign(native_handle_type handle) noexcept = 0;
113
114 /** Move the file position.
115
116 @param offset Signed offset from @p origin.
117 @param origin The reference point for the seek.
118 @return The error code and new absolute position.
119 */
120 virtual capy::io_result<std::uint64_t>
121 seek(std::int64_t offset, file_base::seek_basis origin) noexcept = 0;
122 };
123
124 /** Closes the file if open, cancelling any pending operations.
125 */
126 ~stream_file() override;
127
128 /** Construct from an execution context.
129
130 @param ctx The execution context that owns this file.
131 */
132 explicit stream_file(capy::execution_context& ctx);
133
134 /** Construct from an executor.
135
136 @param ex The executor whose context owns this file.
137 */
138 template<class Ex>
139 requires(!std::same_as<std::remove_cvref_t<Ex>, stream_file>) &&
140 capy::Executor<Ex>
141 2x explicit stream_file(Ex const& ex) : stream_file(ex.context())
142 {
143 2x }
144
145 /** Transfers ownership of the file resources.
146 */
147 2x stream_file(stream_file&& other) noexcept : io_object(std::move(other)) {}
148
149 /** Closes any existing file and transfers ownership.
150
151 @return Reference to this object.
152 */
153 2x stream_file& operator=(stream_file&& other) noexcept
154 {
155 2x if (this != &other)
156 {
157 2x close();
158 2x h_ = std::move(other.h_);
159 }
160 2x return *this;
161 }
162
163 /// Copy construction is disabled; the handle is uniquely owned.
164 stream_file(stream_file const&) = delete;
165 /// Copy assignment is disabled; the handle is uniquely owned.
166 stream_file& operator=(stream_file const&) = delete;
167
168 // read_some() inherited from io_read_stream
169 // write_some() inherited from io_write_stream
170
171 /** Open a file.
172
173 Failures such as a missing file or insufficient permissions
174 are expected runtime conditions and are reported through the
175 returned error code. If the file is already open, it is
176 closed first.
177
178 @param path The filesystem path to open.
179 @param mode Bitmask of @ref file_base::flags specifying
180 access mode and creation behavior.
181
182 @return The error code, empty on success.
183 */
184 [[nodiscard]] std::error_code open(
185 std::filesystem::path const& path,
186 file_base::flags mode = file_base::read_only) noexcept;
187
188 /** Close the file.
189
190 Releases file resources. Pending operations complete through the
191 same path as @ref cancel: one still in flight completes with
192 `errc::operation_canceled`. An operation whose result is already
193 decided reports that result.
194 */
195 void close() noexcept;
196
197 /** Check if the file is open.
198
199 @return `true` if the file is open and ready for I/O.
200 */
201 797x bool is_open() const noexcept
202 {
203 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
204 return h_ && get().native_handle() != ~native_handle_type(0);
205 #else
206 797x return h_ && get().native_handle() >= 0;
207 #endif
208 }
209
210 /** Cancel pending asynchronous operations.
211
212 Operations still in flight complete with
213 `errc::operation_canceled`; an operation whose result is
214 already decided reports that result.
215 */
216 void cancel() noexcept;
217
218 /** Get the native file descriptor or handle.
219
220 @return The native handle, or -1/INVALID_HANDLE_VALUE
221 if not open.
222 */
223 native_handle_type native_handle() const noexcept;
224
225 /** Return the file size in bytes.
226
227 @return The file size in bytes.
228
229 @throws std::system_error If the file is not open, or if the
230 underlying size query fails.
231 */
232 std::uint64_t size() const;
233
234 /** Resize the file to @p new_size bytes.
235
236 Failures such as insufficient disk space are reported
237 through the returned error code. A closed file reports
238 `errc::bad_file_descriptor`.
239
240 @param new_size The new file size.
241
242 @return The error code, empty on success.
243 */
244 [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept;
245
246 /** Synchronize file data to stable storage.
247
248 Write-back failures such as device I/O errors surface here
249 and are reported through the returned error code. A closed
250 file reports `errc::bad_file_descriptor`.
251
252 @return The error code, empty on success.
253 */
254 [[nodiscard]] std::error_code sync_data() noexcept;
255
256 /** Synchronize file data and metadata to stable storage.
257
258 Write-back failures such as device I/O errors surface here
259 and are reported through the returned error code. A closed
260 file reports `errc::bad_file_descriptor`.
261
262 @return The error code, empty on success.
263 */
264 [[nodiscard]] std::error_code sync_all() noexcept;
265
266 /** Release ownership of the native handle.
267
268 The file object becomes not-open. The caller is
269 responsible for closing the returned handle.
270
271 @return The native file descriptor or handle.
272
273 @throws std::system_error `errc::bad_file_descriptor` if the
274 file is not open.
275 */
276 native_handle_type release();
277
278 /** Adopt an existing native handle.
279
280 Closes any currently open file before adopting.
281 The file object takes ownership of the handle. Handles
282 created elsewhere may be unsuitable for asynchronous I/O.
283 Such failures are reported through the returned error code.
284
285 @param handle The native file descriptor or handle.
286
287 @return The error code, empty on success.
288 */
289 [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept;
290
291 /** Move the file position.
292
293 Positions beyond the end of the file are allowed. A
294 resulting negative position is reported through the error
295 code, as offsets often originate from file contents. A
296 closed file reports `errc::bad_file_descriptor`.
297
298 @param offset Signed offset from @p origin.
299 @param origin The reference point for the seek.
300
301 @return The error code and new absolute position.
302 */
303 [[nodiscard]] capy::io_result<std::uint64_t> seek(
304 std::int64_t offset,
305 file_base::seek_basis origin = file_base::seek_set) noexcept;
306
307 protected:
308 /// Default-construct (for derived types that initialize io_object directly).
309 16x stream_file() noexcept = default;
310
311 /** Construct from a pre-built handle (for native_stream_file).
312
313 @param h The pre-built handle to adopt.
314 */
315 explicit stream_file(handle h) noexcept : io_object(std::move(h)) {}
316
317 private:
318 1133x inline implementation& get() const noexcept
319 {
320 1133x return *static_cast<implementation*>(h_.get());
321 }
322 };
323
324 } // namespace boost::corosio
325
326 #endif // BOOST_COROSIO_STREAM_FILE_HPP
327