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>
124  
    explicit stream_file(Ex const& ex) : stream_file(ex.context())
124  
    explicit stream_file(Ex const& ex) : stream_file(ex.context())
125  
    {
125  
    {
126  
    }
126  
    }
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  
    */
132  
    stream_file(stream_file&& other) noexcept : io_object(std::move(other)) {}
132  
    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  
    */
138  
    stream_file& operator=(stream_file&& other) noexcept
138  
    stream_file& operator=(stream_file&& other) noexcept
139  
    {
139  
    {
140  
        if (this != &other)
140  
        if (this != &other)
141  
        {
141  
        {
142  
            close();
142  
            close();
143  
            h_ = std::move(other.h_);
143  
            h_ = std::move(other.h_);
144  
        }
144  
        }
145  
        return *this;
145  
        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  
    */
177  
    bool is_open() const noexcept
177  
    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
182  
        return h_ && get().native_handle() >= 0;
182  
        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:
256  
    inline implementation& get() const noexcept
256  
    inline implementation& get() const noexcept
257  
    {
257  
    {
258  
        return *static_cast<implementation*>(h_.get());
258  
        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