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