include/boost/corosio/stream_file.hpp

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