100.00% Lines (50/50) 100.00% Functions (15/15)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Michael Vandeberg 2   // Copyright (c) 2026 Michael Vandeberg
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 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) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/corosio 7   // Official repository: https://github.com/cppalliance/corosio
8   // 8   //
9   9  
10   #ifndef BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 10   #ifndef BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP
11   #define BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 11   #define BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP
12   12  
13   #include <boost/corosio/detail/config.hpp> 13   #include <boost/corosio/detail/config.hpp>
14   #include <boost/corosio/detail/platform.hpp> 14   #include <boost/corosio/detail/platform.hpp>
15   #include <boost/corosio/detail/except.hpp> 15   #include <boost/corosio/detail/except.hpp>
16   #include <boost/corosio/detail/native_handle.hpp> 16   #include <boost/corosio/detail/native_handle.hpp>
17   #include <boost/corosio/detail/buffer_param.hpp> 17   #include <boost/corosio/detail/buffer_param.hpp>
18   #include <boost/corosio/file_base.hpp> 18   #include <boost/corosio/file_base.hpp>
19   #include <boost/corosio/io/io_object.hpp> 19   #include <boost/corosio/io/io_object.hpp>
20   #include <boost/capy/io_result.hpp> 20   #include <boost/capy/io_result.hpp>
21   #include <boost/capy/ex/executor_ref.hpp> 21   #include <boost/capy/ex/executor_ref.hpp>
22   #include <boost/capy/ex/execution_context.hpp> 22   #include <boost/capy/ex/execution_context.hpp>
23   #include <boost/capy/ex/io_env.hpp> 23   #include <boost/capy/ex/io_env.hpp>
24   #include <boost/capy/concept/executor.hpp> 24   #include <boost/capy/concept/executor.hpp>
25   #include <boost/capy/buffers.hpp> 25   #include <boost/capy/buffers.hpp>
26   26  
27   #include <concepts> 27   #include <concepts>
28   #include <coroutine> 28   #include <coroutine>
29   #include <cstddef> 29   #include <cstddef>
30   #include <cstdint> 30   #include <cstdint>
31   #include <type_traits> 31   #include <type_traits>
32   #include <filesystem> 32   #include <filesystem>
33   #include <stop_token> 33   #include <stop_token>
34   #include <system_error> 34   #include <system_error>
35   35  
36   namespace boost::corosio { 36   namespace boost::corosio {
37   37  
38   /** An asynchronous random-access file for coroutine I/O. 38   /** An asynchronous random-access file for coroutine I/O.
39   39  
40   Provides asynchronous read and write operations at explicit 40   Provides asynchronous read and write operations at explicit
41   byte offsets, without maintaining an implicit file position. 41   byte offsets, without maintaining an implicit file position.
42   42  
43   On POSIX platforms, file I/O is dispatched to a thread pool 43   On POSIX platforms, file I/O is dispatched to a thread pool
44   (blocking `preadv`/`pwritev`) with completion posted back to 44   (blocking `preadv`/`pwritev`) with completion posted back to
45   the scheduler. On Windows, true overlapped I/O is used via IOCP. 45   the scheduler. On Windows, true overlapped I/O is used via IOCP.
46   46  
47   @par Thread Safety 47   @par Thread Safety
48   Distinct objects: Safe.@n 48   Distinct objects: Safe.@n
49   Shared objects: Unsafe. Multiple concurrent reads and writes 49   Shared objects: Unsafe. Multiple concurrent reads and writes
50   are supported from coroutines sharing the same file object, 50   are supported from coroutines sharing the same file object,
51   but external synchronization is required for non-async 51   but external synchronization is required for non-async
52   operations (open, close, size, resize, etc.). 52   operations (open, close, size, resize, etc.).
53   53  
54   @par Example 54   @par Example
55   @par !example random_access_file 55   @par !example random_access_file
56   */ 56   */
57   class BOOST_COROSIO_DECL random_access_file : public io_object 57   class BOOST_COROSIO_DECL random_access_file : public io_object
58   { 58   {
59   public: 59   public:
60   /** Platform-specific random-access file implementation interface. 60   /** Platform-specific random-access file implementation interface.
61   61  
62   Backends derive from this to provide offset-based file I/O. 62   Backends derive from this to provide offset-based file I/O.
63   */ 63   */
64   struct implementation : io_object::implementation 64   struct implementation : io_object::implementation
65   { 65   {
66   /** Initiate a read at the given offset. 66   /** Initiate a read at the given offset.
67   67  
68   @param offset Byte offset into the file. 68   @param offset Byte offset into the file.
69   @param h Coroutine handle to resume on completion. 69   @param h Coroutine handle to resume on completion.
70   @param ex Executor for dispatching the completion. 70   @param ex Executor for dispatching the completion.
71   @param buf The buffer to read into. 71   @param buf The buffer to read into.
72   @param token Stop token for cancellation. 72   @param token Stop token for cancellation.
73   @param ec Output error code. 73   @param ec Output error code.
74   @param bytes_out Output bytes transferred. 74   @param bytes_out Output bytes transferred.
75   @return Coroutine handle to resume immediately. 75   @return Coroutine handle to resume immediately.
76   */ 76   */
77   virtual std::coroutine_handle<> read_some_at( 77   virtual std::coroutine_handle<> read_some_at(
78   std::uint64_t offset, 78   std::uint64_t offset,
79   std::coroutine_handle<> h, 79   std::coroutine_handle<> h,
80   capy::executor_ref ex, 80   capy::executor_ref ex,
81   buffer_param buf, 81   buffer_param buf,
82   std::stop_token token, 82   std::stop_token token,
83   std::error_code* ec, 83   std::error_code* ec,
84   std::size_t* bytes_out) = 0; 84   std::size_t* bytes_out) = 0;
85   85  
86   /** Initiate a write at the given offset. 86   /** Initiate a write at the given offset.
87   87  
88   @param offset Byte offset into the file. 88   @param offset Byte offset into the file.
89   @param h Coroutine handle to resume on completion. 89   @param h Coroutine handle to resume on completion.
90   @param ex Executor for dispatching the completion. 90   @param ex Executor for dispatching the completion.
91   @param buf The buffer to write from. 91   @param buf The buffer to write from.
92   @param token Stop token for cancellation. 92   @param token Stop token for cancellation.
93   @param ec Output error code. 93   @param ec Output error code.
94   @param bytes_out Output bytes transferred. 94   @param bytes_out Output bytes transferred.
95   @return Coroutine handle to resume immediately. 95   @return Coroutine handle to resume immediately.
96   */ 96   */
97   virtual std::coroutine_handle<> write_some_at( 97   virtual std::coroutine_handle<> write_some_at(
98   std::uint64_t offset, 98   std::uint64_t offset,
99   std::coroutine_handle<> h, 99   std::coroutine_handle<> h,
100   capy::executor_ref ex, 100   capy::executor_ref ex,
101   buffer_param buf, 101   buffer_param buf,
102   std::stop_token token, 102   std::stop_token token,
103   std::error_code* ec, 103   std::error_code* ec,
104   std::size_t* bytes_out) = 0; 104   std::size_t* bytes_out) = 0;
105   105  
106   /// Return the platform file descriptor or handle. 106   /// Return the platform file descriptor or handle.
107   virtual native_handle_type native_handle() const noexcept = 0; 107   virtual native_handle_type native_handle() const noexcept = 0;
108   108  
109   /// Cancel pending asynchronous operations. 109   /// Cancel pending asynchronous operations.
110   virtual void cancel() noexcept = 0; 110   virtual void cancel() noexcept = 0;
111   111  
112   /// Return the file size in bytes. 112   /// Return the file size in bytes.
113   virtual std::uint64_t size() const = 0; 113   virtual std::uint64_t size() const = 0;
114   114  
115   /// Resize the file to @p new_size bytes. 115   /// Resize the file to @p new_size bytes.
116   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0; 116   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0;
117   117  
118   /// Synchronize file data to stable storage. 118   /// Synchronize file data to stable storage.
119   virtual std::error_code sync_data() noexcept = 0; 119   virtual std::error_code sync_data() noexcept = 0;
120   120  
121   /// Synchronize file data and metadata to stable storage. 121   /// Synchronize file data and metadata to stable storage.
122   virtual std::error_code sync_all() noexcept = 0; 122   virtual std::error_code sync_all() noexcept = 0;
123   123  
124   /// Release ownership of the native handle. 124   /// Release ownership of the native handle.
125   virtual native_handle_type release() = 0; 125   virtual native_handle_type release() = 0;
126   126  
127   /// Adopt an existing native handle. 127   /// Adopt an existing native handle.
128   virtual std::error_code assign(native_handle_type handle) noexcept = 0; 128   virtual std::error_code assign(native_handle_type handle) noexcept = 0;
129   }; 129   };
130   130  
131   /** Awaitable for async read-at operations. */ 131   /** Awaitable for async read-at operations. */
132   template<class MutableBufferSequence> 132   template<class MutableBufferSequence>
133   struct read_some_at_awaitable 133   struct read_some_at_awaitable
134   { 134   {
135   random_access_file& f_; 135   random_access_file& f_;
136   std::uint64_t offset_; 136   std::uint64_t offset_;
137   MutableBufferSequence buffers_; 137   MutableBufferSequence buffers_;
138   std::stop_token token_; 138   std::stop_token token_;
139   mutable std::error_code ec_; 139   mutable std::error_code ec_;
140   mutable std::size_t bytes_ = 0; 140   mutable std::size_t bytes_ = 0;
141   141  
HITCBC 142   293 read_some_at_awaitable( 142   293 read_some_at_awaitable(
143   random_access_file& f, 143   random_access_file& f,
144   std::uint64_t offset, 144   std::uint64_t offset,
145 - MutableBufferSequence buffers) 145 + MutableBufferSequence
146 - noexcept(std::is_nothrow_move_constructible_v<MutableBufferSequence>) 146 + buffers) noexcept(std::
  147 + is_nothrow_move_constructible_v<
  148 + MutableBufferSequence>)
HITCBC 147   293 : f_(f) 149   293 : f_(f)
HITCBC 148   293 , offset_(offset) 150   293 , offset_(offset)
HITCBC 149   293 , buffers_(std::move(buffers)) 151   293 , buffers_(std::move(buffers))
150   { 152   {
HITCBC 151   293 } 153   293 }
152   154  
HITCBC 153   293 bool await_ready() const noexcept 155   293 bool await_ready() const noexcept
154   { 156   {
155   // A pre-set ec_ means the initiator failed before 157   // A pre-set ec_ means the initiator failed before
156   // dispatch (e.g. a closed object). 158   // dispatch (e.g. a closed object).
HITCBC 157   293 return static_cast<bool>(ec_) || token_.stop_requested(); 159   293 return static_cast<bool>(ec_) || token_.stop_requested();
158   } 160   }
159   161  
HITCBC 160   291 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 162   291 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
161   { 163   {
HITCBC 162   291 if (token_.stop_requested()) 164   291 if (token_.stop_requested())
HITCBC 163   4 return {make_error_code(std::errc::operation_canceled), 0}; 165   4 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 164   287 return {ec_, bytes_}; 166   287 return {ec_, bytes_};
165   } 167   }
166   168  
HITCBC 167   291 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 169   291 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
168   -> std::coroutine_handle<> 170   -> std::coroutine_handle<>
169   { 171   {
HITCBC 170   291 token_ = env->stop_token; 172   291 token_ = env->stop_token;
HITCBC 171   873 return f_.get().read_some_at( 173   873 return f_.get().read_some_at(
HITCBC 172   873 offset_, h, env->executor, buffers_, token_, &ec_, &bytes_); 174   873 offset_, h, env->executor, buffers_, token_, &ec_, &bytes_);
173   } 175   }
174   }; 176   };
175   177  
176   /** Awaitable for async write-at operations. */ 178   /** Awaitable for async write-at operations. */
177   template<class ConstBufferSequence> 179   template<class ConstBufferSequence>
178   struct write_some_at_awaitable 180   struct write_some_at_awaitable
179   { 181   {
180   random_access_file& f_; 182   random_access_file& f_;
181   std::uint64_t offset_; 183   std::uint64_t offset_;
182   ConstBufferSequence buffers_; 184   ConstBufferSequence buffers_;
183   std::stop_token token_; 185   std::stop_token token_;
184   mutable std::error_code ec_; 186   mutable std::error_code ec_;
185   mutable std::size_t bytes_ = 0; 187   mutable std::size_t bytes_ = 0;
186   188  
HITCBC 187   43 write_some_at_awaitable( 189   43 write_some_at_awaitable(
188   random_access_file& f, 190   random_access_file& f,
189   std::uint64_t offset, 191   std::uint64_t offset,
190 - ConstBufferSequence buffers) 192 + ConstBufferSequence
191 - noexcept(std::is_nothrow_move_constructible_v<ConstBufferSequence>) 193 + buffers) noexcept(std::
  194 + is_nothrow_move_constructible_v<
  195 + ConstBufferSequence>)
HITCBC 192   43 : f_(f) 196   43 : f_(f)
HITCBC 193   43 , offset_(offset) 197   43 , offset_(offset)
HITCBC 194   43 , buffers_(std::move(buffers)) 198   43 , buffers_(std::move(buffers))
195   { 199   {
HITCBC 196   43 } 200   43 }
197   201  
HITCBC 198   43 bool await_ready() const noexcept 202   43 bool await_ready() const noexcept
199   { 203   {
200   // A pre-set ec_ means the initiator failed before 204   // A pre-set ec_ means the initiator failed before
201   // dispatch (e.g. a closed object). 205   // dispatch (e.g. a closed object).
HITCBC 202   43 return static_cast<bool>(ec_) || token_.stop_requested(); 206   43 return static_cast<bool>(ec_) || token_.stop_requested();
203   } 207   }
204   208  
HITCBC 205   43 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 209   43 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
206   { 210   {
HITCBC 207   43 if (token_.stop_requested()) 211   43 if (token_.stop_requested())
HITCBC 208   2 return {make_error_code(std::errc::operation_canceled), 0}; 212   2 return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 209   41 return {ec_, bytes_}; 213   41 return {ec_, bytes_};
210   } 214   }
211   215  
HITCBC 212   41 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 216   41 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
213   -> std::coroutine_handle<> 217   -> std::coroutine_handle<>
214   { 218   {
HITCBC 215   41 token_ = env->stop_token; 219   41 token_ = env->stop_token;
HITCBC 216   123 return f_.get().write_some_at( 220   123 return f_.get().write_some_at(
HITCBC 217   123 offset_, h, env->executor, buffers_, token_, &ec_, &bytes_); 221   123 offset_, h, env->executor, buffers_, token_, &ec_, &bytes_);
218   } 222   }
219   }; 223   };
220   224  
221   public: 225   public:
222   /** Destructor. 226   /** Destructor.
223   227  
224   Closes the file if open, cancelling any pending operations. 228   Closes the file if open, cancelling any pending operations.
225   */ 229   */
226   ~random_access_file() override; 230   ~random_access_file() override;
227   231  
228   /** Construct from an execution context. 232   /** Construct from an execution context.
229   233  
230   @param ctx The execution context that will own this file. 234   @param ctx The execution context that will own this file.
231   */ 235   */
232   explicit random_access_file(capy::execution_context& ctx); 236   explicit random_access_file(capy::execution_context& ctx);
233   237  
234   /** Construct from an executor. 238   /** Construct from an executor.
235   239  
236   @param ex The executor whose context will own this file. 240   @param ex The executor whose context will own this file.
237   */ 241   */
238   template<class Ex> 242   template<class Ex>
239   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) && 243   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) &&
240   capy::Executor<Ex> 244   capy::Executor<Ex>
HITCBC 241   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context()) 245   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context())
242   { 246   {
HITCBC 243   2 } 247   2 }
244   248  
245   /** Move constructor. */ 249   /** Move constructor. */
HITCBC 246   2 random_access_file(random_access_file&& other) noexcept 250   2 random_access_file(random_access_file&& other) noexcept
HITCBC 247   2 : io_object(std::move(other)) 251   2 : io_object(std::move(other))
248   { 252   {
HITCBC 249   2 } 253   2 }
250   254  
251   /** Move assignment operator. */ 255   /** Move assignment operator. */
252   random_access_file& operator=(random_access_file&& other) noexcept 256   random_access_file& operator=(random_access_file&& other) noexcept
253   { 257   {
254   if (this != &other) 258   if (this != &other)
255   { 259   {
256   close(); 260   close();
257   h_ = std::move(other.h_); 261   h_ = std::move(other.h_);
258   } 262   }
259   return *this; 263   return *this;
260   } 264   }
261   265  
262   random_access_file(random_access_file const&) = delete; 266   random_access_file(random_access_file const&) = delete;
263   random_access_file& operator=(random_access_file const&) = delete; 267   random_access_file& operator=(random_access_file const&) = delete;
264   268  
265   /** Open a file. 269   /** Open a file.
266   270  
267   Failures such as a missing file or insufficient permissions 271   Failures such as a missing file or insufficient permissions
268   are expected runtime conditions and are reported through the 272   are expected runtime conditions and are reported through the
269   returned error code. If the file is already open, it is 273   returned error code. If the file is already open, it is
270   closed first. 274   closed first.
271   275  
272   @param path The filesystem path to open. 276   @param path The filesystem path to open.
273   @param mode Bitmask of @ref file_base::flags specifying 277   @param mode Bitmask of @ref file_base::flags specifying
274   access mode and creation behavior. 278   access mode and creation behavior.
275   279  
276   @return The error code, empty on success. 280   @return The error code, empty on success.
277   */ 281   */
278   [[nodiscard]] std::error_code open( 282   [[nodiscard]] std::error_code open(
279   std::filesystem::path const& path, 283   std::filesystem::path const& path,
280   file_base::flags mode = file_base::read_only) noexcept; 284   file_base::flags mode = file_base::read_only) noexcept;
281   285  
282   /** Close the file. 286   /** Close the file.
283   287  
284   Releases file resources. Any pending operations complete 288   Releases file resources. Any pending operations complete
285   with `errc::operation_canceled`. 289   with `errc::operation_canceled`.
286   */ 290   */
287   void close() noexcept; 291   void close() noexcept;
288   292  
289   /** Check if the file is open. */ 293   /** Check if the file is open. */
HITCBC 290   682 bool is_open() const noexcept 294   682 bool is_open() const noexcept
291   { 295   {
292   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 296   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
293   return h_ && get().native_handle() != ~native_handle_type(0); 297   return h_ && get().native_handle() != ~native_handle_type(0);
294   #else 298   #else
HITCBC 295   682 return h_ && get().native_handle() >= 0; 299   682 return h_ && get().native_handle() >= 0;
296   #endif 300   #endif
297   } 301   }
298   302  
299   /** Read data at the given offset. 303   /** Read data at the given offset.
300   304  
301   @param offset Byte offset into the file. 305   @param offset Byte offset into the file.
302   @param buffers The buffer sequence to read into. 306   @param buffers The buffer sequence to read into.
303   307  
304   @return An awaitable yielding `(error_code, std::size_t)`. 308   @return An awaitable yielding `(error_code, std::size_t)`.
305   309  
306   A closed file reports `errc::bad_file_descriptor`. 310   A closed file reports `errc::bad_file_descriptor`.
307   */ 311   */
308   template<capy::MutableBufferSequence MB> 312   template<capy::MutableBufferSequence MB>
HITCBC 309   293 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 313   293 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
310   { 314   {
HITCBC 311   293 read_some_at_awaitable<MB> aw(*this, offset, buffers); 315   293 read_some_at_awaitable<MB> aw(*this, offset, buffers);
HITCBC 312   293 if (!is_open()) 316   293 if (!is_open())
HITCBC 313   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 317   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 314   293 return aw; 318   293 return aw;
315   } 319   }
316   320  
317   /** Write data at the given offset. 321   /** Write data at the given offset.
318   322  
319   @param offset Byte offset into the file. 323   @param offset Byte offset into the file.
320   @param buffers The buffer sequence to write from. 324   @param buffers The buffer sequence to write from.
321   325  
322   @return An awaitable yielding `(error_code, std::size_t)`. 326   @return An awaitable yielding `(error_code, std::size_t)`.
323   327  
324   A closed file reports `errc::bad_file_descriptor`. 328   A closed file reports `errc::bad_file_descriptor`.
325   */ 329   */
326   template<capy::ConstBufferSequence CB> 330   template<capy::ConstBufferSequence CB>
HITCBC 327   43 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 331   43 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
328   { 332   {
HITCBC 329   43 write_some_at_awaitable<CB> aw(*this, offset, buffers); 333   43 write_some_at_awaitable<CB> aw(*this, offset, buffers);
HITCBC 330   43 if (!is_open()) 334   43 if (!is_open())
HITCBC 331   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 335   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 332   43 return aw; 336   43 return aw;
333   } 337   }
334   338  
335   /** Cancel pending asynchronous operations. */ 339   /** Cancel pending asynchronous operations. */
336   void cancel() noexcept; 340   void cancel() noexcept;
337   341  
338   /** Get the native file descriptor or handle. */ 342   /** Get the native file descriptor or handle. */
339   native_handle_type native_handle() const noexcept; 343   native_handle_type native_handle() const noexcept;
340   344  
341   /** Return the file size in bytes. 345   /** Return the file size in bytes.
342   346  
343   @throws std::system_error If the file is not open, or if the 347   @throws std::system_error If the file is not open, or if the
344   underlying size query fails. 348   underlying size query fails.
345   */ 349   */
346   std::uint64_t size() const; 350   std::uint64_t size() const;
347   351  
348   /** Resize the file to @p new_size bytes. 352   /** Resize the file to @p new_size bytes.
349   353  
350   Failures such as insufficient disk space are reported 354   Failures such as insufficient disk space are reported
351   through the returned error code. A closed file reports 355   through the returned error code. A closed file reports
352   `errc::bad_file_descriptor`. 356   `errc::bad_file_descriptor`.
353   357  
354   @param new_size The new file size. 358   @param new_size The new file size.
355   359  
356   @return The error code, empty on success. 360   @return The error code, empty on success.
357   */ 361   */
358   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept; 362   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept;
359   363  
360   /** Synchronize file data to stable storage. 364   /** Synchronize file data to stable storage.
361   365  
362   Write-back failures such as device I/O errors surface here 366   Write-back failures such as device I/O errors surface here
363   and are reported through the returned error code. A closed 367   and are reported through the returned error code. A closed
364   file reports `errc::bad_file_descriptor`. 368   file reports `errc::bad_file_descriptor`.
365   369  
366   @return The error code, empty on success. 370   @return The error code, empty on success.
367   */ 371   */
368   [[nodiscard]] std::error_code sync_data() noexcept; 372   [[nodiscard]] std::error_code sync_data() noexcept;
369   373  
370   /** Synchronize file data and metadata to stable storage. 374   /** Synchronize file data and metadata to stable storage.
371   375  
372   Write-back failures such as device I/O errors surface here 376   Write-back failures such as device I/O errors surface here
373   and are reported through the returned error code. A closed 377   and are reported through the returned error code. A closed
374   file reports `errc::bad_file_descriptor`. 378   file reports `errc::bad_file_descriptor`.
375   379  
376   @return The error code, empty on success. 380   @return The error code, empty on success.
377   */ 381   */
378   [[nodiscard]] std::error_code sync_all() noexcept; 382   [[nodiscard]] std::error_code sync_all() noexcept;
379   383  
380   /** Release ownership of the native handle. 384   /** Release ownership of the native handle.
381   385  
382   The file object becomes not-open. The caller is 386   The file object becomes not-open. The caller is
383   responsible for closing the returned handle. 387   responsible for closing the returned handle.
384   388  
385   @return The native file descriptor or handle. 389   @return The native file descriptor or handle.
386   390  
387   @throws std::system_error `errc::bad_file_descriptor` if the 391   @throws std::system_error `errc::bad_file_descriptor` if the
388   file is not open. 392   file is not open.
389   */ 393   */
390   native_handle_type release(); 394   native_handle_type release();
391   395  
392   /** Adopt an existing native handle. 396   /** Adopt an existing native handle.
393   397  
394   Closes any currently open file before adopting. 398   Closes any currently open file before adopting.
395   The file object takes ownership of the handle. Handles 399   The file object takes ownership of the handle. Handles
396   created elsewhere may be unsuitable for asynchronous I/O; 400   created elsewhere may be unsuitable for asynchronous I/O;
397   such failures are reported through the returned error code. 401   such failures are reported through the returned error code.
398   402  
399   @param handle The native file descriptor or handle. 403   @param handle The native file descriptor or handle.
400   404  
401   @return The error code, empty on success. 405   @return The error code, empty on success.
402   */ 406   */
403   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept; 407   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept;
404   408  
405   protected: 409   protected:
406   /// Construct from a pre-built handle (for native_random_access_file). 410   /// Construct from a pre-built handle (for native_random_access_file).
HITCBC 407   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {} 411   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {}
408   412  
409   private: 413   private:
HITCBC 410   1171 inline implementation& get() const noexcept 414   1171 inline implementation& get() const noexcept
411   { 415   {
HITCBC 412   1171 return *static_cast<implementation*>(h_.get()); 416   1171 return *static_cast<implementation*>(h_.get());
413   } 417   }
414   }; 418   };
415   419  
416   } // namespace boost::corosio 420   } // namespace boost::corosio
417   421  
418   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 422   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP