95.12% Lines (39/41) 100.00% Functions (12/12)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2026 Steve Gerbino 2   // Copyright (c) 2026 Steve Gerbino
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_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 10   #ifndef BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
11   #define BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 11   #define BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP
12   12  
13   #include <boost/corosio/random_access_file.hpp> 13   #include <boost/corosio/random_access_file.hpp>
14   #include <boost/corosio/backend.hpp> 14   #include <boost/corosio/backend.hpp>
15   15  
16   #ifndef BOOST_COROSIO_MRDOCS 16   #ifndef BOOST_COROSIO_MRDOCS
17   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \ 17   #if BOOST_COROSIO_HAS_EPOLL || BOOST_COROSIO_HAS_SELECT || \
18   BOOST_COROSIO_HAS_KQUEUE 18   BOOST_COROSIO_HAS_KQUEUE
19   #include <boost/corosio/native/detail/posix/posix_random_access_file_service.hpp> 19   #include <boost/corosio/native/detail/posix/posix_random_access_file_service.hpp>
20   #endif 20   #endif
21   21  
22   #if BOOST_COROSIO_HAS_IO_URING 22   #if BOOST_COROSIO_HAS_IO_URING
23   #include <boost/corosio/native/detail/io_uring/io_uring_random_access_file.hpp> 23   #include <boost/corosio/native/detail/io_uring/io_uring_random_access_file.hpp>
24   #endif 24   #endif
25   25  
26   #if BOOST_COROSIO_HAS_IOCP 26   #if BOOST_COROSIO_HAS_IOCP
27   #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp> 27   #include <boost/corosio/native/detail/iocp/win_random_access_file_service.hpp>
28   #endif 28   #endif
29   #endif // !BOOST_COROSIO_MRDOCS 29   #endif // !BOOST_COROSIO_MRDOCS
30   30  
31   namespace boost::corosio { 31   namespace boost::corosio {
32   32  
33   /** A random-access file with devirtualized async I/O operations. 33   /** A random-access file with devirtualized async I/O operations.
34   34  
35   This class template inherits from @ref random_access_file and 35   This class template inherits from @ref random_access_file and
36   shadows `read_some_at` / `write_some_at` with versions that 36   shadows `read_some_at` / `write_some_at` with versions that
37   call the backend implementation directly, allowing the compiler 37   call the backend implementation directly, allowing the compiler
38   to inline through the entire call chain. 38   to inline through the entire call chain.
39   39  
40   Non-async operations (`open`, `close`, `size`, `resize`, 40   Non-async operations (`open`, `close`, `size`, `resize`,
41   `sync_data`, `sync_all`) remain unchanged and dispatch through 41   `sync_data`, `sync_all`) remain unchanged and dispatch through
42   the compiled library. 42   the compiled library.
43   43  
44   A `native_random_access_file` IS-A `random_access_file` and 44   A `native_random_access_file` IS-A `random_access_file` and
45   can be passed to any function expecting `random_access_file&`, 45   can be passed to any function expecting `random_access_file&`,
46   in which case virtual dispatch is used transparently. 46   in which case virtual dispatch is used transparently.
47   47  
48   @note On POSIX platforms, file I/O is dispatched to a thread 48   @note On POSIX platforms, file I/O is dispatched to a thread
49   pool regardless of the chosen reactor backend, so all three 49   pool regardless of the chosen reactor backend, so all three
50   reactor tags (`epoll`, `select`, `kqueue`) resolve to the same 50   reactor tags (`epoll`, `select`, `kqueue`) resolve to the same
51   underlying implementation. The `Backend` template parameter 51   underlying implementation. The `Backend` template parameter
52   exists for API symmetry with @ref native_tcp_socket and friends. 52   exists for API symmetry with @ref native_tcp_socket and friends.
53   The vtable savings are smaller relative to the thread-pool / 53   The vtable savings are smaller relative to the thread-pool /
54   overlapped-I/O cost than they are for socket operations. 54   overlapped-I/O cost than they are for socket operations.
55   55  
56   @tparam Backend A backend tag value (e.g., `epoll`, `iocp`). 56   @tparam Backend A backend tag value (e.g., `epoll`, `iocp`).
57   57  
58   @par Thread Safety 58   @par Thread Safety
59   Same as @ref random_access_file. 59   Same as @ref random_access_file.
60   60  
61   @par Example 61   @par Example
62   @code 62   @code
63   #include <boost/corosio/native/native_random_access_file.hpp> 63   #include <boost/corosio/native/native_random_access_file.hpp>
64   64  
65   native_io_context<epoll> ctx; 65   native_io_context<epoll> ctx;
66   native_random_access_file<epoll> f(ctx); 66   native_random_access_file<epoll> f(ctx);
67   if (auto ec = f.open("data.bin", file_base::read_only)) 67   if (auto ec = f.open("data.bin", file_base::read_only))
68   co_return; 68   co_return;
69   char buf[4096]; 69   char buf[4096];
70   auto [ec, n] = co_await f.read_some_at( 70   auto [ec, n] = co_await f.read_some_at(
71   0, capy::mutable_buffer(buf, sizeof(buf))); 71   0, capy::mutable_buffer(buf, sizeof(buf)));
72   @endcode 72   @endcode
73   73  
74   @see random_access_file, epoll_t, iocp_t 74   @see random_access_file, epoll_t, iocp_t
75   */ 75   */
76   template<auto Backend> 76   template<auto Backend>
77   class native_random_access_file : public random_access_file 77   class native_random_access_file : public random_access_file
78   { 78   {
79   using backend_type = decltype(Backend); 79   using backend_type = decltype(Backend);
80   using impl_type = typename backend_type::random_access_file_type; 80   using impl_type = typename backend_type::random_access_file_type;
81   using service_type = 81   using service_type =
82   typename backend_type::random_access_file_service_type; 82   typename backend_type::random_access_file_service_type;
83   83  
HITCBC 84   10 impl_type& get_impl() noexcept 84   10 impl_type& get_impl() noexcept
85   { 85   {
HITCBC 86   10 return *static_cast<impl_type*>(h_.get()); 86   10 return *static_cast<impl_type*>(h_.get());
87   } 87   }
88   88  
89   template<class MutableBufferSequence> 89   template<class MutableBufferSequence>
90   struct native_read_at_awaitable 90   struct native_read_at_awaitable
91   { 91   {
92   native_random_access_file& self_; 92   native_random_access_file& self_;
93   std::uint64_t offset_; 93   std::uint64_t offset_;
94   MutableBufferSequence buffers_; 94   MutableBufferSequence buffers_;
95   std::stop_token token_; 95   std::stop_token token_;
96   mutable std::error_code ec_; 96   mutable std::error_code ec_;
97   mutable std::size_t bytes_transferred_ = 0; 97   mutable std::size_t bytes_transferred_ = 0;
98   98  
HITCBC 99   6 native_read_at_awaitable( 99   6 native_read_at_awaitable(
100   native_random_access_file& self, 100   native_random_access_file& self,
101   std::uint64_t offset, 101   std::uint64_t offset,
102   MutableBufferSequence buffers) noexcept 102   MutableBufferSequence buffers) noexcept
HITCBC 103   6 : self_(self) 103   6 : self_(self)
HITCBC 104   6 , offset_(offset) 104   6 , offset_(offset)
HITCBC 105   6 , buffers_(std::move(buffers)) 105   6 , buffers_(std::move(buffers))
106   { 106   {
HITCBC 107   6 } 107   6 }
108   108  
HITCBC 109   6 bool await_ready() const noexcept 109   6 bool await_ready() const noexcept
110   { 110   {
111   // A pre-set ec_ means the initiator failed before 111   // A pre-set ec_ means the initiator failed before
112   // dispatch (e.g. a closed object). 112   // dispatch (e.g. a closed object).
HITCBC 113   6 return static_cast<bool>(ec_) || token_.stop_requested(); 113   6 return static_cast<bool>(ec_) || token_.stop_requested();
114   } 114   }
115   115  
HITCBC 116   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 116   6 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
117   { 117   {
HITCBC 118   6 if (token_.stop_requested()) 118   6 if (token_.stop_requested())
MISUBC 119   return {make_error_code(std::errc::operation_canceled), 0}; 119   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 120   6 return {ec_, bytes_transferred_}; 120   6 return {ec_, bytes_transferred_};
121   } 121   }
122   122  
HITCBC 123   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 123   6 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
124   -> std::coroutine_handle<> 124   -> std::coroutine_handle<>
125   { 125   {
HITCBC 126   6 token_ = env->stop_token; 126   6 token_ = env->stop_token;
HITCBC 127   18 return self_.get_impl().read_some_at( 127   18 return self_.get_impl().read_some_at(
HITCBC 128   6 offset_, h, env->executor, buffers_, 128   6 offset_, h, env->executor, buffers_,
HITCBC 129   18 token_, &ec_, &bytes_transferred_); 129   18 token_, &ec_, &bytes_transferred_);
130   } 130   }
131   }; 131   };
132   132  
133   template<class ConstBufferSequence> 133   template<class ConstBufferSequence>
134   struct native_write_at_awaitable 134   struct native_write_at_awaitable
135   { 135   {
136   native_random_access_file& self_; 136   native_random_access_file& self_;
137   std::uint64_t offset_; 137   std::uint64_t offset_;
138   ConstBufferSequence buffers_; 138   ConstBufferSequence buffers_;
139   std::stop_token token_; 139   std::stop_token token_;
140   mutable std::error_code ec_; 140   mutable std::error_code ec_;
141   mutable std::size_t bytes_transferred_ = 0; 141   mutable std::size_t bytes_transferred_ = 0;
142   142  
HITCBC 143   4 native_write_at_awaitable( 143   4 native_write_at_awaitable(
144   native_random_access_file& self, 144   native_random_access_file& self,
145   std::uint64_t offset, 145   std::uint64_t offset,
146   ConstBufferSequence buffers) noexcept 146   ConstBufferSequence buffers) noexcept
HITCBC 147   4 : self_(self) 147   4 : self_(self)
HITCBC 148   4 , offset_(offset) 148   4 , offset_(offset)
HITCBC 149   4 , buffers_(std::move(buffers)) 149   4 , buffers_(std::move(buffers))
150   { 150   {
HITCBC 151   4 } 151   4 }
152   152  
HITCBC 153   4 bool await_ready() const noexcept 153   4 bool await_ready() const noexcept
154   { 154   {
155   // A pre-set ec_ means the initiator failed before 155   // A pre-set ec_ means the initiator failed before
156   // dispatch (e.g. a closed object). 156   // dispatch (e.g. a closed object).
HITCBC 157   4 return static_cast<bool>(ec_) || token_.stop_requested(); 157   4 return static_cast<bool>(ec_) || token_.stop_requested();
158   } 158   }
159   159  
HITCBC 160   4 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept 160   4 [[nodiscard]] capy::io_result<std::size_t> await_resume() const noexcept
161   { 161   {
HITCBC 162   4 if (token_.stop_requested()) 162   4 if (token_.stop_requested())
MISUBC 163   return {make_error_code(std::errc::operation_canceled), 0}; 163   return {make_error_code(std::errc::operation_canceled), 0};
HITCBC 164   4 return {ec_, bytes_transferred_}; 164   4 return {ec_, bytes_transferred_};
165   } 165   }
166   166  
HITCBC 167   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env) 167   4 auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env)
168   -> std::coroutine_handle<> 168   -> std::coroutine_handle<>
169   { 169   {
HITCBC 170   4 token_ = env->stop_token; 170   4 token_ = env->stop_token;
HITCBC 171   12 return self_.get_impl().write_some_at( 171   12 return self_.get_impl().write_some_at(
HITCBC 172   4 offset_, h, env->executor, buffers_, 172   4 offset_, h, env->executor, buffers_,
HITCBC 173   12 token_, &ec_, &bytes_transferred_); 173   12 token_, &ec_, &bytes_transferred_);
174   } 174   }
175   }; 175   };
176   176  
177   public: 177   public:
178   /** Construct a native random-access file from an execution context. 178   /** Construct a native random-access file from an execution context.
179   179  
180   @param ctx The execution context that will own this file. 180   @param ctx The execution context that will own this file.
181   */ 181   */
HITCBC 182   12 explicit native_random_access_file(capy::execution_context& ctx) 182   12 explicit native_random_access_file(capy::execution_context& ctx)
HITCBC 183   12 : random_access_file(create_handle<service_type>(ctx)) 183   12 : random_access_file(create_handle<service_type>(ctx))
184   { 184   {
HITCBC 185   12 } 185   12 }
186   186  
187   /** Construct a native random-access file from an executor. 187   /** Construct a native random-access file from an executor.
188   188  
189   @param ex The executor whose context will own this file. 189   @param ex The executor whose context will own this file.
190   */ 190   */
191   template<class Ex> 191   template<class Ex>
192   requires(!std::same_as< 192   requires(!std::same_as<
193   std::remove_cvref_t<Ex>, 193   std::remove_cvref_t<Ex>,
194   native_random_access_file>) && 194   native_random_access_file>) &&
195   capy::Executor<Ex> 195   capy::Executor<Ex>
196   explicit native_random_access_file(Ex const& ex) 196   explicit native_random_access_file(Ex const& ex)
197   : native_random_access_file(ex.context()) 197   : native_random_access_file(ex.context())
198   { 198   {
199   } 199   }
200   200  
201   /// Move construct. 201   /// Move construct.
202   native_random_access_file(native_random_access_file&&) noexcept = default; 202   native_random_access_file(native_random_access_file&&) noexcept = default;
203   203  
204   /// Move assign. 204   /// Move assign.
205   native_random_access_file& 205   native_random_access_file&
206   operator=(native_random_access_file&&) noexcept = default; 206   operator=(native_random_access_file&&) noexcept = default;
207   207  
208   native_random_access_file(native_random_access_file const&) = delete; 208   native_random_access_file(native_random_access_file const&) = delete;
209   native_random_access_file& 209   native_random_access_file&
210   operator=(native_random_access_file const&) = delete; 210   operator=(native_random_access_file const&) = delete;
211   211  
212   /** Asynchronously read at the given offset. 212   /** Asynchronously read at the given offset.
213   213  
214   Calls the backend implementation directly, bypassing virtual 214   Calls the backend implementation directly, bypassing virtual
215   dispatch. Otherwise identical to @ref random_access_file::read_some_at. 215   dispatch. Otherwise identical to @ref random_access_file::read_some_at.
216   */ 216   */
217   template<capy::MutableBufferSequence MB> 217   template<capy::MutableBufferSequence MB>
HITCBC 218   6 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers) 218   6 [[nodiscard]] auto read_some_at(std::uint64_t offset, MB const& buffers)
219   { 219   {
HITCBC 220   6 return native_read_at_awaitable<MB>(*this, offset, buffers); 220   6 return native_read_at_awaitable<MB>(*this, offset, buffers);
221   } 221   }
222   222  
223   /** Asynchronously write at the given offset. 223   /** Asynchronously write at the given offset.
224   224  
225   Calls the backend implementation directly, bypassing virtual 225   Calls the backend implementation directly, bypassing virtual
226   dispatch. Otherwise identical to @ref random_access_file::write_some_at. 226   dispatch. Otherwise identical to @ref random_access_file::write_some_at.
227   */ 227   */
228   template<capy::ConstBufferSequence CB> 228   template<capy::ConstBufferSequence CB>
HITCBC 229   4 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers) 229   4 [[nodiscard]] auto write_some_at(std::uint64_t offset, CB const& buffers)
230   { 230   {
HITCBC 231   4 return native_write_at_awaitable<CB>(*this, offset, buffers); 231   4 return native_write_at_awaitable<CB>(*this, offset, buffers);
232   } 232   }
233   }; 233   };
234   234  
235   } // namespace boost::corosio 235   } // namespace boost::corosio
236   236  
237   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP 237   #endif // BOOST_COROSIO_NATIVE_NATIVE_RANDOM_ACCESS_FILE_HPP