text:
// Copyright 2018 The Beam Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "base_tx_builder.h"
#include "core/block_crypt.h"
#include "core/shielded.h"
#include "base_transaction.h"
#include "strings_resources.h"
#include "utility/executor.h"

// TODO: getrandom not available until API 28 in the Android NDK 17b
// https://github.com/boostorg/uuid/issues/76
#if defined(__ANDROID__)
#define BOOST_UUID_RANDOM_PROVIDER_DISABLE_GETRANDOM 1
#endif

#include <boost/uuid/uuid_generators.hpp>
#include <numeric>
#include "utility/logger.h"

namespace beam::wallet
{
    using namespace ECC;
    using namespace std;

    ///////////////////////////////////////
    // BaseTxBuilder::KeyKeeperHandler

    BaseTxBuilder::KeyKeeperHandler::KeyKeeperHandler(BaseTxBuilder& b, Stage& s)
    {
        m_pBuilder = b.weak_from_this();

        m_pStage = &s;
        assert(Stage::None == s);
        s = Stage::InProgress;
    }

    BaseTxBuilder::KeyKeeperHandler::~KeyKeeperHandler()
    {
        if (m_pStage)
        {
            std::shared_ptr<BaseTxBuilder> pBld = m_pBuilder.lock();
            if (pBld)
                Detach(*pBld, Stage::None);
        }
    }

    void BaseTxBuilder::KeyKeeperHandler::Detach(BaseTxBuilder&, Stage s)
    {
        if (m_pStage)
        {
            assert(Stage::InProgress == *m_pStage);
            *m_pStage = s;
            m_pStage = nullptr;
        }
    }

    void BaseTxBuilder::KeyKeeperHandler::OnDone(IPrivateKeyKeeper2::Status::Type n)
    {
        if (m_pStage)
        {
            std::shared_ptr<BaseTxBuilder> pBld = m_pBuilder.lock();
            if (pBld)
            {
                BaseTxBuilder& b = *pBld;
                if (IPrivateKeyKeeper2::Status::Success == n)
                {
                    ITransaction::Ptr pGuard(b.m_Tx.shared_from_this()); // extra ref on transaction object.
                    // Otherwise it can crash in Update() -> CompleteTx(), which will remove its ptr from live tx map

                    try {
                        OnSuccess(b);
                    }
                    catch (const TransactionFailedException& ex) {
                        Detach(b, Stage::None);
                        pBld->m_Tx.OnFailed(ex.GetReason(), ex.ShouldNofify());
                    }
                }
                else
                    OnFailed(b, n);
            }
            else
                m_pStage = nullptr;
        }
    }

    void BaseTxBuilder::KeyKeeperHandler::OnFailed(BaseTxBuilder& b, IPrivateKeyKeeper2::Status::Type n)
    {
        Detach(b, Stage::None);
        b.m_Tx.OnFailed(BaseTransaction::KeyKeeperErrorToFailureReason(n), true);
    }

    void BaseTxBuilder::KeyKeeperHandler::OnAllDone(BaseTxBuilder& b)
    {
        Detach(b, Stage::Done);
        b.m_Tx.Update(); // may complete transaction
    }

    ///////////////////////////////////////
    // BaseTxBuilder::Coins
    void BaseTxBuilder::Coins::AddOffset(ECC::Scalar::Native& kOffs, const Key::IKdf::Ptr& pMasterKdf) const
    {
        ECC::Scalar::Native sk;
        for (const CoinID& cid : m_Input)
        {
            CoinID::Worker(cid).Create(sk, *cid.get_ChildKdf(pMasterKdf));
            kOffs += sk;
        }

        for (const auto& si : m_InputShielded)
        {
            si.get_SkOut(sk, si.m_Fee, *pMasterKdf);
            kOffs += sk;
        }

        kOffs = -kOffs;

        for (const CoinID& cid : m_Output)
        {
            CoinID::Worker(cid).Create(sk, *cid.get_ChildKdf(pMasterKdf));
            kOffs += sk;
        }

        kOffs = -kOffs;
    }

    ///////////////////////////////////////
    // BaseTxBuilder::Balance

    BaseTxBuilder::Balance::Balance(BaseTxBuilder& b)
        :m_Builder(b)
    {
    }

    void BaseTxBuilder::Balance::Add(const Coin::ID& cid, bool bOutp)
    {
        (bOutp ? m_Builder.m_Coins.m_Output : m_Builder.m_Coins.m_Input).push_back(cid);

        Entry& x = m_Map[cid.m_AssetID];
        if (bOutp)
            x.m_Value -= cid.m_Value;
        else
            x.m_Value += cid.m_Value;
    }

    void BaseTxBuilder::Balance::Add(const IPrivateKeyKeeper2::ShieldedInput& si)
    {
        m_Builder.m_Coins.m_InputShielded.push_back(si);

        m_Map[si.m_AssetID].m_Value += si.m_Value;
        m_Map[0].m_Value -= si.m_Fee;
    }

    void BaseTxBuilder::Balance::Add(const ShieldedTxo::ID& sid)
    {
        auto& fs = Transaction::FeeSettings::get(m_Builder.m_Height.m_Min);

        IPrivateKeyKeeper2::ShieldedInput si;
        Cast::Down<ShieldedTxo::ID>(si) = sid;
        si.m_Fee = fs.m_ShieldedInputTotal; // auto-fee for shielded inputs
        Add(si);
    }

    void BaseTxBuilder::Balance::CreateOutput(Amount val, Asset::ID aid, Key::Type type)
    {
        Coin newUtxo = m_Builder.m_Tx.GetWalletDB()->generateNewCoin(val, aid);
        newUtxo.m_ID.m_Type = type;

        newUtxo.m_createTxId = m_Builder.m_Tx.GetTxID();
        m_Builder.m_Tx.GetWalletDB()->storeCoin(newUtxo);

        Add(newUtxo.m_ID, true);
    }

    void BaseTxBuilder::Balance::AddPreselected()
    {
        CoinIDList cidl;
        m_Builder.GetParameter(TxParameterID::PreselectedCoins, cidl);

        for (const auto& cid : cidl)
            Add(cid, false);
    }

    void BaseTxBuilder::Balance::CompleteBalance()
    {
        std::set<Asset::ID> setRcv; // those auto-created coins should be 'Regular' instead of 'Change'
        for (const auto& v : m_Map)
        {
            if (v.second.m_Value > 0)
                setRcv.insert(v.first);
        }

        AddPreselected();

        uint32_t nNeedInputs = 0;
        for (const auto& v : m_Map)
        {
            if (v.second.m_Value < 0)
                nNeedInputs++;
        }

        if (nNeedInputs)
        {
            if (m_Map[0].m_Value >= 0)
                nNeedInputs++; // although there's no dificiency in def asset, assume it may arise due to involuntary fees

            // go by asset type in reverse order, to reach the def asset last
            for (auto it = m_Map.rbegin(); m_Map.rend() != it; it++)
            {
                AmountSigned& val = it->second.m_Value;
                if (val >= 0)
                    continue;

                uint32_t nShieldedMax = Rules::get().Shielded.MaxIns;
                uint32_t nShieldedInUse = static_cast<uint32_t>(m_Builder.m_Coins.m_InputShielded.size()); // preselected?

                if (nShieldedMax <= nShieldedInUse)
                    nShieldedMax = 0;
                else
                    nShieldedMax -= nShieldedInUse;

                assert(nNeedInputs);
                nShieldedMax = (nShieldedMax + nNeedInputs - 1) / nNeedInputs; // leave some reserve to other assets

                Asset::ID aid = it->first;

                std::vector<Coin> vSelStd;
                std::vector<ShieldedCoin> vSelShielded;
                m_Builder.m_Tx.GetWalletDB()->selectCoins2(m_Builder.m_Height.m_Min, -val, aid, vSelStd, vSelShielded, nShieldedMax, true);

                for (const auto& c : vSelStd)
                    Add(c.m_ID, false);

                for (const auto& c : vSelShielded)
                    Add(c.m_CoinID);

                if (val < 0)
                {
                    LOG_ERROR()
                        << m_Builder.m_Tx.GetTxID()
                        << "[" << m_Builder.m_SubTxID << "]"
                        << " Missing "
                        << PrintableAmount(Amount(-val), false, aid);

                    throw TransactionFailedException(!m_Builder.m_Tx.IsInitiator(), TxFailureReason::NoInputs);
                }

                nNeedInputs--;
            }

        }

        for (const auto& v : m_Map)
        {
            AmountSigned val = v.second.m_Value;
            assert(val >= 0);

            if (val > 0)
            {
                Asset::ID aid = v.first;

                bool bRcv = setRcv.end() != setRcv.find(aid);
                CreateOutput(val, aid, bRcv ? Key::Type::Regular : Key::Type::Change);
            }

            assert(!v.second.m_Value);
        }
    }

    ///////////////////////////////////////
    // BaseTxBuilder

    BaseTxBuilder::BaseTxBuilder(BaseTransaction& tx, SubTxID subTxID)
        :m_Tx(tx)
        ,m_SubTxID(subTxID)
    {
        GetParameter(TxParameterID::MinHeight, m_Height.m_Min);
        if (!m_Height.m_Min)
        {
            // automatically set current height
            Block::SystemState::Full s;
            if (m_Tx.GetTip(s))
                SaveAndStore(m_Height.m_Min, TxParameterID::MinHeight, s.m_Height);

        }

        GetParameter(TxParameterID::InputCoins, m_Coins.m_Input);
        GetParameter(TxParameterID::InputCoinsShielded, m_Coins.m_InputShielded);
        GetParameter(TxParameterID::OutputCoins, m_Coins.m_Output);

        m_pTransaction = std::make_shared<Transaction>();
        auto& trans = *m_pTransaction; // alias

        GetParameter(TxParameterID::Inputs, trans.m_vInputs);
        GetParameter(TxParameterID::ExtraKernels, trans.m_vKernels);
        GetParameter(TxParameterID::Outputs, trans.m_vOutputs);

        if (!GetParameter(TxParameterID::Offset, trans.m_Offset))
            trans.m_Offset = Zero;

        GetParameter(TxParameterID::MaxHeight, m_Height.m_Max);
        GetParameter(TxParameterID::Fee, m_Fee);

        bool bNoInOuts = trans.m_vInputs.empty() && trans.m_vOutputs.empty() && !wallet::GetShieldedInputsNum(trans.m_vKernels);
        m_GeneratingInOuts = bNoInOuts ? Stage::None : Stage::Done;

        GetParameter(TxParameterID::MutualTxState, m_Status);

        TxKernel::Ptr pKrn;
        GetParameter(TxParameterID::Kernel, pKrn);
        if (pKrn)
        {
            AddKernel(std::move(pKrn));

            if (Status::FullTx == m_Status)
                trans.NormalizeE(); // everything else must have already been normalized
        }
    }

    void BaseTxBuilder::SaveCoins()
    {
        bool bAssets = false;

        for (const auto& cid : m_Coins.m_Input)
            if (cid.m_AssetID)
                bAssets = true;

        for (const auto& si : m_Coins.m_InputShielded)
            if (si.m_AssetID)
                bAssets = true;

        for (const auto& cid : m_Coins.m_Output)
            if (cid.m_AssetID)
                bAssets = true;

        if (bAssets)
            VerifyAssetsEnabled();

        SetParameter(TxParameterID::InputCoins, m_Coins.m_Input);
        SetParameter(TxParameterID::InputCoinsShielded, m_Coins.m_InputShielded);
        SetParameter(TxParameterID::OutputCoins, m_Coins.m_Output);

        // tag inputs
        for (const auto& cid : m_Coins.m_Input)
        {
            Coin coin;
            coin.m_ID = cid;
            if (m_Tx.GetWalletDB()->findCoin(coin) && coin.m_status == Coin::Status::Available)
            {
                coin.m_spentTxId = m_Tx.GetTxID();
                m_Tx.GetWalletDB()->saveCoin(coin);
            }
            else
            {
                auto message("Coin with ID: " + toString(coin.m_ID) + " is unreachable.");
                LOG_ERROR() << message;
                throw TransactionFailedException(true, TxFailureReason::NoInputs, message.c_str());
            }
        }

        for (auto& cid : m_Coins.m_InputShielded)
        {
            auto pCoin = m_Tx.GetWalletDB()->getShieldedCoin(cid.m_Key);
            if (pCoin)
            {
                pCoin->m_spentTxId = m_Tx.GetTxID();
                m_Tx.GetWalletDB()->saveShieldedCoin(*pCoin);
            }
        }
    }

    bool BaseTxBuilder::IsGeneratingInOuts() const
    {
        return (Stage::InProgress == m_GeneratingInOuts);
    }

    bool BaseTxBuilder::IsSigning() const
    {
        return (Stage::InProgress == m_Signing);
    }

    template <typename TDst, typename TSrc>
    void MoveIntoVec1(std::vector<TDst>& vDst, std::vector<TSrc>& vSrc)
    {
        std::move(vSrc.begin(), vSrc.end(), back_inserter(vDst));
    }

    template <typename T>
    void MoveIntoVec(std::vector<T>& vDst, std::vector<T>& vSrc)
    {
        if (vDst.empty())
            vDst = std::move(vSrc);
        else
            MoveIntoVec1(vDst, vSrc);
    }

    struct BaseTxBuilder::HandlerInOuts
        :public KeyKeeperHandler
        ,public std::enable_shared_from_this<HandlerInOuts>
    {
        using KeyKeeperHandler::KeyKeeperHandler;

        virtual ~HandlerInOuts() {} // auto

        struct Outputs
        {
            std::vector<IPrivateKeyKeeper2::Method::CreateOutput> m_vMethods;
            std::vector<Output::Ptr> m_Done;

            bool IsAllDone() const { return m_vMethods.size() == m_Done.size(); }

            bool OnNext()
            {
                size_t iDone = m_Done.size();
                assert(m_vMethods[iDone].m_pResult);
                m_Done.push_back(std::move(m_vMethods[iDone].m_pResult));
                return true;
            }

        } m_Outputs;

        struct Inputs
        {
            struct CoinPars :public IPrivateKeyKeeper2::Method::get_Kdf {
                CoinID m_Cid;
            };

            std::vector<CoinPars> m_vMethods;
            std::vector<Input::Ptr> m_Done;

            bool IsAllDone() const { return m_vMethods.size() == m_Done.size(); }

            bool OnNext()
            {
                size_t iDone = m_Done.size();
                CoinPars& c = m_vMethods[iDone];

                if (!c.m_pPKdf)
                    return false;

                Point::Native comm;
                CoinID::Worker(c.m_Cid).Recover(comm, *c.m_pPKdf);

                m_Done.emplace_back();
                m_Done.back().reset(new Input);
                m_Done.back()->m_Commitment = comm;

                return true;
            }

        } m_Inputs;

        struct InputsShielded
        {
            struct MyList
                :public Sigma::CmList
            {
                std::vector<ECC::Point::Storage> m_vec;

                ECC::Point::Storage* m_p0;
                uint32_t m_Skip;

                virtual bool get_At(ECC::Point::Storage& res, uint32_t iIdx) override
                {
                    if (iIdx < m_Skip)
                    {
                        res.m_X = Zero;
                        res.m_Y = Zero;
                    }
                    else
                        res = m_p0[iIdx - m_Skip];

                    return true;
                }
            };

            IPrivateKeyKeeper2::Method::CreateInputShielded m_Method;
            MyList m_Lst;

            std::vector<TxKernelShieldedInput::Ptr> m_Done;

            TxoID m_Wnd0;
            uint32_t m_N;
            uint32_t m_Count;

            bool IsAllDone(BaseTxBuilder& b) const { return b.m_Coins.m_InputShielded.size() == m_Done.size(); }

            bool OnNext(BaseTxBuilder& b)
            {
                m_Done.push_back(std::move(m_Method.m_pKernel));
                return MoveNextSafe(b);
            }

            bool MoveNextSafe(BaseTxBuilder&);
            bool OnList(BaseTxBuilder&, proto::ShieldedList& msg);

            IMPLEMENT_GET_PARENT_OBJ(HandlerInOuts, m_InputsShielded)

        } m_InputsShielded;

        void CheckAllDone(BaseTxBuilder& b)
        {
            if (m_Outputs.IsAllDone() && m_Inputs.IsAllDone() && m_InputsShielded.IsAllDone(b))
            {
                MoveIntoVec(b.m_pTransaction->m_vOutputs, m_Outputs.m_Done);
                MoveIntoVec(b.m_pTransaction->m_vInputs, m_Inputs.m_Done);
                MoveIntoVec1(b.m_pTransaction->m_vKernels, m_InputsShielded.m_Done);

                b.SaveInOuts();

                OnAllDone(b);
            }
        }

        bool OnNext(BaseTxBuilder& b)
        {
            if (!m_Outputs.IsAllDone())
                return m_Outputs.OnNext();

            if (!m_Inputs.IsAllDone())
                return m_Inputs.OnNext();

            assert(!m_InputsShielded.IsAllDone(b));
            return m_InputsShielded.OnNext(b);
        }

        virtual void OnSuccess(BaseTxBuilder& b) override
        {
            if (OnNext(b))
                CheckAllDone(b);
            else
                OnFailed(b, IPrivateKeyKeeper2::Status::Unspecified); // although shouldn't happen
        }
    };

    struct MyWrapper
    {
        const TxKernel::Ptr* m_p0;
        size_t m_Count;
        TxKernel* m_pSkip;

        template <typename Archive>
        void serialize(Archive& ar)
        {
            size_t n = m_Count - (!!m_pSkip);
            ar & n;

            for (size_t i = 0; i < m_Count; i++)
            {
                const auto& pKrn = m_p0[i];
                if (pKrn.get() != m_pSkip)
                    ar & pKrn;
            }
        }
    };

    void BaseTxBuilder::SaveInOuts()
    {
        SetParameter(TxParameterID::Inputs, m_pTransaction->m_vInputs);
        SetParameter(TxParameterID::Outputs, m_pTransaction->m_vOutputs);

        // serialize kernels, except the 'main' one
        const auto& v = m_pTransaction->m_vKernels;

        MyWrapper wr;
        wr.m_pSkip = m_pKrn;
        wr.m_Count = v.size();
        if (wr.m_Count)
            wr.m_p0 = &v.front();

        SetParameter(TxParameterID::ExtraKernels, wr);

    }

    void BaseTxBuilder::GenerateInOuts()
    {
        if (Stage::None != m_GeneratingInOuts)
            return;

        if (m_Coins.IsEmpty())
        {
            m_GeneratingInOuts = Stage::Done;
            return;
        }

        KeyKeeperHandler::Ptr pHandler = std::make_shared<HandlerInOuts>(*this, m_GeneratingInOuts);
        HandlerInOuts& x = Cast::Up<HandlerInOuts>(*pHandler);

        // outputs
        x.m_Outputs.m_vMethods.resize(m_Coins.m_Output.size());
        x.m_Outputs.m_Done.reserve(m_Coins.m_Output.size());
        for (size_t i = 0; i < m_Coins.m_Output.size(); i++)
        {
            x.m_Outputs.m_vMethods[i].m_hScheme = m_Height.m_Min;
            x.m_Outputs.m_vMethods[i].m_Cid = m_Coins.m_Output[i];
            FillUserData(Output::User::ToPacked(x.m_Outputs.m_vMethods[i].m_User));
            m_Tx.get_KeyKeeperStrict()->InvokeAsync(x.m_Outputs.m_vMethods[i], pHandler);
        }

        // inputs
        x.m_Inputs.m_vMethods.resize(m_Coins.m_Input.size());
        x.m_Inputs.m_Done.reserve(m_Coins.m_Input.size());
        for (size_t i = 0; i < m_Coins.m_Input.size(); i++)
        {
            HandlerInOuts::Inputs::CoinPars& c = x.m_Inputs.m_vMethods[i];
            c.m_Cid = m_Coins.m_Input[i];
            c.m_Root = !c.m_Cid.get_ChildKdfIndex(c.m_iChild);
            m_Tx.get_KeyKeeperStrict()->InvokeAsync(x.m_Inputs.m_vMethods[i], pHandler);
        }

        // inputs shielded
        x.m_InputsShielded.m_Done.reserve(m_Coins.m_InputShielded.size());
        if (!x.m_InputsShielded.MoveNextSafe(*this))
            throw TransactionFailedException(true, TxFailureReason::Unknown);
    }

    bool BaseTxBuilder::HandlerInOuts::InputsShielded::MoveNextSafe(BaseTxBuilder& b)
    {
        if (IsAllDone(b))
            return true;

        // currently process inputs 1-by-1
        // don't cache retrieved elements (i.e. ignore overlaps for multiple inputs)
        const IPrivateKeyKeeper2::ShieldedInput& si = b.m_Coins.m_InputShielded[m_Done.size()];
        auto pCoin = b.m_Tx.GetWalletDB()->getShieldedCoin(si.m_Key);
        if (!pCoin)
            return false;
        const ShieldedCoin& c = *pCoin;

        Cast::Down<ShieldedTxo::ID>(m_Method) = c.m_CoinID;

        m_Method.m_pKernel = std::make_unique<TxKernelShieldedInput>();
        m_Method.m_pKernel->m_Fee = si.m_Fee;

        TxoID nShieldedCurrently = b.m_Tx.GetWalletDB()->get_ShieldedOuts();
        std::setmax(nShieldedCurrently, c.m_TxoID + 1); // assume stored shielded count may be inaccurate, the being-spent element must be present

        ShieldedCoin::UnlinkStatus us(c, nShieldedCurrently);

        bool bWndLost = us.IsLargeSpendWindowLost();
        m_Method.m_pKernel->m_SpendProof.m_Cfg = bWndLost ?
            Rules::get().Shielded.m_ProofMin :
            Rules::get().Shielded.m_ProofMax;

        m_N = m_Method.m_pKernel->m_SpendProof.m_Cfg.get_N();
        if (!m_N)
            return false;

        m_Method.m_iIdx = c.get_WndIndex(m_N);

        if (!bWndLost && (us.m_WndReserve0 < 0))
        {
            // move the selected window forward
            m_Method.m_iIdx += us.m_WndReserve0; // actually decrease
            assert(m_Method.m_iIdx < m_N);
        }

        m_Wnd0 = c.m_TxoID - m_Method.m_iIdx;
        m_Count = m_N;

        TxoID nWndEnd = m_Wnd0 + m_N;
        if (nWndEnd > nShieldedCurrently)
        {
            // move the selected window backward
            uint32_t nExtra = static_cast<uint32_t>(nWndEnd - nShieldedCurrently);
            if (nExtra < m_Wnd0)
                m_Wnd0 -= nExtra;
            else
            {
                nExtra = static_cast<uint32_t>(m_Wnd0);
                m_Wnd0 = 0;
            }

            m_Method.m_iIdx += nExtra;
            m_Count += nExtra;
        }

        LOG_INFO() << "ShieldedInput window N=" << m_N << ", Wnd0=" << m_Wnd0 << ", iIdx=" << m_Method.m_iIdx << ", TxoID=" << c.m_TxoID << ", PoolSize=" << nShieldedCurrently;

        b.m_Tx.GetGateway().get_shielded_list(b.m_Tx.GetTxID(), m_Wnd0, m_Count,
            [pHandler = get_ParentObj().shared_from_this(), weakTx = b.m_Tx.weak_from_this()](TxoID, uint32_t, proto::ShieldedList& msg)
        {
            auto pBldr = pHandler->m_pBuilder.lock();
            if (!pBldr)
                return;
            BaseTxBuilder& b = *pBldr;

            auto pTx = weakTx.lock();
            if (!pTx)
                return;

            try {
                if (!pHandler->m_InputsShielded.OnList(b, msg))
                    b.m_Tx.OnFailed(TxFailureReason::Unknown);
            }
            catch (const TransactionFailedException& ex) {
                b.m_Tx.OnFailed(ex.GetReason(), ex.ShouldNofify());
            }
        });

        return true;
    }

    bool BaseTxBuilder::HandlerInOuts::InputsShielded::OnList(BaseTxBuilder& b, proto::ShieldedList& msg)
    {
        if (msg.m_Items.size() > m_Count)
        {
            LOG_ERROR() << "ShieldedList message returned more coins than requested " << TRACE(msg.m_Items.size()) << TRACE(m_Count);
            return false;
        }

        uint32_t nItems = static_cast<uint32_t>(msg.m_Items.size());

        m_Lst.m_p0 = &msg.m_Items.front();
        m_Lst.m_Skip = 0;
        m_Lst.m_vec.swap(msg.m_Items);

        m_Method.m_pList = &m_Lst;
        m_Method.m_pKernel->m_NotSerialized.m_hvShieldedState = msg.m_State1;

        m_Method.m_pKernel->m_Height = b.m_Height;
        m_Method.m_pKernel->m_WindowEnd = m_Wnd0 + nItems;

        if (nItems > m_N)
        {
            uint32_t nDelta = nItems - m_N;
            m_Lst.m_p0 += nDelta;

            assert(m_Method.m_iIdx >= nDelta);
            m_Method.m_iIdx -= nDelta;
        }

        if (nItems < m_N)
        {
            if (m_Wnd0 || (nItems <= m_Method.m_iIdx))
            {
                LOG_ERROR() << "ShieldedList message returned unexpected data " << TRACE(m_Wnd0) << TRACE(nItems) << TRACE(m_Method.m_iIdx);
                return false;
            }

            uint32_t nDelta = m_N - nItems;
            m_Lst.m_Skip = nDelta;

            m_Method.m_iIdx += nDelta;
            assert(m_Method.m_iIdx < m_N);
        }

        b.m_Tx.get_KeyKeeperStrict()->InvokeAsync(m_Method, get_ParentObj().shared_from_this());

        return true;
    }

    void BaseTxBuilder::AddOffset(const ECC::Scalar& k1)
    {
        AddOffset(ECC::Scalar::Native(k1));
    }

    void BaseTxBuilder::AddOffset(const ECC::Scalar::Native& k1)
    {
        ECC::Scalar::Native k(m_pTransaction->m_Offset);
        k += k1;
        m_pTransaction->m_Offset = k;

        SetParameter(TxParameterID::Offset, m_pTransaction->m_Offset);
    }

    bool BaseTxBuilder::Aggregate(ECC::Point& ptDst, const ECC::Point::Native& ptSrcN)
    {
        ECC::Point::Native pt;
        if (!pt.Import(ptDst))
            return false;

        pt += ptSrcN;
        pt.Export(ptDst);
        return true;
    }

    bool BaseTxBuilder::Aggregate(ECC::Point& ptDst, ECC::Point::Native& ptSrcN, const ECC::Point& ptSrc)
    {
        return
            ptSrcN.Import(ptSrc) &&
            Aggregate(ptDst, ptSrcN);
    }

    void BaseTxBuilder::SaveKernel()
    {
        // this is ugly hack, but should work without extra code generated
        SetParameter(TxParameterID::Kernel, Cast::Reinterpret<TxKernel::Ptr>(m_pKrn));
    }

    void BaseTxBuilder::SaveKernelID()
    {
        assert(m_pKrn);
        SetParameter(TxParameterID::KernelID, m_pKrn->m_Internal.m_ID);
    }

    void BaseTxBuilder::SetInOuts(IPrivateKeyKeeper2::Method::TxCommon& m)
    {
        m.m_vInputs = m_Coins.m_Input;
        m.m_vOutputs = m_Coins.m_Output;
        m.m_vInputsShielded = m_Coins.m_InputShielded;

        m.m_NonConventional = !IsConventional();
    }

    void BaseTxBuilder::SetCommon(IPrivateKeyKeeper2::Method::TxCommon& m)
    {
        BaseTxBuilder::SetInOuts(m);
        m.m_pKernel.reset(new TxKernelStd);
        m.m_pKernel->m_Fee = m_Fee;
        m.m_pKernel->m_Height = m_Height;
    }

    void BaseTxBuilder::VerifyTx()
    {
        TxBase::Context::Params pars;
        TxBase::Context ctx(pars);
        ctx.m_Height.m_Min = m_Height.m_Min;
        // TODO:DEX set to false, other side will not see it!!!
        if (!m_pTransaction->IsValid(ctx))
            throw TransactionFailedException(false, TxFailureReason::InvalidTransaction);
    }

    void BaseTxBuilder::VerifyAssetsEnabled()
    {
        TxFailureReason res = CheckAssetsEnabled(m_Height.m_Min);
        if (TxFailureReason::Count != res)
            throw TransactionFailedException(!m_Tx.IsInitiator(), res);
    }

    bool BaseTxBuilder::SignTx()
    {
        return true;
    }

    void BaseTxBuilder::FinalyzeTx()
    {
        if (Status::FullTx == m_Status)
            return;

        assert(!IsGeneratingInOuts());

        FinalizeTxInternal();
    }

    void BaseTxBuilder::FinalizeTxInternal()
    {
        m_pTransaction->Normalize();
        VerifyTx();

        SaveInOuts();

        SetStatus(Status::FullTx);
    }

    void BaseTxBuilder::AddKernel(TxKernel::Ptr&& pKrn)
    {
        m_pKrn = pKrn.get();
        m_pTransaction->m_vKernels.push_back(std::move(pKrn));
    }

    void BaseTxBuilder::SetStatus(Status::Type s)
    {
        SaveAndStore(m_Status, TxParameterID::MutualTxState, s);
    }

    void BaseTxBuilder::FillUserData(Output::User::Packed* user)
    {
        user->m_TxID = Blob(m_Tx.GetTxID().data(), (uint32_t)m_Tx.GetTxID().size());
        user->m_Fee = m_Fee;
    }

    string BaseTxBuilder::GetKernelIDString() const
    {
        Merkle::Hash kernelID;
        GetParameter(TxParameterID::KernelID, kernelID);
        char sz[Merkle::Hash::nTxtLen + 1];
        kernelID.Print(sz);
        return string(sz);
    }

    void BaseTxBuilder::CheckMinimumFee(const TxStats* pFromPeer /* = nullptr */)
    {
        // after 1st fork fee should be >= minimal fee
        if (Rules::get().pForks[1].m_Height <= m_Height.m_Min)
        {
            Amount feeInps = 0;
            for (const auto& si : m_Coins.m_InputShielded)
                feeInps += si.m_Fee; // account for shielded inputs, they carry their fee individually

            TxStats ts;
            ts.m_Outputs = static_cast<uint32_t>(m_Coins.m_Output.size());
            ts.m_InputsShielded = static_cast<uint32_t>(m_Coins.m_InputShielded.size());
            ts.m_Kernels = 1 + static_cast<uint32_t>(m_Coins.m_InputShielded.size());

            if (pFromPeer)
                ts += *pFromPeer;

            auto& fs = Transaction::FeeSettings::get(m_Height.m_Min);
            Amount minFee = fs.Calculate(ts);

            if (m_Fee + feeInps < minFee)
            {
                stringstream ss;
                ss << "The minimum fee must be: " << (minFee - feeInps) << " .";
                throw TransactionFailedException(false, TxFailureReason::FeeIsTooSmall, ss.str().c_str());
            }
        }
    }

    ///////////////////////////////////////
    // SimpleTxBuilder

    SimpleTxBuilder::SimpleTxBuilder(BaseTransaction& tx, SubTxID subTxID)
        : BaseTxBuilder(tx, subTxID)
        , m_Lifetime(kDefaultTxLifetime)
    {
        GetParameter(TxParameterID::Amount, m_Amount);
        GetParameter(TxParameterID::AssetID, m_AssetID);
        GetParameter(TxParameterID::Lifetime, m_Lifetime);
    }

    void SimpleTxBuilder::SignSplit()
    {
        if (Stage::None != m_Signing)
            return;

        struct MyHandler
            :public KeyKeeperHandler
        {
            using KeyKeeperHandler::KeyKeeperHandler;

            IPrivateKeyKeeper2::Method::SignSplit m_Method;

            virtual ~MyHandler() {} // auto

            virtual void OnSuccess(BaseTxBuilder& b_) override
            {
                SimpleTxBuilder& b = Cast::Up<SimpleTxBuilder>(b_);
                b.AddOffset(m_Method.m_kOffset);
                b.AddKernel(std::move(m_Method.m_pKernel));
                b.SaveKernel();
                b.SaveKernelID();
                b.SetStatus(Status::SelfSigned);

                OnAllDone(b);
            }
        };

        KeyKeeperHandler::Ptr pHandler = std::make_shared<MyHandler>(*this, m_Signing);
        MyHandler& x = Cast::Up<MyHandler>(*pHandler);

        SetCommon(x.m_Method);
        m_Tx.get_KeyKeeperStrict()->InvokeAsync(x.m_Method, pHandler);
    }

    bool SimpleTxBuilder::SignTx()
    {
        GenerateInOuts();
        SignSplit();

        return (m_Status >= Status::SelfSigned) && !IsGeneratingInOuts();
    }

    void SimpleTxBuilder::FillUserData(Output::User::Packed* user)
    {
        BaseTxBuilder::FillUserData(user);
        user->m_Amount = m_Amount;
    }

    ///////////////////////////////////////
    // MutualTxBuilder

    MutualTxBuilder::MutualTxBuilder(BaseTransaction& tx, SubTxID subTxID)
        :SimpleTxBuilder(tx, subTxID)
    {
        GetParameter(TxParameterID::IsSender, m_IsSender);

        Height responseTime = 0;
        Height responseHeight = 0;
        if (GetParameter(TxParameterID::PeerResponseTime, responseTime)
          && !GetParameter(TxParameterID::PeerResponseHeight, responseHeight))
        {
            auto currentHeight = m_Tx.GetWalletDB()->getCurrentHeight();
            // adjust response height, if min height did not set then it should be equal to responce time
            SetParameter(TxParameterID::PeerResponseHeight, responseTime + currentHeight);
        }
    }

    void MutualTxBuilder::CreateKernel(TxKernelStd::Ptr& pKrn)
    {
        pKrn = make_unique<TxKernelStd>();
        pKrn->m_Fee = m_Fee;
        pKrn->m_Height.m_Min = m_Height.m_Min;
        pKrn->m_Height.m_Max = m_Height.m_Max;
        pKrn->m_Commitment = Zero;
        ZeroObject(pKrn->m_Signature);

        // load kernel's extra data
        Hash::Value hv;
        if (GetParameter(TxParameterID::PeerLockImage, hv))
        {
			pKrn->m_pHashLock = make_unique<TxKernelStd::HashLock>();
			pKrn->m_pHashLock->m_IsImage = true;
			pKrn->m_pHashLock->m_Value = hv;
        }

        if (GetParameter(TxParameterID::PreImage, hv))
        {
			pKrn->m_pHashLock = make_unique<TxKernelStd::HashLock>();
			pKrn->m_pHashLock->m_Value = hv;
		}
    }

    void MutualTxBuilder::AddPeerSignature(const ECC::Point::Native& ptNonce, const ECC::Point::Native& ptExc)
    {
        GetParameterStrict(TxParameterID::PeerSignature, m_pKrn->CastTo_Std().m_Signature.m_k);

        if (!m_pKrn->CastTo_Std().m_Signature.IsValidPartial(m_pKrn->m_Internal.m_ID, ptNonce, ptExc))
            throw TransactionFailedException(true, TxFailureReason::InvalidPeerSignature);

    }

    bool MutualTxBuilder::LoadPeerPart(ECC::Point::Native& ptNonce, ECC::Point::Native& ptExc)
    {
        ECC::Point pt;
        return
            GetParameter(TxParameterID::PeerPublicNonce, pt) &&
            ptNonce.Import(pt) &&
            GetParameter(TxParameterID::PeerPublicExcess, pt) &&
            ptExc.Import(pt);
    }

    void MutualTxBuilder::AddPeerOffset()
    {
        ECC::Scalar k;
        if (GetParameter(TxParameterID::PeerOffset, k))
            AddOffset(k);
    }

    void MutualTxBuilder::FinalizeTxInternal()
    {
        // add peer in/out/offs
        AddPeerOffset();

        std::vector<Input::Ptr> vIns;
        if (GetParameter(TxParameterID::PeerInputs, vIns))
            MoveIntoVec(m_pTransaction->m_vInputs, vIns);

        std::vector<Output::Ptr> vOuts;
        if (GetParameter(TxParameterID::PeerOutputs, vOuts))
            MoveIntoVec(m_pTransaction->m_vOutputs, vOuts);

        SimpleTxBuilder::FinalizeTxInternal();
    }

    void MutualTxBuilder::SignSender(bool initial)
    {
        if (Stage::InProgress == m_Signing)
            return;
        m_Signing = Stage::None;

        struct MyHandler
            :public KeyKeeperHandler
        {
            using KeyKeeperHandler::KeyKeeperHandler;

            IPrivateKeyKeeper2::Method::SignSender m_Method;

            virtual ~MyHandler() {} // auto

            virtual void OnSuccess(BaseTxBuilder& b_) override
            {
                MutualTxBuilder& b = Cast::Up<MutualTxBuilder>(b_);

                if (b.m_pKrn)
                {
                    // final, update the signature only
                    b.m_pKrn->CastTo_Std().m_Signature.m_k = m_Method.m_pKernel->m_Signature.m_k;
                    b.AddOffset(m_Method.m_kOffset);

                    b.m_Tx.FreeSlotSafe(); // release it ASAP

                    b.SetStatus(Status::SndFull);
                }
                else
                {
                    // initial
                    b.SetParameter(TxParameterID::UserConfirmationToken, m_Method.m_UserAgreement);

                    b.AddKernel(std::move(m_Method.m_pKernel));
                    b.SetStatus(Status::SndHalf);
                }

                b.SaveKernel();
                OnAllDone(b);
            }
        };

        KeyKeeperHandler::Ptr pHandler = std::make_shared<MyHandler>(*this, m_Signing);
        MyHandler& x = Cast::Up<MyHandler>(*pHandler);
        IPrivateKeyKeeper2::Method::SignSender& m = x.m_Method;

        SetInOuts(m);

        m.m_Slot = m_Tx.GetSlotSafe(true);

        if (GetParameter(TxParameterID::PeerWalletIdentity, m.m_Peer) &&
            GetParameter(TxParameterID::MyWalletIdentity, m.m_MyID))
        {
            // newer scheme
            GetParameterStrict(TxParameterID::MyAddressID, m.m_MyIDKey);
        }
        else
        {
            // legacy. Will fail for trustless key keeper.
            m.m_MyIDKey = 0;

            WalletID widMy, widPeer;
            if (GetParameter(TxParameterID::PeerID, widPeer) && GetParameter(TxParameterID::MyID, widMy))
            {
                m.m_Peer = widPeer.m_Pk;
                m.m_MyID = widMy.m_Pk;
            }
            else
            {
                if (!m.m_NonConventional)
                    throw TransactionFailedException(true, TxFailureReason::NotEnoughDataForProof);

                ZeroObject(m.m_Peer);
                ZeroObject(m.m_MyID);
            }

        }

        ZeroObject(m.m_PaymentProofSignature);
        m.m_UserAgreement = Zero;

        if (initial)
            CreateKernel(m.m_pKernel);
        else
        {
            m_pKrn->Clone(Cast::Reinterpret<TxKernel::Ptr>(m.m_pKernel));

            GetParameter(TxParameterID::UserConfirmationToken, m.m_UserAgreement);
            if (m.m_UserAgreement == Zero)
                throw TransactionFailedException(true, TxFailureReason::FailedToGetParameter);

            GetParameter(TxParameterID::PaymentConfirmation, m.m_PaymentProofSignature);
        }

        m_Tx.get_KeyKeeperStrict()->InvokeAsync(x.m_Method, pHandler);
    }

    void MutualTxBuilder::SignReceiver()
    {
        if (Stage::InProgress == m_Signing)
            return;
        m_Signing = Stage::None;

        struct MyHandler
            :public KeyKeeperHandler
        {
            using KeyKeeperHandler::KeyKeeperHandler;

            IPrivateKeyKeeper2::Method::SignReceiver m_Method;

            virtual ~MyHandler() {} // auto

            void AssignExtractDiff(BaseTxBuilder& b, ECC::Point& dst, const ECC::Point& src, TxParameterID par)
            {
                dst.m_Y ^= 1;

                ECC::Point::Native pt;
                b.Aggregate(dst, pt, src);
                b.SetParameter(par, dst);

                dst = src;
            }

            virtual void OnSuccess(BaseTxBuilder& b_) override
            {
                MutualTxBuilder& b = Cast::Up<MutualTxBuilder>(b_);

                AssignExtractDiff(b, b.m_pKrn->CastTo_Std().m_Commitment, m_Method.m_pKernel->m_Commitment, TxParameterID::PublicExcess);
                AssignExtractDiff(b, b.m_pKrn->CastTo_Std().m_Signature.m_NoncePub, m_Method.m_pKernel->m_Signature.m_NoncePub, TxParameterID::PublicNonce);
                b.m_pKrn->CastTo_Std().m_Signature.m_k = m_Method.m_pKernel->m_Signature.m_k;

                b.m_pKrn->UpdateID();
                b.SaveKernel();
                b.SaveKernelID();
                b.SetStatus(Status::RcvFullHalfSig);

                b.AddOffset(m_Method.m_kOffset);

                if (m_Method.m_MyIDKey)
                    b.SetParameter(TxParameterID::PaymentConfirmation, m_Method.m_PaymentProofSignature);

                OnAllDone(b);
            }
        };

        KeyKeeperHandler::Ptr pHandler = std::make_shared<MyHandler>(*this, m_Signing);
        MyHandler& x = Cast::Up<MyHandler>(*pHandler);
        IPrivateKeyKeeper2::Method::SignReceiver& m = x.m_Method;

        SetInOuts(m);
        m_pKrn->Clone(Cast::Reinterpret<TxKernel::Ptr>(m.m_pKernel));

        m.m_Peer = Zero;
        m.m_MyIDKey = 0;

        GetParameter(TxParameterID::PeerWalletIdentity, m.m_Peer);

        if (m.m_Peer != Zero)
            GetParameter(TxParameterID::MyAddressID, m.m_MyIDKey);

        m_Tx.get_KeyKeeperStrict()->InvokeAsync(x.m_Method, pHandler);
    }

    void MutualTxBuilder::FinalyzeMaxHeight()
    {
        if (MaxHeight != m_Height.m_Max)
            return; // already decided

        GetParameter(TxParameterID::PeerMaxHeight, m_Height.m_Max);
        GetParameter(TxParameterID::Lifetime, m_Lifetime); // refresh it too

        // receiver is allowed to adjust what was suggested by the sender
        if (!m_IsSender && m_Lifetime)
        {
            // we're allowed to adjust peer's sugggested max height
            Block::SystemState::Full s;
            if (m_Tx.GetTip(s))
                m_Height.m_Max = s.m_Height + m_Lifetime;
        }

        // sanity check
        Height hPeerResponce = 0;
        GetParameter(TxParameterID::PeerResponseHeight, hPeerResponce);

        if (hPeerResponce && (m_Height.m_Max > m_Lifetime + hPeerResponce))
            throw TransactionFailedException(true, TxFailureReason::MaxHeightIsUnacceptable);

        SetParameter(TxParameterID::MaxHeight, m_Height.m_Max);
    }

    bool MutualTxBuilder::SignTx()
    {
        GenerateInOuts();

        bool bRes = m_IsSender ?
            SignTxSender() :
            SignTxReceiver();

        if (!bRes)
            m_Tx.UpdateOnNextTip();

        return bRes;
    }

    bool MutualTxBuilder::SignTxSender()
    {
        switch (m_Status)
        {
        case Status::None:
            SignSender(true);
            break;

        case Status::SndHalf:
            {
                SetTxParameter msg;
                msg
                    .AddParameter(TxParameterID::PeerPublicExcess, m_pKrn->CastTo_Std().m_Commitment)
                    .AddParameter(TxParameterID::PeerPublicNonce, m_pKrn->CastTo_Std().m_Signature.m_NoncePub);

                if (m_pKrn->CastTo_Std().m_pHashLock)
                {
                    ECC::Hash::Value lockImage = m_pKrn->CastTo_Std().m_pHashLock->get_Image(lockImage);
                    msg.AddParameter(TxParameterID::PeerLockImage, lockImage);
                }

                SendToPeer(std::move(msg));

                SetStatus(Status::SndHalfSent);
            }
            // no break;

        case Status::SndHalfSent:
            {
                // check if receiver part is ready
                ECC::Point::Native ptNonce, ptExc;
                if (!LoadPeerPart(ptNonce, ptExc))
                    break;

                Aggregate(m_pKrn->CastTo_Std().m_Commitment, ptExc);
                Aggregate(m_pKrn->CastTo_Std().m_Signature.m_NoncePub, ptNonce);

                FinalyzeMaxHeight();
                m_pKrn->m_Height.m_Max = m_Height.m_Max; // can be different from the original

                m_pKrn->UpdateID(); // must be valid already
                SaveKernelID();

                AddPeerSignature(ptNonce, ptExc);

                SaveKernel();

                SetStatus(Status::SndFullHalfSig);

            }
            // no break;

        case Status::SndFullHalfSig:
            SignSender(false);
        }

        return (m_Status >= Status::SndFull) && !IsGeneratingInOuts();

    }

    bool MutualTxBuilder::SignTxReceiver()
    {
        switch (m_Status)
        {
        case Status::None:
            {
                ECC::Point ptNonce, ptExc;

                if (!GetParameter(TxParameterID::PeerPublicNonce, ptNonce) ||
                    !GetParameter(TxParameterID::PeerPublicExcess, ptExc))
                    break;

                FinalyzeMaxHeight();

                TxKernelStd::Ptr pKrn;
                CreateKernel(pKrn);
                pKrn->m_Commitment = ptExc;
                pKrn->m_Signature.m_NoncePub = ptNonce;

                AddKernel(std::move(pKrn));
                SaveKernel();

                SetStatus(Status::RcvHalf);
            }
            // no break;

        case Status::RcvHalf:
            SignReceiver();
            break;

        case Status::RcvFullHalfSig:
            {
                if (IsGeneratingInOuts())
                    break;

                SetTxParameter msg;
                msg
                    .AddParameter(TxParameterID::PeerPublicExcess, GetParameterStrict<ECC::Point>(TxParameterID::PublicExcess))
                    .AddParameter(TxParameterID::PeerPublicNonce, GetParameterStrict<ECC::Point>(TxParameterID::PublicNonce))
                    .AddParameter(TxParameterID::PeerSignature, m_pKrn->CastTo_Std().m_Signature.m_k)
                    .AddParameter(TxParameterID::PeerInputs, m_pTransaction->m_vInputs)
                    .AddParameter(TxParameterID::PeerOutputs, m_pTransaction->m_vOutputs)
                    .AddParameter(TxParameterID::PeerOffset, m_pTransaction->m_Offset);

                Signature sig;
                if (GetParameter(TxParameterID::PaymentConfirmation, sig))
                    msg.AddParameter(TxParameterID::PaymentConfirmation, sig);

                SendToPeer(std::move(msg));

                SetStatus(Status::RcvFullHalfSigSent);
            }

        }

        return (m_Status >= Status::RcvFullHalfSigSent);
    }

    void MutualTxBuilder::FillUserData(Output::User::Packed* user)
    {
        SimpleTxBuilder::FillUserData(user);
        PeerID peerID = Zero;
        m_Tx.GetParameter(TxParameterID::PeerWalletIdentity, peerID);
        user->m_Peer = peerID;
    }
}

max_stars_repo_name: Webonnix/beam

max_issues_repo_name: Webonnix/beam

max_forks_repo_name: Webonnix/beam


text:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <athomerobot/tcpconnector.h>
#include <athomerobot/tcpconnector.hpp>
#include <athomerobot/tcpacceptor.hpp>
#include <athomerobot/tcpstream.hpp>

#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <errno.h>

#include <ros/ros.h>
#include <stdio.h>

#include <boost/thread.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>

using namespace std;
bool App_exit = false;

int receive_image(int socket)
{ // Start function

int buffersize = 0, recv_size = 0,size = 0, read_size, write_size, packet_index =1,stat;

char imagearray[10241],verify = '1';
FILE *image;

//Find the size of the image
do{
stat = read(socket, &size, sizeof(int));
}while(stat<0);

printf("Packet received.\n");
printf("Packet size: %i\n",stat);
printf("Image size: %i\n",size);
printf(" \n");

char buffer[] = "Got it";

//Send our verification signal
do{
stat = write(socket, &buffer, sizeof(int));
}while(stat<0);

printf("Reply sent\n");
printf(" \n");

image = fopen("Capture2.png", "w");

if( image == NULL) {
printf("Error has occurred. Image file could not be opened\n");
return -1; }

//Loop while we have not received the entire file yet

int need_exit = 0;
struct timeval timeout = {10,0};

fd_set fds;
int buffer_fd, buffer_out;

while(recv_size < size) {
//while(packet_index < 2){

    FD_ZERO(&fds);
    FD_SET(socket,&fds);

    buffer_fd = select(FD_SETSIZE,&fds,NULL,NULL,&timeout);

    if (buffer_fd < 0)
       printf("error: bad file descriptor set.\n");

    if (buffer_fd == 0)
       printf("error: buffer read timeout expired.\n");

    if (buffer_fd > 0)
    {
        do{
               read_size = read(socket,imagearray, 10241);
            }while(read_size <0);

            printf("Packet number received: %i\n",packet_index);
        printf("Packet size: %i\n",read_size);

        //Write the currently read data into our image file
         write_size = fwrite(imagearray,1,read_size, image);
         printf("Written image size: %i\n",write_size);

             if(read_size !=write_size) {
                 printf("error in read write\n");    }

             //Increment the total number of bytes read
             recv_size += read_size;
             packet_index++;
             printf("Total received image size: %i\n",recv_size);
             printf(" \n");
             printf(" \n");
    }

}

  fclose(image);
  printf("Image successfully Received!\n");
  return 1;
  }

int receive_image_main(int port){
	int socket_desc;
    struct sockaddr_in server;
    char *parray;

  //Create socket
  socket_desc = socket(AF_INET , SOCK_STREAM , 0);

  if (socket_desc == -1) {
  printf("Could not create socket");
  }

  memset(&server,0,sizeof(server));
  server.sin_addr.s_addr = inet_addr("127.0.0.1");
  server.sin_family = AF_INET;
  server.sin_port = htons(port);

  //Connect to remote server
  if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0) {
  cout<<strerror(errno);
  close(socket_desc);
  puts("Connect Error");
  return 1;
  }

  puts("Connected\n");

  receive_image(socket_desc);

  close(socket_desc);

  return 0;
}

int receive_message_main(int argc,char** argv)
{

    int len;
    string message;
    char line[256];
    TCPConnector* connector = new TCPConnector();
    TCPStream* stream = connector->connect("127.0.0.1", 3300);
    if (stream) {
        message = "Is there life on Mars?";
        stream->send(message.c_str(), message.size());
        printf("sent - %s\n", message.c_str());
        len = stream->receive(line, sizeof(line));
        line[len] = 0;
        printf("received - %s\n", line);
        delete stream;
    }

    stream = connector->connect("127.0.0.1", 3300);
    if (stream) {
        message = "Why is there air?";
        stream->send(message.c_str(), message.size());
        printf("sent - %s\n", message.c_str());
        len = stream->receive(line, sizeof(line));
        line[len] = 0;
        printf("received - %s\n", line);
        delete stream;
    }

}

int main(int argc, char** argv)
{
  ros::init(argc, argv, "tcp_server");
  ros::Time::init();

  cout << "SEPANTA TCP/IP CLIENT STARTED DONE (93/04/02)" << endl;

   receive_message_main(argc,argv);

  ros::Rate ros_rate(20);

  while(ros::ok())
  {
    ros::spinOnce();
    ros_rate.sleep();
  }

  App_exit = true;

  return 0;
}

ext: cpp


text:
#include "mjolnir/graphtilebuilder.h"

#include "midgard/logging.h"
#include "baldr/datetime.h"
#include "baldr/edgeinfo.h"
#include "baldr/tilehierarchy.h"
#include <boost/format.hpp>
#include <boost/filesystem/operations.hpp>
#include <stdexcept>
#include <list>
#include <algorithm>

using namespace valhalla::baldr;

namespace valhalla {
namespace mjolnir {

// Constructor given an existing tile. This is used to read in the tile
// data and then add to it (e.g. adding node connections between hierarchy
// levels. If the deserialize flag is set then all objects are serialized
// from memory into builders that can be added to and then stored using
// StoreTileData.
GraphTileBuilder::GraphTileBuilder(const std::string& tile_dir,
                                   const GraphId& graphid, bool deserialize)
    : tile_dir_(tile_dir),
      GraphTile(tile_dir, graphid) {

  // Copy tile header to a builder (if tile exists). Always set the tileid
  if (header_) {
    header_builder_ = *header_;
  }
  header_builder_.set_graphid(graphid);

  // Done if not deserializing and creating builders for everything
  if (!deserialize) {
    textlistbuilder_.emplace_back("");
    text_offset_map_.emplace("", 0);
    text_list_offset_ = 1;

    // Add a dummy admin record at index 0 to be used if admin records are
    // not used/created or if none is found.
    AddAdmin("None","None","","");
    return;
  }

  // Street name info. Unique set of offsets into the text list
  std::set<NameInfo> name_info;
  name_info.insert({0});

  // Create vectors of the fixed size objects
  size_t n = header_->nodecount();
  nodes_builder_.resize(n);
  memcpy(&nodes_builder_[0], nodes_, n * sizeof(NodeInfo));
  n = header_->directededgecount();
  directededges_builder_.resize(n);
  memcpy(&directededges_builder_[0], directededges_, n * sizeof(DirectedEdge));

  // Create access restriction list
  for (uint32_t i = 0; i < header_->access_restriction_count(); i++) {
    access_restriction_builder_.emplace_back(std::move(access_restrictions_[i]));
  }

  // Create transit builders and add any text offsets to the set
  for (uint32_t i = 0; i < header_->departurecount(); i++) {
    departure_builder_.emplace_back(std::move(departures_[i]));
    name_info.insert({departures_[i].headsign_offset()});
  }
  for (uint32_t i = 0; i < header_->stopcount(); i++) {
    stop_builder_.emplace_back(std::move(transit_stops_[i]));
    name_info.insert({transit_stops_[i].one_stop_offset()});
    name_info.insert({transit_stops_[i].name_offset()});
  }
  for (uint32_t i = 0; i < header_->routecount(); i++) {
    route_builder_.emplace_back(std::move(transit_routes_[i]));
    name_info.insert({transit_routes_[i].one_stop_offset()});
    name_info.insert({transit_routes_[i].op_by_onestop_id_offset()});
    name_info.insert({transit_routes_[i].op_by_name_offset()});
    name_info.insert({transit_routes_[i].op_by_website_offset()});
    name_info.insert({transit_routes_[i].short_name_offset()});
    name_info.insert({transit_routes_[i].long_name_offset()});
    name_info.insert({transit_routes_[i].desc_offset()});
  }
  for (uint32_t i = 0; i < header_->schedulecount(); i++) {
    schedule_builder_.emplace_back(std::move(transit_schedules_[i]));
  }

  // Create sign builders
  for (uint32_t i = 0; i < header_->signcount(); i++) {
    name_info.insert({signs_[i].text_offset()});
    signs_builder_.emplace_back(signs_[i].edgeindex(), signs_[i].type(),
                                signs_[i].text_offset());
  }

  // Create admin builders
  for (uint32_t i = 0; i < header_->admincount(); i++) {
    admins_builder_.emplace_back(admins_[i].country_offset(),
                admins_[i].state_offset(), admins_[i].country_iso(),
                admins_[i].state_iso());
    name_info.insert({admins_[i].country_offset()});
    name_info.insert({admins_[i].state_offset()});
  }

  // Edge bins are gotten by parent

  // Create an ordered set of edge info offsets
  std::set<uint32_t> edge_info_offsets;
  for (auto& diredge : directededges_builder_) {
    edge_info_offsets.insert(diredge.edgeinfo_offset());
  }

  // create the forward list of complex restrictions.
  complex_restriction_forward_list_offset_ = 0;
  while (complex_restriction_forward_list_offset_ <
      complex_restriction_forward_size_) {

    ComplexRestriction cr(complex_restriction_forward_ + complex_restriction_forward_list_offset_);

    ComplexRestrictionBuilder crb;
    crb.set_from_id(cr.from_id());
    crb.set_to_id(cr.to_id());
    /** TODO - design common date/time structure
    crb.set_begin_time(cr.begin_time());
    crb.set_elapsed_time(cr.end_time());
    crb.set_begin_day(cr.begin_day());
    crb.set_end_day(cr.end_day());
    */
    crb.set_via_list(cr.GetVias());
    crb.set_modes(cr.modes());

    complex_restriction_forward_list_offset_ += crb.SizeOf();
    complex_restriction_forward_builder_.emplace_back(std::move(crb));
  }

  if (complex_restriction_forward_list_offset_ != complex_restriction_forward_size_) {
    LOG_WARN("GraphTileBuilder TileID: " +
          std::to_string(header_->graphid().tileid()) +
          " offsets are off for complex restrictions: = " +
          std::to_string(complex_restriction_forward_list_offset_) +
          " size = " + std::to_string(complex_restriction_forward_size_));
  }

  // create the reverse list of complex restrictions.
  complex_restriction_reverse_list_offset_ = 0;
  while (complex_restriction_reverse_list_offset_ < complex_restriction_reverse_size_) {

    ComplexRestriction cr(complex_restriction_reverse_ + complex_restriction_reverse_list_offset_);

    ComplexRestrictionBuilder crb;
    crb.set_from_id(cr.from_id());
    crb.set_to_id(cr.to_id());
    /** TODO
    crb.set_begin_time(cr.begin_time());
    crb.set_elapsed_time(cr.end_time());
    crb.set_begin_day(cr.begin_day());
    crb.set_end_day(cr.end_day());
    */
    crb.set_via_list(cr.GetVias());
    crb.set_modes(cr.modes());

    complex_restriction_reverse_list_offset_ += crb.SizeOf();
    complex_restriction_reverse_builder_.emplace_back(std::move(crb));
  }

  if (complex_restriction_reverse_list_offset_ != complex_restriction_reverse_size_) {
    LOG_WARN("GraphTileBuilder TileID: " +
          std::to_string(header_->graphid().tileid()) +
          " offsets are off for complex restrictions: = " +
          std::to_string(complex_restriction_reverse_list_offset_) +
          " size = " + std::to_string(complex_restriction_reverse_size_));
  }

  // EdgeInfo. Create list of EdgeInfoBuilders. Add to text offset set.
  edge_info_offset_ = 0;
  for (auto offset : edge_info_offsets) {
    // Verify the offsets match as we create the edge info builder list
    if (offset != edge_info_offset_) {
      LOG_WARN("GraphTileBuilder TileID: " +
            std::to_string(header_->graphid().tileid()) +
            " offset stored in directed edge: = " + std::to_string(offset) +
            " current ei offset= " + std::to_string(edge_info_offset_));
    }

    EdgeInfo ei(edgeinfo_ + offset, textlist_, textlist_size_);
    EdgeInfoBuilder eib;
    eib.set_wayid(ei.wayid());
    for (uint32_t nm = 0; nm < ei.name_count(); nm++) {
      NameInfo info = ei.GetNameInfo(nm);
      name_info.insert(info);
      eib.AddNameInfo(info);
    }
    eib.set_encoded_shape(ei.encoded_shape());
    edge_info_offset_ += eib.SizeOf();
    edgeinfo_list_.emplace_back(std::move(eib));
  }

  // Text list
  for (auto ni : name_info) {
    // Verify offsets as we add text. Identify any strings in the text list
    // that are not referenced by any objects.
    while (ni.name_offset_ != text_list_offset_) {
      std::string unused_string(textlist_ + text_list_offset_);
      textlistbuilder_.push_back(unused_string);
      text_offset_map_.emplace(unused_string, text_list_offset_);
      text_list_offset_ += unused_string.length() + 1;
      LOG_WARN("Unused text string: " + unused_string);
    }
    std::string str(textlist_ + ni.name_offset_);
    textlistbuilder_.push_back(str);
    uint32_t offset = ni.name_offset_;
    text_offset_map_.emplace(str, offset);
    text_list_offset_ += str.length() + 1;
  }

  // Lane connectivity
  lane_connectivity_offset_ = lane_connectivity_size_;
  n = lane_connectivity_size_ / sizeof(LaneConnectivity);
  lane_connectivity_builder_.reserve(n);
  std::copy(lane_connectivity_, lane_connectivity_ + n,
    std::back_inserter(lane_connectivity_builder_));

  // Edge elevation
  if (header_->has_edge_elevation()) {
    // Edge elevation count is the same as the directed edge count
    n = header_->directededgecount();
    edge_elevation_builder_.reserve(n);
    std::copy(edge_elevation_, edge_elevation_ + n,
        std::back_inserter(edge_elevation_builder_));
  }
}

// Output the tile to file. Stores as binary data.
void GraphTileBuilder::StoreTileData() {
  // Get the name of the file
  boost::filesystem::path filename = tile_dir_ + '/'
      + GraphTile::FileSuffix(header_builder_.graphid());

  // Make sure the directory exists on the system
  if (!boost::filesystem::exists(filename.parent_path()))
    boost::filesystem::create_directories(filename.parent_path());

  // Open file and truncate
  std::stringstream in_mem;
  std::ofstream file(filename.c_str(),
                     std::ios::out | std::ios::binary | std::ios::trunc);
  if (file.is_open()) {
    // Write the nodes
    header_builder_.set_nodecount(nodes_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&nodes_builder_[0]),
               nodes_builder_.size() * sizeof(NodeInfo));

    // Write the directed edges
    header_builder_.set_directededgecount(directededges_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&directededges_builder_[0]),
               directededges_builder_.size() * sizeof(DirectedEdge));

    // Sort and write the access restrictions
    header_builder_.set_access_restriction_count(access_restriction_builder_.size());
    std::sort(access_restriction_builder_.begin(), access_restriction_builder_.end());
    in_mem.write(reinterpret_cast<const char*>(&access_restriction_builder_[0]),
               access_restriction_builder_.size() * sizeof(AccessRestriction));

    // Sort and write the transit departures
    header_builder_.set_departurecount(departure_builder_.size());
    std::sort(departure_builder_.begin(), departure_builder_.end());
    in_mem.write(reinterpret_cast<const char*>(&departure_builder_[0]),
               departure_builder_.size() * sizeof(TransitDeparture));

    // Sort write the transit stops
    header_builder_.set_stopcount(stop_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&stop_builder_[0]),
               stop_builder_.size() * sizeof(TransitStop));

    // Write the transit routes
    header_builder_.set_routecount(route_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&route_builder_[0]),
               route_builder_.size() * sizeof(TransitRoute));

    // Write transit schedules
    header_builder_.set_schedulecount(schedule_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&schedule_builder_[0]),
               schedule_builder_.size() * sizeof(TransitSchedule));

    // TODO add transfers later
    header_builder_.set_transfercount(0);

    // Write the signs
    header_builder_.set_signcount(signs_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&signs_builder_[0]),
               signs_builder_.size() * sizeof(Sign));

    // Write the admins
    header_builder_.set_admincount(admins_builder_.size());
    in_mem.write(reinterpret_cast<const char*>(&admins_builder_[0]),
               admins_builder_.size() * sizeof(Admin));

    // Edge bins can only be added after you've stored the tile

    // Write the forward complex restriction data
    header_builder_.set_complex_restriction_forward_offset(
         (sizeof(GraphTileHeader))
             + (nodes_builder_.size() * sizeof(NodeInfo))
             + (directededges_builder_.size() * sizeof(DirectedEdge))
             + (access_restriction_builder_.size() * sizeof(AccessRestriction))
             + (departure_builder_.size() * sizeof(TransitDeparture))
             + (stop_builder_.size() * sizeof(TransitStop))
             + (route_builder_.size() * sizeof(TransitRoute))
             + (schedule_builder_.size() * sizeof(TransitSchedule))
             // TODO - once transit transfers are added need to update here
             + (signs_builder_.size() * sizeof(Sign))
             + (admins_builder_.size() * sizeof(Admin)));
    for (const auto& complex_restriction : complex_restriction_forward_builder_)
      in_mem << complex_restriction;

    // Write the reverse complex restriction data
    header_builder_.set_complex_restriction_reverse_offset(
            header_builder_.complex_restriction_forward_offset() +
            complex_restriction_forward_list_offset_);
    for (const auto& complex_restriction : complex_restriction_reverse_builder_)
      in_mem << complex_restriction;

    // Write the edge data
    header_builder_.set_edgeinfo_offset(
            header_builder_.complex_restriction_reverse_offset() +
            complex_restriction_reverse_list_offset_);
    for (const auto& edgeinfo : edgeinfo_list_)
      in_mem << edgeinfo;

    // Write the names
    header_builder_.set_textlist_offset(
            header_builder_.edgeinfo_offset() + edge_info_offset_);
    for (const auto& text : textlistbuilder_)
      in_mem << text << '\0';

    // Add padding (if needed) to align to 8-byte word.
    int tmp = in_mem.tellp() % 8;
    int padding = (tmp > 0) ? 8 - tmp : 0;
    if (padding > 0 && padding < 8) {
      in_mem.write("\0\0\0\0\0\0\0\0", padding);
    }

    // At this point there is no traffic segment data, so set traffic segment
    // Id offset and chunk offset to same value
    header_builder_.set_traffic_id_count(0);
    header_builder_.set_traffic_segmentid_offset(header_builder_.textlist_offset() +
                                                 text_list_offset_ + padding);
    header_builder_.set_traffic_chunk_offset(header_builder_.traffic_segmentid_offset());

    // Write lane connections
    header_builder_.set_lane_connectivity_offset(header_builder_.traffic_chunk_offset());
    std::sort(lane_connectivity_builder_.begin(), lane_connectivity_builder_.end());
    in_mem.write(reinterpret_cast<const char*>(&lane_connectivity_builder_[0]),
               lane_connectivity_builder_.size() * sizeof(LaneConnectivity));

    // Write the edge elevation data. Make sure that if it exists it has
    // the same count as directed edges.
    header_builder_.set_edge_elevation_offset(header_builder_.lane_connectivity_offset() +
       (lane_connectivity_builder_.size() * sizeof(LaneConnectivity)));
    if (edge_elevation_builder_.size() > 0) {
      if (edge_elevation_builder_.size() != directededges_builder_.size()) {
        LOG_ERROR("Edge elevation count is not equal to directed edge count!");
      }
      header_builder_.set_has_edge_elevation(true);
      in_mem.write(reinterpret_cast<const char*>(&edge_elevation_builder_[0]),
                         edge_elevation_builder_.size() * sizeof(EdgeElevation));
    }

    // Set the end offset
    header_builder_.set_end_offset(header_builder_.edge_elevation_offset() +
      (edge_elevation_builder_.size() * sizeof(EdgeElevation)));

    // Sanity check for the end offset
    uint32_t curr = static_cast<uint32_t>(in_mem.tellp()) +
                    static_cast<uint32_t>(sizeof(GraphTileHeader));
    if (header_builder_.end_offset() != curr) {
      LOG_ERROR("Mismatch in end offset " + std::to_string(header_builder_.end_offset()) +
                " vs in_mem stream " + std::to_string(curr) + " padding = " + std::to_string(padding));
    }

    LOG_DEBUG((boost::format("Write: %1% nodes = %2% directededges = %3% signs %4% edgeinfo offset = %5% textlist offset = %6% lane connections = %7%" )
      % filename % nodes_builder_.size() % directededges_builder_.size() % signs_builder_.size() % edge_info_offset_ % text_list_offset_ % lane_connectivity_builder_.size()).str());
    LOG_DEBUG((boost::format("   admins = %1%  departures = %2% stops = %3% routes = %4%" )
      % admins_builder_.size() % departure_builder_.size() % stop_builder_.size() % route_builder_.size()).str());

    // Write the header then the rest of the tile from the in memory buffer
    file.write(reinterpret_cast<const char*>(&header_builder_), sizeof(GraphTileHeader));
    file << in_mem.rdbuf();
    file.close();
  } else {
    throw std::runtime_error("Failed to open file " + filename.string());
  }
}

// Update a graph tile with new nodes and directed edges. The rest of the
// tile contents remains the same.
void GraphTileBuilder::Update(const std::vector<NodeInfo>& nodes,
    const std::vector<DirectedEdge>& directededges) {

  // Get the name of the file
  boost::filesystem::path filename = tile_dir_ + '/' +
        GraphTile::FileSuffix(header_->graphid());

  // Make sure the directory exists on the system
  if (!boost::filesystem::exists(filename.parent_path()))
    boost::filesystem::create_directories(filename.parent_path());

  // Open file. Truncate so we replace the contents.
  std::ofstream file(filename.c_str(),
                     std::ios::out | std::ios::binary | std::ios::trunc);
  if (file.is_open()) {
    // Write the header
    file.write(reinterpret_cast<const char*>(header_), sizeof(GraphTileHeader));

    // Write the updated nodes. Make sure node count matches.
    if (nodes.size() != header_->nodecount()) {
      throw std::runtime_error("GraphTileBuilder::Update - node count has changed");
    }
    file.write(reinterpret_cast<const char*>(&nodes[0]),
               nodes.size() * sizeof(NodeInfo));

    // Write the updated directed edges. Make sure edge count matches.
    if (directededges.size() != header_->directededgecount()) {
      throw std::runtime_error("GraphTileBuilder::Update - directed edge count has changed");
    }
    file.write(reinterpret_cast<const char*>(&directededges[0]),
               directededges.size() * sizeof(DirectedEdge));

    // Write the rest of the tiles
    auto begin = reinterpret_cast<const char*>(&access_restrictions_[0]);
    auto end = reinterpret_cast<const char*>(header()) + header()->end_offset();
    file.write(begin, end - begin);
    file.close();
  } else {
    throw std::runtime_error("GraphTileBuilder::Update - Failed to open file " + filename.string());
  }
}

// Gets a reference to the header builder.
GraphTileHeader& GraphTileBuilder::header_builder() {
  return header_builder_;
}

// Get the current list of node builders.
std::vector<NodeInfo>& GraphTileBuilder::nodes() {
  return nodes_builder_;
}

// Gets the current list of directed edge (builders).
std::vector<DirectedEdge>& GraphTileBuilder::directededges() {
  return directededges_builder_;
}

// Add a transit departure.
void GraphTileBuilder::AddTransitDeparture(const TransitDeparture& departure) {
  departure_builder_.emplace_back(std::move(departure));
}

// Add a transit stop.
void GraphTileBuilder::AddTransitStop(const TransitStop& stop)  {
  stop_builder_.emplace_back(std::move(stop));
}

// Add a transit route.
void GraphTileBuilder::AddTransitRoute(const TransitRoute& route)  {
  route_builder_.emplace_back(std::move(route));
}

// Add a transit schedule.
void GraphTileBuilder::AddTransitSchedule(const TransitSchedule& schedule)  {
  schedule_builder_.emplace_back(std::move(schedule));
}

// Add an access restriction.
void GraphTileBuilder::AddAccessRestriction(const AccessRestriction& access_restriction) {
  access_restriction_builder_.emplace_back(std::move(access_restriction));
}

// Add access restrictions
void GraphTileBuilder::AddAccessRestrictions(const std::vector<AccessRestriction>& restrictions) {
  access_restriction_builder_.clear();
  // Add restrictions to the list
  access_restriction_builder_ = restrictions;
}

// Add signs
void GraphTileBuilder::AddSigns(const uint32_t idx,
                                const std::vector<SignInfo>& signs) {
  // Iterate through the list of sign info (with sign text) and add sign
  // text to the text list. Skip signs with no text.
  for (const auto& sign : signs) {
    if (!(sign.text().empty())) {
      uint32_t offset = AddName(sign.text());
      signs_builder_.emplace_back(idx, sign.type(), offset);
    }
  }
}

// Add lane connectivity
void GraphTileBuilder::AddLaneConnectivity(const std::vector<baldr::LaneConnectivity>& lc)
{
  lane_connectivity_builder_.insert(lane_connectivity_builder_.end(), lc.begin(), lc.end());
  lane_connectivity_offset_ += sizeof(baldr::LaneConnectivity) * lc.size();
}

bool GraphTileBuilder::HasEdgeInfo(const uint32_t edgeindex, const baldr::GraphId& nodea,
                     const baldr::GraphId& nodeb, uint32_t& edge_info_offset) {
  auto edge_tuple_item = EdgeTuple(edgeindex, nodea, nodeb);
  auto existing_edge_offset_item = edge_offset_map_.find(edge_tuple_item);
  if (existing_edge_offset_item != edge_offset_map_.cend()) {
    edge_info_offset = existing_edge_offset_item->second;
    return true;
  }
  return false;
}

// Add the complex restrictions and update the list offset.  The to, from, and vias should all point to edgeids.
void GraphTileBuilder::UpdateComplexRestrictions(const std::list<ComplexRestrictionBuilder>& complex_restriction_builder,
                                                 const bool forward) {

  if (forward) {
    complex_restriction_forward_list_offset_ = 0;
    complex_restriction_forward_builder_.clear();
    // Add a new complex restrictions to the lists
    complex_restriction_forward_builder_ = complex_restriction_builder;

    for (const auto& crb : complex_restriction_builder) {
      // Update edge offset for next item
      complex_restriction_forward_list_offset_ += crb.SizeOf();
    }
  } else {
    complex_restriction_reverse_list_offset_ = 0;
    complex_restriction_reverse_builder_.clear();
    // Add a new complex restrictions to the lists
    complex_restriction_reverse_builder_ = complex_restriction_builder;

    for (const auto& crb : complex_restriction_builder) {
      // Update edge offset for next item
      complex_restriction_reverse_list_offset_ += crb.SizeOf();
    }
  }
}

// Add edge info
template <class shape_container_t>
uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex,
                                       const GraphId& nodea,
                                       const baldr::GraphId& nodeb,
                                       const uint64_t wayid,
                                       const shape_container_t& lls,
                                       const std::vector<std::string>& names,
                                       bool& added) {
  // If we haven't yet added edge info for this edge tuple
  auto edge_tuple_item = EdgeTuple(edgeindex, nodea, nodeb);
  auto existing_edge_offset_item = edge_offset_map_.find(edge_tuple_item);
  if (existing_edge_offset_item == edge_offset_map_.end()) {
    // Add a new EdgeInfo to the list and get a reference to it
    edgeinfo_list_.emplace_back();
    EdgeInfoBuilder& edgeinfo = edgeinfo_list_.back();
    edgeinfo.set_wayid(wayid);
    edgeinfo.set_shape(lls);

    // Add names to the common text/name list. Skip blank names.
    std::vector<NameInfo> name_info_list;
    name_info_list.reserve(std::min(names.size(), kMaxNamesPerEdge));
    size_t name_count = 0;
    for (const auto& name : names) {
      // Stop adding names if max count has been reached
      if (name_count == kMaxNamesPerEdge) {
        LOG_WARN("Too many names for edgeindex: " + std::to_string(edgeindex));
        break;
      }

      // Verify name is not empty
      if (!(name.empty())) {
        // Add name and add its offset to edge info's list.
        NameInfo ni({AddName(name)});
        name_info_list.emplace_back(ni);
        ++name_count;
      }
    }
    edgeinfo.set_name_info_list(name_info_list);

    // Add to the map
    edge_offset_map_.emplace(edge_tuple_item, edge_info_offset_);

    // Set current edge offset
    uint32_t current_edge_offset = edge_info_offset_;

    // Update edge offset for next item
    edge_info_offset_ += edgeinfo.SizeOf();

    // Return the offset to this edge info
    added = true;
    return current_edge_offset;
  }

  // Already have this edge - return the offset
  added = false;
  return existing_edge_offset_item->second;

}
template uint32_t GraphTileBuilder::AddEdgeInfo<std::vector<PointLL> >
  (const uint32_t edgeindex, const GraphId&, const baldr::GraphId&,const uint64_t,
   const std::vector<PointLL>&, const std::vector<std::string>&, bool&);
template uint32_t GraphTileBuilder::AddEdgeInfo<std::list<PointLL> >
  (const uint32_t edgeindex, const GraphId&, const baldr::GraphId&,const uint64_t,
   const std::list<PointLL>&, const std::vector<std::string>&, bool&);

// AddEdgeInfo - accepts an encoded shape string.
uint32_t GraphTileBuilder::AddEdgeInfo(const uint32_t edgeindex,
                     const baldr::GraphId& nodea,
                     const baldr::GraphId& nodeb,
                     const uint64_t wayid,
                     const std::string& llstr,
                     const std::vector<std::string>& names,
                     bool& added){
  // If we haven't yet added edge info for this edge tuple
  auto edge_tuple_item = EdgeTuple(edgeindex, nodea, nodeb);
  auto existing_edge_offset_item = edge_offset_map_.find(edge_tuple_item);
  if (existing_edge_offset_item == edge_offset_map_.end()) {
    // Add a new EdgeInfo to the list and get a reference to it
    edgeinfo_list_.emplace_back();
    EdgeInfoBuilder& edgeinfo = edgeinfo_list_.back();
    edgeinfo.set_wayid(wayid);
    edgeinfo.set_encoded_shape(llstr);

    // Add names to the common text/name list. Skip blank names.
    std::vector<NameInfo> name_info_list;
    name_info_list.reserve(std::min(names.size(), kMaxNamesPerEdge));
    size_t name_count = 0;
    for (const auto& name : names) {
      // Stop adding names if max count has been reached
      if (name_count == kMaxNamesPerEdge) {
        LOG_WARN("Too many names for edgeindex: " + std::to_string(edgeindex));
        break;
      }

      // Verify name is not empty
      if (!(name.empty())) {
        // Add name and add its offset to edge info's list.
        NameInfo ni({AddName(name)});
        name_info_list.emplace_back(ni);
        ++name_count;
      }
    }
    edgeinfo.set_name_info_list(name_info_list);

    // Add to the map
    edge_offset_map_.emplace(edge_tuple_item, edge_info_offset_);

    // Set current edge offset
    uint32_t current_edge_offset = edge_info_offset_;

    // Update edge offset for next item
    edge_info_offset_ += edgeinfo.SizeOf();

    // Return the offset to this edge info
    added = true;
    return current_edge_offset;
  }

  // Already have this edge - return the offset
  added = false;
  return existing_edge_offset_item->second;
}

// Add a name to the text list
uint32_t GraphTileBuilder::AddName(const std::string& name) {
  if (name.empty()) {
    return 0;
  }

  // If nothing already used this name
  auto existing_text_offset = text_offset_map_.find(name);
  if (existing_text_offset == text_offset_map_.end()) {
    // Save the current offset and add name to text list
    uint32_t offset = text_list_offset_;
    textlistbuilder_.emplace_back(name);

    // Add name/offset pair to map and update text offset value
    // to length of string plus null terminator
    text_offset_map_.emplace(name, text_list_offset_);
    text_list_offset_ += (name.length() + 1);
    return offset;
  } else {
    // Return the offset to the existing name
    return existing_text_offset->second;
  }
}

// Add admin
uint32_t GraphTileBuilder::AddAdmin(const std::string& country_name,
            const std::string& state_name, const std::string& country_iso,
            const std::string& state_iso) {
  // Check if admin already exists
  auto existing_admin_info_offset_item = admin_info_offset_map_.find(country_iso+state_name);
  if (existing_admin_info_offset_item == admin_info_offset_map_.end()) {
    // Add names and add to the admin builder
    uint32_t country_offset = AddName(country_name);
    uint32_t state_offset   = AddName(state_name);
    admins_builder_.emplace_back(country_offset, state_offset,
                                 country_iso, state_iso);

    // Add to the map
    admin_info_offset_map_.emplace(country_iso+state_name, admins_builder_.size()-1);
    return admins_builder_.size()-1;
  } else {
    // Already have this admin - return the offset
    return existing_admin_info_offset_item->second;
  }
}

// Gets a non-const node from existing tile data.
NodeInfo& GraphTileBuilder::node(const size_t idx) {
  if (idx < header_->nodecount())
    return nodes_[idx];
  throw std::runtime_error("GraphTileBuilder NodeInfo index out of bounds");
}

// Get the node builder at the specified index.
NodeInfo& GraphTileBuilder::node_builder(const size_t idx) {
  if (idx < header_->nodecount())
    return nodes_builder_[idx];
  throw std::runtime_error("GraphTileBuilder NodeInfo index out of bounds");
}

// Gets a non-const directed edge from existing tile data.
DirectedEdge& GraphTileBuilder::directededge(const size_t idx) {
  if (idx < header_->directededgecount())
    return directededges_[idx];
  throw std::runtime_error("GraphTile DirectedEdge id out of bounds");
}

// Gets a pointer to directed edges within the list being built.
const DirectedEdge* GraphTileBuilder::directededges(const size_t idx) {
  if (idx < header_->directededgecount())
    return &directededges_builder_[idx];
  throw std::runtime_error("GraphTile DirectedEdge id out of bounds");
}

// Get the directed edge builder at the specified index.
DirectedEdge& GraphTileBuilder::directededge_builder(const size_t idx) {
  if (idx < header_->directededgecount())
    return directededges_builder_[idx];
  throw std::runtime_error("GraphTile DirectedEdge id out of bounds");
}

// Gets a non-const access restriction from existing tile data.
AccessRestriction& GraphTileBuilder::accessrestriction(const size_t idx) {
  if (idx < header_->access_restriction_count()) {
    return access_restrictions_[idx];
  }
  throw std::runtime_error("GraphTileBuilder access restriction index is out of bounds");
}

// Gets an access restriction builder at the specified index.
AccessRestriction& GraphTileBuilder::accessrestriction_builder(const size_t idx) {
  if (idx < header_->access_restriction_count())
     return access_restriction_builder_[idx];
  throw std::runtime_error("GraphTileBuilder access restriction index is out of bounds");
}

// Gets a non-const sign from existing tile data.
Sign& GraphTileBuilder::sign(const size_t idx) {
  if (idx < header_->signcount())
    return signs_[idx];
  throw std::runtime_error("GraphTileBuilder sign index is out of bounds");
}

// Gets a sign builder at the specified index.
Sign& GraphTileBuilder::sign_builder(const size_t idx) {
  if (idx < header_->signcount())
    return signs_builder_[idx];
  throw std::runtime_error("GraphTileBuilder sign index is out of bounds");
}

// Gets a const admin builder at specified index.
const Admin& GraphTileBuilder::admins_builder(size_t idx) {
  if (idx < admins_builder_.size())
    return admins_builder_.at(idx);
  throw std::runtime_error("GraphTileBuilder admin index is out of bounds");
}

// Add the tile creation date
void GraphTileBuilder::AddTileCreationDate(const uint32_t tile_creation_date) {
  header_builder_.set_date_created(tile_creation_date);
}

