text:
/* Copyright (C) 2014 InfiniDB, Inc.
 * Copyright (C) 2016-2020 MariaDB Corporation.

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; version 2 of
   the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
   MA 02110-1301, USA. */

//
// $Id: distributedenginecomm.cpp 9655 2013-06-25 23:08:13Z xlou $
//
// C++ Implementation: distributedenginecomm
//
// Description:
//
//
// Author:  <pfigg@calpont.com>, (C) 2006
//
// Copyright: See COPYING file that comes with this distribution
//
//

#include <sstream>
#include <stdexcept>
#include <cassert>
#include <ctime>
#include <algorithm>
#include <unistd.h>
#include <chrono>
#include <thread>
using namespace std;

#include <boost/scoped_array.hpp>
#include <boost/thread.hpp>
using namespace boost;

#include "distributedenginecomm.h"

#include "messagequeue.h"
#include "bytestream.h"
using namespace messageqcpp;

#include "configcpp.h"
using namespace config;

#include "errorids.h"
#include "exceptclasses.h"
#include "messagelog.h"
#include "messageobj.h"
#include "loggingid.h"
using namespace logging;

#include "liboamcpp.h"
using namespace oam;

#include "jobstep.h"
using namespace joblist;

#include "atomicops.h"

namespace
{

void  writeToLog(const char* file, int line, const string& msg, LOG_TYPE logto = LOG_TYPE_INFO)
{
    LoggingID lid(05);
    MessageLog ml(lid);
    Message::Args args;
    Message m(0);
    args.add(file);
    args.add("@");
    args.add(line);
    args.add(msg);
    m.format(args);

    switch (logto)
    {
        case LOG_TYPE_DEBUG:
            ml.logDebugMessage(m);
            break;

        case LOG_TYPE_INFO:
            ml.logInfoMessage(m);
            break;

        case LOG_TYPE_WARNING:
            ml.logWarningMessage(m);
            break;

        case LOG_TYPE_ERROR:
            ml.logWarningMessage(m);
            break;

        case LOG_TYPE_CRITICAL:
            ml.logCriticalMessage(m);
            break;
    }
}

// @bug 1463. this function is added for PM failover. for dual/more nic PM,
// this function is used to get the module name
string getModuleNameByIPAddr(oam::ModuleTypeConfig moduletypeconfig,
                             string ipAddress)
{
    string modulename = "";
    DeviceNetworkList::iterator pt = moduletypeconfig.ModuleNetworkList.begin();

    for ( ; pt != moduletypeconfig.ModuleNetworkList.end() ; pt++)
    {
        modulename = (*pt).DeviceName;
        HostConfigList::iterator pt1 = (*pt).hostConfigList.begin();

        for ( ; pt1 != (*pt).hostConfigList.end() ; pt1++)
        {
            if (ipAddress == (*pt1).IPAddr)
                return modulename;
        }
    }

    return modulename;
}

struct EngineCommRunner
{
    EngineCommRunner(joblist::DistributedEngineComm* jl,
                     boost::shared_ptr<MessageQueueClient> cl, uint32_t connectionIndex) : jbl(jl), client(cl),
        connIndex(connectionIndex) {}
    joblist::DistributedEngineComm* jbl;
    boost::shared_ptr<MessageQueueClient> client;
    uint32_t connIndex;
    void operator()()
    {
        //cout << "Listening on client at 0x" << hex << (ptrdiff_t)client << dec << endl;
        try
        {
            jbl->Listen(client, connIndex);
        }
        catch (std::exception& ex)
        {
            string what(ex.what());
            cerr << "exception caught in EngineCommRunner: " << what << endl;

            if (what.find("St9bad_alloc") != string::npos)
            {
                writeToLog(__FILE__, __LINE__, what, LOG_TYPE_CRITICAL);
//           abort();
            }
            else  writeToLog(__FILE__, __LINE__, what);
        }
        catch (...)
        {
            string msg("exception caught in EngineCommRunner.");
            writeToLog(__FILE__, __LINE__, msg);
            cerr << msg << endl;
        }
    }
};

template <typename T>
struct QueueShutdown : public unary_function<T&, void>
{
    void operator()(T& x)
    {
        x.shutdown();
    }
};

}

/** Debug macro */
#define THROTTLE_DEBUG 0
#if THROTTLE_DEBUG
#define THROTTLEDEBUG std::cout
#else
#define THROTTLEDEBUG if (false) std::cout
#endif

