include/boost/corosio/stream_file.hpp

100.0% Lines (12/12) 100.0% List of functions (5/5)
stream_file.hpp
f(x) Functions (5)
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 1x explicit stream_file(Ex const& ex) : stream_file(ex.context())
125 {
126 1x }
127
128 /** Move constructor.
129
130 Transfers ownership of the file resources.
131 */
132 1x 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 1x stream_file& operator=(stream_file&& other) noexcept
139 {
140 1x if (this != &other)
141 {
142 1x close();
143 1x h_ = std::move(other.h_);
144 }
145 1x 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 /** Close the file.
167
168 Releases file resources. Any pending operations complete
169 with `errc::operation_canceled`.
170 */
171 void close();
172
173 /** Check if the file is open.
174
175 @return `true` if the file is open and ready for I/O.
176 */
177 83x bool is_open() const noexcept
178 {
179 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
180 return h_ && get().native_handle() != ~native_handle_type(0);
181 #else
182 83x return h_ && get().native_handle() >= 0;
183 #endif
184 }
185
186 /** Cancel pending asynchronous operations.
187
188 All outstanding operations complete with
189 `errc::operation_canceled`.
190 */
191 void cancel();
192
193 /** Get the native file descriptor or handle.
194
195 @return The native handle, or -1/INVALID_HANDLE_VALUE
196 if not open.
197 */
198 native_handle_type native_handle() const noexcept;
199
200 /** Return the file size in bytes.
201
202 @throws std::system_error on failure.
203 */
204 std::uint64_t size() const;
205
206 /** Resize the file to @p new_size bytes.
207
208 @param new_size The new file size.
209 @throws std::system_error on failure.
210 */
211 void resize(std::uint64_t new_size);
212
213 /** Synchronize file data to stable storage.
214
215 @throws std::system_error on failure.
216 */
217 void sync_data();
218
219 /** Synchronize file data and metadata to stable storage.
220
221 @throws std::system_error on failure.
222 */
223 void sync_all();
224
225 /** Release ownership of the native handle.
226
227 The file object becomes not-open. The caller is
228 responsible for closing the returned handle.
229
230 @return The native file descriptor or handle.
231 */
232 native_handle_type release();
233
234 /** Adopt an existing native handle.
235
236 Closes any currently open file before adopting.
237 The file object takes ownership of the handle.
238
239 @param handle The native file descriptor or handle.
240 @throws std::system_error on failure.
241 */
242 void assign(native_handle_type handle);
243
244 /** Move the file position.
245
246 @param offset Signed offset from @p origin.
247 @param origin The reference point for the seek.
248 @return The new absolute position.
249 @throws std::system_error on failure.
250 */
251 std::uint64_t
252 seek(std::int64_t offset,
253 file_base::seek_basis origin = file_base::seek_set);
254
255 private:
256 116x inline implementation& get() const noexcept
257 {
258 116x return *static_cast<implementation*>(h_.get());
259 }
260 };
261
262 } // namespace boost::corosio
263
264 #endif // BOOST_COROSIO_STREAM_FILE_HPP
265