//return this tiles' edges' bins and its edges' tweeners' bins
using tweeners_t = std::unordered_map<GraphId, std::array<std::vector<GraphId>, kBinCount> >;
std::array<std::vector<GraphId>, kBinCount> GraphTileBuilder::BinEdges(const GraphTile* tile, tweeners_t& tweeners) {
  std::array<std::vector<GraphId>, kBinCount> bins;
  //we store these at the highest level
  auto max_level = TileHierarchy::levels().rbegin()->first;
  //skip transit or other special levels and empty tiles
  if(tile->header()->graphid().level() > max_level || tile->header()->directededgecount() == 0)
    return bins;
  //is this the highest level
  auto max = tile->header()->graphid().level() == max_level;
  auto tiles = TileHierarchy::levels().rbegin()->second.tiles;

  //each edge please
  std::unordered_set<uint64_t> ids(tile->header()->directededgecount() / 2);
  const auto* start_edge = tile->directededge(0);
  for(const DirectedEdge* edge = start_edge; edge < start_edge + tile->header()->directededgecount(); ++edge) {
    //dont bin these
    if(edge->is_shortcut() || edge->IsTransition() || edge->use() == Use::kTransitConnection ||
       edge->use() == Use::kPlatformConnection || edge->use() == Use::kEgressConnection)
      continue;

    //get the shape or bail if none
    auto info = tile->edgeinfo(edge->edgeinfo_offset());
    const auto& shape = info.shape();
    if(shape.empty())
      continue;

    //avoid duplicates and minimize leaving a tile for shape by:
    //writing the edge to the tile it originates in
    //not writing the edge to the tile it terminates in
    //writing the edge to tweeners if originating < terminating or the edge leaves and comes back
    auto start_id = tiles.TileId(edge->forward() ? shape.front() : shape.back());
    auto end_id = tiles.TileId(edge->forward() ? shape.back() : shape.front());
    auto intermediate = start_id < end_id;

    //if this starts and ends in the same tile and we've seen it already we can skip it
    if(start_id == end_id && !ids.insert(edge->edgeinfo_offset()).second)
      continue;

    //for each bin that got intersected
    auto intersection = tiles.Intersect(shape);
    GraphId edge_id(tile->header()->graphid().tileid(), tile->header()->graphid().level(), edge - start_edge);
    for(const auto& i : intersection) {
      //as per the rules above about when to add intersections
      auto originating = i.first == start_id;
      auto terminating = i.first == end_id;
      auto loop_back = i.first != start_id && i.first != end_id && start_id == end_id;
      if(originating || (intermediate && !terminating) || loop_back) {
        //which set of bins, either this local set or tweeners to be added later
        auto& out_bins = originating && max ? bins : tweeners.insert({GraphId(i.first, max_level, 0), {}}).first->second;
        //keep the edge id
        for(auto bin : i.second)
          out_bins[bin].push_back(edge_id);
      }
    }
  }

  //give back this tiles bins
  return bins;
}

void GraphTileBuilder::AddBins(const std::string& tile_dir,
                const GraphTile* tile,
                const std::array<std::vector<GraphId>, kBinCount>& more_bins) {
  //read bins and append and keep track of how much is appended
  std::vector<GraphId> bins[kBinCount];
  uint32_t shift = 0;
  for(size_t i = 0; i < kBinCount; ++i) {
    auto bin = tile->GetBin(i % kBinsDim, i / kBinsDim);
    bins[i].assign(bin.begin(), bin.end());
    bins[i].insert(bins[i].end(), more_bins[i].cbegin(), more_bins[i].cend());
    shift += more_bins[i].size();
  }
  shift *= sizeof(GraphId);
  //update header bin indices
  uint32_t offsets[kBinCount] = { static_cast<uint32_t>(bins[0].size()) };
  for(size_t i = 1 ; i < kBinCount; ++i)
    offsets[i] = static_cast<uint32_t>(bins[i].size()) + offsets[i - 1];
  //update header offsets
  //NOTE: if format changes to add more things here we need to make a change here as well
  GraphTileHeader header = *tile->header();
  header.set_edge_bin_offsets(offsets);
  header.set_complex_restriction_forward_offset(header.complex_restriction_forward_offset() + shift);
  header.set_complex_restriction_reverse_offset(header.complex_restriction_reverse_offset() + shift);
  header.set_edgeinfo_offset(header.edgeinfo_offset() + shift);
  header.set_textlist_offset(header.textlist_offset() + shift);
  header.set_traffic_segmentid_offset(header.traffic_segmentid_offset() + shift);
  header.set_traffic_chunk_offset(header.traffic_chunk_offset() + shift);
  header.set_lane_connectivity_offset(header.lane_connectivity_offset() + shift);
  header.set_edge_elevation_offset(header.edge_elevation_offset() + shift);
  header.set_end_offset(header.end_offset() + shift);
  //rewrite the tile
  boost::filesystem::path filename = tile_dir + '/' + GraphTile::FileSuffix(header.graphid());
  if(!boost::filesystem::exists(filename.parent_path()))
    boost::filesystem::create_directories(filename.parent_path());
  std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
  //open it
  if(file.is_open()) {
    //new header
    file.write(reinterpret_cast<const char*>(&header), sizeof(GraphTileHeader));
    //a bunch of stuff between header and bins
    const auto* begin = reinterpret_cast<const char*>(tile->header()) + sizeof(GraphTileHeader);
    const auto* end = reinterpret_cast<const char*>(tile->GetBin(0, 0).begin());
    file.write(begin, end - begin);
    //the updated bins
    for(const auto& bin : bins)
      file.write(reinterpret_cast<const char*>(bin.data()), bin.size() * sizeof(GraphId));
    //the rest of the stuff after bins
    begin = reinterpret_cast<const char*>(tile->GetBin(kBinsDim - 1, kBinsDim - 1).end());
    end = reinterpret_cast<const char*>(tile->header()) + tile->header()->end_offset();
    file.write(begin, end - begin);
  }//failed
  else
    throw std::runtime_error("Failed to open file " + filename.string());
}

// Initialize traffic segment association. Sizes the traffic segment Id list
// and sets them all to Invalid.
void GraphTileBuilder::InitializeTrafficSegments() {
  if(header_->traffic_id_count()) {
    traffic_segment_builder_.assign(traffic_segments_, traffic_segments_ + header_->traffic_id_count());
  } else {
    traffic_segment_builder_.resize(header_builder_.directededgecount());
  }
}

// Initialize traffic chunks. Copies existing chunks into the chunk builder.
// This is executed before adding "leftovers" and again before adding chunks.
void GraphTileBuilder::InitializeTrafficChunks() {
  // Assign the chunks to its builder counterpart
  if (traffic_chunk_size_ > 0) {
    size_t count = traffic_chunk_size_ / sizeof(TrafficChunk);
    traffic_chunk_builder_.assign(traffic_chunks_, traffic_chunks_ + count);
  }
}

// Add a traffic segment association - used when an edge associates to
// a single traffic segment.
void GraphTileBuilder::AddTrafficSegment(const GraphId& edgeid,
                        const TrafficChunk& seg) {
  // Check if edge Id is within range and part of this tile
  if (edgeid.Tile_Base() != header_builder_.graphid()) {
    LOG_ERROR("AddTrafficSegments - edge does not belong to this tile");
    return;
  }
  if (edgeid.id() >= header_builder_.directededgecount()) {
    LOG_ERROR("AddTrafficSegments - edge is not valid for this tile");
    return;
  }

  // If this segment is in the same tile we have a 1:1 association of an edge
  // to a segment in this tile - create a single segment association
  if (seg.segment_id().Tile_Base() == edgeid.Tile_Base()) {
    traffic_segment_builder_[edgeid.id()] = { seg.segment_id().id(),
          seg.starts_segment(), seg.ends_segment() };
  } else {
    // Associates to a single traffic segment but in a different tile. Set
    // the TrafficAssociation to represent a chunk count (1) and index. Store
    // a single TrafficChunk.
    traffic_segment_builder_[edgeid.id()] = { 1, traffic_chunk_builder_.size() };
    traffic_chunk_builder_.emplace_back(seg);
  }
}

// Add a traffic segment association - used when an edge associates to
// more than one traffic segment.
void GraphTileBuilder::AddTrafficSegments(const baldr::GraphId& edgeid,
                         const std::vector<baldr::TrafficChunk>& segs) {
  // Check if edge Id is within range and part of this tile
  if (edgeid.Tile_Base() != header_builder_.graphid()) {
    LOG_ERROR("AddTrafficSegments - edge does not belong to this tile");
    return;
  }
  if (edgeid.id() >= header_builder_.directededgecount()) {
    LOG_ERROR("AddTrafficSegments - edge is not valid for this tile");
    return;
  }

  // This edge associates to many segments or portions of segments.
  // Set the TrafficAssociation to represent a chunk count and index.
  // Store each TrafficChunk.
  traffic_segment_builder_[edgeid.id()] = { segs.size(), traffic_chunk_builder_.size() };
  for (const auto& seg : segs) {
    traffic_chunk_builder_.push_back(seg);
  }
}

/**
 * Updates a tile with traffic segment and chunk data. UpdateTrafficSegments
 * is called 3 times - first to add segments within the tile, then to add any
 * "leftover" segments for OSMLR segments that cross tiles, and then to add
 * "chunks". Need to make sure the "shift" for offsets to data after the
 * traffic information are only increased by the amount of "new" segments.
 */
void GraphTileBuilder::UpdateTrafficSegments(const bool update_dir_edges) {
  // Get the number of new segments and chunks added with this call.
  uint32_t new_segments = traffic_segment_builder_.size() -
                          header_->traffic_id_count();
  uint32_t new_chunks = traffic_chunk_builder_.size() -
      (header_->lane_connectivity_offset() - header_->traffic_chunk_offset()) / sizeof(TrafficChunk);

  // Update header to include the traffic segment count and update the
  // offset to chunks (based on size of traffic segments).
  // Padding should already be done so we start traffic info on an 8-byte boundary
  header_builder_.set_traffic_id_count(traffic_segment_builder_.size());
  header_builder_.set_traffic_chunk_offset(header_builder_.traffic_segmentid_offset() +
          traffic_segment_builder_.size() * sizeof(TrafficAssociation));

  // Shift offsets to anything that comes after traffic
  uint32_t shift = new_segments * sizeof(TrafficAssociation) + new_chunks * sizeof(TrafficChunk);
  header_builder_.set_lane_connectivity_offset(header_builder_.lane_connectivity_offset() + shift);
  header_builder_.set_edge_elevation_offset(header_builder_.edge_elevation_offset() + shift);
  header_builder_.set_end_offset(header_builder_.end_offset() + shift);

  // Get the name of the file
  boost::filesystem::path filename = tile_dir_ + '/'
      + GraphTile::FileSuffix(header_builder_.graphid());

  // Make sure the directory exists on the system
  if (!boost::filesystem::exists(filename.parent_path()))
    boost::filesystem::create_directories(filename.parent_path());

  // Open file and truncate
  std::stringstream in_mem;
  std::ofstream file(filename.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
  if (file.is_open()) {
    // Write a new header
    file.write(reinterpret_cast<const char*>(&header_builder_), sizeof(GraphTileHeader));

    // Copy the tile contents from nodes to the beginning of traffic segments
    file.write(reinterpret_cast<const char*>(nodes_),
               header_->traffic_segmentid_offset() - sizeof(GraphTileHeader));

    // Append the traffic segment list
    file.write(reinterpret_cast<const char*>(&traffic_segment_builder_[0]),
               traffic_segment_builder_.size() * sizeof(TrafficAssociation));

    // Append the traffic chunks
    file.write(reinterpret_cast<const char*>(&traffic_chunk_builder_[0]),
               traffic_chunk_builder_.size() * sizeof(TrafficChunk));

    // Write rest of the stuff after traffic chunks (includes lane connectivity
    // and edge elevation...so far).
    const auto* begin = reinterpret_cast<const char*>(header_) +
                header_->lane_connectivity_offset();
    const auto* end = reinterpret_cast<const char*>(header_) +
                header_->end_offset();
    file.write(begin, end - begin);

    // Update directed edge flags
    if (update_dir_edges) {
      // Copy directed edges so we can modify them
      uint32_t n = header_->directededgecount();
      directededges_builder_.resize(n);
      memcpy(&directededges_builder_[0], directededges_, n * sizeof(DirectedEdge));

      // Iterate through directed edges and set traffic segment flag for aby
      // that have any traffic segments.
      for (uint32_t i = 0; i < n; i++) {
        const TrafficAssociation& t = traffic_segment_builder_[i];
        if (t.chunk() || t.count() == 1) {
          directededges_builder_[i].set_traffic_seg(true);
        }
      }

      // Write the updated directed edges
      size_t offset = sizeof(GraphTileHeader) + header_->nodecount() * sizeof(NodeInfo);
      file.seekp(offset, std::ios_base::beg);
      file.write(reinterpret_cast<const char*>(&directededges_builder_[0]),
                 directededges_builder_.size() * sizeof(DirectedEdge));
    }

    // Close the file
    file.close();
  }
}

// Gets the current list of directed edge (builders).
std::vector<EdgeElevation>& GraphTileBuilder::edge_elevations() {
  return edge_elevation_builder_;
}

}
}

ext: cc

max_stars_repo_name: komoot/valhalla

max_issues_repo_name: komoot/valhalla

max_forks_repo_name: komoot/valhalla