namespace joblist
{
DistributedEngineComm* DistributedEngineComm::fInstance = 0;

/*static*/
DistributedEngineComm* DistributedEngineComm::instance(ResourceManager* rm, bool isExeMgr)
{
    if (fInstance == 0)
        fInstance = new DistributedEngineComm(rm, isExeMgr);

    return fInstance;
}

/*static*/
void DistributedEngineComm::reset()
{
    delete fInstance;
    fInstance = 0;
}

DistributedEngineComm::DistributedEngineComm(ResourceManager* rm, bool isExeMgr) :
    fRm(rm),
    fLBIDShift(fRm->getPsLBID_Shift()),
    pmCount(0),
    fIsExeMgr(isExeMgr)
{

    Setup();
}

DistributedEngineComm::~DistributedEngineComm()
{
    Close();
    fInstance = 0;
}

void DistributedEngineComm::Setup()
{
    // This is here to ensure that this function does not get invoked multiple times simultaneously.
    boost::mutex::scoped_lock setupLock(fSetupMutex);

    makeBusy(true);

    // This needs to be here to ensure that whenever Setup function is called, the lists are
    // empty. It's possible junk was left behind if exception.
    ClientList::iterator iter;

    for (iter = newClients.begin(); iter != newClients.end(); ++iter)
    {
        (*iter)->shutdown();
    }

    newClients.clear();
    newLocks.clear();

    uint32_t newPmCount = fRm->getPsCount();
    throttleThreshold = fRm->getDECThrottleThreshold();
    tbpsThreadCount = fRm->getJlNumScanReceiveThreads();
    fDECConnectionsPerQuery = fRm->getDECConnectionsPerQuery();
    unsigned numConnections = getNumConnections();
    oam::Oam oam;
    ModuleTypeConfig moduletypeconfig;

    try
    {
        oam.getSystemConfig("pm", moduletypeconfig);
    }
    catch (...)
    {
        writeToLog(__FILE__, __LINE__, "oam.getSystemConfig error, unknown exception", LOG_TYPE_ERROR);
        throw runtime_error("Setup failed");
    }

    if (newPmCount == 0)
        writeToLog(__FILE__, __LINE__, "Got a config file with 0 PMs",
                   LOG_TYPE_CRITICAL);

    //This needs to make sense when compared to the extent size
    //     fLBIDShift = static_cast<unsigned>(config::Config::uFromText(fConfig->getConfig(section, "LBID_Shift")));

    auto* config = fRm->getConfig();
    std::vector<messageqcpp::AddrAndPortPair> pmsAddressesAndPorts;
    for (size_t i = 1; i <= newPmCount; ++i)
    {
        std::string pmConfigNodeName("PMS");
        pmConfigNodeName.append(std::to_string(i));
        // The port returned by getAddressAndPort can be 0 but this will be handled by
        // MessageQueueClient::connect
        pmsAddressesAndPorts.push_back(messageqcpp::getAddressAndPort(config,
                                                                      pmConfigNodeName));
    }

    // numConnections must be calculated as number of PMs * number of connections per PM.
    // This happens earlier in getNumConnections().
    for (size_t i = 0; i < numConnections; ++i)
    {
        size_t connectionId = i % newPmCount;
        boost::shared_ptr<MessageQueueClient> cl(new MessageQueueClient(pmsAddressesAndPorts[connectionId].first,
                                                                        pmsAddressesAndPorts[connectionId].second));
        boost::shared_ptr<boost::mutex> nl(new boost::mutex());

        try
        {
            if (cl->connect())
            {
                newClients.push_back(cl);
                // assign the module name
                cl->moduleName(getModuleNameByIPAddr(moduletypeconfig, cl->addr2String()));
                newLocks.push_back(nl);
                StartClientListener(cl, i);
            }
            else
            {
                throw runtime_error("Connection refused from PMS" + std::to_string(connectionId));
            }
        }
        catch (std::exception& ex)
        {
            if (i < newPmCount)
                newPmCount--;

            writeToLog(__FILE__, __LINE__, "Could not connect to PMS" + std::to_string(connectionId) + ": " + ex.what(), LOG_TYPE_ERROR);
            cerr << "Could not connect to PMS" << std::to_string(connectionId) << ": " << ex.what() << endl;
        }
        catch (...)
        {
            if (i < newPmCount)
                newPmCount--;

            writeToLog(__FILE__, __LINE__, "Could not connect to PMS" + std::to_string(connectionId), LOG_TYPE_ERROR);
        }
    }

    // for every entry in newClients up to newPmCount, scan for the same ip in the
    // first pmCount.  If there is no match, it's a new node,
    //    call the event listeners' newPMOnline() callbacks.
    boost::mutex::scoped_lock lock(eventListenerLock);

    for (uint32_t i = 0; i < newPmCount; i++)
    {
        uint32_t j;

        for (j = 0; j < pmCount; j++)
        {
            if (!fPmConnections.empty() && j < fPmConnections.size() &&
                newClients[i]->isSameAddr(*fPmConnections[j]))
            {
                break;
            }
        }

        if (j == pmCount)
            for (uint32_t k = 0; k < eventListeners.size(); k++)
                eventListeners[k]->newPMOnline(i);
    }

    lock.unlock();

    fWlock.swap(newLocks);
    fPmConnections.swap(newClients);
    // memory barrier to prevent the pmCount assignment migrating upward
    atomicops::atomicMb();
    pmCount = newPmCount;

    newLocks.clear();
    newClients.clear();
}

int DistributedEngineComm::Close()
{
    //cout << "DistributedEngineComm::Close() called" << endl;

    makeBusy(false);
    // for each MessageQueueClient in pmConnections delete the MessageQueueClient;
    fPmConnections.clear();
    fPmReader.clear();
    return 0;
}

void DistributedEngineComm::Listen(boost::shared_ptr<MessageQueueClient> client, uint32_t connIndex)
{
    SBS sbs;

    try
    {
        while (Busy())
        {
            Stats stats;
            //TODO: This call blocks so setting Busy() in another thread doesn't work here...
            sbs = client->read(0, NULL, &stats);

            if (sbs->length() != 0)
            {
                addDataToOutput(sbs, connIndex, &stats);
            }
            else // got zero bytes on read, nothing more will come
                goto Error;
        }

        return;
    }
    catch (std::exception& e)
    {
        cerr << "DEC Caught EXCEPTION: " << e.what() << endl;
        goto Error;
    }
    catch (...)
    {
        cerr << "DEC Caught UNKNOWN EXCEPT" << endl;
        goto Error;
    }

Error:
    // @bug 488 - error condition! push 0 length bs to messagequeuemap and
    // eventually let jobstep error out.
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok;
    sbs.reset(new ByteStream(0));

    for (map_tok = fSessionMessages.begin(); map_tok != fSessionMessages.end(); ++map_tok)
    {
        map_tok->second->queue.clear();
        (void)atomicops::atomicInc(&map_tok->second->unackedWork[0]);
        map_tok->second->queue.push(sbs);
    }
    lk.unlock();

    if (fIsExeMgr)
    {
        //std::cout << "WARNING: DEC READ 0 LENGTH BS FROM "
        //    << client->otherEnd()<< " OR GOT AN EXCEPTION READING" << std::endl;
        decltype(pmCount) originalPMCount = pmCount;
        // Re-establish if a remote PM restarted.
        std::this_thread::sleep_for(std::chrono::seconds(3));
        Setup();
        if (originalPMCount != pmCount)
        {
            ostringstream os;
            os << "DEC: lost connection to " << client->addr2String();
            writeToLog(__FILE__, __LINE__, os.str(), LOG_TYPE_ERROR);
        }

/*
        // reset the pmconnection vector
        ClientList tempConns;
        boost::mutex::scoped_lock onErrLock(fOnErrMutex);
        string moduleName = client->moduleName();
        //cout << "moduleName=" << moduleName << endl;
        for ( uint32_t i = 0; i < fPmConnections.size(); i++)
        {
            if (moduleName != fPmConnections[i]->moduleName())
                tempConns.push_back(fPmConnections[i]);
            //else
            //cout << "DEC remove PM" << fPmConnections[i]->otherEnd() << " moduleName=" << fPmConnections[i]->moduleName() << endl;
        }

        if (tempConns.size() == fPmConnections.size()) return;

        fPmConnections.swap(tempConns);
        pmCount = (pmCount == 0 ? 0 : pmCount - 1);
        //cout << "PMCOUNT=" << pmCount << endl;

        // log it
        ostringstream os;
        os << "DEC: lost connection to " << client->addr2String();
        writeToLog(__FILE__, __LINE__, os.str(), LOG_TYPE_CRITICAL);
*/
    }
    return;
}

void DistributedEngineComm::addQueue(uint32_t key, bool sendACKs)
{
    bool b;

    boost::mutex* lock = new boost::mutex();
    condition* cond = new condition();
    uint32_t firstPMInterleavedConnectionId = key % (fPmConnections.size() / pmCount) * fDECConnectionsPerQuery * pmCount % fPmConnections.size();
    boost::shared_ptr<MQE> mqe(new MQE(pmCount, firstPMInterleavedConnectionId));

    mqe->queue = StepMsgQueue(lock, cond);
    mqe->sendACKs = sendACKs;
    mqe->throttled = false;

    boost::mutex::scoped_lock lk ( fMlock );
    b = fSessionMessages.insert(pair<uint32_t, boost::shared_ptr<MQE> >(key, mqe)).second;

    if (!b)
    {
        ostringstream os;
        os << "DEC: attempt to add a queue with a duplicate ID " << key << endl;
        throw runtime_error(os.str());
    }
}

void DistributedEngineComm::removeQueue(uint32_t key)
{
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
        return;

    map_tok->second->queue.shutdown();
    map_tok->second->queue.clear();
    fSessionMessages.erase(map_tok);
}

void DistributedEngineComm::shutdownQueue(uint32_t key)
{
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
        return;

    map_tok->second->queue.shutdown();
    map_tok->second->queue.clear();
}

void DistributedEngineComm::read(uint32_t key, SBS& bs)
{
    boost::shared_ptr<MQE> mqe;

    //Find the StepMsgQueueList for this session
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
    {
        ostringstream os;

        os << "DEC: attempt to read(bs) from a nonexistent queue\n";
        throw runtime_error(os.str());
    }

    mqe = map_tok->second;
    lk.unlock();

    //this method can block: you can't hold any locks here...
    TSQSize_t queueSize = mqe->queue.pop(&bs);

    if (bs && mqe->sendACKs)
    {
        boost::mutex::scoped_lock lk(ackLock);

        if (mqe->throttled && !mqe->hasBigMsgs && queueSize.size <= disableThreshold)
            setFlowControl(false, key, mqe);

        vector<SBS> v;
        v.push_back(bs);
        sendAcks(key, v, mqe, queueSize.size);
    }

    if (!bs)
        bs.reset(new ByteStream());
}

const ByteStream DistributedEngineComm::read(uint32_t key)
{
    SBS sbs;
    boost::shared_ptr<MQE> mqe;

    //Find the StepMsgQueueList for this session
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
    {
        ostringstream os;

        os << "DEC: read(): attempt to read from a nonexistent queue\n";
        throw runtime_error(os.str());
    }

    mqe = map_tok->second;
    lk.unlock();

    TSQSize_t queueSize = mqe->queue.pop(&sbs);

    if (sbs && mqe->sendACKs)
    {
        boost::mutex::scoped_lock lk(ackLock);

        if (mqe->throttled && !mqe->hasBigMsgs && queueSize.size <= disableThreshold)
            setFlowControl(false, key, mqe);

        vector<SBS> v;
        v.push_back(sbs);
        sendAcks(key, v, mqe, queueSize.size);
    }

    if (!sbs)
        sbs.reset(new ByteStream());

    return *sbs;
}

void DistributedEngineComm::read_all(uint32_t key, vector<SBS>& v)
{
    boost::shared_ptr<MQE> mqe;

    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
    {
        ostringstream os;
        os << "DEC: read_all(): attempt to read from a nonexistent queue\n";
        throw runtime_error(os.str());
    }

    mqe = map_tok->second;
    lk.unlock();

    mqe->queue.pop_all(v);

    if (mqe->sendACKs)
    {
        boost::mutex::scoped_lock lk(ackLock);
        sendAcks(key, v, mqe, 0);
    }
}

void DistributedEngineComm::read_some(uint32_t key, uint32_t divisor, vector<SBS>& v,
                                      bool* flowControlOn)
{
    boost::shared_ptr<MQE> mqe;

    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
    {
        ostringstream os;

        os << "DEC: read_some(): attempt to read from a nonexistent queue\n";
        throw runtime_error(os.str());
    }

    mqe = map_tok->second;
    lk.unlock();

    TSQSize_t queueSize = mqe->queue.pop_some(divisor, v, 1);   // need to play with the min #

    if (flowControlOn)
        *flowControlOn = false;

    if (mqe->sendACKs)
    {
        boost::mutex::scoped_lock lk(ackLock);

        if (mqe->throttled && !mqe->hasBigMsgs && queueSize.size <= disableThreshold)
            setFlowControl(false, key, mqe);

        sendAcks(key, v, mqe, queueSize.size);

        if (flowControlOn)
            *flowControlOn = mqe->throttled;
    }
}

void DistributedEngineComm::sendAcks(uint32_t uniqueID, const vector<SBS>& msgs,
                                     boost::shared_ptr<MQE> mqe, size_t queueSize)
{
    ISMPacketHeader* ism;
    uint32_t l_msgCount = msgs.size();

    /* If the current queue size > target, do nothing.
     * If the original queue size > target, ACK the msgs below the target.
     */
    if (!mqe->throttled || queueSize >= mqe->targetQueueSize)
    {
        /* no acks will be sent, but update unackedwork to keep the #s accurate */
        uint16_t numack = 0;
        uint32_t sockidx = 0;

        while (l_msgCount > 0)
        {
            nextPMToACK(mqe, l_msgCount, &sockidx, &numack);
            idbassert(numack <= l_msgCount);
            l_msgCount -= numack;
        }

        return;
    }

    size_t totalMsgSize = 0;

    for (uint32_t i = 0; i < msgs.size(); i++)
        totalMsgSize += msgs[i]->lengthWithHdrOverhead();

    if (queueSize + totalMsgSize > mqe->targetQueueSize)
    {
        /* update unackedwork for the overage that will never be acked */
        int64_t overage = queueSize + totalMsgSize - mqe->targetQueueSize;
        uint16_t numack = 0;
        uint32_t sockidx = 0;
        uint32_t msgsToIgnore;

        for (msgsToIgnore = 0; overage >= 0; msgsToIgnore++)
            overage -= msgs[msgsToIgnore]->lengthWithHdrOverhead();

        if (overage < 0)
            msgsToIgnore--;

        l_msgCount = msgs.size() - msgsToIgnore;  // this num gets acked

        while (msgsToIgnore > 0)
        {
            nextPMToACK(mqe, msgsToIgnore, &sockidx, &numack);
            idbassert(numack <= msgsToIgnore);
            msgsToIgnore -= numack;
        }
    }

    if (l_msgCount > 0)
    {
        ByteStream msg(sizeof(ISMPacketHeader));
        uint16_t* toAck;
        vector<bool> pmAcked(pmCount, false);

        ism = (ISMPacketHeader*) msg.getInputPtr();
        // The only var checked by ReadThread is the Command var.  The others
        // are wasted space.  We hijack the Size, & Flags fields for the
        // params to the ACK msg.

        ism->Interleave = uniqueID;
        ism->Command = BATCH_PRIMITIVE_ACK;
        toAck = &ism->Size;

        msg.advanceInputPtr(sizeof(ISMPacketHeader));

        while (l_msgCount > 0)
        {
            /* could have to send up to pmCount ACKs */
            uint32_t sockIndex = 0;

            /* This will reset the ACK field in the Bytestream directly, and nothing
             * else needs to change if multiple msgs are sent. */
            nextPMToACK(mqe, l_msgCount, &sockIndex, toAck);
            idbassert(*toAck <= l_msgCount);
            l_msgCount -= *toAck;
            pmAcked[sockIndex] = true;
            writeToClient(sockIndex, msg);
        }

        // @bug4436, when no more unacked work, send an ack to all PMs that haven't been acked.
        // This is apply to the big message case only.  For small messages, the flow control is
        // disabled when the queue size is below the disableThreshold.
        if (mqe->hasBigMsgs)
        {
            uint64_t totalUnackedWork = 0;

            for (uint32_t i = 0; i < pmCount; i++)
                totalUnackedWork += mqe->unackedWork[i];

            if (totalUnackedWork == 0)
            {
                *toAck = 1;

                for (uint32_t i = 0; i < pmCount; i++)
                {
                    if (!pmAcked[i])
                        writeToClient(i, msg);
                }
            }
        }
    }
}

void DistributedEngineComm::nextPMToACK(boost::shared_ptr<MQE> mqe, uint32_t maxAck,
                                        uint32_t* sockIndex, uint16_t* numToAck)
{
    uint32_t i;
    uint32_t& nextIndex = mqe->ackSocketIndex;

    /* Other threads can be touching mqe->unackedWork at the same time, but because of
     * the locking env, mqe->unackedWork can only grow; whatever gets latched in this fcn
     * is a safe minimum at the point of use. */

    if (mqe->unackedWork[nextIndex] >= maxAck)
    {
        (void)atomicops::atomicSub(&mqe->unackedWork[nextIndex], maxAck);
        *sockIndex = nextIndex;
        //FIXME: we're going to truncate here from 32 to 16 bits. Hopefully this will always fit...
        *numToAck = maxAck;

        if (pmCount > 0)
            nextIndex = (nextIndex + 1) % pmCount;

        return;
    }
    else
    {
        for (i = 0; i < pmCount; i++)
        {
            uint32_t curVal = mqe->unackedWork[nextIndex];
            uint32_t unackedWork = (curVal > maxAck ? maxAck : curVal);

            if (unackedWork > 0)
            {
                (void)atomicops::atomicSub(&mqe->unackedWork[nextIndex], unackedWork);
                *sockIndex = nextIndex;
                *numToAck = unackedWork;

                if (pmCount > 0)
                    nextIndex = (nextIndex + 1) % pmCount;

                return;
            }

            if (pmCount > 0)
                nextIndex = (nextIndex + 1) % pmCount;
        }

        cerr << "DEC::nextPMToACK(): Couldn't find a PM to ACK! ";

        for (i = 0; i < pmCount; i++)
            cerr << mqe->unackedWork[i] << " ";

        cerr << " max: " << maxAck;
        cerr << endl;
        //make sure the returned vars are legitimate
        *sockIndex = nextIndex;
        *numToAck = maxAck / pmCount;

        if (pmCount > 0)
            nextIndex = (nextIndex + 1) % pmCount;

        return;
    }
}

void DistributedEngineComm::setFlowControl(bool enabled, uint32_t uniqueID, boost::shared_ptr<MQE> mqe)
{
    mqe->throttled = enabled;
    ByteStream msg(sizeof(ISMPacketHeader));
    ISMPacketHeader* ism = (ISMPacketHeader*) msg.getInputPtr();

    ism->Interleave = uniqueID;
    ism->Command = BATCH_PRIMITIVE_ACK;
    ism->Size = (enabled ? 0 : -1);

#ifdef VALGRIND
    /* XXXPAT: For testing in valgrind, init the vars that don't get used */
    ism->Flags = 0;
    ism->Type = 0;
    ism->MsgCount = 0;
    ism->Status = 0;
#endif

    msg.advanceInputPtr(sizeof(ISMPacketHeader));

    for (uint32_t i = 0; i < mqe->pmCount; i++)
        writeToClient(i, msg);
}

void DistributedEngineComm::write(uint32_t senderID, ByteStream& msg)
{
    ISMPacketHeader* ism = (ISMPacketHeader*) msg.buf();
    uint32_t dest;
    uint32_t numConn = fPmConnections.size();

    if (numConn > 0)
    {
        switch (ism->Command)
        {
            case BATCH_PRIMITIVE_CREATE:
                /* Disable flow control initially */
                msg << (uint32_t) - 1;
		    /* FALLTHRU */

            case BATCH_PRIMITIVE_DESTROY:
            case BATCH_PRIMITIVE_ADD_JOINER:
            case BATCH_PRIMITIVE_END_JOINER:
            case BATCH_PRIMITIVE_ABORT:
            case DICT_CREATE_EQUALITY_FILTER:
            case DICT_DESTROY_EQUALITY_FILTER:
                /* XXXPAT: This relies on the assumption that the first pmCount "PMS*"
                entries in the config file point to unique PMs */
                uint32_t i;

                for (i = 0; i < pmCount; i++)
                    writeToClient(i, msg, senderID);

                return;

            case BATCH_PRIMITIVE_RUN:
            case DICT_TOKEN_BY_SCAN_COMPARE:
                // for efficiency, writeToClient() grabs the interleaving factor for the caller,
                // and decides the final connection index because it already grabs the
                // caller's queue information
                dest = ism->Interleave;
                writeToClient(dest, msg, senderID, true);
                break;

            default:
                idbassert_s(0, "Unknown message type");
        }
    }
    else
    {
        writeToLog(__FILE__, __LINE__, "No PrimProcs are running", LOG_TYPE_DEBUG);
        throw IDBExcept(ERR_NO_PRIMPROC);
    }
}

void DistributedEngineComm::write(messageqcpp::ByteStream& msg, uint32_t connection)
{
    ISMPacketHeader* ism = (ISMPacketHeader*) msg.buf();
    PrimitiveHeader* pm = (PrimitiveHeader*) (ism + 1);
    uint32_t senderID = pm->UniqueID;

    boost::mutex::scoped_lock lk(fMlock, boost::defer_lock_t());
    MessageQueueMap::iterator it;
    // This keeps mqe's stats from being freed until end of function
    boost::shared_ptr<MQE> mqe;
    Stats* senderStats = NULL;

    lk.lock();
    it = fSessionMessages.find(senderID);

    if (it != fSessionMessages.end())
    {
        mqe = it->second;
        senderStats = &(mqe->stats);
    }

    lk.unlock();

    newClients[connection]->write(msg, NULL, senderStats);
}

void DistributedEngineComm::StartClientListener(boost::shared_ptr<MessageQueueClient> cl, uint32_t connIndex)
{
    boost::thread* thrd = new boost::thread(EngineCommRunner(this, cl, connIndex));
    fPmReader.push_back(thrd);
}

void DistributedEngineComm::addDataToOutput(SBS sbs, uint32_t connIndex, Stats* stats)
{
    ISMPacketHeader* hdr = (ISMPacketHeader*)(sbs->buf());
    PrimitiveHeader* p = (PrimitiveHeader*)(hdr + 1);
    uint32_t uniqueId = p->UniqueID;
    boost::shared_ptr<MQE> mqe;

    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(uniqueId);

    if (map_tok == fSessionMessages.end())
    {
        // For debugging...
        //cerr << "DistributedEngineComm::AddDataToOutput: tried to add a message to a dead session: " << uniqueId << ", size " << sbs->length() << ", step id " << p->StepID << endl;
        return;
    }

    mqe = map_tok->second;
    lk.unlock();

    if (pmCount > 0)
    {
        (void)atomicops::atomicInc(&mqe->unackedWork[connIndex % pmCount]);
    }

    TSQSize_t queueSize = mqe->queue.push(sbs);

    if (mqe->sendACKs)
    {
        boost::mutex::scoped_lock lk(ackLock);
        uint64_t msgSize = sbs->lengthWithHdrOverhead();

        if (!mqe->throttled && msgSize > (targetRecvQueueSize / 2))
            doHasBigMsgs(mqe, (300 * 1024 * 1024 > 3 * msgSize ?
                               300 * 1024 * 1024 : 3 * msgSize)); //buffer at least 3 big msgs

        if (!mqe->throttled && queueSize.size >= mqe->targetQueueSize)
            setFlowControl(true, uniqueId, mqe);
    }

    if (stats)
        mqe->stats.dataRecvd(stats->dataRecvd());
}

void DistributedEngineComm::doHasBigMsgs(boost::shared_ptr<MQE> mqe, uint64_t targetSize)
{
    mqe->hasBigMsgs = true;

    if (mqe->targetQueueSize < targetSize)
        mqe->targetQueueSize = targetSize;
}

int DistributedEngineComm::writeToClient(size_t aPMIndex, const ByteStream& bs, uint32_t senderUniqueID, bool doInterleaving)
{
    boost::mutex::scoped_lock lk(fMlock, boost::defer_lock_t());
    MessageQueueMap::iterator it;
    // Keep mqe's stats from being freed early
    boost::shared_ptr<MQE> mqe;
    Stats* senderStats = NULL;

    if (fPmConnections.size() == 0)
        return 0;

    uint32_t connectionId = aPMIndex;
    if (senderUniqueID != numeric_limits<uint32_t>::max())
    {
        lk.lock();
        it = fSessionMessages.find(senderUniqueID);

        if (it != fSessionMessages.end())
        {
            mqe = it->second;
            senderStats = &(mqe->stats);
            size_t pmIndex = aPMIndex % mqe->pmCount;
            connectionId = it->second->getNextConnectionId(pmIndex,
                                                           fPmConnections.size(),
                                                           fDECConnectionsPerQuery);
        }

        lk.unlock();
    }

    try
    {
        ClientList::value_type client = fPmConnections[connectionId];

        if (!client->isAvailable()) return 0;

        boost::mutex::scoped_lock lk(*(fWlock[connectionId]));
        client->write(bs, NULL, senderStats);
        return 0;
    }
    catch (...)
    {
        // @bug 488. error out under such condition instead of re-trying other connection,
        // by pushing 0 size bytestream to messagequeue and throw exception
        SBS sbs;
        lk.lock();
        //std::cout << "WARNING: DEC WRITE BROKEN PIPE. PMS index = " << index << std::endl;
        MessageQueueMap::iterator map_tok;
        sbs.reset(new ByteStream(0));

        for (map_tok = fSessionMessages.begin(); map_tok != fSessionMessages.end(); ++map_tok)
        {
            map_tok->second->queue.clear();
            (void)atomicops::atomicInc(&map_tok->second->unackedWork[0]);
            map_tok->second->queue.push(sbs);
        }

        lk.unlock();
        /*
        		// reconfig the connection array
        		ClientList tempConns;
        		{
        			//cout << "WARNING: DEC WRITE BROKEN PIPE " << fPmConnections[index]->otherEnd()<< endl;
        			boost::mutex::scoped_lock onErrLock(fOnErrMutex);
        			string moduleName = fPmConnections[index]->moduleName();
        			//cout << "module name = " << moduleName << endl;
        			if (index >= fPmConnections.size()) return 0;

        			for (uint32_t i = 0; i < fPmConnections.size(); i++)
        			{
        				if (moduleName != fPmConnections[i]->moduleName())
        					tempConns.push_back(fPmConnections[i]);
        			}
        			if (tempConns.size() == fPmConnections.size()) return 0;
        			fPmConnections.swap(tempConns);
        			pmCount = (pmCount == 0 ? 0 : pmCount - 1);
        		}
        // send alarm
        ALARMManager alarmMgr;
        string alarmItem("UNKNOWN");

        if (index < fPmConnections.size())
        {
            alarmItem = fPmConnections[index]->addr2String();
        }

        alarmItem.append(" PrimProc");
        alarmMgr.sendAlarmReport(alarmItem.c_str(), oam::CONN_FAILURE, SET);
        */
        throw runtime_error("DistributedEngineComm::write: Broken Pipe error");
    }
}

uint32_t DistributedEngineComm::size(uint32_t key)
{
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator map_tok = fSessionMessages.find(key);

    if (map_tok == fSessionMessages.end())
        throw runtime_error("DEC::size() attempt to get the size of a nonexistant queue!");

    boost::shared_ptr<MQE> mqe = map_tok->second;
    //TODO: should probably check that this is a valid iter...
    lk.unlock();
    return mqe->queue.size().count;
}

void DistributedEngineComm::addDECEventListener(DECEventListener* l)
{
    boost::mutex::scoped_lock lk(eventListenerLock);
    eventListeners.push_back(l);
}

void DistributedEngineComm::removeDECEventListener(DECEventListener* l)
{
    boost::mutex::scoped_lock lk(eventListenerLock);
    std::vector<DECEventListener*> newListeners;
    uint32_t s = eventListeners.size();

    for (uint32_t i = 0; i < s; i++)
        if (eventListeners[i] != l)
            newListeners.push_back(eventListeners[i]);

    eventListeners.swap(newListeners);
}

Stats DistributedEngineComm::getNetworkStats(uint32_t uniqueID)
{
    boost::mutex::scoped_lock lk(fMlock);
    MessageQueueMap::iterator it;
    Stats empty;

    it = fSessionMessages.find(uniqueID);

    if (it != fSessionMessages.end())
        return it->second->stats;

    return empty;
}

DistributedEngineComm::MQE::MQE(const uint32_t pCount, const uint32_t initialInterleaverValue)
    : ackSocketIndex(0), pmCount(pCount), hasBigMsgs(false), targetQueueSize(targetRecvQueueSize)
{
    unackedWork.reset(new volatile uint32_t[pmCount]);
    interleaver.reset(new uint32_t[pmCount]);
    memset((void*) unackedWork.get(), 0, pmCount * sizeof(uint32_t));
    uint32_t interleaverValue = initialInterleaverValue;
    initialConnectionId = initialInterleaverValue;
    for (size_t pmId = 0; pmId < pmCount; ++pmId)
    {
        interleaver.get()[pmId] = interleaverValue++;
    }
}

// Here is the assumed connections distribution schema in a pmConnections vector.
// (PM1-PM2...-PMX)-(PM1-PM2..-PMX)...
uint32_t DistributedEngineComm::MQE::getNextConnectionId(const size_t pmIndex,
                                                         const size_t pmConnectionsNumber,
                                                         const uint32_t DECConnectionsPerQuery)

{
    uint32_t nextConnectionId = (interleaver[pmIndex] + pmCount) % pmConnectionsNumber;
    // Wrap around the connection id.
    if ((nextConnectionId - pmIndex) % DECConnectionsPerQuery == 0)
    {
        // The sum must be < connections vector size.
        nextConnectionId = initialConnectionId + pmIndex;
        interleaver[pmIndex] = nextConnectionId;
    }
    else
    {
        interleaver[pmIndex] = nextConnectionId;
    }
    return nextConnectionId;
}

}
// vim:ts=4 sw=4:

