text:
//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail 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)
//
// Official repository: https://github.com/boostorg/beast
//

#ifndef BOOST_BEAST_WEBSOCKET_IMPL_WRITE_HPP
#define BOOST_BEAST_WEBSOCKET_IMPL_WRITE_HPP

#include <boost/beast/websocket/detail/mask.hpp>
#include <boost/beast/core/async_base.hpp>
#include <boost/beast/core/bind_handler.hpp>
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffers_cat.hpp>
#include <boost/beast/core/buffers_prefix.hpp>
#include <boost/beast/core/buffers_range.hpp>
#include <boost/beast/core/buffers_suffix.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/core/stream_traits.hpp>
#include <boost/beast/core/detail/bind_continuation.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/core/detail/config.hpp>
#include <boost/beast/websocket/detail/frame.hpp>
#include <boost/beast/websocket/impl/stream_impl.hpp>
#include <boost/asio/coroutine.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/throw_exception.hpp>
#include <algorithm>
#include <memory>

namespace boost {
namespace beast {
namespace websocket {

template<class NextLayer, bool deflateSupported>
template<class Handler, class Buffers>
class stream<NextLayer, deflateSupported>::write_some_op
    : public beast::async_base<
        Handler, beast::executor_type<stream>>
    , public asio::coroutine
{
    enum
    {
        do_nomask_nofrag,
        do_nomask_frag,
        do_mask_nofrag,
        do_mask_frag,
        do_deflate
    };

    boost::weak_ptr<impl_type> wp_;
    buffers_suffix<Buffers> cb_;
    detail::frame_header fh_;
    detail::prepared_key key_;
    std::size_t bytes_transferred_ = 0;
    std::size_t remain_;
    std::size_t in_;
    int how_;
    bool fin_;
    bool more_ = false; // for ubsan
    bool cont_ = false;

public:
    static constexpr int id = 2; // for soft_mutex

    template<class Handler_>
    write_some_op(
        Handler_&& h,
        boost::shared_ptr<impl_type> const& sp,
        bool fin,
        Buffers const& bs)
        : beast::async_base<Handler,
            beast::executor_type<stream>>(
                std::forward<Handler_>(h),
                    sp->stream().get_executor())
        , wp_(sp)
        , cb_(bs)
        , fin_(fin)
    {
        auto& impl = *sp;

        // Set up the outgoing frame header
        if(! impl.wr_cont)
        {
            impl.begin_msg();
            fh_.rsv1 = impl.wr_compress;
        }
        else
        {
            fh_.rsv1 = false;
        }
        fh_.rsv2 = false;
        fh_.rsv3 = false;
        fh_.op = impl.wr_cont ?
            detail::opcode::cont : impl.wr_opcode;
        fh_.mask =
            impl.role == role_type::client;

        // Choose a write algorithm
        if(impl.wr_compress)
        {
            how_ = do_deflate;
        }
        else if(! fh_.mask)
        {
            if(! impl.wr_frag)
            {
                how_ = do_nomask_nofrag;
            }
            else
            {
                BOOST_ASSERT(impl.wr_buf_size != 0);
                remain_ = buffer_bytes(cb_);
                if(remain_ > impl.wr_buf_size)
                    how_ = do_nomask_frag;
                else
                    how_ = do_nomask_nofrag;
            }
        }
        else
        {
            if(! impl.wr_frag)
            {
                how_ = do_mask_nofrag;
            }
            else
            {
                BOOST_ASSERT(impl.wr_buf_size != 0);
                remain_ = buffer_bytes(cb_);
                if(remain_ > impl.wr_buf_size)
                    how_ = do_mask_frag;
                else
                    how_ = do_mask_nofrag;
            }
        }
        (*this)({}, 0, false);
    }

    void operator()(
        error_code ec = {},
        std::size_t bytes_transferred = 0,
        bool cont = true);
};

template<class NextLayer, bool deflateSupported>
template<class Buffers, class Handler>
void
stream<NextLayer, deflateSupported>::
write_some_op<Buffers, Handler>::
operator()(
    error_code ec,
    std::size_t bytes_transferred,
    bool cont)
{
    using beast::detail::clamp;
    std::size_t n;
    net::mutable_buffer b;
    auto sp = wp_.lock();
    if(! sp)
    {
        ec = net::error::operation_aborted;
        bytes_transferred_ = 0;
        return this->complete(cont, ec, bytes_transferred_);
    }
    auto& impl = *sp;
    BOOST_ASIO_CORO_REENTER(*this)
    {
        // Acquire the write lock
        if(! impl.wr_block.try_lock(this))
        {
        do_suspend:
            BOOST_ASIO_CORO_YIELD
            {
                BOOST_ASIO_HANDLER_LOCATION((
                    __FILE__, __LINE__,
                    fin_ ?
                        "websocket::async_write" :
                        "websocket::async_write_some"
                    ));

                impl.op_wr.emplace(std::move(*this));
            }
            impl.wr_block.lock(this);
            BOOST_ASIO_CORO_YIELD
            {
                BOOST_ASIO_HANDLER_LOCATION((
                    __FILE__, __LINE__,
                    fin_ ?
                        "websocket::async_write" :
                        "websocket::async_write_some"
                    ));

                net::post(std::move(*this));
            }
            BOOST_ASSERT(impl.wr_block.is_locked(this));
        }
        if(impl.check_stop_now(ec))
            goto upcall;

        //------------------------------------------------------------------

        if(how_ == do_nomask_nofrag)
        {
            // send a single frame
            fh_.fin = fin_;
            fh_.len = buffer_bytes(cb_);
            impl.wr_fb.clear();
            detail::write<flat_static_buffer_base>(
                impl.wr_fb, fh_);
            impl.wr_cont = ! fin_;
            BOOST_ASIO_CORO_YIELD
            {
                BOOST_ASIO_HANDLER_LOCATION((
                    __FILE__, __LINE__,
                    fin_ ?
                        "websocket::async_write" :
                        "websocket::async_write_some"
                    ));

                net::async_write(impl.stream(),
                    buffers_cat(impl.wr_fb.data(), cb_),
                        beast::detail::bind_continuation(std::move(*this)));
            }
            bytes_transferred_ += clamp(fh_.len);
            if(impl.check_stop_now(ec))
                goto upcall;
            goto upcall;
        }

        //------------------------------------------------------------------

        if(how_ == do_nomask_frag)
        {
            // send multiple frames
            for(;;)
            {
                n = clamp(remain_, impl.wr_buf_size);
                fh_.len = n;
                remain_ -= n;
                fh_.fin = fin_ ? remain_ == 0 : false;
                impl.wr_fb.clear();
                detail::write<flat_static_buffer_base>(
                    impl.wr_fb, fh_);
                impl.wr_cont = ! fin_;
                // Send frame
                BOOST_ASIO_CORO_YIELD
                {
                    BOOST_ASIO_HANDLER_LOCATION((
                        __FILE__, __LINE__,
                        fin_ ?
                            "websocket::async_write" :
                            "websocket::async_write_some"
                        ));

                    net::async_write(impl.stream(), buffers_cat(
                        impl.wr_fb.data(),
                        buffers_prefix(clamp(fh_.len), cb_)),
                            beast::detail::bind_continuation(std::move(*this)));
                }
                n = clamp(fh_.len); // restore `n` on yield
                bytes_transferred_ += n;
                if(impl.check_stop_now(ec))
                    goto upcall;
                if(remain_ == 0)
                    break;
                cb_.consume(n);
                fh_.op = detail::opcode::cont;

                // Give up the write lock in between each frame
                // so that outgoing control frames might be sent.
                impl.wr_block.unlock(this);
                if( impl.op_close.maybe_invoke()
                    || impl.op_idle_ping.maybe_invoke()
                    || impl.op_rd.maybe_invoke()
                    || impl.op_ping.maybe_invoke())
                {
                    BOOST_ASSERT(impl.wr_block.is_locked());
                    goto do_suspend;
                }
                impl.wr_block.lock(this);
            }
            goto upcall;
        }

        //------------------------------------------------------------------

        if(how_ == do_mask_nofrag)
        {
            // send a single frame using multiple writes
            remain_ = beast::buffer_bytes(cb_);
            fh_.fin = fin_;
            fh_.len = remain_;
            fh_.key = impl.create_mask();
            detail::prepare_key(key_, fh_.key);
            impl.wr_fb.clear();
            detail::write<flat_static_buffer_base>(
                impl.wr_fb, fh_);
            n = clamp(remain_, impl.wr_buf_size);
            net::buffer_copy(net::buffer(
                impl.wr_buf.get(), n), cb_);
            detail::mask_inplace(net::buffer(
                impl.wr_buf.get(), n), key_);
            remain_ -= n;
            impl.wr_cont = ! fin_;
            // write frame header and some payload
            BOOST_ASIO_CORO_YIELD
            {
                BOOST_ASIO_HANDLER_LOCATION((
                    __FILE__, __LINE__,
                    fin_ ?
                        "websocket::async_write" :
                        "websocket::async_write_some"
                    ));

                net::async_write(impl.stream(), buffers_cat(
                    impl.wr_fb.data(),
                    net::buffer(impl.wr_buf.get(), n)),
                        beast::detail::bind_continuation(std::move(*this)));
            }
            // VFALCO What about consuming the buffer on error?
            bytes_transferred_ +=
                bytes_transferred - impl.wr_fb.size();
            if(impl.check_stop_now(ec))
                goto upcall;
            while(remain_ > 0)
            {
                cb_.consume(impl.wr_buf_size);
                n = clamp(remain_, impl.wr_buf_size);
                net::buffer_copy(net::buffer(
                    impl.wr_buf.get(), n), cb_);
                detail::mask_inplace(net::buffer(
                    impl.wr_buf.get(), n), key_);
                remain_ -= n;
                // write more payload
                BOOST_ASIO_CORO_YIELD
                {
                    BOOST_ASIO_HANDLER_LOCATION((
                        __FILE__, __LINE__,
                        fin_ ?
                            "websocket::async_write" :
                            "websocket::async_write_some"
                        ));

                    net::async_write(impl.stream(),
                        net::buffer(impl.wr_buf.get(), n),
                            beast::detail::bind_continuation(std::move(*this)));
                }
                bytes_transferred_ += bytes_transferred;
                if(impl.check_stop_now(ec))
                    goto upcall;
            }
            goto upcall;
        }

        //------------------------------------------------------------------

        if(how_ == do_mask_frag)
        {
            // send multiple frames
            for(;;)
            {
                n = clamp(remain_, impl.wr_buf_size);
                remain_ -= n;
                fh_.len = n;
                fh_.key = impl.create_mask();
                fh_.fin = fin_ ? remain_ == 0 : false;
                detail::prepare_key(key_, fh_.key);
                net::buffer_copy(net::buffer(
                    impl.wr_buf.get(), n), cb_);
                detail::mask_inplace(net::buffer(
                    impl.wr_buf.get(), n), key_);
                impl.wr_fb.clear();
                detail::write<flat_static_buffer_base>(
                    impl.wr_fb, fh_);
                impl.wr_cont = ! fin_;
                // Send frame
                BOOST_ASIO_CORO_YIELD
                {
                    BOOST_ASIO_HANDLER_LOCATION((
                        __FILE__, __LINE__,
                        fin_ ?
                            "websocket::async_write" :
                            "websocket::async_write_some"
                        ));

                    net::async_write(impl.stream(), buffers_cat(
                        impl.wr_fb.data(),
                        net::buffer(impl.wr_buf.get(), n)),
                            beast::detail::bind_continuation(std::move(*this)));
                }
                n = bytes_transferred - impl.wr_fb.size();
                bytes_transferred_ += n;
                if(impl.check_stop_now(ec))
                    goto upcall;
                if(remain_ == 0)
                    break;
                cb_.consume(n);
                fh_.op = detail::opcode::cont;
                // Give up the write lock in between each frame
                // so that outgoing control frames might be sent.
                impl.wr_block.unlock(this);
                if( impl.op_close.maybe_invoke()
                    || impl.op_idle_ping.maybe_invoke()
                    || impl.op_rd.maybe_invoke()
                    || impl.op_ping.maybe_invoke())
                {
                    BOOST_ASSERT(impl.wr_block.is_locked());
                    goto do_suspend;
                }
                impl.wr_block.lock(this);
            }
            goto upcall;
        }

        //------------------------------------------------------------------

        if(how_ == do_deflate)
        {
            // send compressed frames
            for(;;)
            {
                b = net::buffer(impl.wr_buf.get(),
                    impl.wr_buf_size);
                more_ = impl.deflate(b, cb_, fin_, in_, ec);
                if(impl.check_stop_now(ec))
                    goto upcall;
                n = buffer_bytes(b);
                if(n == 0)
                {
                    // The input was consumed, but there is
                    // no output due to compression latency.
                    BOOST_ASSERT(! fin_);
                    BOOST_ASSERT(buffer_bytes(cb_) == 0);
                    goto upcall;
                }
                if(fh_.mask)
                {
                    fh_.key = impl.create_mask();
                    detail::prepared_key key;
                    detail::prepare_key(key, fh_.key);
                    detail::mask_inplace(b, key);
                }
                fh_.fin = ! more_;
                fh_.len = n;
                impl.wr_fb.clear();
                detail::write<
                    flat_static_buffer_base>(impl.wr_fb, fh_);
                impl.wr_cont = ! fin_;
                // Send frame
                BOOST_ASIO_CORO_YIELD
                {
                    BOOST_ASIO_HANDLER_LOCATION((
                        __FILE__, __LINE__,
                        fin_ ?
                            "websocket::async_write" :
                            "websocket::async_write_some"
                        ));

                    net::async_write(impl.stream(), buffers_cat(
                        impl.wr_fb.data(), b),
                            beast::detail::bind_continuation(std::move(*this)));
                }
                bytes_transferred_ += in_;
                if(impl.check_stop_now(ec))
                    goto upcall;
                if(more_)
                {
                    fh_.op = detail::opcode::cont;
                    fh_.rsv1 = false;
                    // Give up the write lock in between each frame
                    // so that outgoing control frames might be sent.
                    impl.wr_block.unlock(this);
                    if( impl.op_close.maybe_invoke()
                        || impl.op_idle_ping.maybe_invoke()
                        || impl.op_rd.maybe_invoke()
                        || impl.op_ping.maybe_invoke())
                    {
                        BOOST_ASSERT(impl.wr_block.is_locked());
                        goto do_suspend;
                    }
                    impl.wr_block.lock(this);
                }
                else
                {
                    if(fh_.fin)
                        impl.do_context_takeover_write(impl.role);
                    goto upcall;
                }
            }
        }

    //--------------------------------------------------------------------------

    upcall:
        impl.wr_block.unlock(this);
        impl.op_close.maybe_invoke()
            || impl.op_idle_ping.maybe_invoke()
            || impl.op_rd.maybe_invoke()
            || impl.op_ping.maybe_invoke();
        this->complete(cont, ec, bytes_transferred_);
    }
}

template<class NextLayer, bool deflateSupported>
struct stream<NextLayer, deflateSupported>::
    run_write_some_op
{
    template<
        class WriteHandler,
        class ConstBufferSequence>
    void
    operator()(
        WriteHandler&& h,
        boost::shared_ptr<impl_type> const& sp,
        bool fin,
        ConstBufferSequence const& b)
    {
        // If you get an error on the following line it means
        // that your handler does not meet the documented type
        // requirements for the handler.

        static_assert(
            beast::detail::is_invocable<WriteHandler,
                void(error_code, std::size_t)>::value,
            "WriteHandler type requirements not met");

        write_some_op<
            typename std::decay<WriteHandler>::type,
            ConstBufferSequence>(
                std::forward<WriteHandler>(h),
                sp,
                fin,
                b);
    }
};

//------------------------------------------------------------------------------

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer, deflateSupported>::
write_some(bool fin, ConstBufferSequence const& buffers)
{
    static_assert(is_sync_stream<next_layer_type>::value,
        "SyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    error_code ec;
    auto const bytes_transferred =
        write_some(fin, buffers, ec);
    if(ec)
        BOOST_THROW_EXCEPTION(system_error{ec});
    return bytes_transferred;
}

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer, deflateSupported>::
write_some(bool fin,
    ConstBufferSequence const& buffers, error_code& ec)
{
    static_assert(is_sync_stream<next_layer_type>::value,
        "SyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    using beast::detail::clamp;
    auto& impl = *impl_;
    std::size_t bytes_transferred = 0;
    ec = {};
    if(impl.check_stop_now(ec))
        return bytes_transferred;
    detail::frame_header fh;
    if(! impl.wr_cont)
    {
        impl.begin_msg();
        fh.rsv1 = impl.wr_compress;
    }
    else
    {
        fh.rsv1 = false;
    }
    fh.rsv2 = false;
    fh.rsv3 = false;
    fh.op = impl.wr_cont ?
        detail::opcode::cont : impl.wr_opcode;
    fh.mask = impl.role == role_type::client;
    auto remain = buffer_bytes(buffers);
    if(impl.wr_compress)
    {

        buffers_suffix<
            ConstBufferSequence> cb(buffers);
        for(;;)
        {
            auto b = net::buffer(
                impl.wr_buf.get(), impl.wr_buf_size);
            auto const more = impl.deflate(
                b, cb, fin, bytes_transferred, ec);
            if(impl.check_stop_now(ec))
                return bytes_transferred;
            auto const n = buffer_bytes(b);
            if(n == 0)
            {
                // The input was consumed, but there
                // is no output due to compression
                // latency.
                BOOST_ASSERT(! fin);
                BOOST_ASSERT(buffer_bytes(cb) == 0);
                fh.fin = false;
                break;
            }
            if(fh.mask)
            {
                fh.key = this->impl_->create_mask();
                detail::prepared_key key;
                detail::prepare_key(key, fh.key);
                detail::mask_inplace(b, key);
            }
            fh.fin = ! more;
            fh.len = n;
            detail::fh_buffer fh_buf;
            detail::write<
                flat_static_buffer_base>(fh_buf, fh);
            impl.wr_cont = ! fin;
            net::write(impl.stream(),
                buffers_cat(fh_buf.data(), b), ec);
            if(impl.check_stop_now(ec))
                return bytes_transferred;
            if(! more)
                break;
            fh.op = detail::opcode::cont;
            fh.rsv1 = false;
        }
        if(fh.fin)
            impl.do_context_takeover_write(impl.role);
    }
    else if(! fh.mask)
    {
        if(! impl.wr_frag)
        {
            // no mask, no autofrag
            fh.fin = fin;
            fh.len = remain;
            detail::fh_buffer fh_buf;
            detail::write<
                flat_static_buffer_base>(fh_buf, fh);
            impl.wr_cont = ! fin;
            net::write(impl.stream(),
                buffers_cat(fh_buf.data(), buffers), ec);
            if(impl.check_stop_now(ec))
                return bytes_transferred;
            bytes_transferred += remain;
        }
        else
        {
            // no mask, autofrag
            BOOST_ASSERT(impl.wr_buf_size != 0);
            buffers_suffix<
                ConstBufferSequence> cb{buffers};
            for(;;)
            {
                auto const n = clamp(remain, impl.wr_buf_size);
                remain -= n;
                fh.len = n;
                fh.fin = fin ? remain == 0 : false;
                detail::fh_buffer fh_buf;
                detail::write<
                    flat_static_buffer_base>(fh_buf, fh);
                impl.wr_cont = ! fin;
                net::write(impl.stream(),
                    beast::buffers_cat(fh_buf.data(),
                        beast::buffers_prefix(n, cb)), ec);
                bytes_transferred += n;
                if(impl.check_stop_now(ec))
                    return bytes_transferred;
                if(remain == 0)
                    break;
                fh.op = detail::opcode::cont;
                cb.consume(n);
            }
        }
    }
    else if(! impl.wr_frag)
    {
        // mask, no autofrag
        fh.fin = fin;
        fh.len = remain;
        fh.key = this->impl_->create_mask();
        detail::prepared_key key;
        detail::prepare_key(key, fh.key);
        detail::fh_buffer fh_buf;
        detail::write<
            flat_static_buffer_base>(fh_buf, fh);
        buffers_suffix<
            ConstBufferSequence> cb{buffers};
        {
            auto const n =
                clamp(remain, impl.wr_buf_size);
            auto const b =
                net::buffer(impl.wr_buf.get(), n);
            net::buffer_copy(b, cb);
            cb.consume(n);
            remain -= n;
            detail::mask_inplace(b, key);
            impl.wr_cont = ! fin;
            net::write(impl.stream(),
                buffers_cat(fh_buf.data(), b), ec);
            bytes_transferred += n;
            if(impl.check_stop_now(ec))
                return bytes_transferred;
        }
        while(remain > 0)
        {
            auto const n =
                clamp(remain, impl.wr_buf_size);
            auto const b =
                net::buffer(impl.wr_buf.get(), n);
            net::buffer_copy(b, cb);
            cb.consume(n);
            remain -= n;
            detail::mask_inplace(b, key);
            net::write(impl.stream(), b, ec);
            bytes_transferred += n;
            if(impl.check_stop_now(ec))
                return bytes_transferred;
        }
    }
    else
    {
        // mask, autofrag
        BOOST_ASSERT(impl.wr_buf_size != 0);
        buffers_suffix<
            ConstBufferSequence> cb(buffers);
        for(;;)
        {
            fh.key = this->impl_->create_mask();
            detail::prepared_key key;
            detail::prepare_key(key, fh.key);
            auto const n =
                clamp(remain, impl.wr_buf_size);
            auto const b =
                net::buffer(impl.wr_buf.get(), n);
            net::buffer_copy(b, cb);
            detail::mask_inplace(b, key);
            fh.len = n;
            remain -= n;
            fh.fin = fin ? remain == 0 : false;
            impl.wr_cont = ! fh.fin;
            detail::fh_buffer fh_buf;
            detail::write<
                flat_static_buffer_base>(fh_buf, fh);
            net::write(impl.stream(),
                buffers_cat(fh_buf.data(), b), ec);
            bytes_transferred += n;
            if(impl.check_stop_now(ec))
                return bytes_transferred;
            if(remain == 0)
                break;
            fh.op = detail::opcode::cont;
            cb.consume(n);
        }
    }
    return bytes_transferred;
}

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
stream<NextLayer, deflateSupported>::
async_write_some(bool fin,
    ConstBufferSequence const& bs, WriteHandler&& handler)
{
    static_assert(is_async_stream<next_layer_type>::value,
        "AsyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    return net::async_initiate<
        WriteHandler,
        void(error_code, std::size_t)>(
            run_write_some_op{},
            handler,
            impl_,
            fin,
            bs);
}

//------------------------------------------------------------------------------

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers)
{
    static_assert(is_sync_stream<next_layer_type>::value,
        "SyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    error_code ec;
    auto const bytes_transferred = write(buffers, ec);
    if(ec)
        BOOST_THROW_EXCEPTION(system_error{ec});
    return bytes_transferred;
}

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence>
std::size_t
stream<NextLayer, deflateSupported>::
write(ConstBufferSequence const& buffers, error_code& ec)
{
    static_assert(is_sync_stream<next_layer_type>::value,
        "SyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    return write_some(true, buffers, ec);
}

template<class NextLayer, bool deflateSupported>
template<class ConstBufferSequence, BOOST_BEAST_ASYNC_TPARAM2 WriteHandler>
BOOST_BEAST_ASYNC_RESULT2(WriteHandler)
stream<NextLayer, deflateSupported>::
async_write(
    ConstBufferSequence const& bs, WriteHandler&& handler)
{
    static_assert(is_async_stream<next_layer_type>::value,
        "AsyncStream type requirements not met");
    static_assert(net::is_const_buffer_sequence<
        ConstBufferSequence>::value,
            "ConstBufferSequence type requirements not met");
    return net::async_initiate<
        WriteHandler,
        void(error_code, std::size_t)>(
            run_write_some_op{},
            handler,
            impl_,
            true,
            bs);
}

} // websocket
} // beast
} // boost

#endif

max_stars_repo_name: detcitty/math

max_issues_repo_name: detcitty/math

max_forks_repo_name: detcitty/math


text:
//
// 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/CPPAlliance/url
//

#ifndef BOOST_URL_PARAMS_VIEW_HPP
#define BOOST_URL_PARAMS_VIEW_HPP

#include <boost/url/detail/config.hpp>
#include <boost/url/string.hpp>
#include <boost/url/detail/parts_base.hpp>
#include <iterator>
#include <type_traits>

namespace boost {
namespace urls {

#ifndef BOOST_URL_DOCS
class url_view;
class params_encoded_view;
#endif

class params_view
    : private detail::parts_base
{
    friend class url_view;
    friend class params_encoded_view;

    string_view s_;
    std::size_t n_;
    string_value::allocator a_;

    template<class Allocator>
    params_view(
        string_view s,
        std::size_t n,
        Allocator const& a);

public:
    class iterator;

    class value_type
    {
    public:
        string_value key;
        string_value value;
        bool has_value;

        BOOST_URL_DECL
        ~value_type() noexcept;

        BOOST_URL_DECL
        value_type() noexcept;

        BOOST_URL_DECL
        value_type(
            value_type const& other) noexcept;

        BOOST_URL_DECL
        value_type&
        operator=(
            value_type const& other) noexcept;

    private:
        friend class params_view;
        friend class iterator;

        BOOST_URL_DECL
        value_type(
            char const* s,
            std::size_t nk,
            std::size_t nv,
            string_value::allocator a);
    };

    using reference = value_type;
    using const_reference = value_type;
    using size_type = std::size_t;
    using difference_type = std::ptrdiff_t;

    //--------------------------------------------
    //
    // Element Access
    //
    //--------------------------------------------

    BOOST_URL_DECL
    string_value
    at(string_view key) const;

    template<class Key>
#ifdef BOOST_URL_DOCS
    string_value
#else
    typename std::enable_if<
        is_stringlike<Key>::value,
        string_value>::type
#endif
    at(Key const& key) const;

    //--------------------------------------------
    //
    // Iterators
    //
    //--------------------------------------------

    BOOST_URL_DECL
    iterator
    begin() const noexcept;

    BOOST_URL_DECL
    iterator
    end() const noexcept;

    //--------------------------------------------
    //
    // Capacity
    //
    //--------------------------------------------

    inline
    bool
    empty() const noexcept;

    inline
    std::size_t
    size() const noexcept;

    //--------------------------------------------
    //
    // Lookup
    //
    //--------------------------------------------

    BOOST_URL_DECL
    std::size_t
    count(string_view key) const noexcept;

    template<class Key>
#ifdef BOOST_URL_DOCS
    std::size_t
#else
    typename std::enable_if<
        is_stringlike<Key>::value,
        std::size_t>::type
#endif
    count(Key const& key) const noexcept;

    inline
    iterator
    find(string_view key) const noexcept;

    template<class Key>
#ifdef BOOST_URL_DOCS
    iterator
#else
    typename std::enable_if<
        is_stringlike<Key>::value,
        iterator>::type
#endif
    find(Key const& key) const noexcept;

    /** Search [from, end), from==end is valid
    */
    BOOST_URL_DECL
    iterator
    find(
        iterator from,
        string_view key) const noexcept;

    template<class Key>
#ifdef BOOST_URL_DOCS
    iterator
#else
    typename std::enable_if<
        is_stringlike<Key>::value,
        iterator>::type
#endif
    find(
        iterator from,
        Key const& key) const noexcept;

    inline
    bool
    contains(string_view key) const noexcept;

    template<class Key>
#ifdef BOOST_URL_DOCS
    bool
#else
    typename std::enable_if<
        is_stringlike<Key>::value,
        bool>::type
#endif
    contains(Key const& key) const noexcept;
};

} // urls
} // boost

// VFALCO This include is at the bottom of
// url_view.hpp because of a circular dependency
//#include <boost/url/impl/params_view.hpp>

#endif

ext: hpp

max_stars_repo_name: sandmanhome/liuguang

max_issues_repo_name: sandmanhome/liuguang

max_forks_repo_name: sandmanhome/liuguang


text:
/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 Sylko Olzscher
 *
 */

#include "cluster.h"
#include <smf/cluster/generator.h>
#include <cyng/async/task/task_builder.hpp>
#include <cyng/io/serializer.h>
#include <cyng/vm/generator.h>
#include <boost/uuid/random_generator.hpp>

namespace node
{
	cluster::cluster(cyng::async::base_task* btp
		, cyng::logging::log_ptr logger
		, boost::uuids::uuid tag
		, cluster_config_t const& cfg
		, boost::asio::ip::tcp::endpoint ep)
	: base_(*btp)
		, bus_(bus_factory(btp->mux_, logger, boost::uuids::random_generator()(), btp->get_id()))
		, logger_(logger)
		, config_(cfg)
	{
		CYNG_LOG_INFO(logger_, "initialize task #"
			<< base_.get_id()
			<< " <"
			<< base_.get_class_name()
			<< ">");

		//
		//	implement request handler
		//
		bus_->vm_.register_function("bus.reconfigure", 1, std::bind(&cluster::reconfigure, this, std::placeholders::_1));
		bus_->vm_.async_run(cyng::generate_invoke("log.msg.info", cyng::invoke("lib.size"), "callbacks registered"));

	}

	cyng::continuation cluster::run()
	{
		if (!bus_->is_online())
		{
			connect();
		}
		else
		{
			CYNG_LOG_DEBUG(logger_, "cluster bus is online");
		}

		return cyng::continuation::TASK_CONTINUE;
	}

	void cluster::stop()
	{
		//
		//	stop server
		//
// 		server_.close();

		//
		//	sign off from cluster
		//
		bus_->stop();

		CYNG_LOG_INFO(logger_, "task #"
			<< base_.get_id()
			<< " <"
			<< base_.get_class_name()
			<< "> stopped - leaving cluster");
	}

	//	slot 0
	cyng::continuation cluster::process(cyng::version const& v)
	{
		if (v < cyng::version(NODE_VERSION_MAJOR, NODE_VERSION_MINOR))
		{
			CYNG_LOG_WARNING(logger_, "insufficient cluster protocol version: "	<< v);
		}

		//
		//	start http server
		//
// 		server_.run();

		return cyng::continuation::TASK_CONTINUE;
	}

	cyng::continuation cluster::process()
	{
		//
		//	switch to other configuration
		//
		reconfigure_impl();
		return cyng::continuation::TASK_CONTINUE;
	}

	void cluster::connect()
	{
		BOOST_ASSERT_MSG(!bus_->vm_.is_halted(), "cluster bus is halted");
		bus_->vm_.async_run(bus_req_login(config_.get().host_
			, config_.get().service_
			, config_.get().account_
			, config_.get().pwd_
			, config_.get().auto_config_
			, config_.get().group_
			, "lora"));

		CYNG_LOG_INFO(logger_, "cluster login request is sent to "
			<< config_.get().host_
			<< ':'
			<< config_.get().service_);
	}

	void cluster::reconfigure(cyng::context& ctx)
	{
		reconfigure_impl();
	}

	void cluster::reconfigure_impl()
	{
		//
		//	switch to other master
		//
		if (config_.next())
		{
			CYNG_LOG_INFO(logger_, "switch to redundancy "
				<< config_.get().host_
				<< ':'
				<< config_.get().service_);
		}
		else
		{
			CYNG_LOG_ERROR(logger_, "cluster login failed - no other redundancy available");
		}

		//
		//	trigger reconnect
		//
		CYNG_LOG_INFO(logger_, "reconnect to cluster in "
			<< config_.get().monitor_.count()
			<< " seconds");
		base_.suspend(config_.get().monitor_);

	}
}

ext: cpp

max_stars_repo_name: solosTec/node

max_issues_repo_name: solosTec/node

max_forks_repo_name: solosTec/node


text:
/// @author Владимир Керимов

#define BOOST_TEST_MODULE dozen unit tests
#include <boost/test/unit_test.hpp>

max_stars_repo_name: qualab/dozen

max_issues_repo_name: qualab/dozen

max_forks_repo_name: qualab/dozen


text:
#include <boost/test/unit_test.hpp>
#include <stringconversion/Containers.h>

BOOST_AUTO_TEST_SUITE(containers)

BOOST_AUTO_TEST_CASE(int_delimeters_vector)
{
  std::vector<int> numbers;
  std::string str = "53,45,67,89;45;89;1002";

  stringconversion::generate_from_delimeted<int>(str.begin(), str.end(), ",;", std::back_inserter(numbers));
  auto answers = { 53, 45, 67, 89, 45, 89, 1002 };
  BOOST_CHECK_EQUAL_COLLECTIONS(numbers.begin(), numbers.end(), answers.begin(), answers.end());
}

BOOST_AUTO_TEST_CASE(double_delimeters_array)
{
  std::array<double, 7> numbers;
  std::string str = "53.5,45.5,67.5,89.5;45.5;89.5;1002.5";

  stringconversion::generate_from_delimeted<double>(str.begin(), str.end(), ",;", numbers.begin());
  auto answers = { 53.5, 45.5, 67.5, 89.5, 45.5, 89.5, 1002.5 };
  BOOST_CHECK_EQUAL_COLLECTIONS(numbers.begin(), numbers.end(), answers.begin(), answers.end());
}

BOOST_AUTO_TEST_SUITE_END()

max_stars_repo_name: tuncb/stringconversion

max_issues_repo_name: tuncb/stringconversion

max_forks_repo_name: tuncb/stringconversion


text:
//----------------------------------------------------------------------------
/** @file SgTimeControlTest.cpp
    Unit tests for SgTimeControl.
*/
//----------------------------------------------------------------------------

#include "SgSystem.h"

#include <boost/test/auto_unit_test.hpp>
#include "SgTimeControl.h"
#include "SgTimeRecord.h"

using namespace std;

//----------------------------------------------------------------------------

namespace {

//----------------------------------------------------------------------------

/** Concrete subclass of SgDefaultTimeControl for testing purposes. */
class TimeControl
    : public SgDefaultTimeControl
{
public:
    void GetPositionInfo(SgBlackWhite& toPlay, int& movesPlayed,
                         int& estimatedRemainingMoves);

    /** Set values returned by the next call of GetPositionInfo() */
    void SetPositionInfo(SgBlackWhite toPlay, int movesPlayed,
                         int estimatedRemainingMoves);

private:
    SgBlackWhite m_toPlay;

    int m_movesPlayed;

    int m_estimatedRemainingMoves;
};

inline void TimeControl::GetPositionInfo(SgBlackWhite& toPlay,
                                         int& movesPlayed,
                                         int& estimatedRemainingMoves)
{
    toPlay = m_toPlay;
    movesPlayed = m_movesPlayed;
    estimatedRemainingMoves = m_estimatedRemainingMoves;
}

inline void TimeControl::SetPositionInfo(SgBlackWhite toPlay, int movesPlayed,
                                         int estimatedRemainingMoves)
{
    m_toPlay = toPlay;
    m_movesPlayed = movesPlayed;
    m_estimatedRemainingMoves = estimatedRemainingMoves;
}

//----------------------------------------------------------------------------

/** Test border cases in which bugs could make SgDefaultTimeControl return
    infinity.
*/
BOOST_AUTO_TEST_CASE(SgDefaultTimeControlTest_Infinity)
{
    TimeControl control;
    SgTimeRecord timeRecord;
    const double epsilon = 1.0;
    double timeMove;

    // Played moves and remaining moves is 0, in main time, no overtime
    timeRecord.SetTimeLeft(SG_BLACK, 100.0);
    timeRecord.SetMovesLeft(SG_BLACK, 0);
    control.SetPositionInfo(SG_BLACK, 0, 0);
    timeMove = control.TimeForCurrentMove(timeRecord, true/*quiet*/);
    //BOOST_CHECK(timeMove < 100.0 + epsilon);

    // Remaining moves is 0, in main time, with overtime
    timeRecord.SetTimeLeft(SG_BLACK, 100.0);
    timeRecord.SetMovesLeft(SG_BLACK, 0);
    timeRecord.SetOTPeriod(50.0);
    timeRecord.SetOTNumMoves(5);
    control.SetPositionInfo(SG_BLACK, 10, 0);
    timeMove = control.TimeForCurrentMove(timeRecord, true/*quiet*/);
    BOOST_CHECK(timeMove < 100.0 + epsilon);
}

//----------------------------------------------------------------------------

} // namespace

max_stars_repo_name: MisterTea/HyperNEAT

max_issues_repo_name: afcarl/HyperNEAT

max_forks_repo_name: afcarl/HyperNEAT


text:
/**
* This file is part of Fast-Planner.
*
* Copyright 2019 Boyu Zhou, Aerial Robotics Group, Hong Kong University of Science and Technology, <uav.ust.hk>
* Developed by Boyu Zhou <bzhouai at connect dot ust dot hk>, <uv dot boyuzhou at gmail dot com>
* for more information see <https://github.com/HKUST-Aerial-Robotics/Fast-Planner>.
* If you use this code, please cite the respective publications as
* listed on the above website.
*
* Fast-Planner is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Fast-Planner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Fast-Planner. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _POLYNOMIAL_TRAJ_H
#define _POLYNOMIAL_TRAJ_H

#include <Eigen/Eigen>
#include <vector>

using std::vector;

class PolynomialTraj {
private:
  vector<double> times;        // time of each segment
  vector<vector<double>> cxs;  // coefficient of x of each segment, from high order to low
  vector<vector<double>> cys;  // coefficient of y of each segment
  vector<vector<double>> czs;  // coefficient of z of each segment

  double time_sum;
  int num_seg;

  /* evaluation */
  vector<Eigen::Vector3d> traj_vec3d;
  double length;

public:
  PolynomialTraj(/* args */) {
  }
  ~PolynomialTraj() {
  }

  void reset() {
    times.clear(), cxs.clear(), cys.clear(), czs.clear();
    time_sum = 0.0, num_seg = 0;
  }

  void addSegment(vector<double> cx, vector<double> cy, vector<double> cz, double t) {
    cxs.push_back(cx), cys.push_back(cy), czs.push_back(cz), times.push_back(t);
  }

  void init() {
    num_seg = times.size();
    time_sum = 0.0;
    for (int i = 0; i < times.size(); ++i) {
      time_sum += times[i];
    }
  }

  Eigen::Vector3d evaluate(double t) {
    /* detetrmine segment num */
    int idx = 0;
    while (times[idx] < t) {
      t -= times[idx];
      ++idx;
    }

    /* evaluation */
    int order = cxs[idx].size();
    Eigen::VectorXd cx(order), cy(order), cz(order), tv(order);
    for (int i = 0; i < order; ++i) {
      cx(i) = cxs[idx][i], cy(i) = cys[idx][i], cz(i) = czs[idx][i];
      tv(order - 1 - i) = std::pow(t, double(i));
    }

    Eigen::Vector3d pt;
    pt(0) = tv.dot(cx), pt(1) = tv.dot(cy), pt(2) = tv.dot(cz);
    return pt;
  }

  Eigen::Vector3d evaluateVel(double t) {
    /* detetrmine segment num */
    int idx = 0;
    while (times[idx] < t) {
      t -= times[idx];
      ++idx;
    }

    /* evaluation */
    int order = cxs[idx].size();
    Eigen::VectorXd vx(order - 1), vy(order - 1), vz(order - 1);

    /* coef of vel */
    for (int i = 0; i < order - 1; ++i) {
      vx(i) = double(i + 1) * cxs[idx][order - 2 - i];
      vy(i) = double(i + 1) * cys[idx][order - 2 - i];
      vz(i) = double(i + 1) * czs[idx][order - 2 - i];
    }
    double ts = t;
    Eigen::VectorXd tv(order - 1);
    for (int i = 0; i < order - 1; ++i)
      tv(i) = pow(ts, i);

    Eigen::Vector3d vel;
    vel(0) = tv.dot(vx), vel(1) = tv.dot(vy), vel(2) = tv.dot(vz);
    return vel;
  }

  Eigen::Vector3d evaluateAcc(double t) {
    /* detetrmine segment num */
    int idx = 0;
    while (times[idx] < t) {
      t -= times[idx];
      ++idx;
    }

    /* evaluation */
    int order = cxs[idx].size();
    Eigen::VectorXd ax(order - 2), ay(order - 2), az(order - 2);

    /* coef of vel */
    for (int i = 0; i < order - 2; ++i) {
      ax(i) = double((i + 2) * (i + 1)) * cxs[idx][order - 3 - i];
      ay(i) = double((i + 2) * (i + 1)) * cys[idx][order - 3 - i];
      az(i) = double((i + 2) * (i + 1)) * czs[idx][order - 3 - i];
    }
    double ts = t;
    Eigen::VectorXd tv(order - 2);
    for (int i = 0; i < order - 2; ++i)
      tv(i) = pow(ts, i);

    Eigen::Vector3d acc;
    acc(0) = tv.dot(ax), acc(1) = tv.dot(ay), acc(2) = tv.dot(az);
    return acc;
  }

  /* for evaluating traj, should be called in sequence!!! */
  double getTimeSum() {
    return this->time_sum;
  }

  vector<Eigen::Vector3d> getTraj() {
    double eval_t = 0.0;
    traj_vec3d.clear();
    while (eval_t < time_sum) {
      Eigen::Vector3d pt = evaluate(eval_t);
      traj_vec3d.push_back(pt);
      eval_t += 0.01;
    }
    return traj_vec3d;
  }

  double getLength() {
    length = 0.0;

    Eigen::Vector3d p_l = traj_vec3d[0], p_n;
    for (int i = 1; i < traj_vec3d.size(); ++i) {
      p_n = traj_vec3d[i];
      length += (p_n - p_l).norm();
      p_l = p_n;
    }
    return length;
  }

  double getMeanVel() {
    double mean_vel = length / time_sum;
  }

  double getAccCost() {
    double cost = 0.0;
    int order = cxs[0].size();

    for (int s = 0; s < times.size(); ++s) {
      Eigen::Vector3d um;
      um(0) = 2 * cxs[s][order - 3], um(1) = 2 * cys[s][order - 3], um(2) = 2 * czs[s][order - 3];
      cost += um.squaredNorm() * times[s];
    }

    return cost;
  }

  double getJerk() {
    double jerk = 0.0;

    /* evaluate jerk */
    for (int s = 0; s < times.size(); ++s) {
      Eigen::VectorXd cxv(cxs[s].size()), cyv(cys[s].size()), czv(czs[s].size());
      /* convert coefficient */
      int order = cxs[s].size();
      for (int j = 0; j < order; ++j) {
        cxv(j) = cxs[s][order - 1 - j], cyv(j) = cys[s][order - 1 - j], czv(j) = czs[s][order - 1 - j];
      }
      double ts = times[s];

      /* jerk matrix */
      Eigen::MatrixXd mat_jerk(order, order);
      mat_jerk.setZero();
      for (double i = 3; i < order; i += 1)
        for (double j = 3; j < order; j += 1) {
          mat_jerk(i, j) =
              i * (i - 1) * (i - 2) * j * (j - 1) * (j - 2) * pow(ts, i + j - 5) / (i + j - 5);
        }

      jerk += (cxv.transpose() * mat_jerk * cxv)(0, 0);
      jerk += (cyv.transpose() * mat_jerk * cyv)(0, 0);
      jerk += (czv.transpose() * mat_jerk * czv)(0, 0);
    }

    return jerk;
  }

  void getMeanAndMaxVel(double& mean_v, double& max_v) {
    int num = 0;
    mean_v = 0.0, max_v = -1.0;
    for (int s = 0; s < times.size(); ++s) {
      int order = cxs[s].size();
      Eigen::VectorXd vx(order - 1), vy(order - 1), vz(order - 1);

      /* coef of vel */
      for (int i = 0; i < order - 1; ++i) {
        vx(i) = double(i + 1) * cxs[s][order - 2 - i];
        vy(i) = double(i + 1) * cys[s][order - 2 - i];
        vz(i) = double(i + 1) * czs[s][order - 2 - i];
      }
      double ts = times[s];

      double eval_t = 0.0;
      while (eval_t < ts) {
        Eigen::VectorXd tv(order - 1);
        for (int i = 0; i < order - 1; ++i)
          tv(i) = pow(ts, i);
        Eigen::Vector3d vel;
        vel(0) = tv.dot(vx), vel(1) = tv.dot(vy), vel(2) = tv.dot(vz);
        double vn = vel.norm();
        mean_v += vn;
        if (vn > max_v) max_v = vn;
        ++num;

        eval_t += 0.01;
      }
    }

    mean_v = mean_v / double(num);
  }

  void getMeanAndMaxAcc(double& mean_a, double& max_a) {
    int num = 0;
    mean_a = 0.0, max_a = -1.0;
    for (int s = 0; s < times.size(); ++s) {
      int order = cxs[s].size();
      Eigen::VectorXd ax(order - 2), ay(order - 2), az(order - 2);

      /* coef of acc */
      for (int i = 0; i < order - 2; ++i) {
        ax(i) = double((i + 2) * (i + 1)) * cxs[s][order - 3 - i];
        ay(i) = double((i + 2) * (i + 1)) * cys[s][order - 3 - i];
        az(i) = double((i + 2) * (i + 1)) * czs[s][order - 3 - i];
      }
      double ts = times[s];

      double eval_t = 0.0;
      while (eval_t < ts) {
        Eigen::VectorXd tv(order - 2);
        for (int i = 0; i < order - 2; ++i)
          tv(i) = pow(ts, i);
        Eigen::Vector3d acc;
        acc(0) = tv.dot(ax), acc(1) = tv.dot(ay), acc(2) = tv.dot(az);
        double an = acc.norm();
        mean_a += an;
        if (an > max_a) max_a = an;
        ++num;

        eval_t += 0.01;
      }
    }

    mean_a = mean_a / double(num);
  }
};

#endif


text:
#include "moja/datarepository/tileblockcellindexer.h"

#include <boost/format.hpp>
#include <boost/format/group.hpp>

#include <iomanip>

namespace moja {
namespace datarepository {

// --------------------------------------------------------------------------------------------

Rectangle::Rectangle(Point p, Size s) : _location(p), _size(s) {}
Rectangle::Rectangle(double x, double y, double width, double height) : _location({y, x}), _size({height, width}) {}
Rectangle::~Rectangle() {}

// --------------------------------------------------------------------------------------------

TileBlockCellIndexer::TileBlockCellIndexer(double tileLatSize, double tileLonSize, double blockLatSize,
                                           double blockLonSize, double cellLatSize, double cellLonSize) {
   // TODO: Check all sizes fit evenly, can't have part tiles/blocks/cells
   // throw an exception if we have an issue
   SanityChecksLatLon(tileLatSize, tileLonSize, blockLatSize, blockLonSize, cellLatSize, cellLonSize);

   SetGlobalData();

   tileDesc.latSize = tileLatSize;
   tileDesc.lonSize = tileLonSize;
   tileDesc.latSizeInv = 1.0 / tileDesc.latSize;
   tileDesc.lonSizeInv = 1.0 / tileDesc.lonSize;
   tileDesc.cols = convOtoI(globalDesc.lonSize, tileLonSize);
   tileDesc.rows = convOtoI(globalDesc.latSize, tileLatSize);
   tileDesc.indexLimit = tileDesc.cols * tileDesc.rows;

   blockDesc.latSize = blockLatSize;
   blockDesc.lonSize = blockLonSize;
   blockDesc.latSizeInv = 1.0 / blockDesc.latSize;
   blockDesc.lonSizeInv = 1.0 / blockDesc.lonSize;
   blockDesc.cols = convOtoI(tileDesc.lonSize, blockLonSize);
   blockDesc.rows = convOtoI(tileDesc.latSize, blockLatSize);
   blockDesc.indexLimit = blockDesc.cols * blockDesc.rows;

   cellDesc.latSize = cellLatSize;
   cellDesc.lonSize = cellLonSize;
   cellDesc.latSizeInv = 1.0 / cellDesc.latSize;
   cellDesc.lonSizeInv = 1.0 / cellDesc.lonSize;
   cellDesc.cols = convOtoI(blockDesc.lonSize, cellLonSize);
   cellDesc.rows = convOtoI(blockDesc.latSize, cellLatSize);
   cellDesc.indexLimit = cellDesc.cols * cellDesc.rows;

   cellResCompareValueLon = Int32((cellDesc.lonSize * 100000) + MathEx::k0Plus);  //  0.00025;
   cellResCompareValueLat = Int32((cellDesc.latSize * 100000) + MathEx::k0Plus);
}

// --------------------------------------------------------------------------------------------

TileBlockCellIndexer::TileBlockCellIndexer(UInt32 tileRows, UInt32 tileCols, UInt32 blockRows, UInt32 blockCols,
                                           UInt32 cellRows, UInt32 cellCols) {
   // TODO: Check all sizes fit evenly, can't have part tiles/blocks/cells
   // throw an exception if we have an issue
   SanityChecksIndex(tileRows, tileCols, blockRows, blockCols, cellRows, cellCols);
   SetGlobalData();

   tileDesc.cols = tileCols;
   tileDesc.rows = tileRows;
   tileDesc.latSize = globalDesc.latSize / tileRows;  // TODO: do I use MathEx::k0Plus here?
   tileDesc.lonSize = globalDesc.lonSize / tileCols;  // TODO: do I use MathEx::k0Plus here?
   tileDesc.latSizeInv = 1.0 / tileDesc.latSize;
   tileDesc.lonSizeInv = 1.0 / tileDesc.lonSize;
   tileDesc.indexLimit = tileDesc.cols * tileDesc.rows;

   blockDesc.cols = blockCols;
   blockDesc.rows = blockRows;
   blockDesc.latSize = tileDesc.latSize / blockRows;  // TODO: do I use MathEx::k0Plus here?
   blockDesc.lonSize = tileDesc.lonSize / blockCols;  // TODO: do I use MathEx::k0Plus here?
   blockDesc.latSizeInv = 1.0 / blockDesc.latSize;
   blockDesc.lonSizeInv = 1.0 / blockDesc.lonSize;
   blockDesc.indexLimit = blockDesc.cols * blockDesc.rows;

   cellDesc.cols = cellCols;
   cellDesc.rows = cellRows;
   cellDesc.latSize = blockDesc.latSize / cellRows;  // TODO: do I use MathEx::k0Plus here?
   cellDesc.lonSize = blockDesc.lonSize / cellCols;  // TODO: do I use MathEx::k0Plus here?
   cellDesc.latSizeInv = 1.0 / cellDesc.latSize;
   cellDesc.lonSizeInv = 1.0 / cellDesc.lonSize;
   cellDesc.indexLimit = cellDesc.cols * cellDesc.rows;

   cellResCompareValueLon = Int32((cellDesc.lonSize * 100000) + MathEx::k0Plus);  //  0.00025;
   cellResCompareValueLat = Int32((cellDesc.latSize * 100000) + MathEx::k0Plus);
}

// --------------------------------------------------------------------------------------------

void TileBlockCellIndexer::SetGlobalData() {
   globalDesc.name = "Global";
   tileDesc.name = "Tile";
   blockDesc.name = "Block";
   cellDesc.name = "Cell";

   globalDesc.latSize = 180.0;
   globalDesc.lonSize = 360.0;
   globalDesc.latSizeInv = 1.0 / 180.0;
   globalDesc.lonSizeInv = 1.0 / 360.0;
   globalDesc.cols = 1;
   globalDesc.rows = 1;
   globalDesc.indexLimit = 1;
}

// --------------------------------------------------------------------------------------------

void TileBlockCellIndexer::SanityChecksLatLon(double tileLatSize, double tileLonSize, double blockLatSize,
                                              double blockLonSize, double cellLatSize, double cellLonSize) {
   // Can't get these to work as the floating point MOD has issues
#if 0
	double result = fmod(180.0, tileLatSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << tileLatSize << ", 180.0) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("180.0 % tileLatSize == 0"));
	}
	result = fmod(360.0, tileLonSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << tileLonSize << ", 360.0) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("360.0 % tileLonSize == 0"));
	}

	result = fmod(tileLatSize, blockLatSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << blockLatSize << ", tileLatSize) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("tileLatSize % blockLatSize == 0"));
	}

	result = fmod(tileLonSize, blockLonSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << blockLonSize << ", tileLonSize) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("tileLonSize % blockLonSize == 0"));
	}

	result = fmod(blockLatSize, cellLatSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << cellLatSize << ", blockLatSize) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("blockLatSize % cellLatSize == 0"));
	}

	result = fmod(blockLonSize, cellLonSize);
	if (!FloatCmp::equalTo(result, 0.0)) {
		std::stringstream ss;
		ss << "fmod(" << cellLonSize << ", blockLonSize) = " << result;
		BOOST_THROW_EXCEPTION(TileBlockCellIndexerInvalidParameterException() << Component(ss.str()) << Constraint("blockLonSize % cellLonSize == 0"));
	}
#endif
}

// --------------------------------------------------------------------------------------------

void TileBlockCellIndexer::SanityChecksIndex(UInt32 tileRows, UInt32 tileCols, UInt32 blockRows, UInt32 blockCols,
                                             UInt32 cellRows, UInt32 cellCols) {}

// --------------------------------------------------------------------------------------------

std::string TileBlockCellIndexer::toString() const {
   std::string text;
   const TileBlockCellLayerDesc* _globalDesc[4] = {&globalDesc, &tileDesc, &blockDesc, &cellDesc};
   for (auto i = 0; i < 4; i++) {
      text += (boost::format("%1%,%2%,%3%,%4%,%5%\n") % _globalDesc[i]->name %
               boost::io::group(std::setprecision(15), _globalDesc[i]->latSize) %
               boost::io::group(std::setprecision(15), _globalDesc[i]->lonSize) % _globalDesc[i]->rows %
               _globalDesc[i]->cols)
                  .str();
   }
   return text;
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getLatLonFromIndex(const CellIdx& cellIndex) const {
   auto originLon = (cellIndex.tileX() * tileDesc.lonSize) + (cellIndex.blockX() * blockDesc.lonSize) +
                    (cellIndex.cellX() * cellDesc.lonSize);
   auto originLat = (cellIndex.tileY() * tileDesc.latSize) + (cellIndex.blockY() * blockDesc.latSize) +
                    (cellIndex.cellY() * cellDesc.latSize);
   return Point({convertFromLatOrigin(originLat), convertFromLonOrigin(originLon)});
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getLatLonFromIndex(const BlockIdx& blockIndex) const {
   auto originLon = (blockIndex.tileX() * tileDesc.lonSize) + (blockIndex.blockX() * blockDesc.lonSize);
   auto originLat = (blockIndex.tileY() * tileDesc.latSize) + (blockIndex.blockY() * blockDesc.latSize);
   return Point({convertFromLatOrigin(originLat), convertFromLonOrigin(originLon)});
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getLatLonFromIndex(const TileIdx& tileIndex) const {
   auto originLon = tileIndex.tileX() * tileDesc.lonSize;
   auto originLat = tileIndex.tileY() * tileDesc.latSize;
   return Point({convertFromLatOrigin(originLat), convertFromLonOrigin(originLon)});
}

// --------------------------------------------------------------------------------------------

double TileBlockCellIndexer::getLatFromIndex(const CellIdx& cellIndex) const {
   auto originLat = (cellIndex.tileY() * tileDesc.latSize) + (cellIndex.blockY() * blockDesc.latSize) +
                    (cellIndex.cellY() * cellDesc.latSize);
   return convertFromLatOrigin(originLat);
}

// --------------------------------------------------------------------------------------------

double TileBlockCellIndexer::getLatFromIndex(const BlockIdx& blockIndex) const {
   auto originLat = (blockIndex.tileY() * tileDesc.latSize) + (blockIndex.blockY() * blockDesc.latSize);
   return convertFromLatOrigin(originLat);
}

// --------------------------------------------------------------------------------------------

double TileBlockCellIndexer::getLatFromIndex(const TileIdx& tileIndex) const {
   auto originLat = tileIndex.tileY() * tileDesc.latSize;
   return convertFromLatOrigin(originLat);
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getOriginFromIndex(const CellIdx& cellIndex) const {
   auto oLon = (cellIndex.tileX() * tileDesc.lonSize) + (cellIndex.blockX() * blockDesc.lonSize) +
               (cellIndex.cellX() * cellDesc.lonSize);
   auto oLat = (cellIndex.tileY() * tileDesc.latSize) + (cellIndex.blockY() * blockDesc.latSize) +
               (cellIndex.cellY() * cellDesc.latSize);
   return Point({oLat, oLon});
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getOriginFromIndex(const BlockIdx& blockIndex) const {
   auto oLon = (blockIndex.tileX() * tileDesc.lonSize) + (blockIndex.blockX() * blockDesc.lonSize);
   auto oLat = (blockIndex.tileY() * tileDesc.latSize) + (blockIndex.blockY() * blockDesc.latSize);
   return Point({oLat, oLon});
}

// --------------------------------------------------------------------------------------------

Point TileBlockCellIndexer::getOriginFromIndex(const TileIdx& tileIndex) const {
   auto oLon = (tileIndex.tileX() * tileDesc.lonSize);
   auto oLat = (tileIndex.tileY() * tileDesc.latSize);
   return Point({oLat, oLon});
}

// --------------------------------------------------------------------------------------------

CellIdx TileBlockCellIndexer::getCellIndexFromLatLon(const Point& point) const {
   auto oLon = convertToLonOrigin(point.lon);
   auto oLat = convertToLatOrigin(point.lat);
   auto tileX = convOtoI(oLon, tileDesc.lonSize);
   auto tileY = convOtoI(oLat, tileDesc.latSize);
   auto blockX = convOtoI(oLon - (tileX * tileDesc.lonSize), blockDesc.lonSize);
   auto blockY = convOtoI(oLat - (tileY * tileDesc.latSize), blockDesc.latSize);
   auto cellX = convOtoI(oLon - (tileX * tileDesc.lonSize) - (blockX * blockDesc.lonSize), cellDesc.lonSize);
   auto cellY = convOtoI(oLat - (tileY * tileDesc.latSize) - (blockY * blockDesc.latSize), cellDesc.latSize);
   CellIdx cellIdx(XYIndex({tileX, tileY}), XYIndex({blockX, blockY}), XYIndex({cellX, cellY}), *this);
   return cellIdx;
}

// --------------------------------------------------------------------------------------------

BlockIdx TileBlockCellIndexer::getBlockIndexFromLatLon(const Point& point) const {
   auto oLon = convertToLonOrigin(point.lon);
   auto oLat = convertToLatOrigin(point.lat);
   auto tileX = convOtoI(oLon, tileDesc.lonSize);
   auto tileY = convOtoI(oLat, tileDesc.latSize);
   auto blockX = convOtoI(oLon - (tileX * tileDesc.lonSize), blockDesc.lonSize);
   auto blockY = convOtoI(oLat - (tileY * tileDesc.latSize), blockDesc.latSize);
   BlockIdx blockIdx(XYIndex({tileX, tileY}), XYIndex({blockX, blockY}), *this);
   return blockIdx;
}

// --------------------------------------------------------------------------------------------

TileIdx TileBlockCellIndexer::getTileIndexFromLatLon(const Point& point) const {
   auto oLon = convertToLonOrigin(point.lon);
   auto oLat = convertToLatOrigin(point.lat);
   auto tileX = convOtoI(oLon, tileDesc.lonSize);
   auto tileY = convOtoI(oLat, tileDesc.latSize);
   TileIdx tileIdx(XYIndex({tileX, tileY}), *this);
   return tileIdx;
}

// --------------------------------------------------------------------------------------------

CellIdx TileBlockCellIndexer::convertIndex(const CellIdx& cell) const {
   if (compare(*cell._indexer)) return cell;
   auto cellX = convOtoI(cell.cellX() * cell._indexer->cellDesc.lonSize, cellDesc.lonSize);
   auto cellY = convOtoI(cell.cellY() * cell._indexer->cellDesc.latSize, cellDesc.latSize);
   auto cellIdx = (cellY * cellDesc.cols + cellX);
   return CellIdx(cell.tileIdx, cell.blockIdx, cellIdx, *this);
}

// --------------------------------------------------------------------------------------------

std::string TileIdx::toString() const {
   std::string text;

#if 1
   text += (boost::format("(%1%)") % boost::io::group(std::setw(6), tileIdx)).str();
#else
   text += (boost::format("(%1%:[%2%,%3%])") % boost::io::group(std::setw(6), tileIdx) %
            boost::io::group(std::setw(3), tileX()) % boost::io::group(std::setw(3), tileY()))
               .str();
#endif
   return text;
}

// --------------------------------------------------------------------------------------------

std::string BlockIdx::toString() const {
   std::string text;

#if 1
   text +=
       (boost::format("(%1%,%2%)") % boost::io::group(std::setw(6), tileIdx) % boost::io::group(std::setw(3), blockIdx))
           .str();
#else
   text += (boost::format("(%1%,%2%:[%3%,%4%],[%5%,%6%])") % boost::io::group(std::setw(6), tileIdx) %
            boost::io::group(std::setw(3), blockIdx) % boost::io::group(std::setw(3), tileX()) %
            boost::io::group(std::setw(3), tileY()) % boost::io::group(std::setw(3), blockX()) %
            boost::io::group(std::setw(3), blockY()))
               .str();
#endif
   return text;
}

// --------------------------------------------------------------------------------------------

std::string CellIdx::toString() const {
   std::string text;

#if 1
   text += (boost::format("(%1%,%2%,%3%)") % boost::io::group(std::setw(6), tileIdx) %
            boost::io::group(std::setw(3), blockIdx) % boost::io::group(std::setw(6), cellIdx))
               .str();
#else
   text += (boost::format("(%1%,%2%,%3%:[%4%,%5%],[%6%,%7%],[%8%,%9%])") % boost::io::group(std::setw(6), tileIdx) %
            boost::io::group(std::setw(3), blockIdx) % cellIdx % boost::io::group(std::setw(3), tileX()) %
            boost::io::group(std::setw(3), tileY()) % boost::io::group(std::setw(3), blockX()) %
            boost::io::group(std::setw(3), blockY()) % boost::io::group(std::setw(4), cellX()) %
            boost::io::group(std::setw(4), cellY()))
               .str();
#endif
   return text;
}

}  // namespace datarepository
}  // namespace moja

max_stars_repo_name: moja-global/flint

max_issues_repo_name: moja-global/flint

max_forks_repo_name: moja-global/flint


text:
#ifndef IOD_JSON_HH_
# define IOD_JSON_HH_

#include <vector>
#include <fstream>
#include <cassert>
#include <tuple>
#include <string>
#include <sstream>
#include <iostream>
#include <stdexcept>
#include <map>
#include <boost/lexical_cast.hpp>

#include <iod/sio.hh>
#include <iod/foreach.hh>
#include <iod/symbols.hh>
#include <iod/pow_10.hh>
#include <iod/stringview.hh>

// Json IOD api.

namespace iod
{

  using namespace s;

  // Decode \o from a json string \str.
  template <typename ...T>
  inline void json_decode(sio<T...>& o, const stringview& str);

  // Encode \o into a json string.
  template <typename ...T>
  inline std::string json_encode(const sio<T...>& o);

  // Encode \o into a json string.
  template <typename T>
  inline std::string json_encode(const std::vector<T>& v);

  // Encode \o into a stream.
  template <typename S, typename ...Tail>
  inline void json_encode(const sio<Tail...>& o, S& stream);

  namespace json_internals
  {

    // Json encoder.
    // =============================================
    template <typename T, typename S>
    inline void json_encode_(const T& t, S& ss)
    {
      ss << t;
    }

    template <typename S>
    inline void json_encode_(const char* t, S& ss)
    {
      ss << '"' << t << '"';
    }

    template <typename S>
    inline void json_encode_(const stringview& s, S& ss)
    {
      ss << '"';
      for (int i = 0; i < s.len; i++)
        ss << s.str[i];
      ss << '"';
    }

    template <typename S>
    inline void json_encode_(const std::string& t, S& ss)
    {
      json_encode_(t.c_str(), ss);
    }

    // Forward declaration.
    template <typename S, typename ...Tail>
    inline void json_encode_(const sio<Tail...>& o, S& ss);

    template <typename T, typename S>
    inline void json_encode_(const std::vector<T>& array, S& ss)
    {
      ss << '[';
      for (const auto& t : array)
      {
        json_encode_(t, ss);
        if (&t != &array.back())
          ss << ',';
      }
      ss << ']';
    }

    template <typename S, typename ...Tail>
    inline void json_encode_(const sio<Tail...>& o, S& ss)
    {
      ss << '{';
      int i = 0;
      bool first = true;
      foreach(o) | [&] (const auto& m)
      {
        if (!m.attributes().has(_json_skip))
        {
          if (!first) { ss << ','; }
          first = false;
          json_encode_(m.attributes().get(_json_key, m.symbol()).name(), ss);
          ss << ':';
          json_encode_(m.value(), ss);
        }
        i++;
      };
      ss << '}';
    }

    // Json decoder.
    // =============================================

    template <typename T>
    struct fill_ {
      inline fill_(T& _r) : r(_r) {}
      T& r;
    };

    template <typename T>
    inline fill_<T> fill(T& t) { return fill_<T>(t); }

    struct json_parser
    {
      struct spaces_ {} spaces;

      inline json_parser(std::istringstream& _stream) : str(_stream.str()), pos(0) {}
      inline json_parser(const std::string& _str) : str(_str.c_str(), _str.size()), pos(0) {}
      inline json_parser(const stringview& _str) : str(_str), pos(0) {}

      inline char peak() { return str[pos]; }
      inline char eof() { return pos == str.size(); }
      inline char eat_one() { return pos++; }

      template <typename E>
      inline void format_error(E&) {}

      template <typename E, typename T1, typename... T>
      inline void format_error(E& err, T1 a, T... args)
      {
        err << a;
        format_error(err, args...);
      }

      template <typename... T>
      inline std::runtime_error json_error(T... message)
      {
        std::stringstream err;

        int w = 20;
        int b = pos > w ? pos - w : 0;
        int e = pos < int(str.size()) - w ? pos + w : int(str.size()) - 1;
        std::string near(str.data() + b, str.data() + e);
        err << std::endl << "Json parse error near " << near << std::endl;
        err << "                      ";
        for (int i = 0; i < pos - b - 1; i++) err << ' ';
        err << "^^^"<< std::endl;
        format_error(err, message...);
        return std::runtime_error(err.str());
      }

      inline json_parser& fill(std::string& t)
      {
        int start = pos;
        int end = pos;
        t.clear();

        char buffer[128];
        int buffer_pos = 0;
        auto flush = [&] () { t.append(buffer, buffer_pos); buffer_pos = 0; };
        auto append_char = [&] (char c)
        {
          if (buffer_pos == sizeof(buffer)) flush();

          buffer[buffer_pos] = c;
          buffer_pos++;
        };
        auto append_str = [&] (const char* str, int len)
        {
          if (buffer_pos + len > int(sizeof(buffer))) flush();
          memcpy(buffer + buffer_pos, str, len);
          buffer_pos += len;
        };

        while (true)
        {
          while (!eof() and str[end] != '"' and str[end] != '\\')
            end++;

          if (eof()) throw json_error("Unexpected end of string when parsing a string.");
          append_str(str.data() + start, end - start);

          if (str[end] == '"') break;

          end++;
          switch (str[end])
          {
          case '\'': append_char('\''); break;
          case '"': append_char('"'); break;
          case '\\': append_char('\\'); break;
          case '/': append_char('/'); break;
          case 'n': append_char('\n'); break;
          case 'r': append_char('\r'); break;
          case 't': append_char('\t'); break;
          case 'b': append_char('\b'); break;
          case 'f': append_char('\f'); break;
          case 'v': append_char('\v'); break;
          case '0': append_char('\0'); break;
          case 'u':
            while (true)
            {
              if (str.size() < end + 4)
                throw json_error("Unexpected end of string when decoding an utf8 character");
              end++;

              auto decode_hex_c = [this] (char c) {
                if (c >= '0' and c <= '9') return c - '0';
                else return (10 + c - 'A');
              };

              const char* str2 = str.data() + end;
              char x = (decode_hex_c(str2[0]) << 4) + decode_hex_c(str2[1]);
              if (x) append_char(x);
              append_char((decode_hex_c(str2[2]) << 4) + decode_hex_c(str2[3]));

              end += 4;

              if (str[end] == '\\' and str[end + 1] == 'u')
                end += 1;
              else break;
            }
            break;
          }

          start = end;
        }
        flush();
        pos = end;
        return *this;
      }

      inline json_parser& fill(stringview& t)
      {
        int start = pos;
        int end = pos;

        while (!eof() and str[end] != '"' and str[end - 1] != '\\') end++;
        t.str = str.data() + start;
        t.len = end - start;
        pos = end;
        return *this;
      }

      template <typename I, int N>
      inline json_parser& fill_int(I& val)
      {
        int sign = 1;
        if (std::is_signed<I>::value and str[pos] == '-') { sign = -1; eat_one(); }
        else if (str[pos] == '+') { eat_one(); }

        int end = pos;

        val = 0;

        const char* s = str.data() + pos;

        for (int i = 0; i < N; i++)
        {
          if (s[i] < '0' or s[i] > '9') break;
          val = val * 10 + (s[i] - '0');
          end++;
        }
        val *= sign;

        if (end == pos) throw json_error("Could not find the expected number.");

        pos = end;
        return *this;
      }

      inline json_parser& fill(float& val)
      {
        float res = 0;

        int ent = 0;
        fill_int<int, 10>(ent);

        res = ent;

        if (str[pos] == '.')
        {
          eat_one();
          unsigned int floating = 0;
          int start = pos;
          fill_int<unsigned int, 10>(floating);
          int end = pos;
          res += float(floating) / pow_10(end - start);
        }

        if (str[pos] == 'e')
        {
          eat_one();
          int exp = 0;
          fill_int<int, 10>(exp);
          res *= pow_10(exp);
        }

        val = res;
        return *this;
      }

      inline json_parser& fill(int& val) { return fill_int<int, 10>(val); }
      inline json_parser& fill(unsigned int& val) { return fill_int<unsigned int, 10>(val); }

      template <typename T>
      inline json_parser& fill(T& t)
      {
        int end = pos;
        while(!eof() and str[end] != ',' and str[end] != '}' and str[end] != ']') end++;
        t = boost::lexical_cast<std::remove_reference_t<T>>(str.data() + pos, end - pos);
        pos = end;
        return *this;
      }

      template <typename T>
      inline json_parser& operator>>(fill_<T>&& t)
      {
        return fill(t.r);
      }

      inline json_parser& operator>>(char t)
      {
        if (!eof() and str[pos] == t)
        {
          pos++;
          return *this;
        }
        else
        {
          if (eof())
            throw json_error("Expected ", t, " got eof");
          else
            throw json_error("Expected ", t, " got ", str[pos]);
        }
      }

      inline json_parser& operator>>(const char* t)
      {
        int start = pos;
        int end = pos;
        while (!eof() and (t[end - start] == str[end] or str[end] != '"')) end++;

        if (t[end - start] == '\0')
        {
          pos = end;
          return *this;
        }
        else
          throw json_error("Expected ", t, " got something else.");
      }

      inline json_parser& operator>>(spaces_)
      {
        while (!eof() and std::isspace(str[pos])) pos++;
        return *this;
      }

      int line_cpt, char_cpt;
      const char* cur;
      stringview str;
      int pos;
    };

    inline void iod_attr_from_json(sio<>&, json_parser&)
    {
    }

    template <typename T>
    inline void iod_from_json_(T& t, json_parser& p)
    {
      p >> fill(t);
    }

    inline void iod_from_json_(std::string& t, json_parser& p)
    {
      p >> '"' >> fill(t) >> '"';
    }

    inline void iod_from_json_(stringview& t, json_parser& p)
    {
      p >> '"' >> fill(t) >> '"';
    }

    // Parse a json hashmap ordered the field in the object \o.
    template <typename T, typename ...Tail>
    inline void iod_attr_from_json_strict(sio<T, Tail...>& o, json_parser& p)
    {
      T* attr = &o;

      p >> p.spaces >> '"' >> attr->symbol().name() >> '"' >> p.spaces >> ':';

      iod_from_json_(attr->value(), p);
      if (sizeof...(Tail) != 0)
        p >> p.spaces >> ',';

      iod_attr_from_json(*static_cast<sio<Tail...>*>(&o), p);
    }

    // Parse a json hashmap.
    template <typename T, typename ...Tail>
    inline void iod_attr_from_json(sio<T, Tail...>& o, json_parser& p)
    {
      p >> p.spaces;

      struct attr_info { bool filled; stringview name; };

      attr_info A[std::remove_reference_t<decltype(o)>::size()];

      int i = 0;
      foreach(o) | [&] (auto& m)
      {
        A[i].filled = false;
        stringview name(m.symbol().name(), strlen(m.symbol().name()));
        if (m.attributes().has(_json_key))
        {
          const char* new_name = m.attributes().get(_json_key, _json_key).name();
          name = stringview(new_name, strlen(new_name));
        }
        A[i].name = name;
        i++;
      };

      while (p.peak() != '}')
      {
        stringview attr_name;
        p >> p.spaces >> '"' >> fill(attr_name) >> '"' >> p.spaces >> ':' >> p.spaces;

        int i = 0;
        bool attr_found = false;
        foreach(o) | [&] (auto& m)
        {
          if (!m.attributes().has(_json_skip) and !attr_found and attr_name == A[i].name)
          {
            iod_from_json_(m.value(), p);
            A[i].filled = true;
            attr_found = true;
          }
          i++;
        };
        // Fixme: if !attr_found, skip the json value.
        p >> p.spaces;
        if (p.peak() == ',')
          p.eat_one();
        else
          break;
      }

      if (p.peak() != '}')
      {
        throw p.json_error("Expected } got ", p.peak());
      }

      i = 0;
      foreach(o) | [&] (auto& m) {
        if (!m.attributes().has(_json_skip) and !m.attributes().has(_optional) and !A[i].filled)
          throw std::runtime_error(std::string("json_decode error: missing field ") +
                                   m.symbol().name());
        i++;
      };
    }

    // Parse an array.
    template <typename T>
    inline void iod_from_json_(std::vector<T>& array, json_parser& p)
    {
      p >> '[' >> p.spaces;
      if (p.peak() == ']')
      {
        p >> ']';
        return;
      }

      array.clear();
      while (p.peak() != ']')
      {
        T t;
        iod_from_json_(t, p);
        array.push_back(t);
        p >> p.spaces;
        if (p.peak() == ']')
          break;
        else
          p >> ',';
      }

      p >> ']';
    }

    template <typename ...Tail>
    inline void iod_from_json_(sio<Tail...>& o, json_parser& p)
    {
      p >> p.spaces >> '{';
      iod_attr_from_json(o, p);
      p >> p.spaces >> '}';
    }

    template <typename ...Tail>
    inline void iod_from_json_(sio<Tail...>& o, const std::string& str)
    {
      json_parser p(str);
      if (str.size() > 0)
        iod_from_json_(o, p);
      else
        throw std::runtime_error("Empty string.");
    }
  }

  template <typename ...Tail>
  inline void json_decode(sio<Tail...>& o, const stringview& str, int& n_read)
  {
    if (o.size() == 0) return;
    json_internals::json_parser p(str);
    if (str.size() > 0)
      iod_from_json_(o, p);
    else
      throw std::runtime_error("Empty string.");
    n_read = p.pos;
  }

  template <typename ...Tail>
  inline void json_decode(sio<Tail...>& o, const stringview& str)
  {
    if (o.size() == 0) return;
    json_internals::json_parser p(str);
    if (str.size() > 0)
      iod_from_json_(o, p);
    else
      throw std::runtime_error("Empty string.");
  }

  template <typename ...Tail>
  inline void json_decode(sio<Tail...>& o, std::istringstream& stream)
  {
    if (o.size() == 0) return;
    json_internals::json_parser p(stream);
    if (stream.str().size() > 0)
      iod_from_json_(o, p);
    else
      throw std::runtime_error("Empty string.");
  }

  template <typename ...Tail>
  inline std::string json_encode(const sio<Tail...>& o)
  {
    std::stringstream ss;
    json_internals::json_encode_(o, ss);
    return ss.str();
  }

  template <typename S, typename ...Tail>
  inline void json_encode(const sio<Tail...>& o, S& stream)
  {
    json_internals::json_encode_(o, stream);
  }

  template <typename T>
  inline std::string json_encode(const std::vector<T>& v)
  {
    std::stringstream ss;
    json_internals::json_encode_(v, ss);
    return ss.str();
  }

}

#endif

ext: hh

max_stars_repo_name: pdebus/MTVMTL

max_issues_repo_name: pdebus/MTVMTL

max_forks_repo_name: pdebus/MTVMTL