100.00% Lines (38/38) 100.00% Functions (11/11)
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/native_handle.hpp> 15   #include <boost/corosio/detail/native_handle.hpp>
16   #include <boost/corosio/detail/buffer_param.hpp> 16   #include <boost/corosio/detail/buffer_param.hpp>
17   #include <boost/corosio/detail/op_base.hpp> 17   #include <boost/corosio/detail/op_base.hpp>
18   #include <boost/corosio/error.hpp> 18   #include <boost/corosio/error.hpp>
19   #include <boost/corosio/file_base.hpp> 19   #include <boost/corosio/file_base.hpp>
20   #include <boost/corosio/io/io_object.hpp> 20   #include <boost/corosio/io/io_object.hpp>
21   #include <boost/capy/continuation.hpp> 21   #include <boost/capy/continuation.hpp>
22   #include <boost/capy/results.hpp> 22   #include <boost/capy/results.hpp>
23   #include <boost/capy/ex/executor_ref.hpp> 23   #include <boost/capy/ex/executor_ref.hpp>
24   #include <boost/capy/ex/execution_context.hpp> 24   #include <boost/capy/ex/execution_context.hpp>
25   #include <boost/capy/ex/await_context.hpp> 25   #include <boost/capy/ex/await_context.hpp>
26   #include <boost/capy/concept/executor.hpp> 26   #include <boost/capy/concept/executor.hpp>
27   #include <boost/capy/buffers.hpp> 27   #include <boost/capy/buffers.hpp>
28   28  
29   #include <concepts> 29   #include <concepts>
30   #include <coroutine> 30   #include <coroutine>
31   #include <cstddef> 31   #include <cstddef>
32   #include <cstdint> 32   #include <cstdint>
33   #include <type_traits> 33   #include <type_traits>
34   #include <filesystem> 34   #include <filesystem>
35   #include <stop_token> 35   #include <stop_token>
36   #include <system_error> 36   #include <system_error>
37   37  
38   namespace boost::corosio { 38   namespace boost::corosio {
39   39  
40   /** Reads and writes a file at arbitrary offsets, from a coroutine. 40   /** Reads and writes a file at arbitrary offsets, from a coroutine.
41   41  
42   Provides asynchronous read and write operations at explicit 42   Provides asynchronous read and write operations at explicit
43   byte offsets, without maintaining an implicit file position. 43   byte offsets, without maintaining an implicit file position.
44   44  
45   On POSIX platforms, file I/O is dispatched to a thread pool 45   On POSIX platforms, file I/O is dispatched to a thread pool
46   (blocking `preadv`/`pwritev`) with completion posted back to 46   (blocking `preadv`/`pwritev`) with completion posted back to
47   the scheduler. On Windows, true overlapped I/O is used via IOCP. 47   the scheduler. On Windows, true overlapped I/O is used via IOCP.
48   48  
49   On Windows, while the file is open, its handle is bound to the 49   On Windows, while the file is open, its handle is bound to the
50   execution context's completion port. Every overlapped call on the 50   execution context's completion port. Every overlapped call on the
51   handle queues a packet to that port. Do not issue your own 51   handle queues a packet to that port. Do not issue your own
52   overlapped I/O on `native_handle()` (`DeviceIoControl`, 52   overlapped I/O on `native_handle()` (`DeviceIoControl`,
53   `ReadFile`) unless the `OVERLAPPED`'s `hEvent` has its low-order 53   `ReadFile`) unless the `OVERLAPPED`'s `hEvent` has its low-order
54   bit set, which suppresses the packet. 54   bit set, which suppresses the packet.
55   55  
56   @par Thread Safety 56   @par Thread Safety
57   Distinct objects: Safe.@n 57   Distinct objects: Safe.@n
58   Shared objects: Unsafe. Coroutines sharing the same file object may 58   Shared objects: Unsafe. Coroutines sharing the same file object may
59   run multiple concurrent reads and writes. Non-async operations such as open, close, size, and resize require external synchronization. 59   run multiple concurrent reads and writes. Non-async operations such as open, close, size, and resize require external synchronization.
60   60  
61   @par Example 61   @par Example
62   @par !example random_access_file 62   @par !example random_access_file
63   */ 63   */
64   class BOOST_COROSIO_DECL random_access_file : public io_object 64   class BOOST_COROSIO_DECL random_access_file : public io_object
65   { 65   {
66   public: 66   public:
67   /** Declares the offset-based file operations a platform backend 67   /** Declares the offset-based file operations a platform backend
68   must implement. 68   must implement.
69   69  
70   Backends derive from this to provide offset-based file I/O. 70   Backends derive from this to provide offset-based file I/O.
71   */ 71   */
72   struct implementation : io_object::implementation 72   struct implementation : io_object::implementation
73   { 73   {
74   /** Initiate a read at the given offset. 74   /** Initiate a read at the given offset.
75   75  
76   @param offset Byte offset into the file. 76   @param offset Byte offset into the file.
77   @param cont The awaiting coroutine's continuation. It must 77   @param cont The awaiting coroutine's continuation. It must
78   stay valid until `cont.h` is resumed through @p ex. 78   stay valid until `cont.h` is resumed through @p ex.
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   capy::continuation& cont, 88   capy::continuation& cont,
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 cont The awaiting coroutine's continuation. It must 98   @param cont The awaiting coroutine's continuation. It must
99   stay valid until `cont.h` is resumed through @p ex. 99   stay valid until `cont.h` is resumed through @p ex.
100   @param ex Executor for dispatching the completion. 100   @param ex Executor for dispatching the completion.
101   @param buf The buffer to write from. 101   @param buf The buffer to write from.
102   @param token Stop token for cancellation. 102   @param token Stop token for cancellation.
103   @param ec Output error code. 103   @param ec Output error code.
104   @param bytes_out Output bytes transferred. 104   @param bytes_out Output bytes transferred.
105   @return Coroutine handle to resume immediately. 105   @return Coroutine handle to resume immediately.
106   */ 106   */
107   virtual std::coroutine_handle<> write_some_at( 107   virtual std::coroutine_handle<> write_some_at(
108   std::uint64_t offset, 108   std::uint64_t offset,
109   capy::continuation& cont, 109   capy::continuation& cont,
110   capy::executor_ref ex, 110   capy::executor_ref ex,
111   buffer_param buf, 111   buffer_param buf,
112   std::stop_token token, 112   std::stop_token token,
113   std::error_code* ec, 113   std::error_code* ec,
114   std::size_t* bytes_out) = 0; 114   std::size_t* bytes_out) = 0;
115   115  
116   /// Return the platform file descriptor or handle. 116   /// Return the platform file descriptor or handle.
117   virtual native_handle_type native_handle() const noexcept = 0; 117   virtual native_handle_type native_handle() const noexcept = 0;
118   118  
119   /// Cancel pending asynchronous operations. 119   /// Cancel pending asynchronous operations.
120   virtual void cancel() noexcept = 0; 120   virtual void cancel() noexcept = 0;
121   121  
122   /// Return the file size in bytes. 122   /// Return the file size in bytes.
123   virtual capy::results<std::uint64_t> size() const noexcept = 0; 123   virtual capy::results<std::uint64_t> size() const noexcept = 0;
124   124  
125   /** Resize the file to @p new_size bytes. 125   /** Resize the file to @p new_size bytes.
126   126  
127   @param new_size The requested size in bytes. 127   @param new_size The requested size in bytes.
128   128  
129   @return The error code, empty on success. 129   @return The error code, empty on success.
130   */ 130   */
131   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0; 131   virtual std::error_code resize(std::uint64_t new_size) noexcept = 0;
132   132  
133   /** Synchronize file data to stable storage. 133   /** Synchronize file data to stable storage.
134   134  
135   @return The error code, empty on success. 135   @return The error code, empty on success.
136   */ 136   */
137   virtual std::error_code sync_data() noexcept = 0; 137   virtual std::error_code sync_data() noexcept = 0;
138   138  
139   /** Synchronize file data and metadata to stable storage. 139   /** Synchronize file data and metadata to stable storage.
140   140  
141   @return The error code, empty on success. 141   @return The error code, empty on success.
142   */ 142   */
143   virtual std::error_code sync_all() noexcept = 0; 143   virtual std::error_code sync_all() noexcept = 0;
144   144  
145   /// Release ownership of the native handle. 145   /// Release ownership of the native handle.
146   virtual capy::results<native_handle_type> release() noexcept = 0; 146   virtual capy::results<native_handle_type> release() noexcept = 0;
147   147  
148   /** Adopt an existing native handle. 148   /** Adopt an existing native handle.
149   149  
150   @param handle The native handle to adopt. The implementation takes 150   @param handle The native handle to adopt. The implementation takes
151   ownership and closes it. 151   ownership and closes it.
152   152  
153   @return The error code, empty on success. 153   @return The error code, empty on success.
154   */ 154   */
155   virtual std::error_code assign(native_handle_type handle) noexcept = 0; 155   virtual std::error_code assign(native_handle_type handle) noexcept = 0;
156   }; 156   };
157   157  
158   /** Awaitable for async read-at operations. */ 158   /** Awaitable for async read-at operations. */
159   template<class MutableBufferSequence> 159   template<class MutableBufferSequence>
160   struct read_some_at_awaitable 160   struct read_some_at_awaitable
161   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>> 161   : detail::bytes_op_base<read_some_at_awaitable<MutableBufferSequence>>
162   { 162   {
163   private: 163   private:
164   friend random_access_file; 164   friend random_access_file;
165   friend detail::bytes_op_base< 165   friend detail::bytes_op_base<
166   read_some_at_awaitable<MutableBufferSequence>>; 166   read_some_at_awaitable<MutableBufferSequence>>;
167   167  
168   random_access_file& f_; 168   random_access_file& f_;
169   std::uint64_t offset_; 169   std::uint64_t offset_;
170   MutableBufferSequence buffers_; 170   MutableBufferSequence buffers_;
171   171  
HITCBC 172   398 read_some_at_awaitable( 172   398 read_some_at_awaitable(
173   random_access_file& f, 173   random_access_file& f,
174   std::uint64_t offset, 174   std::uint64_t offset,
175   MutableBufferSequence 175   MutableBufferSequence
176   buffers) noexcept(std:: 176   buffers) noexcept(std::
177   is_nothrow_move_constructible_v< 177   is_nothrow_move_constructible_v<
178   MutableBufferSequence>) 178   MutableBufferSequence>)
HITCBC 179   398 : f_(f) 179   398 : f_(f)
HITCBC 180   398 , offset_(offset) 180   398 , offset_(offset)
HITCBC 181   398 , buffers_(std::move(buffers)) 181   398 , buffers_(std::move(buffers))
182   { 182   {
HITCBC 183   398 } 183   398 }
184   184  
185   std::coroutine_handle<> 185   std::coroutine_handle<>
HITCBC 186   392 dispatch(capy::continuation& cont, capy::executor_ref ex) const 186   392 dispatch(capy::continuation& cont, capy::executor_ref ex) const
187   { 187   {
188   // The continuation lives in the awaiting frame, which stays 188   // The continuation lives in the awaiting frame, which stays
189   // put until resumption -- unlike the per-call op, which is 189   // put until resumption -- unlike the per-call op, which is
190   // freed before the coroutine runs. 190   // freed before the coroutine runs.
HITCBC 191   784 return f_.get().read_some_at( 191   784 return f_.get().read_some_at(
HITCBC 192   392 offset_, cont, ex, buffers_, this->token_, &this->ec_, 192   392 offset_, cont, ex, buffers_, this->token_, &this->ec_,
HITCBC 193   784 &this->bytes_); 193   784 &this->bytes_);
194   } 194   }
195   }; 195   };
196   196  
197   /** Awaitable for async write-at operations. */ 197   /** Awaitable for async write-at operations. */
198   template<class ConstBufferSequence> 198   template<class ConstBufferSequence>
199   struct write_some_at_awaitable 199   struct write_some_at_awaitable
200   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>> 200   : detail::bytes_op_base<write_some_at_awaitable<ConstBufferSequence>>
201   { 201   {
202   private: 202   private:
203   friend random_access_file; 203   friend random_access_file;
204   friend detail::bytes_op_base< 204   friend detail::bytes_op_base<
205   write_some_at_awaitable<ConstBufferSequence>>; 205   write_some_at_awaitable<ConstBufferSequence>>;
206   206  
207   random_access_file& f_; 207   random_access_file& f_;
208   std::uint64_t offset_; 208   std::uint64_t offset_;
209   ConstBufferSequence buffers_; 209   ConstBufferSequence buffers_;
210   210  
HITCBC 211   134 write_some_at_awaitable( 211   134 write_some_at_awaitable(
212   random_access_file& f, 212   random_access_file& f,
213   std::uint64_t offset, 213   std::uint64_t offset,
214   ConstBufferSequence 214   ConstBufferSequence
215   buffers) noexcept(std:: 215   buffers) noexcept(std::
216   is_nothrow_move_constructible_v< 216   is_nothrow_move_constructible_v<
217   ConstBufferSequence>) 217   ConstBufferSequence>)
HITCBC 218   134 : f_(f) 218   134 : f_(f)
HITCBC 219   134 , offset_(offset) 219   134 , offset_(offset)
HITCBC 220   134 , buffers_(std::move(buffers)) 220   134 , buffers_(std::move(buffers))
221   { 221   {
HITCBC 222   134 } 222   134 }
223   223  
224   std::coroutine_handle<> 224   std::coroutine_handle<>
HITCBC 225   130 dispatch(capy::continuation& cont, capy::executor_ref ex) const 225   130 dispatch(capy::continuation& cont, capy::executor_ref ex) const
226   { 226   {
HITCBC 227   260 return f_.get().write_some_at( 227   260 return f_.get().write_some_at(
HITCBC 228   130 offset_, cont, ex, buffers_, this->token_, &this->ec_, 228   130 offset_, cont, ex, buffers_, this->token_, &this->ec_,
HITCBC 229   260 &this->bytes_); 229   260 &this->bytes_);
230   } 230   }
231   }; 231   };
232   232  
233   public: 233   public:
234   /** Destructor. 234   /** Destructor.
235   235  
236   Closes the file if open, cancelling any pending operations. 236   Closes the file if open, cancelling any pending operations.
237   */ 237   */
238   ~random_access_file() override; 238   ~random_access_file() override;
239   239  
240   /** Construct from an execution context. 240   /** Construct from an execution context.
241   241  
242   @param ctx The execution context that owns this file. 242   @param ctx The execution context that owns this file.
243   */ 243   */
244   explicit random_access_file(capy::execution_context& ctx); 244   explicit random_access_file(capy::execution_context& ctx);
245   245  
246   /** Construct from an executor. 246   /** Construct from an executor.
247   247  
248   @param ex The executor whose context owns this file. 248   @param ex The executor whose context owns this file.
249   */ 249   */
250   template<class Ex> 250   template<class Ex>
251   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) && 251   requires(!std::same_as<std::remove_cvref_t<Ex>, random_access_file>) &&
252   capy::Executor<Ex> 252   capy::Executor<Ex>
HITCBC 253   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context()) 253   2 explicit random_access_file(Ex const& ex) : random_access_file(ex.context())
254   { 254   {
HITCBC 255   2 } 255   2 }
256   256  
257   /** Move constructor. */ 257   /** Move constructor. */
HITCBC 258   10 random_access_file(random_access_file&& other) noexcept 258   10 random_access_file(random_access_file&& other) noexcept
HITCBC 259   10 : io_object(std::move(other)) 259   10 : io_object(std::move(other))
260   { 260   {
HITCBC 261   10 } 261   10 }
262   262  
263   /** Move assignment operator. */ 263   /** Move assignment operator. */
264   random_access_file& operator=(random_access_file&& other) noexcept 264   random_access_file& operator=(random_access_file&& other) noexcept
265   { 265   {
266   if (this != &other) 266   if (this != &other)
267   { 267   {
268   close(); 268   close();
269   h_ = std::move(other.h_); 269   h_ = std::move(other.h_);
270   } 270   }
271   return *this; 271   return *this;
272   } 272   }
273   273  
274   /// Copy construction is disabled; the handle is uniquely owned. 274   /// Copy construction is disabled; the handle is uniquely owned.
275   random_access_file(random_access_file const&) = delete; 275   random_access_file(random_access_file const&) = delete;
276   /// Copy assignment is disabled; the handle is uniquely owned. 276   /// Copy assignment is disabled; the handle is uniquely owned.
277   random_access_file& operator=(random_access_file const&) = delete; 277   random_access_file& operator=(random_access_file const&) = delete;
278   278  
279   /** Open a file. 279   /** Open a file.
280   280  
281   Failures such as a missing file or insufficient permissions 281   Failures such as a missing file or insufficient permissions
282   are expected runtime conditions and are reported through the 282   are expected runtime conditions and are reported through the
283   returned error code. If the file is already open, it is 283   returned error code. If the file is already open, it is
284   closed first. 284   closed first.
285   285  
286   @param path The filesystem path to open. 286   @param path The filesystem path to open.
287   @param mode Bitmask of @ref file_base::flags specifying 287   @param mode Bitmask of @ref file_base::flags specifying
288   access mode and creation behavior. 288   access mode and creation behavior.
289   289  
290   @return The error code, empty on success. 290   @return The error code, empty on success.
291   */ 291   */
292   [[nodiscard]] std::error_code open( 292   [[nodiscard]] std::error_code open(
293   std::filesystem::path const& path, 293   std::filesystem::path const& path,
294   file_base::flags mode = file_base::read_only) noexcept; 294   file_base::flags mode = file_base::read_only) noexcept;
295   295  
296   /** Close the file. 296   /** Close the file.
297   297  
298   Releases file resources. Pending operations complete through the 298   Releases file resources. Pending operations complete through the
299   same path as @ref cancel: one still in flight completes with 299   same path as @ref cancel: one still in flight completes with
300   `errc::operation_canceled`. An operation whose result is already 300   `errc::operation_canceled`. An operation whose result is already
301   decided reports that result. 301   decided reports that result.
302   */ 302   */
303   void close() noexcept; 303   void close() noexcept;
304   304  
305   /** Check if the file is open. 305   /** Check if the file is open.
306   306  
307   @return `true` if the file holds an open handle. 307   @return `true` if the file holds an open handle.
308   */ 308   */
HITCBC 309   1302 bool is_open() const noexcept 309   1302 bool is_open() const noexcept
310   { 310   {
311   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 311   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
312   return h_ && get().native_handle() != ~native_handle_type(0); 312   return h_ && get().native_handle() != ~native_handle_type(0);
313   #else 313   #else
HITCBC 314   1302 return h_ && get().native_handle() >= 0; 314   1302 return h_ && get().native_handle() >= 0;
315   #endif 315   #endif
316   } 316   }
317   317  
318   /** Read data at the given offset. 318   /** Read data at the given offset.
319   319  
320   @param offset Byte offset into the file. 320   @param offset Byte offset into the file.
321   @param buffers The buffer sequence to read into. 321   @param buffers The buffer sequence to read into.
322   322  
323   @return An awaitable yielding `(error_code, std::size_t)`. 323   @return An awaitable yielding `(error_code, std::size_t)`.
324   324  
325   A closed file reports `errc::bad_file_descriptor`. 325   A closed file reports `errc::bad_file_descriptor`.
326   */ 326   */
327   template<capy::MutableBufferSequence MB> 327   template<capy::MutableBufferSequence MB>
HITCBC 328   398 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 328   398 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
329   { 329   {
HITCBC 330   398 read_some_at_awaitable<MB> aw(*this, offset, buffers); 330   398 read_some_at_awaitable<MB> aw(*this, offset, buffers);
HITCBC 331   398 if (!is_open()) 331   398 if (!is_open())
HITCBC 332   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 332   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 333   398 return aw; 333   398 return aw;
334   } 334   }
335   335  
336   /** Write data at the given offset. 336   /** Write data at the given offset.
337   337  
338   @param offset Byte offset into the file. 338   @param offset Byte offset into the file.
339   @param buffers The buffer sequence to write from. 339   @param buffers The buffer sequence to write from.
340   340  
341   @return An awaitable yielding `(error_code, std::size_t)`. 341   @return An awaitable yielding `(error_code, std::size_t)`.
342   342  
343   A closed file reports `errc::bad_file_descriptor`. 343   A closed file reports `errc::bad_file_descriptor`.
344   */ 344   */
345   template<capy::ConstBufferSequence CB> 345   template<capy::ConstBufferSequence CB>
HITCBC 346   134 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 346   134 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
347   { 347   {
HITCBC 348   134 write_some_at_awaitable<CB> aw(*this, offset, buffers); 348   134 write_some_at_awaitable<CB> aw(*this, offset, buffers);
HITCBC 349   134 if (!is_open()) 349   134 if (!is_open())
HITCBC 350   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor); 350   2 aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
HITCBC 351   134 return aw; 351   134 return aw;
352   } 352   }
353   353  
354   /** Cancel pending asynchronous operations. */ 354   /** Cancel pending asynchronous operations. */
355   void cancel() noexcept; 355   void cancel() noexcept;
356   356  
357   /** Get the native file descriptor or handle. */ 357   /** Get the native file descriptor or handle. */
358   native_handle_type native_handle() const noexcept; 358   native_handle_type native_handle() const noexcept;
359   359  
360   /** Return the file size in bytes. 360   /** Return the file size in bytes.
361   361  
362   A closed file reports `errc::bad_file_descriptor`. Other 362   A closed file reports `errc::bad_file_descriptor`. Other
363   failures carry the error code from the operating system. On 363   failures carry the error code from the operating system. On
364   failure the size is zero. 364   failure the size is zero.
365   365  
366   @return The error code and the file size in bytes. 366   @return The error code and the file size in bytes.
367   */ 367   */
368   [[nodiscard]] capy::results<std::uint64_t> size() const noexcept; 368   [[nodiscard]] capy::results<std::uint64_t> size() const noexcept;
369   369  
370   /** Resize the file to @p new_size bytes. 370   /** Resize the file to @p new_size bytes.
371   371  
372   Failures such as insufficient disk space are reported 372   Failures such as insufficient disk space are reported
373   through the returned error code. A closed file reports 373   through the returned error code. A closed file reports
374   `errc::bad_file_descriptor`. 374   `errc::bad_file_descriptor`.
375   375  
376   @param new_size The new file size. 376   @param new_size The new file size.
377   377  
378   @return The error code, empty on success. 378   @return The error code, empty on success.
379   */ 379   */
380   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept; 380   [[nodiscard]] std::error_code resize(std::uint64_t new_size) noexcept;
381   381  
382   /** Synchronize file data to stable storage. 382   /** Synchronize file data to stable storage.
383   383  
384   Write-back failures such as device I/O errors surface here 384   Write-back failures such as device I/O errors surface here
385   and are reported through the returned error code. A closed 385   and are reported through the returned error code. A closed
386   file reports `errc::bad_file_descriptor`. 386   file reports `errc::bad_file_descriptor`.
387   387  
388   @return The error code, empty on success. 388   @return The error code, empty on success.
389   */ 389   */
390   [[nodiscard]] std::error_code sync_data() noexcept; 390   [[nodiscard]] std::error_code sync_data() noexcept;
391   391  
392   /** Synchronize file data and metadata to stable storage. 392   /** Synchronize file data and metadata to stable storage.
393   393  
394   Write-back failures such as device I/O errors surface here 394   Write-back failures such as device I/O errors surface here
395   and are reported through the returned error code. A closed 395   and are reported through the returned error code. A closed
396   file reports `errc::bad_file_descriptor`. 396   file reports `errc::bad_file_descriptor`.
397   397  
398   @return The error code, empty on success. 398   @return The error code, empty on success.
399   */ 399   */
400   [[nodiscard]] std::error_code sync_all() noexcept; 400   [[nodiscard]] std::error_code sync_all() noexcept;
401   401  
402   /** Release ownership of the native handle. 402   /** Release ownership of the native handle.
403   403  
404   On success the file object becomes not-open, and the caller 404   On success the file object becomes not-open, and the caller
405   is responsible for closing the returned handle. 405   is responsible for closing the returned handle.
406   406  
407   `release()` cancels pending operations first. It reports 407   `release()` cancels pending operations first. It reports
408   `errc::device_or_resource_busy` if one is still in flight on 408   `errc::device_or_resource_busy` if one is still in flight on
409   Windows. Where file I/O runs on a thread pool, it reports the 409   Windows. Where file I/O runs on a thread pool, it reports the
410   same while a worker thread is already performing one. Call 410   same while a worker thread is already performing one. Call
411   `release()` again once the cancelled operations have completed. On 411   `release()` again once the cancelled operations have completed. On
412   Windows it reports `errc::operation_not_supported` if the 412   Windows it reports `errc::operation_not_supported` if the
413   handle cannot be detached from the execution context's 413   handle cannot be detached from the execution context's
414   completion port. Detaching requires Windows 8.1 or later. 414   completion port. Detaching requires Windows 8.1 or later.
415   415  
416   A closed file reports `errc::bad_file_descriptor`. On 416   A closed file reports `errc::bad_file_descriptor`. On
417   failure the object keeps the handle and the returned handle 417   failure the object keeps the handle and the returned handle
418   is the invalid sentinel ( -1 or `INVALID_HANDLE_VALUE` ), never 418   is the invalid sentinel ( -1 or `INVALID_HANDLE_VALUE` ), never
419   zero. 419   zero.
420   420  
421   @return The error code and the native file descriptor or handle. 421   @return The error code and the native file descriptor or handle.
422   */ 422   */
423   [[nodiscard]] capy::results<native_handle_type> release() noexcept; 423   [[nodiscard]] capy::results<native_handle_type> release() noexcept;
424   424  
425   /** Adopt an existing native handle. 425   /** Adopt an existing native handle.
426   426  
427   The object must be closed. To replace a held file, `close()` 427   The object must be closed. To replace a held file, `close()`
428   or `release()` it first. On success the object takes 428   or `release()` it first. On success the object takes
429   ownership of @p handle. Handles created elsewhere may be 429   ownership of @p handle. Handles created elsewhere may be
430   unsuitable for asynchronous I/O. `assign()` reports most such 430   unsuitable for asynchronous I/O. `assign()` reports most such
431   failures through the returned error code. 431   failures through the returned error code.
432   432  
433   @param handle The native file descriptor or handle. 433   @param handle The native file descriptor or handle.
434   434  
435   @return An error code describing the outcome. 435   @return An error code describing the outcome.
436   `error::already_open` if this object is open. 436   `error::already_open` if this object is open.
437   `errc::bad_file_descriptor` if @p handle is invalid. 437   `errc::bad_file_descriptor` if @p handle is invalid.
438   `errc::operation_not_supported` if a file object cannot 438   `errc::operation_not_supported` if a file object cannot
439   use it. On Windows, the rejected handles are a pipe, a 439   use it. On Windows, the rejected handles are a pipe, a
440   socket, a console, a directory, a handle opened without 440   socket, a console, a directory, a handle opened without
441   `FILE_FLAG_OVERLAPPED`, or one 441   `FILE_FLAG_OVERLAPPED`, or one
442   already in skip-completion-port-on-success mode. On 442   already in skip-completion-port-on-success mode. On
443   Windows, `errc::invalid_argument` when @p handle is 443   Windows, `errc::invalid_argument` when @p handle is
444   bound to another completion port. Any other failure is 444   bound to another completion port. Any other failure is
445   the code reported by the system. Otherwise, the code is 445   the code reported by the system. Otherwise, the code is
446   empty. 446   empty.
447   447  
448   @par Exception Safety 448   @par Exception Safety
449   Throws nothing. On failure the object is unchanged and the 449   Throws nothing. On failure the object is unchanged and the
450   caller still owns @p handle. 450   caller still owns @p handle.
451   451  
452   @note On POSIX, the rejected descriptors are, in practice, a 452   @note On POSIX, the rejected descriptors are, in practice, a
453   directory, a pipe, a socket, or any other anonymous inode. 453   directory, a pipe, a socket, or any other anonymous inode.
454   Adopt a pipe, a socket, or an anonymous inode into a 454   Adopt a pipe, a socket, or an anonymous inode into a
455   @ref posix_stream_descriptor instead. `assign()` accepts a 455   @ref posix_stream_descriptor instead. `assign()` accepts a
456   non-seekable character device such as a tty. Its first 456   non-seekable character device such as a tty. Its first
457   read or write then fails with `ESPIPE` on the epoll, 457   read or write then fails with `ESPIPE` on the epoll,
458   kqueue, and select I/O backends. 458   kqueue, and select I/O backends.
459   459  
460   @see @ref release 460   @see @ref release
461   */ 461   */
462   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept; 462   [[nodiscard]] std::error_code assign(native_handle_type handle) noexcept;
463   463  
464   protected: 464   protected:
465   /** Construct from a pre-built handle (for `native_random_access_file`). 465   /** Construct from a pre-built handle (for `native_random_access_file`).
466   466  
467   @param h The pre-built handle to adopt. 467   @param h The pre-built handle to adopt.
468   */ 468   */
HITCBC 469   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {} 469   16 explicit random_access_file(handle h) noexcept : io_object(std::move(h)) {}
470   470  
471   private: 471   private:
HITCBC 472   2127 inline implementation& get() const noexcept 472   2127 inline implementation& get() const noexcept
473   { 473   {
HITCBC 474   2127 return *static_cast<implementation*>(h_.get()); 474   2127 return *static_cast<implementation*>(h_.get());
475   } 475   }
476   }; 476   };
477   477  
478   } // namespace boost::corosio 478   } // namespace boost::corosio
479   479  
480   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP 480   #endif // BOOST_COROSIO_RANDOM_ACCESS_FILE_HPP