max_stars_repo_name: zettadb/zettalib

max_issues_repo_name: zettadb/zettalib

max_forks_repo_name: zettadb/zettalib


text:
/*
 Copyright (c) Sebastian Herrlinger - All rights reserved.
 This code is intended for use with the Cinder C++ library: http://libcinder.org

 Redistribution and use in source and binary forms, with or without modification, are permitted provided that
 the following conditions are met:

 * Redistributions of source code must retain the above copyright notice, this list of conditions and
 the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
 the following disclaimer in the documentation and/or other materials provided with the distribution.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef _CinderjsApp_hpp_
#define _CinderjsApp_hpp_

#pragma once

#include "cinder/gl/gl.h"
#include "cinder/Filesystem.h"
#include "cinder/ConcurrentCircularBuffer.h"

#include <map>
#include <functional>
#include <boost/any.hpp>

#include "js_natives.h"
#include "AppConsole.h"
#include "CinderAppBase.hpp"
#include "StaticFactory.hpp"
#include "Timer.h"

#include "v8.h"
#include "libplatform/libplatform.h"

// Trying not to use namespaces in header files
// using namespace cinder;

namespace cjs {

typedef boost::filesystem::path Path;

class BufferedEventHolder {
  public:
  int type;
  cinder::app::MouseEvent mEvt;
  cinder::app::KeyEvent kEvt;
  std::vector<cinder::fs::path> fdFiles;
  v8::Persistent<v8::Function> v8Fn;
};
typedef std::shared_ptr<BufferedEventHolder> BufferedEvent;

class NextFrameFnHolder {
  public:
  v8::Persistent<v8::Function> v8Fn;
  bool repeat = false;
};
typedef std::shared_ptr<NextFrameFnHolder> NextFrameFn;

enum EventType {
  CJS_SHUTDOWN_REQUEST = 0,
  //CJS_NEXT_FRAME = 1,
  CJS_RESIZE = 10,
  CJS_KEY_DOWN = 20,
  CJS_KEY_UP = 30,
  CJS_MOUSE_DOWN = 40,
  CJS_MOUSE_UP = 50,
  CJS_FILE_DROP = 100
};

// TODO
// - Expose Env info like OS etc.
// - Expose versions object (cinder, v8, cinderjs)
// - uncaughtException handler (process "uncaughtException" event)
// - Bind Lambdas in modules instead of static class methods

// Design Notes:
// - The implementation and native/js communication is trying to avoid object instantiation
//   to have less conversion (object wrapping/unwrapping) costs when calling C++ methods from js and the other way around.
//   This behaviour can be mimiced within js itself. Converting numbers is way faster then
//   Unpacking full fledged js objects to C++ seems more expensive. ( eg. args[0]->ToObject()->Get(...) )

class CinderjsApp : public CinderAppBase  {
  public:
  CinderjsApp() : mEventQueue(1024) {}
  ~CinderjsApp(){}

  // Cinder App
	void setup();
  void cleanup();
  void mouseDown( cinder::app::MouseEvent event );
	void mouseUp( cinder::app::MouseEvent event );
	void mouseMove( cinder::app::MouseEvent event );
  void keyDown( cinder::app::KeyEvent event );
  void keyUp( cinder::app::KeyEvent event );
	void update();
	void draw();
  void resize();
  void fileDrop( cinder::app::FileDropEvent event );

  // V8 Setup
  void v8Setup( std::string jsFileContents );
  static v8::Local<v8::Value> executeScriptString( std::string scriptStr, v8::Isolate* isolate,
    v8::Local<v8::Context> context, v8::Handle<v8::String> filename );

  //
  private:

  // Stats
  volatile int v8Frames = 0;
  volatile double v8FPS = 0;
  volatile int mLastUpdate = 0;
  volatile int mUpdateInterval = 1000;

  // Threading
  volatile bool mShouldQuit;
  std::mutex mMainMutex;

  std::condition_variable cvMainThread;
  volatile bool _mainRun = false;
  std::condition_variable cvEventThread;
  volatile bool _eventRun = false;

  cinder::app::RendererRef glRenderer;

  // Eventing
  cinder::ConcurrentCircularBuffer<BufferedEvent> mEventQueue;
  static cinder::ConcurrentCircularBuffer<NextFrameFn> sExecutionQueue;
  volatile cinder::vec2 mousePosBuf;

  // Path
  Path mCwd;

  // Threads
  void v8EventThread( cinder::gl::ContextRef context );
  std::shared_ptr<std::thread> mV8EventThread;

  // Modules
  static v8::Persistent<v8::Object> sModuleCache;
  static v8::Persistent<v8::Array> sModuleList;

  // GC
  static void gcPrologueCb(v8::Isolate *isolate, v8::GCType type, v8::GCCallbackFlags flags);
  static int sGCRuns;

  // Default Bindings
  static void setDrawCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void setEventCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void toggleAppConsole(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void toggleV8Stats(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void toggleFPS(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void requestQuit(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void nextFrameJS(const v8::FunctionCallbackInfo<v8::Value>& args);
  static void setTimer(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Default Process Bindings
  static void NativeBinding(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Default Callbacks
  static v8::Persistent<v8::Function> sDrawCallback;
  static v8::Local<v8::Function> _fnDrawCallback;
  static v8::Persistent<v8::Function> sEventCallback; // TODO: only push events that were subscribed to in v8

  //
  static v8::Persistent<v8::Object> sEmptyObject;

  // Console
  static volatile bool _consoleActive;
  static volatile bool _v8StatsActive;
  static volatile bool _fpsActive;

  // Error Handling
  static void handleV8TryCatch( v8::TryCatch &tryCatch, std::string info );

  // Timers
  static Timer _mainTimer;
  static std::function<void(boost::any passOn)> _timerCallback;

  // Quit
  static bool sQuitRequested;

  // Default key behaviour
  double mLastEscPressed;

  void v8Draw();
  double lastFrameTime = 0;
  v8::HeapStatistics _mHeapStats;
};

} // namespace cjs

#endif

ext: hpp

max_stars_repo_name: kommander/cinderjs

max_issues_repo_name: kommander/cinderjs

max_forks_repo_name: kommander/cinderjs


text:
// Copyright (c) 2019 Tim Perkins

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// “Software”), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#ifndef TTFRM_TFRM_HPP
#define TTFRM_TFRM_HPP

#include <exception>
#include <tuple>

#include <fmt/format.h>
#include <Eigen/Geometry>

namespace ttfrm {

using Quat = Eigen::Quaterniond;
using Vec3 = Eigen::Vector3d;

template <typename FrameId>
class Tfrm;

template <typename FrameId_>
struct FramePair {
  using FrameId = FrameId_;

  FrameId to_frame;
  FrameId from_frame;
};

template <typename FrameId>
std::string Stringify(const Tfrm<FrameId>& tfrm);

template <typename FrameId>
std::string Stringify(const FramePair<FrameId>& frame_pair);

template <typename FrameId_>
class Tfrm {
 public:
  using FrameId = FrameId_;

  Tfrm(const FrameId& to_frame, const FrameId& from_frame, const Quat& rot, const Vec3& trans);
  Tfrm(const Tfrm& other_tfrm);
  Tfrm(Tfrm&& other_tfrm);

  static Tfrm<FrameId> Identity(const FrameId& to_frame, const FrameId& from_frame);
  static Tfrm<FrameId> FromRotation(const FrameId& to_frame, const FrameId& from_frame,
                                    const Quat& rot);
  static Tfrm<FrameId> FromTranslation(const FrameId& to_frame, const FrameId& from_frame,
                                       const Vec3& trans);
  static Tfrm<FrameId> FromIsometry(const FrameId& to_frame, const FrameId& from_frame,
                                    const Eigen::Isometry3d& isometry);

  Tfrm<FrameId>& operator=(const Tfrm<FrameId>& other_tfrm);
  Tfrm<FrameId>& operator=(Tfrm<FrameId>&& other_tfrm);

  bool operator==(const Tfrm<FrameId>& other_tfrm) const;
  bool operator!=(const Tfrm<FrameId>& other_tfrm) const;
  bool IsApprox(const Tfrm& other_tfrm, double precision = 1.0e-6) const;

  FramePair<FrameId> Frames() const;
  FrameId ToFrame() const;
  FrameId FromFrame() const;

  Quat Rotation() const;
  Vec3 Translation() const;

  Tfrm<FrameId> Inverse() const;

  Tfrm<FrameId> Compose(const Tfrm<FrameId>& other_tfrm) const;
  Tfrm<FrameId> operator*(const Tfrm<FrameId>& other_tfrm) const;
  Tfrm<FrameId> operator()(const Tfrm<FrameId>& other_tfrm) const;

  Vec3 Apply(const Vec3& trans) const;
  Vec3 operator*(const Vec3& trans) const;
  Vec3 operator()(const Vec3& trans) const;

  Tfrm<FrameId> Interpolate(const FrameId& interp_to_frame, const Tfrm<FrameId>& other_tfrm,
                            double t) const;

  Eigen::Isometry3d AsIsometry() const;

 private:
  FrameId to_frame_;
  FrameId from_frame_;
  Quat rot_;
  Vec3 trans_;

  friend std::string Stringify<FrameId>(const Tfrm<FrameId>& tfrm);
  friend std::string Stringify<FrameId>(const FramePair<FrameId>& frame_pair);
};

class TfrmComposeException : public std::runtime_error {
 public:
  template <typename FrameId>
  TfrmComposeException(const Tfrm<FrameId>& tfrm, const Tfrm<FrameId>& other_tfrm);
  TfrmComposeException(const TfrmComposeException& other_comp_excp) = default;
  TfrmComposeException(TfrmComposeException&& other_comp_excp) = default;
};

class TfrmInterpolateException : public std::runtime_error {
 public:
  template <typename FrameId>
  TfrmInterpolateException(const Tfrm<FrameId>& tfrm, const Tfrm<FrameId>& other_tfrm);
  TfrmInterpolateException(const TfrmInterpolateException& other_comp_excp) = default;
  TfrmInterpolateException(TfrmInterpolateException&& other_comp_excp) = default;
};

template <typename FrameId>
Tfrm<FrameId>::Tfrm(const FrameId& to_frame, const FrameId& from_frame, const Quat& rot,
                    const Vec3& trans)
    : to_frame_(to_frame), from_frame_(from_frame), rot_(rot.normalized()), trans_(trans)
{
  // Do nothing
}

template <typename FrameId>
Tfrm<FrameId>::Tfrm(const Tfrm<FrameId>& other_tfrm)
    : to_frame_(other_tfrm.to_frame_),
      from_frame_(other_tfrm.from_frame_),
      rot_(other_tfrm.rot_),
      trans_(other_tfrm.trans_)
{
  // Do nothing
}

template <typename FrameId>
Tfrm<FrameId>::Tfrm(Tfrm&& other_tfrm)
    : to_frame_(std::move(other_tfrm.to_frame_)),
      from_frame_(std::move(other_tfrm.from_frame_)),
      rot_(std::move(other_tfrm.rot_)),
      trans_(std::move(other_tfrm.trans_))
{
  // Do nothing
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::Identity(const FrameId& to_frame, const FrameId& from_frame)
{
  return Tfrm<FrameId>(to_frame, from_frame, Quat::Identity(), Vec3::Zero());
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::FromRotation(const FrameId& to_frame, const FrameId& from_frame,
                                          const Quat& rot)
{
  return Tfrm<FrameId>(to_frame, from_frame, rot, Vec3::Zero());
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::FromTranslation(const FrameId& to_frame, const FrameId& from_frame,
                                             const Vec3& trans)
{
  return Tfrm<FrameId>(to_frame, from_frame, Quat::Identity(), trans);
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::FromIsometry(const FrameId& to_frame, const FrameId& from_frame,
                                          const Eigen::Isometry3d& isometry)
{
  return Tfrm<FrameId>(to_frame, from_frame, Quat(isometry.rotation()), isometry.translation());
}

template <typename FrameId>
Tfrm<FrameId>& Tfrm<FrameId>::operator=(const Tfrm& other_tfrm)
{
  to_frame_ = other_tfrm.to_frame_;
  from_frame_ = other_tfrm.from_frame_;
  rot_ = other_tfrm.rot_;
  trans_ = other_tfrm.trans_;
  return *this;
}

template <typename FrameId>
Tfrm<FrameId>& Tfrm<FrameId>::operator=(Tfrm&& other_tfrm)
{
  to_frame_ = std::move(other_tfrm.to_frame_);
  from_frame_ = std::move(other_tfrm.from_frame_);
  rot_ = std::move(other_tfrm.rot_);
  trans_ = std::move(other_tfrm.trans_);
  return *this;
}

template <typename FrameId>
bool Tfrm<FrameId>::operator==(const Tfrm& other_tfrm) const
{
  static auto QuatEquality = [](const Quat& q1, const Quat& q2) {
    return (q1.w() == q2.w() && q1.x() == q2.x() && q1.y() == q2.y() && q1.z() == q2.z());
  };
  return (to_frame_ == other_tfrm.to_frame_ && from_frame_ == other_tfrm.from_frame_
          && QuatEquality(rot_, other_tfrm.rot_) && trans_ == other_tfrm.trans_);
}

template <typename FrameId>
bool Tfrm<FrameId>::operator!=(const Tfrm& other_tfrm) const
{
  return !(*this == other_tfrm);
}

template <typename FrameId>
bool Tfrm<FrameId>::IsApprox(const Tfrm& other_tfrm, double precision) const
{
  return (to_frame_ == other_tfrm.to_frame_ && from_frame_ == other_tfrm.from_frame_
          && rot_.isApprox(other_tfrm.rot_, precision)
          && trans_.isApprox(other_tfrm.trans_, precision));
}

template <typename FrameId>
FramePair<FrameId> Tfrm<FrameId>::Frames() const
{
  return {to_frame_, from_frame_};
}

template <typename FrameId>
FrameId Tfrm<FrameId>::ToFrame() const
{
  return to_frame_;
}

template <typename FrameId>
FrameId Tfrm<FrameId>::FromFrame() const
{
  return from_frame_;
}

template <typename FrameId>
Quat Tfrm<FrameId>::Rotation() const
{
  return rot_;
}

template <typename FrameId>
Vec3 Tfrm<FrameId>::Translation() const
{
  return trans_;
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::Inverse() const
{
  const Quat inv_rot = rot_.inverse();
  return Tfrm<FrameId>(from_frame_, to_frame_, inv_rot, inv_rot * -trans_);
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::Compose(const Tfrm& other_tfrm) const
{
  if (from_frame_ != other_tfrm.to_frame_) {
    throw TfrmComposeException(*this, other_tfrm);
  }
  // T2(T1(v)) = R2 * (R1 * v + t1) + t2 = (R2 * R1) * v + (t2 + R2 * t1)
  return Tfrm<FrameId>(to_frame_, other_tfrm.from_frame_, rot_ * other_tfrm.rot_,
                       trans_ + rot_ * other_tfrm.trans_);
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::operator*(const Tfrm& other_tfrm) const
{
  return Compose(other_tfrm);
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::operator()(const Tfrm& other_tfrm) const
{
  return Compose(other_tfrm);
}

template <typename FrameId>
Vec3 Tfrm<FrameId>::Apply(const Vec3& trans) const
{
  // T(v) = R * v + t
  return Vec3(rot_ * trans + trans_);
}

template <typename FrameId>
Vec3 Tfrm<FrameId>::operator*(const Vec3& trans) const
{
  return Apply(trans);
}

template <typename FrameId>
Vec3 Tfrm<FrameId>::operator()(const Vec3& trans) const
{
  return Apply(trans);
}

template <typename FrameId>
Tfrm<FrameId> Tfrm<FrameId>::Interpolate(const FrameId& interp_to_frame,
                                         const Tfrm<FrameId>& other_tfrm, double ratio) const
{
  if (from_frame_ != other_tfrm.from_frame_) {
    throw TfrmInterpolateException(*this, other_tfrm);
  }
  return Tfrm<FrameId>(interp_to_frame, from_frame_, rot_.slerp(ratio, other_tfrm.rot_),
                       trans_ + ratio * (other_tfrm.trans_ - trans_));
}

template <typename FrameId>
Eigen::Isometry3d Tfrm<FrameId>::AsIsometry() const
{
  // The concatenation of the translation and the rotation
  return Eigen::Translation3d(trans_) * rot_;
}

template <typename FrameId>
TfrmComposeException::TfrmComposeException(const Tfrm<FrameId>& tfrm,
                                           const Tfrm<FrameId>& other_tfrm)
    : std::runtime_error(fmt::format("Cannot compose transforms {} and {}",
                                     Stringify(tfrm.Frames()), Stringify(other_tfrm.Frames())))
{
  // Do nothing
}

template <typename FrameId>
TfrmInterpolateException::TfrmInterpolateException(const Tfrm<FrameId>& tfrm,
                                                   const Tfrm<FrameId>& other_tfrm)
    : std::runtime_error(fmt::format("Cannot interpolate transforms {} and {}",
                                     Stringify(tfrm.Frames()), Stringify(other_tfrm.Frames())))
{
  // Do nothing
}

template <typename FrameId>
std::string Stringify(const Tfrm<FrameId>& tfrm)
{
  return fmt::format(
      "([{}] <- [{}], ROT: (W: {}, X: {}, Y: {}, Z: {}), TRANS: (X: {}, Y: {}, Z: {}))",
      tfrm.to_frame_, tfrm.from_frame_, tfrm.rot_.w(), tfrm.rot_.x(), tfrm.rot_.y(), tfrm.rot_.z(),
      tfrm.trans_.x(), tfrm.trans_.y(), tfrm.trans_.z());
}

template <typename FrameId>
std::string Stringify(const FramePair<FrameId>& frame_pair)
{
  return fmt::format("([{}] <- [{}])", frame_pair.to_frame, frame_pair.from_frame);
}

}  // namespace ttfrm

#endif  // TTFRM_TFRM_HPP


text:
// Copyright 2011 Shinichiro Hamaji. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   1. Redistributions of source code must retain the above copyright
//      notice, this list of  conditions and the following disclaimer.
//
//   2. Redistributions in binary form must reproduce the above
//      copyright notice, this list of conditions and the following
//      disclaimer in the documentation and/or other materials
//      provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY Shinichiro Hamaji ``AS IS'' AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Shinichiro Hamaji OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE.

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <unistd.h>

#include <boost/cstdint.hpp>

#include "read_fat.h"
#include "log.h"
#include "mach-o.h"
#include "loader.h"

DEFINE_bool(READ_SYMTAB,
            false,
            "Read symtab for better backtrace");
DEFINE_bool(READ_DYSYMTAB, false, "Read dysymtab");

typedef long long ll;
typedef unsigned long long ull;

struct sym {
	boost::uint32_t name;
	boost::uint32_t addr;
	boost::uint32_t flag;
};

struct sym64 {
	boost::uint32_t name;
	boost::uint64_t addr;
	boost::uint32_t flag;
};

// See mach-o/nlist.h for this layout
struct nlist {
	boost::uint32_t n_strx;
	boost::uint8_t n_type;
	boost::uint8_t n_sect;
	boost::uint16_t n_desc;
	boost::uint64_t n_value;
};

static boost::uint64_t uleb128(const boost::uint8_t*& p) {
	boost::uint64_t r = 0;
  int s = 0;
  do {
	  r |= (boost::uint64_t)(*p & 0x7f) << s;
    s += 7;
  } while (*p++ >= 0x80);
  return r;
}

static boost::int64_t sleb128(const boost::uint8_t*& p) {
	boost::int64_t r = 0;
  int s = 0;
  for (;;) {
	  boost::uint8_t b = *p++;
    if (b < 0x80) {
      if (b & 0x40) {
        r -= (0x80 - b) << s;
      }
      else {
        r |= (b & 0x3f) << s;
      }
      break;
    }
    r |= (b & 0x7f) << s;
    s += 7;
  }
  return r;
}

class MachOImpl : public MachO {
 public:
  // Take ownership of fd.
  // If len is 0, the size of file will be used as len.
  MachOImpl(const char* filename, FILE *f, size_t offset, size_t len,
            bool need_exports);
  virtual ~MachOImpl();
  virtual void close();

 private:
  class RebaseState;
  friend class MachOImpl::RebaseState;
  class BindState;
  friend class MachOImpl::BindState;

  template <class segment_command, class section>
  void readSegment(char* cmds_ptr,
                   vector<segment_command*>* segments,
                   vector<section*>* bind_sections);
  void readRebase(const uint8_t* p, const uint8_t* end);
  void readBind(const uint8_t* p, const uint8_t* end, bool is_weak);
  void readExport(const uint8_t* start, const uint8_t* p, const uint8_t* end,
                  string* name_buf);

  template <class section>
  void readClassicBind(const section& sec,
                       uint32_t* dysyms,
                       uint32_t* symtab,
                       const char* symstrtab) {
    uint32_t indirect_offset = sec.reserved1;
    int count = sec.size / ptrsize_;
    for (int i = 0; i < count; i++) {
      uint32_t dysym = dysyms[indirect_offset + i];
      uint32_t index = dysym & 0x3fffffff;
      uint32_t* sym = symtab;
      sym += index * (is64_ ? 4 : 3);

      MachO::Bind* bind = new MachO::Bind();
      bind->name = symstrtab + sym[0];
      bind->vmaddr = sec.addr + i * ptrsize_;
      bind->addend = 0;
      bind->type = BIND_TYPE_POINTER;
      bind->ordinal = 1;
      binds_.push_back(bind);
    }
  }

  char* mapped_;
  size_t mapped_size_;
  bool need_exports_;
};

template <class segment_command, class section>
void MachOImpl::readSegment(char* cmds_ptr,
                            vector<segment_command*>* segments,
                            vector<section*>* bind_sections) {
  segment_command* segment =
    reinterpret_cast<segment_command*>(cmds_ptr);
  segments->push_back(segment);

  LOGF("segment %s: vmaddr=%p vmsize=%llu "
       "fileoff=%llu filesize=%llu "
       "maxprot=%d initprot=%d nsects=%u flags=%u\n",
       segment->segname,
       (void*)segment->vmaddr, (ull)segment->vmsize,
       (ull)segment->fileoff, (ull)segment->filesize,
       segment->maxprot, segment->initprot,
       segment->nsects, segment->flags);

  section* sections = reinterpret_cast<section*>(
    cmds_ptr + sizeof(segment_command));
  for (uint32_t j = 0; j < segment->nsects; j++) {
    const section& sec = sections[j];
    LOGF("section %s in %s: "
         "addr=%p size=%llu offset=%u align=%u "
         "reloff=%u nreloc=%u flags=%u "
         "reserved1=%u reserved2=%u\n",
         sec.sectname, sec.segname,
         (void*)sec.addr, (ull)sec.size,
         sec.offset, sec.align,
         sec.reloff, sec.nreloc, sec.flags,
         sec.reserved1, sec.reserved2);

    if (!strcmp(sec.sectname, "__dyld") &&
        !strcmp(sec.segname, "__DATA")) {
      dyld_data_ = sec.addr;
    }

    int section_type = sec.flags & SECTION_TYPE;
    switch (section_type) {
    case S_REGULAR:
        /* Regular section: nothing to do */
        break;

    case S_MOD_INIT_FUNC_POINTERS: {
      for (uint64_t p = sec.addr; p < sec.addr + sec.size; p += ptrsize_) {
        init_funcs_.push_back(p);
      }
      break;
    }
    case S_NON_LAZY_SYMBOL_POINTERS:
    case S_LAZY_SYMBOL_POINTERS: {
      bind_sections->push_back(sections + j);
      break;
    }
    case S_ZEROFILL:
    case S_CSTRING_LITERALS:
    case S_4BYTE_LITERALS:
    case S_8BYTE_LITERALS:
    case S_LITERAL_POINTERS:
    case S_SYMBOL_STUBS:
    case S_MOD_TERM_FUNC_POINTERS:
      // TODO(hamaji): Support term_funcs.
    case S_COALESCED:
    case S_GB_ZEROFILL:
    case S_INTERPOSING:
    case S_16BYTE_LITERALS:
    case S_DTRACE_DOF:
    case S_LAZY_DYLIB_SYMBOL_POINTERS:
      LOGF("FIXME: section type %d will not be handled for %s in %s\n",
           section_type, sec.sectname, sec.segname);
      break;

    default:
        fprintf(stderr, "Unknown section type: %d\n", section_type);
        abort();
        break;
    }
  }
}

