100.00% Lines (12/12) 100.00% Functions (5/5)
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_STREAM_FILE_HPP 10   #ifndef BOOST_COROSIO_STREAM_FILE_HPP
11   #define BOOST_COROSIO_STREAM_FILE_HPP 11   #define BOOST_COROSIO_STREAM_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/file_base.hpp> 17   #include <boost/corosio/file_base.hpp>
18   #include <boost/corosio/io/io_stream.hpp> 18   #include <boost/corosio/io/io_stream.hpp>
19   #include <boost/capy/ex/execution_context.hpp> 19   #include <boost/capy/ex/execution_context.hpp>
20   #include <boost/capy/concept/executor.hpp> 20   #include <boost/capy/concept/executor.hpp>
21   21  
22   #include <concepts> 22   #include <concepts>
23   #include <cstdint> 23   #include <cstdint>
24   #include <filesystem> 24   #include <filesystem>
25   25  
26   namespace boost::corosio { 26   namespace boost::corosio {
27   27  
28   /** An asynchronous sequential file for coroutine I/O. 28   /** An asynchronous sequential file for coroutine I/O.
29   29  
30   Provides asynchronous read and write operations on a regular 30   Provides asynchronous read and write operations on a regular
31   file with an implicit position that advances after each 31   file with an implicit position that advances after each
32   operation. 32   operation.
33   33  
34   Inherits from @ref io_stream, so `read_some` and `write_some` 34   Inherits from @ref io_stream, so `read_some` and `write_some`
35   are available and work with any algorithm that accepts an 35   are available and work with any algorithm that accepts an
36   `io_stream&`. 36   `io_stream&`.
37   37  
38   On POSIX platforms, file I/O is dispatched to a thread pool 38   On POSIX platforms, file I/O is dispatched to a thread pool
39   (blocking `preadv`/`pwritev`) with completion posted back to 39   (blocking `preadv`/`pwritev`) with completion posted back to
40   the scheduler. On Windows, true overlapped I/O is used via IOCP. 40   the scheduler. On Windows, true overlapped I/O is used via IOCP.
41   41  
42   @par Thread Safety 42   @par Thread Safety
43   Distinct objects: Safe.@n 43   Distinct objects: Safe.@n
44   Shared objects: Unsafe. Only one asynchronous operation 44   Shared objects: Unsafe. Only one asynchronous operation
45   may be in flight at a time. 45   may be in flight at a time.
46   46  
47   @par Example 47   @par Example
48   @code 48   @code
49   io_context ioc; 49   io_context ioc;
50   stream_file f(ioc); 50   stream_file f(ioc);
51   f.open("data.bin", file_base::read_only); 51   f.open("data.bin", file_base::read_only);
52   52  
53   char buf[4096]; 53   char buf[4096];
54   auto [ec, n] = co_await f.read_some( 54   auto [ec, n] = co_await f.read_some(
55   capy::mutable_buffer(buf, sizeof(buf))); 55   capy::mutable_buffer(buf, sizeof(buf)));
56   if (ec == capy::cond::eof) 56   if (ec == capy::cond::eof)
57   // end of file 57   // end of file
58   @endcode 58   @endcode
59   */ 59   */
60   class BOOST_COROSIO_DECL stream_file : public io_stream 60   class BOOST_COROSIO_DECL stream_file : public io_stream
61   { 61   {
62   public: 62   public:
63   /** Platform-specific file implementation interface. 63   /** Platform-specific file implementation interface.
64   64  
65   Backends derive from this to provide file I/O. 65   Backends derive from this to provide file I/O.
66   `read_some` and `write_some` are inherited from 66   `read_some` and `write_some` are inherited from
67   @ref io_stream::implementation. 67   @ref io_stream::implementation.
68   */ 68   */
69   struct implementation : io_stream::implementation 69   struct implementation : io_stream::implementation
70   { 70   {
71   /// Return the platform file descriptor or handle. 71   /// Return the platform file descriptor or handle.
72   virtual native_handle_type native_handle() const noexcept = 0; 72   virtual native_handle_type native_handle() const noexcept = 0;
73   73  
74   /// Cancel pending asynchronous operations. 74   /// Cancel pending asynchronous operations.
75   virtual void cancel() noexcept = 0; 75   virtual void cancel() noexcept = 0;
76   76  
77   /// Return the file size in bytes. 77   /// Return the file size in bytes.
78   virtual std::uint64_t size() const = 0; 78   virtual std::uint64_t size() const = 0;
79   79  
80   /// Resize the file to @p new_size bytes. 80   /// Resize the file to @p new_size bytes.
81   virtual void resize(std::uint64_t new_size) = 0; 81   virtual void resize(std::uint64_t new_size) = 0;
82   82  
83   /// Synchronize file data to stable storage. 83   /// Synchronize file data to stable storage.
84   virtual void sync_data() = 0; 84   virtual void sync_data() = 0;
85   85  
86   /// Synchronize file data and metadata to stable storage. 86   /// Synchronize file data and metadata to stable storage.
87   virtual void sync_all() = 0; 87   virtual void sync_all() = 0;
88   88  
89   /// Release ownership of the native handle. 89   /// Release ownership of the native handle.
90   virtual native_handle_type release() = 0; 90   virtual native_handle_type release() = 0;
91   91  
92   /// Adopt an existing native handle. 92   /// Adopt an existing native handle.
93   virtual void assign(native_handle_type handle) = 0; 93   virtual void assign(native_handle_type handle) = 0;
94   94  
95   /** Move the file position. 95   /** Move the file position.
96   96  
97   @param offset Signed offset from @p origin. 97   @param offset Signed offset from @p origin.
98   @param origin The reference point for the seek. 98   @param origin The reference point for the seek.
99   @return The new absolute position. 99   @return The new absolute position.
100   */ 100   */
101   virtual std::uint64_t 101   virtual std::uint64_t
102   seek(std::int64_t offset, file_base::seek_basis origin) = 0; 102   seek(std::int64_t offset, file_base::seek_basis origin) = 0;
103   }; 103   };
104   104  
105   /** Destructor. 105   /** Destructor.
106   106  
107   Closes the file if open, cancelling any pending operations. 107   Closes the file if open, cancelling any pending operations.
108   */ 108   */
109   ~stream_file() override; 109   ~stream_file() override;
110   110  
111   /** Construct from an execution context. 111   /** Construct from an execution context.
112   112  
113   @param ctx The execution context that will own this file. 113   @param ctx The execution context that will own this file.
114   */ 114   */
115   explicit stream_file(capy::execution_context& ctx); 115   explicit stream_file(capy::execution_context& ctx);
116   116  
117   /** Construct from an executor. 117   /** Construct from an executor.
118   118  
119   @param ex The executor whose context will own this file. 119   @param ex The executor whose context will own this file.
120   */ 120   */
121   template<class Ex> 121   template<class Ex>
122   requires(!std::same_as<std::remove_cvref_t<Ex>, stream_file>) && 122   requires(!std::same_as<std::remove_cvref_t<Ex>, stream_file>) &&
123   capy::Executor<Ex> 123   capy::Executor<Ex>
HITCBC 124   1 explicit stream_file(Ex const& ex) : stream_file(ex.context()) 124   1 explicit stream_file(Ex const& ex) : stream_file(ex.context())
125   { 125   {
HITCBC 126   1 } 126   1 }
127   127  
128   /** Move constructor. 128   /** Move constructor.
129   129  
130   Transfers ownership of the file resources. 130   Transfers ownership of the file resources.
131   */ 131   */
HITCBC 132   1 stream_file(stream_file&& other) noexcept : io_object(std::move(other)) {} 132   1 stream_file(stream_file&& other) noexcept : io_object(std::move(other)) {}
133   133  
134   /** Move assignment operator. 134   /** Move assignment operator.
135   135  
136   Closes any existing file and transfers ownership. 136   Closes any existing file and transfers ownership.
137   */ 137   */
HITCBC 138   1 stream_file& operator=(stream_file&& other) noexcept 138   1 stream_file& operator=(stream_file&& other) noexcept
139   { 139   {
HITCBC 140   1 if (this != &other) 140   1 if (this != &other)
141   { 141   {
HITCBC 142   1 close(); 142   1 close();
HITCBC 143   1 h_ = std::move(other.h_); 143   1 h_ = std::move(other.h_);
144   } 144   }
HITCBC 145   1 return *this; 145   1 return *this;
146   } 146   }
147   147  
148   stream_file(stream_file const&) = delete; 148   stream_file(stream_file const&) = delete;
149   stream_file& operator=(stream_file const&) = delete; 149   stream_file& operator=(stream_file const&) = delete;
150   150  
151   // read_some() inherited from io_read_stream 151   // read_some() inherited from io_read_stream
152   // write_some() inherited from io_write_stream 152   // write_some() inherited from io_write_stream
153   153  
154   /** Open a file. 154   /** Open a file.
155   155  
156   @param path The filesystem path to open. 156   @param path The filesystem path to open.
157   @param mode Bitmask of @ref file_base::flags specifying 157   @param mode Bitmask of @ref file_base::flags specifying
158   access mode and creation behavior. 158   access mode and creation behavior.
159   159  
160   @throws std::system_error on failure. 160   @throws std::system_error on failure.
161   */ 161   */
162   void open( 162   void open(
163   std::filesystem::path const& path, 163   std::filesystem::path const& path,
164   file_base::flags mode = file_base::read_only); 164   file_base::flags mode = file_base::read_only);
165   165  
166   /** Close the file. 166   /** Close the file.
167   167  
168   Releases file resources. Any pending operations complete 168   Releases file resources. Any pending operations complete
169   with `errc::operation_canceled`. 169   with `errc::operation_canceled`.
170   */ 170   */
171   void close(); 171   void close();
172   172  
173   /** Check if the file is open. 173   /** Check if the file is open.
174   174  
175   @return `true` if the file is open and ready for I/O. 175   @return `true` if the file is open and ready for I/O.
176   */ 176   */
HITCBC 177   83 bool is_open() const noexcept 177   83 bool is_open() const noexcept
178   { 178   {
179   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS) 179   #if BOOST_COROSIO_HAS_IOCP && !defined(BOOST_COROSIO_MRDOCS)
180   return h_ && get().native_handle() != ~native_handle_type(0); 180   return h_ && get().native_handle() != ~native_handle_type(0);
181   #else 181   #else
HITCBC 182   83 return h_ && get().native_handle() >= 0; 182   83 return h_ && get().native_handle() >= 0;
183   #endif 183   #endif
184   } 184   }
185   185  
186   /** Cancel pending asynchronous operations. 186   /** Cancel pending asynchronous operations.
187   187  
188   All outstanding operations complete with 188   All outstanding operations complete with
189   `errc::operation_canceled`. 189   `errc::operation_canceled`.
190   */ 190   */
191   void cancel(); 191   void cancel();
192   192  
193   /** Get the native file descriptor or handle. 193   /** Get the native file descriptor or handle.
194   194  
195   @return The native handle, or -1/INVALID_HANDLE_VALUE 195   @return The native handle, or -1/INVALID_HANDLE_VALUE
196   if not open. 196   if not open.
197   */ 197   */
198   native_handle_type native_handle() const noexcept; 198   native_handle_type native_handle() const noexcept;
199   199  
200   /** Return the file size in bytes. 200   /** Return the file size in bytes.
201   201  
202   @throws std::system_error on failure. 202   @throws std::system_error on failure.
203   */ 203   */
204   std::uint64_t size() const; 204   std::uint64_t size() const;
205   205  
206   /** Resize the file to @p new_size bytes. 206   /** Resize the file to @p new_size bytes.
207   207  
208   @param new_size The new file size. 208   @param new_size The new file size.
209   @throws std::system_error on failure. 209   @throws std::system_error on failure.
210   */ 210   */
211   void resize(std::uint64_t new_size); 211   void resize(std::uint64_t new_size);
212   212  
213   /** Synchronize file data to stable storage. 213   /** Synchronize file data to stable storage.
214   214  
215   @throws std::system_error on failure. 215   @throws std::system_error on failure.
216   */ 216   */
217   void sync_data(); 217   void sync_data();
218   218  
219   /** Synchronize file data and metadata to stable storage. 219   /** Synchronize file data and metadata to stable storage.
220   220  
221   @throws std::system_error on failure. 221   @throws std::system_error on failure.
222   */ 222   */
223   void sync_all(); 223   void sync_all();
224   224  
225   /** Release ownership of the native handle. 225   /** Release ownership of the native handle.
226   226  
227   The file object becomes not-open. The caller is 227   The file object becomes not-open. The caller is
228   responsible for closing the returned handle. 228   responsible for closing the returned handle.
229   229  
230   @return The native file descriptor or handle. 230   @return The native file descriptor or handle.
231   */ 231   */
232   native_handle_type release(); 232   native_handle_type release();
233   233  
234   /** Adopt an existing native handle. 234   /** Adopt an existing native handle.
235   235  
236   Closes any currently open file before adopting. 236   Closes any currently open file before adopting.
237   The file object takes ownership of the handle. 237   The file object takes ownership of the handle.
238   238  
239   @param handle The native file descriptor or handle. 239   @param handle The native file descriptor or handle.
240   @throws std::system_error on failure. 240   @throws std::system_error on failure.
241   */ 241   */
242   void assign(native_handle_type handle); 242   void assign(native_handle_type handle);
243   243  
244   /** Move the file position. 244   /** Move the file position.
245   245  
246   @param offset Signed offset from @p origin. 246   @param offset Signed offset from @p origin.
247   @param origin The reference point for the seek. 247   @param origin The reference point for the seek.
248   @return The new absolute position. 248   @return The new absolute position.
249   @throws std::system_error on failure. 249   @throws std::system_error on failure.
250   */ 250   */
251   std::uint64_t 251   std::uint64_t
252   seek(std::int64_t offset, 252   seek(std::int64_t offset,
253   file_base::seek_basis origin = file_base::seek_set); 253   file_base::seek_basis origin = file_base::seek_set);
254   254  
255   private: 255   private:
HITCBC 256   116 inline implementation& get() const noexcept 256   116 inline implementation& get() const noexcept
257   { 257   {
HITCBC 258   116 return *static_cast<implementation*>(h_.get()); 258   116 return *static_cast<implementation*>(h_.get());
259   } 259   }
260   }; 260   };
261   261  
262   } // namespace boost::corosio 262   } // namespace boost::corosio
263   263  
264   #endif // BOOST_COROSIO_STREAM_FILE_HPP 264   #endif // BOOST_COROSIO_STREAM_FILE_HPP