text: //
  // ssl/detail/io.hpp
  // ~~~~~~~~~~~~~~~~~
  //
  // Copyright (c) 2003-2017 Christopher M. Kohlhoff (chris at kohlhoff dot 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)
  //
  
  #ifndef BOOST_ASIO_SSL_DETAIL_IO_HPP
  #define BOOST_ASIO_SSL_DETAIL_IO_HPP
  
  #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  # pragma once
  #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  
  #include <boost/asio/detail/config.hpp>
  
  #if !defined(BOOST_ASIO_ENABLE_OLD_SSL)
  # include <boost/asio/ssl/detail/engine.hpp>
  # include <boost/asio/ssl/detail/stream_core.hpp>
  # include <boost/asio/write.hpp>
  #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
  
  #include <boost/asio/detail/push_options.hpp>
  
  namespace boost {
  namespace asio {
  namespace ssl {
  namespace detail {
  
  #if !defined(BOOST_ASIO_ENABLE_OLD_SSL)
  
  template <typename Stream, typename Operation>
  std::size_t io(Stream& next_layer, stream_core& core,
      const Operation& op, boost::system::error_code& ec)
  {
    std::size_t bytes_transferred = 0;
    do switch (op(core.engine_, ec, bytes_transferred))
    {
    case engine::want_input_and_retry:
  
      // If the input buffer is empty then we need to read some more data from
      // the underlying transport.
      if (boost::asio::buffer_size(core.input_) == 0)
        core.input_ = boost::asio::buffer(core.input_buffer_,
            next_layer.read_some(core.input_buffer_, ec));
  
      // Pass the new input data to the engine.
      core.input_ = core.engine_.put_input(core.input_);
  
      // Try the operation again.
      continue;
  
    case engine::want_output_and_retry:
  
      // Get output data from the engine and write it to the underlying
      // transport.
      boost::asio::write(next_layer,
          core.engine_.get_output(core.output_buffer_), ec);
  
      // Try the operation again.
      continue;
  
    case engine::want_output:
  
      // Get output data from the engine and write it to the underlying
      // transport.
      boost::asio::write(next_layer,
          core.engine_.get_output(core.output_buffer_), ec);
  
      // Operation is complete. Return result to caller.
      core.engine_.map_error_code(ec);
      return bytes_transferred;
  
    default:
  
      // Operation is complete. Return result to caller.
      core.engine_.map_error_code(ec);
      return bytes_transferred;
  
    } while (!ec);
  
    // Operation failed. Return result to caller.
    core.engine_.map_error_code(ec);
    return 0;
  }
  
  template <typename Stream, typename Operation, typename Handler>
  class io_op
  {
  public:
    io_op(Stream& next_layer, stream_core& core,
        const Operation& op, Handler& handler)
      : next_layer_(next_layer),
        core_(core),
        op_(op),
        start_(0),
        want_(engine::want_nothing),
        bytes_transferred_(0),
        handler_(BOOST_ASIO_MOVE_CAST(Handler)(handler))
    {
    }
  
  #if defined(BOOST_ASIO_HAS_MOVE)
    io_op(const io_op& other)
      : next_layer_(other.next_layer_),
        core_(other.core_),
        op_(other.op_),
        start_(other.start_),
        want_(other.want_),
        ec_(other.ec_),
        bytes_transferred_(other.bytes_transferred_),
        handler_(other.handler_)
    {
    }
  
    io_op(io_op&& other)
      : next_layer_(other.next_layer_),
        core_(other.core_),
        op_(other.op_),
        start_(other.start_),
        want_(other.want_),
        ec_(other.ec_),
        bytes_transferred_(other.bytes_transferred_),
        handler_(BOOST_ASIO_MOVE_CAST(Handler)(other.handler_))
    {
    }
  #endif // defined(BOOST_ASIO_HAS_MOVE)
  
    void operator()(boost::system::error_code ec,
        std::size_t bytes_transferred = ~std::size_t(0), int start = 0)
    {
      switch (start_ = start)
      {
      case 1: // Called after at least one async operation.
        do
        {
          switch (want_ = op_(core_.engine_, ec_, bytes_transferred_))
          {
          case engine::want_input_and_retry:
  
            // If the input buffer already has data in it we can pass it to the
            // engine and then retry the operation immediately.
            if (boost::asio::buffer_size(core_.input_) != 0)
            {
              core_.input_ = core_.engine_.put_input(core_.input_);
              continue;
            }
  
            // The engine wants more data to be read from input. However, we
            // cannot allow more than one read operation at a time on the
            // underlying transport. The pending_read_ timer's expiry is set to
            // pos_infin if a read is in progress, and neg_infin otherwise.
            if (core_.pending_read_.expires_at() == core_.neg_infin())
            {
              // Prevent other read operations from being started.
              core_.pending_read_.expires_at(core_.pos_infin());
  
              // Start reading some data from the underlying transport.
              next_layer_.async_read_some(
                  boost::asio::buffer(core_.input_buffer_),
                  BOOST_ASIO_MOVE_CAST(io_op)(*this));
            }
            else
            {
              // Wait until the current read operation completes.
              core_.pending_read_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
            }
  
            // Yield control until asynchronous operation completes. Control
            // resumes at the "default:" label below.
            return;
  
          case engine::want_output_and_retry:
          case engine::want_output:
  
            // The engine wants some data to be written to the output. However, we
            // cannot allow more than one write operation at a time on the
            // underlying transport. The pending_write_ timer's expiry is set to
            // pos_infin if a write is in progress, and neg_infin otherwise.
            if (core_.pending_write_.expires_at() == core_.neg_infin())
            {
              // Prevent other write operations from being started.
              core_.pending_write_.expires_at(core_.pos_infin());
  
              // Start writing all the data to the underlying transport.
              boost::asio::async_write(next_layer_,
                  core_.engine_.get_output(core_.output_buffer_),
                  BOOST_ASIO_MOVE_CAST(io_op)(*this));
            }
            else
            {
              // Wait until the current write operation completes.
              core_.pending_write_.async_wait(BOOST_ASIO_MOVE_CAST(io_op)(*this));
            }
  
            // Yield control until asynchronous operation completes. Control
            // resumes at the "default:" label below.
            return;
  
          default:
  
            // The SSL operation is done and we can invoke the handler, but we
            // have to keep in mind that this function might be being called from
            // the async operation's initiating function. In this case we're not
            // allowed to call the handler directly. Instead, issue a zero-sized
            // read so the handler runs "as-if" posted using io_service::post().
            if (start)
            {
              next_layer_.async_read_some(
                  boost::asio::buffer(core_.input_buffer_, 0),
                  BOOST_ASIO_MOVE_CAST(io_op)(*this));
  
              // Yield control until asynchronous operation completes. Control
              // resumes at the "default:" label below.
              return;
            }
            else
            {
              // Continue on to run handler directly.
              break;
            }
          }
  
          default:
          if (bytes_transferred == ~std::size_t(0))
            bytes_transferred = 0; // Timer cancellation, no data transferred.
          else if (!ec_)
            ec_ = ec;
  
          switch (want_)
          {
          case engine::want_input_and_retry:
  
            // Add received data to the engine's input.
            core_.input_ = boost::asio::buffer(
                core_.input_buffer_, bytes_transferred);
            core_.input_ = core_.engine_.put_input(core_.input_);
  
            // Release any waiting read operations.
            core_.pending_read_.expires_at(core_.neg_infin());
  
            // Try the operation again.
            continue;
  
          case engine::want_output_and_retry:
  
            // Release any waiting write operations.
            core_.pending_write_.expires_at(core_.neg_infin());
  
            // Try the operation again.
            continue;
  
          case engine::want_output:
  
            // Release any waiting write operations.
            core_.pending_write_.expires_at(core_.neg_infin());
  
            // Fall through to call handler.
  
          default:
  
            // Pass the result to the handler.
            op_.call_handler(handler_,
                core_.engine_.map_error_code(ec_),
                ec_ ? 0 : bytes_transferred_);
  
            // Our work here is done.
            return;
          }
        } while (!ec_);
  
        // Operation failed. Pass the result to the handler.
        op_.call_handler(handler_, core_.engine_.map_error_code(ec_), 0);
      }
    }
  
  //private:
    Stream& next_layer_;
    stream_core& core_;
    Operation op_;
    int start_;
    engine::want want_;
    boost::system::error_code ec_;
    std::size_t bytes_transferred_;
    Handler handler_;
  };
  
  template <typename Stream, typename Operation,  typename Handler>
  inline void* asio_handler_allocate(std::size_t size,
      io_op<Stream, Operation, Handler>* this_handler)
  {
    return boost_asio_handler_alloc_helpers::allocate(
        size, this_handler->handler_);
  }
  
  template <typename Stream, typename Operation, typename Handler>
  inline void asio_handler_deallocate(void* pointer, std::size_t size,
      io_op<Stream, Operation, Handler>* this_handler)
  {
    boost_asio_handler_alloc_helpers::deallocate(
        pointer, size, this_handler->handler_);
  }
  
  template <typename Stream, typename Operation, typename Handler>
  inline bool asio_handler_is_continuation(
      io_op<Stream, Operation, Handler>* this_handler)
  {
    return this_handler->start_ == 0 ? true
      : boost_asio_handler_cont_helpers::is_continuation(this_handler->handler_);
  }
  
  template <typename Function, typename Stream,
      typename Operation, typename Handler>
  inline void asio_handler_invoke(Function& function,
      io_op<Stream, Operation, Handler>* this_handler)
  {
    boost_asio_handler_invoke_helpers::invoke(
        function, this_handler->handler_);
  }
  
  template <typename Function, typename Stream,
      typename Operation, typename Handler>
  inline void asio_handler_invoke(const Function& function,
      io_op<Stream, Operation, Handler>* this_handler)
  {
    boost_asio_handler_invoke_helpers::invoke(
        function, this_handler->handler_);
  }
  
  template <typename Stream, typename Operation, typename Handler>
  inline void async_io(Stream& next_layer, stream_core& core,
      const Operation& op, Handler& handler)
  {
    io_op<Stream, Operation, Handler>(
      next_layer, core, op, handler)(
        boost::system::error_code(), 0, 1);
  }
  
  #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL)
  
  } // namespace detail
  } // namespace ssl
  } // namespace asio
  } // namespace boost
  
  #include <boost/asio/detail/pop_options.hpp>
  
  #endif // BOOST_ASIO_SSL_DETAIL_IO_HPP
  