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