struct MachOImpl::RebaseState {
  explicit RebaseState(MachOImpl* mach0)
    : mach(mach0), type(0), seg_index(0), seg_offset(0) {}

  bool readRebaseOp(const uint8_t*& p) {
    uint8_t op = *p & REBASE_OPCODE_MASK;
    uint8_t imm = *p & REBASE_IMMEDIATE_MASK;
    p++;
    switch (op) {
    case REBASE_OPCODE_DONE:
      return false;

    case REBASE_OPCODE_SET_TYPE_IMM:
      type = imm;
      break;

    case REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
      seg_index = imm;
      seg_offset = uleb128(p);
      break;

    case REBASE_OPCODE_ADD_ADDR_ULEB:
      seg_offset += uleb128(p);
      break;

    case REBASE_OPCODE_ADD_ADDR_IMM_SCALED:
      seg_offset += imm * mach->ptrsize_;
      break;

    case REBASE_OPCODE_DO_REBASE_IMM_TIMES:
      for (int i = 0; i < imm; i++) {
        addRebase();
      }
      break;

    case REBASE_OPCODE_DO_REBASE_ULEB_TIMES: {
      int count = uleb128(p);
      for (int i = 0; i < count; i++) {
        addRebase();
      }
      break;
    }

    case REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB:
      addRebase();
      seg_offset += uleb128(p);
      break;

    case REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB: {
      int count = uleb128(p);
      uint64_t skip = uleb128(p);
      for (int i = 0; i < count; i++) {
        addRebase();
        seg_offset += skip;
      }
      break;
    }

    default:
      fprintf(stderr, "unknown op: %x\n", op);
    }

    return true;
  }

