diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e40dc4e..c9d7b71b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,6 @@ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/doc/index.html add_custom_target(boost_async_doc DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/doc/index.html) - if(BOOST_ASYNC_IS_ROOT) #include(CTest) endif() @@ -79,18 +78,56 @@ if (NOT MSVC) endif() add_library(boost_async - src/detail/exception.cpp - src/detail/util.cpp - src/error.cpp - src/channel.cpp - src/main.cpp - src/this_thread.cpp - src/thread.cpp) + src/detail/exception.cpp + src/detail/util.cpp + src/error.cpp + src/channel.cpp + src/main.cpp + src/this_thread.cpp + src/thread.cpp + src/io/buffers/circular_buffer.cpp + src/io/buffers/const_buffer_pair.cpp + src/io/buffers/const_buffer_subspan.cpp + src/io/buffers/mutable_buffer_pair.cpp + src/io/buffers/mutable_buffer_subspan.cpp + src/io/detail/duplicate.cpp + src/io/detail/stream.cpp + src/io/acceptor.cpp + src/io/datagram_socket.cpp + src/io/endpoint.cpp + src/io/pipe.cpp + src/io/resolver.cpp + src/io/serial_port.cpp + src/io/signal_set.cpp + src/io/seq_packet_socket.cpp + src/io/socket.cpp + src/io/ssl.cpp + src/io/steady_timer.cpp + src/io/stream_socket.cpp + src/io/system_timer.cpp + src/io/stream_file.cpp + src/io/random_access_file.cpp + src/io/file.cpp + src/io/read.cpp + src/io/read_all.cpp + src/io/read_at.cpp + src/io/read_until.cpp + src/io/popen.cpp + src/io/process.cpp + src/io/buffers/register.cpp + src/io/write.cpp + src/io/write_at.cpp + src/io/detail/random_access_device.cpp + src/io/copy.cpp + src/io/copy_n.cpp + src/io/sleep.cpp) + target_include_directories(boost_async PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") target_link_libraries(boost_async PUBLIC Boost::system Threads::Threads) target_compile_definitions(boost_async PRIVATE BOOST_ASYNC_SOURCE=1 ) +target_compile_definitions(boost_async PUBLIC BOOST_PROCESS_USE_STD_FS=1) if (BOOST_ASYNC_USE_BOOST_CONTAINER) target_link_libraries(boost_async PUBLIC Boost::container) diff --git a/doc/reference/io.adoc b/doc/reference/io.adoc new file mode 100644 index 00000000..c973ed95 --- /dev/null +++ b/doc/reference/io.adoc @@ -0,0 +1,6 @@ +[#io] +== async/io.hpp + +include::io/timer.adoc[] +include::io/endpoint.adoc[] +include::io/resolver.adoc[] diff --git a/doc/reference/io/buffers/overview.adoc b/doc/reference/io/buffers/overview.adoc new file mode 100644 index 00000000..fc28344e --- /dev/null +++ b/doc/reference/io/buffers/overview.adoc @@ -0,0 +1,29 @@ +//// +Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) + + Distributed under the Boost Software License, Version 1.0. (See accompanying + file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + + Official repository: https://github.com/CPPAlliance/buffers +//// + +== Overview + +Boost.Buffers is a portable, low-level C++ library which provides +containers and algorithms for describing contiguous buffers of +arbitrary bytes. + +Boost.Buffers offers these features: + +* Require only C++11 +* Works without exceptions +* Fast compilation, few templates +* Does not require Asio + +=== Requirements + +* Requires Boost and a compiler supporting at least C++20 +* Aliases for standard types use their Boost equivalents +* Link to a built static or dynamic Boost library, or use header-only (see below) +* Supports `-fno-exceptions`, detected automatically + diff --git a/doc/reference/io/endpoint.adoc b/doc/reference/io/endpoint.adoc new file mode 100644 index 00000000..ded2b1b6 --- /dev/null +++ b/doc/reference/io/endpoint.adoc @@ -0,0 +1,132 @@ +[#io::endpoint] +=== async/io/endpoint.hpp + +The endpoint header provides a simple wrapper around endpoints. + +The usage is simple: + +[source,cpp] +---- +async::io::endpoint domain_socket{async::io::local_stream, "/var/sys/socket"}; +async::io::local_endpoint le = get(domain_socket); + + +async::io::endpoint ip_socket{async::io::tcp, "192.168.0.1", 8080}; +async::io::local_endpoint le = get(domain_socket); +---- + +NOTE: `AF_UNSPEC` is treated as an IP that's either ipv6 or ip_v4. + +==== Reference + + +[source,cpp] +---- +// a simple type holding the triplet to describe any endpoint +struct protocol_type +{ + using family_t = *implementation-defined*; + using type_t = *implementation-defined*; + using protocol_t = *implementation-defined*; + + constexpr family_t family() const noexcept {return family_;}; + constexpr type_t type() const noexcept {return type_;}; + constexpr protocol_t protocol() const noexcept {return protocol_;}; + + constexpr explicit + protocol_type(family_t family = static_cast(0), + type_t type = static_cast(0), + protocol_t protocol = static_cast(0)) noexcept + + constexpr protocol_type(const OtherProtocol & op) noexcept; + {} + // asio compatibility + using endpoint = endpoint; +}; + +// the class holding the endpiont +struct endpoint +{ + using storage_type = boost::asio::detail::sockaddr_storage_type; + using addr_type = boost::asio::detail::socket_addr_type; + void resize(std::size_t size); + + void * data(); + const void * data() const; + std::size_t size(); + + protocol_type protocol() const; + + endpoint() = default; + endpoint(const endpoint & ep); + + template + endpoint(static_protocol proto, Args && ... args); + +}; + +// get the type of the endpoint family, see below. +cosnt auto * get_if(const endpoint * ep); +// get the type of the endpoint family, see below. throws `bad_endpoint_access` if a mismatched. +const auto & get(const endpoint & ep); +---- + +To make it type-safe, the library provides a `static_protocol` template: + +[source,cpp] +---- +template(0), + protocol_type::type_t Type = static_cast(0), + protocol_type::protocol_t Protocol = static_cast(0)> +struct static_protocol +{ + using family_t = protocol_type::family_t ; + using type_t = protocol_type::type_t ; + using protocol_t = protocol_type::protocol_t; + + constexpr family_t family() const noexcept {return Family;}; + constexpr type_t type() const noexcept {return Type;}; + constexpr protocol_t protocol() const noexcept {return Protocol;}; + + using endpoint = endpoint; +}; +---- + +These can be used to define a constexpr constant for an endpoint type. The following are provided: + +[source,cpp] +---- +constexpr static_protocol ip {}; +constexpr static_protocol ip_v4 {}; +constexpr static_protocol ip_v6 {}; +constexpr static_protocol tcp {}; +constexpr static_protocol tcp_v4{}; +constexpr static_protocol tcp_v6{}; +constexpr static_protocol udp {}; +constexpr static_protocol udp_v4{}; +constexpr static_protocol udp_v6{}; +constexpr static_protocol icmp {}; +constexpr static_protocol local_stream {}; +constexpr static_protocol local_datagram {}; +constexpr static_protocol local_seqpacket{}; +constexpr static_protocol local_protocol {}; +---- + +To add your own family type you need to implement those two `tag_invoke` functions: + +[source,cpp] +---- +/// returns the size of the emplaced type. +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, // memory to in-place new + ... /* the arguments you need */); + +/// get a pointer to the endpoint type. +const your_endpoint* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr); +---- + diff --git a/doc/reference/io/resolver.adoc b/doc/reference/io/resolver.adoc new file mode 100644 index 00000000..f2c2260f --- /dev/null +++ b/doc/reference/io/resolver.adoc @@ -0,0 +1,76 @@ +[#io::resolve] +=== async/io/resolver.hpp + +[source,cpp] +---- +// a simple type holding the triplet to describe any endpoint +struct protocol_type +{ + using family_t = *implementation-defined*; + using type_t = *implementation-defined*; + using protocol_t = *implementation-defined*; + + constexpr family_t family() const noexcept {return family_;}; + constexpr type_t type() const noexcept {return type_;}; + constexpr protocol_t protocol() const noexcept {return protocol_;}; + + constexpr explicit + protocol_type(family_t family = static_cast(0), + type_t type = static_cast(0), + protocol_t protocol = static_cast(0)) noexcept + + constexpr protocol_type(const OtherProtocol & op) noexcept; + {} + // asio compatibility + using endpoint = endpoint; +}; + +// the class holding the endpiont +struct endpoint +{ + using storage_type = boost::asio::detail::sockaddr_storage_type; + using addr_type = boost::asio::detail::socket_addr_type; + void resize(std::size_t size); + + void * data(); + const void * data() const; + std::size_t size(); + + protocol_type protocol() const; + + endpoint() = default; + endpoint(const endpoint & ep); + + template + endpoint(static_protocol proto, Args && ... args); + +}; + +// get the type of the endpoint family, see below. +cosnt auto * get_if(const endpoint * ep); +// get the type of the endpoint family, see below. throws `bad_endpoint_access` if a mismatched. +const auto & get(const endpoint & ep); +---- + +To make it type-safe, the library provides a `static_protocol` template: + +[source,cpp] +---- + +struct resolver +{ + using resolve_result = system::result>; + resolver(); + resolver(resolver && ) = delete; + + void cancel(); + + awaitable resolve_op_ resolve(core::string_view host, core::string_view service); +}; + +awaitable lookup(core::string_view host, core::string_view service); +---- + diff --git a/doc/reference/io/timer.adoc b/doc/reference/io/timer.adoc new file mode 100644 index 00000000..2fe88e23 --- /dev/null +++ b/doc/reference/io/timer.adoc @@ -0,0 +1,52 @@ +[#io::timer] +=== async/io/steady_timer.hpp, async/io/system_timer.hpp & async/io/sleep.hpp + +async.io provides simple timers based on a `std::chrono::steady_clock` and `std::chrono::system_clock`. + +The interface between `boost::async::io::steady_timer` and `boost::async::io::system_timer` are equivalent. + +[source,cpp] +---- +struct steady_timer +{ + + using wait_result = system::result; + + /// The clock type. + typedef std::chrono::steady_clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + steady_timer(); + steady_timer(const time_point& expiry_time); + steady_timer(const duration& expiry_duration); + + void cancel(); + + time_point expiry() const; + void reset(const time_point& expiry_time); + void reset(const duration& expiry_time); + bool expired() const; + + [[nodiscard]] awaitable> wait() { return wait_op_{this}; } + // allow the timer to be directly be awaited + awaitable> operator co_await () { return wait(); } +}; + +awaitable> sleep(const steady_timer::time_point& expiry_time); +awaitable> sleep(const steady_timer::duration& expiry_duration); +---- + +==== Example + +[source,cpp] +---- +async::promise do_delay() +{ + co_await sleep(std::chrono::milliseconds(100)); +} +---- \ No newline at end of file diff --git a/include/boost/async/config.hpp b/include/boost/async/config.hpp index 50fa94a9..65da00d6 100644 --- a/include/boost/async/config.hpp +++ b/include/boost/async/config.hpp @@ -66,7 +66,17 @@ namespace pmr = boost::container::pmr; #if defined(BOOST_ASYNC_USE_STD_PMR) namespace pmr = std::pmr; #endif - } +# define BOOST_ASYNC_ERR(ev) ( \ + ::boost::system::error_code( (ev), [] { \ + static constexpr auto loc((BOOST_CURRENT_LOCATION)); \ + return &loc; }())) +# define BOOST_ASYNC_RETURN_EC(ev) \ + do { \ + static constexpr auto loc ## __LINE__((BOOST_CURRENT_LOCATION)); \ + return ::boost::system::error_code((ev), &loc ## __LINE__); \ + } while(false) + + #endif //BOOST_ASYNC_CONFIG_HPP diff --git a/include/boost/async/detail/exception.hpp b/include/boost/async/detail/exception.hpp index 575f88db..ff4eedd2 100644 --- a/include/boost/async/detail/exception.hpp +++ b/include/boost/async/detail/exception.hpp @@ -23,6 +23,12 @@ BOOST_ASYNC_DECL std::exception_ptr wait_not_ready(); BOOST_ASYNC_DECL std::exception_ptr already_awaited(); BOOST_ASYNC_DECL std::exception_ptr allocation_failed(); +BOOST_ASYNC_DECL void BOOST_NORETURN throw_invalid_argument( + source_location const& loc = BOOST_CURRENT_LOCATION); + +BOOST_ASYNC_DECL void BOOST_NORETURN throw_length_error( + source_location const& loc = BOOST_CURRENT_LOCATION); + template std::exception_ptr wait_not_ready() { return boost::async::detail::wait_not_ready();} diff --git a/include/boost/async/detail/forward_cancellation.hpp b/include/boost/async/detail/forward_cancellation.hpp index 58157000..35d0c5d1 100644 --- a/include/boost/async/detail/forward_cancellation.hpp +++ b/include/boost/async/detail/forward_cancellation.hpp @@ -15,6 +15,7 @@ namespace boost::async { + // Requests cancellation where a successful cancellation results // in no apparent side effects and where the op can re-awaited. template diff --git a/include/boost/async/detail/util.hpp b/include/boost/async/detail/util.hpp index 32b51615..da46531f 100644 --- a/include/boost/async/detail/util.hpp +++ b/include/boost/async/detail/util.hpp @@ -134,7 +134,8 @@ struct coro_deleter }; template -auto get_resume_result(Awaitable & aw) -> system::result +auto assign_resume_result(system::result().await_resume()), std::exception_ptr> & res, + Awaitable & aw) -> system::result().await_resume()), std::exception_ptr> & { using type = decltype(aw.await_resume()); try @@ -142,14 +143,19 @@ auto get_resume_result(Awaitable & aw) -> system::result) { aw.await_resume(); - return {}; + res.emplace(); } else - return aw.await_resume(); + res.emplace(aw.await_resume()); + return res; } catch(...) { - return std::current_exception(); + // TODO: Talk to @pdimov. + res.~result(); + return *new (&res) system::result().await_resume()), std::exception_ptr>( + system::in_place_error, + std::current_exception()); } } diff --git a/include/boost/async/io/acceptor.hpp b/include/boost/async/io/acceptor.hpp new file mode 100644 index 00000000..66741a6a --- /dev/null +++ b/include/boost/async/io/acceptor.hpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_ACCEPTOR_HPP +#define BOOST_ASYNC_IO_ACCEPTOR_HPP + +#include +#include +#include +#include + + +namespace boost::async::io +{ + +struct acceptor +{ + BOOST_ASYNC_DECL acceptor(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL acceptor(endpoint ep, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL system::result bind(endpoint ep); + BOOST_ASYNC_DECL system::result listen(int backlog = asio::socket_base::max_listen_connections); // int backlog = asio::max_backlog() + BOOST_ASYNC_DECL endpoint local_endpoint(); + + private: + + struct accept_op_ final : result_op + { + accept_op_(asio::basic_socket_acceptor & acceptor, + socket &socket) + : acceptor_(acceptor), socket_(socket) {} + BOOST_ASYNC_DECL void initiate(completion_handler h) override; + + private: + asio::basic_socket_acceptor & acceptor_; + socket &socket_; + }; + + public: + [[nodiscard]] BOOST_ASYNC_DECL accept_op_ accept(socket & sock); + private: + asio::basic_socket_acceptor acceptor_; +}; + +} + +#endif //BOOST_ASYNC_IO_ACCEPTOR_HPP diff --git a/include/boost/async/io/buffers.hpp b/include/boost/async/io/buffers.hpp new file mode 100644 index 00000000..08b15683 --- /dev/null +++ b/include/boost/async/io/buffers.hpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_BUFFERS_HPP +#define BOOST_ASYNC_BUFFERS_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/include/boost/async/io/buffers/algorithm.hpp b/include/boost/async/io/buffers/algorithm.hpp new file mode 100644 index 00000000..a6e29c3d --- /dev/null +++ b/include/boost/async/io/buffers/algorithm.hpp @@ -0,0 +1,205 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_ALGORITHM_HPP +#define BOOST_ASYNC_IO_BUFFERS_ALGORITHM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +#ifdef BOOST_ASYNC_IO_BUFFERS_DOCS + +/** Returns the type of a prefix of a buffer sequence. +*/ +template +using prefix_type = __see_below__; + +/** Returns the type of a suffix of a buffer sequence. +*/ +template +using suffix_type = __see_below__; + +/** Return a prefix of the buffer sequence. +*/ +template +prefix_type +prefix( + BufferSequence const& b, + std::size_t n); + +/** Return a prefix of the buffer sequence. +*/ +template +prefix_type +sans_suffix( + BufferSequence const& b, + std::size_t n); + +/** Return a suffix of the buffer sequence. +*/ +template +suffix_type +suffix( + BufferSequence const& b, + std::size_t n); + +/** Return a suffix of the buffer sequence. +*/ +template +suffix_type +sans_prefix( + BufferSequence const& b, + std::size_t n); + +/** Return the first buffer in a sequence. +*/ +template +__see_below__ +front( + BufferSequence const& b); + +#else + +template +void +tag_invoke( + prefix_tag const&, + BufferSequence const&, + std::size_t) = delete; + +template +void +tag_invoke( + suffix_tag const&, + BufferSequence const&, + std::size_t) = delete; + +template +using prefix_type = decltype( + tag_invoke( + prefix_tag{}, + std::declval(), + std::size_t{})); + +template +using suffix_type = decltype( + tag_invoke( + suffix_tag{}, + std::declval(), + std::size_t{})); + +namespace detail { + +struct prefix_impl +{ + template + prefix_type + operator()( + BufferSequence const& b, + std::size_t n) const + { + return tag_invoke( + prefix_tag{}, b, n); + } +}; + +struct sans_suffix_impl +{ + template + prefix_type + operator()( + BufferSequence const& b, + std::size_t n) const + { + auto const n0 = buffer_size(b); + if(n < n0) + return tag_invoke( + prefix_tag{}, b, n0 - n); + return tag_invoke( + prefix_tag{}, b, 0); + } +}; + +struct suffix_impl +{ + template + suffix_type + operator()( + BufferSequence const& b, + std::size_t n) const + { + return tag_invoke( + suffix_tag{}, b, n); + } +}; + +struct sans_prefix_impl +{ + template + suffix_type + operator()( + BufferSequence const& b, + std::size_t n) const + { + auto const n0 = buffer_size(b); + if(n < n0) + return tag_invoke( + suffix_tag{}, b, n0 - n); + return tag_invoke( + suffix_tag{}, b, 0); + } +}; + +struct front_impl +{ + template + mutable_buffer + operator()( + MutableBufferSequence const& bs) const noexcept + { + auto const it = begin(bs); + if(it != end(bs)) + return *it; + return {}; + } + + template + requires (!mutable_buffer_sequence) + const_buffer + operator()( + ConstBufferSequence const& bs) const noexcept + { + auto const it = bs.begin(); + if(it != bs.end()) + return *it; + return {}; + } +}; + +} // detail + +constexpr detail::prefix_impl prefix{}; +constexpr detail::suffix_impl suffix{}; +constexpr detail::sans_prefix_impl sans_prefix{}; +constexpr detail::sans_suffix_impl sans_suffix{}; +constexpr detail::front_impl front{}; + +#endif + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/any_dynamic_buffer.hpp b/include/boost/async/io/buffers/any_dynamic_buffer.hpp new file mode 100644 index 00000000..87475d81 --- /dev/null +++ b/include/boost/async/io/buffers/any_dynamic_buffer.hpp @@ -0,0 +1,169 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_ANY_DYNAMIC_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_ANY_DYNAMIC_BUFFER_HPP + +#include +#include +#include +#include +#include + +#include + + +#include + +namespace boost::async::io::buffers { + +/** An abstract, type-erased dynamic buffer. +*/ +struct any_dynamic_buffer +{ + using const_buffers_type = + buffers::const_buffer_span; + + using mutable_buffers_type = + buffers::mutable_buffer_span; + + virtual ~any_dynamic_buffer() = default; + virtual std::size_t size() const = 0; + virtual std::size_t max_size() const = 0; + virtual std::size_t capacity() const = 0; + virtual const_buffers_type data() const = 0; + virtual mutable_buffers_type prepare(std::size_t) = 0; + virtual void commit(std::size_t) = 0; + virtual void consume(std::size_t) = 0; +}; + +//----------------------------------------------- + +/** A type-erased dynamic buffer. +*/ +template< + class DynamicBuffer, + std::size_t N = asio::detail::max_iov_len> +class any_dynamic_buffer_impl + : public any_dynamic_buffer +{ + DynamicBuffer b_; + buffers::const_buffer data_[N]; + buffers::mutable_buffer out_[N]; + std::size_t data_len_ = 0; + std::size_t out_len_ = 0; + + template + static + std::size_t + unroll( + Buffers const& bs, + value_type* dest, + std::size_t len) + { + std::size_t i = 0; + for(auto b : buffers::range(bs)) + { + dest[i++] = b; + if(i == len) + break; + } + return i; + } + +public: + template + any_dynamic_buffer_impl( + DynamicBuffer_&& b) + : b_(std::forward< + DynamicBuffer_>(b)) + { + } + + DynamicBuffer& + buffer() noexcept + { + return b_; + } + + DynamicBuffer const& + buffer() const noexcept + { + return b_; + } + + std::size_t + size() const override + { + return b_.size(); + } + + std::size_t + max_size() const override + { + return b_.max_size(); + } + + std::size_t + capacity() const override + { + return b_.capacity(); + } + + const_buffers_type + data() const override + { + return const_buffers_type( + data_, data_len_); + } + + auto + prepare( + std::size_t n) -> + mutable_buffers_type override + { + out_len_ = unroll( + b_.prepare(n), out_, N); + return mutable_buffers_type( + out_, out_len_); + } + + void + commit( + std::size_t n) override + { + b_.commit(n); + data_len_ = unroll( + b_.data(), data_, N); + } + + void + consume( + std::size_t n) override + { + b_.consume(n); + data_len_ = unroll( + b_.data(), data_, N); + } +}; + +template +auto +make_any(DynamicBuffer&& b) -> + any_dynamic_buffer_impl::type> +{ + return any_dynamic_buffer_impl::type>( + std::forward(b)); +} + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/buffer.hpp b/include/boost/async/io/buffers/buffer.hpp new file mode 100644 index 00000000..aa68ac61 --- /dev/null +++ b/include/boost/async/io/buffers/buffer.hpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_BUFFER_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** Return a buffer. +*/ +inline +mutable_buffer +buffer( + mutable_buffer const& b) noexcept +{ + return b; +} + +/** Return a buffer. +*/ +inline +mutable_buffer +buffer( + void* data, + std::size_t size) noexcept +{ + return mutable_buffer(data, size); +} + +/** Return a buffer. +*/ +inline +const_buffer +buffer( + const_buffer const& b) noexcept +{ + return b; +} + +/** Return a buffer. +*/ +inline +const_buffer +buffer( + void const* data, + std::size_t size) noexcept +{ + return const_buffer(data, size); +} + +/** Return a buffer. +*/ +template + requires std::is_trivially_copyable::value +mutable_buffer +buffer( + T (&data)[N]) noexcept +{ + return mutable_buffer( + data, N * sizeof(T)); +} + +/** Return a buffer. +*/ +template + requires std::is_trivially_copyable::value +const_buffer +buffer( + T const (&data)[N]) noexcept +{ + return const_buffer( + data, N * sizeof(T)); +} + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/buffer_copy.hpp b/include/boost/async/io/buffers/buffer_copy.hpp new file mode 100644 index 00000000..d74a0eca --- /dev/null +++ b/include/boost/async/io/buffers/buffer_copy.hpp @@ -0,0 +1,117 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_BUFFER_COPY_HPP +#define BOOST_ASYNC_IO_BUFFERS_BUFFER_COPY_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +#ifdef BOOST_ASYNC_IO_BUFFERS_DOCS + +/** Copy buffer contents +*/ +template< + class MutableBufferSequence, + class ConstBufferSequence> +std::size_t +buffer_copy( + MutableBufferSequence const& to, + ConstBufferSequence const& from, + std::size_t at_most = + std::size_t(-1)) noexcept; + +#else + +namespace detail { + +struct buffer_copy_impl +{ + // If you get a compile error here it + // means that one or both of your types + // do not meet the requirements. + template< + mutable_buffer_sequence MutableBuffers, + const_buffer_sequence ConstBuffers> + std::size_t + operator()( + MutableBuffers const& to, + ConstBuffers const& from, + std::size_t at_most = + std::size_t(-1)) const noexcept + { + std::size_t total = 0; + std::size_t pos0 = 0; + std::size_t pos1 = 0; + auto const end0 = end(from); + auto const end1 = end(to); + auto it0 = begin(from); + auto it1 = begin(to); + while( + total < at_most && + it0 != end0 && + it1 != end1) + { + const_buffer b0 = + const_buffer(*it0) + pos0; + mutable_buffer b1 = + mutable_buffer(*it1) + pos1; + std::size_t const amount = + [&] + { + std::size_t n = b0.size(); + if( n > b1.size()) + n = b1.size(); + if( n > at_most - total) + n = at_most - total; + std::memcpy( + b1.data(), + b0.data(), + n); + return n; + }(); + total += amount; + if(amount == b1.size()) + { + ++it1; + pos1 = 0; + } + else + { + pos1 += amount; + } + if(amount == b0.size()) + { + ++it0; + pos0 = 0; + } + else + { + pos0 += amount; + } + } + return total; + } +}; + +} // detail + +constexpr detail::buffer_copy_impl buffer_copy{}; + +#endif + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/buffer_size.hpp b/include/boost/async/io/buffers/buffer_size.hpp new file mode 100644 index 00000000..af8d0d47 --- /dev/null +++ b/include/boost/async/io/buffers/buffer_size.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_BUFFER_SIZE_HPP +#define BOOST_ASYNC_IO_BUFFERS_BUFFER_SIZE_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +#ifdef BOOST_ASYNC_IO_BUFFERS_DOCS + +/** Return the total octets in a buffer sequence + + @par Constraints + @code + is_const_buffer_sequence< ConstBufferSequence >::value == true + @endcode +*/ +template< + class ConstBufferSequence> +std::size_t +buffer_size( + ConstBufferSequence const& b) noexcept; + +#else + +template +std::size_t +tag_invoke( + size_tag const&, + Buffers const& bs) noexcept +{ + std::size_t n = 0; + for(const_buffer b : range(bs)) + n += b.size(); + return n; +} + +namespace detail { + +struct buffer_size_impl +{ + // If you get a compile error here it + // means that your type does not meet + // the requirements. + template + std::size_t + operator()( + Buffers const& bs) const noexcept + { + + return tag_invoke(size_tag{}, bs); + } +}; + +} // detail + +constexpr detail::buffer_size_impl buffer_size{}; + +#endif + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/circular_buffer.hpp b/include/boost/async/io/buffers/circular_buffer.hpp new file mode 100644 index 00000000..1487804f --- /dev/null +++ b/include/boost/async/io/buffers/circular_buffer.hpp @@ -0,0 +1,124 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CIRCULAR_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_CIRCULAR_BUFFER_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +class circular_buffer +{ + unsigned char* base_ = nullptr; + std::size_t cap_ = 0; + std::size_t in_pos_ = 0; + std::size_t in_len_ = 0; + std::size_t out_size_ = 0; + +public: + using const_buffers_type = + const_buffer_pair; + + using mutable_buffers_type = + mutable_buffer_pair; + + /** Constructor. + */ + circular_buffer() = default; + + /** Constructor. + */ + circular_buffer( + circular_buffer const&) = default; + +#if 0 + /** Constructor. + */ + circular_buffer( + mutable_buffer b) noexcept + : base_(static_cast< + unsigned char*>(b.data())) + , cap_(b.size()) + { + } +#endif + + /** Constructor. + */ + circular_buffer( + void* base, + std::size_t capacity) noexcept + : base_(static_cast< + unsigned char*>(base)) + , cap_(capacity) + { + } + + /** Constructor. + */ + circular_buffer( + void* base, + std::size_t capacity, + std::size_t initial_size) + : base_(static_cast< + unsigned char*>(base)) + , cap_(capacity) + , in_len_(initial_size) + { + if(in_len_ > capacity) + async::detail::throw_invalid_argument(); + } + + /** Assignment. + */ + circular_buffer& operator=( + circular_buffer const&) = default; + + std::size_t + size() const noexcept + { + return in_len_; + } + + std::size_t + max_size() const noexcept + { + return cap_; + } + + std::size_t + capacity() const noexcept + { + return cap_ - in_len_; + } + + BOOST_ASYNC_DECL + const_buffers_type + data() const noexcept; + + BOOST_ASYNC_DECL + mutable_buffers_type + prepare(std::size_t n); + + BOOST_ASYNC_DECL + void + commit(std::size_t n) noexcept; + + BOOST_ASYNC_DECL + void + consume(std::size_t n) noexcept; +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/concepts.hpp b/include/boost/async/io/buffers/concepts.hpp new file mode 100644 index 00000000..5e7e05cc --- /dev/null +++ b/include/boost/async/io/buffers/concepts.hpp @@ -0,0 +1,136 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CONCEPTS_HPP +#define BOOST_ASYNC_IO_BUFFERS_CONCEPTS_HPP + +#include +#include + +namespace boost::async::io::buffers { + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS +class const_buffer; +class mutable_buffer; +#endif + +// https://www.boost.org/doc/libs/1_65_0/doc/html/boost_asio/reference/ConstBufferSequence.html + +/** Determine if T is a ConstBuffers. +*/ +#if BOOST_ASYNC_IO_BUFFERS_DOCS +template +struct is_const_buffer_sequence + : std::integral_constant{}; +#else + + + +template +concept const_buffer_sequence = + std::same_as || + std::same_as || + ((std::same_as || + std::same_as) && + std::bidirectional_iterator && + requires (const T & buf) + { + {buf.begin()} -> std::same_as; + {buf.end()} -> std::same_as; + + } && + (std::same_as::value_type>, + const_buffer> + || + std::same_as::value_type>, + mutable_buffer>) + ) +; + + +#endif + +/** Determine if T is a MutableBuffers. +*/ +#if BOOST_ASYNC_IO_BUFFERS_DOCS +template +struct is_mutable_buffer_sequence + : std::integral_constant{}; +#else +template +concept mutable_buffer_sequence = + std::same_as || + (std::same_as && + std::bidirectional_iterator && + requires (const T &buf) + { + {buf.begin()} -> std::same_as; + {buf.end()} -> std::same_as; + } && + std::same_as< + std::remove_const_t::value_type>, + mutable_buffer> + ) +; + + +#endif + +//------------------------------------------------ + +/** Determine if T is a DynamicBuffer +*/ +#if BOOST_ASYNC_IO_BUFFERS_DOCS +template +struct is_dynamic_buffer + : std::integral_constant{}; +#else + +template +concept dynamic_buffer = + requires (const T & buf, std::size_t n) + { + { buf.size() } -> std::same_as; + { buf.max_size() } -> std::same_as; + { buf.capacity() } -> std::same_as; + } && + requires (T & buf, std::size_t n) + { + + {buf.commit(n)}; + {buf.consume(n)}; + {buf.data()} -> const_buffer_sequence; + {buf.prepare(n)}-> mutable_buffer_sequence; + } + && const_buffer_sequence + && mutable_buffer_sequence + ; + + +/** Return the underlying buffer type of a sequence. +*/ +template +using value_type = typename + std::conditional< + mutable_buffer_sequence, + mutable_buffer, + const_buffer + >::type; + +#endif + +template +concept buffer_byte = (sizeof(T) == 1u) && std::is_trivial_v; + + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/const_buffer.hpp b/include/boost/async/io/buffers/const_buffer.hpp new file mode 100644 index 00000000..b3ad65ed --- /dev/null +++ b/include/boost/async/io/buffers/const_buffer.hpp @@ -0,0 +1,180 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_HPP + +#include +#include + +namespace boost::async::io::buffers { + +/** Holds a buffer that cannot be modified. +*/ +class const_buffer +{ + unsigned char const* p_ = nullptr; + std::size_t n_ = 0; + +public: + /** Constructor. + */ + const_buffer() = default; + + /** Constructor. + */ + const_buffer( + void const* data, + std::size_t size) noexcept + : p_(static_cast< + unsigned char const*>(data)) + , n_(size) + { + } + + /** Constructor + */ + template + requires ( + requires (const Container & ct) + { + {ct.data()} -> std::convertible_to; + {ct.size()} -> std::convertible_to; + } + && std::is_trivial_v) + const_buffer(const Container & ct) : const_buffer(ct.data(), sizeof(typename Container::value_type) * ct.size()) {} + + /** Constructor for strings */ + template + const_buffer(const char (&string)[N]) : const_buffer(string, std::strlen(string)) {} + + /** Constructor for strings */ + template + requires std::is_trivial_v + const_buffer(const T (&arr)[N]) : const_buffer(&arr[0], sizeof(T) * N) {} + + /** Constructor for arrays */ + template + requires std::is_trivial_v + const_buffer(std::pair p) : const_buffer(p.first, sizeof(T) * p.second) {} + + + /** Constructor. + */ + const_buffer( + const_buffer const&) = default; + + /** Constructor. + */ + const_buffer( + mutable_buffer const& b) noexcept + : p_(static_cast< + unsigned char const*>(b.data())) + , n_(b.size()) + { + } + + /** Assignment. + */ + const_buffer& operator=( + const_buffer const&) = default; + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + // conversion to boost::asio::const_buffer + template + requires (std::constructible_from + && !std::same_as + && !std::same_as) + operator T() const noexcept + { + return T{ data(), size() }; + } +#endif + + void const* + data() const noexcept + { + return p_; + } + + std::size_t + size() const noexcept + { + return n_; + } + + /** Remove a prefix from the buffer. + */ + const_buffer& + operator+=(std::size_t n) noexcept + { + if(n >= n_) + { + p_ = p_ + n_; + n_ = 0; + return *this; + } + p_ = p_ + n; + n_ -= n; + return *this; + } + + /** Return the buffer with a prefix removed. + */ + friend + const_buffer + operator+( + const_buffer b, + std::size_t n) noexcept + { + return b += n; + } + + /** Return the buffer with a prefix removed. + */ + friend + const_buffer + operator+( + std::size_t n, + const_buffer b) noexcept + { + return b += n; + } + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + const_buffer + tag_invoke( + prefix_tag const&, + const_buffer const& b, + std::size_t n) noexcept + { + if(n < b.size()) + return { b.data(), n }; + return b; + } + + friend + const_buffer + tag_invoke( + suffix_tag const&, + const_buffer const& b, + std::size_t n) noexcept + { + auto const n0 = b.size(); + if(n < n0) + return { b.p_ + (n0 - n), n }; + return b; + } +#endif +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/const_buffer_pair.hpp b/include/boost/async/io/buffers/const_buffer_pair.hpp new file mode 100644 index 00000000..a8fced7e --- /dev/null +++ b/include/boost/async/io/buffers/const_buffer_pair.hpp @@ -0,0 +1,138 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_PAIR_HPP +#define BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_PAIR_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** A constant buffer pair +*/ +class const_buffer_pair +{ +public: + using value_type = const_buffer; + + using const_iterator = value_type const*; + + /** Constructor. + */ + const_buffer_pair() = default; + + /** Constructor. + */ + const_buffer_pair( + const_buffer_pair const&) = default; + + /** Constructor. + */ + const_buffer_pair( + const_buffer const& b0, + const_buffer const& b1) noexcept + : b_{ b0, b1 } + { + } + + /** Constructor. + */ + const_buffer_pair( + mutable_buffer_pair const& bs) noexcept + : b_{ bs.begin()[0], bs.begin()[1] } + { + } + + /** Assignment. + */ + const_buffer_pair& operator=( + const_buffer_pair const&) = default; + + /** Assignment. + */ + const_buffer_pair& operator=( + mutable_buffer_pair const& other) noexcept + { + b_[0] = other.begin()[0]; + b_[1] = other.begin()[1]; + return *this; + } + + const_buffer const& + operator[](unsigned i) const noexcept + { + BOOST_ASSERT(i < 2); + return b_[i]; + } + + const_buffer& + operator[](unsigned i) noexcept + { + BOOST_ASSERT(i < 2); + return b_[i]; + } + + const_iterator + begin() const noexcept + { + return b_; + } + + const_iterator + end() const noexcept + { + return b_ + 2; + } + + const_buffer * data() { return b_; } + const const_buffer * data() const { return b_; } + std::size_t size() const {return 2u;} + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + const_buffer_pair + tag_invoke( + prefix_tag const&, + const_buffer_pair const& b, + std::size_t n) noexcept + { + return b.prefix_impl(n); + } + + friend + const_buffer_pair + tag_invoke( + suffix_tag const&, + const_buffer_pair const& b, + std::size_t n) noexcept + { + return b.suffix_impl(n); + } +#endif + +private: + BOOST_ASYNC_DECL + const_buffer_pair + prefix_impl( + std::size_t n) const noexcept; + + BOOST_ASYNC_DECL + const_buffer_pair + suffix_impl( + std::size_t n) const noexcept; + + const_buffer b_[2]; +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/const_buffer_span.hpp b/include/boost/async/io/buffers/const_buffer_span.hpp new file mode 100644 index 00000000..f3089fe4 --- /dev/null +++ b/include/boost/async/io/buffers/const_buffer_span.hpp @@ -0,0 +1,133 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_SPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_SPAN_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** Holds a span of buffers that cannot be modified. + + Objects of this type meet the requirements + of ConstBufferSequence. +*/ +class const_buffer_span +{ + const_buffer const* p_ = nullptr; + std::size_t n_ = 0; + + friend class const_buffer_subspan; + +public: + /** The type of buffer. + */ + using value_type = const_buffer; + + /** The type of iterators returned. + */ + using const_iterator = value_type const*; + + /** Constructor. + */ + const_buffer_span() = default; + + /** Constructor. + */ + const_buffer_span( + const_buffer const* p, + std::size_t n) noexcept + : p_(p) + , n_(n) + { + } + + /** Constructor. + */ + template + requires requires (const ConstBufferSequence & seq) + { + {seq.data()} -> std::convertible_to; + {seq.size()} -> std::same_as; + } + const_buffer_span( + ConstBufferSequence const& bs) noexcept + : p_(bs.data()) + , n_(bs.size()) + { + } + + + + /** Constructor. + */ + const_buffer_span( + const_buffer_span const&) = default; + + /** Assignment. + */ + const_buffer_span& operator=( + const_buffer_span const&) = default; + + /** Return an iterator to the beginning. + */ + const_iterator + begin() const noexcept + { + return p_; + } + + /** Return an iterator to the end. + */ + const_iterator + end() const noexcept + { + return p_ + n_; + } + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + const_buffer_subspan + tag_invoke( + prefix_tag const&, + const_buffer_span const& s, + std::size_t n) noexcept + { + return s.prefix_impl(n); + } + + friend + const_buffer_subspan + tag_invoke( + suffix_tag const&, + const_buffer_span const& s, + std::size_t n) noexcept + { + return s.suffix_impl(n); + } +#endif + +private: + const_buffer_subspan prefix_impl( + std::size_t n) const noexcept; + const_buffer_subspan suffix_impl( + std::size_t n) const noexcept; +}; + +//----------------------------------------------- + +} // boost::buffers + +#include + +#endif diff --git a/include/boost/async/io/buffers/const_buffer_subspan.hpp b/include/boost/async/io/buffers/const_buffer_subspan.hpp new file mode 100644 index 00000000..09892067 --- /dev/null +++ b/include/boost/async/io/buffers/const_buffer_subspan.hpp @@ -0,0 +1,120 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_SUBSPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_CONST_BUFFER_SUBSPAN_HPP + +#include +#include + +namespace boost::async::io::buffers { + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS +class const_buffer_span; +#endif + +/** Holds a span of buffers that cannot be modified. + + Objects of this type meet the requirements + of ConstBufferSequence. +*/ +class const_buffer_subspan +{ + const_buffer const* p_ = nullptr; + std::size_t n_ = 0; + std::size_t p0_ = 0; + std::size_t p1_ = 0; + + friend class const_buffer_span; + + const_buffer_subspan( + const_buffer const* p, + std::size_t n, + std::size_t p0, + std::size_t p1) noexcept; + +public: + /** The type of buffer. + */ + using value_type = const_buffer; + + /** The type of iterators returned. + */ + class const_iterator; + + /** Constructor. + */ + const_buffer_subspan() = default; + + /** Constructor. + */ + BOOST_ASYNC_DECL + const_buffer_subspan( + const_buffer const* p, + std::size_t n) noexcept; + + /** Constructor. + */ + const_buffer_subspan( + const_buffer_span const& s) noexcept; + + /** Constructor. + */ + const_buffer_subspan( + const_buffer_subspan const&) = default; + + /** Assignment. + */ + const_buffer_subspan& operator=( + const_buffer_subspan const&) = default; + + /** Return an iterator to the beginning. + */ + const_iterator + begin() const noexcept; + + /** Return an iterator to the end. + */ + const_iterator + end() const noexcept; + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + const_buffer_subspan + tag_invoke( + prefix_tag const&, + const_buffer_subspan const& s, + std::size_t n) noexcept + { + return s.prefix_impl(n); + } + + friend + const_buffer_subspan + tag_invoke( + suffix_tag const&, + const_buffer_subspan const& s, + std::size_t n) noexcept + { + return s.suffix_impl(n); + } +#endif + +private: + BOOST_ASYNC_DECL const_buffer_subspan + prefix_impl(std::size_t n) const noexcept; + BOOST_ASYNC_DECL const_buffer_subspan + suffix_impl(std::size_t n) const noexcept; +}; + +} // boost::buffers + +#include + +#endif diff --git a/include/boost/async/io/buffers/dynamic_buffer_view.hpp b/include/boost/async/io/buffers/dynamic_buffer_view.hpp new file mode 100644 index 00000000..f7157849 --- /dev/null +++ b/include/boost/async/io/buffers/dynamic_buffer_view.hpp @@ -0,0 +1,102 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_BUFFER_DYNAMIC_BUFFER_VIEW_HPP +#define BOOST_ASYNC_IO_BUFFER_DYNAMIC_BUFFER_VIEW_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost::async::io::buffers +{ + +struct dynamic_buffer_view +{ + using mutable_buffers_type = container::static_vector; + using const_buffers_type = container::static_vector< const_buffer, boost::asio::detail::max_iov_len>; + + std::size_t size () const { return vtable_.size (this_, position_); } + std::size_t max_size() const { return vtable_.max_size(this_); } + std::size_t capacity() const { return vtable_.capacity(this_); } + const_buffers_type data () const { return vtable_.data (this_, position_); } + mutable_buffers_type prepare (std::size_t n) { return vtable_.prepare (this_, n, position_); } + void commit (std::size_t n) { return vtable_.commit (this_, n, position_); } + void consume (std::size_t n) { return vtable_.consume (this_, n, position_); } + + dynamic_buffer_view(const dynamic_buffer_view&) noexcept = default; + dynamic_buffer_view(dynamic_buffer_view&&) noexcept =default; + + template + dynamic_buffer_view(Buffer &other); + + template + requires ( + requires (const Container & ct) + { + {ct.data()} -> std::convertible_to; + {ct.size()} -> std::convertible_to; + {ct.max_size()} -> std::convertible_to; + {ct.capacity()} -> std::convertible_to; + } + && requires (Container & ct) + { + {ct.data()} -> std::convertible_to; + {ct.resize(std::size_t())}; + {ct.erase(ct.begin(), ct.end())}; + } + && std::is_trivial_v + && sizeof(typename Container::value_type) == 1u) + dynamic_buffer_view(Container &other); + + template + requires (std::is_trivial_v && sizeof(T) == 1u) + dynamic_buffer_view(boost::circular_buffer &other); + + template + requires (std::is_trivial_v && sizeof(T) == 1u) + dynamic_buffer_view(boost::container::deque &other); + + private: + struct vtable_t + { + std::size_t (*size) (const void * this_, std::size_t pos) = 0; + std::size_t (*max_size)(const void * this_) = 0; + std::size_t (*capacity)(const void * this_) = 0; + const_buffers_type (*data) (const void * this_, std::size_t pos) = 0; + mutable_buffers_type (*prepare) (void * this_, std::size_t n, std::size_t pos) = 0; + void (*commit) (void * this_, std::size_t n, std::size_t & pos) = 0; + void (*consume) (void * this_, std::size_t n, std::size_t & pos) = 0; + }; + struct vtables; + + void* this_; + const vtable_t & vtable_; + std::size_t position_{0u}; +}; + + + + + + +} + +#include + +#endif //BOOST_ASYNC_IO_BUFFER_DYNAMIC_BUFFER_VIEW_HPP diff --git a/include/boost/async/io/buffers/flat_buffer.hpp b/include/boost/async/io/buffers/flat_buffer.hpp new file mode 100644 index 00000000..247267a9 --- /dev/null +++ b/include/boost/async/io/buffers/flat_buffer.hpp @@ -0,0 +1,167 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_FLAT_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_FLAT_BUFFER_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** A DynamicBuffer with a fixed capacity +*/ +class flat_buffer +{ + unsigned char* data_ = nullptr; + std::size_t cap_ = 0; + std::size_t in_pos_ = 0; + std::size_t in_size_ = 0; + std::size_t out_size_ = 0; + +public: + using const_buffers_type = + const_buffer; + + using mutable_buffers_type = + mutable_buffer; + + /** Constructor. + */ + flat_buffer() = default; + + /** Constructor. + */ + flat_buffer( + void* data, + std::size_t capacity, + std::size_t initial_size = 0) + : data_(static_cast< + unsigned char*>(data)) + , cap_(capacity) + , in_size_(initial_size) + { + // initial size too large + if(in_size_ > cap_) + async::detail::throw_invalid_argument(); + } + + + /** Constructor. + */ + template + requires requires (Container & container) + { + {container.data()} -> std::convertible_to; + {container.size()} -> std::convertible_to; + } + flat_buffer(Container & cont, + std::size_t initial_size = 0) + : data_(cont.data()) + , cap_(cont.size()) + , in_size_(initial_size) + { + // initial size too large + if(in_size_ > cap_) + async::detail::throw_invalid_argument(); + } + + /** Constructor. + */ + flat_buffer( + mutable_buffer const& b, + std::size_t initial_size = 0) + : flat_buffer( + b.data(), + b.size(), + initial_size) + { + } + + /** Constructor. + */ + flat_buffer( + flat_buffer const&) = default; + + /** Constructor. + */ + flat_buffer& operator=( + flat_buffer const&) = default; + + std::size_t + size() const noexcept + { + return in_size_; + } + + std::size_t + max_size() const noexcept + { + return cap_; + } + + std::size_t + capacity() const noexcept + { + return cap_ - in_pos_; + } + + const_buffers_type + data() const noexcept + { + return { + data_ + in_pos_, + in_size_ }; + } + + mutable_buffers_type + prepare(std::size_t n) + { + // n exceeds available space + if(n > cap_ - in_size_) + async::detail::throw_invalid_argument(); + + out_size_ = n; + return { data_ + + in_pos_ + in_size_, n }; + } + + void + commit( + std::size_t n) noexcept + { + if(n < out_size_) + in_size_ += n; + else + in_size_ += out_size_; + out_size_ = 0; + } + + void + consume( + std::size_t n) noexcept + { + if(n < in_size_) + { + in_pos_ += n; + in_size_ -= n; + } + else + { + in_pos_ = 0; + in_size_ = 0; + } + } +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/impl/const_buffer_span.hpp b/include/boost/async/io/buffers/impl/const_buffer_span.hpp new file mode 100644 index 00000000..5d3dc0c0 --- /dev/null +++ b/include/boost/async/io/buffers/impl/const_buffer_span.hpp @@ -0,0 +1,56 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_IMPL_CONST_BUFFER_SPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_IMPL_CONST_BUFFER_SPAN_HPP + +namespace boost::async::io::buffers { + +inline +const_buffer_subspan +const_buffer_span:: +prefix_impl( + std::size_t n) const noexcept +{ + return const_buffer_subspan( + *this).prefix_impl(n); +} + +inline +const_buffer_subspan +const_buffer_span:: +suffix_impl( + std::size_t n) const noexcept +{ + return const_buffer_subspan( + *this).suffix_impl(n); +} + +//----------------------------------------------- + +// here because circular dependency +inline +const_buffer_subspan:: +const_buffer_subspan( + const_buffer_span const& s) noexcept + : p_(s.p_) + , n_(s.n_) + , p1_([&]() -> std::size_t + { + if(n_ > 0) + return p_[n_-1].size(); + return 0; + }()) +{ +} + + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/impl/const_buffer_subspan.hpp b/include/boost/async/io/buffers/impl/const_buffer_subspan.hpp new file mode 100644 index 00000000..6b67dc5e --- /dev/null +++ b/include/boost/async/io/buffers/impl/const_buffer_subspan.hpp @@ -0,0 +1,144 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_IMPL_CONST_BUFFER_SUBSPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_IMPL_CONST_BUFFER_SUBSPAN_HPP + +#include +#include + +namespace boost::async::io::buffers { + +class const_buffer_subspan:: + const_iterator +{ + const_buffer_subspan const* s_ = nullptr; + std::size_t i_ = 0; + + friend class const_buffer_subspan; + + const_iterator( + const_buffer_subspan const& s, + std::size_t i) noexcept + : s_(&s) + , i_(i) + { + } + +public: + using value_type = const_buffer; + using reference = const_buffer; + using pointer = void; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator( + const_iterator const&) = default; + const_iterator& operator=( + const_iterator const&) = default; + + bool + operator==( + const_iterator const& other) const noexcept + { + return + s_ == other.s_ && + i_ == other.i_; + } + + bool + operator!=( + const_iterator const& other) const noexcept + { + return !(*this == other); + } + + BOOST_ASYNC_DECL + reference + operator*() const noexcept; + + const_iterator& + operator++() noexcept + { + BOOST_ASSERT(i_ < s_->n_); + ++i_; + return *this; + } + + const_iterator + operator++(int) noexcept + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() noexcept + { + BOOST_ASSERT(i_ > 0); + --i_; + return *this; + } + + const_iterator + operator--(int) noexcept + { + auto temp = *this; + --(*this); + return temp; + } +}; + +inline +auto +const_buffer_subspan:: +begin() const noexcept -> + const_iterator +{ + return { *this, 0 }; +} + +inline +auto +const_buffer_subspan:: +end() const noexcept -> + const_iterator +{ + return { *this, n_ }; +} + +inline +const_buffer_subspan:: +const_buffer_subspan( + const_buffer const* p, + std::size_t n, + std::size_t p0, + std::size_t p1) noexcept + : p_(p) + , n_(n) + , p0_(p0) + , p1_(p1) +{ + BOOST_ASSERT( + n_ > 1 || + p1_ >= p0_); + BOOST_ASSERT( + n_ == 0 || + p0 < p[0].size()); + BOOST_ASSERT( + n_ == 0 || + p1 <= p[n_ - 1].size()); +} + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/impl/dynamic_buffer_view.hpp b/include/boost/async/io/buffers/impl/dynamic_buffer_view.hpp new file mode 100644 index 00000000..592b14d7 --- /dev/null +++ b/include/boost/async/io/buffers/impl/dynamic_buffer_view.hpp @@ -0,0 +1,246 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_IMPL_DYNAMIC_BUFFER_VIEW_HPP +#define BOOST_ASYNC_IO_BUFFERS_IMPL_DYNAMIC_BUFFER_VIEW_HPP + +#include + +namespace boost::async::io::buffers +{ + +struct dynamic_buffer_view::vtables +{ + + template + constexpr static dynamic_buffer_view::vtable_t dynamic_buffer_vtable_{ + .size =[](const void *this_, std::size_t) { return static_cast(this_)->size(); }, + .max_size =[](const void *this_) { return static_cast(this_)->max_size(); }, + .capacity =[](const void *this_) { return static_cast(this_)->capacity(); }, + .data =[](const void *this_, std::size_t) + { + auto d = static_cast(this_)->data(); + dynamic_buffer_view::const_buffers_type cbt{begin(d), end(d)}; + return cbt; + }, + .prepare =[](void *this_, std::size_t n, std::size_t) + { + auto p = static_cast(this_)->prepare(n); + dynamic_buffer_view::mutable_buffers_type mbt{begin(p), end(p)}; + return mbt; + }, + .commit =[](void *this_, std::size_t n, std::size_t &) { static_cast(this_)->commit(n); }, + .consume =[](void *this_, std::size_t n, std::size_t &) { static_cast(this_)->consume(n); } + }; + + + template + constexpr static dynamic_buffer_view::vtable_t contiguous_vtable_{ + .size = [](const void *this_, std::size_t pos) { return pos; }, + .max_size = [](const void *this_) { return static_cast(this_)->max_size(); }, + .capacity = [](const void *this_) { return static_cast(this_)->capacity(); }, + .data =[](const void *this_, std::size_t position) + { + auto self = static_cast(this_); + const auto p = self->data(); + const_buffer cb{p, position}; + dynamic_buffer_view::const_buffers_type cbt{cb}; + return cbt; + }, + .prepare =[](void *this_, std::size_t n, std::size_t position) + { + auto self = static_cast(this_); + auto sz = position + n; + if (sz > self->size()) + self->resize(sz); + + mutable_buffer buf{self->data(), position + n}; + buf += position; + return mutable_buffers_type{buf}; + }, + .commit =[](void *this_, std::size_t n, std::size_t &position) + { + position += n; + static_cast(this_)->resize(position); + }, + .consume =[](void *this_, std::size_t n, std::size_t &position) + { + auto self = static_cast(this_); + std::size_t consume_length = (std::min)(n, position); + self->erase(self->begin(), std::next(self->begin(), consume_length)); + position -= consume_length; + } + }; + + + template + constexpr static dynamic_buffer_view::vtable_t circular_vtable_{ + .size = [](const void *this_, std::size_t pos) { return pos; }, + .max_size = [](const void *this_) { return static_cast(this_)->max_size(); }, + .capacity = [](const void *this_) { return static_cast(this_)->capacity(); }, + .data = [](const void *this_, std::size_t position) + { + auto self = static_cast(this_); + const_buffers_type res; + + const_buffer_pair tmp{ + self->array_one(), + self->array_two() + }; + auto cb = prefix(tmp, position); + + for (auto val: cb) + if (val.size() > 0u) + res.push_back(val); + return res; + }, + .prepare = [](void *this_, std::size_t n, std::size_t position) + { + auto self = static_cast(this_); + + auto sz = (std::min)(position + n, self->max_size()); + if (sz > self->size()) + self->resize(sz); + + mutable_buffer_pair buf{self->array_one(), self->array_two()}; + mutable_buffers_type res; + for (auto val: sans_prefix(buf, position)) + if (val.size() > 0u) + res.push_back(val); + return res; + }, + .commit =[](void *this_, std::size_t n, std::size_t &position) + { + position += n; + static_cast(this_)->resize(position); + }, + .consume =[](void *this_, std::size_t n, std::size_t &position) + { + auto self = static_cast(this_); + self->erase_begin(n); + position -= n; + } + }; + + template + constexpr static dynamic_buffer_view::vtable_t deque_vtable_{ + .size = [](const void *this_, std::size_t pos) { return pos; }, + .max_size = [](const void *this_) { return static_cast(this_)->max_size(); }, + .capacity = [](const void *this_) { return static_cast(this_)->size(); }, + .data = [](const void *this_, std::size_t position) + { + auto self = static_cast< const Buffer *>(this_); + position = (std::min)(position, self->size()); + const_buffers_type res; + + const auto bs = self->get_block_size(); + auto itr = self->begin(); + auto fbs = (std::min)(static_cast(itr.get_last() - itr.get_cur()), position); + + res.emplace_back(itr.get_cur(), fbs); + position -= fbs; + itr = std::next(itr, fbs); + while (position > 0u && itr != self->end() && (res.size() < res.capacity())) + { + auto seg = (std::min)(position, bs); + res.emplace_back(itr.get_cur(), seg); + position -= seg; + itr = std::next(itr, seg); + } + return res; + }, + .prepare = [](void *this_, std::size_t n, std::size_t position) + { + auto self = static_cast< Buffer *>(this_); + mutable_buffers_type res; + + auto sz = (std::min)(position + n, self->max_size()); + if (sz > self->size()) + self->resize(sz); + + auto itr = (std::min)(std::next(self->begin(), position), self->end()); + auto seg = itr, end = self->end(); + + while (itr != self->end()) + { + auto ptr = itr++; + position--; + if (itr == end) + { + res.emplace_back(&*seg, std::distance(seg, itr)); + break; + } + + if ((&*ptr + 1) == &*itr) // aligned + continue; + + res.emplace_back(&*seg, std::distance(seg, itr)); + seg = itr; + if (res.size() == res.capacity()) + break ; + } + // segment the thingy. + return res; + }, + .commit =[](void *this_, std::size_t n, std::size_t &position) + { + position += n; + static_cast(this_)->resize(position); + }, + .consume =[](void *this_, std::size_t n, std::size_t &position) + { + auto self = static_cast(this_); + using type = typename Buffer::value_type; + self->erase(self->begin(), (std::min)(std::next(self->begin(), n), self->end())); + position -= n; + } + }; + +}; + +template +dynamic_buffer_view::dynamic_buffer_view(Buffer &other) + : this_(&other), vtable_(vtables::dynamic_buffer_vtable_) +{} + +template + requires ( + requires (const Container & ct) + { + {ct.data()} -> std::convertible_to; + {ct.size()} -> std::convertible_to; + {ct.max_size()} -> std::convertible_to; + {ct.capacity()} -> std::convertible_to; + } + && requires (Container & ct) + { + {ct.data()} -> std::convertible_to; + {ct.resize(std::size_t())}; + {ct.erase(ct.begin(), ct.end())}; + } + && std::is_trivial_v + && sizeof(typename Container::value_type) == 1u) +dynamic_buffer_view::dynamic_buffer_view(Container &other) + : this_(&other), vtable_(vtables::contiguous_vtable_) +{ +} + +template + requires (std::is_trivial_v && sizeof(T) == 1u) +dynamic_buffer_view::dynamic_buffer_view(boost::circular_buffer &other) + : this_(&other), vtable_(vtables::circular_vtable_>) +{ +} + +template +requires (std::is_trivial_v && sizeof(T) == 1u) +dynamic_buffer_view::dynamic_buffer_view(boost::container::deque &other) + : this_(&other), vtable_(vtables::deque_vtable_>) {} + +} + +#endif //BOOST_ASYNC_IO_BUFFERS_IMPL_DYNAMIC_BUFFER_VIEW_HPP diff --git a/include/boost/async/io/buffers/impl/mutable_buffer_span.hpp b/include/boost/async/io/buffers/impl/mutable_buffer_span.hpp new file mode 100644 index 00000000..fab18dd1 --- /dev/null +++ b/include/boost/async/io/buffers/impl/mutable_buffer_span.hpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_IMPL_MUTABLE_BUFFER_SPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_IMPL_MUTABLE_BUFFER_SPAN_HPP + +namespace boost::async::io::buffers { + +inline +mutable_buffer_subspan +mutable_buffer_span:: +prefix_impl( + std::size_t n) const noexcept +{ + return mutable_buffer_subspan( + *this).prefix_impl(n); +} + +inline +mutable_buffer_subspan +mutable_buffer_span:: +suffix_impl( + std::size_t n) const noexcept +{ + return mutable_buffer_subspan( + *this).suffix_impl(n); +} + +//----------------------------------------------- + +// here because circular dependency +inline +mutable_buffer_subspan:: +mutable_buffer_subspan( + mutable_buffer_span const& s) noexcept + : p_(s.p_) + , n_(s.n_) + , p1_([&]() -> std::size_t + { + if(n_ > 0) + return p_[n_-1].size(); + return 0; + }()) +{ +} + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/impl/mutable_buffer_subspan.hpp b/include/boost/async/io/buffers/impl/mutable_buffer_subspan.hpp new file mode 100644 index 00000000..ec7ae9a2 --- /dev/null +++ b/include/boost/async/io/buffers/impl/mutable_buffer_subspan.hpp @@ -0,0 +1,144 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_IMPL_MUTABLE_BUFFER_SUBSPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_IMPL_MUTABLE_BUFFER_SUBSPAN_HPP + +#include +#include + +namespace boost::async::io::buffers { + +class mutable_buffer_subspan:: + const_iterator +{ + mutable_buffer_subspan const* s_ = nullptr; + std::size_t i_ = 0; + + friend class mutable_buffer_subspan; + + const_iterator( + mutable_buffer_subspan const& s, + std::size_t i) noexcept + : s_(&s) + , i_(i) + { + } + +public: + using value_type = mutable_buffer; + using reference = mutable_buffer; + using pointer = void; + using difference_type = std::ptrdiff_t; + using iterator_category = + std::bidirectional_iterator_tag; + + const_iterator() = default; + const_iterator( + const_iterator const&) = default; + const_iterator& operator=( + const_iterator const&) = default; + + bool + operator==( + const_iterator const& other) const noexcept + { + return + s_ == other.s_ && + i_ == other.i_; + } + + bool + operator!=( + const_iterator const& other) const noexcept + { + return !(*this == other); + } + + BOOST_ASYNC_DECL + reference + operator*() const noexcept; + + const_iterator& + operator++() noexcept + { + BOOST_ASSERT(i_ < s_->n_); + ++i_; + return *this; + } + + const_iterator + operator++(int) noexcept + { + auto temp = *this; + ++(*this); + return temp; + } + + const_iterator& + operator--() noexcept + { + BOOST_ASSERT(i_ > 0); + --i_; + return *this; + } + + const_iterator + operator--(int) noexcept + { + auto temp = *this; + --(*this); + return temp; + } +}; + +inline +auto +mutable_buffer_subspan:: +begin() const noexcept -> + const_iterator +{ + return { *this, 0 }; +} + +inline +auto +mutable_buffer_subspan:: +end() const noexcept -> + const_iterator +{ + return { *this, n_ }; +} + +inline +mutable_buffer_subspan:: +mutable_buffer_subspan( + mutable_buffer const* p, + std::size_t n, + std::size_t p0, + std::size_t p1) noexcept + : p_(p) + , n_(n) + , p0_(p0) + , p1_(p1) +{ + BOOST_ASSERT( + n_ > 1 || + p1_ >= p0_); + BOOST_ASSERT( + n_ == 0 || + p0 < p[0].size()); + BOOST_ASSERT( + n_ == 0 || + p1 <= p[n_ - 1].size()); +} + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/mutable_buffer.hpp b/include/boost/async/io/buffers/mutable_buffer.hpp new file mode 100644 index 00000000..05b91708 --- /dev/null +++ b/include/boost/async/io/buffers/mutable_buffer.hpp @@ -0,0 +1,182 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** Holds a buffer that can be modified. +*/ +class mutable_buffer +{ + unsigned char* p_ = nullptr; + std::size_t n_ = 0; + +public: + using value_type = mutable_buffer; + using const_iterator = + value_type const*; + + /** Constructor. + */ + mutable_buffer() = default; + + /** Constructor. + */ + mutable_buffer( + mutable_buffer const&) = default; + + /** Constructor. + */ + mutable_buffer( + void* data, + std::size_t size) noexcept + : p_(static_cast< + unsigned char*>(data)) + , n_(size) + { + } + + /** Constructor + */ + template + requires ( + requires (Container & ct) + { + {std::data(ct)} -> std::convertible_to; + {std::size(ct)} -> std::convertible_to; + } + && std::is_trivial_v) + mutable_buffer(Container & ct) : mutable_buffer(std::data(ct), sizeof(typename Container::value_type) * std::size(ct)) {} + + /** Constructor for arrays */ + template + requires std::is_trivial_v + mutable_buffer(T (&arr)[N]) : mutable_buffer(&arr[0], sizeof(T) * N) {} + + /** Constructor for arrays */ + template + requires std::is_trivial_v + mutable_buffer(std::pair p) : mutable_buffer(p.first, sizeof(T) * p.second) {} + + + /** Assignment. + */ + mutable_buffer& operator=( + mutable_buffer const&) = default; + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + // conversion to boost::asio::mutable_buffer + template + requires (std::constructible_from + && !std::same_as) + operator T() const noexcept + { + return T{ data(), size() }; + } +#endif + + void* + data() const noexcept + { + return p_; + } + + std::size_t + size() const noexcept + { + return n_; + } + + const_iterator + begin() const noexcept + { + return this; + } + + const_iterator + end() const noexcept + { + return this + 1; + } + + /** Remove a prefix from the buffer. + */ + mutable_buffer& + operator+=(std::size_t n) noexcept + { + if(n >= n_) + { + p_ = p_ + n_; + n_ = 0; + return *this; + } + p_ = p_ + n; + n_ -= n; + return *this; + } + + /** Return the buffer with a prefix removed. + */ + friend + mutable_buffer + operator+( + mutable_buffer b, + std::size_t n) noexcept + { + return b += n; + } + + /** Return the buffer with a prefix removed. + */ + friend + mutable_buffer + operator+( + std::size_t n, + mutable_buffer b) noexcept + { + return b += n; + } + +#ifndef BOOST_BUFFER_DOCS + friend + mutable_buffer + tag_invoke( + prefix_tag const&, + mutable_buffer const& b, + std::size_t n) noexcept + { + if(n < b.size()) + return { b.p_, n }; + return b; + } + + friend + mutable_buffer + tag_invoke( + suffix_tag const&, + mutable_buffer const& b, + std::size_t n) noexcept + { + if(n < b.size()) + return { b.p_ + b.n_ - n, n }; + return b; + } +#endif +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/mutable_buffer_pair.hpp b/include/boost/async/io/buffers/mutable_buffer_pair.hpp new file mode 100644 index 00000000..354bca21 --- /dev/null +++ b/include/boost/async/io/buffers/mutable_buffer_pair.hpp @@ -0,0 +1,119 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_PAIR_HPP +#define BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_PAIR_HPP + +#include +#include +#include + +namespace boost::async::io::buffers { + +/** A mutable buffer pair +*/ +class mutable_buffer_pair +{ +public: + using value_type = mutable_buffer; + + using const_iterator = value_type const*; + + /** Constructor. + */ + mutable_buffer_pair() = default; + + /** Constructor. + */ + mutable_buffer_pair( + mutable_buffer_pair const&) = default; + + /** Constructor. + */ + mutable_buffer_pair( + mutable_buffer const& b0, + mutable_buffer const& b1) noexcept + : b_{ b0, b1 } + { + } + + /** Assignment. + */ + mutable_buffer_pair& operator=( + mutable_buffer_pair const&) = default; + + mutable_buffer const& + operator[](unsigned i) const noexcept + { + BOOST_ASSERT(i < 2); + return b_[i]; + } + + mutable_buffer& + operator[](unsigned i) noexcept + { + BOOST_ASSERT(i < 2); + return b_[i]; + } + + const_iterator + begin() const noexcept + { + return b_; + } + + const_iterator + end() const noexcept + { + return b_ + 2; + } + + mutable_buffer * data() { return b_; } + const mutable_buffer * data() const { return b_; } + std::size_t size() const {return 2u;} + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + mutable_buffer_pair + tag_invoke( + prefix_tag const&, + mutable_buffer_pair const& b, + std::size_t n) noexcept + { + return b.prefix_impl(n); + } + + friend + mutable_buffer_pair + tag_invoke( + suffix_tag const&, + mutable_buffer_pair const& b, + std::size_t n) noexcept + { + return b.suffix_impl(n); + } +#endif + +private: + BOOST_ASYNC_DECL + mutable_buffer_pair + prefix_impl( + std::size_t n) const noexcept; + + BOOST_ASYNC_DECL + mutable_buffer_pair + suffix_impl( + std::size_t n) const noexcept; + + mutable_buffer b_[2]; +}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/mutable_buffer_span.hpp b/include/boost/async/io/buffers/mutable_buffer_span.hpp new file mode 100644 index 00000000..ea4cfcc8 --- /dev/null +++ b/include/boost/async/io/buffers/mutable_buffer_span.hpp @@ -0,0 +1,129 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_SPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_SPAN_HPP + +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** Holds a span of buffers that are modifiable. + + Objects of this type meet the requirements + of MutableBufferSequence. +*/ +class mutable_buffer_span +{ + mutable_buffer const* p_ = nullptr; + std::size_t n_ = 0; + + friend class mutable_buffer_subspan; + +public: + /** The type of buffer. + */ + using value_type = mutable_buffer; + + /** The type of iterators returned. + */ + using const_iterator = value_type const*; + + /** Constructor. + */ + mutable_buffer_span() = default; + + /** Constructor. + */ + mutable_buffer_span( + mutable_buffer const* p, + std::size_t n) noexcept + : p_(p) + , n_(n) + { + } + + /** Constructor. + */ + template + requires requires (const MutableBufferSequence & seq) + { + {seq.data()} -> std::convertible_to; + {seq.size()} -> std::same_as; + } + mutable_buffer_span( + const MutableBufferSequence & bs) noexcept + : p_(bs.data()) + , n_(bs.size()) + { + } + + /** Constructor. + */ + mutable_buffer_span( + mutable_buffer_span const&) = default; + + /** Assignment. + */ + mutable_buffer_span& operator=( + mutable_buffer_span const&) = default; + + /** Return an iterator to the beginning. + */ + const_iterator + begin() const noexcept + { + return p_; + } + + /** Return an iterator to the end. + */ + const_iterator + end() const noexcept + { + return p_ + n_; + } + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + mutable_buffer_subspan + tag_invoke( + prefix_tag const&, + mutable_buffer_span const& s, + std::size_t n) noexcept + { + return s.prefix_impl(n); + } + + friend + mutable_buffer_subspan + tag_invoke( + suffix_tag const&, + mutable_buffer_span const& s, + std::size_t n) noexcept + { + return s.suffix_impl(n); + } +#endif + +private: + mutable_buffer_subspan prefix_impl( + std::size_t n) const noexcept; + mutable_buffer_subspan suffix_impl( + std::size_t n) const noexcept; +}; + +} // boost::buffers + +#include + +#endif diff --git a/include/boost/async/io/buffers/mutable_buffer_subspan.hpp b/include/boost/async/io/buffers/mutable_buffer_subspan.hpp new file mode 100644 index 00000000..e14c3623 --- /dev/null +++ b/include/boost/async/io/buffers/mutable_buffer_subspan.hpp @@ -0,0 +1,120 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_SUBSPAN_HPP +#define BOOST_ASYNC_IO_BUFFERS_MUTABLE_BUFFER_SUBSPAN_HPP + +#include +#include + +namespace boost::async::io::buffers { + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS +class mutable_buffer_span; +#endif + +/** Holds a span of buffers whose contents are modifiable. + + Objects of this type meet the requirements + of MutableBufferSequence. +*/ +class mutable_buffer_subspan +{ + mutable_buffer const* p_ = nullptr; + std::size_t n_ = 0; + std::size_t p0_ = 0; + std::size_t p1_ = 0; + + friend class mutable_buffer_span; + + mutable_buffer_subspan( + mutable_buffer const* p, + std::size_t n, + std::size_t p0, + std::size_t p1) noexcept; + +public: + /** The type of buffer. + */ + using value_type = mutable_buffer; + + /** The type of iterators returned. + */ + class const_iterator; + + /** Constructor. + */ + mutable_buffer_subspan() = default; + + /** Constructor. + */ + BOOST_ASYNC_DECL + mutable_buffer_subspan( + mutable_buffer const* p, + std::size_t n) noexcept; + + /** Constructor. + */ + mutable_buffer_subspan( + mutable_buffer_span const& s) noexcept; + + /** Constructor. + */ + mutable_buffer_subspan( + mutable_buffer_subspan const&) = default; + + /** Assignment. + */ + mutable_buffer_subspan& operator=( + mutable_buffer_subspan const&) = default; + + /** Return an iterator to the beginning. + */ + const_iterator + begin() const noexcept; + + /** Return an iterator to the end. + */ + const_iterator + end() const noexcept; + +#ifndef BOOST_ASYNC_IO_BUFFERS_DOCS + friend + mutable_buffer_subspan + tag_invoke( + prefix_tag const&, + mutable_buffer_subspan const& s, + std::size_t n) noexcept + { + return s.prefix_impl(n); + } + + friend + mutable_buffer_subspan + tag_invoke( + suffix_tag const&, + mutable_buffer_subspan const& s, + std::size_t n) noexcept + { + return s.suffix_impl(n); + } +#endif + +private: + BOOST_ASYNC_DECL mutable_buffer_subspan + prefix_impl(std::size_t n) const noexcept; + BOOST_ASYNC_DECL mutable_buffer_subspan + suffix_impl(std::size_t n) const noexcept; +}; + +} // boost::buffers + +#include + +#endif diff --git a/include/boost/async/io/buffers/range.hpp b/include/boost/async/io/buffers/range.hpp new file mode 100644 index 00000000..c9758b77 --- /dev/null +++ b/include/boost/async/io/buffers/range.hpp @@ -0,0 +1,237 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_RANGE_HPP +#define BOOST_ASYNC_IO_BUFFERS_RANGE_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +#ifdef BOOST_ASYNC_IO_BUFFERS_DOCS + +/** Return an iterator to the beginning of the buffer sequence. +*/ +template +__see_below__ +begin(BufferSequence&& b) noexcept; + +/** Return an iterator to the end of the buffer sequence. +*/ +template +__see_below__ +end(BufferSequence&& b) noexcept; + +/** Return a range representing the buffer sequence. +*/ +template +__see_below__ +range(BufferSequence&& bs) noexcept; + +#else + +namespace detail { + +struct begin_impl +{ + template + auto + operator()( + MutableBuffer const& b) const noexcept -> + typename std::enable_if< + std::is_convertible< + MutableBuffer const*, + mutable_buffer const*>::value, + mutable_buffer const*>::type + { + return static_cast< + mutable_buffer const*>( + std::addressof(b)); + } + + template + auto + operator()( + ConstBuffer const& b) const noexcept -> + typename std::enable_if< + std::is_convertible< + ConstBuffer const*, + const_buffer const*>::value, + const_buffer const*>::type + { + return static_cast< + const_buffer const*>( + std::addressof(b)); + } + + template + auto + operator()( + BufferSequence& bs) const noexcept -> + typename std::enable_if< + ! std::is_convertible< + BufferSequence const*, + const_buffer const*>::value && + ! std::is_convertible< + BufferSequence const*, + mutable_buffer const*>::value, + decltype(bs.begin())>::type + { + return bs.begin(); + } + + template + auto + operator()( + BufferSequence const& bs) const noexcept -> + typename std::enable_if< + ! std::is_convertible< + BufferSequence const*, + const_buffer const*>::value && + ! std::is_convertible< + BufferSequence const*, + mutable_buffer const*>::value, + decltype(bs.begin())>::type + { + return bs.begin(); + } +}; + +struct end_impl +{ + template + auto + operator()( + MutableBuffer const& b) const noexcept -> + typename std::enable_if< + std::is_convertible< + MutableBuffer const*, + mutable_buffer const*>::value, + mutable_buffer const*>::type + { + return static_cast< + mutable_buffer const*>( + std::addressof(b)) + 1; + } + + template + auto + operator()( + ConstBuffer const& b) const noexcept -> + typename std::enable_if< + std::is_convertible< + ConstBuffer const*, + const_buffer const*>::value, + const_buffer const*>::type + { + return static_cast< + const_buffer const*>( + std::addressof(b)) + 1; + } + + template + auto + operator()( + BufferSequence& bs) const noexcept -> + typename std::enable_if< + ! std::is_convertible< + BufferSequence const*, + const_buffer const*>::value && + ! std::is_convertible< + BufferSequence const*, + mutable_buffer const*>::value, + decltype(bs.end())>::type + { + return bs.end(); + } + + template + auto + operator()( + BufferSequence const& bs) const noexcept -> + typename std::enable_if< + ! std::is_convertible< + BufferSequence const*, + const_buffer const*>::value && + ! std::is_convertible< + BufferSequence const*, + mutable_buffer const*>::value, + decltype(bs.end())>::type + { + return bs.end(); + } +}; + +} // detail + +constexpr detail::begin_impl begin{}; +constexpr detail::end_impl end{}; + +//------------------------------------------------ + +namespace detail { + +template +class iter_range +{ + using begin_type = decltype( + buffers::begin(std::declval())); + using end_type = decltype( + buffers::end(std::declval())); + + begin_type begin_; + end_type end_; + +public: + iter_range(T& t) noexcept + : begin_(buffers::begin(t)) + , end_(buffers::end(t)) + { + } + + begin_type + begin() const noexcept + { + return begin_; + } + + end_type + end() const noexcept + { + return end_; + } +}; + +struct range_impl +{ + template + auto + operator()( + BufferSequence&& bs) const noexcept -> + iter_range::type> + { + return { bs }; + } +}; + +} // detail + +constexpr detail::range_impl range{}; + +#endif + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/buffers/register.hpp b/include/boost/async/io/buffers/register.hpp new file mode 100644 index 00000000..d682173f --- /dev/null +++ b/include/boost/async/io/buffers/register.hpp @@ -0,0 +1,28 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_REGISTER_HPP +#define BOOST_ASYNC_IO_BUFFERS_REGISTER_HPP + +#include + +#include +#include + +namespace boost::async::io::buffers +{ + +using buffer_registration = asio::buffer_registration>; + +BOOST_ASYNC_DECL buffer_registration register_(buffers::mutable_buffer mutable_buffer); +BOOST_ASYNC_DECL buffer_registration register_(buffers::mutable_buffer_span mutable_buffers); +BOOST_ASYNC_DECL buffer_registration register_(buffers::mutable_buffer_subspan mutable_buffers); + +} + +#endif //BOOST_ASYNC_IO_BUFFERS_REGISTER_HPP diff --git a/include/boost/async/io/buffers/string_buffer.hpp b/include/boost/async/io/buffers/string_buffer.hpp new file mode 100644 index 00000000..12c029d5 --- /dev/null +++ b/include/boost/async/io/buffers/string_buffer.hpp @@ -0,0 +1,163 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_STRING_BUFFER_HPP +#define BOOST_ASYNC_IO_BUFFERS_STRING_BUFFER_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +/** A dynamic buffer using an underlying string +*/ +template< + buffer_byte CharT, + class Traits = std::char_traits, + class Allocator = std::allocator> + requires (sizeof(CharT) == 1u) +class basic_string_buffer +{ + std::basic_string< + CharT, Traits, Allocator>* s_; + std::size_t max_size_; + +public: + using string_type = std::basic_string< + CharT, Traits, Allocator>; + + unsigned char* data_ = nullptr; + std::size_t in_size_ = 0; + std::size_t out_size_ = 0; + +public: + using const_buffers_type = + const_buffer; + + using mutable_buffers_type = + mutable_buffer; + + ~basic_string_buffer() + { + if(s_) + s_->resize(in_size_); + } + + /** Constructor. + */ + basic_string_buffer( + basic_string_buffer&& other) noexcept + : s_(other.s_) + , max_size_(other.max_size_) + { + other.s_ = nullptr; + } + + /** Constructor. + */ + basic_string_buffer( + string_type* s, + std::size_t max_size = + std::size_t(-1)) noexcept + : s_(s) + , max_size_( + max_size > s_->max_size() + ? s_->max_size() + : max_size) + { + if(s_->size() > max_size_) + s_->resize(max_size_); + in_size_ = s_->size(); + } + + /** Assignment. + */ + basic_string_buffer& operator=( + basic_string_buffer const&) = delete; + + std::size_t + size() const noexcept + { + return in_size_; + } + + std::size_t + max_size() const noexcept + { + return max_size_; + } + + std::size_t + capacity() const noexcept + { + if(s_->capacity() <= max_size_) + return s_->capacity() - in_size_; + return max_size_ - in_size_; + } + + const_buffers_type + data() const noexcept + { + return { + s_->data(), + in_size_ }; + } + + mutable_buffers_type + prepare(std::size_t n) + { + // n exceeds available space + if(n > max_size_ - in_size_) + async::detail::throw_invalid_argument(); + + if( s_->size() < in_size_ + n) + s_->resize(in_size_ + n); + out_size_ = n; + return { + &(*s_)[in_size_], + out_size_ }; + } + + void + commit( + std::size_t n) noexcept + { + if(n < out_size_) + in_size_ += n; + else + in_size_ += out_size_; + out_size_ = 0; + } + + void + consume( + std::size_t n) noexcept + { + if(n < in_size_) + { + s_->erase(0, n); + in_size_ -= n; + } + else + { + in_size_ = 0; + } + out_size_ = 0; + } +}; + +using string_buffer = basic_string_buffer; + +} // boost::async::io::buffers + +#endif diff --git a/include/boost/async/io/buffers/tag_invoke.hpp b/include/boost/async/io/buffers/tag_invoke.hpp new file mode 100644 index 00000000..47badf5a --- /dev/null +++ b/include/boost/async/io/buffers/tag_invoke.hpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_ASYNC_IO_BUFFERS_TAG_INVOKE_HPP +#define BOOST_ASYNC_IO_BUFFERS_TAG_INVOKE_HPP + +#include + +namespace boost::async::io::buffers { + +/** size tag for tag_invoke. +*/ +struct size_tag {}; + +/** prefix tag for tag_invoke. +*/ +struct prefix_tag {}; + +/** suffix tag for tag-invoke. +*/ +struct suffix_tag {}; + +} // boost::buffers + +#endif diff --git a/include/boost/async/io/copy.hpp b/include/boost/async/io/copy.hpp new file mode 100644 index 00000000..8ecff75d --- /dev/null +++ b/include/boost/async/io/copy.hpp @@ -0,0 +1,28 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_COPY_HPP +#define BOOST_ASYNC_IO_COPY_HPP + +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise> +copy(stream & source, stream & sink); + +BOOST_ASYNC_DECL promise> +copy(stream & source, stream & sink, buffers::dynamic_buffer_view buffer, std::size_t chunk_size = 4096); + + + +} + +#endif //BOOST_ASYNC_IO_COPY_HPP diff --git a/include/boost/async/io/copy_n.hpp b/include/boost/async/io/copy_n.hpp new file mode 100644 index 00000000..e1778ce0 --- /dev/null +++ b/include/boost/async/io/copy_n.hpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_COPY_N_HPP +#define BOOST_ASYNC_IO_COPY_N_HPP + +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise> +copy_n(stream & source, stream & sink, std::size_t n); + +BOOST_ASYNC_DECL promise> +copy_n(stream & source, stream & sink, buffers::dynamic_buffer_view buffer, + std::size_t n, std::size_t chunk_size = 4096); + +} + +#endif //BOOST_ASYNC_IO_COPY_N_HPP diff --git a/include/boost/async/io/datagram_socket.hpp b/include/boost/async/io/datagram_socket.hpp new file mode 100644 index 00000000..85bd7ec4 --- /dev/null +++ b/include/boost/async/io/datagram_socket.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DATAGRAM_SOCKET_HPP +#define BOOST_ASYNC_IO_DATAGRAM_SOCKET_HPP + +#include +#include + +#include +#include + +namespace boost::async::io +{ + +struct [[nodiscard]] datagram_socket final : socket +{ + // duplicate onto another thread + system::result duplicate(const async::executor & executor = this_thread::get_executor()); + + datagram_socket(const async::executor & executor = this_thread::get_executor()); + datagram_socket(datagram_socket && lhs); + datagram_socket(native_handle_type h, protocol_type protocol = protocol_type(), + const async::executor & executor = this_thread::get_executor()); + datagram_socket(endpoint ep, + const async::executor & executor = this_thread::get_executor()); + private: + struct receive_op_; + struct receive_op_seq_; + struct receive_from_op_; + struct receive_from_op_seq_; + struct send_op_; + struct send_op_seq_; + struct send_to_op_; + struct send_to_op_seq_; + BOOST_ASYNC_DECL void adopt_endpoint_(endpoint & ep) override; + + public: + + [[nodiscard]] receive_op_seq_ receive(buffers::mutable_buffer_subspan buffers); + [[nodiscard]] receive_op_seq_ receive(buffers::mutable_buffer_span buffers); + [[nodiscard]] receive_op_ receive(buffers::mutable_buffer buffer); + [[nodiscard]] receive_from_op_seq_ receive_from(buffers::mutable_buffer_subspan buffers, endpoint & ep); + [[nodiscard]] receive_from_op_seq_ receive_from(buffers::mutable_buffer_span buffers, endpoint & ep); + [[nodiscard]] receive_from_op_ receive_from(buffers::mutable_buffer buffer, endpoint & ep); + [[nodiscard]] send_op_seq_ send(buffers::const_buffer_subspan buffers); + [[nodiscard]] send_op_seq_ send(buffers::const_buffer_span buffers); + [[nodiscard]] send_op_ send(buffers::const_buffer buffer); + [[nodiscard]] send_to_op_seq_ send_to(buffers::const_buffer_subspan buffers, const endpoint & target); + [[nodiscard]] send_to_op_seq_ send_to(buffers::const_buffer_span buffers, const endpoint & target); + [[nodiscard]] send_to_op_ send_to(buffers::const_buffer buffer, const endpoint & target); + + asio::basic_datagram_socket datagram_socket_; +}; + +inline system::result> make_pair(decltype(local_datagram) protocol) +{ + std::pair res; + auto c = connect_pair(protocol, res.first, res.second); + if (c) + return res; + else + return c.error(); +} + + +} + +#include + +#endif //BOOST_ASYNC_IO_DATAGRAM_SOCKET_HPP diff --git a/include/boost/async/io/detail/datagram_socket.hpp b/include/boost/async/io/detail/datagram_socket.hpp new file mode 100644 index 00000000..74c5a3dc --- /dev/null +++ b/include/boost/async/io/detail/datagram_socket.hpp @@ -0,0 +1,131 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DETAIL_DATAGRAM_SOCKET_HPP +#define BOOST_ASYNC_IO_DETAIL_DATAGRAM_SOCKET_HPP + +#include +#include + +namespace boost::async::io +{ + +struct datagram_socket::receive_op_ final : transfer_op +{ + receive_op_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer buffer) + : datagram_socket_(rs), buffer_(buffer) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::mutable_buffer buffer_; +}; + + +struct datagram_socket::receive_op_seq_ final : transfer_op +{ + receive_op_seq_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer_subspan buffer) + : datagram_socket_(rs), buffer_(buffer) {} + + receive_op_seq_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer_span buffer) + : datagram_socket_(rs), buffer_(buffer) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::mutable_buffer_subspan buffer_; +}; + + +struct datagram_socket::receive_from_op_ final : transfer_op +{ + receive_from_op_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer buffer, endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::mutable_buffer buffer_; + endpoint &ep_; +}; + + +struct datagram_socket::receive_from_op_seq_ final : transfer_op +{ + receive_from_op_seq_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer_subspan buffer, endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + receive_from_op_seq_(asio::basic_datagram_socket & rs, + buffers::mutable_buffer_span buffer, endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::mutable_buffer_subspan buffer_; + endpoint & ep_; +}; + +struct datagram_socket::send_op_ final : transfer_op +{ + send_op_(asio::basic_datagram_socket & rs, + buffers::const_buffer buffer) + : datagram_socket_(rs), buffer_(buffer) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::const_buffer buffer_; +}; + + +struct datagram_socket::send_op_seq_ final : transfer_op +{ + send_op_seq_(asio::basic_datagram_socket & rs, + buffers::const_buffer_subspan buffer) + : datagram_socket_(rs), buffer_(buffer) {} + send_op_seq_(asio::basic_datagram_socket & rs, + buffers::const_buffer_span buffer) + : datagram_socket_(rs), buffer_(buffer) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::const_buffer_subspan buffer_; +}; + + +struct datagram_socket::send_to_op_ final : transfer_op +{ + send_to_op_(asio::basic_datagram_socket & rs, + buffers::const_buffer buffer, const endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::const_buffer buffer_; + const endpoint &ep_; +}; + + +struct datagram_socket::send_to_op_seq_ final : transfer_op +{ + send_to_op_seq_(asio::basic_datagram_socket & rs, + buffers::const_buffer_subspan buffer, const endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + + send_to_op_seq_(asio::basic_datagram_socket & rs, + buffers::const_buffer_span buffer, const endpoint & ep) + : datagram_socket_(rs), buffer_(buffer), ep_(ep) {} + void initiate(async::completion_handler h); + private: + asio::basic_datagram_socket &datagram_socket_; + buffers::const_buffer_subspan buffer_; + const endpoint & ep_; +}; + +} + +#endif //BOOST_ASYNC_IO_DETAIL_DATAGRAM_SOCKET_HPP diff --git a/include/boost/async/io/detail/duplicate.hpp b/include/boost/async/io/detail/duplicate.hpp new file mode 100644 index 00000000..0f3451a6 --- /dev/null +++ b/include/boost/async/io/detail/duplicate.hpp @@ -0,0 +1,28 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DUPLICATE_HPP +#define BOOST_ASYNC_IO_DUPLICATE_HPP + +#include +#include +#include + +namespace boost::async::detail::io +{ + +#if defined(BOOST_ASIO_WINDOWS) +BOOST_ASYNC_DECL system::result duplicate_handle(HANDLE h); +BOOST_ASYNC_DECL system::result duplicate_socket(SOCKET fd); +#else +BOOST_ASYNC_DECL system::result duplicate_handle(int fd); +BOOST_ASYNC_DECL system::result duplicate_socket(int fd); +#endif + +} + +#endif //BOOST_ASYNC_IO_DUPLICATE_HPP diff --git a/include/boost/async/io/detail/random_access_device.hpp b/include/boost/async/io/detail/random_access_device.hpp new file mode 100644 index 00000000..3f963afc --- /dev/null +++ b/include/boost/async/io/detail/random_access_device.hpp @@ -0,0 +1,92 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DETAIL_RANDOM_ACCESS_DEVICE_HPP +#define BOOST_ASYNC_IO_DETAIL_RANDOM_ACCESS_DEVICE_HPP + +#include +#include + +namespace boost::async::io +{ + +struct random_access_device::read_some_at_op_ final : transfer_op +{ + read_some_at_op_(read_some_at_op_ && ) noexcept = default; + + void initiate(completion_handler h) override + { + random_access_device_.async_read_some_at_impl_(offset_, {&buffer_, 1u}, std::move(h)); + } + + read_some_at_op_(random_access_device & rs, std::uint64_t offset, buffers::mutable_buffer buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + + private: + random_access_device & random_access_device_; + std::uint64_t offset_; + buffers::mutable_buffer buffer_; +}; + + +struct random_access_device::read_some_at_op_seq_ final : transfer_op +{ + + void initiate(completion_handler h) override + { + random_access_device_.async_read_some_at_impl_(offset_, buffer_, std::move(h)); + } + + read_some_at_op_seq_(random_access_device & rs, std::uint64_t offset, buffers::mutable_buffer_subspan buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + read_some_at_op_seq_(random_access_device & rs, std::uint64_t offset, buffers::mutable_buffer_span buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + + private: + random_access_device & random_access_device_; + std::uint64_t offset_; + buffers::mutable_buffer_subspan buffer_; +}; +struct random_access_device::write_some_at_op_ final : transfer_op +{ + write_some_at_op_(write_some_at_op_ && ) noexcept = default; + void initiate(completion_handler h) override + { + random_access_device_.async_write_some_at_impl_(offset_, {&buffer_, 1}, std::move(h)); + } + + write_some_at_op_(random_access_device & rs, std::uint64_t offset, buffers::const_buffer buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + + private: + random_access_device & random_access_device_; + std::uint64_t offset_; + buffers::const_buffer buffer_; +}; + + +struct random_access_device::write_some_at_op_seq_ final : transfer_op +{ + void initiate(completion_handler h) override + { + random_access_device_.async_write_some_at_impl_(offset_, buffer_, std::move(h)); + } + + write_some_at_op_seq_(random_access_device & rs, std::uint64_t offset, buffers::const_buffer_subspan buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + write_some_at_op_seq_(random_access_device & rs, std::uint64_t offset, buffers::const_buffer_span buffer) + : random_access_device_(rs), offset_(offset), buffer_(buffer) {} + + private: + random_access_device & random_access_device_; + std::uint64_t offset_; + buffers::const_buffer_subspan buffer_; +}; + +} + +#endif //BOOST_ASYNC_IO_DETAIL_RANDOM_ACCESS_DEVICE_HPP diff --git a/include/boost/async/io/detail/seq_packet_socket.hpp b/include/boost/async/io/detail/seq_packet_socket.hpp new file mode 100644 index 00000000..f1d3b20c --- /dev/null +++ b/include/boost/async/io/detail/seq_packet_socket.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DETAIL_SEQ_PACKET_SOCKET_HPP +#define BOOST_ASYNC_IO_DETAIL_SEQ_PACKET_SOCKET_HPP + +#include +#include + +namespace boost::async::io +{ + +struct seq_packet_socket::receive_op_ final : transfer_op +{ + receive_op_(asio::basic_seq_packet_socket & rs, + buffers::mutable_buffer buffer, message_flags &out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_seq_packet_socket &seq_packet_socket_; + buffers::mutable_buffer buffer_; + message_flags &out_flags_; +}; + + +struct seq_packet_socket::receive_op_seq_ final : transfer_op +{ + receive_op_seq_(asio::basic_seq_packet_socket & rs, + buffers::mutable_buffer_subspan buffer, message_flags &out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + receive_op_seq_(asio::basic_seq_packet_socket & rs, + buffers::mutable_buffer_span buffer, message_flags &out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_seq_packet_socket &seq_packet_socket_; + buffers::mutable_buffer_subspan buffer_; + message_flags &out_flags_; +}; + +struct seq_packet_socket::send_op_ final : transfer_op +{ + send_op_(asio::basic_seq_packet_socket & rs, + buffers::const_buffer buffer, message_flags out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_seq_packet_socket &seq_packet_socket_; + buffers::const_buffer buffer_; + message_flags out_flags_; +}; + + +struct seq_packet_socket::send_op_seq_ final : transfer_op +{ + send_op_seq_(asio::basic_seq_packet_socket & rs, + buffers::const_buffer_subspan buffer, message_flags out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + send_op_seq_(asio::basic_seq_packet_socket & rs, + buffers::const_buffer_span buffer, message_flags out_flags) + : seq_packet_socket_(rs), buffer_(buffer), out_flags_(out_flags) {} + void initiate(async::completion_handler h) override; + private: + asio::basic_seq_packet_socket &seq_packet_socket_; + buffers::const_buffer_subspan buffer_; + message_flags out_flags_; +}; + +} + +#endif //BOOST_ASYNC_IO_DETAIL_SEQ_PACKET_SOCKET_HPP diff --git a/include/boost/async/io/detail/socket.hpp b/include/boost/async/io/detail/socket.hpp new file mode 100644 index 00000000..6954807e --- /dev/null +++ b/include/boost/async/io/detail/socket.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DETAIL_SOCKET_HPP +#define BOOST_ASYNC_IO_DETAIL_SOCKET_HPP + +#include +#include +#include + +namespace boost::async::io +{ + +struct socket::wait_op_ : result_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler); + + wait_op_(asio::basic_socket & socket, + asio::socket_base::wait_type wt) : socket_(socket), wt_(wt) {} + private: + asio::basic_socket & socket_; + asio::socket_base::wait_type wt_; +}; + + +struct socket::connect_op_ : result_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler); + + connect_op_(socket* socket, + endpoint ep) : socket_(socket), ep_(ep) {} + private: + socket * socket_; + endpoint ep_; +}; + + +} + +#endif //BOOST_ASYNC_IO_SOCKET_HPP diff --git a/include/boost/async/io/detail/ssl.hpp b/include/boost/async/io/detail/ssl.hpp new file mode 100644 index 00000000..0e337992 --- /dev/null +++ b/include/boost/async/io/detail/ssl.hpp @@ -0,0 +1,59 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_SSL_HPP +#define BOOST_ASYNC_SSL_HPP + +#include +#include + +namespace boost::async::io +{ +struct ssl_stream::handshake_op_ final : result_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + handshake_op_(ssl_stream & stream, handshake_type ht) : stream(stream), ht(ht) {} + private: + + ssl_stream & stream; + handshake_type ht; +}; + +struct ssl_stream::handshake_op_buf_ final : transfer_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + handshake_op_buf_(ssl_stream & stream, + handshake_type ht, + buffers::const_buffer buf) : stream(stream), ht(ht), buf(buf) {} + private: + ssl_stream & stream; + handshake_type ht; + buffers::const_buffer buf; +}; + +struct ssl_stream::handshake_op_buf_seq_ final : transfer_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + handshake_op_buf_seq_(ssl_stream & stream, handshake_type ht, + buffers::const_buffer_subspan buf) : stream(stream), ht(ht), buf(buf) {} + private: + ssl_stream & stream; + handshake_type ht; + buffers::const_buffer_subspan buf; +}; + +struct ssl_stream::shutdown_op_ final : result_op +{ + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + shutdown_op_(ssl_stream & stream) : stream(stream) {} + private: + ssl_stream & stream; +}; + +} + +#endif //BOOST_ASYNC_SSL_HPP diff --git a/include/boost/async/io/detail/stream.hpp b/include/boost/async/io/detail/stream.hpp new file mode 100644 index 00000000..a2f6f181 --- /dev/null +++ b/include/boost/async/io/detail/stream.hpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_DETAIL_STREAM_HPP +#define BOOST_ASYNC_IO_DETAIL_STREAM_HPP + +#include +#include + +namespace boost::async::io +{ + +struct stream::read_some_op_ final : transfer_op +{ + read_some_op_(read_some_op_ && ) noexcept = default; + read_some_op_(stream & rs, buffers::mutable_buffer buffer) + : rstream_(rs), buffer_(buffer) {} + + BOOST_ASYNC_DECL void initiate(completion_handler h) override; + private: + stream & rstream_; + buffers::mutable_buffer buffer_; +}; + + +struct stream::read_some_op_seq_ final : transfer_op +{ + read_some_op_seq_(stream & rs, buffers::mutable_buffer_subspan buffer) + : rstream_(rs), buffer_(buffer) {} + read_some_op_seq_(stream & rs, buffers::mutable_buffer_span buffer) + : rstream_(rs), buffer_(buffer) {} + + BOOST_ASYNC_DECL void initiate(completion_handler h) override; + private: + stream & rstream_; + buffers::mutable_buffer_subspan buffer_; +}; + +struct stream::write_some_op_ final : transfer_op +{ + write_some_op_(write_some_op_ && ) noexcept = default; + write_some_op_(stream & rs, buffers::const_buffer buffer) + : rstream_(rs), buffer_(buffer) {} + + BOOST_ASYNC_DECL void initiate(completion_handler h) override; + + private: + stream & rstream_; + buffers::const_buffer buffer_; +}; + + +struct stream::write_some_op_seq_ final : transfer_op +{ + write_some_op_seq_(stream & rs, buffers::const_buffer_subspan buffer) + : rstream_(rs), buffer_(buffer) {} + write_some_op_seq_(stream & rs, buffers::const_buffer_span buffer) + : rstream_(rs), buffer_(buffer) {} + + BOOST_ASYNC_DECL void initiate(completion_handler h) override; + + private: + stream & rstream_; + buffers::const_buffer_subspan buffer_; +}; + +} + +#endif //BOOST_ASYNC_IO_DETAIL_STREAM_HPP diff --git a/include/boost/async/io/endpoint.hpp b/include/boost/async/io/endpoint.hpp new file mode 100644 index 00000000..0e1d86ce --- /dev/null +++ b/include/boost/async/io/endpoint.hpp @@ -0,0 +1,337 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_ENDPOINT_HPP +#define BOOST_ASYNC_IO_ENDPOINT_HPP + +#include +#include + +#include +#include +#include +#include + +#include + +namespace boost::async::detail +{ + +BOOST_ASYNC_DECL BOOST_NORETURN void +throw_bad_endpoint_access( + source_location const& loc); + +} + +namespace boost::async::io +{ + +struct endpoint; +struct stream_socket; + + +#if __GNUC__ && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsubobject-linkage" +#endif + +struct protocol_type +{ + using family_t = decltype(BOOST_ASIO_OS_DEF(AF_INET)); + using type_t = decltype(BOOST_ASIO_OS_DEF(SOCK_STREAM)); + using protocol_t = decltype(BOOST_ASIO_OS_DEF(IPPROTO_TCP)); + + constexpr family_t family() const noexcept {return family_;}; + constexpr type_t type() const noexcept {return type_;}; + constexpr protocol_t protocol() const noexcept {return protocol_;}; + + constexpr explicit + protocol_type(family_t family = static_cast(0), + type_t type = static_cast(0), + protocol_t protocol = static_cast(0)) noexcept + : family_(family), type_(type), protocol_(protocol) + {} + + template + requires requires (const OtherProtocol & op) + { + {op.family()} -> std::convertible_to; + {op.type()} -> std::convertible_to; + {op.protocol()} -> std::convertible_to; + } + constexpr protocol_type(const OtherProtocol & op) noexcept + : family_(op.family()), type_(op.type()), protocol_(op.protocol()) + {} + + friend + constexpr auto operator<=>(const protocol_type & , const protocol_type &) noexcept = default; + + using endpoint = io::endpoint; + // for the asio acceptor + using socket = stream_socket; + private: + family_t family_ = static_cast(0); + type_t type_ = static_cast(0); + protocol_t protocol_ = static_cast(0); +}; + +template(0), + protocol_type::type_t Type = static_cast(0), + protocol_type::protocol_t Protocol = static_cast(0)> +struct static_protocol +{ + using family_t = protocol_type::family_t ; + using type_t = protocol_type::type_t ; + using protocol_t = protocol_type::protocol_t; + + constexpr family_t family() const noexcept {return Family;}; + constexpr type_t type() const noexcept {return Type;}; + constexpr protocol_t protocol() const noexcept {return Protocol;}; + + using endpoint = io::endpoint; +}; + + +constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip {}; +constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v4 {}; +constexpr static_protocol(0), BOOST_ASIO_OS_DEF(IPPROTO_IP)> ip_v6 {}; +constexpr static_protocol tcp {}; +constexpr static_protocol tcp_v4{}; +constexpr static_protocol tcp_v6{}; +constexpr static_protocol udp {}; +constexpr static_protocol udp_v4{}; +constexpr static_protocol udp_v6{}; +constexpr static_protocol icmp {}; +constexpr static_protocol local_stream {}; +constexpr static_protocol local_datagram {}; +constexpr static_protocol local_seqpacket{}; +constexpr static_protocol local_protocol {}; + +template +struct make_endpoint_tag {}; + +template +struct get_endpoint_tag {}; + +struct endpoint +{ + using storage_type = boost::asio::detail::sockaddr_storage_type; + using addr_type = boost::asio::detail::socket_addr_type; + void resize(std::size_t size) + { + BOOST_ASSERT(size < sizeof(storage_)); + size_ = size; + } + + void * data() {return &storage_; } + const void * data() const {return &storage_; } + std::size_t size() const {return size_;} + std::size_t capacity() const {return sizeof(storage_);} + + void set_type (protocol_type::type_t type) { type_ = type;} + void set_protocol(protocol_type::protocol_t protocol) { protocol_ = protocol;} + + protocol_type protocol() const + { + return protocol_type{static_cast(base_.sa_family), type_, protocol_}; + } + + endpoint() = default; + endpoint(const endpoint & ep) : storage_(ep.storage_), size_(ep.size_), protocol_(ep.protocol_), type_(ep.type_) + { + } + + template + requires requires (make_endpoint_tag proto, + addr_type* addr, Args && ... args) + { + {tag_invoke(proto, addr, std::forward(args)...)} -> std::convertible_to; + } + endpoint(static_protocol proto, Args && ... args) + : base_{}, + protocol_(Protocol), type_(Type) + { + size_ = tag_invoke(make_endpoint_tag{}, &base_, std::forward(args)...); + } + + template + requires requires (get_endpoint_tag tag, + protocol_type actual, + const addr_type * addr) {{tag_invoke(tag, actual, addr)};} + friend auto get_if(const endpoint * ep) + -> decltype(tag_invoke(get_endpoint_tag{}, protocol_type{}, + static_cast(nullptr))) + { + const auto actual = ep->protocol(); + if (Protocol.type() != 0 && actual.type() != 0 && actual.type() != Protocol.type()) + return nullptr; + if (Protocol.protocol() != 0 && actual.protocol() != 0 && actual.protocol() != Protocol.protocol()) + return nullptr; + return tag_invoke(get_endpoint_tag{}, ep->protocol(), &ep->base_); + } + + private: + union { + boost::asio::detail::socket_addr_type base_{}; + storage_type storage_; + }; + std::size_t size_{sizeof(base_)}; + protocol_type::protocol_t protocol_ = static_cast(0); + protocol_type::type_t type_ = static_cast(0); +}; + +#if __GNUC__ && !defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +class bad_endpoint_access : public std::exception +{ + public: + + bad_endpoint_access() noexcept = default; + + char const * what() const noexcept + { + return "bad_endpoint_access"; + } +}; + +template + requires requires (get_endpoint_tag tag, + protocol_type actual, + endpoint::addr_type * addr) {{tag_invoke(tag, actual, addr)};} +auto get(const endpoint & ep, const boost::source_location & loc = BOOST_CURRENT_LOCATION) +{ + auto e = get_if(&ep); + if (!e) + detail::throw_bad_endpoint_access(loc); + return *e; +} + +struct local_endpoint +{ + core::string_view path() const { return unix_.sun_path;} + private: + union { + boost::asio::detail::sockaddr_storage_type addr_; + boost::asio::detail::sockaddr_un_type unix_; + }; +}; + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + core::string_view sv); + +BOOST_ASYNC_DECL +const local_endpoint* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr); + +struct ip_address_v4 +{ + std::uint16_t port() const {return in_.sin_port;} + std::uint32_t addr() const {return in_.sin_addr.s_addr;} + BOOST_ASYNC_DECL static_string<15> addr_str() const; + private: + union { + boost::asio::detail::sockaddr_storage_type addr_{}; + boost::asio::detail::sockaddr_in4_type in_; + }; +}; + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::uint32_t address, + std::uint16_t port); + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port); + +BOOST_ASYNC_DECL +const ip_address_v4* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr); + + +struct ip_address_v6 +{ + std::uint16_t port() const {return in_.sin6_port;} + std::array addr() const + { + std::array res; + const auto & in = in_.sin6_addr.s6_addr; + std::copy(std::begin(in), std::end(in), res.begin()); + return res; + } + BOOST_ASYNC_DECL static_string<45> addr_str() const; + private: + union { + boost::asio::detail::sockaddr_storage_type addr_{}; + boost::asio::detail::sockaddr_in6_type in_; + }; +}; + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::span address, + std::uint16_t port); + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port); + +BOOST_ASYNC_DECL +const ip_address_v6* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr); + + +struct ip_address +{ + + bool is_ipv6() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET6); } + bool is_ipv4() const { return addr_.ss_family == BOOST_ASIO_OS_DEF(AF_INET); } + + std::uint16_t port() const {return addr_.ss_family == AF_INET ? in_.sin_port : in6_.sin6_port;} + + BOOST_ASYNC_DECL std::array addr() const; + BOOST_ASYNC_DECL static_string<45> addr_str() const; + private: + union { + boost::asio::detail::sockaddr_storage_type addr_{}; + boost::asio::detail::sockaddr_in4_type in_; + boost::asio::detail::sockaddr_in6_type in6_; + }; +}; + +BOOST_ASYNC_DECL +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port); + +BOOST_ASYNC_DECL +const ip_address* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr); + + +} + +#endif //BOOST_ASYNC_IO_ENDPOINT_HPP diff --git a/include/boost/async/io/file.hpp b/include/boost/async/io/file.hpp new file mode 100644 index 00000000..81537b6d --- /dev/null +++ b/include/boost/async/io/file.hpp @@ -0,0 +1,55 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_FILE_HPP +#define BOOST_ASYNC_IO_FILE_HPP + +#include + +#if defined(BOOST_ASIO_HAS_FILE) + +#include +#include +#include + +namespace boost::async::io +{ + + +struct file +{ + using seek_basis = boost::asio::file_base::seek_basis; + using flags = boost::asio::file_base::flags; + + [[nodiscard]] BOOST_ASYNC_DECL system::result open(core::string_view file, + flags open_flags = flags::read_write); + [[nodiscard]] BOOST_ASYNC_DECL system::result close(); + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel(); + [[nodiscard]] BOOST_ASYNC_DECL bool is_open() const; + [[nodiscard]] BOOST_ASYNC_DECL system::result resize(std::uint64_t size); + [[nodiscard]] BOOST_ASYNC_DECL system::result size() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result sync_all(); + [[nodiscard]] BOOST_ASYNC_DECL system::result sync_data(); + + + using native_handle_type = asio::basic_file::native_handle_type; + BOOST_ASYNC_DECL native_handle_type native_handle(); + + BOOST_ASYNC_DECL system::result assign(native_handle_type native_handle); + BOOST_ASYNC_DECL system::result release(); + + private: + asio::basic_file & file_; + public: + file(asio::basic_file & file) : file_(file) {} +}; + +} + +#endif +#endif //BOOST_ASYNC_IO_FILE_HPP diff --git a/include/boost/async/io/pipe.hpp b/include/boost/async/io/pipe.hpp new file mode 100644 index 00000000..8156dd22 --- /dev/null +++ b/include/boost/async/io/pipe.hpp @@ -0,0 +1,80 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_PIPE_HPP +#define BOOST_ASYNC_IO_PIPE_HPP + +#include + +#include +#include + +namespace boost::async::io +{ + +system::result> make_pipe( + const async::executor & executor = this_thread::get_executor() + ); + +struct readable_pipe final : stream +{ + BOOST_ASYNC_DECL system::result close() override; + BOOST_ASYNC_DECL system::result cancel() override; + BOOST_ASYNC_DECL bool is_open() const override; + + using native_handle_type = typename asio::basic_readable_pipe::native_handle_type; + native_handle_type native_handle() {return pipe_.native_handle();} + + BOOST_ASYNC_DECL readable_pipe(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL readable_pipe(native_handle_type native_handle, const async::executor & executor = this_thread::get_executor()); + + BOOST_ASYNC_DECL system::result assign(native_handle_type native_handle); + BOOST_ASYNC_DECL system::result release(); + + BOOST_ASYNC_DECL system::result duplicate(const async::executor & executor = this_thread::get_executor()); + + void write_some(buffers::mutable_buffer_subspan buffers) = delete; + void write_some(buffers::mutable_buffer buffer) = delete; + + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + + friend system::result> make_pipe(const async::executor & exec); + asio::basic_readable_pipe pipe_; +}; + +struct writable_pipe final : stream +{ + BOOST_ASYNC_DECL system::result close() override; + BOOST_ASYNC_DECL system::result cancel() override; + BOOST_ASYNC_DECL bool is_open() const override; + + using native_handle_type = typename asio::basic_readable_pipe::native_handle_type; + native_handle_type native_handle() {return pipe_.native_handle();} + + BOOST_ASYNC_DECL writable_pipe(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL writable_pipe(native_handle_type native_handle, + const async::executor & executor = this_thread::get_executor()); + + BOOST_ASYNC_DECL system::result assign(native_handle_type native_handle); + BOOST_ASYNC_DECL system::result release(); + BOOST_ASYNC_DECL system::result duplicate(const async::executor & executor = this_thread::get_executor()); + + void read_some(buffers::mutable_buffer_subspan buffers) = delete; + void read_some(buffers::mutable_buffer buffer) = delete; + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + friend system::result> make_pipe(const async::executor & exec); + asio::basic_writable_pipe pipe_; +}; + + +} + +#endif //BOOST_ASYNC_IO_PIPE_HPP diff --git a/include/boost/async/io/popen.hpp b/include/boost/async/io/popen.hpp new file mode 100644 index 00000000..733c4c7a --- /dev/null +++ b/include/boost/async/io/popen.hpp @@ -0,0 +1,64 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_POPEN_HPP +#define BOOST_ASYNC_IO_POPEN_HPP + +#include +#include +#include +#include + +namespace boost::async::io +{ + +struct popen : stream +{ + using wait_result = system::result; + using handle_type = typename boost::process::v2::basic_process::handle_type; + using native_handle_type = typename boost::process::v2::basic_process::native_handle_type; + + BOOST_ASYNC_DECL popen(boost::process::v2::filesystem::path executable, + std::initializer_list args, + process_initializer initializer = {}, + const async::executor & executor = this_thread::get_executor()); + + + BOOST_ASYNC_DECL popen(boost::process::v2::filesystem::path executable, + std::span args, + process_initializer initializer = {}, + const async::executor & executor = this_thread::get_executor()); + + [[nodiscard]] BOOST_ASYNC_DECL system::result interrupt(); + [[nodiscard]] BOOST_ASYNC_DECL system::result request_exit(); + [[nodiscard]] BOOST_ASYNC_DECL system::result suspend(); + [[nodiscard]] BOOST_ASYNC_DECL system::result resume(); + [[nodiscard]] BOOST_ASYNC_DECL system::result terminate(); + [[nodiscard]] BOOST_ASYNC_DECL handle_type detach(); + [[nodiscard]] BOOST_ASYNC_DECL system::result running(); + + + [[nodiscard]] pid_type id() const; + + [[nodiscard]] system::result close() override; + [[nodiscard]] system::result cancel() override; + [[nodiscard]] bool is_open() const override; + + private: + void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h)override; + void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + public: + [[nodiscard]] process::wait_op_ wait() { return process::wait_op_{popen_}; } + process::wait_op_ operator co_await () { return wait(); } + private: + boost::process::v2::basic_popen popen_; +}; + + +} + +#endif //BOOST_ASYNC_IO_POPEN_HPP diff --git a/include/boost/async/io/process.hpp b/include/boost/async/io/process.hpp new file mode 100644 index 00000000..db09ea30 --- /dev/null +++ b/include/boost/async/io/process.hpp @@ -0,0 +1,87 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_PROCESS_HPP +#define BOOST_ASYNC_IO_PROCESS_HPP + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace boost::async::io +{ + +using boost::process::v2::pid_type; + +struct process_initializer +{ + process::v2::process_stdio stdio; + process::v2::process_start_dir start_dir{process::v2::filesystem::current_path()}; + process::v2::process_environment env{process::v2::environment::current()}; +}; + +struct process +{ + using wait_result = system::result; + using handle_type = typename boost::process::v2::basic_process::handle_type; + using native_handle_type = typename boost::process::v2::basic_process::native_handle_type; + + BOOST_ASYNC_DECL process(boost::process::v2::filesystem::path executable, + std::initializer_list args, + process_initializer initializer = {}, + const async::executor & executor = this_thread::get_executor()); + + + BOOST_ASYNC_DECL process(boost::process::v2::filesystem::path executable, + std::span args, + process_initializer initializer = {}, + const async::executor & executor = this_thread::get_executor()); + + BOOST_ASYNC_DECL process(pid_type pid, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL process(pid_type pid, native_handle_type native_handle, + const async::executor & executor = this_thread::get_executor()); + + [[nodiscard]] BOOST_ASYNC_DECL system::result interrupt(); + [[nodiscard]] BOOST_ASYNC_DECL system::result request_exit(); + [[nodiscard]] BOOST_ASYNC_DECL system::result suspend(); + [[nodiscard]] BOOST_ASYNC_DECL system::result resume(); + [[nodiscard]] BOOST_ASYNC_DECL system::result terminate(); + [[nodiscard]] BOOST_ASYNC_DECL handle_type detach(); + [[nodiscard]] BOOST_ASYNC_DECL system::result running(); + + + [[nodiscard]] pid_type id() const; + + private: + struct wait_op_ final : result_op + { + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + + wait_op_(boost::process::v2::basic_process & process) : process_(process) {} + private: + boost::process::v2::basic_process & process_; + }; + public: + [[nodiscard]] wait_op_ wait() { return wait_op_{process_}; } + wait_op_ operator co_await () { return wait(); } + private: + boost::process::v2::basic_process process_; + friend struct popen; +}; + + +} + +#endif //BOOST_ASYNC_IO_PROCESS_HPP diff --git a/include/boost/async/io/random_access_device.hpp b/include/boost/async/io/random_access_device.hpp new file mode 100644 index 00000000..a9fd8f2d --- /dev/null +++ b/include/boost/async/io/random_access_device.hpp @@ -0,0 +1,43 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_RANDOM_ACCESS_DEVICE_HPP +#define BOOST_ASYNC_IO_RANDOM_ACCESS_DEVICE_HPP + +#include + +namespace boost::async::io +{ + +struct random_access_device +{ + [[nodiscard]] virtual system::result close() = 0; + [[nodiscard]] virtual system::result cancel() = 0; + [[nodiscard]] virtual bool is_open() const = 0; + + virtual ~random_access_device() = default; + protected: + virtual void async_read_some_at_impl_ (std::uint64_t offset, buffers::mutable_buffer_subspan buffer, async::completion_handler h) = 0; + virtual void async_write_some_at_impl_(std::uint64_t offset, buffers:: const_buffer_subspan buffer, async::completion_handler h) = 0; + private: + struct read_some_at_op_; + struct read_some_at_op_seq_; + struct write_some_at_op_; + struct write_some_at_op_seq_; + public: + BOOST_ASYNC_DECL [[nodiscard]] read_some_at_op_seq_ read_some_at(std::uint64_t offset, buffers::mutable_buffer_span buffers); + BOOST_ASYNC_DECL [[nodiscard]] read_some_at_op_seq_ read_some_at(std::uint64_t offset, buffers::mutable_buffer_subspan buffers); + BOOST_ASYNC_DECL [[nodiscard]] read_some_at_op_ read_some_at(std::uint64_t offset, buffers::mutable_buffer buffer); + BOOST_ASYNC_DECL [[nodiscard]] write_some_at_op_seq_ write_some_at(std::uint64_t offset, buffers::const_buffer_span buffers); + BOOST_ASYNC_DECL [[nodiscard]] write_some_at_op_seq_ write_some_at(std::uint64_t offset, buffers::const_buffer_subspan buffers); + BOOST_ASYNC_DECL [[nodiscard]] write_some_at_op_ write_some_at(std::uint64_t offset, buffers::const_buffer buffer); +}; + +} + +#include +#endif //BOOST_ASYNC_IO_STREAM_HPP diff --git a/include/boost/async/io/random_access_file.hpp b/include/boost/async/io/random_access_file.hpp new file mode 100644 index 00000000..a34873b1 --- /dev/null +++ b/include/boost/async/io/random_access_file.hpp @@ -0,0 +1,40 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_RANDOM_ACCESS_FILE_HPP +#define BOOST_ASYNC_RANDOM_ACCESS_FILE_HPP + +#include +#if defined(BOOST_ASIO_HAS_FILE) + +#include +#include + +namespace boost::async::io +{ + +struct random_access_file : file, random_access_device +{ + BOOST_ASYNC_DECL system::result duplicate(); + + [[nodiscard]] BOOST_ASYNC_DECL system::result close() override; + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel() override; + [[nodiscard]] BOOST_ASYNC_DECL bool is_open() const override; + + BOOST_ASYNC_DECL random_access_file(); + BOOST_ASYNC_DECL random_access_file(random_access_file && lhs); + BOOST_ASYNC_DECL random_access_file(native_handle_type h); + BOOST_ASYNC_DECL random_access_file(core::string_view file, flags open_flags = flags::read_write); + private: + BOOST_ASYNC_DECL void async_read_some_at_impl_ (std::uint64_t offset, buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_at_impl_(std::uint64_t offset, buffers::const_buffer_subspan buffer, async::completion_handler h) override; + asio::basic_random_access_file random_access_file_; + +}; +} +#endif +#endif //BOOST_ASYNC_RANDOM_ACCESS_FILE_HPP diff --git a/include/boost/async/io/read.hpp b/include/boost/async/io/read.hpp new file mode 100644 index 00000000..00f88da9 --- /dev/null +++ b/include/boost/async/io/read.hpp @@ -0,0 +1,47 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_READ_HPP +#define BOOST_ASYNC_IO_READ_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise read(stream & source, buffers::mutable_buffer buffer); +BOOST_ASYNC_DECL promise read(stream & source, buffers::mutable_buffer_span buffer); +BOOST_ASYNC_DECL promise read(stream & source, buffers::mutable_buffer_subspan buffer); + +template + requires (!std::convertible_to) +promise read(stream & source, MutableBufferSequence && buffer) +{ + buffers::mutable_buffer buf[asio::detail::max_iov_len]; + + transfer_result tr{}; + + for (auto itr = buffers::begin(buffer), end = buffers::end(buffer); itr != end;) + { + auto ie = (std::min)(end, std::next(itr, asio::detail::max_iov_len)); + auto oe = std::copy(itr, ie, buf); + + buffers::mutable_buffer_span cbs{buf, std::distance(buf, oe)}; + tr += co_await read(source, cbs); + itr = ie; + } + co_return tr; +} + + +} + +#endif //BOOST_ASYNC_IO_READ_HPP diff --git a/include/boost/async/io/read_all.hpp b/include/boost/async/io/read_all.hpp new file mode 100644 index 00000000..c34d209d --- /dev/null +++ b/include/boost/async/io/read_all.hpp @@ -0,0 +1,25 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_READ_ALL_HPP +#define BOOST_ASYNC_IO_READ_ALL_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise read_all(stream & source, buffers::dynamic_buffer_view buffer, + std::size_t chunk_size = 4096); + +} + +#endif //BOOST_ASYNC_IO_READ_ALL_HPP diff --git a/include/boost/async/io/read_at.hpp b/include/boost/async/io/read_at.hpp new file mode 100644 index 00000000..5798dd53 --- /dev/null +++ b/include/boost/async/io/read_at.hpp @@ -0,0 +1,47 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_READ_AT_HPP +#define BOOST_ASYNC_READ_AT_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise read_at(random_access_device & source, std::uint64_t offset, buffers::mutable_buffer buffer); +BOOST_ASYNC_DECL promise read_at(random_access_device & source, std::uint64_t offset, buffers::mutable_buffer_span buffer); +BOOST_ASYNC_DECL promise read_at(random_access_device & source, std::uint64_t offset, buffers::mutable_buffer_subspan buffer); +template + requires (!std::convertible_to) +promise read(random_access_device & source, std::uint64_t offset, MutableBufferSequence && buffer) +{ + buffers::mutable_buffer buf[asio::detail::max_iov_len]; + + transfer_result tr{}; + + for (auto itr = buffers::begin(buffer), end = buffers::end(buffer); itr != end;) + { + auto ie = (std::min)(end, std::next(itr, asio::detail::max_iov_len)); + auto oe = std::copy(itr, ie, buf); + + buffers::mutable_buffer_span cbs{buf, std::distance(buf, oe)}; + tr += co_await read_at(source, offset + tr.transferred, cbs); + itr = ie; + } + co_return tr; +} + + +} + + +#endif //BOOST_ASYNC_READ_AT_HPP diff --git a/include/boost/async/io/read_until.hpp b/include/boost/async/io/read_until.hpp new file mode 100644 index 00000000..3726bc7d --- /dev/null +++ b/include/boost/async/io/read_until.hpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_READ_UNTIL_HPP +#define BOOST_ASYNC_IO_READ_UNTIL_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise read_until(stream & source, buffers::dynamic_buffer_view buffer, + char delim, std::size_t chunk_size = 4096); +BOOST_ASYNC_DECL promise read_until(stream & source, buffers::dynamic_buffer_view buffer, + core::string_view delim, std::size_t chunk_size = 4096); + +} + +#endif //BOOST_ASYNC_IO_READ_UNTIL_HPP diff --git a/include/boost/async/io/resolver.hpp b/include/boost/async/io/resolver.hpp new file mode 100644 index 00000000..0be9c3fc --- /dev/null +++ b/include/boost/async/io/resolver.hpp @@ -0,0 +1,72 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_RESOLVER_HPP +#define BOOST_ASYNC_IO_RESOLVER_HPP + +#include +#include + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + + +struct resolver +{ + using resolve_result = system::result>; + + BOOST_ASYNC_DECL resolver(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL resolver(resolver && ) = delete; + + BOOST_ASYNC_DECL void cancel(); + + private: + + struct resolve_op_ final : result_op + { + resolve_op_(asio::ip::basic_resolver & resolver, + core::string_view host, core::string_view service) + : resolver_(resolver), host_(host), service_(service) {} + BOOST_ASYNC_DECL void initiate(completion_handler); + private: + asio::ip::basic_resolver & resolver_; + core::string_view host_; + core::string_view service_; + }; + public: + + [[nodiscard]] resolve_op_ resolve(core::string_view host, core::string_view service) + { + return resolve_op_{resolver_, host, service}; + } + private: + asio::ip::basic_resolver resolver_; +}; + +struct lookup final : result_op +{ + lookup(core::string_view host, core::string_view service) + : host_(host), service_(service) {} + + BOOST_ASYNC_DECL + void initiate(completion_handler); + + private: + core::string_view host_; + core::string_view service_; + std::optional> resolver_; +}; + +} + +#endif //BOOST_ASYNC_IO_RESOLVER_HPP diff --git a/include/boost/async/io/result.hpp b/include/boost/async/io/result.hpp new file mode 100644 index 00000000..bf6d568d --- /dev/null +++ b/include/boost/async/io/result.hpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_RESULT_HPP +#define BOOST_ASYNC_IO_RESULT_HPP + +#include +#include +#include + +#include + +namespace boost::async::io +{ + +template +struct result_op +{ + using value_type = T; + using error_type = Error; + + virtual void ready(async::handler h) {}; + virtual void initiate(async::completion_handler complete) = 0 ; + virtual ~result_op() = default; + + result_op() = default; + result_op(result_op && lhs) + : error(std::move(lhs.error)) + , result(std::move(lhs.result)) + { + BOOST_ASSERT(!lhs.resource); + } + + bool await_ready() + { + ready(handler(result)); + return result.has_value(); + } + + template + bool await_suspend(std::coroutine_handle h) + { + try + { + auto & res = resource.emplace(buffer, sizeof(buffer), + asio::get_associated_allocator(h.promise(), this_thread::get_allocator()).resource()); + completed_immediately = detail::completed_immediately_t::initiating; + initiate(completion_handler{h, result, &res, &completed_immediately}); + if (completed_immediately == detail::completed_immediately_t::initiating) + completed_immediately = detail::completed_immediately_t::no; + return completed_immediately != detail::completed_immediately_t::yes; + } + catch(...) + { + error = std::current_exception(); + return false; + } + } + + [[nodiscard]] system::result await_resume() + { + if (error) + std::rethrow_exception(std::exchange(error, nullptr)); + if (std::get<0>(*this->result)) + return system::result(system::in_place_error, std::get<0>(std::move(*this->result))); + else + return system::result(system::in_place_value, std::get<1>(std::move(*this->result))); + } + + struct vawaitable + { + vawaitable(result_op * op_) : op_(op_) {} + + bool await_ready() { return op_->await_ready();} + + template + bool await_suspend(std::coroutine_handle h) + { + return op_->await_suspend(h); + } + + T await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION) + { + return op_->await_resume().value(loc); + } + private: + result_op * op_; + }; + + vawaitable value() && { return vawaitable{this}; } + private: + std::exception_ptr error; + std::optional> result; + char buffer[2048]; + std::optional resource; + detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no; +}; + + + +template +struct result_op +{ + using value_type = void; + using error_type = Error; + + virtual void ready(async::handler h) {}; + virtual void initiate(async::completion_handler complete) = 0 ; + virtual ~result_op() = default; + + result_op() noexcept = default; + result_op(result_op && lhs) + : error(std::move(lhs.error)) + , result(std::move(lhs.result)) + { + BOOST_ASSERT(!lhs.resource); + } + + bool await_ready() + { + ready(handler(result)); + return result.has_value(); + } + + template + bool await_suspend(std::coroutine_handle h) + { + try + { + auto & res = resource.emplace(buffer, sizeof(buffer), + asio::get_associated_allocator(h.promise(), this_thread::get_allocator()).resource()); + initiate(completion_handler{h, result, &res, &completed_immediately}); + return completed_immediately != detail::completed_immediately_t::yes; + } + catch(...) + { + error = std::current_exception(); + return false; + } + } + + [[nodiscard]] system::result await_resume() + { + if (error) + std::rethrow_exception(std::exchange(error, nullptr)); + if (std::get<0>(*this->result)) + return system::result(system::in_place_error, std::get<0>(std::move(*this->result))); + else + return system::in_place_value; + + } + + struct vawaitable + { + vawaitable(result_op * op_) : op_(op_) {} + + bool await_ready() { return op_->await_ready();} + + template + bool await_suspend(std::coroutine_handle h) + { + return op_->await_suspend(h); + } + + void await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION) + { + op_->await_resume().value(loc); + } + private: + result_op * op_; + }; + + vawaitable value() && { return vawaitable{this}; } + + private: + std::exception_ptr error; + std::optional> result; + char buffer[2048]; + std::optional resource; + detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no; +}; + + +} + +#endif //BOOST_ASYNC_IO_RESULT_HPP diff --git a/include/boost/async/io/seq_packet_socket.hpp b/include/boost/async/io/seq_packet_socket.hpp new file mode 100644 index 00000000..f18c96e0 --- /dev/null +++ b/include/boost/async/io/seq_packet_socket.hpp @@ -0,0 +1,74 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SEQ_PACKET_SOCKET_HPP +#define BOOST_ASYNC_IO_SEQ_PACKET_SOCKET_HPP + +#include +#include +#include +#include + +namespace boost::async::io +{ + +struct [[nodiscard]] seq_packet_socket final : socket +{ + // duplicate onto another thread + system::result duplicate(); + + using message_flags = boost::asio::socket_base::message_flags; + /// Peek at incoming data without removing it from the input queue. + static constexpr int message_peek = boost::asio::socket_base::message_peek; + + /// Process out-of-band data. + static constexpr int message_out_of_band = boost::asio::socket_base::message_out_of_band; + + /// Specify that the data should not be subject to routing. + static constexpr int message_do_not_route = boost::asio::socket_base::message_do_not_route; + + /// Specifies that the data marks the end of a record. + static constexpr int message_end_of_record = boost::asio::socket_base::message_end_of_record; + + BOOST_ASYNC_DECL seq_packet_socket(); + BOOST_ASYNC_DECL seq_packet_socket(seq_packet_socket && lhs); + BOOST_ASYNC_DECL seq_packet_socket(native_handle_type h, protocol_type protocol = protocol_type()); + BOOST_ASYNC_DECL seq_packet_socket(endpoint ep); + private: + struct receive_op_; + struct receive_op_seq_; + struct send_op_; + struct send_op_seq_; + BOOST_ASYNC_DECL void adopt_endpoint_(endpoint & ep) override; + public: + + [[nodiscard]] BOOST_ASYNC_DECL receive_op_seq_ receive(buffers::mutable_buffer_subspan buffers, message_flags & out_flags); + [[nodiscard]] BOOST_ASYNC_DECL receive_op_seq_ receive(buffers::mutable_buffer_span buffers, message_flags & out_flags); + [[nodiscard]] BOOST_ASYNC_DECL receive_op_ receive(buffers::mutable_buffer buffer, message_flags & out_flags); + [[nodiscard]] BOOST_ASYNC_DECL send_op_seq_ send(buffers::const_buffer_subspan buffers, message_flags out_flags); + [[nodiscard]] BOOST_ASYNC_DECL send_op_seq_ send(buffers::const_buffer_span buffers, message_flags out_flags); + [[nodiscard]] BOOST_ASYNC_DECL send_op_ send(buffers::const_buffer buffer, message_flags out_flags); + + asio::basic_seq_packet_socket seq_packet_socket_; +}; + + +inline system::result> make_pair(decltype(local_seqpacket) protocol) +{ + std::pair res; + auto c = connect_pair(protocol, res.first, res.second); + if (c) + return res; + else + return c.error(); +} + +} + +#include + +#endif //BOOST_ASYNC_IO_SEQ_PACKET_SOCKET_HPP diff --git a/include/boost/async/io/serial_port.hpp b/include/boost/async/io/serial_port.hpp new file mode 100644 index 00000000..96615546 --- /dev/null +++ b/include/boost/async/io/serial_port.hpp @@ -0,0 +1,67 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SERIAL_PORT_HPP +#define BOOST_ASYNC_IO_SERIAL_PORT_HPP + +#include + +#include +#include + +namespace boost::async::io +{ + +struct [[nodiscard]] serial_port final : stream +{ + BOOST_ASYNC_DECL system::result close() override; + BOOST_ASYNC_DECL system::result cancel() override; + BOOST_ASYNC_DECL bool is_open() const override; + + [[nodiscard]] BOOST_ASYNC_DECL system::result send_break(); + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_baud_rate(unsigned rate); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_baud_rate(); + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_character_size(unsigned rate); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_character_size(); + + using flow_control = asio::serial_port_base::flow_control::type; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_flow_control(flow_control rate); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_flow_control(); + + using parity = asio::serial_port_base::parity::type; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_parity(parity rate); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_parity(); + + using native_handle_type = typename asio::basic_serial_port::native_handle_type; + native_handle_type native_handle() {return serial_port_.native_handle();} + + BOOST_ASYNC_DECL serial_port(); + BOOST_ASYNC_DECL serial_port(serial_port && lhs) = default; + BOOST_ASYNC_DECL serial_port(core::string_view device); + BOOST_ASYNC_DECL serial_port(native_handle_type native_handle); + + [[nodiscard]] BOOST_ASYNC_DECL system::result assign(native_handle_type native_handle); + [[nodiscard]] BOOST_ASYNC_DECL system::result release(); + [[nodiscard]] BOOST_ASYNC_DECL system::result duplicate(); + + BOOST_ASYNC_DECL [[nodiscard]] system::result open(core::string_view device); + + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + + asio::basic_serial_port serial_port_; +}; + + +} + +#endif //BOOST_ASYNC_IO_SERIAL_PORT_HPP diff --git a/include/boost/async/io/signal_set.hpp b/include/boost/async/io/signal_set.hpp new file mode 100644 index 00000000..70063310 --- /dev/null +++ b/include/boost/async/io/signal_set.hpp @@ -0,0 +1,52 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SIGNAL_SET_HPP +#define BOOST_ASYNC_IO_SIGNAL_SET_HPP + +#include + +#include +#include + +#include + +namespace boost::async::io +{ + +struct signal_set +{ + using wait_result = system::result; + + BOOST_ASYNC_DECL signal_set(); + BOOST_ASYNC_DECL signal_set(std::initializer_list sigs); + + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel(); + [[nodiscard]] BOOST_ASYNC_DECL system::result clear(); + [[nodiscard]] BOOST_ASYNC_DECL system::result add(int signal_number); + [[nodiscard]] BOOST_ASYNC_DECL system::result remove(int signal_number); + + + private: + struct wait_op_ final : result_op + { + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + wait_op_(boost::asio::basic_signal_set & signal_set) : signal_set_(signal_set) {} + private: + boost::asio::basic_signal_set & signal_set_; + }; + public: + [[nodiscard]] wait_op_ wait() { return wait_op_{signal_set_}; } + wait_op_ operator co_await () { return wait(); } + private: + boost::asio::basic_signal_set signal_set_; +}; + + +} + +#endif //BOOST_ASYNC_IO_SIGNAL_SET_HPP diff --git a/include/boost/async/io/sleep.hpp b/include/boost/async/io/sleep.hpp new file mode 100644 index 00000000..33c9b55b --- /dev/null +++ b/include/boost/async/io/sleep.hpp @@ -0,0 +1,90 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SLEEP_HPP +#define BOOST_ASYNC_IO_SLEEP_HPP + + +#include +#include + +#include + +#include + +namespace boost::async::detail::io +{ + + +struct steady_sleep final : async::io::result_op<> +{ + steady_sleep(const std::chrono::steady_clock::time_point & tp) : time_{tp} {} + steady_sleep(const std::chrono::steady_clock::duration & du) + : time_{std::chrono::steady_clock::now() + du} {} + + BOOST_ASYNC_DECL void ready(async::handler h); + BOOST_ASYNC_DECL void initiate(async::completion_handler complete); + + private: + std::chrono::steady_clock::time_point time_; + std::optional, + executor>> timer_; +}; + + +struct system_sleep final : async::io::result_op<> +{ + system_sleep(const std::chrono::system_clock::time_point & tp) : time_{tp} {} + system_sleep(const std::chrono::system_clock::duration & du) + : time_{std::chrono::system_clock::now() + du} {} + + BOOST_ASYNC_DECL void ready(async::handler h); + BOOST_ASYNC_DECL void initiate(async::completion_handler complete); + + private: + std::chrono::system_clock::time_point time_; + std::optional, + executor>> timer_; +}; + +} + +namespace boost::async::io +{ + + + +// NOTE: these don't need to be coros, we can optimize that out. Not sure that's worth it though +inline detail::io::steady_sleep sleep(const std::chrono::steady_clock::duration & d) { return d;} +inline detail::io::steady_sleep sleep(const std::chrono::steady_clock::time_point & tp) { return tp;} +inline detail::io::system_sleep sleep(const std::chrono::system_clock::time_point & tp) { return tp;} + +template +detail::io::steady_sleep sleep(const std::chrono::time_point & tp) +{ + return sleep(std::chrono::time_point_cast(tp)); +} + + +template +detail::io::system_sleep sleep(const std::chrono::time_point & tp) +{ + return sleep(std::chrono::time_point_cast(tp)); +} + + +template +detail::io::steady_sleep sleep(const std::chrono::duration & dur) +{ + return sleep(std::chrono::duration_cast(dur)); +} + +} + +#endif //BOOST_ASYNC_IO_SLEEP_HPP diff --git a/include/boost/async/io/socket.hpp b/include/boost/async/io/socket.hpp new file mode 100644 index 00000000..2d154dba --- /dev/null +++ b/include/boost/async/io/socket.hpp @@ -0,0 +1,104 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SOCKET_HPP +#define BOOST_ASYNC_IO_SOCKET_HPP + +#include +#include +#include + +namespace boost::async::io +{ + +struct socket +{ + [[nodiscard]] system::result open(protocol_type prot = protocol_type {}); + [[nodiscard]] system::result close(); + [[nodiscard]] system::result cancel(); + [[nodiscard]] bool is_open() const; + + // asio acceptor compatibility + template + struct rebind_executor {using other = socket;}; + + using shutdown_type = asio::socket_base::shutdown_type; + using wait_type = asio::socket_base::wait_type; + using message_flags = asio::socket_base::message_flags; + constexpr static int message_peek = asio::socket_base::message_peek; + constexpr static int message_out_of_band = asio::socket_base::message_out_of_band; + constexpr static int message_do_not_route = asio::socket_base::message_do_not_route; + constexpr static int message_end_of_record = asio::socket_base::message_end_of_record; + + using native_handle_type = asio::basic_socket::native_handle_type; + native_handle_type native_handle(); + + [[nodiscard]] BOOST_ASYNC_DECL system::result shutdown(shutdown_type = shutdown_type::shutdown_both); + + [[nodiscard]] BOOST_ASYNC_DECL system::result local_endpoint() const; + [[nodiscard]] BOOST_ASYNC_DECL system::result remote_endpoint() const; + + + BOOST_ASYNC_DECL system::result assign(protocol_type protocol, native_handle_type native_handle); + BOOST_ASYNC_DECL system::result release(); + + /// copied from what asio does + [[nodiscard]] BOOST_ASYNC_DECL system::result bytes_readable(); + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_debug(bool debug); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_debug() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_do_not_route(bool do_not_route); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_do_not_route() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_enable_connection_aborted(bool enable_connection_aborted); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_enable_connection_aborted() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_keep_alive(bool keep_alive); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_keep_alive() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_linger(bool linger, int timeout); + [[nodiscard]] BOOST_ASYNC_DECL system::result> get_linger() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_receive_buffer_size(std::size_t receive_buffer_size); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_receive_buffer_size() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_send_buffer_size(std::size_t send_buffer_size); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_send_buffer_size() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_receive_low_watermark(std::size_t receive_low_watermark); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_receive_low_watermark() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_send_low_watermark(std::size_t send_low_watermark); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_send_low_watermark() const; + + [[nodiscard]] BOOST_ASYNC_DECL system::result set_reuse_address(bool reuse_address); + [[nodiscard]] BOOST_ASYNC_DECL system::result get_reuse_address() const; + + private: + + virtual void adopt_endpoint_(endpoint & ) {} + struct connect_op_; + struct wait_op_; + friend struct acceptor; + asio::basic_socket & socket_; + + public: + socket(asio::basic_socket & socket) : socket_(socket) {} + + BOOST_ASYNC_DECL connect_op_ connect(endpoint ep); + BOOST_ASYNC_DECL wait_op_ wait(wait_type = wait_type::wait_read); + BOOST_ASYNC_DECL wait_op_ operator co_await(); // co_await sock; +}; + +BOOST_ASYNC_DECL system::result connect_pair(protocol_type protocol, socket & socket1, socket & socket2); + +} + +#include + +#endif //BOOST_ASYNC_IO_SOCKET_HPP diff --git a/include/boost/async/io/ssl.hpp b/include/boost/async/io/ssl.hpp new file mode 100644 index 00000000..ade9dc48 --- /dev/null +++ b/include/boost/async/io/ssl.hpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SSL_HPP +#define BOOST_ASYNC_IO_SSL_HPP + +#include +#include +#include +#include + +namespace boost::async::detail +{ + +struct ssl_stream_base +{ + + template + ssl_stream_base(Args && ...args) : ssl_stream_(std::forward(args)...) {} + + asio::ssl::stream> ssl_stream_; +}; + +} + +namespace boost::async::io +{ + +struct ssl_stream : private detail::ssl_stream_base, stream, socket +{ + using shutdown_result = system::result; + + [[nodiscard]] BOOST_ASYNC_DECL system::result close() override; + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel() override; + [[nodiscard]] BOOST_ASYNC_DECL bool is_open() const override; + + BOOST_ASYNC_DECL ssl_stream(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL ssl_stream(ssl_stream && steam); + BOOST_ASYNC_DECL ssl_stream(stream_socket && socket); + + BOOST_ASYNC_DECL ssl_stream(asio::ssl::context & ctx, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL ssl_stream(asio::ssl::context & ctx, stream_socket && socket); + + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + + struct handshake_op_; + struct handshake_op_buf_; + struct handshake_op_buf_seq_; + struct shutdown_op_; + void adopt_endpoint_(endpoint & ep) override; + public: + using handshake_type = boost::asio::ssl::stream_base::handshake_type; + BOOST_ASYNC_DECL handshake_op_ async_handshake(handshake_type ht); + BOOST_ASYNC_DECL handshake_op_buf_ async_handshake(handshake_type ht, buffers::const_buffer buf); + BOOST_ASYNC_DECL handshake_op_buf_seq_ async_handshake(handshake_type ht, buffers::const_buffer_subspan buf); + BOOST_ASYNC_DECL handshake_op_buf_seq_ async_handshake(handshake_type ht, buffers::const_buffer_span buf); + BOOST_ASYNC_DECL shutdown_op_ async_shutdown(); + private: + + // duplicate onto another thread + system::result duplicate() = delete; +}; + +} + +#include + + +#endif //BOOST_ASYNC_IO_SSL_HPP diff --git a/include/boost/async/io/steady_timer.hpp b/include/boost/async/io/steady_timer.hpp new file mode 100644 index 00000000..9ba6cd2f --- /dev/null +++ b/include/boost/async/io/steady_timer.hpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_STEADY_TIMER_HPP +#define BOOST_ASYNC_IO_STEADY_TIMER_HPP + +#include +#include + +#include + +namespace boost::async::detail::io +{ +struct steady_sleep; +} + +namespace boost::async::io +{ + +struct steady_timer +{ + using wait_result = system::result; + + /// The clock type. + typedef std::chrono::steady_clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + BOOST_ASYNC_DECL steady_timer(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL steady_timer(const time_point& expiry_time, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL steady_timer(const duration& expiry_time, const async::executor & executor = this_thread::get_executor()); + + BOOST_ASYNC_DECL void cancel(); + + BOOST_ASYNC_DECL time_point expiry() const; + BOOST_ASYNC_DECL void reset(const time_point& expiry_time); + BOOST_ASYNC_DECL void reset(const duration& expiry_time); + BOOST_ASYNC_DECL bool expired() const; + + private: + struct wait_op_ final : result_op + { + void ready(async::handler h) override + { + if (timer_->expired()) h({}); + } + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + wait_op_(steady_timer * timer) : timer_(timer) {} + private: + steady_timer * timer_; + }; + + public: + [[nodiscard]] wait_op_ wait() { return wait_op_{this}; } + wait_op_ operator co_await () { return wait(); } + private: + friend struct detail::io::steady_sleep; + boost::asio::basic_waitable_timer, + executor> timer_; +}; + +} + +#endif //BOOST_ASYNC_IO_STEADY_TIMER_HPP diff --git a/include/boost/async/io/stream.hpp b/include/boost/async/io/stream.hpp new file mode 100644 index 00000000..1ceaa969 --- /dev/null +++ b/include/boost/async/io/stream.hpp @@ -0,0 +1,52 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_STREAM_HPP +#define BOOST_ASYNC_IO_STREAM_HPP + +#include + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + + + + +struct stream +{ + [[nodiscard]] virtual system::result close() = 0; + [[nodiscard]] virtual system::result cancel() = 0; + [[nodiscard]] virtual bool is_open() const = 0; + + virtual ~stream() = default; + protected: + virtual void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) = 0; + virtual void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) = 0; + private: + struct read_some_op_; + struct read_some_op_seq_; + struct write_some_op_; + struct write_some_op_seq_; + public: + BOOST_ASYNC_DECL [[nodiscard]] read_some_op_seq_ read_some(buffers::mutable_buffer_subspan buffers); + BOOST_ASYNC_DECL [[nodiscard]] read_some_op_seq_ read_some(buffers::mutable_buffer_span buffers); + BOOST_ASYNC_DECL [[nodiscard]] read_some_op_ read_some(buffers::mutable_buffer buffer); + BOOST_ASYNC_DECL [[nodiscard]] write_some_op_seq_ write_some(buffers::const_buffer_subspan buffers); + BOOST_ASYNC_DECL [[nodiscard]] write_some_op_seq_ write_some(buffers::const_buffer_span buffers); + BOOST_ASYNC_DECL [[nodiscard]] write_some_op_ write_some(buffers::const_buffer buffer); +}; + +} + +#include +#endif //BOOST_ASYNC_IO_STREAM_HPP diff --git a/include/boost/async/io/stream_file.hpp b/include/boost/async/io/stream_file.hpp new file mode 100644 index 00000000..b2da4751 --- /dev/null +++ b/include/boost/async/io/stream_file.hpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_STREAM_FILE_HPP +#define BOOST_ASYNC_IO_STREAM_FILE_HPP + +#include +#if defined(BOOST_ASIO_HAS_FILE) + +#include +#include + +namespace boost::async::io +{ + +struct stream_file : file, stream +{ + BOOST_ASYNC_DECL + system::result duplicate(const async::executor & executor = this_thread::get_executor()); + + [[nodiscard]] BOOST_ASYNC_DECL system::result close() override; + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel() override; + [[nodiscard]] BOOST_ASYNC_DECL bool is_open() const override; + + BOOST_ASYNC_DECL stream_file(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL stream_file(stream_file && lhs); + BOOST_ASYNC_DECL stream_file(native_handle_type h, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL stream_file(core::string_view file, flags open_flags = flags::read_write, + const async::executor & executor = this_thread::get_executor()); + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + asio::basic_stream_file stream_file_; + +}; + +} + +#endif +#endif //BOOST_ASYNC_IO_STREAM_FILE_HPP diff --git a/include/boost/async/io/stream_socket.hpp b/include/boost/async/io/stream_socket.hpp new file mode 100644 index 00000000..64565471 --- /dev/null +++ b/include/boost/async/io/stream_socket.hpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_STREAM_SOCKET_HPP +#define BOOST_ASYNC_IO_STREAM_SOCKET_HPP + +#include +#include +#include +#include + +namespace boost::async::io +{ + +struct [[nodiscard]] stream_socket final : stream, socket +{ + // duplicate onto another thread + BOOST_ASYNC_DECL system::result duplicate(const async::executor & executor = this_thread::get_executor()); + + [[nodiscard]] BOOST_ASYNC_DECL system::result close() override; + [[nodiscard]] BOOST_ASYNC_DECL system::result cancel() override; + [[nodiscard]] BOOST_ASYNC_DECL bool is_open() const override; + + BOOST_ASYNC_DECL stream_socket(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL stream_socket(stream_socket && lhs); + BOOST_ASYNC_DECL stream_socket(native_handle_type h, protocol_type protocol = protocol_type(), + const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL stream_socket(endpoint ep, const async::executor & executor = this_thread::get_executor()); + private: + BOOST_ASYNC_DECL void async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) override; + BOOST_ASYNC_DECL void async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) override; + asio::basic_stream_socket stream_socket_; + friend struct ssl_stream; + BOOST_ASYNC_DECL void adopt_endpoint_(endpoint & ep) override; +}; + +inline system::result> make_pair(decltype(local_stream) protocol) +{ + std::pair res; + auto c = connect_pair(protocol, res.first, res.second); + if (c) + return res; + else + return c.error(); +} + +} + +#endif //BOOST_ASYNC_IO_STREAM_SOCKET_HPP diff --git a/include/boost/async/io/system_timer.hpp b/include/boost/async/io/system_timer.hpp new file mode 100644 index 00000000..2e8e2317 --- /dev/null +++ b/include/boost/async/io/system_timer.hpp @@ -0,0 +1,69 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_SYSTEM_TIMER_HPP +#define BOOST_ASYNC_IO_SYSTEM_TIMER_HPP + +#include +#include + +#include + +namespace boost::async::detail::io +{ +struct system_sleep; +} +namespace boost::async::io +{ + +struct system_timer final +{ + using wait_result = system::result; + + /// The clock type. + typedef std::chrono::system_clock clock_type; + + /// The duration type of the clock. + typedef typename clock_type::duration duration; + + /// The time point type of the clock. + typedef typename clock_type::time_point time_point; + + BOOST_ASYNC_DECL system_timer(const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL system_timer(const time_point& expiry_time, const async::executor & executor = this_thread::get_executor()); + BOOST_ASYNC_DECL system_timer(const duration& expiry_time, const async::executor & executor = this_thread::get_executor()); + + BOOST_ASYNC_DECL void cancel(); + + BOOST_ASYNC_DECL time_point expiry() const; + BOOST_ASYNC_DECL void reset(const time_point& expiry_time); + BOOST_ASYNC_DECL void reset(const duration& expiry_time); + BOOST_ASYNC_DECL bool expired() const; + private: + struct wait_op_ : result_op + { + void ready(async::handler h) override {if (timer_->expired()) h({});} + BOOST_ASYNC_DECL void initiate(completion_handler handler) override; + + wait_op_(system_timer * timer) : timer_(timer) {} + private: + system_timer * timer_; + }; + + public: + [[nodiscard]] wait_op_ wait() { return wait_op_{this}; } + wait_op_ operator co_await () { return wait(); } + private: + friend struct detail::io::system_sleep; + boost::asio::basic_waitable_timer, + executor> timer_; +}; + +} + +#endif //BOOST_ASYNC_IO_SYSTEM_TIMER_HPP diff --git a/include/boost/async/io/transfer_result.hpp b/include/boost/async/io/transfer_result.hpp new file mode 100644 index 00000000..79b25206 --- /dev/null +++ b/include/boost/async/io/transfer_result.hpp @@ -0,0 +1,144 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_TRANSFER_RESULT_HPP +#define BOOST_ASYNC_TRANSFER_RESULT_HPP + +#include +#include +#include +#include +#include + + +namespace boost::async::io +{ + +struct [[nodiscard]] transfer_result +{ + system::error_code error; + std::size_t transferred{0u}; + + using value_type = std::size_t; + using error_type = system::error_code; + + // queries + constexpr bool has_value() const noexcept { return transferred != 0; } + constexpr bool has_error() const noexcept { return error.failed(); } + constexpr explicit operator bool() const noexcept { return has_value() || !has_error(); } + constexpr std::size_t value( boost::source_location const& loc = BOOST_CURRENT_LOCATION ) const noexcept + { + if (!has_value() || has_error()) + throw_exception_from_error(error, loc); + return transferred; + } + constexpr std::size_t operator*() const noexcept { BOOST_ASSERT(has_value()); return transferred; } + + bool operator==(const system::error_code &ec) const { return error == ec;} + bool operator!=(const system::error_code &ec) const { return error != ec;} + + auto operator<=>(std::size_t n) const { return value() <=> n;} + bool operator==(std::size_t n) const { return value() == n;} + + template + requires system::is_error_code_enum::value + bool operator==(E e) const { return error == e;} + + template + requires system::is_error_code_enum::value + bool operator!=(E e) const { return error != e;} +}; + +inline transfer_result & operator+=(transfer_result & lhs, const transfer_result & rhs) +{ + lhs.transferred += rhs.transferred; + if (!lhs.error) + lhs.error = rhs.error; + return lhs; +} + +struct transfer_op +{ + virtual void ready(async::handler h) {}; + virtual void initiate(async::completion_handler complete) = 0 ; + virtual ~transfer_op() = default; + + transfer_op() = default; + transfer_op(transfer_op && lhs) + : error(std::move(lhs.error)) + , result(std::move(lhs.result)) + { + BOOST_ASSERT(!lhs.resource); + } + + constexpr static bool await_ready() + { + return false; + } + + template + bool await_suspend(std::coroutine_handle h) + { + try + { + auto & res = resource.emplace(buffer, sizeof(buffer), + asio::get_associated_allocator(h.promise(), this_thread::get_allocator()).resource()); + completed_immediately = detail::completed_immediately_t::initiating; + initiate(completion_handler{h, result, &res, &completed_immediately}); + if (completed_immediately == detail::completed_immediately_t::initiating) + completed_immediately = detail::completed_immediately_t::no; + return completed_immediately != detail::completed_immediately_t::yes; + } + catch(...) + { + error = std::current_exception(); + return false; + } + } + + [[nodiscard]] transfer_result await_resume() + { + if (error) + std::rethrow_exception(std::exchange(error, nullptr)); + + return transfer_result{std::get<0>(*result), std::get<1>(*result)}; + } + + struct vawaitable + { + vawaitable(transfer_op * op_) : op_(op_) {} + + bool await_ready() { return op_->await_ready();} + + template + bool await_suspend(std::coroutine_handle h) + { + return op_->await_suspend(h); + } + + std::size_t await_resume(const boost::source_location & loc = BOOST_CURRENT_LOCATION) + { + return op_->await_resume().value(loc); + } + private: + transfer_op * op_; + }; + + vawaitable value() { return vawaitable{this}; } + private: + std::exception_ptr error; + std::optional> result; + char buffer[2048]; + std::optional resource; + detail::completed_immediately_t completed_immediately = detail::completed_immediately_t::no; +}; + + + +} + +#endif //BOOST_ASYNC_TRANSFER_RESULT_HPP diff --git a/include/boost/async/io/write.hpp b/include/boost/async/io/write.hpp new file mode 100644 index 00000000..704e70aa --- /dev/null +++ b/include/boost/async/io/write.hpp @@ -0,0 +1,46 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_IO_WRITE_HPP +#define BOOST_ASYNC_IO_WRITE_HPP + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise write(stream & source, buffers::const_buffer buffer); +BOOST_ASYNC_DECL promise write(stream & source, buffers::const_buffer_span buffer); +BOOST_ASYNC_DECL promise write(stream & source, buffers::const_buffer_subspan buffer); + +template + requires (!std::convertible_to) +promise write(stream & source, ConstBufferSequence && buffer) +{ + buffers::const_buffer buf[asio::detail::max_iov_len]; + + transfer_result tr{}; + + for (auto itr = buffers::begin(buffer), end = buffers::end(buffer); itr != end;) + { + auto ie = (std::min)(end, std::next(itr, asio::detail::max_iov_len)); + auto oe = std::copy(itr, ie, buf); + + buffers::const_buffer_span cbs{buf, std::distance(buf, oe)}; + tr += co_await write(source, cbs); + itr = ie; + } + co_return tr; +} + +} + +#endif //BOOST_ASYNC_IO_WRITE_HPP diff --git a/include/boost/async/io/write_at.hpp b/include/boost/async/io/write_at.hpp new file mode 100644 index 00000000..3a68f3f0 --- /dev/null +++ b/include/boost/async/io/write_at.hpp @@ -0,0 +1,37 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_ASYNC_WRITE_AT_HPP +#define BOOST_ASYNC_WRITE_AT_HPP + +#include +#include +#include +#include + +namespace boost::async::io +{ + +BOOST_ASYNC_DECL promise write_at(random_access_device & source, std::uint64_t offset, buffers::const_buffer buffer); +BOOST_ASYNC_DECL promise write_at(random_access_device & source, std::uint64_t offset, buffers::const_buffer_span buffer); +BOOST_ASYNC_DECL promise write_at(random_access_device & source, std::uint64_t offset, buffers::const_buffer_subspan buffer); + +template + requires (!std::convertible_to) +promise write(random_access_device & source, std::uint64_t offset, MutableBufferSequence && buffer) +{ + buffers::const_buffer buf[32]; + pmr::monotonic_buffer_resource res{buf, sizeof(buf), this_thread::get_default_resource()}; + pmr::vector buf_span{buffer.begin(), buffer.end(), &res}; + co_return co_await write_at(source, offset, buffers::const_buffer_span{buf_span}); +} + + +} + + +#endif //BOOST_ASYNC_WRITE_AT_HPP diff --git a/src/detail/exception.cpp b/src/detail/exception.cpp index 1269aad9..3eeda915 100644 --- a/src/detail/exception.cpp +++ b/src/detail/exception.cpp @@ -62,5 +62,22 @@ std::exception_ptr allocation_failed() return ep; } +void +throw_invalid_argument( + source_location const& loc) +{ + throw_exception( + std::invalid_argument( + "invalid argument"), loc); +} + +void +throw_length_error( + source_location const& loc) +{ + throw_exception( + std::length_error( + "length error"), loc); +} } diff --git a/src/io/acceptor.cpp b/src/io/acceptor.cpp new file mode 100644 index 00000000..9fcfb167 --- /dev/null +++ b/src/io/acceptor.cpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ +acceptor::acceptor(const async::executor & exec) + : acceptor_{exec} {} +acceptor::acceptor(endpoint ep, const async::executor & exec) + : acceptor_{exec, ep} {} + + +system::result acceptor::bind(endpoint ep) +{ + system::error_code ec; + acceptor_.bind(ep, ec); + return ec ? ec : system::result{}; +} +system::result acceptor::listen(int backlog) +{ + system::error_code ec; + acceptor_.listen(backlog, ec); + return ec ? ec : system::result{}; +} + +endpoint acceptor::local_endpoint() +{ + return acceptor_.local_endpoint(); +} + +auto acceptor::accept(socket & sock) -> accept_op_ { return accept_op_{acceptor_, sock};} + +void acceptor::accept_op_::initiate(completion_handler h) +{ + acceptor_.async_accept(socket_.socket_, std::move(h)); +} + + +} \ No newline at end of file diff --git a/src/io/buffers/circular_buffer.cpp b/src/io/buffers/circular_buffer.cpp new file mode 100644 index 00000000..9a10f671 --- /dev/null +++ b/src/io/buffers/circular_buffer.cpp @@ -0,0 +1,92 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#include +#include +#include +#include +#include + +namespace boost::async::io::buffers { + +BOOST_STATIC_ASSERT(dynamic_buffer); + +auto +circular_buffer:: +data() const noexcept -> + const_buffers_type +{ + if(in_pos_ + in_len_ <= cap_) + return { + const_buffer{ + base_ + in_pos_, in_len_ }, + const_buffer{ base_, 0} }; + return { + const_buffer{ + base_ + in_pos_, cap_ - in_pos_}, + const_buffer{ + base_, in_len_- (cap_ - in_pos_)}}; +} + +auto +circular_buffer:: +prepare(std::size_t n) -> + mutable_buffers_type +{ + // Buffer is too small for n + if(n > cap_ - in_len_) + detail::throw_length_error(); + + out_size_ = n; + auto const pos = ( + in_pos_ + in_len_) % cap_; + if(pos + n <= cap_) + return { + mutable_buffer{ + base_ + pos, n}, + mutable_buffer{base_, 0}}; + return { + mutable_buffer{ + base_ + pos, cap_ - pos}, + mutable_buffer{ + base_, n - (cap_ - pos)}}; +} + +void +circular_buffer:: +commit( + std::size_t n) noexcept +{ + if(n < out_size_) + in_len_ += n; + else + in_len_ += out_size_; + out_size_ = 0; +} + +void +circular_buffer:: +consume( + std::size_t n) noexcept +{ + if(n < in_len_) + { + in_pos_ = (in_pos_ + n) % cap_; + in_len_ -= n; + } + else + { + // make prepare return a + // bigger single buffer + in_pos_ = 0; + in_len_ = 0; + } +} + +} // boost::buffers diff --git a/src/io/buffers/const_buffer_pair.cpp b/src/io/buffers/const_buffer_pair.cpp new file mode 100644 index 00000000..fb1b47b1 --- /dev/null +++ b/src/io/buffers/const_buffer_pair.cpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#include + +namespace boost::async::io::buffers { + +const_buffer_pair +const_buffer_pair:: +prefix_impl( + std::size_t n) const noexcept +{ + auto const it0 = begin(); + if(n <= it0->size()) + return { { + it0->data(), n }, + const_buffer{} }; + n -= it0->size(); + auto it1 = it0; + ++it1; + if(n < it1->size()) + return { *it0, { + it1->data(), n } }; + return *this; +} + +const_buffer_pair +const_buffer_pair:: +suffix_impl( + std::size_t n) const noexcept +{ + auto it0 = end(); + --it0; + if(n <= it0->size()) + return { *it0 + ( + it0->size() - n), + const_buffer{} }; + n -= it0->size(); + auto it1 = it0; + --it1; + if(n < it1->size()) + return { *it1 + ( + it1->size() - n), *it0 }; + return *this; +} + +} // boost::buffers diff --git a/src/io/buffers/const_buffer_subspan.cpp b/src/io/buffers/const_buffer_subspan.cpp new file mode 100644 index 00000000..daf2f729 --- /dev/null +++ b/src/io/buffers/const_buffer_subspan.cpp @@ -0,0 +1,154 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#include +#include +#include + +namespace boost::async::io::buffers { + +auto +const_buffer_subspan:: +const_iterator:: +operator*() const noexcept -> + reference +{ + BOOST_ASSERT(s_->n_ > 0); + auto b = s_->p_[i_]; + if(s_->n_ > 1) + { + if(i_ > 0) + { + if(i_ < s_->n_ - 1) + return b; + return prefix(b, s_->p1_); + } + return sans_prefix(b, s_->p0_); + } + return { static_cast< + unsigned char const*>( + b.data()) + s_->p0_, + s_->p1_ - s_->p0_ }; +} + +const_buffer_subspan:: +const_buffer_subspan( + const_buffer const* p, + std::size_t n) noexcept + : p_(p) + , n_(n) + , p1_([&]() -> std::size_t + { + if(n > 0) + return p[n-1].size(); + return 0; + }()) +{ +} + +const_buffer_subspan +const_buffer_subspan:: +prefix_impl( + std::size_t n) const noexcept +{ + switch(n_) + { + case 0: + { + return *this; + } + case 1: + { + if(n == 0) + return { p_, 0, p0_, p0_ }; + if(n == std::size_t(-1)) + return *this; + auto const d = p1_ - p0_; + if(n <= d) + return { p_, 1, p0_, p0_ + n }; + return *this; + } + default: + { + if(n == 0) + return { p_, 0, p0_, p0_ }; + if(n == std::size_t(-1)) + return *this; + auto d = p_[0].size() - p0_; + if(n <= d) + return { p_, 1, p0_, p0_ + n }; + n -= d; + std::size_t i = 1; + for(;;) + { + if(i == n_ - 1) + break; + if(n <= p_[i].size()) + return { p_, i + 1, p0_, n }; + n -= p_[i].size(); + ++i; + } + if(n <= p1_) + return { p_, n_, p0_, n }; + return { p_, n_, p0_, p1_ }; + } + } +} + +const_buffer_subspan +const_buffer_subspan:: +suffix_impl( + std::size_t n) const noexcept +{ + switch(n_) + { + case 0: + { + return *this; + } + case 1: + { + if(n == 0) + return { p_, 0, p1_, p1_ }; + if(n == std::size_t(-1)) + return *this; + auto const d = p1_ - p0_; + if(n < d) + return { p_, 1, p1_ - n, p1_ }; + return *this; + } + default: + { + if(n == 0) + return { p_, 0, p1_, p1_ }; + if(n == std::size_t(-1)) + return *this; + std::size_t i = n_ - 1; + if(n <= p1_) + return { p_ + i, 1, p1_ - n, p1_ }; + n -= p1_; + for(;;) + { + if(--i == 0) + break; + if(n <= p_[i].size()) + return { p_ + i, n_ - i, + p_[i].size() - n, p1_ }; + n -= p_[i].size(); + } + auto d = p_[0].size() - p0_; + if(n <= d) + return { p_, n_, + p_[0].size() - n, p1_ }; + return { p_, n_, p0_, p1_ }; + } + } +} + +} // boost::buffers diff --git a/src/io/buffers/mutable_buffer_pair.cpp b/src/io/buffers/mutable_buffer_pair.cpp new file mode 100644 index 00000000..3b0f3404 --- /dev/null +++ b/src/io/buffers/mutable_buffer_pair.cpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#include + +namespace boost::async::io::buffers { + +mutable_buffer_pair +mutable_buffer_pair:: +prefix_impl( + std::size_t n) const noexcept +{ + auto const it0 = begin(); + if(n <= it0->size()) + return { { + it0->data(), n }, + mutable_buffer{} }; + n -= it0->size(); + auto it1 = it0; + ++it1; + if(n < it1->size()) + return { *it0, { + it1->data(), n } }; + return *this; +} + +mutable_buffer_pair +mutable_buffer_pair:: +suffix_impl( + std::size_t n) const noexcept +{ + auto it0 = end(); + --it0; + if(n <= it0->size()) + return { *it0 + ( + it0->size() - n), + mutable_buffer{} }; + n -= it0->size(); + auto it1 = it0; + --it1; + if(n < it1->size()) + return { *it1 + ( + it1->size() - n), *it0 }; + return *this; +} + +} // boost::buffers diff --git a/src/io/buffers/mutable_buffer_subspan.cpp b/src/io/buffers/mutable_buffer_subspan.cpp new file mode 100644 index 00000000..ee5e9539 --- /dev/null +++ b/src/io/buffers/mutable_buffer_subspan.cpp @@ -0,0 +1,155 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#include +#include +#include + +namespace boost::async::io::buffers { + +auto +mutable_buffer_subspan:: +const_iterator:: +operator*() const noexcept -> + reference +{ + BOOST_ASSERT(s_->n_ > 0); + auto b = s_->p_[i_]; + if(s_->n_ > 1) + { + if(i_ > 0) + { + if(i_ < s_->n_ - 1) + return b; + return prefix(b, s_->p1_); + } + return sans_prefix(b, s_->p0_); + } + return { static_cast< + unsigned char*>( + b.data()) + s_->p0_, + s_->p1_ - s_->p0_ }; +} + +mutable_buffer_subspan:: +mutable_buffer_subspan( + mutable_buffer const* p, + std::size_t n) noexcept + : p_(p) + , n_(n) + , p1_([&]() -> std::size_t + { + if(n > 0) + return p[n-1].size(); + return 0; + }()) +{ +} + +mutable_buffer_subspan +mutable_buffer_subspan:: +prefix_impl( + std::size_t n) const noexcept +{ + switch(n_) + { + case 0: + { + return *this; + } + case 1: + { + if(n == 0) + return { p_, 0, p0_, p0_ }; + if(n == std::size_t(-1)) + return *this; + auto const d = p1_ - p0_; + if(n <= d) + return { p_, 1, p0_, p0_ + n }; + return *this; + } + default: + { + if(n == 0) + return { p_, 0, p0_, p0_ }; + if(n == std::size_t(-1)) + return *this; + auto d = p_[0].size() - p0_; + if(n <= d) + return { p_, 1, p0_, p0_ + n }; + n -= d; + std::size_t i = 1; + for(;;) + { + if(i == n_ - 1) + break; + if(n <= p_[i].size()) + return { p_, i + 1, p0_, n }; + n -= p_[i].size(); + ++i; + } + if(n <= p1_) + return { p_, n_, p0_, n }; + return { p_, n_, p0_, p1_ }; + } + } +} + +mutable_buffer_subspan +mutable_buffer_subspan:: +suffix_impl( + std::size_t n) const noexcept +{ + switch(n_) + { + case 0: + { + return *this; + } + case 1: + { + if(n == 0) + return { p_, 0, p1_, p1_ }; + if(n == std::size_t(-1)) + return *this; + auto const d = p1_ - p0_; + if(n < d) + return { p_, 1, p1_ - n, p1_ }; + return *this; + } + default: + { + if(n == 0) + return { p_, 0, p1_, p1_ }; + if(n == std::size_t(-1)) + return *this; + std::size_t i = n_ - 1; + if(n <= p1_) + return { p_ + i, 1, p1_ - n, p1_ }; + n -= p1_; + for(;;) + { + if(--i == 0) + break; + if(n <= p_[i].size()) + return { p_ + i, n_ - i, + p_[i].size() - n, p1_ }; + n -= p_[i].size(); + } + auto d = p_[0].size() - p0_; + if(n <= d) + return { p_, n_, + p_[0].size() - n, p1_ }; + return { p_, n_, p0_, p1_ }; + } + } +} + +} // boost::buffers + diff --git a/src/io/buffers/register.cpp b/src/io/buffers/register.cpp new file mode 100644 index 00000000..094e6392 --- /dev/null +++ b/src/io/buffers/register.cpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + +namespace boost::async::io::buffers +{ + +buffer_registration register_(buffers::mutable_buffer buffer) +{ + return register_(buffers::mutable_buffer_subspan{&buffer, 1u}); + +} +buffer_registration register_(buffers::mutable_buffer_span buffer) +{ + return register_(buffers::mutable_buffer_subspan{buffer}); +} + +buffer_registration register_(buffers::mutable_buffer_subspan buffer) +{ + return asio::register_buffers(this_thread::get_executor().context(), + buffer, this_thread::get_allocator()); +} + +} \ No newline at end of file diff --git a/src/io/copy.cpp b/src/io/copy.cpp new file mode 100644 index 00000000..9c657a1e --- /dev/null +++ b/src/io/copy.cpp @@ -0,0 +1,83 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +namespace boost::async::io +{ + +promise> +copy(stream & source, stream & sink) +{ + constexpr std::size_t chunk_size = 4096; + char mem[chunk_size * 2]; + buffers::circular_buffer buf{mem, sizeof(mem)}; + + transfer_result r = co_await source.read_some(buf.prepare(chunk_size)), + w = {}; + + buf.commit(r.transferred); + + while (!r.has_error() && !w.has_error()) + { + auto [r2, w2] = co_await join( + source.read_some(buf.prepare(chunk_size)), + sink.write_some(buf.data())); + buf.commit(r2.transferred); + buf.consume(w2.transferred); + r.transferred += r2.transferred; + w.transferred += w2.transferred; + r.error = r2.error; + w.error = w2.error; + } + // remaining readable stuff + while (r.has_error() && !w.has_error() && buffers::buffer_size(buf.data()) > 0u) + { + auto w2 = co_await sink.write_some(buffers::const_buffer_span(buf.data())); + buf.consume(w2.transferred); + w.transferred = w2.transferred; + w.error = w2.error; + } + co_return {r, w}; +} + +promise> +copy(stream & source, stream & sink, buffers::dynamic_buffer_view buf, std::size_t chunk_size) +{ + transfer_result r = co_await source.read_some(buffers::mutable_buffer_span(buf.prepare(chunk_size))), + w = {}; + + buf.commit(r.transferred); + + while (!r.has_error() && !w.has_error()) + { + auto [r2, w2] = co_await join( + source.read_some(buffers::mutable_buffer_span(buf.prepare(chunk_size))), + sink.write_some(buffers::const_buffer_span(buf.data()))); + buf.commit(r2.transferred); + buf.consume(w2.transferred); + r.transferred += r2.transferred; + w.transferred += w2.transferred; + r.error = r2.error; + w.error = w2.error; + } + // remaining readable stuff + while (r.has_error() && !w.has_error() && buffers::buffer_size(buf.data()) > 0u) + { + auto w2 = co_await sink.write_some(buffers::const_buffer_span(buf.data())); + buf.consume(w2.transferred); + w.transferred = w2.transferred; + w.error = w2.error; + } + co_return {r, w}; +} + + +} diff --git a/src/io/copy_n.cpp b/src/io/copy_n.cpp new file mode 100644 index 00000000..b395b9a9 --- /dev/null +++ b/src/io/copy_n.cpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +namespace boost::async::io +{ + +promise> +copy_n(stream & source, stream & sink, std::size_t n) +{ + constexpr std::size_t chunk_size = 4096; + char mem[chunk_size * 2]; + buffers::circular_buffer buf{mem, sizeof(mem)}; + + transfer_result r = co_await source.read_some(buf.prepare(chunk_size)), + w = {}; + + buf.commit(r.transferred); + + while (!r.has_error() && !w.has_error() && (n > 0u)) + { + auto [r2, w2] = co_await join( + source.read_some(buf.prepare((std::min)(chunk_size, n))), + sink.write_some(buffers::const_buffer_span(buf.data()))); + buf.commit(r2.transferred); + n -= r2.transferred; + buf.consume(w2.transferred); + r.transferred += r2.transferred; + w.transferred += w2.transferred; + r.error = r2.error; + w.error = w2.error; + } + // remaining readable stuff + while (r.has_error() && !w.has_error() && buffers::buffer_size(buf.data()) > 0u) + { + assert(false); + auto w2 = co_await sink.write_some(buffers::const_buffer_span(buf.data())); + buf.consume(w2.transferred); + w.transferred = w2.transferred; + w.error = w2.error; + } + + co_return {r, w}; +} + +promise> +copy_n(stream & source, stream & sink, buffers::dynamic_buffer_view buf, + std::size_t n, std::size_t chunk_size) +{ + transfer_result r = co_await source.read_some(buf.prepare(chunk_size)), + w = {}; + + buf.commit(r.transferred); + + while (!r.has_error() && !w.has_error()) + { + auto [r2, w2] = co_await join( + source.read_some(buf.prepare((std::min)(chunk_size, n))), + sink.write_some(buffers::const_buffer_span(buf.data()))); + buf.commit(r2.transferred); + buf.consume(w2.transferred); + n -= r2.transferred; + r.transferred += r2.transferred; + w.transferred += w2.transferred; + r.error = r2.error; + w.error = w2.error; + } + // remaining readable stuff + while (r.has_error() && !w.has_error() && buffers::buffer_size(buf.data()) > 0u) + { + assert(false); + auto w2 = co_await sink.write_some(buffers::const_buffer_span(buf.data())); + buf.consume(w2.transferred); + w.transferred = w2.transferred; + w.error = w2.error; + } + co_return {r, w}; +} + + +} diff --git a/src/io/datagram_socket.cpp b/src/io/datagram_socket.cpp new file mode 100644 index 00000000..21ad53be --- /dev/null +++ b/src/io/datagram_socket.cpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +system::result datagram_socket::duplicate(const async::executor & exec) +{ + auto res = detail::io::duplicate_handle(datagram_socket_.native_handle()); + if (!res) + return res.error(); + return {system::in_place_value, datagram_socket(*res, local_endpoint()->protocol(), exec)}; +} + + + +datagram_socket::datagram_socket(const async::executor & exec) + : socket(datagram_socket_), datagram_socket_(exec) +{ +} + +datagram_socket::datagram_socket(native_handle_type h, protocol_type protocol, const async::executor & exec) + : socket(datagram_socket_), datagram_socket_(exec, protocol, h) +{ +} + +datagram_socket::datagram_socket(datagram_socket && lhs) + : socket(datagram_socket_), datagram_socket_(std::move(lhs.datagram_socket_)) +{ +} +datagram_socket::datagram_socket(endpoint ep, const async::executor & exec) + : socket(datagram_socket_), datagram_socket_(exec, ep) +{ +} + +auto datagram_socket::receive(buffers::mutable_buffer_subspan buffers) -> receive_op_seq_ +{ + return receive_op_seq_{datagram_socket_, buffers}; +} +auto datagram_socket::receive(buffers::mutable_buffer_span buffers) -> receive_op_seq_ +{ + return receive_op_seq_{datagram_socket_, buffers}; +} +auto datagram_socket::receive(buffers::mutable_buffer buffer) -> receive_op_ +{ + return receive_op_{datagram_socket_, buffer}; +} +auto datagram_socket::receive_from(buffers::mutable_buffer_subspan buffers, endpoint & ep) -> receive_from_op_seq_ +{ + return receive_from_op_seq_{datagram_socket_, buffers, ep}; +} +auto datagram_socket::receive_from(buffers::mutable_buffer_span buffers, endpoint & ep) -> receive_from_op_seq_ +{ + return receive_from_op_seq_{datagram_socket_, buffers, ep}; +} +auto datagram_socket::receive_from(buffers::mutable_buffer buffer, endpoint & ep) -> receive_from_op_ +{ + return receive_from_op_{datagram_socket_, buffer, ep}; +} +auto datagram_socket::send(buffers::const_buffer_subspan buffers) -> send_op_seq_ +{ + return send_op_seq_{datagram_socket_, buffers}; +} +auto datagram_socket::send(buffers::const_buffer_span buffers) -> send_op_seq_ +{ + return send_op_seq_{datagram_socket_, buffers}; +} +auto datagram_socket::send(buffers::const_buffer buffer) -> send_op_ +{ + return send_op_{datagram_socket_, buffer}; +} +auto datagram_socket::send_to(buffers::const_buffer_subspan buffers, const endpoint & target) -> send_to_op_seq_ +{ + return send_to_op_seq_{datagram_socket_, buffers, target}; +} +auto datagram_socket::send_to(buffers::const_buffer_span buffers, const endpoint & target) -> send_to_op_seq_ +{ + return send_to_op_seq_{datagram_socket_, buffers, target}; +} +auto datagram_socket::send_to(buffers::const_buffer buffer, const endpoint & target) -> send_to_op_ +{ + return send_to_op_{datagram_socket_, buffer, target}; +} + +void datagram_socket::receive_op_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_receive(buffers::mutable_buffer_subspan{&buffer_, 1u}, std::move(h)); +} + +void datagram_socket::receive_op_seq_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_receive(buffer_, std::move(h)); +} + +void datagram_socket::receive_from_op_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_receive_from(buffers::mutable_buffer_subspan{&buffer_, 1u}, ep_, std::move(h)); +} + +void datagram_socket::receive_from_op_seq_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_receive_from(buffer_, ep_, std::move(h)); +} + +void datagram_socket::send_op_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_send(buffers::const_buffer_subspan{&buffer_, 1u}, std::move(h)); +} + +void datagram_socket::send_op_seq_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_send(buffer_, std::move(h)); +} + +void datagram_socket::send_to_op_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_send_to(buffers::const_buffer_subspan{&buffer_, 1u}, ep_, std::move(h)); +} + +void datagram_socket::send_to_op_seq_::initiate(async::completion_handler h) +{ + this->datagram_socket_.async_send_to(buffer_, ep_, std::move(h)); +} + +void datagram_socket::adopt_endpoint_(endpoint & ep) +{ + + switch (ep.protocol().family()) + { + case BOOST_ASIO_OS_DEF(AF_INET): BOOST_FALLTHROUGH; + case BOOST_ASIO_OS_DEF(AF_INET6): + if (ep.protocol().protocol() == BOOST_ASIO_OS_DEF(IPPROTO_IP)) + ep.set_protocol(BOOST_ASIO_OS_DEF(IPPROTO_UDP)); + BOOST_FALLTHROUGH; + case AF_UNIX: + if (ep.protocol().type() == 0) + ep.set_type(BOOST_ASIO_OS_DEF(SOCK_DGRAM)); + + } +} + + +} \ No newline at end of file diff --git a/src/io/detail/duplicate.cpp b/src/io/detail/duplicate.cpp new file mode 100644 index 00000000..08a80458 --- /dev/null +++ b/src/io/detail/duplicate.cpp @@ -0,0 +1,75 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#if defined(BOOST_ASIO_WINDOWS) +#include +#else +// implement windows shite +#include +#endif + +namespace boost::async::detail::io +{ + +#if defined(BOOST_ASIO_WINDOWS) + +system::result duplicate_handle(HANDLE h) +{ + HANDLE ho; + if (!::DuplicateHandle( + ::GetCurrentProcess(), + h, + ::GetCurrentProcess(), + &ho, + 0, + TRUE, + DUPLICATE_SAME_ACCESS)) + return system::error_code(::GetLastError(), system::system_category()); + return ho; +} + +system::result duplicate_handle(SOCKET sock) +{ + WSAPROTOCOL_INFOW info; + if (SOCKET_ERROR == ::WSADuplicateSocketW( + sock, + ::GetCurrentProcessId(), + &info + )) + return system::error_code(::WSAGetLastError(), system::system_category()); + + auto res = WSASocketW( + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &info, + 0, + 0); + if (res == INVALID_SOCKET) + return system::error_code(::WSAGetLastError(), system::system_category()); + + + return res; +} + +#else + +system::result duplicate_handle(int fd) +{ + auto r = dup(fd); + if (r == -1) + return system::error_code(errno, system::system_category()); + return r; +} + +system::result duplicate_socket(int fd) {return duplicate_handle(fd);} + +#endif +} + diff --git a/src/io/detail/random_access_device.cpp b/src/io/detail/random_access_device.cpp new file mode 100644 index 00000000..1f8558d9 --- /dev/null +++ b/src/io/detail/random_access_device.cpp @@ -0,0 +1,44 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ +auto random_access_device:: read_some_at(std::uint64_t offset, buffers::mutable_buffer_span buffers) + -> read_some_at_op_seq_ +{ + return read_some_at_op_seq_{*this, offset, buffers}; +} +auto random_access_device:: read_some_at(std::uint64_t offset, buffers::mutable_buffer_subspan buffers) + -> read_some_at_op_seq_ +{ + return read_some_at_op_seq_{*this, offset, buffers}; +} +auto random_access_device:: read_some_at(std::uint64_t offset, buffers::mutable_buffer buffer) + -> read_some_at_op_ +{ + return read_some_at_op_{*this, offset, buffer}; +} +auto random_access_device::write_some_at(std::uint64_t offset, buffers::const_buffer_span buffers) + -> write_some_at_op_seq_ +{ + return write_some_at_op_seq_{*this, offset, buffers}; +} +auto random_access_device::write_some_at(std::uint64_t offset, buffers::const_buffer_subspan buffers) + -> write_some_at_op_seq_ +{ + return write_some_at_op_seq_{*this, offset, buffers}; +} +auto random_access_device::write_some_at(std::uint64_t offset, buffers::const_buffer buffer) + -> write_some_at_op_ +{ + return write_some_at_op_{*this, offset, buffer}; +} + + +} \ No newline at end of file diff --git a/src/io/detail/stream.cpp b/src/io/detail/stream.cpp new file mode 100644 index 00000000..4381a04f --- /dev/null +++ b/src/io/detail/stream.cpp @@ -0,0 +1,42 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ + +auto stream:: read_some(buffers::mutable_buffer_subspan buffers) -> read_some_op_seq_ { return read_some_op_seq_{*this, buffers};} +auto stream:: read_some(buffers::mutable_buffer_span buffers) -> read_some_op_seq_ { return read_some_op_seq_{*this, buffers};} +auto stream:: read_some(buffers::mutable_buffer buffer) -> read_some_op_ { return read_some_op_{*this, buffer};} +auto stream::write_some(buffers::const_buffer_subspan buffers) -> write_some_op_seq_ { return write_some_op_seq_{*this, buffers};} +auto stream::write_some(buffers::const_buffer_span buffers) -> write_some_op_seq_ { return write_some_op_seq_{*this, buffers};} +auto stream::write_some(buffers::const_buffer buffer) -> write_some_op_ { return write_some_op_{*this, buffer};} + +void stream::read_some_op_::initiate(completion_handler h) +{ + rstream_.async_read_some_impl_({&buffer_, 1u}, std::move(h)); +} + +void stream::read_some_op_seq_::initiate(completion_handler h) +{ + rstream_.async_read_some_impl_(buffer_, std::move(h)); +} + +void stream::write_some_op_::initiate(completion_handler h) +{ + rstream_.async_write_some_impl_({&buffer_, 1u}, std::move(h)); +} + +void stream::write_some_op_seq_::initiate(completion_handler h) +{ + rstream_.async_write_some_impl_(buffer_, std::move(h)); +} + + + +} \ No newline at end of file diff --git a/src/io/endpoint.cpp b/src/io/endpoint.cpp new file mode 100644 index 00000000..4936d3ba --- /dev/null +++ b/src/io/endpoint.cpp @@ -0,0 +1,226 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +#include +#include + +namespace boost::async::detail +{ + +void +throw_bad_endpoint_access( + source_location const &loc) +{ + throw_exception( + async::io::bad_endpoint_access(), loc); +} + +} + +namespace boost::async::io +{ +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); +static_assert(std::is_standard_layout_v); + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + core::string_view sv) +{ + auto un = new (base) boost::asio::detail::sockaddr_un_type{ + .sun_family=AF_UNIX + }; + + if (sv.size() >= sizeof(un->sun_path)) + detail::throw_length_error(); + + *std::copy(sv.begin(), sv.end(), un->sun_path) = '\0'; + return sizeof(un->sun_family + sv.size() + 1u); +} + +const local_endpoint* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr) +{ + if (actual.family() != AF_UNIX) + return nullptr; + return reinterpret_cast(addr); +} + +static_string<15> ip_address_v4::addr_str() const +{ + char buf[16]; + inet_ntop(AF_INET, &in_.sin_addr.s_addr, buf, sizeof(buf)); + return buf; +} + + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::uint32_t address, + std::uint16_t port) +{ + new (base) boost::asio::detail::sockaddr_in4_type{ + .sin_family=AF_INET, + .sin_port=port, + .sin_addr={address} + }; + return sizeof(boost::asio::detail::sockaddr_in4_type); +} + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port) +{ + if (address.size() > 15) + detail::throw_length_error(); + static_string<16> addr{address.begin(), address.end()}; + + + auto a = boost::asio::ip::address_v4::from_string(addr.c_str()).to_uint(); + auto in4 = new (base) boost::asio::detail::sockaddr_in4_type{ + .sin_family=AF_INET, + .sin_port=port, + .sin_addr={boost::asio::detail::socket_ops::host_to_network_long(a)} + }; + return sizeof(boost::asio::detail::sockaddr_in4_type); +} + +const ip_address_v4* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr) +{ + if (actual.family() != AF_INET) + return nullptr; + return reinterpret_cast(addr); +} + + +static_string<45> ip_address_v6::addr_str() const +{ + char buf[46]; + inet_ntop(AF_INET6, &in_.sin6_addr.s6_addr, buf, sizeof(buf)); + return buf; +} + + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::span address, + std::uint16_t port) +{ + auto in6 = new (base) boost::asio::detail::sockaddr_in6_type{ + .sin6_family=AF_INET6, + .sin6_port=port + }; + std::copy(address.begin(), address.end(), in6->sin6_addr.s6_addr); + return sizeof(boost::asio::detail::sockaddr_in6_type); +} + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port) +{ + if (address.size() > 45) + detail::throw_length_error(); + static_string<46> addr{address.begin(), address.end()}; + + auto a = boost::asio::ip::address_v6::from_string(addr.c_str()).to_bytes(); + auto in6 = new (base) boost::asio::detail::sockaddr_in6_type{ + .sin6_family=AF_INET6, + .sin6_port=port + }; + std::copy(a.begin(), a.end(), in6->sin6_addr.s6_addr); + return sizeof(boost::asio::detail::sockaddr_in6_type); +} + +const ip_address_v6* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr) +{ + if (actual.family() != AF_INET6) + return nullptr; + return reinterpret_cast(addr); +} + + +std::array ip_address::addr() const +{ + std::array res; + if (addr_.ss_family == AF_INET) + { + const auto & in = in_.sin_addr.s_addr; + const auto c = reinterpret_cast(&in); + auto itr = std::fill_n(res.begin(), 12u, 0u); + std::copy(c, c + 4, itr); + } + else + { + const auto & in = in6_.sin6_addr.s6_addr; + std::copy(std::begin(in), std::end(in), res.begin()); + } + return res; +} + +static_string<45> ip_address::addr_str() const +{ + char buf[46]; + inet_ntop(addr_.ss_family, + (addr_.ss_family == AF_INET6) ? + static_cast(&in6_.sin6_addr.s6_addr) : &in_.sin_addr.s_addr, + buf, sizeof(buf)); + return buf; +} + +std::size_t tag_invoke(make_endpoint_tag, + boost::asio::detail::socket_addr_type* base, + std::string_view address, + std::uint16_t port) +{ + if (address.size() > 45) + detail::throw_length_error(); + static_string<46> addr{address.begin(), address.end()}; + + auto ad = boost::asio::ip::address::from_string(addr.c_str()); + if (ad.is_v4()) + { + new (base) boost::asio::detail::sockaddr_in4_type{ + .sin_family=AF_INET, + .sin_port=port, + .sin_addr={boost::asio::detail::socket_ops::network_to_host_long(ad.to_v4().to_uint())} + }; + return sizeof(boost::asio::detail::sockaddr_in4_type); + } + else + { + auto in6 = new (base) boost::asio::detail::sockaddr_in6_type{ + .sin6_family=AF_INET6, + .sin6_port=port + }; + auto a = ad.to_v6().to_bytes(); + std::copy(a.begin(), a.end(), in6->sin6_addr.s6_addr); + return sizeof(boost::asio::detail::sockaddr_in4_type); + } +} + +const ip_address* tag_invoke(get_endpoint_tag, + protocol_type actual, + const endpoint::addr_type * addr) +{ + if ((actual.family() != AF_INET) && (actual.family() != AF_INET6)) + return nullptr; + return reinterpret_cast(addr); +} + +} \ No newline at end of file diff --git a/src/io/file.cpp b/src/io/file.cpp new file mode 100644 index 00000000..4d27acc6 --- /dev/null +++ b/src/io/file.cpp @@ -0,0 +1,86 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#if defined(BOOST_ASIO_HAS_FILE) + +namespace boost::async::io +{ + + +system::result file::open(core::string_view file, flags open_flags) +{ + system::error_code ec; + file_.open(std::string(file), open_flags, ec); + return ec ? ec : system::result{}; +} + + +system::result file::close() +{ + system::error_code ec; + file_.close(ec); + return ec ? ec : system::result{}; +} + +system::result file::cancel() +{ + system::error_code ec; + file_.cancel(ec); + return ec ? ec : system::result{}; +} + + +bool file::is_open() const +{ + return file_.is_open(); +} +system::result file::resize(std::uint64_t size) +{ + system::error_code ec; + file_.resize(size, ec); + return ec ? ec : system::result{}; +} + + +system::result file::size() const +{ + system::error_code ec; + auto res = file_.size(ec); + return ec ? ec : system::result(res); +} + +system::result file::sync_all() +{ + system::error_code ec; + file_.sync_all(ec); + return ec ? ec : system::result{}; +} + +system::result file::sync_data() +{ + system::error_code ec; + file_.sync_data(ec); + return ec ? ec : system::result{}; +} + +system::result file::assign(native_handle_type native_handle) +{ + system::error_code ec; + file_.assign(native_handle, ec); + return ec ? ec : system::result{}; +} + +system::result file::release() +{ + system::error_code ec; + auto h = file_.release(ec); + return ec ? ec : system::result(h); +} + +} +#endif \ No newline at end of file diff --git a/src/io/pipe.cpp b/src/io/pipe.cpp new file mode 100644 index 00000000..0b7f408c --- /dev/null +++ b/src/io/pipe.cpp @@ -0,0 +1,168 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include + +namespace boost::async::io +{ + +readable_pipe::readable_pipe(const async::executor & exec) : pipe_(exec) {} +readable_pipe::readable_pipe(native_handle_type native_handle, const async::executor & exec) + : pipe_(exec, native_handle) {} + +system::result readable_pipe::close() +{ + system::error_code ec; + pipe_.close(ec); + return ec ? ec : system::result(); +} + +system::result readable_pipe::cancel() +{ + system::error_code ec; + pipe_.cancel(ec); + return ec ? ec : system::result(); +} + +bool readable_pipe::is_open() const { return pipe_.is_open(); } + +void readable_pipe::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + pipe_.async_read_some(buffer, std::move(h)); +} + +void readable_pipe::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + constexpr static source_location loc{BOOST_CURRENT_LOCATION}; + system::error_code ec{asio::error::operation_not_supported, &loc}; + if (h.completed_immediately) + { + *h.completed_immediately = detail::completed_immediately_t::maybe; + h(ec, 0ul); + } + else + asio::post(h.executor_, asio::append(std::move(h), ec, 0ul)); +} + + +system::result readable_pipe::assign(native_handle_type native_handle) +{ + system::error_code ec; + pipe_.assign(native_handle, ec); + return ec ? ec : system::result(); +} +auto readable_pipe::release() -> system::result +{ + system::error_code ec; + auto h = pipe_.release(ec); + if (ec) + return ec; + else + return h; +} + +system::result readable_pipe::duplicate(const async::executor & exec) +{ + auto res = detail::io::duplicate_handle(pipe_.native_handle()); + if (!res) + return res.error(); + + return readable_pipe(*res, std::move(exec)); +} + + +writable_pipe::writable_pipe(const async::executor & exec) : pipe_(std::move(exec)) {} +writable_pipe::writable_pipe(native_handle_type native_handle, const async::executor & exec) + : pipe_(std::move(exec), native_handle) {} + +system::result writable_pipe::close() +{ + system::error_code ec; + pipe_.close(ec); + return ec ? ec : system::result(); +} + +system::result writable_pipe::cancel() +{ + system::error_code ec; + pipe_.cancel(ec); + return ec ? ec : system::result(); +} + +bool writable_pipe::is_open() const { return pipe_.is_open(); } + +void writable_pipe::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + pipe_.async_write_some(buffer, std::move(h)); +} + + +void writable_pipe::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + constexpr static source_location loc{BOOST_CURRENT_LOCATION}; + system::error_code ec{asio::error::operation_not_supported, &loc}; + if (h.completed_immediately) + { + *h.completed_immediately = detail::completed_immediately_t::maybe; + h(ec, 0ul); + } + else + asio::post(h.executor_, asio::append(std::move(h), ec, 0ul)); +} + +system::result writable_pipe::assign(native_handle_type native_handle) +{ + system::error_code ec; + pipe_.assign(native_handle, ec); + return ec ? ec : system::result(); +} +auto writable_pipe::release() -> system::result +{ + system::error_code ec; + auto h = pipe_.release(ec); + if (ec) + return ec; + else + return h; +} + +system::result writable_pipe::duplicate(const async::executor & exec) +{ + auto res = detail::io::duplicate_handle(pipe_.native_handle()); + if (!res) + return res.error(); + + return writable_pipe(*res, std::move(exec)); +} + +system::result> make_pipe(const async::executor & exec) +{ + readable_pipe rp{exec}; + writable_pipe wp{exec}; + + system::error_code ec; + asio::connect_pipe(rp.pipe_, wp.pipe_, ec); + if (ec) + return ec; + else + return std::make_pair(std::move(rp), std::move(wp)); +} + + +} \ No newline at end of file diff --git a/src/io/popen.cpp b/src/io/popen.cpp new file mode 100644 index 00000000..1c222d45 --- /dev/null +++ b/src/io/popen.cpp @@ -0,0 +1,111 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ + +popen::popen(boost::process::v2::filesystem::path executable, + std::initializer_list args, + process_initializer initializer, + const async::executor & exec) + : popen_(exec, + executable, + args, + initializer.stdio, + initializer.start_dir, + initializer.env) {} + + +popen::popen(boost::process::v2::filesystem::path executable, + std::span args, + process_initializer initializer, + const async::executor & exec) + : popen_(exec, + executable, + args, + initializer.stdio, + initializer.start_dir, + initializer.env) {} + +pid_type popen::id() const {return popen_.id();} + +system::result popen::interrupt() +{ + system::error_code ec; + popen_.interrupt(ec); + return ec ? ec : system::result(); +} +system::result popen::request_exit() +{ + system::error_code ec; + popen_.request_exit(ec); + return ec ? ec : system::result(); +} +system::result popen::suspend() +{ + system::error_code ec; + popen_.suspend(ec); + return ec ? ec : system::result(); +} +system::result popen::resume() +{ + system::error_code ec; + popen_.resume(ec); + return ec ? ec : system::result(); +} +system::result popen::terminate() +{ + system::error_code ec; + popen_.terminate(ec); + return ec ? ec : system::result(); +} +popen::handle_type popen::detach() +{ + return popen_.detach(); +} +system::result popen::running() +{ + system::error_code ec; + auto res = popen_.running(ec); + return ec ? system::result(system::in_place_error, ec) : system::result(res); +} + +system::result popen::close() +{ + return this->terminate(); +} + +system::result popen::cancel() +{ + system::error_code ec; + popen_.get_stdin().cancel(ec); + if (ec) + return ec; + popen_.get_stdout().cancel(ec); + if (ec) + return ec; + return {}; +} + +bool popen::is_open() const +{ + return this->popen_.is_open(); +} + +void popen::async_read_some_impl_(buffers::mutable_buffer_subspan buffer, async::completion_handler h) +{ + popen_.async_read_some(buffer, std::move(h)); +} +void popen::async_write_some_impl_(buffers::const_buffer_subspan buffer, async::completion_handler h) +{ + popen_.async_write_some(buffer, std::move(h)); +} + + +} \ No newline at end of file diff --git a/src/io/process.cpp b/src/io/process.cpp new file mode 100644 index 00000000..e7b2049a --- /dev/null +++ b/src/io/process.cpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ + +process::process(boost::process::v2::filesystem::path executable, + std::initializer_list args, + process_initializer initializer, + const async::executor & exec) + : process_(exec, + executable, + args, + initializer.stdio, + initializer.start_dir, + initializer.env) {} + + +process::process(boost::process::v2::filesystem::path executable, + std::span args, + process_initializer initializer, + const async::executor & exec) + : process_(exec, + executable, + args, + initializer.stdio, + initializer.start_dir, + initializer.env) {} + + +process::process(pid_type pid, const async::executor & exec) : process_(exec, pid) {} +process::process(pid_type pid, native_handle_type native_handle, const async::executor & exec) + : process_(exec, pid, native_handle) {} + +void process::wait_op_::initiate(completion_handler handler) +{ + process_.async_wait(std::move(handler)); +} + +pid_type process::id() const {return process_.id();} + +system::result process::interrupt() +{ + system::error_code ec; + process_.interrupt(ec); + return ec ? ec : system::result(); +} +system::result process::request_exit() +{ + system::error_code ec; + process_.request_exit(ec); + return ec ? ec : system::result(); +} +system::result process::suspend() +{ + system::error_code ec; + process_.suspend(ec); + return ec ? ec : system::result(); +} +system::result process::resume() +{ + system::error_code ec; + process_.resume(ec); + return ec ? ec : system::result(); +} +system::result process::terminate() +{ + system::error_code ec; + process_.terminate(ec); + return ec ? ec : system::result(); +} +process::handle_type process::detach() +{ + return process_.detach(); +} +system::result process::running() +{ + system::error_code ec; + auto res = process_.running(ec); + return ec ? system::result(system::in_place_error, ec) : system::result(res); +} + +} \ No newline at end of file diff --git a/src/io/random_access_file.cpp b/src/io/random_access_file.cpp new file mode 100644 index 00000000..bdbbc363 --- /dev/null +++ b/src/io/random_access_file.cpp @@ -0,0 +1,70 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#if defined(BOOST_ASIO_HAS_FILE) +#include + +namespace boost::async::io +{ + +system::result random_access_file::duplicate() +{ + auto res = detail::io::duplicate_handle(random_access_file_.native_handle()); + if (!res) + return res.error(); + + return {system::in_place_value, random_access_file(*res)}; +} + + + +random_access_file::random_access_file() + : file(random_access_file_), random_access_file_(this_thread::get_executor()) +{ +} + +random_access_file::random_access_file(native_handle_type h) + : file(random_access_file_), random_access_file_(this_thread::get_executor()) +{ +} + +random_access_file::random_access_file(random_access_file && lhs) + : file(random_access_file_), random_access_file_(std::move(lhs.random_access_file_)) +{ +} + +random_access_file::random_access_file(core::string_view file_, flags open_flags) + : file(random_access_file_), random_access_file_(this_thread::get_executor(), std::string(file_), open_flags) +{ +} + + +void random_access_file::async_read_some_at_impl_( + std::uint64_t offset, + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + random_access_file_.async_read_some_at(offset, buffer, std::move(h)); +} + +void random_access_file::async_write_some_at_impl_( + std::uint64_t offset, + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + random_access_file_.async_write_some_at(offset, buffer, std::move(h)); +} + +system::result random_access_file::close() { return file::close(); } +system::result random_access_file::cancel() { return file::cancel(); } +bool random_access_file::is_open() const {return file::is_open();} + +} + +#endif \ No newline at end of file diff --git a/src/io/read.cpp b/src/io/read.cpp new file mode 100644 index 00000000..8cf9b3d4 --- /dev/null +++ b/src/io/read.cpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +promise read(stream & source, buffers::mutable_buffer buffer) +{ + transfer_result tr; + + do + { + auto rd = co_await source.read_some(buffer); + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer += rd.transferred; + } + while (buffer.size() > 0 && !tr.has_error()); + co_return tr; +} + +promise read(stream & source, buffers::mutable_buffer_subspan buffer) +{ + transfer_result tr; + do + { + auto rd = co_await source.read_some(buffer); + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer = buffers::sans_prefix(buffer, rd.transferred); + } + while (buffers::buffer_size(buffer) > 0 && !tr.has_error()); + co_return tr; +} + +promise read(stream & source, buffers::mutable_buffer_span buffer) +{ + return read(source, buffers::mutable_buffer_subspan(buffer)); +} + + + +} \ No newline at end of file diff --git a/src/io/read_all.cpp b/src/io/read_all.cpp new file mode 100644 index 00000000..11c78efa --- /dev/null +++ b/src/io/read_all.cpp @@ -0,0 +1,38 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost::async::io +{ + +promise read_all(stream & source, buffers::dynamic_buffer_view buffer, std::size_t chunk_size) +{ + transfer_result tr; + + do + { + auto rd = co_await source.read_some(buffer.prepare((std::min)(chunk_size, buffer.max_size() - buffer.size()))); + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer.commit(rd.transferred); + } + while (buffer.max_size() > buffer.size() && !tr.has_error()); + co_return tr; +} + + + + +} \ No newline at end of file diff --git a/src/io/read_at.cpp b/src/io/read_at.cpp new file mode 100644 index 00000000..0e6abb91 --- /dev/null +++ b/src/io/read_at.cpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +promise read_at(random_access_device & source, std::uint64_t offset,buffers::mutable_buffer buffer) +{ + transfer_result tr; + + do + { + auto rd = co_await source.read_some_at(offset, buffer); + tr.transferred += rd.transferred; + offset += rd.transferred; + tr.error = rd.error; + buffer += rd.transferred; + } + while (buffer.size() > 0 && !tr.has_error()); + co_return tr; +} + +promise read_at(random_access_device & source, std::uint64_t offset, buffers::mutable_buffer_subspan buffer) +{ + transfer_result tr; + do + { + auto rd = co_await source.read_some_at(offset, buffer); + tr.transferred += rd.transferred; + offset += rd.transferred; + tr.error = rd.error; + buffer = buffers::sans_prefix(buffer, rd.transferred); + } + while (buffers::buffer_size(buffer) > 0 && !tr.has_error()); + co_return tr; +} + +promise read_at(random_access_device & source, std::uint64_t offset, buffers::mutable_buffer_span buffer) +{ + return read_at(source, offset, buffers::mutable_buffer_subspan(buffer)); +} + + +} \ No newline at end of file diff --git a/src/io/read_until.cpp b/src/io/read_until.cpp new file mode 100644 index 00000000..4945bf3a --- /dev/null +++ b/src/io/read_until.cpp @@ -0,0 +1,73 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include + +#include +#include +#include + +namespace boost::async::io +{ + +promise read_until(stream & source, buffers::dynamic_buffer_view buffer, + char delim, std::size_t chunk_size) +{ + transfer_result tr{}; + + bool matched = false; + do + { + auto buf = buffer.prepare((std::min)(chunk_size, buffer.max_size() - buffer.size())); + async::io::buffers::mutable_buffer_span cb{buf}; + auto rd = co_await source.read_some(buf); + + auto begin = asio::buffers_begin(buf); + auto end = std::next(asio::buffers_begin(buf), rd.transferred); + matched = std::find(begin, end, delim) != end; + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer.commit(rd.transferred); + } + while (buffer.max_size() > buffer.size() && !tr.has_error() && !matched); + co_return tr; +} + +promise read_until(stream & source, buffers::dynamic_buffer_view buffer, + core::string_view delim, std::size_t chunk_size) +{ + transfer_result tr; + if (delim.empty()) + co_return tr; + + std::size_t offset = 0; + + bool matched = false; + do + { + auto buf = buffer.prepare((std::min)(chunk_size, buffer.max_size() - buffer.size())); + auto rd = co_await source.read_some(buf); + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer.commit(rd.transferred); + + auto relevant_memory = (std::min)(delim.size() + rd.transferred, buffers::buffer_size(buf)); + auto begin = std::prev(asio::buffers_end(buffer.data()), relevant_memory); + auto end = asio::buffers_end(buffer.data()); + auto itr = std::search(begin, end, delim.begin(), delim.end()); + matched = (itr != end); + } + while (buffer.max_size() >= buffer.size() && !tr.has_error() && !matched); + co_return tr; +} + + + +} \ No newline at end of file diff --git a/src/io/resolver.cpp b/src/io/resolver.cpp new file mode 100644 index 00000000..dea2a37d --- /dev/null +++ b/src/io/resolver.cpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include +#include + +namespace boost::async::io +{ + + +resolver::resolver(const async::executor & exec) : resolver_(exec) {} +void resolver::cancel() { resolver_.cancel(); } + + +void do_initiate(asio::ip::basic_resolver & resolver_, + core::string_view host, core::string_view service, + completion_handler h) +{ + using results_type = typename asio::ip::basic_resolver_results; + resolver_.async_resolve( + async::io::ip, host, service, + asio::deferred([](system::error_code ec, results_type res) + { + pmr::vector r{this_thread::get_allocator()}; + r.assign(res.begin(), res.end()); + return asio::deferred.values(ec, std::move(r)); + }))(std::move(h)); +} + + +void resolver::resolve_op_::initiate( + completion_handler h) +{ + do_initiate(resolver_, host_, service_, std::move(h)); +} + +BOOST_ASYNC_DECL +void lookup::initiate( + completion_handler handler) +{ + auto & res = resolver_.emplace(handler.get_executor()); + do_initiate(res, host_, service_, std::move(handler)); +} + +} \ No newline at end of file diff --git a/src/io/seq_packet_socket.cpp b/src/io/seq_packet_socket.cpp new file mode 100644 index 00000000..c9651dff --- /dev/null +++ b/src/io/seq_packet_socket.cpp @@ -0,0 +1,110 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +system::result seq_packet_socket::duplicate() +{ + auto res = detail::io::duplicate_handle(seq_packet_socket_.native_handle()); + if (!res) + return res.error(); + + return {system::in_place_value, seq_packet_socket(*res)}; +} + + + +seq_packet_socket::seq_packet_socket() + : socket(seq_packet_socket_), seq_packet_socket_(this_thread::get_executor()) +{ +} + +seq_packet_socket::seq_packet_socket(native_handle_type h, protocol_type protocol) + : socket(seq_packet_socket_), seq_packet_socket_(this_thread::get_executor(), protocol, h) +{ +} + +seq_packet_socket::seq_packet_socket(seq_packet_socket && lhs) + : socket(seq_packet_socket_), seq_packet_socket_(std::move(lhs.seq_packet_socket_)) +{ +} +seq_packet_socket::seq_packet_socket(endpoint ep) + : socket(seq_packet_socket_), seq_packet_socket_(this_thread::get_executor(), ep) +{ +} + +auto seq_packet_socket::receive(buffers::mutable_buffer_subspan buffers, message_flags &out_flags) -> receive_op_seq_ +{ + return receive_op_seq_{seq_packet_socket_, buffers, out_flags}; +} +auto seq_packet_socket::receive(buffers::mutable_buffer_span buffers, message_flags &out_flags) -> receive_op_seq_ +{ + return receive_op_seq_{seq_packet_socket_, buffers, out_flags}; +} +auto seq_packet_socket::receive(buffers::mutable_buffer buffer, message_flags &out_flags) -> receive_op_ +{ + return receive_op_{seq_packet_socket_, buffer, out_flags}; +} +auto seq_packet_socket::send(buffers::const_buffer_subspan buffers, message_flags out_flags) -> send_op_seq_ +{ + return send_op_seq_{seq_packet_socket_, buffers, out_flags}; +} +auto seq_packet_socket::send(buffers::const_buffer_span buffers, message_flags out_flags) -> send_op_seq_ +{ + return send_op_seq_{seq_packet_socket_, buffers, out_flags}; +} +auto seq_packet_socket::send(buffers::const_buffer buffer,message_flags out_flags) -> send_op_ +{ + return send_op_{seq_packet_socket_, buffer, out_flags}; +} + +void seq_packet_socket::receive_op_::initiate(async::completion_handler h) +{ + this->seq_packet_socket_.async_receive(buffers::mutable_buffer_subspan{&buffer_, 1u}, out_flags_, std::move(h)); +} + +void seq_packet_socket::receive_op_seq_::initiate(async::completion_handler h) +{ + this->seq_packet_socket_.async_receive(buffer_, out_flags_, std::move(h)); +} + +void seq_packet_socket::send_op_::initiate(async::completion_handler h) +{ + this->seq_packet_socket_.async_send(buffers::const_buffer_subspan{&buffer_, 1u}, out_flags_, std::move(h)); +} + +void seq_packet_socket::send_op_seq_::initiate(async::completion_handler h) +{ + this->seq_packet_socket_.async_send(buffer_, out_flags_, std::move(h)); +} + +void seq_packet_socket::adopt_endpoint_(endpoint & ep) +{ + + switch (ep.protocol().family()) + { + +#if defined(IPPROTO_SCTP) + case BOOST_ASIO_OS_DEF(AF_INET): BOOST_FALLTHROUGH; + case BOOST_ASIO_OS_DEF(AF_INET6): + if (ep.protocol().protocol() == BOOST_ASIO_OS_DEF(IPPROTO_IP)) + ep.set_protocol(IPPROTO_SCTP); + BOOST_FALLTHROUGH; +#endif + + case AF_UNIX: + if (ep.protocol().type() == 0) + ep.set_type(BOOST_ASIO_OS_DEF(SOCK_SEQPACKET)); + break; + } +} + +} \ No newline at end of file diff --git a/src/io/serial_port.cpp b/src/io/serial_port.cpp new file mode 100644 index 00000000..25a3ebfd --- /dev/null +++ b/src/io/serial_port.cpp @@ -0,0 +1,154 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +system::result serial_port::close() +{ + system::error_code ec; + serial_port_.close(ec); + return ec ? ec : system::result(); +} + +system::result serial_port::cancel() +{ + system::error_code ec; + serial_port_.cancel(ec); + return ec ? ec : system::result(); +} + +bool serial_port::is_open() const { return serial_port_.is_open(); } + +[[nodiscard]] system::result serial_port::send_break() +{ + system::error_code ec; + serial_port_.send_break(ec); + return ec ? ec : system::result{}; +} + +system::result serial_port::set_baud_rate(unsigned rate) +{ + system::error_code ec; + serial_port_.set_option(asio::serial_port_base::baud_rate(rate), ec); + return ec ? ec : system::result(); +} + +system::result serial_port::get_baud_rate() +{ + system::error_code ec; + asio::serial_port_base::baud_rate br; + serial_port_.get_option(br, ec); + return ec ? ec : system::result(br.value()); +} + +system::result serial_port::set_character_size(unsigned rate) +{ + system::error_code ec; + serial_port_.set_option(asio::serial_port_base::character_size(rate), ec); + return ec ? ec : system::result(); +} + +system::result serial_port::get_character_size() +{ + system::error_code ec; + asio::serial_port_base::character_size br; + serial_port_.get_option(br, ec); + return ec ? ec : system::result(br.value()); +} + + +system::result serial_port::set_flow_control(flow_control rate) +{ + system::error_code ec; + serial_port_.set_option(asio::serial_port_base::flow_control(rate), ec); + return ec ? ec : system::result(); +} + +auto serial_port::get_flow_control() -> system::result +{ + system::error_code ec; + asio::serial_port_base::flow_control br; + serial_port_.get_option(br, ec); + return ec ? ec : system::result(br.value()); +} + + +system::result serial_port::set_parity(parity rate) +{ + system::error_code ec; + serial_port_.set_option(asio::serial_port_base::parity(rate), ec); + return ec ? ec : system::result(); +} + +auto serial_port::get_parity() -> system::result +{ + system::error_code ec; + asio::serial_port_base::parity br; + serial_port_.get_option(br, ec); + return ec ? ec : system::result(br.value()); +} + +serial_port::serial_port() + : serial_port_(this_thread::get_executor()) {} +serial_port::serial_port(core::string_view device) + : serial_port_(this_thread::get_executor(), std::string(device)) {} +serial_port::serial_port(native_handle_type native_handle) + : serial_port_(this_thread::get_executor(), native_handle) {} + +system::result serial_port::assign(native_handle_type native_handle) +{ + system::error_code ec; + serial_port_.assign(native_handle, ec); + return ec ? ec : system::result{}; +} + +[[nodiscard]] system::result serial_port::open(core::string_view device) +{ + std::string dev{device}; + system::error_code ec; + serial_port_.open(dev, ec); + if (ec) + return ec; + else + return system::in_place_value; +} + + +void serial_port::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + serial_port_.async_read_some(buffer, std::move(h)); +} + +void serial_port::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + serial_port_.async_write_some(buffer, std::move(h)); +} + +auto serial_port::release() -> system::result +{ + // ain't done in asio, for some reason. + constexpr static source_location loc{BOOST_CURRENT_LOCATION}; + system::error_code ec{asio::error::operation_not_supported, &loc}; + return ec; +} +auto serial_port::duplicate() -> system::result +{ + auto fd = detail::io::duplicate_handle(serial_port_.native_handle()); + if (fd.has_error()) + return fd.error(); + return serial_port(*fd); +} + +} \ No newline at end of file diff --git a/src/io/signal_set.cpp b/src/io/signal_set.cpp new file mode 100644 index 00000000..0e25ff4a --- /dev/null +++ b/src/io/signal_set.cpp @@ -0,0 +1,54 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +namespace boost::async::io +{ + +signal_set::signal_set() : signal_set_(this_thread::get_executor()) {} +signal_set::signal_set(std::initializer_list sigs) + : signal_set_(this_thread::get_executor()) +{ + for (auto i : sigs) + add(i).value(); +} + +system::result signal_set::cancel() +{ + system::error_code ec; + signal_set_.cancel(ec); + return ec ? ec : system::result{}; +} + +system::result signal_set::clear() +{ + system::error_code ec; + signal_set_.clear(ec); + return ec ? ec : system::result{}; +} +system +::result signal_set::add(int signal_number) +{ + system::error_code ec; + signal_set_.add(signal_number, ec); + return ec ? ec : system::result{}; +} + +system::result signal_set::remove(int signal_number) +{ + system::error_code ec; + signal_set_.remove(signal_number, ec); + return ec ? ec : system::result{}; +} + +void signal_set::wait_op_::initiate(completion_handler handler) +{ + signal_set_.async_wait(std::move(handler)); +} + +} \ No newline at end of file diff --git a/src/io/sleep.cpp b/src/io/sleep.cpp new file mode 100644 index 00000000..2fb0c10b --- /dev/null +++ b/src/io/sleep.cpp @@ -0,0 +1,34 @@ +// Copyright (c) 2023 Klemens D. Morgenstern +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include + +namespace boost::async::detail::io +{ + +void steady_sleep::ready(async::handler h) +{ + if (time_ < std::chrono::steady_clock::now()) + h({}); +} +void steady_sleep::initiate(async::completion_handler complete) +{ + auto & timer = timer_.emplace(complete.get_executor(), time_); + timer.async_wait(std::move(complete)); +} + +void system_sleep::ready(async::handler h) +{ + if (time_ < std::chrono::system_clock::now()) + h({}); +} +void system_sleep::initiate(async::completion_handler complete) +{ + auto &timer = timer_.emplace(complete.get_executor(), time_); + timer.async_wait(std::move(complete)); +} + + +} \ No newline at end of file diff --git a/src/io/socket.cpp b/src/io/socket.cpp new file mode 100644 index 00000000..5b84df1c --- /dev/null +++ b/src/io/socket.cpp @@ -0,0 +1,181 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + +namespace boost::async::io +{ + +system::result socket::open(protocol_type prot) +{ + system::error_code ec; + socket_.open(prot, ec); + return ec ? ec : system::result{}; +} + +system::result socket::close() +{ + system::error_code ec; + socket_.close(ec); + return ec ? ec : system::result{}; +} + +system::result socket::cancel() +{ + system::error_code ec; + socket_.cancel(ec); + return ec ? ec : system::result{}; +} + +bool socket::is_open() const +{ + return socket_.is_open(); +} + +auto socket::native_handle() -> native_handle_type { return socket_.native_handle(); } + +system::result socket::shutdown(shutdown_type st) +{ + system::error_code ec; + socket_.shutdown(st, ec); + return ec ? ec : system::result{}; +} + +system::result socket::local_endpoint() const +{ + system::error_code ec; + auto res = socket_.local_endpoint(ec); + return ec ? ec : system::result(res); +} + +system::result socket::remote_endpoint() const +{ + system::error_code ec; + auto res = socket_.remote_endpoint(ec); + return ec ? ec : system::result(res); +} + + +system::result socket::assign(protocol_type protocol, native_handle_type native_handle) +{ + system::error_code ec; + socket_.assign(protocol, native_handle, ec); + return ec ? ec : system::result{}; +} + + + +auto socket::release() -> system::result +{ + system::error_code ec; + auto h = socket_.release(ec); + return ec ? ec : system::result(h); +} + +system::result socket::bytes_readable() +{ + system::error_code ec; + asio::socket_base::bytes_readable opt; + socket_.io_control(opt, ec); + return ec ? ec : system::result(opt.get()); +} + +#define DEFINE_OPTION(Name, Type) \ +system::result socket::set_##Name(Type value) \ +{ \ + system::error_code ec; \ + socket_.set_option(asio::socket_base::Name(value), ec); \ + return ec ? ec : system::result{}; \ +} \ + \ +system::result socket::get_##Name() const \ +{ \ + system::error_code ec; \ + asio::socket_base::Name opt; \ + socket_.get_option(opt, ec); \ + return ec \ + ? system::result(system::in_place_error, ec) \ + : system::result(system::in_place_value, opt.value()); \ +} + +DEFINE_OPTION(debug, bool); +DEFINE_OPTION(do_not_route, bool); +DEFINE_OPTION(enable_connection_aborted , bool); +DEFINE_OPTION(keep_alive , bool); +DEFINE_OPTION(receive_buffer_size, std::size_t); +DEFINE_OPTION(send_buffer_size, std::size_t); +DEFINE_OPTION(receive_low_watermark, std::size_t); +DEFINE_OPTION(send_low_watermark, std::size_t); +DEFINE_OPTION(reuse_address, bool); + +system::result socket::set_linger(bool linger, int timeout) +{ + system::error_code ec; + socket_.set_option(asio::socket_base::linger(linger, timeout), ec); + return ec ? ec : system::result{}; +} + +system::result> socket::get_linger() const +{ + system::error_code ec; + asio::socket_base::linger opt; + socket_.get_option(opt, ec); + return ec ? ec : system::result>(opt.enabled(), opt.timeout()); +} + + +auto socket::connect(endpoint ep) -> connect_op_ {return connect_op_{this, ep};} +auto socket::wait(wait_type wt) -> wait_op_ {return wait_op_{socket_, wt};} +auto socket::operator co_await() -> wait_op_ {return wait_op_{socket_, wait_type::wait_read};} + +system::result connect_pair(protocol_type protocol, socket & socket1, socket & socket2) +{ + system::error_code ec; + boost::asio::detail::socket_type sv[2]; + if (boost::asio::detail::socket_ops::socketpair(protocol.family(), + protocol.type(), protocol.protocol(), sv, ec) + == boost::asio::detail::socket_error_retval) + BOOST_ASYNC_RETURN_EC(ec); + + auto res = socket1.assign(protocol, sv[0]); + if (!res) + { + boost::system::error_code temp_ec; + boost::asio::detail::socket_ops::state_type state[2] = { 0, 0 }; + boost::asio::detail::socket_ops::close(sv[0], state[0], true, temp_ec); + boost::asio::detail::socket_ops::close(sv[1], state[1], true, temp_ec); + return res; + } + + res = socket2.assign(protocol, sv[1]); + if (!res) + { + boost::system::error_code temp_ec; + (void)socket1.close(); + boost::asio::detail::socket_ops::state_type state = 0; + boost::asio::detail::socket_ops::close(sv[1], state, true, temp_ec); + return res; + } + + return {}; +} + +void socket::wait_op_::initiate(completion_handler handler) +{ + return socket_.async_wait(wt_, std::move(handler)); +} + +void socket::connect_op_::initiate(completion_handler handler) +{ + socket_->adopt_endpoint_(ep_); + socket_->socket_.async_connect(ep_, std::move(handler)); +} + + +} \ No newline at end of file diff --git a/src/io/ssl.cpp b/src/io/ssl.cpp new file mode 100644 index 00000000..b2cb9435 --- /dev/null +++ b/src/io/ssl.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include + +namespace boost::async::io +{ +/* +system::result ssl_stream::duplicate() +{ + SSL_dup(ssl_stream_.native_handle()); + auto res = detail::io::duplicate_handle(ssl_stream_.native_handle()); + if (!res) + return res.error(); + + return {system::in_place_value, ssl_stream(*res)}; +}*/ + +static asio::ssl::context & get_ssl_context() +{ + thread_local static asio::ssl::context ctx{asio::ssl::context_base::tlsv13}; + return ctx; +} + + +ssl_stream::ssl_stream(const async::executor & exec) + : ssl_stream_base(exec, get_ssl_context()), socket(ssl_stream_.next_layer()) +{ +} + +ssl_stream::ssl_stream(ssl_stream && lhs) + : ssl_stream_base(std::move(lhs.ssl_stream_)), socket(ssl_stream_.next_layer()) +{ +} + +ssl_stream::ssl_stream(stream_socket && socket_) + : ssl_stream_base(std::move(socket_.stream_socket_), get_ssl_context()), socket(ssl_stream_.next_layer()) {} + +ssl_stream::ssl_stream(asio::ssl::context & ctx, const async::executor & exec) + : ssl_stream_base(exec, ctx), socket(ssl_stream_.next_layer()) {} + +ssl_stream::ssl_stream(asio::ssl::context & ctx, stream_socket && socket_) + : ssl_stream_base(std::move(socket_.stream_socket_), ctx), socket(ssl_stream_.next_layer()) {} + + + +void ssl_stream::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + ssl_stream_.async_read_some(buffer, std::move(h)); +} + +void ssl_stream::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + ssl_stream_.async_write_some(buffer, std::move(h)); +} + +system::result ssl_stream::close() { return socket::close(); } +system::result ssl_stream::cancel() { return socket::cancel(); } +bool ssl_stream::is_open() const {return socket::is_open();} + +ssl_stream::handshake_op_ ssl_stream::async_handshake(handshake_type ht) +{ + return handshake_op_{*this, ht}; +} +ssl_stream::handshake_op_buf_ ssl_stream::async_handshake(handshake_type ht, buffers::const_buffer buf) +{ + return handshake_op_buf_{*this, ht, buf}; +} +ssl_stream::handshake_op_buf_seq_ ssl_stream::async_handshake(handshake_type ht, buffers::const_buffer_span buf) +{ + return handshake_op_buf_seq_{*this, ht, buffers::const_buffer_subspan{buf}}; +} +ssl_stream::handshake_op_buf_seq_ ssl_stream::async_handshake(handshake_type ht, buffers::const_buffer_subspan buf) +{ + return handshake_op_buf_seq_{*this, ht, buf}; +} + +ssl_stream::shutdown_op_ ssl_stream::async_shutdown() +{ + return shutdown_op_{*this}; +} + +void ssl_stream::adopt_endpoint_(endpoint & ep) +{ + + switch (ep.protocol().family()) + { + case BOOST_ASIO_OS_DEF(AF_INET): BOOST_FALLTHROUGH; + case BOOST_ASIO_OS_DEF(AF_INET6): + if (ep.protocol().protocol() == BOOST_ASIO_OS_DEF(IPPROTO_IP)) + ep.set_protocol(BOOST_ASIO_OS_DEF(IPPROTO_TCP)); + case AF_UNIX: + if (ep.protocol().type() == 0) + ep.set_type(BOOST_ASIO_OS_DEF(SOCK_STREAM)); + } +} + +void ssl_stream::handshake_op_::initiate(completion_handler handler) +{ + stream.ssl_stream_.async_handshake(ht, std::move(handler)); + +} + +void ssl_stream::handshake_op_buf_::initiate(completion_handler handler) +{ + stream.ssl_stream_.async_handshake(ht, io::buffers::const_buffer_subspan{&buf, 1u}, std::move(handler)); +} + +void ssl_stream::handshake_op_buf_seq_::initiate(completion_handler handler) +{ + stream.ssl_stream_.async_handshake(ht, buf, std::move(handler)); +} + +void ssl_stream::shutdown_op_::initiate(completion_handler handler) +{ + stream.ssl_stream_.async_shutdown(std::move(handler)); +} + + + + +} \ No newline at end of file diff --git a/src/io/steady_timer.cpp b/src/io/steady_timer.cpp new file mode 100644 index 00000000..8a8c6ab4 --- /dev/null +++ b/src/io/steady_timer.cpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include + +namespace boost::async::io +{ + +steady_timer::steady_timer(const async::executor & exec) + : timer_(exec) {} +steady_timer::steady_timer(const time_point &expiry_time, const async::executor & exec) + : timer_(exec, expiry_time) {} +steady_timer::steady_timer(const duration &expiry_time, const async::executor & exec) + : timer_(exec, expiry_time) {} + +void steady_timer::cancel() +{ + timer_.cancel(); +} + + +auto steady_timer::expiry() const -> time_point +{ + return timer_.expiry(); +} + +void steady_timer::reset(const time_point &expiry_time) +{ + timer_.expires_at(expiry_time); +} + +void steady_timer::reset(const duration &expiry_time) +{ + timer_.expires_after(expiry_time); +} + +bool steady_timer::expired() const { return timer_.expiry() < clock_type::now(); } + +void steady_timer::wait_op_::initiate(completion_handler handler) +{ + timer_->timer_.async_wait(std::move(handler)); +} + + +} \ No newline at end of file diff --git a/src/io/stream_file.cpp b/src/io/stream_file.cpp new file mode 100644 index 00000000..b4ac8832 --- /dev/null +++ b/src/io/stream_file.cpp @@ -0,0 +1,68 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#if defined(BOOST_ASIO_HAS_FILE) +#include + +namespace boost::async::io +{ + +system::result stream_file::duplicate(const async::executor & exec) +{ + auto res = detail::io::duplicate_handle(stream_file_.native_handle()); + if (!res) + return res.error(); + + return {system::in_place_value, stream_file(*res, exec)}; +} + + + +stream_file::stream_file(const async::executor & exec) + : file(stream_file_), stream_file_(exec) +{ +} + +stream_file::stream_file(native_handle_type h, const async::executor & exec) + : file(stream_file_), stream_file_(exec) +{ +} + +stream_file::stream_file(stream_file && lhs) + : file(stream_file_), stream_file_(std::move(lhs.stream_file_)) +{ +} + +stream_file::stream_file(core::string_view file_, flags open_flags, const async::executor & exec) + : file(stream_file_), stream_file_(exec, std::string(file_), open_flags) +{ +} + + +void stream_file::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + stream_file_.async_read_some(buffer, std::move(h)); +} + +void stream_file::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + stream_file_.async_write_some(buffer, std::move(h)); +} + +system::result stream_file::close() { return file::close(); } +system::result stream_file::cancel() { return file::cancel(); } +bool stream_file::is_open() const {return file::is_open();} + +} + +#endif \ No newline at end of file diff --git a/src/io/stream_socket.cpp b/src/io/stream_socket.cpp new file mode 100644 index 00000000..65b3cfe7 --- /dev/null +++ b/src/io/stream_socket.cpp @@ -0,0 +1,77 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +system::result stream_socket::duplicate(const async::executor & exec) +{ + auto res = detail::io::duplicate_handle(stream_socket_.native_handle()); + if (!res) + return res.error(); + + return {system::in_place_value, stream_socket(*res, local_endpoint()->protocol(), exec)}; +} + + + +stream_socket::stream_socket(const async::executor & exec) + : socket(stream_socket_), stream_socket_(exec) +{ +} + +stream_socket::stream_socket(native_handle_type h, protocol_type protocol, const async::executor & exec) + : socket(stream_socket_), stream_socket_(exec, protocol, h) +{ +} + +stream_socket::stream_socket(stream_socket && lhs) + : socket(stream_socket_), stream_socket_(std::move(lhs.stream_socket_)) +{ +} +stream_socket::stream_socket(endpoint ep, const async::executor & exec) + : socket(stream_socket_), stream_socket_(exec, ep) +{ +} + +void stream_socket::async_read_some_impl_( + buffers::mutable_buffer_subspan buffer, + async::completion_handler h) +{ + stream_socket_.async_read_some(buffer, std::move(h)); +} + +void stream_socket::async_write_some_impl_( + buffers::const_buffer_subspan buffer, + async::completion_handler h) +{ + stream_socket_.async_write_some(buffer, std::move(h)); +} + +system::result stream_socket::close() { return socket::close(); } +system::result stream_socket::cancel() { return socket::cancel(); } +bool stream_socket::is_open() const {return socket::is_open();} + +void stream_socket::adopt_endpoint_(endpoint & ep) +{ + + switch (ep.protocol().family()) + { + case BOOST_ASIO_OS_DEF(AF_INET): BOOST_FALLTHROUGH; + case BOOST_ASIO_OS_DEF(AF_INET6): + if (ep.protocol().protocol() == BOOST_ASIO_OS_DEF(IPPROTO_IP)) + ep.set_protocol(BOOST_ASIO_OS_DEF(IPPROTO_TCP)); + case AF_UNIX: + if (ep.protocol().type() == 0) + ep.set_type(BOOST_ASIO_OS_DEF(SOCK_STREAM)); + } +} + +} \ No newline at end of file diff --git a/src/io/system_timer.cpp b/src/io/system_timer.cpp new file mode 100644 index 00000000..685a050b --- /dev/null +++ b/src/io/system_timer.cpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include + +namespace boost::async::io +{ + +system_timer::system_timer(const async::executor & exec) + : timer_(exec) {} +system_timer::system_timer(const time_point &expiry_time, const async::executor & exec) + : timer_(exec, expiry_time) {} +system_timer::system_timer(const duration &expiry_time, const async::executor & exec) + : timer_(exec, expiry_time) {} + +void system_timer::cancel() +{ + timer_.cancel(); +} + +auto system_timer::expiry() const -> time_point +{ + return timer_.expiry(); +} + +void system_timer::reset(const time_point &expiry_time) +{ + timer_.expires_at(expiry_time); +} + +void system_timer::reset(const duration &expiry_time) +{ + timer_.expires_after(expiry_time); +} + +bool system_timer::expired() const { return timer_.expiry() < clock_type::now(); } + + +void system_timer::wait_op_::initiate(completion_handler handler) +{ + timer_->timer_.async_wait(std::move(handler)); +} + +} \ No newline at end of file diff --git a/src/io/write.cpp b/src/io/write.cpp new file mode 100644 index 00000000..54bb8746 --- /dev/null +++ b/src/io/write.cpp @@ -0,0 +1,48 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +promise write(stream & source, buffers::const_buffer buffer) +{ + transfer_result tr{}; + + do + { + auto rd = co_await source.write_some(buffer); + tr += rd; + buffer += rd.transferred; + } + while (buffer.size() > 0 && !tr.has_error()); + co_return tr; +} + +promise write(stream & source, buffers::const_buffer_subspan buffer) +{ + transfer_result tr; + do + { + auto rd = co_await source.write_some(buffer); + tr.transferred += rd.transferred; + tr.error = rd.error; + buffer = buffers::sans_prefix(buffer, rd.transferred); + } + while (buffers::buffer_size(buffer) > 0 && !tr.has_error()); + co_return tr; +} + +promise write(stream & source, buffers::const_buffer_span buffer) +{ + return write(source, buffers::const_buffer_subspan(buffer)); +} + + +} \ No newline at end of file diff --git a/src/io/write_at.cpp b/src/io/write_at.cpp new file mode 100644 index 00000000..e8a5f11f --- /dev/null +++ b/src/io/write_at.cpp @@ -0,0 +1,51 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include + +namespace boost::async::io +{ + +promise write_at(random_access_device & source, std::uint64_t offset,buffers::const_buffer buffer) +{ + transfer_result tr; + + do + { + auto rd = co_await source.write_some_at(offset, buffer); + tr.transferred += rd.transferred; + offset += rd.transferred; + tr.error = rd.error; + buffer += rd.transferred; + } + while (buffer.size() > 0 && !tr.has_error()); + co_return tr; +} + +promise write_at(random_access_device & source, std::uint64_t offset, buffers::const_buffer_subspan buffer) +{ + transfer_result tr; + do + { + auto rd = co_await source.write_some_at(offset, buffer); + tr.transferred += rd.transferred; + offset += rd.transferred; + tr.error = rd.error; + buffer = buffers::sans_prefix(buffer, rd.transferred); + } + while (buffers::buffer_size(buffer) > 0 && !tr.has_error()); + co_return tr; +} + +promise write_at(random_access_device & source, std::uint64_t offset, buffers::const_buffer_span buffer) +{ + return write_at(source, offset, buffers::const_buffer_subspan(buffer)); +} + + +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0ee48eb7..0f235110 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -15,7 +15,7 @@ target_link_libraries(boost_async_main_compile Boost::async OpenSSL::SSL) target_link_libraries(boost_async_basic_tests Boost::async OpenSSL::SSL) add_test(NAME boost_async-main COMMAND boost_async_main) -add_test(NAME boost_async-basic_tests COMMAND boost_async_basic_tests) +add_test(NAME boost_async-basic COMMAND boost_async_basic_tests) file(GLOB ALL_PUBLIC_HEADERS RELATIVE ${CMAKE_SOURCE_DIR}/include/async ${CMAKE_SOURCE_DIR}/include/boost/async/*.hpp) set(ALL_PUBLIC_HEADER_TESTS "") @@ -28,4 +28,6 @@ foreach(HEADER ${ALL_PUBLIC_HEADERS}) endforeach() add_library(boost_async_test_all_public_headers_test OBJECT ${ALL_PUBLIC_HEADER_TESTS}) -target_link_libraries(boost_async_test_all_public_headers_test PUBLIC Boost::headers) \ No newline at end of file +target_link_libraries(boost_async_test_all_public_headers_test PUBLIC Boost::headers) + +add_subdirectory(io) diff --git a/test/io/CMakeLists.txt b/test/io/CMakeLists.txt new file mode 100644 index 00000000..b0978b4b --- /dev/null +++ b/test/io/CMakeLists.txt @@ -0,0 +1,19 @@ +add_subdirectory(buffers) + +add_executable(boost_async_io_tests + acceptor.cpp + datagram_socket.cpp + stream_socket.cpp + seq_packet_socket.cpp + endpoint.cpp + pipe.cpp + resolver.cpp + sleep.cpp + ssl.cpp + ../doctest.cpp + read.cpp + copy.cpp + read_until.cpp + process.cpp) +target_link_libraries(boost_async_io_tests Boost::container Boost::async OpenSSL::SSL) +add_test(NAME boost_async-io COMMAND boost_async_io_tests) diff --git a/test/io/acceptor.cpp b/test/io/acceptor.cpp new file mode 100644 index 00000000..973b019d --- /dev/null +++ b/test/io/acceptor.cpp @@ -0,0 +1,38 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include + +#include + +CO_TEST_CASE("acceptor") +{ + using namespace boost::async; + io::endpoint ep{io::tcp, "127.0.0.1", 8080}; + io::acceptor acceptor{ep}; + io::stream_socket ss{}; + + io::stream_socket accepted; + auto [_, connected] = + co_await join(acceptor.accept(accepted).value(), + ss.connect(ep)); + + CHECK(!connected.has_error()); + + CHECK(accepted.remote_endpoint()->protocol() == ss.local_endpoint()->protocol()); + CHECK(accepted.local_endpoint()->protocol() == ss.remote_endpoint()->protocol()); + + CHECK(get(*accepted.remote_endpoint()).port() == get(*ss.local_endpoint()) .port()); + CHECK(get(*accepted.local_endpoint()) .port() == get(*ss.remote_endpoint()).port()); + CHECK(get(*accepted.remote_endpoint()).addr() == get(*ss.local_endpoint()) .addr()); + CHECK(get(*accepted.local_endpoint()) .addr() == get(*ss.remote_endpoint()).addr()); +} diff --git a/test/io/buffers/CMakeLists.txt b/test/io/buffers/CMakeLists.txt new file mode 100644 index 00000000..6ed2ea17 --- /dev/null +++ b/test/io/buffers/CMakeLists.txt @@ -0,0 +1,46 @@ +# +# Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/CPPAlliance/buffers +# + +set(PFILES + CMakeLists.txt + Jamfile + test_helpers.hpp + algorithm.cpp + any_dynamic_buffer.cpp + buffer.cpp + buffer_copy.cpp + buffer_size.cpp + buffers.cpp + circular_buffer.cpp + const_buffer.cpp + const_buffer_pair.cpp + const_buffer_span.cpp + const_buffer_subspan.cpp + dynamic_buffer_view.cpp + flat_buffer.cpp + mutable_buffer.cpp + mutable_buffer_pair.cpp + mutable_buffer_span.cpp + mutable_buffer_subspan.cpp + range.cpp + string_buffer.cpp + tag_invoke.cpp + test_main.cpp + type_traits.cpp + ) + +add_executable(boost_async_io_buffers_tests ${PFILES} ${EXTRAFILES}) + +add_test(NAME boost_async_io-buffers_tests COMMAND boost_async_io_buffers_tests) + +target_include_directories(boost_async_io_buffers_tests PRIVATE . ) +target_link_libraries( + boost_async_io_buffers_tests PRIVATE + boost_async + ) diff --git a/test/io/buffers/Jamfile b/test/io/buffers/Jamfile new file mode 100644 index 00000000..b5279826 --- /dev/null +++ b/test/io/buffers/Jamfile @@ -0,0 +1,48 @@ +# +# Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# Official repository: https://github.com/CPPAlliance/buffers +# + +import testing ; + +project + : requirements + $(c11-requires) + /boost/async//boost_async + ../../url/extra/test_main.cpp + . + ../../url/extra + ; + +local SOURCES = + algorithm.cpp + any_dynamic_buffer.cpp + buffers.cpp + buffer_copy.cpp + buffer_size.cpp + buffers.cpp + circular_buffer.cpp + const_buffer.cpp + const_buffer_pair.cpp + const_buffer_span.cpp + const_buffer_subspan.cpp + flat_buffer.cpp + mutable_buffer.cpp + mutable_buffer_pair.cpp + mutable_buffer_span.cpp + mutable_buffer_subspan.cpp + range.cpp + string_buffer.cpp + tag_invoke.cpp + type_traits.cpp + ; + +for local f in $(SOURCES) +{ + run $(f) : : : ; + #run $(f) : target-name $(f:B)_ ; +} diff --git a/test/io/buffers/algorithm.cpp b/test/io/buffers/algorithm.cpp new file mode 100644 index 00000000..3e6335be --- /dev/null +++ b/test/io/buffers/algorithm.cpp @@ -0,0 +1,204 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include +#include "test_suite.hpp" + +namespace boost::async::io::buffers { + +struct asio_mutable_buffer +{ + std::size_t size() const noexcept { return 0; } + void* data() const noexcept { return nullptr; } +}; + +struct asio_const_buffer +{ + std::size_t size() const noexcept { return 0; } + void const* data() const noexcept { return nullptr; } +}; + +struct not_a_buffer +{ + std::size_t size() const noexcept; + char* data() const noexcept; +}; + +struct asio_mutable_buffers +{ + asio_mutable_buffer const* begin() const noexcept; + asio_mutable_buffer const* end() const noexcept; +}; + +struct asio_const_buffers +{ + asio_const_buffer const* begin() const noexcept; + asio_const_buffer const* end() const noexcept; +}; + +BOOST_STATIC_ASSERT( const_buffer_sequence ); +BOOST_STATIC_ASSERT( const_buffer_sequence ); +BOOST_STATIC_ASSERT(! mutable_buffer_sequence ); +BOOST_STATIC_ASSERT( mutable_buffer_sequence ); + +//BOOST_STATIC_ASSERT( is_const_buffer_sequence ::value); +//BOOST_STATIC_ASSERT( is_const_buffer_sequence ::value); +//BOOST_STATIC_ASSERT( is_mutable_buffer_sequence ::value); +//BOOST_STATIC_ASSERT(! is_mutable_buffer_sequence ::value); + +BOOST_STATIC_ASSERT( const_buffer_sequence ); +BOOST_STATIC_ASSERT( const_buffer_sequence ); +BOOST_STATIC_ASSERT(! mutable_buffer_sequence ); +BOOST_STATIC_ASSERT( mutable_buffer_sequence ); + +struct algorithm_test +{ + void + testBufferSize() + { + { + char a[7]{}; + char b[11]{}; + const_buffer_pair p( + const_buffer(a, sizeof(a)), + const_buffer(b, sizeof(b))); + BOOST_TEST_EQ( + buffer_size(p), + sizeof(a) + sizeof(b)); + } + } + + void + testBufferCopy() + { + std::string const s = + "Howdy partner"; + auto const N = s.size(); + for(std::size_t i = 0; + i < N; ++i) + { + for(std::size_t j = 0; + j < N; ++j) + { + for(std::size_t k = 0; + k < N + 2; ++k) + { + const_buffer_pair p0( + const_buffer( + s.data(), i), + const_buffer( + s.data() + i, N - i)); + char tmp[13]; + std::memset(tmp, 0, sizeof(tmp)); + mutable_buffer_pair p1( + mutable_buffer( + tmp, j), + mutable_buffer( + tmp + j, N - j)); + auto const n = buffer_copy( + p1, p0, k); + BOOST_TEST_LE(n, N); + BOOST_TEST_EQ( + s.substr(0, n), + std::string(tmp, n)); + } + } + } + } + + void + testAlgorithms() + { + // prefix + + { + char buf[16]{}; + const_buffer b(buf, sizeof(buf)); + const_buffer bp = prefix(b, 5); + BOOST_TEST_EQ(bp.size(), 5); + } + + { + char buf[16]{}; + mutable_buffer b(buf, sizeof(buf)); + mutable_buffer bp = prefix(b, 5); + BOOST_TEST_EQ(bp.size(), 5); + } + + // sans_prefix + + { + char buf[16]{}; + const_buffer b(buf, sizeof(buf)); + const_buffer bp = sans_prefix(b, 5); + BOOST_TEST_EQ(bp.size(), 11); + } + + { + char buf[16]{}; + mutable_buffer b(buf, sizeof(buf)); + mutable_buffer bp = sans_prefix(b, 5); + BOOST_TEST_EQ(bp.size(), 11); + } + + // suffix + + { + char buf[16]{}; + const_buffer b(buf, sizeof(buf)); + const_buffer bp = suffix(b, 5); + BOOST_TEST_EQ(bp.size(), 5); + } + + { + char buf[16]{}; + mutable_buffer b(buf, sizeof(buf)); + mutable_buffer bp = suffix(b, 5); + BOOST_TEST_EQ(bp.size(), 5); + } + + // sans_suffix + + { + char buf[16]{}; + const_buffer b(buf, sizeof(buf)); + const_buffer bp = sans_suffix(b, 5); + BOOST_TEST_EQ(bp.size(), 11); + } + + { + char buf[16]{}; + mutable_buffer b(buf, sizeof(buf)); + mutable_buffer bp = sans_suffix(b, 5); + BOOST_TEST_EQ(bp.size(), 11); + } + } + + void + run() + { + testBufferSize(); + testBufferCopy(); + testAlgorithms(); + } +}; + +TEST_SUITE( + algorithm_test, + "boost.buffers.algorithm"); + +} // boost::buffers diff --git a/test/io/buffers/any_dynamic_buffer.cpp b/test/io/buffers/any_dynamic_buffer.cpp new file mode 100644 index 00000000..b771c1ca --- /dev/null +++ b/test/io/buffers/any_dynamic_buffer.cpp @@ -0,0 +1,84 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct any_dynamic_buffer_test +{ + BOOST_STATIC_ASSERT(dynamic_buffer); + + void + testAny() + { + auto const& pat = test_pattern(); + + for(std::size_t i = 0; + i <= pat.size(); ++i) + for(std::size_t j = 0; + j <= pat.size(); ++j) + for(std::size_t k = 0; + k <= pat.size(); ++k) + { + std::string s(pat.size(), 0); + auto db = make_any(circular_buffer( + &s[0], s.size())); + if( j < pat.size() && + i > 0) + { + db.prepare(i); + db.commit(i); + db.consume(i - 1); + db.commit(buffer_copy( + db.prepare(j), + buffer( + pat.data(), + pat.size()))); + db.consume(1); + } + else + { + db.commit(buffer_copy( + db.prepare(j), + buffer( + pat.data(), + pat.size()))); + } + db.commit(buffer_copy( + db.prepare(pat.size() - j), + buffer( + pat.data() + j, + pat.size() - j))); + BOOST_TEST_EQ(test_to_string( + db.data()), pat); + test_buffer_sequence(db.data()); + db.consume(k); + BOOST_TEST_EQ(test_to_string( + db.data()), pat.substr(k)); + } + } + + void + run() + { + testAny(); + } +}; + +TEST_SUITE( + any_dynamic_buffer_test, + "boost.buffers.any_dynamic_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/buffer.cpp b/test/io/buffers/buffer.cpp new file mode 100644 index 00000000..f2007b70 --- /dev/null +++ b/test/io/buffers/buffer.cpp @@ -0,0 +1,82 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_suite.hpp" + +namespace boost::async::io::buffers { + +struct buffer_test +{ + void + testBuffer() + { + char buf[3]{}; + char const* cbuf = buf; + + // buffer(mutable_buffer) + { + mutable_buffer b(buf, 3); + auto b1 = buffer(b); + BOOST_TEST_EQ(b1.data(), b.data()); + BOOST_TEST_EQ(b1.size(), b.size()); + } + + // buffer(void*, std::size_t) + { + auto b = buffer(buf, 3); + BOOST_TEST_EQ(b.data(), buf); + BOOST_TEST_EQ(b.size(), 3); + } + + // buffer(const_buffer) + { + const_buffer b(cbuf, 3); + auto b1 = buffer(b); + BOOST_TEST_EQ(b1.data(), b.data()); + BOOST_TEST_EQ(b1.size(), b.size()); + } + + // buffer(void const*, std::size_t) + { + auto b = buffer(cbuf, 3); + BOOST_TEST_EQ(b.data(), cbuf); + BOOST_TEST_EQ(b.size(), 3); + } + + // buffer(T(&)[N]) + { + mutable_buffer b = buffer(buf); + BOOST_TEST_EQ(b.data(), buf); + BOOST_TEST_EQ(b.size(), 3); + } + + // buffer(T const(&)[N]) + { + char const cbuf3[3]{}; + const_buffer b = buffer(cbuf3); + BOOST_TEST_EQ(b.data(), cbuf3); + BOOST_TEST_EQ(b.size(), 3); + } + } + + void + run() + { + testBuffer(); + } +}; + +TEST_SUITE( + buffer_test, + "boost.buffers.buffer"); + +} // boost::buffers diff --git a/test/io/buffers/buffer_copy.cpp b/test/io/buffers/buffer_copy.cpp new file mode 100644 index 00000000..c4ffa875 --- /dev/null +++ b/test/io/buffers/buffer_copy.cpp @@ -0,0 +1,85 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct buffer_copy_test +{ + void + testBufferCopy() + { + auto const& pat = test_pattern(); + + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + for(std::size_t j = 0; + j <= pat.size(); ++j) + { + std::string s; + s.resize(pat.size()); + const_buffer cb[2] = { + { &pat[0], i }, + { &pat[i], + pat.size() - i } }; + mutable_buffer mb[2] = { + { &s[0], j }, + { &s[j], + pat.size() - j } }; + auto n = buffer_copy( + mutable_buffer_span(mb, 2), + const_buffer_span(cb, 2)); + BOOST_TEST_EQ(n, pat.size()); + BOOST_TEST_EQ(s, pat); + } + for(std::size_t j = 0; + j <= pat.size(); ++j) + { + for(std::size_t k = 0; + k <= pat.size(); ++k) + { + std::string s; + s.resize(pat.size()); + const_buffer cb[2] = { + { &pat[0], i }, + { &pat[i], + pat.size() - i } }; + mutable_buffer mb[2] = { + { &s[0], j }, + { &s[j], + pat.size() - j } }; + auto n = buffer_copy( + mutable_buffer_span(mb, 2), + const_buffer_span(cb, 2), k); + s.resize(n); + BOOST_TEST_EQ(s, + pat.substr(0, k)); + } + } + } + } + void + run() + { + testBufferCopy(); + } +}; + +TEST_SUITE( + buffer_copy_test, + "boost.buffers.buffer_copy"); + +} // boost::buffers diff --git a/test/io/buffers/buffer_size.cpp b/test/io/buffers/buffer_size.cpp new file mode 100644 index 00000000..24be1f0c --- /dev/null +++ b/test/io/buffers/buffer_size.cpp @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include +#include + +#include "test_suite.hpp" + +namespace boost::async::io::buffers { + +struct buffer_size_test +{ + void + testBufferSize() + { + char data[9]; + for(std::size_t i = 0; i < 3; ++i) + for(std::size_t j = 0; j < 3; ++j) + for(std::size_t k = 0; k < 3; ++k) + { + const_buffer cb[3] = { + { data, i }, + { data + i, j }, + { data + i + j, k } + }; + const_buffer_span s(cb, 3); + BOOST_TEST_EQ( + buffer_size(s), i + j + k); + } + } + + void + run() + { + testBufferSize(); + } +}; + +TEST_SUITE( + buffer_size_test, + "boost.buffers.buffer_size"); + +} // boost::buffers diff --git a/test/io/buffers/buffers.cpp b/test/io/buffers/buffers.cpp new file mode 100644 index 00000000..f6cf257c --- /dev/null +++ b/test/io/buffers/buffers.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include diff --git a/test/io/buffers/circular_buffer.cpp b/test/io/buffers/circular_buffer.cpp new file mode 100644 index 00000000..da348101 --- /dev/null +++ b/test/io/buffers/circular_buffer.cpp @@ -0,0 +1,185 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct circular_buffer_test +{ + void + testMembers() + { + std::string pat = test_pattern(); + + // circular_buffer() + { + circular_buffer cb; + BOOST_TEST_EQ(cb.size(), 0); + } + +#if 0 + // circular_buffer(mutable_buffer) + { + circular_buffer cb( + buffer(&pat[0], pat.size())); + BOOST_TEST_EQ(cb.size(), 0); + BOOST_TEST_EQ(cb.capacity(), pat.size()); + BOOST_TEST_EQ(cb.max_size(), pat.size()); + } +#endif + + // circular_buffer(void*, std::size_t) + { + circular_buffer cb( + &pat[0], pat.size()); + BOOST_TEST_EQ(cb.size(), 0); + BOOST_TEST_EQ(cb.capacity(), pat.size()); + BOOST_TEST_EQ(cb.max_size(), pat.size()); + } + + // circular_buffer( + // void*, std::size_t, std:size_t) + { + circular_buffer cb( + &pat[0], pat.size(), 6); + BOOST_TEST_EQ(cb.size(), 6); + BOOST_TEST_EQ( + cb.capacity(), pat.size() - 6); + BOOST_TEST_EQ(cb.max_size(), pat.size()); + BOOST_TEST_EQ( + test_to_string(cb.data()), + pat.substr(0, 6)); + } + { + BOOST_TEST_THROWS( + circular_buffer( + &pat[0], pat.size(), 600), + std::exception); + } + + // circular_buffer( + // circular_buffer const&) + { + circular_buffer cb0( + buffer(&pat[0], pat.size())); + circular_buffer cb1(cb0); + BOOST_TEST_EQ(cb1.size(), cb0.size()); + BOOST_TEST_EQ(cb1.capacity(), cb0.capacity()); + BOOST_TEST_EQ(cb1.max_size(), cb0.max_size()); + } + + // operator=( + // circular_buffer const&) + { + circular_buffer cb0( + buffer(&pat[0], pat.size())); + circular_buffer cb1; + cb1 = cb0; + BOOST_TEST_EQ(cb1.size(), cb0.size()); + BOOST_TEST_EQ(cb1.capacity(), cb0.capacity()); + BOOST_TEST_EQ(cb1.max_size(), cb0.max_size()); + } + + // prepare(std::size_t) + { + circular_buffer cb(buffer( + &pat[0], pat.size())); + BOOST_TEST_THROWS( + cb.prepare(cb.capacity() + 1), + std::length_error); + } + + // commit(std::size_t) + { + circular_buffer cb(buffer( + &pat[0], pat.size())); + auto n = pat.size() / 2; + cb.prepare(pat.size()); + cb.commit(n); + BOOST_TEST_EQ( + test_to_string(cb.data()), + pat.substr(0, n)); + } + } + + void + testBuffer() + { + auto const& pat = test_pattern(); + + for(std::size_t i = 0; + i <= pat.size(); ++i) + for(std::size_t j = 0; + j <= pat.size(); ++j) + for(std::size_t k = 0; + k <= pat.size(); ++k) + { + std::string s(pat.size(), 0); + circular_buffer bs( + &s[0], s.size()); + BOOST_TEST_EQ( + bs.capacity(), s.size()); + if( j < pat.size() && + i > 0) + { + bs.prepare(i); + bs.commit(i); + BOOST_TEST_EQ( + bs.capacity(), + bs.max_size() - bs.size()); + bs.consume(i - 1); + bs.commit(buffer_copy( + bs.prepare(j), + buffer( + pat.data(), + pat.size()))); + bs.consume(1); + } + else + { + bs.commit(buffer_copy( + bs.prepare(j), + buffer( + pat.data(), + pat.size()))); + BOOST_TEST_EQ( + bs.capacity(), + bs.max_size() - bs.size()); + } + bs.commit(buffer_copy( + bs.prepare(pat.size() - j), + buffer( + pat.data() + j, + pat.size() - j))); + BOOST_TEST_EQ(test_to_string( + bs.data()), pat); + test_buffer_sequence(bs.data()); + bs.consume(k); + BOOST_TEST_EQ(test_to_string( + bs.data()), pat.substr(k)); + } + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + circular_buffer_test, + "boost.buffers.circular_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/const_buffer.cpp b/test/io/buffers/const_buffer.cpp new file mode 100644 index 00000000..33584e18 --- /dev/null +++ b/test/io/buffers/const_buffer.cpp @@ -0,0 +1,114 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct const_buffer_test +{ + void + testMembers() + { + // const_buffer() + BOOST_TEST_EQ(const_buffer().size(), 0); + + // const_buffer(void const*, size_t) + { + auto p = "12345"; + const_buffer b( p, 5 ); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // const_buffer(const_buffer) + { + auto p = "12345"; + const_buffer b0( p, 5 ); + const_buffer b(b0); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // const_buffer(mutable_buffer) + { + char buf[6] = "12345"; + mutable_buffer b0( buf, 5 ); + const_buffer b(b0); + BOOST_TEST_EQ(b.data(), buf); + BOOST_TEST_EQ(b.size(), 5); + } + + // operator=(const_buffer) + { + auto p = "12345"; + const_buffer b; + b = const_buffer(p, 5); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // operator+=(std::size_t) + { + { + auto p = "12345"; + const_buffer b; + b = const_buffer(p, 5); + b += 2; + BOOST_TEST_EQ(b.data(), p + 2); + BOOST_TEST_EQ(b.size(), 3); + } + { + auto p = "12345"; + const_buffer b; + b = const_buffer(p, 5); + b += 6; + BOOST_TEST_EQ(b.size(), 0); + } + } + + // operator+(const_buffer, size_t) + // operator+(size_t, const_buffer) + { + auto p = "12345"; + const_buffer b0(p, 5); + const_buffer b1(p, 5); + b0 = b0 + 2; + b1 = 2 + b1; + BOOST_TEST_EQ(b0.data(), p + 2); + BOOST_TEST_EQ(b0.size(), 3); + BOOST_TEST_EQ(b0.data(), b1.data()); + BOOST_TEST_EQ(b0.size(), b1.size()); + } + } + + void + testBuffer() + { + auto const& pat = test_pattern(); + const_buffer cb(&pat[0], pat.size()); + test_buffer_sequence(cb); + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + const_buffer_test, + "boost.buffers.const_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/const_buffer_pair.cpp b/test/io/buffers/const_buffer_pair.cpp new file mode 100644 index 00000000..6ece786a --- /dev/null +++ b/test/io/buffers/const_buffer_pair.cpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct const_buffer_pair_test +{ + void + testMembers() + { + auto const& pat = test_pattern(); + + // const_buffer_pair() + { + const_buffer_pair cb; + BOOST_TEST_EQ( + buffer_size(cb), 0); + } + + // const_buffer_pair( + // const_buffer_pair const&), + // const_buffer_pair( + // const_buffer const&) + // const_buffer const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + const_buffer_pair cb0( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + const_buffer_pair cb1(cb0); + BOOST_TEST_EQ( + test_to_string(cb0), pat); + BOOST_TEST_EQ( + test_to_string(cb0), + test_to_string(cb1)); + BOOST_TEST_EQ( + cb0[0].data(), cb1[0].data()); + BOOST_TEST_EQ( + cb0[1].size(), cb1[1].size()); + auto const& ccb0 = cb0; + auto const& ccb1 = cb1; + BOOST_TEST_EQ( + ccb0[0].data(), ccb1[0].data()); + BOOST_TEST_EQ( + ccb0[1].size(), ccb1[1].size()); + } + } + + // const_buffer_pair( + // mutable_buffer_pair) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + auto s = pat; + mutable_buffer_pair b( + { &s[0], i }, + { &s[i], + s.size() - i }); + const_buffer_pair cb(b); + BOOST_TEST_EQ( + test_to_string(cb), pat); + BOOST_TEST_EQ( + test_to_string(cb), + test_to_string(b)); + } + } + + // operator=(const_buffer_pair const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + const_buffer_pair cb0( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + const_buffer_pair cb1; + cb1 = cb0; + BOOST_TEST_EQ( + test_to_string(cb0), pat); + BOOST_TEST_EQ( + test_to_string(cb0), + test_to_string(cb1)); + } + } + + // operator=(mutable_buffer_pair const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + auto s = pat; + mutable_buffer_pair b( + { &s[0], i }, + { &s[i], + s.size() - i }); + const_buffer_pair cb; + cb = b; + BOOST_TEST_EQ( + test_to_string(cb), pat); + BOOST_TEST_EQ( + test_to_string(cb), + test_to_string(b)); + } + } + } + + void + testBuffer() + { + auto const& pat = test_pattern(); + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + const_buffer_pair cb( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + test_buffer_sequence(cb); + } + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + const_buffer_pair_test, + "boost.buffers.const_buffer_pair"); + +} // boost::buffers diff --git a/test/io/buffers/const_buffer_span.cpp b/test/io/buffers/const_buffer_span.cpp new file mode 100644 index 00000000..c588091f --- /dev/null +++ b/test/io/buffers/const_buffer_span.cpp @@ -0,0 +1,81 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct const_buffer_span_test +{ + void + testMembers() + { + auto const& pat = test_pattern(); + const_buffer cb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + + // const_buffer_span() + { + const_buffer_span bs; + BOOST_TEST_EQ(buffer_size(bs), 0); + } + + // const_buffer_span( + // const_buffer const*, + // std::size_t n) + { + const_buffer_span cbs(cb, 3); + test_buffer_sequence(cbs); + } + + // const_buffer_span( + // ConstBufferSequence + { + const_buffer_pair bp; + const_buffer_span sp(bp); + BOOST_TEST( + sp.end() - sp.begin() == 2); + } + + // const_buffer_span( + // const_buffer_span const&) + { + const_buffer_span cbs0(cb, 3); + const_buffer_span cbs1(cbs0); + test_buffer_sequence(cbs1); + } + + // operator=( + // const_buffer_span const&) + { + const_buffer_span cbs0(cb, 3); + const_buffer_span cbs1; + cbs1 = cbs0; + test_buffer_sequence(cbs1); + } + } + + void + run() + { + testMembers(); + } +}; + +TEST_SUITE( + const_buffer_span_test, + "boost.buffers.const_buffer_span"); + +} // boost::buffers diff --git a/test/io/buffers/const_buffer_subspan.cpp b/test/io/buffers/const_buffer_subspan.cpp new file mode 100644 index 00000000..05844f50 --- /dev/null +++ b/test/io/buffers/const_buffer_subspan.cpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct const_buffer_subspan_test +{ + void + testMembers() + { + auto const& pat = test_pattern(); + const_buffer const cb[3] = { + { pat.data(), 3 }, + { pat.data() + 3, 5 }, + { pat.data() + 8, 7 } }; + + // const_buffer_subspan() + { + const_buffer_subspan bs; + BOOST_TEST_EQ(buffer_size(bs), 0); + } + + // const_buffer_subspan( + // const_buffer const*, std::size_t) + { + const_buffer_subspan s(cb, 3); + BOOST_TEST_EQ(buffer_size(s), 15); + } + { + const_buffer_subspan s(cb, 0); + BOOST_TEST_EQ(buffer_size(s), 0); + } + + // const_buffer_subspan( + // const_buffer_span const&) + { + const_buffer_span cs0(cb, 3); + const_buffer_subspan cs1(cs0); + BOOST_TEST_EQ( + buffer_size(cs0), + buffer_size(cs1)); + } + { + const_buffer_span cs0(cb, 0); + const_buffer_subspan cs1(cs0); + BOOST_TEST_EQ( + buffer_size(cs0), + buffer_size(cs1)); + } + + // const_buffer_subspan( + // const_buffer_subspan const&) + { + const_buffer_subspan s0(cb, 3); + const_buffer_subspan s1(s0); + BOOST_TEST_EQ( + buffer_size(s1), + buffer_size(s0)); + } + + // operator=( + // const_buffer_subspan const&) + { + const_buffer_subspan s; + BOOST_TEST_EQ(buffer_size(s), 0); + s = const_buffer_subspan(cb, 3); + BOOST_TEST_EQ(buffer_size(s), 15); + } + } + + void + testSequence() + { + auto const pat = test_pattern(); + + // length 1 + { + const_buffer cb = { + &pat[0], pat.size() }; + const_buffer_subspan s(&cb, 1); + test_buffer_sequence(s); + } + + // length 2 + { + const_buffer const cb[2] = { + { &pat[0], 7 }, + { &pat[7], 8 } }; + const_buffer_subspan s(cb, 2); + test_buffer_sequence(s); + } + + // length 3 + { + const_buffer const cb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + const_buffer_subspan s(cb, 3); + test_buffer_sequence(s); + } + } + + void + testSubspan() + { + std::string tmp; + auto const& pat = test_pattern(); + const_buffer const cb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + const_buffer_span const cs0(cb, 3); + + for(std::size_t i = 0; i <= pat.size(); ++i ) + { + { + auto b0 = prefix(cs0, i); + auto b1 = sans_prefix(cs0, i); + tmp = std::string(pat.size(), ' '); + mutable_buffer dest(&tmp[0], tmp.size()); + auto n = buffer_copy(dest, b0); + dest += n; + n += buffer_copy(dest, b1); + BOOST_TEST_EQ(n, pat.size()); + BOOST_TEST_EQ(tmp, pat); + } + for(std::size_t j = 0; j <= pat.size(); ++j) + { + auto b = prefix(sans_prefix(cs0, i), j); + tmp.resize(pat.size()); + tmp.resize(buffer_copy( + mutable_buffer( + &tmp[0], tmp.size()), b)); + if(i <= pat.size()) + BOOST_TEST_EQ(tmp, pat.substr(i, j)); + else + BOOST_TEST(tmp.empty()); + } + for(std::size_t j = 0; j <= pat.size(); ++j) + { + auto b = suffix(sans_suffix(cs0, i), j); + tmp.resize(pat.size()); + tmp.resize(buffer_copy( + mutable_buffer( + &tmp[0], tmp.size()), b)); + if(i <= pat.size()) + { + auto n = pat.size() - i; // inner length + if(n >= j) + BOOST_TEST_EQ(tmp, + pat.substr(n - j, j)); + else + BOOST_TEST_EQ(tmp, + pat.substr(0, n)); + } + else + { + BOOST_TEST(tmp.empty()); + } + } + } + } + + void + run() + { + testMembers(); + testSequence(); + testSubspan(); + } +}; + +TEST_SUITE( + const_buffer_subspan_test, + "boost.buffers.const_buffer_subspan"); + +} // boost::buffers diff --git a/test/io/buffers/dynamic_buffer_view.cpp b/test/io/buffers/dynamic_buffer_view.cpp new file mode 100644 index 00000000..8945fef5 --- /dev/null +++ b/test/io/buffers/dynamic_buffer_view.cpp @@ -0,0 +1,245 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct dynamic_buffer_view_test +{ + BOOST_STATIC_ASSERT( const_buffer_sequence); + BOOST_STATIC_ASSERT(mutable_buffer_sequence); + BOOST_STATIC_ASSERT(dynamic_buffer); + + void testOther() + { + char cc[64]; + flat_buffer fb{cc, 64}; + dynamic_buffer_view dbv{fb}; + + std::string data; + data.resize(64); + char c = ' '; + std::generate(data.begin(), data.end(), [&]{return c++;}); + + BOOST_TEST_EQ(dbv.capacity(), fb.capacity()); + BOOST_TEST_EQ(dbv.capacity(), 64u); + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), fb.capacity()); + BOOST_TEST_EQ(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), fb.size()); + BOOST_TEST_EQ(dbv.size(), 64u); + BOOST_TEST_EQ(fb.data().size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + dbv.consume(32); + BOOST_TEST_EQ(dbv.capacity(), fb.size()); + BOOST_TEST_EQ(dbv.size(), 32u); + BOOST_TEST_EQ(buffer_size(fb.data()), 32u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 32u); + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin() + 32, + data.end())); + + BOOST_TEST(std::memcmp(fb.data().data(), data.data(), fb.data().size())); + } + + + void testString() + { + std::vector vec; + dynamic_buffer_view dbv{vec}; + + std::string data; + data.resize(64); + char c = ' '; + std::generate(data.begin(), data.end(), [&]{return c++;}); + + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.capacity(), 0u); + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST_EQ(dbv.size(), 64u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 32u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 32u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin() + 32, + data.end())); + + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.capacity()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.capacity()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 96u); + + BOOST_TEST_EQ(dbv.size(), 96u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 64); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin(), + data.end())); + } + + void testCircular() + { + boost::circular_buffer vec{64u}; + dynamic_buffer_view dbv{vec}; + + std::string data; + data.resize(64); + char c = ' '; + std::generate(data.begin(), data.end(), [&]{return c++;}); + + BOOST_TEST_EQ(buffer_size(dbv.prepare(64)), 64u); + BOOST_TEST_EQ(dbv.capacity(), vec.capacity()); + BOOST_TEST_EQ(dbv.capacity(), 64u); + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST_EQ(dbv.size(), 64u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 32u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 32u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin() + 32, + data.end())); + + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 96u); + + BOOST_TEST_EQ(dbv.size(), 96u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 64); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin(), + data.end())); + } + + + void testQueue() + { + boost::container::deque>> vec; + dynamic_buffer_view dbv{vec}; + + std::string data; + data.resize(64); + char c = ' '; + std::generate(data.begin(), data.end(), [&]{return c++;}); + + BOOST_TEST_EQ(buffer_size(dbv.prepare(64)), 64u); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.capacity(), 64u); + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST_EQ(dbv.size(), 64u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 32u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 32u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin() + 32, + data.end())); + + BOOST_TEST_EQ(asio::buffer_copy(dbv.prepare(64), asio::buffer(data)), 64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.capacity(), 64u); + dbv.commit(64); + BOOST_TEST_EQ(dbv.capacity(), vec.size()); + BOOST_TEST_GE(dbv.size(), 64u); + BOOST_TEST_EQ(buffer_size(dbv.data()), 96u); + + BOOST_TEST_EQ(dbv.size(), 96u); + dbv.consume(32); + BOOST_TEST_GE(dbv.capacity(), vec.size()); + BOOST_TEST_EQ(dbv.size(), 64); + BOOST_TEST_EQ(buffer_size(dbv.data()), 64u); + + BOOST_TEST( + std::equal(asio::buffers_begin(dbv.data()), + asio::buffers_end(dbv.data()), + data.begin(), + data.end())); + } + + void + run() + { + testOther(); + testString(); + testCircular(); + testQueue(); + } +}; + +TEST_SUITE( + dynamic_buffer_view_test, + "boost.buffers.dynamic_buffer_view"); + +} // boost::buffers diff --git a/test/io/buffers/flat_buffer.cpp b/test/io/buffers/flat_buffer.cpp new file mode 100644 index 00000000..84e990d3 --- /dev/null +++ b/test/io/buffers/flat_buffer.cpp @@ -0,0 +1,167 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct flat_buffer_test +{ + BOOST_STATIC_ASSERT(dynamic_buffer); + + void + testMembers() + { + std::string pat = test_pattern(); + + // flat_buffer() + { + flat_buffer fb; + BOOST_TEST_EQ(fb.size(), 0); + BOOST_TEST_EQ(fb.max_size(), 0); + BOOST_TEST_EQ(fb.capacity(), 0); + } + + // flat_buffer(void*, size_t) + { + std::string s = pat; + flat_buffer fb(&s[0], s.size()); + BOOST_TEST_EQ(fb.size(), 0); + BOOST_TEST_EQ(fb.max_size(), s.size()); + BOOST_TEST_EQ(fb.capacity(), s.size()); + } + + // flat_buffer(void*, size_t, size_t) + { + std::string s = pat; + flat_buffer fb(&s[0], s.size(), 6); + BOOST_TEST_EQ(fb.size(), 6); + BOOST_TEST_EQ(fb.max_size(), s.size()); + BOOST_TEST_EQ(fb.capacity(), s.size()); + } + { + std::string s = pat; + BOOST_TEST_THROWS( + flat_buffer(&s[0], s.size(), + s.size() + 1), + std::invalid_argument); + } + + // flat_buffer(mutable_buffer) + { + std::string s = pat; + flat_buffer fb(mutable_buffer( + &s[0], s.size())); + BOOST_TEST_EQ(fb.size(), 0); + BOOST_TEST_EQ(fb.max_size(), s.size()); + BOOST_TEST_EQ(fb.capacity(), s.size()); + } + + // flat_buffer(mutable_buffer, size_t) + { + std::string s = pat; + flat_buffer fb(mutable_buffer( + &s[0], s.size()), 6); + BOOST_TEST_EQ(fb.size(), 6); + BOOST_TEST_EQ(fb.max_size(), s.size()); + BOOST_TEST_EQ(fb.capacity(), s.size()); + } + + // flat_buffer(flat_buffer const&) + { + std::string s = pat; + flat_buffer fb0(&s[0], s.size()); + flat_buffer fb1(fb0); + BOOST_TEST_EQ(fb1.size(), fb0.size()); + BOOST_TEST_EQ(fb1.max_size(), fb0.max_size()); + BOOST_TEST_EQ(fb1.capacity(), fb0.capacity()); + } + + // operator=(flat_buffer const&) + { + std::string s = pat; + flat_buffer fb0(&s[0], s.size()); + flat_buffer fb1; + fb1 = fb0; + BOOST_TEST_EQ(fb1.size(), fb0.size()); + BOOST_TEST_EQ(fb1.max_size(), fb0.max_size()); + BOOST_TEST_EQ(fb1.capacity(), fb0.capacity()); + } + + // prepare(std::size_t) + { + std::string s = pat; + flat_buffer fb(&s[0], s.size()); + BOOST_TEST_THROWS( + fb.prepare(s.size() + 1), + std::invalid_argument); + } + + // commit(std::size_t) + { + std::string s = pat; + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + flat_buffer fb( + &s[0], s.size()); + fb.prepare(s.size()); + fb.commit(i); + BOOST_TEST_EQ( + test_to_string(fb.data()), + pat.substr(0, i)); + } + } + + // consume(std::size_t) + { + } + } + + void + testBuffer() + { + auto const& pat = test_pattern(); + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + std::string s(pat.size(), 0); + flat_buffer fb(&s[0], s.size()); + fb.commit(buffer_copy( + fb.prepare(i), + buffer(&pat[0], i))); + fb.commit(buffer_copy( + fb.prepare(pat.size() - i), + buffer(&pat[i], pat.size() - i))); + BOOST_TEST_EQ(test_to_string( + fb.data()), pat); + fb.consume(i); + BOOST_TEST_EQ(test_to_string( + fb.data()), pat.substr(i)); + } + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + flat_buffer_test, + "boost.buffers.flat_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/mutable_buffer.cpp b/test/io/buffers/mutable_buffer.cpp new file mode 100644 index 00000000..dcc712de --- /dev/null +++ b/test/io/buffers/mutable_buffer.cpp @@ -0,0 +1,114 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct mutable_buffer_test +{ + void + testMembers() + { + // mutable_buffer() + BOOST_TEST_EQ(mutable_buffer().size(), 0); + + // mutable_buffer(void const*, size_t) + { + char p[6] = "12345"; + mutable_buffer b( p, 5 ); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // mutable_buffer(mutable_buffer) + { + char p[6] = "12345"; + mutable_buffer b0( p, 5 ); + mutable_buffer b(b0); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // mutable_buffer(mutable_buffer) + { + char buf[6] = "12345"; + mutable_buffer b0( buf, 5 ); + mutable_buffer b(b0); + BOOST_TEST_EQ(b.data(), buf); + BOOST_TEST_EQ(b.size(), 5); + } + + // operator=(mutable_buffer) + { + char p[6] = "12345"; + mutable_buffer b; + b = mutable_buffer(p, 5); + BOOST_TEST_EQ(b.data(), p); + BOOST_TEST_EQ(b.size(), 5); + } + + // operator+=(std::size_t) + { + { + char p[6] = "12345"; + mutable_buffer b; + b = mutable_buffer(p, 5); + b += 2; + BOOST_TEST_EQ(b.data(), p + 2); + BOOST_TEST_EQ(b.size(), 3); + } + { + char p[6] = "12345"; + mutable_buffer b; + b = mutable_buffer(p, 5); + b += 6; + BOOST_TEST_EQ(b.size(), 0); + } + } + + // operator+(mutable_buffer, size_t) + // operator+(size_t, mutable_buffer) + { + char p[6] = "12345"; + mutable_buffer b0(p, 5); + mutable_buffer b1(p, 5); + b0 = b0 + 2; + b1 = 2 + b1; + BOOST_TEST_EQ(b0.data(), p + 2); + BOOST_TEST_EQ(b0.size(), 3); + BOOST_TEST_EQ(b0.data(), b1.data()); + BOOST_TEST_EQ(b0.size(), b1.size()); + } + } + + void + testBuffer() + { + std::string s = test_pattern(); + mutable_buffer cb(&s[0], s.size()); + test_buffer_sequence(cb); + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + mutable_buffer_test, + "boost.buffers.mutable_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/mutable_buffer_pair.cpp b/test/io/buffers/mutable_buffer_pair.cpp new file mode 100644 index 00000000..558c69fa --- /dev/null +++ b/test/io/buffers/mutable_buffer_pair.cpp @@ -0,0 +1,130 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct mutable_buffer_pair_test +{ + void + testMembers() + { + std::string pat = test_pattern(); + + // mutable_buffer_pair() + { + mutable_buffer_pair mb; + BOOST_TEST_EQ( + buffer_size(mb), 0); + } + + // mutable_buffer_pair( + // mutable_buffer_pair const&), + // mutable_buffer_pair( + // mutable_buffer const&) + // mutable_buffer const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + mutable_buffer_pair mb0( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + mutable_buffer_pair mb1(mb0); + BOOST_TEST_EQ( + test_to_string(mb0), pat); + BOOST_TEST_EQ( + test_to_string(mb0), + test_to_string(mb1)); + BOOST_TEST_EQ( + mb0[0].data(), mb1[0].data()); + BOOST_TEST_EQ( + mb0[1].size(), mb1[1].size()); + auto const& cmb0 = mb0; + auto const& cmb1 = mb1; + BOOST_TEST_EQ( + cmb0[0].data(), cmb1[0].data()); + BOOST_TEST_EQ( + cmb0[1].size(), cmb1[1].size()); + } + } + + // operator=(mutable_buffer_pair const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + mutable_buffer_pair mb0( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + mutable_buffer_pair mb1; + mb1 = mb0; + BOOST_TEST_EQ( + test_to_string(mb0), pat); + BOOST_TEST_EQ( + test_to_string(mb0), + test_to_string(mb1)); + } + } + + // operator=(mutable_buffer_pair const&) + { + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + auto s = pat; + mutable_buffer_pair b( + { &s[0], i }, + { &s[i], + s.size() - i }); + mutable_buffer_pair mb; + mb = b; + BOOST_TEST_EQ( + test_to_string(mb), pat); + BOOST_TEST_EQ( + test_to_string(mb), + test_to_string(b)); + } + } + } + + void + testBuffer() + { + std::string pat = test_pattern(); + for(std::size_t i = 0; + i <= pat.size(); ++i) + { + mutable_buffer_pair cb( + { &pat[0], i }, + { &pat[i], + pat.size() - i }); + test_buffer_sequence(cb); + } + } + + void + run() + { + testMembers(); + testBuffer(); + } +}; + +TEST_SUITE( + mutable_buffer_pair_test, + "boost.buffers.mutable_buffer_pair"); + +} // boost::buffers diff --git a/test/io/buffers/mutable_buffer_span.cpp b/test/io/buffers/mutable_buffer_span.cpp new file mode 100644 index 00000000..04b27c84 --- /dev/null +++ b/test/io/buffers/mutable_buffer_span.cpp @@ -0,0 +1,81 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct mutable_buffer_span_test +{ + void + testMembers() + { + std::string pat = test_pattern(); + mutable_buffer mb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + + // mutable_buffer_span() + { + mutable_buffer_span bs; + BOOST_TEST_EQ(buffer_size(bs), 0); + } + + // mutable_buffer_span( + // mutable_buffer const*, + // std::size_t n) + { + mutable_buffer_span mbs(mb, 3); + test_buffer_sequence(mbs); + } + + // mutable_buffer_span( + // MutableBufferSequence) + { + mutable_buffer_pair bp; + mutable_buffer_span sp(bp); + BOOST_TEST( + sp.end() - sp.begin() == 2); + } + + // mutable_buffer_span( + // mutable_buffer_span const&) + { + mutable_buffer_span mbs0(mb, 3); + mutable_buffer_span mbs1(mbs0); + test_buffer_sequence(mbs1); + } + + // operator=( + // mutable_buffer_span const&) + { + mutable_buffer_span mbs0(mb, 3); + mutable_buffer_span mbs1; + mbs1 = mbs0; + test_buffer_sequence(mbs1); + } + } + + void + run() + { + testMembers(); + } +}; + +TEST_SUITE( + mutable_buffer_span_test, + "boost.buffers.mutable_buffer_span"); + +} // boost::buffers diff --git a/test/io/buffers/mutable_buffer_subspan.cpp b/test/io/buffers/mutable_buffer_subspan.cpp new file mode 100644 index 00000000..47e824c4 --- /dev/null +++ b/test/io/buffers/mutable_buffer_subspan.cpp @@ -0,0 +1,190 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct mutable_buffer_subspan_test +{ + void + testMembers() + { + std::string pat = test_pattern(); + mutable_buffer const mb[3] = { + { &pat[0], 3 }, + { &pat[0] + 3, 5 }, + { &pat[0] + 8, 7 } }; + + // mutable_buffer_subspan() + { + mutable_buffer_subspan bs; + BOOST_TEST_EQ(buffer_size(bs), 0); + } + + // mutable_buffer_subspan( + // mutable_buffer const*, std::size_t) + { + mutable_buffer_subspan s(mb, 3); + BOOST_TEST_EQ(buffer_size(s), 15); + } + { + mutable_buffer_subspan s(mb, 0); + BOOST_TEST_EQ(buffer_size(s), 0); + } + + // mutable_buffer_subspan( + // mutable_buffer_span const&) + { + mutable_buffer_span ms0(mb, 3); + mutable_buffer_subspan ms1(ms0); + BOOST_TEST_EQ( + buffer_size(ms0), + buffer_size(ms1)); + } + { + mutable_buffer_span ms0(mb, 0); + mutable_buffer_subspan ms1(ms0); + BOOST_TEST_EQ( + buffer_size(ms0), + buffer_size(ms1)); + } + + // mutable_buffer_subspan( + // mutable_buffer_subspan const&) + { + mutable_buffer_subspan s0(mb, 3); + mutable_buffer_subspan s1(s0); + BOOST_TEST_EQ( + buffer_size(s1), + buffer_size(s0)); + } + + // operator=( + // mutable_buffer_subspan const&) + { + mutable_buffer_subspan s; + BOOST_TEST_EQ(buffer_size(s), 0); + s = mutable_buffer_subspan(mb, 3); + BOOST_TEST_EQ(buffer_size(s), 15); + } + } + + void + testSequence() + { + std::string pat = test_pattern(); + + // length 1 + { + mutable_buffer mb = { + &pat[0], pat.size() }; + mutable_buffer_subspan s(&mb, 1); + test_buffer_sequence(s); + } + + // length 2 + { + mutable_buffer const mb[2] = { + { &pat[0], 7 }, + { &pat[7], 8 } }; + mutable_buffer_subspan s(mb, 2); + test_buffer_sequence(s); + } + + // length 3 + { + mutable_buffer const mb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + mutable_buffer_subspan s(mb, 3); + test_buffer_sequence(s); + } + } + + void + testSubspan() + { + std::string tmp; + std::string pat = test_pattern(); + mutable_buffer const mb[3] = { + { &pat[0], 3 }, + { &pat[3], 5 }, + { &pat[8], 7 } }; + mutable_buffer_span const cs0(mb, 3); + + for(std::size_t i = 0; i <= pat.size(); ++i ) + { + { + auto b0 = prefix(cs0, i); + auto b1 = sans_prefix(cs0, i); + tmp = std::string(pat.size(), ' '); + mutable_buffer dest(&tmp[0], tmp.size()); + auto n = buffer_copy(dest, b0); + dest += n; + n += buffer_copy(dest, b1); + BOOST_TEST_EQ(n, pat.size()); + BOOST_TEST_EQ(tmp, pat); + } + for(std::size_t j = 0; j <= pat.size(); ++j) + { + auto b = prefix(sans_prefix(cs0, i), j); + tmp.resize(pat.size()); + tmp.resize(buffer_copy( + mutable_buffer( + &tmp[0], tmp.size()), b)); + if(i <= pat.size()) + BOOST_TEST_EQ(tmp, pat.substr(i, j)); + else + BOOST_TEST(tmp.empty()); + } + for(std::size_t j = 0; j <= pat.size(); ++j) + { + auto b = suffix(sans_suffix(cs0, i), j); + tmp.resize(pat.size()); + tmp.resize(buffer_copy( + mutable_buffer( + &tmp[0], tmp.size()), b)); + if(i <= pat.size()) + { + auto n = pat.size() - i; // inner length + if(n >= j) + BOOST_TEST_EQ(tmp, + pat.substr(n - j, j)); + else + BOOST_TEST_EQ(tmp, + pat.substr(0, n)); + } + else + { + BOOST_TEST(tmp.empty()); + } + } + } + } + + void + run() + { + testMembers(); + testSequence(); + testSubspan(); + } +}; + +TEST_SUITE( + mutable_buffer_subspan_test, + "boost.buffers.mutable_buffer_subspan"); + +} // boost::buffers diff --git a/test/io/buffers/range.cpp b/test/io/buffers/range.cpp new file mode 100644 index 00000000..4592d137 --- /dev/null +++ b/test/io/buffers/range.cpp @@ -0,0 +1,29 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include "test_suite.hpp" + +namespace boost::async::io::buffers { + +struct range_test +{ + void + run() + { + } +}; + +TEST_SUITE( + range_test, + "boost.buffers.range"); + +} // boost::buffers diff --git a/test/io/buffers/string_buffer.cpp b/test/io/buffers/string_buffer.cpp new file mode 100644 index 00000000..4243ce53 --- /dev/null +++ b/test/io/buffers/string_buffer.cpp @@ -0,0 +1,173 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include + +#include +#include "test_helpers.hpp" + +namespace boost::async::io::buffers { + +struct string_buffer_test +{ + BOOST_STATIC_ASSERT(dynamic_buffer); + + void + testMembers() + { + std::string s; + std::wstring ws = L"1234"; + + // ~string_buffer + { + s = ""; + string_buffer b(&s); + BOOST_TEST(s.empty()); + } + + // string_buffer + { + std::string s0; + { + string_buffer b0(&s0); + string_buffer b1(std::move(b0)); + auto n = buffer_copy( + b1.prepare(5), + buffer("12345", 5)); + BOOST_TEST_EQ(n, 5); + b1.commit(5); + } + BOOST_TEST_EQ(s0, "12345"); + } + + // string_buffer(std::string) + { + s = ""; + string_buffer b(&s); + BOOST_TEST_EQ( + b.max_size(), s.max_size()); + } + + // string_buffer(std::string, std::size_t) + // max_size() + { + s = ""; + string_buffer b(&s, 20); + BOOST_TEST_EQ(b.max_size(), 20); + } + + // size() + { + s = "1234"; + string_buffer b(&s); + BOOST_TEST_EQ(b.size(), 4); + } + + // capacity() + { + { + s = ""; + s.reserve(30); + string_buffer b(&s); + BOOST_TEST_GE(b.capacity(), 30); + } + { + s = ""; + s.reserve(30); + string_buffer b(&s, 10); + BOOST_TEST_GE(b.capacity(), 10); + } + } + + // data() + { + s = "1234"; + string_buffer b(&s); + BOOST_TEST_EQ( + test_to_string(b.data()), + "1234"); + } + + // prepare() + { + { + string_buffer b(&s, 3); + BOOST_TEST_THROWS( + b.prepare(5), + std::invalid_argument); + } + { + s = std::string(); + string_buffer b(&s); + auto dest = b.prepare(10); + BOOST_TEST_GE(s.capacity(), + buffer_size(dest)); + } + { + s = std::string(); + string_buffer b(&s); + b.prepare(10); + auto dest = b.prepare(10); + BOOST_TEST_EQ( + buffer_size(dest), + 10); + } + } + + // commit() + { + s = ""; + { + string_buffer b(&s); + auto n = buffer_copy( + b.prepare(5), + buffer("12345", 5)); + BOOST_TEST_EQ(n, 5); + b.commit(3); + BOOST_TEST_EQ(b.size(), 3); + } + BOOST_TEST_EQ(s, "123"); + } + + // consume() + { + { + s = "12345"; + { + string_buffer b(&s); + b.consume(2); + } + BOOST_TEST_EQ(s, "345"); + } + { + s = "12345"; + { + string_buffer b(&s); + b.consume(5); + BOOST_TEST_EQ( + buffer_size(b.data()), 0); + } + BOOST_TEST(s.empty()); + } + } + } + + void + run() + { + testMembers(); + } +}; + +TEST_SUITE( + string_buffer_test, + "boost.buffers.string_buffer"); + +} // boost::buffers diff --git a/test/io/buffers/tag_invoke.cpp b/test/io/buffers/tag_invoke.cpp new file mode 100644 index 00000000..66ce9766 --- /dev/null +++ b/test/io/buffers/tag_invoke.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include diff --git a/test/io/buffers/test_helpers.hpp b/test/io/buffers/test_helpers.hpp new file mode 100644 index 00000000..645b6c6c --- /dev/null +++ b/test/io/buffers/test_helpers.hpp @@ -0,0 +1,214 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +#ifndef BOOST_BUFFERS_TEST_HELPERS_HPP +#define BOOST_BUFFERS_TEST_HELPERS_HPP + +#include +#include +#include +#include +#include +#include +#include "test_suite.hpp" + +namespace boost::async::io::buffers { + +inline +std::string const& +test_pattern() +{ + static std::string const pat = + "012" "34567" "89abcde"; + return pat; +} + +template +std::string +test_to_string(Buffers const& bs) +{ + std::string s( + buffer_size(bs), 0); + s.resize(buffer_copy( + buffer(&s[0], s.size()), + bs)); + return s; +} + +template +void +test_buffer_sequence(T&& t) +{ + static_assert(const_buffer_sequence>); + + auto const& pat = test_pattern(); + auto const& ct = t; + + // operator++() + { + std::string s; + auto it = begin(t); + auto const end_ = end(t); + while(it != end_) + { + auto b = *it; + s.append(static_cast< + char const*>(b.data()), + b.size()); + ++it; + } + BOOST_TEST_EQ(s, pat); + } + // operator++(int) + { + std::string s; + auto it = begin(t); + auto const end_ = end(t); + while(it != end_) + { + auto b = *it; + s.append(static_cast< + char const*>(b.data()), + b.size()); + it++; + } + BOOST_TEST_EQ(s, pat); + } + // operator++() const + { + std::string s; + auto it = begin(ct); + auto const end_ = end(ct); + while(it != end_) + { + auto b = *it; + s.append(static_cast< + char const*>(b.data()), + b.size()); + ++it; + } + BOOST_TEST_EQ(s, pat); + } + // operator++(int) const + { + std::string s; + auto it = begin(ct); + auto const end_ = end(ct); + while(it != end_) + { + auto b = *it; + s.append(static_cast< + char const*>(b.data()), + b.size()); + it++; + } + BOOST_TEST_EQ(s, pat); + } + + // operator--() + { + std::string s; + auto it = end(t); + auto const begin_ = begin(t); + while(it != begin_) + { + --it; + auto b = *it; + s.insert(0, static_cast< + char const*>(b.data()), + b.size()); + } + BOOST_TEST_EQ(s, pat); + } + // operator--(int) + { + std::string s; + auto it = end(t); + auto const begin_ = begin(t); + while(it != begin_) + { + it--; + auto b = *it; + s.insert(0, static_cast< + char const*>(b.data()), + b.size()); + } + BOOST_TEST_EQ(s, pat); + } + // operator--() const + { + std::string s; + auto it = end(ct); + auto const begin_ = begin(ct); + while(it != begin_) + { + --it; + auto b = *it; + s.insert(0, static_cast< + char const*>(b.data()), + b.size()); + } + BOOST_TEST_EQ(s, pat); + } + // operator--(int) const + { + std::string s; + auto it = end(ct); + auto const begin_ = begin(ct); + while(it != begin_) + { + it--; + auto b = *it; + s.insert(0, static_cast< + char const*>(b.data()), + b.size()); + } + BOOST_TEST_EQ(s, pat); + } + + // prefix + for(std::size_t i = 0; + i <= pat.size(); ++i) + BOOST_TEST_EQ( + test_to_string(prefix(t, i)), + pat.substr(0, i)); + BOOST_TEST_EQ(test_to_string(prefix( + t, std::size_t(-1))), pat); + + // prefix + for(std::size_t i = 0; + i <= pat.size(); ++i) + BOOST_TEST_EQ( + test_to_string(prefix(ct, i)), + pat.substr(0, i)); + BOOST_TEST_EQ(test_to_string(prefix( + ct, std::size_t(-1))), pat); + + // suffix + for(std::size_t i = 0; + i <= pat.size(); ++i) + BOOST_TEST_EQ( + test_to_string(suffix(t, i)), + pat.substr(pat.size() - i, i)); + BOOST_TEST_EQ(test_to_string(suffix( + t, std::size_t(-1))), pat); + + // suffix + for(std::size_t i = 0; + i <= pat.size(); ++i) + BOOST_TEST_EQ( + test_to_string(suffix(ct, i)), + pat.substr(pat.size() - i, i)); + BOOST_TEST_EQ(test_to_string(suffix( + ct, std::size_t(-1))), pat); +} + +} // boost::buffers + +#endif diff --git a/test/io/buffers/test_main.cpp b/test/io/buffers/test_main.cpp new file mode 100644 index 00000000..c10ff426 --- /dev/null +++ b/test/io/buffers/test_main.cpp @@ -0,0 +1,544 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/url +// + +#include "test_suite.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#endif + +namespace test_suite { + +//------------------------------------------------ +// +// debug_stream +// +//------------------------------------------------ + +#ifdef _MSC_VER + +namespace detail { + +class debug_streambuf + : public std::stringbuf +{ + std::ostream& os_; + bool dbg_; + + void + write(char const* s) + { + if(dbg_) + ::OutputDebugStringA(s); + os_ << s; + } + +public: + explicit + debug_streambuf( + std::ostream& os) + : os_(os) + , dbg_(::IsDebuggerPresent() != 0) + { + } + + ~debug_streambuf() + { + sync(); + } + + int sync() override + { + write(this->str().c_str()); + this->str(""); + return 0; + } +}; + +} // detail + +//------------------------------------------------ + +/** std::ostream with Visual Studio IDE redirection. + + Instances of this stream wrap a specified + `ostream` (such as `cout` or `cerr`). If a + debugger is attached when the stream is + created, output will additionally copied + to the debugger's output window. +*/ +class debug_stream : public std::ostream +{ + detail::debug_streambuf buf_; + +public: + /** Construct a stream. + + @param os The output stream to wrap. + */ + explicit + debug_stream( + std::ostream& os) + : std::ostream(&buf_) + , buf_(os) + { + if(os.flags() & std::ios::unitbuf) + std::unitbuf(*this); + } + +}; + +#else + +using debug_stream = std::ostream&; + +#endif + +//------------------------------------------------ +// +// suite +// +//------------------------------------------------ + +any_suite:: +~any_suite() = default; + +//------------------------------------------------ + +suites& +suites:: +instance() noexcept +{ + class suites_impl : public suites + { + std::vector v_; + + public: + virtual ~suites_impl() = default; + + void + insert(any_suite const& t) override + { + v_.push_back(&t); + } + + iterator + begin() const noexcept override + { + if(! v_.empty()) + return &v_[0]; + return nullptr; + } + + iterator + end() const noexcept override + { + if(! v_.empty()) + return &v_[0] + v_.size(); + return nullptr; + } + + void + sort() override + { + std::sort( + v_.begin(), + v_.end(), + []( any_suite const* p0, + any_suite const* p1) + { + auto s0 = p0->name(); + auto n0 = std::strlen(s0); + auto s1 = p1->name(); + auto n1 = std::strlen(s1); + return std::lexicographical_compare( + s0, s0 + n0, s1, s1 + n1); + }); + } + }; + static suites_impl impl; + return impl; +} + +//------------------------------------------------ +// +// runner +// +//------------------------------------------------ + +any_runner*& +any_runner:: +instance_impl() noexcept +{ + static any_runner* p = nullptr; + return p; +} + +any_runner& +any_runner:: +instance() noexcept +{ + return *instance_impl(); +} + +any_runner:: +any_runner() noexcept + : prev_(instance_impl()) +{ + instance_impl() = this; +} + +any_runner:: +~any_runner() +{ + instance_impl() = prev_; +} + +//------------------------------------------------ +// +// implementation +// +//------------------------------------------------ + +namespace detail { + +bool +test_impl( + bool cond, + char const* expr, + char const* func, + char const* file, + int line) +{ + return any_runner::instance().test( + cond, expr, func, file, line); +} + +void +throw_failed_impl( + const char* expr, + char const* excep, + char const* func, + char const* file, + int line) +{ + std::stringstream ss; + ss << + "expression '" << expr << + "' didn't throw '" << excep << + "' in function '" << func << + "'"; + any_runner::instance().test(false, ss.str().c_str(), + func, file, line); +} + +void +no_throw_failed_impl( + const char* expr, + char const* excep, + char const* func, + char const* file, + int line) +{ + std::stringstream ss; + ss << + "expression '" << expr << + "' threw '" << excep << + "' in function '" << func << + "'"; + any_runner::instance().test(false, ss.str().c_str(), + func, file, line); +} + +void +no_throw_failed_impl( + const char* expr, + char const* func, + char const* file, + int line) +{ + std::stringstream ss; + ss << + "expression '" << expr << + "' threw in function '" << func << "'"; + any_runner::instance().test(false, ss.str().c_str(), + func, file, line); +} + +//------------------------------------------------ +// +// simple_runner +// +//------------------------------------------------ + +using clock_type = + std::chrono::steady_clock; + +struct elapsed +{ + clock_type::duration d; +}; + +std::ostream& +operator<<( + std::ostream& os, + elapsed const& e) +{ + using namespace std::chrono; + auto const ms = duration_cast< + milliseconds>(e.d); + if(ms < seconds{1}) + { + os << ms.count() << "ms"; + } + else + { + std::streamsize width{ + os.width()}; + std::streamsize precision{ + os.precision()}; + os << std::fixed << + std::setprecision(1) << + (ms.count() / 1000.0) << "s"; + os.precision(precision); + os.width(width); + } + return os; +} + +} // detail +} // test_suite + +//------------------------------------------------ + +namespace test_suite { +namespace detail { + +class simple_runner : public any_runner +{ + struct summary + { + char const* name; + clock_type::time_point start; + clock_type::duration elapsed; + std::atomic failed; + std::atomic total; + + summary(summary const& other) noexcept + : name(other.name) + , start(other.start) + , elapsed(other.elapsed) + , failed(other.failed.load()) + , total(other.total.load()) + { + } + + explicit + summary(char const* name_) noexcept + : name(name_) + , start(clock_type::now()) + , failed(0) + , total(0) + { + } + }; + + summary all_; + std::ostream& log_; + std::vector v_; + +public: + explicit + simple_runner( + std::ostream& os) + : all_("(all)") + , log_(os) + { + std::unitbuf(log_); + v_.reserve(256); + } + + virtual ~simple_runner() + { + log_ << + elapsed{clock_type::now() - + all_.start } << ", " << + v_.size() << " suites, " << + all_.failed.load() << " failures, " << + all_.total.load() << " total." << + std::endl; + } + + // true if no failures + bool + success() const noexcept + { + return all_.failed.load() == 0; + } + + void + run(any_suite const& test) override + { + v_.emplace_back(test.name()); + log_ << test.name() << "\n"; + test.run(); + v_.back().elapsed = + clock_type::now() - + v_.back().start; + } + + void + note(char const* msg) override + { + log_ << msg << "\n"; + } + + std::ostream& + log() noexcept override + { + return log_; + } + + char const* + filename( + char const* file) + { + auto const p0 = file; + auto p = p0 + std::strlen(file); + while(p-- != p0) + { + #ifdef _MSC_VER + if(*p == '\\') + #else + if(*p == '/') + #endif + break; + } + return p + 1; + } + + bool test(bool, char const*, char const*, char const*, int) override; +}; + +//------------------------------------------------ + +bool +simple_runner:: +test( + bool cond, + char const* expr, + char const* func, + char const* file, + int line) +{ + auto const id = ++all_.total; + ++v_.back().total; + if(cond) + return true; + ++all_.failed; + ++v_.back().failed; + (void)func; + log_ << + "#" << id << " " << + filename(file) << "(" << line << ") " + "failed: " << expr << + //" in " << func << + "\n"; + log_.flush(); + return false; +} + +//------------------------------------------------ + +int +run(std::ostream& out, + int argc, char const* const* argv) +{ + if(argc == 2) + { + std::string arg(argv[1]); + if(arg == "-h" || arg == "--help") + { + log << + "Usage:\n" + " " << argv[0] << ": { ... }" << + std::endl; + return EXIT_SUCCESS; + } + } + + simple_runner any_runner(out); + suites::instance().sort(); + if(argc == 1) + { + for(any_suite const* sp : + suites::instance()) + any_runner.run(*sp); + } + else + { + std::vector args; + args.reserve(argc - 1); + for(int i = 0; i < argc - 1; ++i) + args.push_back(argv[i + 1]); + for(auto const& e : suites::instance()) + { + std::string s(e->name()); + if(std::find_if( + args.begin(), args.end(), + [&](std::string const& arg) + { + if(arg.size() > s.size()) + return false; + return s.compare( + s.size() - arg.size(), + arg.size(), + arg.data(), + arg.size()) == 0; + }) != args.end()) + { + any_runner.run(*e); + } + } + } + return any_runner.success() ? + EXIT_SUCCESS : EXIT_FAILURE; +} + +} // detail +} // test_suite + +//------------------------------------------------ + +// Simple main used to produce stand +// alone executables that run unit tests. +int main(int argc, char const* const* argv) +{ +#if defined(_MSC_VER) && !defined(__clang__) + int flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flags |= _CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag(flags); +#endif + + ::test_suite::debug_stream log(std::cerr); + return ::test_suite::detail::run(log, argc, argv); +} diff --git a/test/io/buffers/test_suite.hpp b/test/io/buffers/test_suite.hpp new file mode 100644 index 00000000..9eb33428 --- /dev/null +++ b/test/io/buffers/test_suite.hpp @@ -0,0 +1,502 @@ +// +// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/boostorg/url +// + +#ifndef BOOST_URL_EXTRA_TEST_SUITE_HPP +#define BOOST_URL_EXTRA_TEST_SUITE_HPP + +#if defined(_MSC_VER) +# pragma once +#endif + +#include +#include +#include +#include + +// This is a derivative work +// Copyright 2002-2018 Peter Dimov +// Copyright (c) 2002, 2009, 2014 Peter Dimov +// Copyright (2) Beman Dawes 2010, 2011 +// Copyright (3) Ion Gaztanaga 2013 +// +// Copyright 2018 Glen Joseph Fernandes +// (glenjofe@gmail.com) + +namespace test_suite { + +//------------------------------------------------ + +struct any_suite +{ + virtual ~any_suite() = 0; + virtual char const* name() const noexcept = 0; + virtual void run() const = 0; +}; + +//------------------------------------------------ + +struct suites +{ + virtual ~suites() = default; + + using iterator = any_suite const* const*; + virtual void insert(any_suite const&) = 0; + virtual iterator begin() const noexcept = 0; + virtual iterator end() const noexcept = 0; + + // DEPRECATED + virtual void sort() = 0; + + static suites& instance() noexcept; +}; + +//------------------------------------------------ + +template +class suite : public any_suite +{ + char const* name_; + +public: + explicit + suite(char const* name) noexcept + : name_(name) + { + suites::instance().insert(*this); + } + + char const* + name() const noexcept override + { + return name_; + } + + void + run() const override + { + T().run(); + } +}; + +//------------------------------------------------ + +class any_runner +{ + any_runner* prev_; + + static any_runner*& + instance_impl() noexcept; + +public: + static any_runner& instance() noexcept; + + any_runner() noexcept; + virtual ~any_runner(); + + virtual void run(any_suite const& test) = 0; + virtual void note(char const* msg) = 0; + virtual bool test(bool cond, + char const* expr, char const* func, + char const* file, int line) = 0; + virtual std::ostream& log() noexcept = 0; +}; + +//------------------------------------------------ + +namespace detail { + +// In the comparisons below, it is possible that +// T and U are signed and unsigned integer types, +// which generates warnings in some compilers. +// A cleaner fix would require common_type trait +// or some meta-programming, which would introduce +// a dependency on Boost.TypeTraits. To avoid +// the dependency we just disable the warnings. +#if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wsign-compare") +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-compare" +# endif +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4389) +# pragma warning(disable: 4018) +#elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif + +template +struct make_void +{ + using type = void; +}; + +template +struct is_streamable : std::false_type +{}; + +template +struct is_streamable< + T, std::void_t() << std::declval()) + > > : std::true_type +{}; + +template +auto +test_output_impl(T const& v) -> + typename std::enable_if< + is_streamable::value, + T const&>::type +{ + return v; +} + +template +auto +test_output_impl(T const&) -> + typename std::enable_if< + ! is_streamable::value, + std::string>::type +{ + return "?"; +} + +// specialize test output for char pointers to avoid printing as cstring +template + const void* test_output_impl(T volatile* v) { return const_cast(v); } +inline const void* test_output_impl(const char* v) { return v; } +inline const void* test_output_impl(const unsigned char* v) { return v; } +inline const void* test_output_impl(const signed char* v) { return v; } +inline const void* test_output_impl(char* v) { return v; } +inline const void* test_output_impl(unsigned char* v) { return v; } +inline const void* test_output_impl(signed char* v) { return v; } +inline const void* test_output_impl(std::nullptr_t) { return nullptr; } + +// print chars as numeric +inline int test_output_impl( signed char const& v ) { return v; } +inline unsigned test_output_impl( unsigned char const& v ) { return v; } + +// Whether wchar_t is signed is implementation-defined +template struct lwt_long_type {}; +template<> struct lwt_long_type { typedef long type; }; +template<> struct lwt_long_type { typedef unsigned long type; }; +inline lwt_long_type< + (static_cast(-1) < static_cast(0)) + >::type test_output_impl( wchar_t const& v ) { return v; } + +#if !defined( BOOST_NO_CXX11_CHAR16_T ) +inline unsigned long test_output_impl( char16_t const& v ) { return v; } +#endif + +#if !defined( BOOST_NO_CXX11_CHAR32_T ) +inline unsigned long test_output_impl( char32_t const& v ) { return v; } +#endif + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4996) +#endif + +inline std::string test_output_impl( char const& v ) +{ + if( std::isprint( static_cast( v ) ) ) + { + return { 1, v }; + } + else + { + char buffer[ 8 ]; + std::sprintf( buffer, "\\x%02X", static_cast( v ) ); + + return buffer; + } +} + +bool +test_impl( + bool cond, + char const* expr, + char const* func, + char const* file, + int line); + +void +throw_failed_impl( + const char* expr, + char const* excep, + char const* func, + char const* file, + int line); + +void +no_throw_failed_impl( + const char* expr, + char const* excep, + char const* func, + char const* file, + int line); + +void +no_throw_failed_impl( + const char* expr, + char const* func, + char const* file, + int line); + +struct lw_test_eq +{ + template + bool operator()(const T& t, const U& u) const + { + return t == u; + } +}; + +struct lw_test_ne +{ + + template + bool operator()(const T& t, const U& u) const + { + return t != u; + } +}; + +struct lw_test_lt +{ + template + bool operator()(const T& t, const U& u) const + { + return t < u; + } +}; + +struct lw_test_gt +{ + template + bool operator()(const T& t, const U& u) const + { + return t > u; + } +}; + +struct lw_test_le +{ + template + bool operator()(const T& t, const U& u) const + { + return t <= u; + } +}; + +struct lw_test_ge +{ + template + bool operator()(const T& t, const U& u) const + { + return t >= u; + } +}; + +// lwt_predicate_name + +template char const * lwt_predicate_name( T const& ) +{ + return "~="; +} + +inline char const * lwt_predicate_name( lw_test_eq const& ) +{ + return "=="; +} + +inline char const * lwt_predicate_name( lw_test_ne const& ) +{ + return "!="; +} + +inline char const * lwt_predicate_name( lw_test_lt const& ) +{ + return "<"; +} + +inline char const * lwt_predicate_name( lw_test_le const& ) +{ + return "<="; +} + +inline char const * lwt_predicate_name( lw_test_gt const& ) +{ + return ">"; +} + +inline char const * lwt_predicate_name( lw_test_ge const& ) +{ + return ">="; +} + +//------------------------------------------------ + +template +bool +test_with_impl( + Pred pred, + char const* expr1, + char const* expr2, + char const* func, + char const* file, + int line, + T const& t, U const& u) +{ + if(pred(t, u)) + { + any_runner::instance().test( + true, "", func, file, line); + return true; + } + std::stringstream ss; + ss << + "\"" << test_output_impl(t) << "\" " << + lwt_predicate_name(pred) << + " \"" << test_output_impl(u) << "\" (" << + expr1 << " " << + lwt_predicate_name(pred) << + " " << expr2 << ")"; + any_runner::instance().test( + false, ss.str().c_str(), func, file, line); + return false; +} + +#if defined(__clang__) && defined(__has_warning) +# if __has_warning("-Wsign-compare") +# pragma clang diagnostic pop +# endif +#elif defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__GNUC__) && !(defined(__INTEL_COMPILER) || defined(__ICL) || defined(__ICC) || defined(__ECC)) && (__GNUC__ * 100 + __GNUC_MINOR__) >= 406 +# pragma GCC diagnostic pop +#endif + +//------------------------------------------------ + +struct log_type +{ + template + friend + std::ostream& + operator<<( + log_type const&, T&& t) + { + return any_runner::instance().log() << t; + } +}; + +//------------------------------------------------ + +} // detail + +/** Log output to the current suite +*/ +constexpr detail::log_type log{}; + +#define BOOST_TEST(expr) ( \ + ::test_suite::detail::test_impl( \ + (expr) ? true : false, #expr, \ + BOOST_CURRENT_FUNCTION, __FILE__, __LINE__ ) ) + +#define BOOST_ERROR(msg) \ + ::test_suite::detail::test_impl( \ + false, msg, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__ ) + +#define BOOST_TEST_WITH(expr1,expr2,predicate) ( \ + ::test_suite::detail::test_with_impl( \ + predicate, #expr1, #expr2, BOOST_CURRENT_FUNCTION, \ + __FILE__, __LINE__, expr1, expr2) ) + +#define BOOST_TEST_EQ(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_eq() ) + +#define BOOST_TEST_CSTR_EQ(expr1,expr2) \ + BOOST_TEST_EQ( string_view(expr1), string_view(expr2) ) + +#define BOOST_TEST_NE(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_ne() ) + +#define BOOST_TEST_LT(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_lt() ) + +#define BOOST_TEST_LE(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_le() ) + +#define BOOST_TEST_GT(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_gt() ) + +#define BOOST_TEST_GE(expr1,expr2) \ + BOOST_TEST_WITH( expr1, expr2, \ + ::test_suite::detail::lw_test_ge() ) + +#define BOOST_TEST_PASS() BOOST_TEST(true) + +#define BOOST_TEST_FAIL() BOOST_TEST(false) + +#define BOOST_TEST_NOT(expr) BOOST_TEST(!(expr)) + +#ifndef BOOST_NO_EXCEPTIONS +# define BOOST_TEST_THROWS( expr, ex ) \ + try { \ + (void)(expr); \ + ::test_suite::detail::throw_failed_impl( \ + #expr, #ex, BOOST_CURRENT_FUNCTION, \ + __FILE__, __LINE__); \ + } \ + catch(ex const&) { \ + BOOST_TEST_PASS(); \ + } \ + catch(...) { \ + ::test_suite::detail::throw_failed_impl( \ + #expr, #ex, BOOST_CURRENT_FUNCTION, \ + __FILE__, __LINE__); \ + } + // +#else + #define BOOST_TEST_THROWS( expr, ex ) +#endif + +#ifndef BOOST_NO_EXCEPTIONS +# define BOOST_TEST_NO_THROW( expr ) \ + try { \ + (void)(expr); \ + BOOST_TEST_PASS(); \ + } catch (const std::exception& e) { \ + ::test_suite::detail::no_throw_failed_impl( \ + #expr, e.what(), BOOST_CURRENT_FUNCTION, \ + __FILE__, __LINE__); \ + } catch (...) { \ + ::test_suite::detail::no_throw_failed_impl( \ + #expr, BOOST_CURRENT_FUNCTION, \ + __FILE__, __LINE__); \ + } + // +#else +# define BOOST_TEST_NO_THROW( expr ) ( [&]{ expr; return true; }() ) +#endif + +#define TEST_SUITE(type, name) \ + static ::test_suite::suite type##_(name) + +} // test_suite + +#endif diff --git a/test/io/buffers/type_traits.cpp b/test/io/buffers/type_traits.cpp new file mode 100644 index 00000000..68445987 --- /dev/null +++ b/test/io/buffers/type_traits.cpp @@ -0,0 +1,11 @@ +// +// Copyright (c) 2023 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/CPPAlliance/buffers +// + +// Test that header file is self-contained. +#include diff --git a/test/io/copy.cpp b/test/io/copy.cpp new file mode 100644 index 00000000..fa4b725c --- /dev/null +++ b/test/io/copy.cpp @@ -0,0 +1,68 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace boost::async; + +promise do_write__(io::stream & str, const std::string & input ) +{ + auto w = co_await io::write(str, input); + CHECK(w.transferred == input.size()); + str.close().value(); +}; + +promise do_copy(io::stream & in, io::stream & out) +{ + auto [r, w] = co_await io::copy(in, out); + CHECK(r.transferred == (1024*1024)); + CHECK(w.value() == (1024*1024)); + out.close().value(); + +}; + +CO_TEST_CASE("copy") +{ + std::signal(SIGPIPE, SIG_IGN); + auto [r, wi] = io::make_pipe().value(); + auto [ri, w] = io::make_pipe().value(); + + std::string input; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution distPrintable(32,126); + + input.resize(1024*1024); + std::generate(input.begin(), input.end(), [&]{return static_cast(distPrintable(rng));}); + + auto p = do_write__(w, input); + auto c = do_copy(ri, wi); + std::string output; + auto res = co_await io::read_all(r, output); + + CHECK(res.transferred == output.size()); + CHECK(res.transferred == input.size()); + + CHECK(std::equal(output.begin(), std::next(output.begin(), res.transferred), input.begin())); + + CHECK(!r.close().has_error()); + co_await p; + co_await c; +} + diff --git a/test/io/datagram_socket.cpp b/test/io/datagram_socket.cpp new file mode 100644 index 00000000..5916daf0 --- /dev/null +++ b/test/io/datagram_socket.cpp @@ -0,0 +1,31 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include + +#include + +CO_TEST_CASE("local_datagram") +{ + using namespace boost::async; + auto [r, w] = io::make_pair(io::local_datagram).value(); + + char data[7] = "nodata"; + + auto [written, read] = + co_await join(w.send(io::buffers::buffer("foobar", 6)), + r.receive(io::buffers::buffer(data))); + + CHECK(written.transferred == 6u); + CHECK(read.transferred == 6u); + CHECK(data == std::string("foobar")); +} diff --git a/test/io/endpoint.cpp b/test/io/endpoint.cpp new file mode 100644 index 00000000..f7fe8f28 --- /dev/null +++ b/test/io/endpoint.cpp @@ -0,0 +1,177 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include "../doctest.h" + + +TEST_SUITE_BEGIN("endpoint"); + +using namespace boost::async::io; + +/* + use-case analyzer + + vector eps = resolve("http://www.google.com"); + auto tcc = get(eps.front()); + assert(ipa.port() == 80); + + endpoint loc{"tcp://127.0.0.1"}; + endpoint unx{"unix:/home/klemens/.local_thingy"}; + unx.set_type(SOCK_DGRAM); + endpoint uns{local_stream, "/home/klemens/.local_thingy"}; + + endpoint ep{tcp_v4, "127.0.0.1", 5000}; + + auto ip = get(ep); +*/ + + +TEST_CASE("unix") +{ + endpoint ep{local_protocol, "~/thingy"}; + CHECK(ep.protocol() == local_protocol); + //CHECK(ep.protocol() == any_local); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get(ep).path() == "~/thingy"); + + ep = endpoint{local_stream, "/home/klemens/thingy"}; + CHECK(ep.protocol() == local_stream); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get(ep).path() == "/home/klemens/thingy"); +} + +TEST_CASE("ip_v4") +{ + endpoint ep{ip_v4, "129.168.0.1", 8080}; + CHECK(ep.protocol() == ip_v4); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8080); + CHECK(get(ep).addr_str() == "129.168.0.1"); + + ep = endpoint{tcp_v4, "192.168.1.4", 8181}; + CHECK(ep.protocol() == tcp_v4); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8181); + CHECK(get(ep).addr_str() == "192.168.1.4"); +} + + +TEST_CASE("ip_v6") +{ + endpoint ep{ip_v6, "2001:db8::", 8080}; + CHECK(ep.protocol() == ip_v6); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8080); + CHECK(get(ep).addr_str() == "2001:db8::"); + + ep = endpoint{tcp_v6, "2001:db8:1::ab9:c0a8:102", 8181}; + CHECK(ep.protocol() == tcp_v6); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8181); + CHECK(get(ep).addr_str() == "2001:db8:1::ab9:c0a8:102"); +} + + +TEST_CASE("ip") +{ + endpoint ep{ip, "129.168.0.1", 8080}; + CHECK(ep.protocol() == ip_v4); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8080); + CHECK(get(ep).addr_str() == "129.168.0.1"); + + ep = endpoint{tcp, "192.168.1.4", 8181}; + CHECK(ep.protocol() == tcp_v4); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8181); + CHECK(get(ep).addr_str() == "192.168.1.4"); + + ep = endpoint{ip, "2001:db8::", 8080}; + CHECK(ep.protocol() == ip_v6); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8080); + CHECK(get(ep).addr_str() == "2001:db8::"); + + ep = endpoint{tcp, "2001:db8:1::ab9:c0a8:102", 8181}; + CHECK(ep.protocol() == tcp_v6); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(!get_if(&ep)); + CHECK(get(ep).port() == 8181); + CHECK(get(ep).addr_str() == "2001:db8:1::ab9:c0a8:102"); +} + +TEST_SUITE_END(); \ No newline at end of file diff --git a/test/io/pipe.cpp b/test/io/pipe.cpp new file mode 100644 index 00000000..a0795f69 --- /dev/null +++ b/test/io/pipe.cpp @@ -0,0 +1,40 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include +#include + +CO_TEST_CASE("pipe") +{ + using namespace boost::async; + auto [r, w] = io::make_pipe().value(); + + { + io::stream & rs = r; + io::stream & ws = w; + char buf[4096]; + auto ec = co_await ws. read_some(io::buffers::buffer(buf)); + CHECK(co_await ws. read_some(io::buffers::buffer(buf)) == boost::asio::error::operation_not_supported); + CHECK(co_await rs.write_some(io::buffers::buffer(buf)) == boost::asio::error::operation_not_supported); + } + + char data[7] = "nodata"; + + auto [written, read] = + co_await join(w.write_some("foobar"), + r.read_some(data)); + + CHECK(written.transferred == 6u); + CHECK(read.transferred == 6u); + CHECK(data == std::string("foobar")); + +} + diff --git a/test/io/process.cpp b/test/io/process.cpp new file mode 100644 index 00000000..9acf10b9 --- /dev/null +++ b/test/io/process.cpp @@ -0,0 +1,39 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include + +#include + +using namespace boost::async; +namespace bpv = boost::process::v2; + +#if defined(BOOST_PROCESS_V2_WINDOWS) +bpv::filesystem::path shell() +{ + return bpv::environment::find_executable("cmd"); +} +#else +bpv::filesystem::path shell() +{ + return bpv::environment::find_executable("sh"); +} +#endif + +CO_TEST_CASE("process") +{ + +#if defined(BOOST_PROCESS_V2_WINDOWS) + CHECK(42 == co_await io::process(bpv::environment::find_executable("cmd"), {"/c", "exit 42"})); + +#else + CHECK(42 == co_await io::process(bpv::environment::find_executable("sh"), {"-c", "exit 42"})); +#endif +} + diff --git a/test/io/read.cpp b/test/io/read.cpp new file mode 100644 index 00000000..abb55fc0 --- /dev/null +++ b/test/io/read.cpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include +#include +#include +#include + +#include + +using namespace boost::async; + +promise do_write(io::stream & str, std::string & input ) +{ + boost::ignore_unused(co_await io::write(str, {input.data(), input.size()})); +}; + +CO_TEST_CASE("read") +{ + auto [r, w] = io::make_pipe().value(); + + std::string input; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution distPrintable(32,126); + + input.resize(1024*1024); + std::generate(input.begin(), input.end(), [&]{return static_cast(distPrintable(rng));}); + + auto p = do_write(w, input); + + std::string output; + output.resize(1024*16); + auto res = co_await io::read(r, io::buffers::buffer(output.data(), output.size())); + + CHECK(res.transferred == output.size()); + CHECK(!res.has_error()); + + CHECK(std::equal(output.begin(), output.end(), input.begin())); + + CHECK(!r.close().has_error()); + co_await p; +} + diff --git a/test/io/read_all.cpp b/test/io/read_all.cpp new file mode 100644 index 00000000..1a9b1d76 --- /dev/null +++ b/test/io/read_all.cpp @@ -0,0 +1,53 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include +#include +#include +#include + +#include + +using namespace boost::async; + +static promise do_write_2(io::stream & str, std::string & input ) +{ + boost::ignore_unused(co_await io::write(str, {input.data(), input.size()})); +}; + +CO_TEST_CASE("read") +{ + auto [r, w] = io::make_pipe().value(); + + std::string input; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution distPrintable(32,126); + + input.resize(1024*1024); + std::generate(input.begin(), input.end(), [&]{return static_cast(distPrintable(rng));}); + + auto p = do_write_2(w, input); + + std::string output; + output.resize(1024*16); + auto res = co_await io::read_all(r, io::buffers::string_buffer(&output)); + + CHECK(res.transferred == output.size()); + CHECK(!res.has_error()); + + CHECK(std::equal(output.begin(), output.end(), input.begin())); + + CHECK(!r.close().has_error()); + co_await p; +} + diff --git a/test/io/read_until.cpp b/test/io/read_until.cpp new file mode 100644 index 00000000..89f001b5 --- /dev/null +++ b/test/io/read_until.cpp @@ -0,0 +1,88 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include +#include +#include +#include + +#include + +using namespace boost::async; + +promise do_write_(io::stream & str, std::string & input ) +{ + boost::ignore_unused(co_await io::write(str, {input.data(), input.size()})); +}; + + +CO_TEST_CASE("read_until char") +{ + auto [r, w] = io::make_pipe().value(); + + std::string input; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution distPrintable(32,126); + + input.resize(1024*1024); + std::generate(input.begin(), input.end(), [&]{return static_cast(distPrintable(rng));}); + input[4242] = '\n'; + + auto p = do_write_(w, input); + + + std::string output; + auto res = co_await io::read_until(r, output, '\n'); + + CHECK(res.transferred >= 4242); + CHECK(res.transferred < 10000); + CHECK(!res.has_error()); + + CHECK(std::equal(output.begin(), output.end(), input.begin())); + + CHECK(!r.close().has_error()); + co_await p; +} + + +CO_TEST_CASE("read_until string") +{ + auto [r, w] = io::make_pipe().value(); + + std::string input; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution distPrintable(32,126); + + input.resize(1024*1024); + std::generate(input.begin(), input.end(), [&]{return static_cast(distPrintable(rng));}); + input[4242] = '\n'; + input[8000] = '\r'; + input[8001] = '\n'; + + auto p = do_write_(w, input); + + + std::string output; + auto res = co_await io::read_until(r, output, "\r\n"); + + CHECK(res.transferred >= 8001); + CHECK(res.transferred < 16000); + CHECK(!res.has_error()); + + CHECK(std::equal(output.begin(), output.end(), input.begin())); + + CHECK(!r.close().has_error()); + co_await p; +} \ No newline at end of file diff --git a/test/io/resolver.cpp b/test/io/resolver.cpp new file mode 100644 index 00000000..887766e3 --- /dev/null +++ b/test/io/resolver.cpp @@ -0,0 +1,27 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../doctest.h" +#include "../test.hpp" +#include +#include + + + +CO_TEST_CASE("resolver") +{ + using namespace boost; + + auto t = co_await async::io::lookup("boost.org", "http"); + + CHECK(t.error() == system::error_code{}); + REQUIRE(t.value().size() > 0u); + for (auto & ep : *t) + CHECK(ep.protocol() == async::io::ip_v4); + + +} \ No newline at end of file diff --git a/test/io/seq_packet_socket.cpp b/test/io/seq_packet_socket.cpp new file mode 100644 index 00000000..0c1bb720 --- /dev/null +++ b/test/io/seq_packet_socket.cpp @@ -0,0 +1,32 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include + +CO_TEST_CASE("local_seq_packet") +{ + using namespace boost::async; + auto [r, w] = io::make_pair(io::local_seqpacket).value(); + + char data[7] = "nodata"; + + int flags = -1; + auto [written, read] = + co_await join(w.send(io::buffers::buffer("foobar", 6), 0), + r.receive(io::buffers::buffer(data), flags)); + + CHECK(flags == 0); + CHECK(written.transferred == 6u); + CHECK(read.transferred == 6u); + CHECK(data == std::string("foobar")); +} diff --git a/test/io/sleep.cpp b/test/io/sleep.cpp new file mode 100644 index 00000000..c2a9c6a0 --- /dev/null +++ b/test/io/sleep.cpp @@ -0,0 +1,37 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include "../test.hpp" + +#include + +TEST_SUITE_BEGIN("sleep"); + +CO_TEST_CASE_TEMPLATE("sleep-timepoint", Clock, std::chrono::steady_clock, std::chrono::system_clock) +{ + auto pre = Clock::now(); + co_await boost::async::io::sleep(pre).value(); + auto post = Clock::now(); + CHECK((post - pre) < std::chrono::milliseconds(50)); + co_await boost::async::io::sleep(pre + std::chrono::milliseconds(50)).value(); + post = Clock::now(); + CHECK((post - pre) >= std::chrono::milliseconds(50)); +} + +CO_TEST_CASE("sleep-duration") +{ + auto pre = std::chrono::steady_clock::now(); + co_await boost::async::io::sleep(std::chrono::milliseconds(0)).value(); + auto post = std::chrono::steady_clock::now(); + CHECK((post - pre) < std::chrono::milliseconds(50)); + co_await boost::async::io::sleep(std::chrono::milliseconds(50)).value(); + post = std::chrono::steady_clock::now(); + CHECK((post - pre) >= std::chrono::milliseconds(50)); +} + + +TEST_SUITE_END(); \ No newline at end of file diff --git a/test/io/ssl.cpp b/test/io/ssl.cpp new file mode 100644 index 00000000..fa5f37bc --- /dev/null +++ b/test/io/ssl.cpp @@ -0,0 +1,30 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include "../doctest.h" +#include "../test.hpp" + +#include +#include +#include + +CO_TEST_CASE("ssl") +{ + using namespace boost; + asio::ssl::context ctx{asio::ssl::context_base::tls_client}; + auto t = (co_await async::io::lookup("boost.org", "https")).value(); + REQUIRE(!t.empty()); + + async::io::ssl_stream ss{ctx}; + + auto conn = co_await ss.connect(t.front()); + CHECK_MESSAGE(conn, conn.error().message()); + + CHECK_NOTHROW(co_await ss.async_handshake(async::io::ssl_stream::handshake_type::client).value()); + CHECK_NOTHROW(co_await ss.async_shutdown()); +} \ No newline at end of file diff --git a/test/io/stream_socket.cpp b/test/io/stream_socket.cpp new file mode 100644 index 00000000..9c29c8d9 --- /dev/null +++ b/test/io/stream_socket.cpp @@ -0,0 +1,30 @@ +// +// Copyright (c) 2023 Klemens Morgenstern (klemens.morgenstern@gmx.net) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include + +#include "../doctest.h" +#include "../test.hpp" +#include +#include +#include + +CO_TEST_CASE("local_stream") +{ + using namespace boost::async; + auto [r, w] = io::make_pair(io::local_stream).value(); + + char data[7] = "nodata"; + + auto [written, read] = + co_await join(w.write_some(io::buffers::buffer("foobar", 6)), + r.read_some(io::buffers::buffer(data))); + + CHECK(written.transferred == 6u); + CHECK(read.transferred == 6u); + CHECK(data == std::string("foobar")); +} diff --git a/test/test.hpp b/test/test.hpp index 2f2bda5d..30d8457a 100644 --- a/test/test.hpp +++ b/test/test.hpp @@ -40,7 +40,8 @@ inline void test_run(boost::async::task (*func) ()) spawn(ctx, func(), +[](std::exception_ptr e) { - CHECK(e == nullptr); + if (e) + CHECK_NOTHROW(std::rethrow_exception(e)); }); std::size_t n; n = ctx.run(); @@ -71,6 +72,20 @@ DOCTEST_TEST_CASE(__VA_ARGS__) static ::boost::async::task Function() #define CO_TEST_CASE(...) CO_TEST_CASE_IMPL(DOCTEST_ANONYMOUS(CO_DOCTEST_ANON_FUNC_), __VA_ARGS__) +// end::test_case_macro[] + + // tag::test_case_macro[] +#define CO_TEST_CASE_TEMPLATE_IMPL(Function, Name, T, ...) \ +template \ +static ::boost::async::task Function(); \ +DOCTEST_TEST_CASE_TEMPLATE(Name, T, __VA_ARGS__) \ +{ \ + test_run(&Function); \ +} \ +template \ +static ::boost::async::task Function() + +#define CO_TEST_CASE_TEMPLATE(...) CO_TEST_CASE_TEMPLATE_IMPL(DOCTEST_ANONYMOUS(CO_DOCTEST_ANON_FUNC_), __VA_ARGS__) // end::test_case_macro[] struct stop