include/boost/corosio/random_access_file.hpp

95.3% Lines (41/43) 100.0% List of functions (14/14)
random_access_file.hpp
f(x) Functions (14)
Function Calls Lines Blocks
boost::corosio::random_access_file::read_some_at_awaitable<boost::capy::mutable_buffer>::read_some_at_awaitable(boost::corosio::random_access_file&, unsigned long, boost::capy::mutable_buffer) :150 116x 100.0% 100.0% boost::corosio::random_access_file::read_some_at_awaitable<boost::capy::mutable_buffer>::await_ready() const :161 116x 100.0% 100.0% boost::corosio::random_access_file::read_some_at_awaitable<boost::capy::mutable_buffer>::await_resume() const :166 116x 100.0% 100.0% boost::corosio::random_access_file::read_some_at_awaitable<boost::capy::mutable_buffer>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :171 116x 100.0% 77.0% boost::corosio::random_access_file::write_some_at_awaitable<boost::capy::const_buffer>::write_some_at_awaitable(boost::corosio::random_access_file&, unsigned long, boost::capy::const_buffer) :191 10x 100.0% 100.0% boost::corosio::random_access_file::write_some_at_awaitable<boost::capy::const_buffer>::await_ready() const :202 10x 100.0% 100.0% boost::corosio::random_access_file::write_some_at_awaitable<boost::capy::const_buffer>::await_resume() const :207 10x 100.0% 100.0% boost::corosio::random_access_file::write_some_at_awaitable<boost::capy::const_buffer>::await_suspend(std::__n4861::coroutine_handle<void>, boost::capy::io_env const*) :212 10x 100.0% 77.0% boost::corosio::random_access_file::random_access_file<boost::corosio::io_context::executor_type>(boost::corosio::io_context::executor_type const&) :241 1x 100.0% 100.0% boost::corosio::random_access_file::random_access_file(boost::corosio::random_access_file&&) :246 1x 100.0% 100.0% boost::corosio::random_access_file::is_open() const :285 195x 100.0% 100.0% auto boost::corosio::random_access_file::read_some_at<boost::capy::mutable_buffer>(unsigned long, boost::capy::mutable_buffer const&) :304 116x 75.0% 80.0% auto boost::corosio::random_access_file::write_some_at<boost::capy::const_buffer>(unsigned long, boost::capy::const_buffer const&) :321 10x 75.0% 80.0% boost::corosio::random_access_file::get() const :365 348x 100.0% 100.0%
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_RANDOM_ACCESS_FILE_HPP
11 #define BOOST_COROSIO_RANDOM_ACCESS_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/detail/buffer_param.hpp>
18 #include <boost/corosio/file_base.hpp>
19 #include <boost/corosio/io/io_object.hpp>
20 #include <boost/capy/io_result.hpp>
21 #include <boost/capy/ex/executor_ref.hpp>
22 #include <boost/capy/ex/execution_context.hpp>
23 #include <boost/capy/ex/io_env.hpp>
24 #include <boost/capy/concept/executor.hpp>
25 #include <boost/capy/buffers.hpp>
26
27 #include <concepts>
28 #include <coroutine>
29 #include <cstddef>
30 #include <cstdint>
31 #include <type_traits>
32 #include <filesystem>
33 #include <stop_token>
34 #include <system_error>
35
36 namespace boost::corosio {
37
38 /** An asynchronous random-access file for coroutine I/O.
39
40 Provides asynchronous read and write operations at explicit
41 byte offsets, without maintaining an implicit file position.
42
43 On POSIX platforms, file I/O is dispatched to a thread pool
44 (blocking `preadv`/`pwritev`) with completion posted back to
45 the scheduler. On Windows, true overlapped I/O is used via IOCP.
46
47 @par Thread Safety
48 Distinct objects: Safe.@n
49 Shared objects: Unsafe. Multiple concurrent reads and writes
50 are supported from coroutines sharing the same file object,
51 but external synchronization is required for non-async
52 operations (open, close, size, resize, etc.).
53
54 @par Example
55 @code
56 io_context ioc;
57 random_access_file f(ioc);
58 f.open("data.bin", file_base::read_only);
59
60 char buf[4096];
61 auto [ec, n] = co_await f.read_some_at(
62 0, capy::mutable_buffer(buf, sizeof(buf)));
63 @endcode
64 */
65 class BOOST_COROSIO_DECL random_access_file : public io_object
66 {
67 public:
68 /** Platform-specific random-access file implementation interface.
69
70 Backends derive from this to provide offset-based file I/O.
71 */
72 struct implementation : io_object::implementation
73 {
74 /** Initiate a read at the given offset.
75
76 @param offset Byte offset into the file.
77 @param h Coroutine handle to resume on completion.
78 @param ex Executor for dispatching the completion.
79 @param buf The buffer to read into.
80 @param token Stop token for cancellation.
81 @param ec Output error code.
82 @param bytes_out Output bytes transferred.
83 @return Coroutine handle to resume immediately.
84 */
85 virtual std::coroutine_handle<> read_some_at(
86 std::uint64_t offset,
87 std::coroutine_handle<> h,
88 capy::executor_ref ex,
89 buffer_param buf,
90 std::stop_token token,
91 std::error_code* ec,
92 std::size_t* bytes_out) = 0;
93
94 /** Initiate a write at the given offset.
95
96 @param offset Byte offset into the file.
97 @param h Coroutine handle to resume on completion.
98 @param ex Executor for dispatching the completion.
99 @param buf The buffer to write from.
100 @param token Stop token for cancellation.
101 @param ec Output error code.
102 @param bytes_out Output bytes transferred.
103 @return Coroutine handle to resume immediately.
104 */
105 virtual std::coroutine_handle<> write_some_at(
106 std::uint64_t offset,
107 std::coroutine_handle<> h,
108 capy::executor_ref ex,
109 buffer_param buf,
110 std::stop_token token,
111 std::error_code* ec,
112 std::size_t* bytes_out) = 0;
113
114 /// Return the platform file descriptor or handle.
115 virtual native_handle_type native_handle() const noexcept = 0;
116
117 /// Cancel pending asynchronous operations.
118 virtual void cancel() noexcept = 0;
119
120 /// Return the file size in bytes.
121 virtual std::uint64_t size() const = 0;
122
123 /// Resize the file to @p new_size bytes.
124 virtual void resize(std::uint64_t new_size) = 0;
125
126 /// Synchronize file data to stable storage.
127 virtual void sync_data() = 0;
128
129 /// Synchronize file data and metadata to stable storage.
130 virtual void sync_all() = 0;
131
132 /// Release ownership of the native handle.
133 virtual native_handle_type release() = 0;
134
135 /// Adopt an existing native handle.
136 virtual void assign(native_handle_type handle) = 0;
137 };
138
139 /** Awaitable for async read-at operations. */
140 template<class MutableBufferSequence>
141 struct read_some_at_awaitable
142 {
143 random_access_file& f_;
144 std::uint64_t offset_;
145 MutableBufferSequence buffers_;
146 std::stop_token token_;
147 mutable std::error_code ec_;
148 mutable std::size_t bytes_ = 0;
149
150 116x read_some_at_awaitable(
151 random_access_file& f,
152 std::uint64_t offset,
153 MutableBufferSequence buffers)
154 noexcept(std::is_nothrow_move_constructible_v<MutableBufferSequence>)
155 116x : f_(f)
156 116x , offset_(offset)
157 116x , buffers_(std::move(buffers))
158 {
159 116x }
160
161 116x bool await_ready() const noexcept
162 {
163 116x return false;
164 }
165
166 116x capy::io_result<std::size_t> await_resume() const noexcept
167 {
168 116x return {ec_, bytes_};
169 }
170
171 116x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
172 -> std::coroutine_handle<>
173 {
174 116x token_ = env->stop_token;
175 348x return f_.get().read_some_at(
176 348x offset_, h, env->executor, buffers_, token_, &ec_, &bytes_);
177 }
178 };
179
180 /** Awaitable for async write-at operations. */
181 template<class ConstBufferSequence>
182 struct write_some_at_awaitable
183 {
184 random_access_file& f_;
185 std::uint64_t offset_;
186 ConstBufferSequence buffers_;
187 std::stop_token token_;
188 mutable std::error_code ec_;
189 mutable std::size_t bytes_ = 0;
190
191 10x write_some_at_awaitable(
192 random_access_file& f,
193 std::uint64_t offset,
194 ConstBufferSequence buffers)
195 noexcept(std::is_nothrow_move_constructible_v<ConstBufferSequence>)
196 10x : f_(f)
197 10x , offset_(offset)
198 10x , buffers_(std::move(buffers))
199 {
200 10x }
201
202 10x bool await_ready() const noexcept
203 {
204 10x return false;
205 }
206
207 10x capy::io_result<std::size_t> await_resume() const noexcept
208 {
209 10x return {ec_, bytes_};
210 }
211
212 10x auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
213 -> std::coroutine_handle<>
214 {
215 10x token_ = env->stop_token;
216 30x return f_.get().write_some_at(
217 30x offset_, h, env->executor, buffers_, token_, &ec_, &bytes_);
218 }
219 };
220
221 public:
222 /** Destructor.
223
224 Closes the file if open, cancelling any pending operations.
225 */
226 ~random_access_file() override;
227
228 /** Construct from an execution context.
229
230 @param ctx The execution context that will own this file.
231 */
232 explicit random_access_file(capy::execution_context& ctx);
233
234 /** Construct from an executor.
235
236 @param ex The executor whose context will own this file.
237 */
238 template<class Ex>
239 requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) &&
240 capy::Executor<Ex>
241 1x explicit random_access_file(Ex const& ex) : random_access_file(ex.context())
242 {
243 1x }
244
245 /** Move constructor. */
246 1x random_access_file(random_access_file&& other) noexcept
247 1x : io_object(std::move(other))
248 {
249 1x }
250
251 /** Move assignment operator. */
252 random_access_file& operator=(random_access_file&& other) noexcept
253 {
254 if (this != &other)
255 {
256 close();
257 h_ = std::move(other.h_);
258 }
259 return *this;
260 }
261
262 random_access_file(random_access_file const&) = delete;
263 random_access_file& operator=(random_access_file const&) = delete;
264
265 /** Open a file.
266
267 @param path The filesystem path to open.
268 @param mode Bitmask of @ref file_base::flags specifying
269 access mode and creation behavior.
270
271 @throws std::system_error on failure.
272 */
273 void open(
274 std::filesystem::path const& path,
275 file_base::flags mode = file_base::read_only);
276
277 /** Close the file.
278
279 Releases file resources. Any pending operations complete
280 with `errc::operation_canceled`.
281 */
282 void close();
283
284 /** Check if the file is open. */
285 195x bool is_open() const noexcept
286 {
287 #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
288 return h_ && get().native_handle() != ~native_handle_type(0);
289 #else
290 195x return h_ && get().native_handle() >= 0;
291 #endif
292 }
293
294 /** Read data at the given offset.
295
296 @param offset Byte offset into the file.
297 @param buffers The buffer sequence to read into.
298
299 @return An awaitable yielding `(error_code, std::size_t)`.
300
301 @throws std::logic_error if the file is not open.
302 */
303 template<capy::MutableBufferSequence MB>
304 116x auto read_some_at(std::uint64_t offset, MB const& buffers)
305 {
306 116x if (!is_open())
307 detail::throw_logic_error("read_some_at: file not open");
308 116x return read_some_at_awaitable<MB>(*this, offset, buffers);
309 }
310
311 /** Write data at the given offset.
312
313 @param offset Byte offset into the file.
314 @param buffers The buffer sequence to write from.
315
316 @return An awaitable yielding `(error_code, std::size_t)`.
317
318 @throws std::logic_error if the file is not open.
319 */
320 template<capy::ConstBufferSequence CB>
321 10x auto write_some_at(std::uint64_t offset, CB const& buffers)
322 {
323 10x if (!is_open())
324 detail::throw_logic_error("write_some_at: file not open");
325 10x return write_some_at_awaitable<CB>(*this, offset, buffers);
326 }
327
328 /** Cancel pending asynchronous operations. */
329 void cancel();
330
331 /** Get the native file descriptor or handle. */
332 native_handle_type native_handle() const noexcept;
333
334 /** Return the file size in bytes. */
335 std::uint64_t size() const;
336
337 /** Resize the file. */
338 void resize(std::uint64_t new_size);
339
340 /** Synchronize file data to stable storage. */
341 void sync_data();
342
343 /** Synchronize file data and metadata to stable storage. */
344 void sync_all();
345
346 /** Release ownership of the native handle.
347
348 The file object becomes not-open. The caller is
349 responsible for closing the returned handle.
350
351 @return The native file descriptor or handle.
352 */
353 native_handle_type release();
354
355 /** Adopt an existing native handle.
356
357 Closes any currently open file before adopting.
358 The file object takes ownership of the handle.
359
360 @param handle The native file descriptor or handle.
361 */
362 void assign(native_handle_type handle);
363
364 private:
365 348x inline implementation& get() const noexcept
366 {
367 348x return *static_cast<implementation*>(h_.get());
368 }
369 };
370
371 } // namespace boost::corosio
372
373 #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP
374