  void addRebase() {
    MachO::Rebase* rebase = new MachO::Rebase();
    uint64_t vmaddr;
    if (mach->is64_) {
      vmaddr = mach->segments64_[seg_index]->vmaddr;
    } else {
      vmaddr = mach->segments_[seg_index]->vmaddr;
    }
    LOGF("add rebase! seg_index=%d seg_offset=%llu type=%d vmaddr=%p\n",
         seg_index, (ull)seg_offset, type, (void*)vmaddr);
    rebase->vmaddr = vmaddr + seg_offset;
    rebase->type = type;
    mach->rebases_.push_back(rebase);

    seg_offset += mach->ptrsize_;
  }

  MachOImpl* mach;
  uint8_t type;
  int seg_index;
  uint64_t seg_offset;
};

void MachOImpl::readRebase(const uint8_t* p, const uint8_t* end) {
  RebaseState state(this);
  while (p < end) {
    if (!state.readRebaseOp(p))
      break;
  }
}

struct MachOImpl::BindState {
  BindState(MachOImpl* mach0, bool is_weak0)
    : mach(mach0), ordinal(0), sym_name(NULL), type(BIND_TYPE_POINTER),
      addend(0), seg_index(0), seg_offset(0), is_weak(is_weak0) {}

  void readBindOp(const uint8_t*& p) {
    uint8_t op = *p & BIND_OPCODE_MASK;
    uint8_t imm = *p & BIND_IMMEDIATE_MASK;
    p++;
    LOGF("bind: op=%x imm=%d\n", op, imm);
    switch (op) {
    case BIND_OPCODE_DONE:
      break;

    case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM:
      ordinal = imm;
      break;

    case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB:
      ordinal = uleb128(p);
      break;

    case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM:
      if (imm == 0) {
        ordinal = 0;
      } else {
        ordinal = BIND_OPCODE_MASK | imm;
      }
      break;

    case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM:
      sym_name = reinterpret_cast<const char*>(p);
      p += strlen(sym_name) + 1;
      LOGF("sym_name=%s\n", sym_name);
      break;

    case BIND_OPCODE_SET_TYPE_IMM:
      type = imm;
      break;

    case BIND_OPCODE_SET_ADDEND_SLEB:
      addend = sleb128(p);
      break;

    case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB:
      seg_index = imm;
      seg_offset = uleb128(p);
      break;

    case BIND_OPCODE_ADD_ADDR_ULEB:
      seg_offset += uleb128(p);
      break;

    case BIND_OPCODE_DO_BIND:
      addBind();
      break;

    case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB:
      LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB\n");
      addBind();
      seg_offset += uleb128(p);
      break;

    case BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED:
      LOGF("BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED %d\n", (int)imm);
      addBind();
      seg_offset += imm * mach->ptrsize_;
      break;

    case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: {
      uint64_t count = uleb128(p);
      uint64_t skip = uleb128(p);
      LOGF("BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB %u %u\n",
           (unsigned)count, (unsigned)skip);
      for (uint64_t i = 0; i < count; i++) {
        addBind();
        seg_offset += skip;
      }
      break;
    }

    default:
      fprintf(stderr, "unknown op: %x\n", op);
    }
  }

  void addBind() {
    MachO::Bind* bind = new MachO::Bind();
    uint64_t vmaddr;
    if (mach->is64_) {
      vmaddr = mach->segments64_[seg_index]->vmaddr;
    } else {
      vmaddr = mach->segments_[seg_index]->vmaddr;
    }
    LOGF("add bind! %s seg_index=%d seg_offset=%llu "
         "type=%d ordinal=%d addend=%lld vmaddr=%p\n",
         sym_name, seg_index, (ull)seg_offset,
         type, ordinal, (ll)addend, (void*)(vmaddr + seg_offset));
    bind->name = sym_name;
    bind->vmaddr = vmaddr + seg_offset;
    bind->addend = addend;
    bind->type = type;
    bind->ordinal = ordinal;
    bind->is_weak = is_weak;
    mach->binds_.push_back(bind);

    seg_offset += mach->ptrsize_;
  }

  MachOImpl* mach;
  uint8_t ordinal;
  const char* sym_name;
  uint8_t type;
  int64_t addend;
  int seg_index;
  uint64_t seg_offset;
  bool is_weak;
};

void MachOImpl::readBind(const uint8_t* p, const uint8_t* end, bool is_weak) {
  BindState state(this, is_weak);
  while (p < end) {
    state.readBindOp(p);
  }
}

void MachOImpl::readExport(const uint8_t* start,
                           const uint8_t* p,
                           const uint8_t* end,
                           string* name_buf) {
  LOGF("readExport: %p-%p\n", p, end);
#if 0
  char buf[17];
  buf[16] = '\0';
  for (int i = 0; i < 16*8; i++) {
    LOGF("%02x ", p[i]);
    buf[i % 16] = p[i] < 32 ? '?' : p[i];
    if (i % 16 == 15) LOGF("%s\n", buf);
  }
#endif

  if (p >= end) {
    fprintf(stderr, "broken export trie\n");
    exit(1);
  }

  if (uint8_t term_size = *p++) {
    const uint8_t* expected_term_end = p + term_size;
    Export* exp = new Export;
    exp->name = *name_buf;
    exp->flag = uleb128(p);
    exp->addr = uleb128(p);
    LOGF("export: %s %lu %p\n",
         name_buf->c_str(), (long)exp->flag, (void*)exp->addr);

    exports_.push_back(exp);

    CHECK(expected_term_end == p);
  }

  const uint8_t num_children = *p++;
  for (uint8_t i = 0; i < num_children; i++) {
    size_t orig_name_size = name_buf->size();
    while (*p) {
      name_buf->push_back(*p++);
    }
    p++;

    uint64_t off = uleb128(p);
    CHECK(off != 0);
    readExport(start, start + off, end, name_buf);

    name_buf->resize(orig_name_size);
  }
}

static
char *memMapFile(FILE *f, size_t len, size_t off) {
	char *buf = (char *)malloc(len);
	assert(buf != NULL);

	fseek(f, 0, SEEK_SET);
	fseek(f, off, SEEK_SET);

	size_t read = fread(buf, sizeof(uint8_t), len, f);

	assert(read == len);

	return buf;
}

MachOImpl::MachOImpl(const char* filename, FILE *f, size_t offset, size_t len,
                     bool need_exports)
  : mapped_(NULL), mapped_size_(len) {
  filename_ = filename;
  need_exports_ = need_exports;
  dyld_data_ = 0;
  //CHECK(fd);
  //fd_ = fd;
  f_ = f;
  offset_ = offset;

  if (!mapped_size_) {
    //mapped_size_ = lseek(fd_, 0, SEEK_END);
	  fseek(f_, 0, SEEK_END);
	  mapped_size_ = ftell(f_);
  }
  fseek(f_, 0, SEEK_SET);
  char *bin = mapped_ = reinterpret_cast<char*>(
	  memMapFile(f_, mapped_size_, offset_));
  /*char* bin = mapped_ = reinterpret_cast<char*>(
    mmap(NULL, mapped_size_,
         PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd_, offset));*/
  base_ = bin;

  mach_header* header = reinterpret_cast<mach_header*>(bin);
  LOGF("magic=%x cpu=%d cpusub=%d file=%d ncmds=%d sizecmd=%d flags=%x\n",
       header->magic, header->cputype, header->cpusubtype,
       header->filetype, header->ncmds, header->sizeofcmds,
       header->flags);

  is64_ = false;
  if (header->magic == MH_MAGIC_64) {
    is64_ = true;
  } else  if (header->magic != MH_MAGIC) {
    fprintf(stderr, "Not mach-o: %s\n", filename);
    exit(1);
  }
  ptrsize_ = is64_ ? 8 : 4;

  /*if ((header->cputype & 0x00ffffff) != CPU_TYPE_X86) {
    fprintf(stderr, "Unsupported CPU\n");
    exit(1);
  }*/

  struct load_command* cmds_ptr = reinterpret_cast<struct load_command*>(
                                  bin + (is64_ ? sizeof(mach_header_64)
                                               : sizeof(mach_header)));

  uint32_t* symtab = NULL;
  uint32_t* dysyms = NULL;
  const char* symstrtab = NULL;
  dyld_info_command* dyinfo = NULL;
  vector<section_64*> bind_sections_64;
  vector<section*> bind_sections_32;

  for (uint32_t ii = 0; ii < header->ncmds; ii++) {
    LOGF("cmd type:%x\n", cmds_ptr->cmd);

    switch (cmds_ptr->cmd) {
    case LC_SEGMENT_64: {
      readSegment<segment_command_64, section_64>(
          (char*)cmds_ptr, &segments64_, &bind_sections_64);
      break;
    }

    case LC_SEGMENT: {
      readSegment<segment_command, section>(
          (char*)cmds_ptr, &segments_, &bind_sections_32);
      break;
    }

    case LC_DYLD_INFO_ONLY: {
      dyinfo = reinterpret_cast<dyld_info_command*>(cmds_ptr);
      LOGF("dyld info: rebase_off=%u rebase_size=%u "
           "bind_off=%u bind_size=%u "
           "weak_bind_off=%u weak_bind_size=%u "
           "lazy_bind_off=%u lazy_bind_size=%u "
           "export_off=%u export_size=%u\n",
           dyinfo->rebase_off, dyinfo->rebase_size,
           dyinfo->bind_off, dyinfo->bind_size,
           dyinfo->weak_bind_off, dyinfo->weak_bind_size,
           dyinfo->lazy_bind_off, dyinfo->lazy_bind_size,
           dyinfo->export_off, dyinfo->export_size);

      {
        const uint8_t* p = reinterpret_cast<uint8_t*>(
          bin + dyinfo->rebase_off);
        const uint8_t* end = p + dyinfo->rebase_size;
        if (dyinfo->rebase_off && dyinfo->rebase_size) {
          readRebase(p, end);
        }
      }

      {
        const uint8_t* p = reinterpret_cast<uint8_t*>(
          bin + dyinfo->bind_off);
        const uint8_t* end = p + dyinfo->bind_size;
        readBind(p, end, false);
      }

      {
        const uint8_t* p = reinterpret_cast<uint8_t*>(
          bin + dyinfo->lazy_bind_off);
        const uint8_t* end = p + dyinfo->lazy_bind_size;
        readBind(p, end, false);
      }

      {
        const uint8_t* p = reinterpret_cast<uint8_t*>(
          bin + dyinfo->weak_bind_off);
        const uint8_t* end = p + dyinfo->weak_bind_size;
        readBind(p, end, true);
      }

      if (need_exports_) {
        const uint8_t* p = reinterpret_cast<uint8_t*>(
          bin + dyinfo->export_off);
        const uint8_t* end = p + dyinfo->export_size;
        if (dyinfo->export_off && dyinfo->export_size) {
          string buf;
          readExport(p, p, end, &buf);
        }
      }

      break;
    }

    case LC_SYMTAB: {
      symtab_command* symtab_cmd =
        reinterpret_cast<symtab_command*>(cmds_ptr);

      LOGF("symoff=%u nsysm=%u stroff=%u strsize=%u\n",
           symtab_cmd->symoff, symtab_cmd->nsyms,
           symtab_cmd->stroff, symtab_cmd->strsize);

      uint32_t* symtab_top = symtab =
          reinterpret_cast<uint32_t*>(bin + symtab_cmd->symoff);
      symstrtab = bin + symtab_cmd->stroff;

      if (FLAGS_READ_SYMTAB) {
        for (uint32_t i = 0; i < symtab_cmd->nsyms; i++) {
          Symbol sym;
          nlist* nl = (nlist*)symtab;
          sym.name = symstrtab + nl->n_strx;
          if (is64_) {
            sym.addr = nl->n_value;
            symtab += 4;
          } else {
            sym.addr = (uint32_t)nl->n_value;
            symtab += 3;
          }

          LOGF("%d %s(%d) %p\n",
               i, sym.name.c_str(), nl->n_strx, (void*)sym.addr);
          symbols_.push_back(sym);
        }
      }

      // Will be used by other load commands.
      symtab = symtab_top;

      break;
    }

    case LC_DYSYMTAB: {
      dysymtab_command* dysymtab_cmd =
          reinterpret_cast<dysymtab_command*>(cmds_ptr);

      LOGF("dysym:\n"
           " ilocalsym=%u nlocalsym=%u\n"
           " iextdefsym=%u nextdefsym=%u\n"
           " iundefsym=%u nundefsym=%u\n"
           " tocoff=%u ntoc=%u\n"
           " modtaboff=%u nmodtab=%u\n"
           " extrefsymoff=%u nextrefsyms=%u\n"
           " indirectsymoff=%u nindirectsyms=%u\n"
           " extreloff=%u nextrel=%u\n"
           " locreloff=%u nlocrel=%u\n"
           ,
           dysymtab_cmd->ilocalsym, dysymtab_cmd->nlocalsym,
           dysymtab_cmd->iextdefsym, dysymtab_cmd->nextdefsym,
           dysymtab_cmd->iundefsym, dysymtab_cmd->nundefsym,
           dysymtab_cmd->tocoff, dysymtab_cmd->ntoc,
           dysymtab_cmd->modtaboff, dysymtab_cmd->nmodtab,
           dysymtab_cmd->extrefsymoff, dysymtab_cmd->nextrefsyms,
           dysymtab_cmd->indirectsymoff, dysymtab_cmd->nindirectsyms,
           dysymtab_cmd->extreloff, dysymtab_cmd->nextrel,
           dysymtab_cmd->locreloff, dysymtab_cmd->nlocrel);

      if (dysymtab_cmd->nindirectsyms) {
          dysyms = reinterpret_cast<uint32_t*>(
              bin + dysymtab_cmd->indirectsymoff);
      }
      if (FLAGS_READ_DYSYMTAB) {
        for (uint32_t j = 0; j < dysymtab_cmd->nindirectsyms; j++) {
          uint32_t dysym = dysyms[j];
          uint32_t index = dysym & 0x3fffffff;
          const char* local =
            (dysym & INDIRECT_SYMBOL_LOCAL) ? " local" : "";
          const char* abs =
            (dysym & INDIRECT_SYMBOL_ABS) ? " abs" : "";

          uint32_t* sym = symtab;
          sym += index * (is64_ ? 4 : 3);

          LOGF("dysym %d %s(%u)%s%s\n",
               j, symstrtab + sym[0], index, local, abs);
        }

        uint32_t* dymods = reinterpret_cast<uint32_t*>(
            bin + dysymtab_cmd->modtaboff);
        for (uint32_t j = 0; j < dysymtab_cmd->nmodtab; j++) {
          LOGF("dymods: %u\n", dymods[j]);
        }
      }

      break;
    }

    case LC_LOAD_DYLINKER: {
      lc_str name = reinterpret_cast<struct dylinker_command*>(cmds_ptr)->name;
      LOGF("dylinker: %s\n", (char*)cmds_ptr + name.offset);
      break;
    }

    case LC_UUID:
      break;

    case LC_UNIXTHREAD: {
      uint32_t* p = reinterpret_cast<uint32_t*>(cmds_ptr);
      LOGF("UNIXTHREAD");
      for (uint32_t i = 2; i < p[1]; i++) {
        LOGF(" %d:%x", i, p[i]);
      }
      LOGF("\n");
      if (is64_) {
        entry_ = reinterpret_cast<uint64_t*>(cmds_ptr)[18];
      } else {
        entry_ = reinterpret_cast<uint32_t*>(cmds_ptr)[14];
      }
      LOGF("entry=%llx\n", (ull)entry_);
      break;
    }

    case LC_LOAD_DYLIB: {
      dylib* lib = &reinterpret_cast<dylib_command*>(cmds_ptr)->dylib;
      LOGF("dylib: '%s'\n", (char*)cmds_ptr + lib->name.offset);
      dylibs_.push_back((char*)cmds_ptr + lib->name.offset);
      break;
    }

    }

    cmds_ptr = reinterpret_cast<load_command*>(
        reinterpret_cast<char*>(cmds_ptr) + cmds_ptr->cmdsize);
  }

  LOGF("%p vs %p\n", cmds_ptr, bin + mapped_size_);

  // No LC_DYLD_INFO_ONLY, we will read classic binding info.
  if (!dyinfo && dysyms && symtab && symstrtab) {
    for (size_t i = 0; i < bind_sections_64.size(); i++) {
      readClassicBind<section_64>(
          *bind_sections_64[i], dysyms, symtab, symstrtab);
    }
    for (size_t i = 0; i < bind_sections_32.size(); i++) {
      readClassicBind<section>(
          *bind_sections_32[i], dysyms, symtab, symstrtab);
    }
  }
}

MachOImpl::~MachOImpl() {
  close();
}

void MachOImpl::close() {
  for (size_t i = 0; i < binds_.size(); i++) {
    delete binds_[i];
  }
  binds_.clear();
  for (size_t i = 0; i < rebases_.size(); i++) {
    delete rebases_[i];
  }
  rebases_.clear();
  for (size_t i = 0; i < exports_.size(); i++) {
    delete exports_[i];
  }
  exports_.clear();

  if (mapped_) {
    //munmap(mapped_, mapped_size_);
	free(mapped_);
    ::fclose(f_);
    mapped_ = NULL;
    f_ = NULL;
  }
}

MachO* MachO::read(const char* path, const char* arch, bool need_exports) {
  //int fd = open(path, O_RDONLY);
  FILE *f = fopen(path, "rb");
  if (f == NULL) {
    fprintf(stderr, "%s: %s\n", path, strerror(errno));
    exit(1);
  }

  size_t offset = 0, len = 0;
  map<string, fat_arch> archs;
  if (readFatInfo(f, &archs)) {
    map<string, fat_arch>::const_iterator found = archs.find(arch);
    if (found == archs.end()) {
      fprintf(stderr,
              "%s is a fat binary, but doesn't contain %s binary\n",
              path, arch);
      exit(1);
    }
    offset = found->second.offset;
    len = found->second.size;
    LOGF("fat offset=%lu, len=%lu\n",
         (unsigned long)offset, (unsigned long)len);
  }

  return new MachOImpl(path, f, offset, len, need_exports);
}

ext: cc

max_stars_repo_name: trailofbits/codereason

max_issues_repo_name: zeezocode/codereason

max_forks_repo_name: zeezocode/codereason


text:
#pragma once

#include <boost/filesystem.hpp>

#include <elle/reactor/network/socket.hh>

namespace elle
{
  namespace reactor
  {
    namespace network
    {
      /// Specialized StreamSocket for inter-process communication.
      class UnixDomainSocket
        : public StreamSocket<boost::asio::local::stream_protocol::socket,
                              boost::asio::local::stream_protocol::endpoint>
      {
      public:
        using Super = StreamSocket<
          boost::asio::local::stream_protocol::socket,
          boost::asio::local::stream_protocol::endpoint>;
        /// Construct a UnixDomainSocket.
        ///
        /// \param ep The EndPoint of the peer.
        /// \param timeout The  maximum duration before the connection attempt
        ///                times out.
        UnixDomainSocket(EndPoint const& ep,
                         DurationOpt timeout = {});
        /// Construct a UnixDomainSocket.
        ///
        /// \param path The path name to construct an endpoint from.
        /// \param timeout The maximum duration before the connection attempt
        ///                times out.
        UnixDomainSocket(boost::filesystem::path const& path,
                         DurationOpt timeout = {});

      private:
        friend class UnixDomainServer;
        UnixDomainSocket(std::unique_ptr<AsioSocket> socket,
                         AsioSocket::endpoint_type const& endpoint);
      };
    }
  }
}

ext: hh

max_stars_repo_name: infinitio/elle

max_issues_repo_name: infinitio/elle

max_forks_repo_name: infinitio/elle


text:
// Copyright 2018-2022 Emil Dotchevski and Reverge Studios, Inc.

// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/leaf/handle_errors.hpp>
#include "lightweight_test.hpp"

namespace leaf = boost::leaf;

#ifdef BOOST_LEAF_NO_EXCEPTIONS

int main()
{
    int r = leaf::try_catch(
        []
        {
            return 42;
        },
        []
        {
            return 1;
        } );
    BOOST_TEST_EQ(r, 42);

    return boost::report_errors();
}

#else

#ifdef BOOST_LEAF_TEST_SINGLE_HEADER
#   include "leaf.hpp"
#else
#   include <boost/leaf/pred.hpp>
#endif

template <int> struct info { int value; };

struct error1: std::exception { };
struct error2: std::exception { };
struct error3: std::exception { };

struct exc_val: std::exception { int value; explicit exc_val(int v): value(v) { } };

template <class R,class Ex>
R failing( Ex && ex )
{
    throw leaf::exception(std::move(ex), info<1>{1}, info<2>{2}, info<3>{3});
}

template <class R>
R succeeding()
{
    return R(42);
}

int main()
{
    // void, try_catch (success)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                c = succeeding<int>();
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 1;
            } );
        BOOST_TEST_EQ(c, 42);
    }

    // void, try_catch (failure), match_enum (single enum value)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                c = failing<int>(error1());
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 1;
            },
            [&c]( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    // void, try_catch (failure), match_enum (multiple enum values)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                c = failing<int>(error1());
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 1;
            },
            [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    // void, try_catch (failure), match_value (single value)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                c = failing<int>(error1());
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 1;
            },
            [&c]( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    // void, try_catch (failure), match_value (multiple values)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                c = failing<int>(error1());
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 1;
            },
            [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    //////////////////////////////////////

    // void, handle_some (failure, initially not matched), match_enum (single enum value)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                leaf::try_catch(
                    [&c]
                    {
                        c = failing<int>(error1());
                    },
                    [&c]( error2 const & )
                    {
                        BOOST_TEST_EQ(c, 0);
                        c = 1;
                    } );
                BOOST_TEST(false);
            },
            [&c]( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    // void, handle_some (failure, initially not matched), match_enum (multiple enum values)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                leaf::try_catch(
                    [&c]
                    {
                        c = failing<int>(error1());
                    },
                    [&c]( error2 const & )
                    {
                        BOOST_TEST_EQ(c, 0);
                        c = 1;
                    } );
                BOOST_TEST(false);
            },
            [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 2);
    }

    // void, handle_some (failure, initially matched), match_enum (single enum value)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                leaf::try_catch(
                    [&c]
                    {
                        c = failing<int>(error1());
                    },
                    [&c]( error1 const &, info<1> const & x, info<2> y )
                    {
                        BOOST_TEST_EQ(x.value, 1);
                        BOOST_TEST_EQ(y.value, 2);
                        BOOST_TEST_EQ(c, 0);
                        c = 1;
                    } );
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 1);
    }

    // void, handle_some (failure, initially matched), match_enum (multiple enum values)
    {
        int c=0;
        leaf::try_catch(
            [&c]
            {
                leaf::try_catch(
                    [&c]
                    {
                        c = failing<int>(error1());
                    },
                    [&c]( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
                    {
                        BOOST_TEST_EQ(x.value, 1);
                        BOOST_TEST_EQ(y.value, 2);
                        BOOST_TEST_EQ(c, 0);
                        c = 1;
                    } );
            },
            [&c]( error2 const & )
            {
                BOOST_TEST_EQ(c, 0);
                c = 2;
            },
            [&c]
            {
                BOOST_TEST_EQ(c, 0);
                c = 3;
            } );
        BOOST_TEST_EQ(c, 1);
    }

    //////////////////////////////////////

    // int, try_catch (success)
    {
        int r = leaf::try_catch(
            []
            {
                return succeeding<int>();
            },
            []
            {
                return 1;
            } );
        BOOST_TEST_EQ(r, 42);
    }

    // int, try_catch (failure), match_enum (single enum value)
    {
        int r = leaf::try_catch(
            []
            {
                return failing<int>(error1());
            },
            []( error2 const & )
            {
                return 1;
            },
            []( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    // int, try_catch (failure), match_enum (multiple enum values)
    {
        int r = leaf::try_catch(
            []
            {
                return failing<int>(error1());
            },
            []( error2 const & )
            {
                return 1;
            },
            []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    //////////////////////////////////////

    // int, handle_some (failure, matched), match_enum (single enum value)
    {
        int r = leaf::try_catch(
            []
            {
                return failing<int>(error1());
            },
            []( error2 const & )
            {
                return 1;
            },
            []( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    // int, handle_some (failure, matched), match_enum (multiple enum values)
    {
        int r = leaf::try_catch(
            []
            {
                return failing<int>(error1());
            },
            []( error2 const & )
            {
                return 1;
            },
            []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    // int, handle_some (failure, initially not matched), match_enum (single enum value)
    {
        int r = leaf::try_catch(
            []
            {
                int r = leaf::try_catch(
                    []
                    {
                        return failing<int>(error1());
                    },
                    []( error2 const & )
                    {
                        return 1;
                    } );
                BOOST_TEST(false);
                return r;
            },
            []( error1 const &, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    // int, handle_some (failure, initially not matched), match_enum (multiple enum values)
    {
        int r = leaf::try_catch(
            []
            {
                int r = leaf::try_catch(
                    []
                    {
                        return failing<int>(error1());
                    },
                    []( error2 const & )
                    {
                        return 1;
                    } );
                BOOST_TEST(false);
                return r;
            },
            []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
            {
                BOOST_TEST_EQ(x.value, 1);
                BOOST_TEST_EQ(y.value, 2);
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    // int, handle_some (failure, initially matched), match_enum (single enum value)
    {
        int r = leaf::try_catch(
            []
            {
                int r = leaf::try_catch(
                    []
                    {
                        return failing<int>(error1());
                    },
                    []( error1 const &, info<1> const & x, info<2> y )
                    {
                        BOOST_TEST_EQ(x.value, 1);
                        BOOST_TEST_EQ(y.value, 2);
                        return 1;
                    } );
                BOOST_TEST_EQ(r, 1);
                return r;
            },
            []( error1 const & )
            {
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 1);
    }

    // int, handle_some (failure, initially matched), match_enum (multiple enum values)
    {
        int r = leaf::try_catch(
            []
            {
                int r = leaf::try_catch(
                    []
                    {
                        return failing<int>(error1());
                    },
                    []( leaf::catch_<error2,error1>, info<1> const & x, info<2> y )
                    {
                        BOOST_TEST_EQ(x.value, 1);
                        BOOST_TEST_EQ(y.value, 2);
                        return 1;
                    } );
                BOOST_TEST_EQ(r, 1);
                return r;
            },
            []( error1 const & )
            {
                return 2;
            },
            []
            {
                return 3;
            } );
        BOOST_TEST_EQ(r, 1);
    }

    //////////////////////////////////////

    // match<> with exceptions
    {
        int r = leaf::try_catch(
            []
            {
                throw leaf::exception(exc_val{42});
                return 0;
            },
            []( leaf::match_value<exc_val, 42> )
            {
                return 1;
            },
            []
            {
                return 2;
            } );
        BOOST_TEST_EQ(r, 1);
    }
    {
        int r = leaf::try_catch(
            []
            {
                throw leaf::exception(exc_val{42});
                return 0;
            },
            []( leaf::match_value<exc_val, 41> )
            {
                return 1;
            },
            []
            {
                return 2;
            } );
        BOOST_TEST_EQ(r, 2);
    }
    {
        int r = leaf::try_catch(
            []
            {
                throw exc_val{42};
                return 0;
            },
            []( leaf::match_value<exc_val, 42> )
            {
                return 1;
            },
            []
            {
                return 2;
            } );
        BOOST_TEST_EQ(r, 1);
    }
    {
        int r = leaf::try_catch(
            []
            {
                throw exc_val{42};
                return 0;
            },
            []( leaf::match_value<exc_val, 41> )
            {
                return 1;
            },
            []
            {
                return 2;
            } );
        BOOST_TEST_EQ(r, 2);
    }

    //////////////////////////////////////

    return boost::report_errors();
}

#endif

ext: cpp

max_stars_repo_name: clayne/leaf

max_issues_repo_name: clayne/leaf

max_forks_repo_name: clayne/leaf


text:
#include <iostream>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <vector>
#include <boost/program_options.hpp>
#include "ZiegVersion.h"

using namespace std;
using namespace boost::interprocess;
namespace po = boost::program_options;

void clientRun()
{
    try{
       //Erase previous message queue
       //message_queue::remove("message_queue");

       //Create a message_queue.
       message_queue mq
          (open_or_create               //only create
          ,"message_queue"           //name
          ,100                       //max message number
          ,sizeof(int)               //max message size
          );

       //Send 100 numbers
       for(int i = 0; i < 100; ++i){
          mq.send(&i, sizeof(i), 0);
       }
    }
    catch(interprocess_exception &ex){
       std::cout << ex.what() << std::endl;
    }
}

void serverRun()
{
    try{
       //Open a message queue.
       message_queue mq
          (open_or_create        //only create
          ,"message_queue"  //name
           ,100                       //max message number
           ,sizeof(int)               //max message size
           );

       unsigned int priority;
       message_queue::size_type recvd_size;

       //Receive 100 numbers
       for(int i = 0; i < 100; ++i){
          int number;
          mq.receive(&number, sizeof(number), recvd_size, priority);
          if(number != i || recvd_size != sizeof(number))
             return;
          cout << "Number received: " << number << endl;
       }
    }
    catch(interprocess_exception &ex){
       message_queue::remove("message_queue");
       std::cout << ex.what() << std::endl;
       return;
    }
    message_queue::remove("message_queue");
}

int main(int argc, char *argv[])
{
    try {
       po::options_description basic_opts("Generic options");
       basic_opts.add_options()
          ("version,v", "print version string")
          ("help,h","produce help message")
          ("client,c","run in client mode")
          ("server,s","run in server mode")
       ;

       po::options_description cmdline_options;
       cmdline_options.add(basic_opts);
       po::variables_map vm;
       po::store(po::parse_command_line(argc, argv, cmdline_options), vm);
       po::notify(vm);

       if (vm.count("help")) {
          cout << cmdline_options << endl;
          return 0;
       }
       if (vm.count("version")) {
          cout << "   Version: ";
          cout << ZiegVersion::GetFullVersionString() << endl;
          cout << "Build Date: ";
          cout << ZiegVersion::BuildDate << endl;
          cout << "Build Time: ";
          cout << ZiegVersion::BuildTime << endl;
          cout << "      UUID: ";
          cout << ZiegVersion::UUID << endl;
       }
       if (vm.count("client")) {
           cout << "Running in client mode ..." << endl;
           clientRun();
       }
       else if (vm.count("server")) {
           cout << "Running in server mode ..." << endl;
           serverRun();
       }

    }
    catch(exception& ex)
    {
       cout << "Exception " << ex.what() << " thrown." << endl;
    }
    catch(...)
    {
       cout << "Unknown exception thrown." << endl;
    }

    cout << "Bye!" << endl;
    return 0;
}


text:
/*
@copyright Louis Dionne 2014
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
 */

#include <boost/hana/detail/assert.hpp>
#include <boost/hana/detail/constexpr.hpp>
#include <boost/hana/integer_list.hpp>
#include <boost/hana/integral.hpp>
#include <boost/hana/maybe.hpp>
#include <boost/hana/pair.hpp>
using namespace boost::hana;

int main() {
    //! [main]
    BOOST_HANA_CONSTEXPR_LAMBDA auto f = [](auto x) {
        return if_(x == int_<0>, nothing, just(pair(x, x - int_<1>)));
    };

    BOOST_HANA_CONSTANT_ASSERT(
        unfoldr<IntegerList>(f, int_<10>) == integer_list<int, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1>
    );
    //! [main]
}

max_stars_repo_name: rbock/hana

max_issues_repo_name: rbock/hana

max_forks_repo_name: rbock/hana