<commit_before>#include <algorithm>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <string>

#include "boost/chrono.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/thread.hpp"

#include "dsb/comm.hpp"
#include "dsb/control.hpp"
#include "dsb/error.hpp"
#include "dsb/protobuf.hpp"
#include "dsb/util.hpp"

#include "control.pb.h"

#include "mock_slaves.hpp"

#if defined(_MSC_VER) && _MSC_VER <= 1800
#   define noexcept
#endif

class Shutdown : std::exception
{
public:
    const char* what() const noexcept override { return "Normal shutdown requested by master"; }
};

uint16_t NormalMessageType(const std::deque<zmq::message_t>& msg)
{
    const auto mt = dsb::control::NonErrorMessageType(msg);
    if (mt == dsbproto::control::MSG_TERMINATE) throw Shutdown();
    return mt;
}

void EnforceMessageType(
    const std::deque<zmq::message_t>& msg,
    dsbproto::control::MessageType expectedType)
{
    if (NormalMessageType(msg) != expectedType) {
        throw dsb::error::ProtocolViolationException(
            "Invalid reply from master");
    }
}

int main(int argc, const char** argv)
{
try {
    if (argc < 7) {
        std::cerr << "Usage: " << argv[0] << " <id> <control> <data pub> <data sub> <slave type> <other slave>\n"
                  << "  id          = a number in the range 0 - 65535\n"
                  << "  control     = Control socket endpoint (e.g. tcp://myhost:5432)\n"
                  << "  data pub    = Publisher socket endpoint\n"
                  << "  data sub    = Subscriber socket endpoint\n"
                  << "  slave type  = mass_1d or spring_1d\n"
                  << "  other slave = ID of other slave"
                  << std::endl;
        return 0;
    }
    const auto id = std::string(argv[1]);
    const auto controlEndpoint = std::string(argv[2]);
    const auto dataPubEndpoint = std::string(argv[3]);
    const auto dataSubEndpoint = std::string(argv[4]);
    const auto slaveType = std::string(argv[5]);
    const auto otherSlaveId = boost::lexical_cast<uint16_t>(argv[6]);

    auto slaveInstance = NewSlave(slaveType);

    auto context = zmq::context_t();
    auto control = zmq::socket_t(context, ZMQ_REQ);
    control.setsockopt(ZMQ_IDENTITY, id.data(), id.size());
    control.connect(controlEndpoint.c_str());
    auto dataPub = zmq::socket_t(context, ZMQ_PUB);
    dataPub.connect(dataPubEndpoint.c_str());
    auto dataSub = zmq::socket_t(context, ZMQ_SUB);
    dataSub.connect(dataSubEndpoint.c_str());

    // Send HELLO
    std::deque<zmq::message_t> msg;
    dsb::control::CreateHelloMessage(msg, 0);
    dsb::comm::Send(control, msg);

    // Receive HELLO
    dsb::comm::Receive(control, msg);
    if (dsb::control::ParseProtocolVersion(msg.front()) != 0) {
        throw std::runtime_error("Master required unsupported protocol");
    }

    // Send MSG_INIT_READY
    dsb::control::CreateMessage(msg, dsbproto::control::MSG_INIT_READY);
    dsb::comm::Send(control, msg);

    // Receive MSG_INIT_DONE
    dsb::comm::Receive(control, msg);
    EnforceMessageType(msg, dsbproto::control::MSG_INIT_DONE);

    // -------------------------------------------------------------------------
    // Temporary faked code
    const uint16_t inVarRef = 0;
    const uint16_t outVarRef = 1;
    const size_t dataHeaderSize = 4;

    // Build a header to use for subscribing to the other slave's output.
    char otherHeader[dataHeaderSize];
    dsb::util::EncodeUint16(otherSlaveId, otherHeader);
    dsb::util::EncodeUint16(outVarRef, otherHeader + 2);
    dataSub.setsockopt(ZMQ_SUBSCRIBE, otherHeader, dataHeaderSize);

    // Build a header to use for our own output.
    char myHeader[dataHeaderSize];
    dsb::util::EncodeUint16(boost::lexical_cast<uint16_t>(id), myHeader);
    dsb::util::EncodeUint16(outVarRef, myHeader + 2);
    // -------------------------------------------------------------------------

    // MSG_READY loop
    for (;;) {
        dsb::control::CreateMessage(msg, dsbproto::control::MSG_READY);
        dsb::comm::Send(control, msg);

        dsb::comm::Receive(control, msg);
        const auto msgType = NormalMessageType(msg);
        switch (msgType) {
            case dsbproto::control::MSG_STEP: {
                // Extract DoStep message from second (body) frame.
                assert (msg.size() == 2);
                dsbproto::control::StepData stepInfo;
                dsb::protobuf::ParseFromFrame(msg[1], stepInfo);

                // Perform time step
                slaveInstance->DoStep(stepInfo.timepoint(), stepInfo.stepsize());
                // Pretend to work really hard
                boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
                const auto newTime = stepInfo.timepoint() + stepInfo.stepsize();
                std::cout << newTime << " " << slaveInstance->GetVariable(outVarRef) << std::endl;

                // Get value of output variable
                dsbproto::variable::TimestampedValue outVar;
                outVar.set_timestamp(newTime);
                outVar.mutable_value()->set_real_value(slaveInstance->GetVariable(outVarRef));

                // Build data message to be published
                std::deque<zmq::message_t> dataMsg;
                // Header
                dataMsg.push_back(zmq::message_t(dataHeaderSize));
                std::copy(myHeader, myHeader+dataHeaderSize,
                          static_cast<char*>(dataMsg.back().data()));
                // Body
                dataMsg.push_back(zmq::message_t());
                dsb::protobuf::SerializeToFrame(outVar, dataMsg.back());
                // Send it
                dsb::comm::Send(dataPub, dataMsg);

                // Send STEP_OK message
                std::deque<zmq::message_t> stepOkMsg;
                dsb::control::CreateMessage(stepOkMsg, dsbproto::control::MSG_STEP_OK);
                dsb::comm::Send(control, stepOkMsg);

                // Wait for RECV_VARS command
                std::deque<zmq::message_t> recvVarsMsg;
                dsb::comm::Receive(control, recvVarsMsg);
                EnforceMessageType(recvVarsMsg, dsbproto::control::MSG_RECV_VARS);

                // Receive message from other and store the body in inVar.
                const auto allowedTimeError = stepInfo.stepsize() * 1e-6;
                dsbproto::variable::TimestampedValue inVar;
                do {
                    dsb::comm::Receive(dataSub, dataMsg);
                    dsb::protobuf::ParseFromFrame(dataMsg.back(), inVar);
                    assert (inVar.timestamp() < newTime + allowedTimeError
                            && "Data received from the future");
                    // If the message has been queued up from a previous time
                    // step, which could happen if we have joined the simulation
                    // while it's in progress, discard it and retry.
                } while (inVar.timestamp() < newTime - allowedTimeError);

                // Set our input variable.
                slaveInstance->SetVariable(inVarRef, inVar.value().real_value());
                break; }
            default:
                throw dsb::error::ProtocolViolationException(
                    "Invalid reply from master");
        }
    }
} catch (const Shutdown& e) {
    std::cerr << "Shutdown: " << e.what() << std::endl;
}
}
<commit_msg>Refactored "slave"<commit_after>#include <algorithm>
#include <cstdint>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string>

#include "boost/chrono.hpp"
#include "boost/lexical_cast.hpp"
#include "boost/thread.hpp"

#include "dsb/comm.hpp"
#include "dsb/control.hpp"
#include "dsb/error.hpp"
#include "dsb/protobuf.hpp"
#include "dsb/util.hpp"

#include "control.pb.h"

#include "mock_slaves.hpp"

#if defined(_MSC_VER) && _MSC_VER <= 1800
#   define noexcept
#endif

class Shutdown : std::exception
{
public:
    const char* what() const noexcept override { return "Normal shutdown requested by master"; }
};

uint16_t NormalMessageType(const std::deque<zmq::message_t>& msg)
{
    const auto mt = dsb::control::NonErrorMessageType(msg);
    if (mt == dsbproto::control::MSG_TERMINATE) throw Shutdown();
    return mt;
}

void EnforceMessageType(
    const std::deque<zmq::message_t>& msg,
    dsbproto::control::MessageType expectedType)
{
    if (NormalMessageType(msg) != expectedType) {
        throw dsb::error::ProtocolViolationException(
            "Invalid reply from master");
    }
}

/**
\brief  A class which contains the state of the slave and takes care of
        responding to requests from the master node in an appropriate manner.
*/
class Slave
{
public:
    /**
    \brief  Constructs a new Slave.

    \param [in] id              The slave ID.
    \param [in] dataSub         A SUB socket to be used for receiving variables.
    \param [in] dataPub         A PUB socket to be used for sending variables.
    \param [in] slaveInstance   (Temporary) A pointer to the object which
                                contains the slave's mathematical model.
    \param [in] otherSlaveId    (Temporary) The ID of the slave which this
                                slave should be connected to.
    */
    Slave(
        uint16_t id,
        zmq::socket_t dataSub,
        zmq::socket_t dataPub,
        std::unique_ptr<SlaveInstance> slaveInstance,
        uint16_t otherSlaveId
        );

    /**
    \brief  Prepares the first message (HELLO) which is to be sent to the master
            and stores it in `msg`.
    */
    void Start(std::deque<zmq::message_t>& msg);

    /**
    \brief  Responds to a message from the master.

    On input, `msg` must be the message received from master, and on output,
    it will contain the slave's reply.  Internally, the function forwards to
    the handler function that corresponds to the slave's current state.
    */
    void RequestReply(std::deque<zmq::message_t>& msg);

private:
    // Each of these functions correspond to one of the slave's possible states.
    // On input, `msg` is a message from the master node, and when the function
    // returns, `msg` must contain the reply.  If the message triggers a state
    // change, the handler function must update m_stateHandler to point to the
    // function for the new state.
    void ConnectingHandler(std::deque<zmq::message_t>& msg);
    void InitHandler(std::deque<zmq::message_t>& msg);
    void ReadyHandler(std::deque<zmq::message_t>& msg);
    void PublishedHandler(std::deque<zmq::message_t>& msg);

    // Performs the time step for ReadyHandler()
    void Step(const dsbproto::control::StepData& stepData);

    // A pointer to the handler function for the current state.
    void (Slave::* m_stateHandler)(std::deque<zmq::message_t>&);

    zmq::socket_t m_dataSub;
    zmq::socket_t m_dataPub;
    std::unique_ptr<SlaveInstance> m_slaveInstance;
    double m_currentTime;
    double m_lastStepSize;

    // -------------------------------------------------------------------------
    // Temporary
    static const uint16_t IN_VAR_REF = 0;
    static const uint16_t OUT_VAR_REF = 1;
    static const size_t DATA_HEADER_SIZE = 4;

    char otherHeader[DATA_HEADER_SIZE];
    char myHeader[DATA_HEADER_SIZE];
};

int main(int argc, const char** argv)
{
try {
    if (argc < 7) {
        std::cerr << "Usage: " << argv[0] << " <id> <control> <data pub> <data sub> <slave type> <other slave>\n"
                  << "  id          = a number in the range 0 - 65535\n"
                  << "  control     = Control socket endpoint (e.g. tcp://myhost:5432)\n"
                  << "  data pub    = Publisher socket endpoint\n"
                  << "  data sub    = Subscriber socket endpoint\n"
                  << "  slave type  = mass_1d or spring_1d\n"
                  << "  other slave = ID of other slave"
                  << std::endl;
        return 0;
    }
    const auto id = std::string(argv[1]);
    const auto controlEndpoint = std::string(argv[2]);
    const auto dataPubEndpoint = std::string(argv[3]);
    const auto dataSubEndpoint = std::string(argv[4]);
    const auto slaveType = std::string(argv[5]);
    const auto otherSlaveId = boost::lexical_cast<uint16_t>(argv[6]);

    auto context = zmq::context_t();
    auto control = zmq::socket_t(context, ZMQ_REQ);
    control.setsockopt(ZMQ_IDENTITY, id.data(), id.size());
    control.connect(controlEndpoint.c_str());
    auto dataPub = zmq::socket_t(context, ZMQ_PUB);
    dataPub.connect(dataPubEndpoint.c_str());
    auto dataSub = zmq::socket_t(context, ZMQ_SUB);
    dataSub.connect(dataSubEndpoint.c_str());

    Slave slave(boost::lexical_cast<uint16_t>(id),
                std::move(dataSub),
                std::move(dataPub),
                NewSlave(slaveType),
                otherSlaveId);
    std::deque<zmq::message_t> msg;
    slave.Start(msg);
    for (;;) {
        dsb::comm::Send(control, msg);
        dsb::comm::Receive(control, msg);
        slave.RequestReply(msg);
    }
} catch (const Shutdown& e) {
    std::cerr << "Shutdown: " << e.what() << std::endl;
}
}

// =============================================================================
// Slave class function implementations
// =============================================================================

Slave::Slave(
        uint16_t id,
        zmq::socket_t dataSub_,
        zmq::socket_t dataPub_,
        std::unique_ptr<SlaveInstance> slaveInstance_,
        uint16_t otherSlaveId)
    : m_dataSub(std::move(dataSub_)),
      m_dataPub(std::move(dataPub_)),
      m_slaveInstance(std::move(slaveInstance_)),
      m_currentTime(std::numeric_limits<double>::signaling_NaN()),
      m_lastStepSize(std::numeric_limits<double>::signaling_NaN())
{

    // -------------------------------------------------------------------------
    // Temporary

    // Build a header to use for subscribing to the other slave's output.
    dsb::util::EncodeUint16(otherSlaveId, otherHeader);
    dsb::util::EncodeUint16(OUT_VAR_REF, otherHeader + 2);
    m_dataSub.setsockopt(ZMQ_SUBSCRIBE, otherHeader, DATA_HEADER_SIZE);

    // Build a header to use for our own output.
    dsb::util::EncodeUint16(id, myHeader);
    dsb::util::EncodeUint16(OUT_VAR_REF, myHeader + 2);
    // -------------------------------------------------------------------------
}

void Slave::Start(std::deque<zmq::message_t>& msg)
{
    dsb::control::CreateHelloMessage(msg, 0);
    m_stateHandler = &Slave::ConnectingHandler;
}

void Slave::RequestReply(std::deque<zmq::message_t>& msg)
{
    (this->*m_stateHandler)(msg);
}

void Slave::ConnectingHandler(std::deque<zmq::message_t>& msg)
{
    if (dsb::control::ParseProtocolVersion(msg.front()) != 0) {
        throw std::runtime_error("Master required unsupported protocol");
    }
    dsb::control::CreateMessage(msg, dsbproto::control::MSG_INIT_READY);
    m_stateHandler = &Slave::InitHandler;
}

void Slave::InitHandler(std::deque<zmq::message_t>& msg)
{
    EnforceMessageType(msg, dsbproto::control::MSG_INIT_DONE);
    dsb::control::CreateMessage(msg, dsbproto::control::MSG_READY);
    m_stateHandler = &Slave::ReadyHandler;
}

void Slave::ReadyHandler(std::deque<zmq::message_t>& msg)
{
    switch (NormalMessageType(msg)) {
        case dsbproto::control::MSG_STEP: {
            if (msg.size() != 2) {
                throw dsb::error::ProtocolViolationException(
                    "Wrong number of frames in STEP message");
            }
            dsbproto::control::StepData stepData;
            dsb::protobuf::ParseFromFrame(msg[1], stepData);
            Step(stepData);
            dsb::control::CreateMessage(msg, dsbproto::control::MSG_STEP_OK);
            m_stateHandler = &Slave::PublishedHandler;
            break; }
        default:
            throw dsb::error::ProtocolViolationException(
                "Invalid reply from master");
    }
}

void Slave::PublishedHandler(std::deque<zmq::message_t>& msg)
{
    EnforceMessageType(msg, dsbproto::control::MSG_RECV_VARS);

    // Receive message from other and store the body in inVar.
    const auto allowedTimeError = m_lastStepSize * 1e-6;
    std::deque<zmq::message_t> dataMsg;
    dsbproto::variable::TimestampedValue inVar;
    do {
        dsb::comm::Receive(m_dataSub, dataMsg);
        dsb::protobuf::ParseFromFrame(dataMsg.back(), inVar);
        assert (inVar.timestamp() < m_currentTime + allowedTimeError
                && "Data received from the future");
        // If the message has been queued up from a previous time
        // step, which could happen if we have joined the simulation
        // while it's in progress, discard it and retry.
    } while (inVar.timestamp() < m_currentTime - allowedTimeError);

    // Set our input variable.
    m_slaveInstance->SetVariable(IN_VAR_REF, inVar.value().real_value());

    // Send READY message and change state again.
    dsb::control::CreateMessage(msg, dsbproto::control::MSG_READY);
    m_stateHandler = &Slave::ReadyHandler;
}

void Slave::Step(const dsbproto::control::StepData& stepInfo)
{
    // Perform time step
    m_slaveInstance->DoStep(stepInfo.timepoint(), stepInfo.stepsize());
    // Pretend to work really hard
    boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
    m_currentTime = stepInfo.timepoint() + stepInfo.stepsize();
    m_lastStepSize = stepInfo.stepsize();
    std::cout << m_currentTime << " " << m_slaveInstance->GetVariable(OUT_VAR_REF) << std::endl;

    // Get value of output variable
    dsbproto::variable::TimestampedValue outVar;
    outVar.set_timestamp(m_currentTime);
    outVar.mutable_value()->set_real_value(m_slaveInstance->GetVariable(OUT_VAR_REF));

    // Build data message to be published
    std::deque<zmq::message_t> dataMsg;
    // Header
    dataMsg.push_back(zmq::message_t(DATA_HEADER_SIZE));
    std::copy(myHeader, myHeader+DATA_HEADER_SIZE,
                static_cast<char*>(dataMsg.back().data()));
    // Body
    dataMsg.push_back(zmq::message_t());
    dsb::protobuf::SerializeToFrame(outVar, dataMsg.back());
    // Send it
    dsb::comm::Send(m_dataPub, dataMsg);
}
<|endoftext|>


<commit_before>/*
 *  soundpatty.cpp
 *
 *  Copyright (c) 2010 Motiejus Jakštys
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3.

 *  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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "soundpatty.h"
#include "input.h"

void *SoundPatty::go_thread(void *args) {
    // This is the new function that is created in new thread
    SoundPatty *inst = (SoundPatty*) args;

    LOG_DEBUG("Launching SoundPatty::go");
    if (inst->go() == 2) {
        LOG_WARN("Timed out");
    }
    LOG_DEBUG("Terminating SoundPatty instance");
    // Process is over - either timeout or catch. Callbacks launched already.
    // Must terminate SoundPatty and Input instances
    delete inst;
    LOG_DEBUG("SoundPatty and Input instances deleted. Exiting thread");
    return NULL;
}

void SoundPatty::dump_out(const treshold_t args) { // STATIC
    printf ("%d;%.6f;%.6f\n", args.r, args.place, args.sec);
    fflush(stdout);
}

SoundPatty::SoundPatty(action_t action, Input *input, all_cfg_t *all_cfg,
        void *params_uni) {
    _action = action;
    _input = input;
    if (action == ACTION_DUMP) {
        //sp_params_dump_t *params = (sp_params_dump_t*)params_uni;
    } else if (action == ACTION_CAPTURE) {
        sp_params_capture_t *params = (sp_params_capture_t*)params_uni;
        exit_after_capture = params->exit_after_capture;
        vals = params->vals;
        _callback = params->fn;
    }

    findings = deque<treshold_t>();
	cfg = all_cfg->first;
	volume = all_cfg->second;
    gSCounter = gMCounter = 0;

    _input->WAVE = _input->SAMPLE_RATE * cfg["minwavelen"];
    _input->CHUNKSIZE = cfg["chunklen"] * _input->SAMPLE_RATE;
}

SoundPatty::~SoundPatty() {
    delete _input;
}

all_cfg_t SoundPatty::read_cfg (const char * filename) {
	map<string,double> cfg;
	vector<sVolumes> volume;
    ifstream file;
    file.exceptions(ifstream::failbit | ifstream::badbit);
    try {
        file.open(filename);
    } catch (ifstream::failure e) {
        LOG_FATAL("Could not read config file %s", filename);
        exit(1);
    }

    string line;
    int x;
    while (! file.eof() ) {
        getline(file, line);
        x = line.find(":");
        if (x == -1) break; // Last line, exit
        istringstream i(line.substr(x+2));
        double tmp; i >> tmp;
        cfg[line.substr(0,x)] = tmp;
    }
    LOG_DEBUG("Read %d config values from %s", cfg.size(), filename);

    /* Licensed code. */
    cfg["sampletimeout"] = 120.1;

    sVolumes tmp;
    tmp.head = tmp.tail = tmp.max = tmp.min = tmp.proc = 0;
    volume.assign(cfg.size(), tmp); // Assign a bit more then nescesarry
    int max_index = -1; // Number of different tresholds
    for(map<string, double>::iterator C = cfg.begin(); C != cfg.end(); C++) {
        // Failed to use boost::regex :(
        if (C->first.find("treshold") == 0) {
            istringstream tmp(C->first.substr(8));
            int i; tmp >> i;
            max_index = max(max_index, i);
            if (C->first.find("_min") != string::npos) {
                volume[i].min = C->second;
            } else {
                volume[i].max = C->second;
            }
        }
    }
    if (max_index == -1) {
        LOG_FATAL("ERROR. Length of vol array: 0\n");
    }
    volume.assign(volume.begin(), volume.begin()+max_index+1);
	return all_cfg_t(cfg, volume);
}

int SoundPatty::setInput(Input * input) {
    _input = input;
    return 0;
}

int SoundPatty::go() {

    string which_timeout (_action == ACTION_CAPTURE ?
            "catchtimeout" : "sampletimeout");
    buffer_t buf;

    while (_input->giveInput(&buf) != 0) { // Have pointer to data
        LOG_TRACE("Got buffer, length: %d, seconds processed: %f",
                buf.nframes, sec_processed());
        treshold_t ret;

        for (unsigned int i = 0; i < buf.nframes; gSCounter++, i++) {
            sample_t cur = buf.buf[i]<0?-buf.buf[i]:buf.buf[i];
            //printf("Cur: %f, second: %f, frame: %d\n",
            //        cur, sec_processed(), i);
            // Special handling of last bit, because we might still be in a
            // wave which we do not want to miss
            bool last_sample = _input->reading_over && i == buf.nframes - 1;
            if (search_patterns(cur, &ret, last_sample))
            {
                LOG_TRACE("Found pattern (%-.3f) %-.3f; %.6f; %.6f",
                        ret.b, ret.r, ret.place, ret.sec);

                if (_action == ACTION_DUMP)
                    SoundPatty::dump_out(ret);
                else if (_action == ACTION_AGGREGATE)
                    this->findings.push_back(ret);
                else if (_action == ACTION_CAPTURE)
                    if (SoundPatty::do_checking(ret))
                        // Caught pattern
                        if (exit_after_capture)
                            return 1;
            }
        }
        if (buf.delete_me) {
            delete(buf.buf);
        }

        if (sec_processed() > cfg[which_timeout]) {
            // Timeout. Return 2
            return 2;
        }
    }
    return 0;
}

double SoundPatty::sec_processed() {
    return (double)gSCounter/_input->SAMPLE_RATE;
}

vals_t SoundPatty::read_captured_values(const char * filename) {
    vals_t vals;

    ifstream file;
    file.open(filename);
    string line;
    for (int i = 0; !file.eof(); i++) {
        getline(file, line);
        if (line.size() == 0) break;
        vector<string> numbers = explode(";", line);
        istringstream num(numbers[0]);
        istringstream place(numbers[1]);
        istringstream range(numbers[2]);

        double tmp2;
        pair<pair<int, Range>,valsitm_t > tmp;
        num >> tmp2; tmp.first.first = tmp2; // Index in volume
        range >> tmp2; tmp.first.second = Range(tmp2);
        place >> tmp.second.place; // Place in the stream
        tmp.second.c = i; // Counter in the stream
        vals.insert(tmp);
    }
    return vals;
}

/*
 * last means that we are processing the last bit of the file
 */
int SoundPatty::search_patterns (sample_t cur, treshold_t * ret, bool last) {
    int v = 0; // Counter for volume
    for (vector<sVolumes>::iterator V = volume.begin(); V != volume.end(); V++, v++) {
        if (!last && V->min <= cur && cur <= V->max) {
            // ------------------------------------------------------------
            // If it's first item in this wave (proc = processing started)
            //
            if (!V->proc) {
                V->tail = gSCounter;
                V->proc = true;
            }
            // ------------------------------------------------------------
            // Here we are just normally in the wave.
            //
            V->head = gSCounter;
        } else { // We are not in the wave
            if (last || (V->proc &&
                        (V->min < 0.001 || gSCounter - V->head > _input->WAVE)))
                    {

                //------------------------------------------------------------
                // This wave is over
                //
                // Stop processing for both cases: found and not
                V->proc = false;

                if (gSCounter - V->tail >= _input->CHUNKSIZE) {
                    // ---------------------------------------------------------
                    // The previous chunk is big enough to be noticed
                    //
                    ret->r = v;
                    ret->place = (double)V->tail/_input->SAMPLE_RATE;
                    ret->sec = (double)(V->head - V->tail)/_input->SAMPLE_RATE;
                    ret->b = gMCounter++;
                    return 1;
                }
                // ------------------------------------------------------------
                // Else it is too small, but we don't want to do anything
                // So therefore we just say that wave processing is over
                //
            }
        }
    }
    return 0;
}

// --------------------------------------------------------------------------------
// This gets called every time there is a treshold found.
// Work array is global
//
// int r - id of treshold found (in config.cfg: treshold_(<?r>\d)_(min|max))
// double place - place of sample from the stream start (sec)
// double sec - length of a found sample (sec)
// int b - index (overall) of sample found
//
int SoundPatty::do_checking(const treshold_t tr) {
    //pair<vals_t::iterator, vals_t::iterator> pa = vals.equal_range(pair<int,double>(r,sec));
    // Manually searching for matching values because with that pairs equal_range doesnt work
    // Iterate through pa

    unsigned long b = tr.b; vals_t fina; // FoundInA
    Range demorange(tr.sec);
    pair<int,Range> sample(tr.r, demorange);
    for (vals_t::iterator it1 = vals.begin(); it1 != vals.end(); it1++) {
        if (it1->first == sample) {
            fina.insert(*it1);
        }
    }
    //------------------------------------------------------------
    // We put a indexes here that we use for continued threads
    // (we don't want to create a new "thread" with already
    // used length of a sample)
    //
    set<int> used_a;

    //------------------------------------------------------------
    // Iterating through samples that match the found sample
    //
    for (vals_t::iterator in_a = fina.begin(); in_a != fina.end(); in_a++)
    {
        LOG_TRACE("%.6f (%d) matches %.6f (%d)",tr.sec,in_a->first.first,in_a->first.second.tm,in_a->second.c);
        int a = in_a->second.c;
        //------------------------------------------------------------
        // Check if it exists in our work array
        //
        int i = 0; // Work list counter
        for (list<workitm>::iterator w = work.begin(); w != work.end(); i++) {
            if (b - w->b > round(cfg["maxsteps"])) {
                LOG_TRACE("Work item %d removed, no match for a long time", i);
                work.erase(w); w = work.begin(); continue;
            }
            if (b == w->b || a - w->a > round(cfg["maxsteps"]) || w->a >= a) { w++; continue; }
            // ------------------------------------------------------------
            // We fit the "region" here. We either finished,
            // or just increasing len
            //
            w->a = a; w->b = b;
            w->trace.push_back(pair<int,unsigned long>(a,b));
            LOG_TRACE("Work item %d expanded to %d items", i, w->len+1);
            if (++(w->len) < round(cfg["matchme"])) { // Proceeding with the "thread"
                used_a.insert(a);
            } else { // This means the treshold is reached
                // This kind of function is called when the pattern is recognized
                _callback (_input->name, tr.place + tr.sec);
                return 1;
            }
            w++;
            // End of work iteration array
        }
        if (used_a.find(a) == used_a.end()) {
            work.push_back(workitm(a,b));
        }
    }
    return 0;
}

vector<string> explode(const string &delimiter, const string &str) { // Found somewhere on NET
    vector<string> arr;
    int strleng = str.length();
    int delleng = delimiter.length();
    if (delleng == 0)
        return arr; //no change
    int i = 0, k = 0;
    while (i < strleng) {
        int j = 0;
        while (i+j < strleng && j < delleng && str[i+j] == delimiter[j])
            j++;
        if (j == delleng) {
            arr.push_back(str.substr(k, i-k));
            i += delleng;
            k = i;
        } else {
            i++;
        }
    }
    arr.push_back(str.substr(k, i-k));
    return arr;
}

workitm::workitm(const int a, const unsigned long b) {
    this->a = a; this->b = b;
    len = 0;
    trace.push_back(pair<int,unsigned long>(a,b));
}
<commit_msg>Remove corporate code<commit_after>/*
 *  soundpatty.cpp
 *
 *  Copyright (c) 2010 Motiejus Jakštys
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3.

 *  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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "soundpatty.h"
#include "input.h"

void *SoundPatty::go_thread(void *args) {
    // This is the new function that is created in new thread
    SoundPatty *inst = (SoundPatty*) args;

    LOG_DEBUG("Launching SoundPatty::go");
    if (inst->go() == 2) {
        LOG_WARN("Timed out");
    }
    LOG_DEBUG("Terminating SoundPatty instance");
    // Process is over - either timeout or catch. Callbacks launched already.
    // Must terminate SoundPatty and Input instances
    delete inst;
    LOG_DEBUG("SoundPatty and Input instances deleted. Exiting thread");
    return NULL;
}

void SoundPatty::dump_out(const treshold_t args) { // STATIC
    printf ("%d;%.6f;%.6f\n", args.r, args.place, args.sec);
    fflush(stdout);
}

SoundPatty::SoundPatty(action_t action, Input *input, all_cfg_t *all_cfg,
        void *params_uni) {
    _action = action;
    _input = input;
    if (action == ACTION_DUMP) {
        //sp_params_dump_t *params = (sp_params_dump_t*)params_uni;
    } else if (action == ACTION_CAPTURE) {
        sp_params_capture_t *params = (sp_params_capture_t*)params_uni;
        exit_after_capture = params->exit_after_capture;
        vals = params->vals;
        _callback = params->fn;
    }

    findings = deque<treshold_t>();
	cfg = all_cfg->first;
	volume = all_cfg->second;
    gSCounter = gMCounter = 0;

    _input->WAVE = _input->SAMPLE_RATE * cfg["minwavelen"];
    _input->CHUNKSIZE = cfg["chunklen"] * _input->SAMPLE_RATE;
}

SoundPatty::~SoundPatty() {
    delete _input;
}

all_cfg_t SoundPatty::read_cfg (const char * filename) {
	map<string,double> cfg;
	vector<sVolumes> volume;
    ifstream file;
    file.exceptions(ifstream::failbit | ifstream::badbit);
    try {
        file.open(filename);
    } catch (ifstream::failure e) {
        LOG_FATAL("Could not read config file %s", filename);
        exit(1);
    }

    string line;
    int x;
    while (! file.eof() ) {
        getline(file, line);
        x = line.find(":");
        if (x == -1) break; // Last line, exit
        istringstream i(line.substr(x+2));
        double tmp; i >> tmp;
        cfg[line.substr(0,x)] = tmp;
    }
    LOG_DEBUG("Read %d config values from %s", cfg.size(), filename);

    sVolumes tmp;
    tmp.head = tmp.tail = tmp.max = tmp.min = tmp.proc = 0;
    volume.assign(cfg.size(), tmp); // Assign a bit more then nescesarry
    int max_index = -1; // Number of different tresholds
    for(map<string, double>::iterator C = cfg.begin(); C != cfg.end(); C++) {
        // Failed to use boost::regex :(
        if (C->first.find("treshold") == 0) {
            istringstream tmp(C->first.substr(8));
            int i; tmp >> i;
            max_index = max(max_index, i);
            if (C->first.find("_min") != string::npos) {
                volume[i].min = C->second;
            } else {
                volume[i].max = C->second;
            }
        }
    }
    if (max_index == -1) {
        LOG_FATAL("ERROR. Length of vol array: 0\n");
    }
    volume.assign(volume.begin(), volume.begin()+max_index+1);
	return all_cfg_t(cfg, volume);
}

int SoundPatty::setInput(Input * input) {
    _input = input;
    return 0;
}

int SoundPatty::go() {

    string which_timeout (_action == ACTION_CAPTURE ?
            "catchtimeout" : "sampletimeout");
    buffer_t buf;

    while (_input->giveInput(&buf) != 0) { // Have pointer to data
        LOG_TRACE("Got buffer, length: %d, seconds processed: %f",
                buf.nframes, sec_processed());
        treshold_t ret;

        for (unsigned int i = 0; i < buf.nframes; gSCounter++, i++) {
            sample_t cur = buf.buf[i]<0?-buf.buf[i]:buf.buf[i];
            //printf("Cur: %f, second: %f, frame: %d\n",
            //        cur, sec_processed(), i);
            // Special handling of last bit, because we might still be in a
            // wave which we do not want to miss
            bool last_sample = _input->reading_over && i == buf.nframes - 1;
            if (search_patterns(cur, &ret, last_sample))
            {
                LOG_TRACE("Found pattern (%-.3f) %-.3f; %.6f; %.6f",
                        ret.b, ret.r, ret.place, ret.sec);

                if (_action == ACTION_DUMP)
                    SoundPatty::dump_out(ret);
                else if (_action == ACTION_AGGREGATE)
                    this->findings.push_back(ret);
                else if (_action == ACTION_CAPTURE)
                    if (SoundPatty::do_checking(ret))
                        // Caught pattern
                        if (exit_after_capture)
                            return 1;
            }
        }
        if (buf.delete_me) {
            delete(buf.buf);
        }

        if (sec_processed() > cfg[which_timeout]) {
            // Timeout. Return 2
            return 2;
        }
    }
    return 0;
}

double SoundPatty::sec_processed() {
    return (double)gSCounter/_input->SAMPLE_RATE;
}

vals_t SoundPatty::read_captured_values(const char * filename) {
    vals_t vals;

    ifstream file;
    file.open(filename);
    string line;
    for (int i = 0; !file.eof(); i++) {
        getline(file, line);
        if (line.size() == 0) break;
        vector<string> numbers = explode(";", line);
        istringstream num(numbers[0]);
        istringstream place(numbers[1]);
        istringstream range(numbers[2]);

        double tmp2;
        pair<pair<int, Range>,valsitm_t > tmp;
        num >> tmp2; tmp.first.first = tmp2; // Index in volume
        range >> tmp2; tmp.first.second = Range(tmp2);
        place >> tmp.second.place; // Place in the stream
        tmp.second.c = i; // Counter in the stream
        vals.insert(tmp);
    }
    return vals;
}

/*
 * last means that we are processing the last bit of the file
 */
int SoundPatty::search_patterns (sample_t cur, treshold_t * ret, bool last) {
    int v = 0; // Counter for volume
    for (vector<sVolumes>::iterator V = volume.begin(); V != volume.end(); V++, v++) {
        if (!last && V->min <= cur && cur <= V->max) {
            // ------------------------------------------------------------
            // If it's first item in this wave (proc = processing started)
            //
            if (!V->proc) {
                V->tail = gSCounter;
                V->proc = true;
            }
            // ------------------------------------------------------------
            // Here we are just normally in the wave.
            //
            V->head = gSCounter;
        } else { // We are not in the wave
            if (last || (V->proc &&
                        (V->min < 0.001 || gSCounter - V->head > _input->WAVE)))
                    {

                //------------------------------------------------------------
                // This wave is over
                //
                // Stop processing for both cases: found and not
                V->proc = false;

                if (gSCounter - V->tail >= _input->CHUNKSIZE) {
                    // ---------------------------------------------------------
                    // The previous chunk is big enough to be noticed
                    //
                    ret->r = v;
                    ret->place = (double)V->tail/_input->SAMPLE_RATE;
                    ret->sec = (double)(V->head - V->tail)/_input->SAMPLE_RATE;
                    ret->b = gMCounter++;
                    return 1;
                }
                // ------------------------------------------------------------
                // Else it is too small, but we don't want to do anything
                // So therefore we just say that wave processing is over
                //
            }
        }
    }
    return 0;
}

// --------------------------------------------------------------------------------
// This gets called every time there is a treshold found.
// Work array is global
//
// int r - id of treshold found (in config.cfg: treshold_(<?r>\d)_(min|max))
// double place - place of sample from the stream start (sec)
// double sec - length of a found sample (sec)
// int b - index (overall) of sample found
//
int SoundPatty::do_checking(const treshold_t tr) {
    //pair<vals_t::iterator, vals_t::iterator> pa = vals.equal_range(pair<int,double>(r,sec));
    // Manually searching for matching values because with that pairs equal_range doesnt work
    // Iterate through pa

    unsigned long b = tr.b; vals_t fina; // FoundInA
    Range demorange(tr.sec);
    pair<int,Range> sample(tr.r, demorange);
    for (vals_t::iterator it1 = vals.begin(); it1 != vals.end(); it1++) {
        if (it1->first == sample) {
            fina.insert(*it1);
        }
    }
    //------------------------------------------------------------
    // We put a indexes here that we use for continued threads
    // (we don't want to create a new "thread" with already
    // used length of a sample)
    //
    set<int> used_a;

    //------------------------------------------------------------
    // Iterating through samples that match the found sample
    //
    for (vals_t::iterator in_a = fina.begin(); in_a != fina.end(); in_a++)
    {
        LOG_TRACE("%.6f (%d) matches %.6f (%d)",tr.sec,in_a->first.first,in_a->first.second.tm,in_a->second.c);
        int a = in_a->second.c;
        //------------------------------------------------------------
        // Check if it exists in our work array
        //
        int i = 0; // Work list counter
        for (list<workitm>::iterator w = work.begin(); w != work.end(); i++) {
            if (b - w->b > round(cfg["maxsteps"])) {
                LOG_TRACE("Work item %d removed, no match for a long time", i);
                work.erase(w); w = work.begin(); continue;
            }
            if (b == w->b || a - w->a > round(cfg["maxsteps"]) || w->a >= a) { w++; continue; }
            // ------------------------------------------------------------
            // We fit the "region" here. We either finished,
            // or just increasing len
            //
            w->a = a; w->b = b;
            w->trace.push_back(pair<int,unsigned long>(a,b));
            LOG_TRACE("Work item %d expanded to %d items", i, w->len+1);
            if (++(w->len) < round(cfg["matchme"])) { // Proceeding with the "thread"
                used_a.insert(a);
            } else { // This means the treshold is reached
                // This kind of function is called when the pattern is recognized
                _callback (_input->name, tr.place + tr.sec);
                return 1;
            }
            w++;
            // End of work iteration array
        }
        if (used_a.find(a) == used_a.end()) {
            work.push_back(workitm(a,b));
        }
    }
    return 0;
}

vector<string> explode(const string &delimiter, const string &str) { // Found somewhere on NET
    vector<string> arr;
    int strleng = str.length();
    int delleng = delimiter.length();
    if (delleng == 0)
        return arr; //no change
    int i = 0, k = 0;
    while (i < strleng) {
        int j = 0;
        while (i+j < strleng && j < delleng && str[i+j] == delimiter[j])
            j++;
        if (j == delleng) {
            arr.push_back(str.substr(k, i-k));
            i += delleng;
            k = i;
        } else {
            i++;
        }
    }
    arr.push_back(str.substr(k, i-k));
    return arr;
}

workitm::workitm(const int a, const unsigned long b) {
    this->a = a; this->b = b;
    len = 0;
    trace.push_back(pair<int,unsigned long>(a,b));
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Fixstars Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http ://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <iostream>

#include <libsgm.h>

#include "internal.h"
#include "sgm.hpp"

namespace sgm {
	static bool is_cuda_input(EXECUTE_INOUT type) { return (int)type & 0x1; }
	static bool is_cuda_output(EXECUTE_INOUT type) { return (int)type & 0x2; }

	class SemiGlobalMatchingBase {
	public:
		using output_type = uint8_t;
		virtual void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) = 0;
	};

	class SemiGlobalMatching_8_64 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint8_t*)src_L, (const uint8_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint8_t, 64> sgm_engine_;
	};

	class SemiGlobalMatching_8_128 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint8_t*)src_L, (const uint8_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint8_t, 128> sgm_engine_;
	};

	class SemiGlobalMatching_16_64 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint16_t*)src_L, (const uint16_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint16_t, 64> sgm_engine_;
	};

	class SemiGlobalMatching_16_128 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint16_t*)src_L, (const uint16_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint16_t, 128> sgm_engine_;
	};

	struct CudaStereoSGMResources {
		void* d_src_left;
		void* d_src_right;
		void* d_left;
		void* d_right;
		void* d_scost;
		void* d_matching_cost;
		void* d_left_disp;
		void* d_right_disp;

		void* d_tmp_left_disp;
		void* d_tmp_right_disp;

		cudaStream_t cuda_streams[8];

		uint16_t* h_output_16bit_buffer;

		SemiGlobalMatchingBase* sgm_engine;

		CudaStereoSGMResources(int width_, int height_, int disparity_size_, int input_depth_bits_, int output_depth_bits_, EXECUTE_INOUT inout_type_) {

			if (input_depth_bits_ == 8 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatching_8_64();
			else if (input_depth_bits_ == 8 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatching_8_128();
			else if (input_depth_bits_ == 16 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatching_16_64();
			else if (input_depth_bits_ == 16 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatching_16_128();
			else
				abort();

			if (is_cuda_input(inout_type_)) {
				this->d_src_left = NULL;
				this->d_src_right = NULL;
			}
			else {
				CudaSafeCall(cudaMalloc(&this->d_src_left, input_depth_bits_ / 8 * width_ * height_));
				CudaSafeCall(cudaMalloc(&this->d_src_right, input_depth_bits_ / 8 * width_ * height_));
			}

			CudaSafeCall(cudaMalloc(&this->d_left, sizeof(uint64_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right, sizeof(uint64_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_matching_cost, sizeof(uint8_t) * width_ * height_ * disparity_size_));

			CudaSafeCall(cudaMalloc(&this->d_scost, sizeof(uint16_t) * width_ * height_ * disparity_size_));

			CudaSafeCall(cudaMalloc(&this->d_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_tmp_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_tmp_right_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_right_disp, 0, sizeof(uint16_t) * width_ * height_));

			for (int i = 0; i < 8; i++) {
				CudaSafeCall(cudaStreamCreate(&this->cuda_streams[i]));
			}

			// create temporary buffer when dst type is 8bit host pointer
			if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
				this->h_output_16bit_buffer = (uint16_t*)malloc(sizeof(uint16_t) * width_ * height_);
			}
			else {
				this->h_output_16bit_buffer = NULL;
			}
		}

		~CudaStereoSGMResources() {
			CudaSafeCall(cudaFree(this->d_src_left));
			CudaSafeCall(cudaFree(this->d_src_right));

			CudaSafeCall(cudaFree(this->d_left));
			CudaSafeCall(cudaFree(this->d_right));

			CudaSafeCall(cudaFree(this->d_matching_cost));

			CudaSafeCall(cudaFree(this->d_scost));

			CudaSafeCall(cudaFree(this->d_left_disp));
			CudaSafeCall(cudaFree(this->d_right_disp));

			CudaSafeCall(cudaFree(this->d_tmp_left_disp));
			CudaSafeCall(cudaFree(this->d_tmp_right_disp));

			for (int i = 0; i < 8; i++) {
				CudaSafeCall(cudaStreamDestroy(this->cuda_streams[i]));
			}

			free(h_output_16bit_buffer);

			delete sgm_engine;
		}
	};

	StereoSGM::StereoSGM(int width, int height, int disparity_size, int input_depth_bits, int output_depth_bits,
		EXECUTE_INOUT inout_type, const Parameters& param) :
		width_(width),
		height_(height),
		disparity_size_(disparity_size),
		input_depth_bits_(input_depth_bits),
		output_depth_bits_(output_depth_bits),
		inout_type_(inout_type),
		param_(param),
		cu_res_(NULL)
	{
		// check values
		if (width_ % 2 != 0 || height_ % 2 != 0) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("width and height must be even");
		}
		if (input_depth_bits_ != 8 && input_depth_bits_ != 16 && output_depth_bits_ != 8 && output_depth_bits_ != 16) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("depth bits must be 8 or 16");
		}
		if (disparity_size_ != 64 && disparity_size_ != 128) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("disparity size must be 64 or 128");
		}

		cu_res_ = new CudaStereoSGMResources(width_, height_, disparity_size_, input_depth_bits_, output_depth_bits_, inout_type_);
	}

	StereoSGM::~StereoSGM() {
		if (cu_res_) { delete cu_res_; }
	}

	void StereoSGM::execute(const void* left_pixels, const void* right_pixels, void** dst) {

		const void *d_input_left, *d_input_right;

		if (is_cuda_input(inout_type_)) {
			d_input_left = left_pixels;
			d_input_right = right_pixels;
		}
		else {
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_left, left_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_right, right_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			d_input_left = cu_res_->d_src_left;
			d_input_right = cu_res_->d_src_right;
		}

		cu_res_->sgm_engine->execute((uint8_t*)cu_res_->d_left_disp, (uint8_t*)cu_res_->d_right_disp,
			d_input_left, d_input_right, width_, height_, param_.P1, param_.P2, param_.uniqueness);

		sgm::details::median_filter((uint8_t*)cu_res_->d_left_disp, (uint8_t*)cu_res_->d_tmp_left_disp, width_, height_);
		sgm::details::median_filter((uint8_t*)cu_res_->d_right_disp, (uint8_t*)cu_res_->d_tmp_right_disp, width_, height_);
		sgm::details::check_consistency((uint8_t*)cu_res_->d_tmp_left_disp, (uint8_t*)cu_res_->d_tmp_right_disp, d_input_left, width_, height_, input_depth_bits_);

		// output disparity image
		void* disparity_image = cu_res_->d_tmp_left_disp;

		if (!is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			CudaSafeCall(cudaMemcpy(cu_res_->h_output_16bit_buffer, disparity_image, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
			for (int i = 0; i < width_ * height_; i++) { ((uint16_t*)*dst)[i] = (uint16_t)cu_res_->h_output_16bit_buffer[i]; }
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			sgm::details::cast_8bit_16bit_array((const uint8_t*)disparity_image, (uint16_t*)*dst, width_ * height_);
		}
		else if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			CudaSafeCall(cudaMemcpy(*dst, disparity_image, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			*dst = disparity_image; // optimize! no-copy!
		}
		else {
			std::cerr << "not impl" << std::endl;
		}
	}
}
<commit_msg>Remove warning<commit_after>/*
Copyright 2016 Fixstars Corporation

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http ://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#include <iostream>

#include <libsgm.h>

#include "internal.h"
#include "sgm.hpp"

namespace sgm {
	static bool is_cuda_input(EXECUTE_INOUT type) { return (int)type & 0x1; }
	static bool is_cuda_output(EXECUTE_INOUT type) { return (int)type & 0x2; }

	class SemiGlobalMatchingBase {
	public:
		using output_type = uint8_t;
		virtual void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) = 0;

		virtual ~SemiGlobalMatchingBase();
	};

	class SemiGlobalMatching_8_64 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint8_t*)src_L, (const uint8_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint8_t, 64> sgm_engine_;
	};

	class SemiGlobalMatching_8_128 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint8_t*)src_L, (const uint8_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint8_t, 128> sgm_engine_;
	};

	class SemiGlobalMatching_16_64 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint16_t*)src_L, (const uint16_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint16_t, 64> sgm_engine_;
	};

	class SemiGlobalMatching_16_128 : public SemiGlobalMatchingBase {
	public:
		void execute(output_type* dst_L, output_type* dst_R, const void* src_L, const void* src_R,
			size_t w, size_t h, unsigned int P1, unsigned int P2, float uniqueness) override
		{
			sgm_engine_.execute(dst_L, dst_R, (const uint16_t*)src_L, (const uint16_t*)src_R, w, h, P1, P2, uniqueness);
		}
	private:
		SemiGlobalMatching<uint16_t, 128> sgm_engine_;
	};

	struct CudaStereoSGMResources {
		void* d_src_left;
		void* d_src_right;
		void* d_left;
		void* d_right;
		void* d_scost;
		void* d_matching_cost;
		void* d_left_disp;
		void* d_right_disp;

		void* d_tmp_left_disp;
		void* d_tmp_right_disp;

		cudaStream_t cuda_streams[8];

		uint16_t* h_output_16bit_buffer;

		SemiGlobalMatchingBase* sgm_engine;

		CudaStereoSGMResources(int width_, int height_, int disparity_size_, int input_depth_bits_, int output_depth_bits_, EXECUTE_INOUT inout_type_) {

			if (input_depth_bits_ == 8 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatching_8_64();
			else if (input_depth_bits_ == 8 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatching_8_128();
			else if (input_depth_bits_ == 16 && disparity_size_ == 64)
				sgm_engine = new SemiGlobalMatching_16_64();
			else if (input_depth_bits_ == 16 && disparity_size_ == 128)
				sgm_engine = new SemiGlobalMatching_16_128();
			else
				abort();

			if (is_cuda_input(inout_type_)) {
				this->d_src_left = NULL;
				this->d_src_right = NULL;
			}
			else {
				CudaSafeCall(cudaMalloc(&this->d_src_left, input_depth_bits_ / 8 * width_ * height_));
				CudaSafeCall(cudaMalloc(&this->d_src_right, input_depth_bits_ / 8 * width_ * height_));
			}

			CudaSafeCall(cudaMalloc(&this->d_left, sizeof(uint64_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right, sizeof(uint64_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_matching_cost, sizeof(uint8_t) * width_ * height_ * disparity_size_));

			CudaSafeCall(cudaMalloc(&this->d_scost, sizeof(uint16_t) * width_ * height_ * disparity_size_));

			CudaSafeCall(cudaMalloc(&this->d_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_right_disp, sizeof(uint16_t) * width_ * height_));

			CudaSafeCall(cudaMalloc(&this->d_tmp_left_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMalloc(&this->d_tmp_right_disp, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_left_disp, 0, sizeof(uint16_t) * width_ * height_));
			CudaSafeCall(cudaMemset(this->d_tmp_right_disp, 0, sizeof(uint16_t) * width_ * height_));

			for (int i = 0; i < 8; i++) {
				CudaSafeCall(cudaStreamCreate(&this->cuda_streams[i]));
			}

			// create temporary buffer when dst type is 8bit host pointer
			if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
				this->h_output_16bit_buffer = (uint16_t*)malloc(sizeof(uint16_t) * width_ * height_);
			}
			else {
				this->h_output_16bit_buffer = NULL;
			}
		}

		~CudaStereoSGMResources() {
			CudaSafeCall(cudaFree(this->d_src_left));
			CudaSafeCall(cudaFree(this->d_src_right));

			CudaSafeCall(cudaFree(this->d_left));
			CudaSafeCall(cudaFree(this->d_right));

			CudaSafeCall(cudaFree(this->d_matching_cost));

			CudaSafeCall(cudaFree(this->d_scost));

			CudaSafeCall(cudaFree(this->d_left_disp));
			CudaSafeCall(cudaFree(this->d_right_disp));

			CudaSafeCall(cudaFree(this->d_tmp_left_disp));
			CudaSafeCall(cudaFree(this->d_tmp_right_disp));

			for (int i = 0; i < 8; i++) {
				CudaSafeCall(cudaStreamDestroy(this->cuda_streams[i]));
			}

			free(h_output_16bit_buffer);

			delete sgm_engine;
		}
	};

	StereoSGM::StereoSGM(int width, int height, int disparity_size, int input_depth_bits, int output_depth_bits,
		EXECUTE_INOUT inout_type, const Parameters& param) :
		cu_res_(NULL),
		width_(width),
		height_(height),
		disparity_size_(disparity_size),
		input_depth_bits_(input_depth_bits),
		output_depth_bits_(output_depth_bits),
		inout_type_(inout_type),
		param_(param)
	{
		// check values
		if (width_ % 2 != 0 || height_ % 2 != 0) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("width and height must be even");
		}
		if (input_depth_bits_ != 8 && input_depth_bits_ != 16 && output_depth_bits_ != 8 && output_depth_bits_ != 16) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("depth bits must be 8 or 16");
		}
		if (disparity_size_ != 64 && disparity_size_ != 128) {
			width_ = height_ = input_depth_bits_ = output_depth_bits_ = disparity_size_ = 0;
			throw std::runtime_error("disparity size must be 64 or 128");
		}

		cu_res_ = new CudaStereoSGMResources(width_, height_, disparity_size_, input_depth_bits_, output_depth_bits_, inout_type_);
	}

	StereoSGM::~StereoSGM() {
		if (cu_res_) { delete cu_res_; }
	}

	void StereoSGM::execute(const void* left_pixels, const void* right_pixels, void** dst) {

		const void *d_input_left, *d_input_right;

		if (is_cuda_input(inout_type_)) {
			d_input_left = left_pixels;
			d_input_right = right_pixels;
		}
		else {
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_left, left_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			CudaSafeCall(cudaMemcpy(cu_res_->d_src_right, right_pixels, input_depth_bits_ / 8 * width_ * height_, cudaMemcpyHostToDevice));
			d_input_left = cu_res_->d_src_left;
			d_input_right = cu_res_->d_src_right;
		}

		cu_res_->sgm_engine->execute((uint8_t*)cu_res_->d_left_disp, (uint8_t*)cu_res_->d_right_disp,
			d_input_left, d_input_right, width_, height_, param_.P1, param_.P2, param_.uniqueness);

		sgm::details::median_filter((uint8_t*)cu_res_->d_left_disp, (uint8_t*)cu_res_->d_tmp_left_disp, width_, height_);
		sgm::details::median_filter((uint8_t*)cu_res_->d_right_disp, (uint8_t*)cu_res_->d_tmp_right_disp, width_, height_);
		sgm::details::check_consistency((uint8_t*)cu_res_->d_tmp_left_disp, (uint8_t*)cu_res_->d_tmp_right_disp, d_input_left, width_, height_, input_depth_bits_);

		// output disparity image
		void* disparity_image = cu_res_->d_tmp_left_disp;

		if (!is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			CudaSafeCall(cudaMemcpy(cu_res_->h_output_16bit_buffer, disparity_image, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
			for (int i = 0; i < width_ * height_; i++) { ((uint16_t*)*dst)[i] = (uint16_t)cu_res_->h_output_16bit_buffer[i]; }
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 16) {
			sgm::details::cast_8bit_16bit_array((const uint8_t*)disparity_image, (uint16_t*)*dst, width_ * height_);
		}
		else if (!is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			CudaSafeCall(cudaMemcpy(*dst, disparity_image, sizeof(uint8_t) * width_ * height_, cudaMemcpyDeviceToHost));
		}
		else if (is_cuda_output(inout_type_) && output_depth_bits_ == 8) {
			*dst = disparity_image; // optimize! no-copy!
		}
		else {
			std::cerr << "not impl" << std::endl;
		}
	}
}
<|endoftext|>


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

#include "gtest/gtest.h"

#include <memory>

#include "src/binary-reader.h"
#include "src/binary-reader-interp.h"
#include "src/cast.h"
#include "src/error-handler.h"
#include "src/interp.h"
#include "src/make-unique.h"

using namespace wabt;

namespace {

interp::Result TrapCallback(const interp::HostFunc* func,
                            const interp::FuncSignature* sig,
                            const interp::TypedValues& args,
                            interp::TypedValues& results) {
  return interp::Result::TrapHostTrapped;
}

class HostTrapTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    interp::HostModule* host_module = env_.AppendHostModule("host");
    host_module->AppendFuncExport("a", {{}, {}}, TrapCallback);
    executor_ = MakeUnique<interp::Executor>(&env_);
  }

  virtual void TearDown() {}

  interp::ExecResult LoadModuleAndRunStartFunction(
      const std::vector<uint8_t>& data) {
    ErrorHandlerFile error_handler(Location::Type::Binary);
    interp::DefinedModule* module = nullptr;
    ReadBinaryOptions options;
    Result result = ReadBinaryInterp(&env_, data.data(), data.size(), options,
                                     &error_handler, &module);
    EXPECT_EQ(Result::Ok, result);

    if (result == Result::Ok) {
      return executor_->RunStartFunction(module);
    } else {
      return {};
    }
  }

  interp::Environment env_;
  std::unique_ptr<interp::Executor> executor_;
};

}  // end of anonymous namespace

TEST_F(HostTrapTest, Call) {
  // (import "host" "a" (func $0))
  // (func $1 call $0)
  // (start $1)
  std::vector<uint8_t> data = {
      0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
      0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74,
      0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01,
      0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b,
  };
  ASSERT_EQ(interp::Result::TrapHostTrapped,
            LoadModuleAndRunStartFunction(data).result);
}

TEST_F(HostTrapTest, CallIndirect) {
  // (import "host" "a" (func $0))
  // (table anyfunc (elem $0))
  // (func $1 i32.const 0 call_indirect)
  // (start $1)
  std::vector<uint8_t> data = {
      0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60,
      0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x01, 0x61,
      0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01,
      0x01, 0x08, 0x01, 0x01, 0x09, 0x07, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x01,
      0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x41, 0x00, 0x11, 0x00, 0x00, 0x0b,
  };
  ASSERT_EQ(interp::Result::TrapHostTrapped,
            LoadModuleAndRunStartFunction(data).result);
}
<commit_msg>[interp] Add rot13 interpreter example (#890)<commit_after>/*
 * Copyright 2018 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "gtest/gtest.h"

#include <algorithm>
#include <functional>
#include <memory>
#include <string>
#include <vector>

#include "src/binary-reader.h"
#include "src/binary-reader-interp.h"
#include "src/cast.h"
#include "src/error-handler.h"
#include "src/interp.h"
#include "src/make-unique.h"

using namespace wabt;

namespace {

interp::Result TrapCallback(const interp::HostFunc* func,
                            const interp::FuncSignature* sig,
                            const interp::TypedValues& args,
                            interp::TypedValues& results) {
  return interp::Result::TrapHostTrapped;
}

class HostTrapTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    interp::HostModule* host_module = env_.AppendHostModule("host");
    host_module->AppendFuncExport("a", {{}, {}}, TrapCallback);
  }

  virtual void TearDown() {}

  interp::ExecResult LoadModuleAndRunStartFunction(
      const std::vector<uint8_t>& data) {
    ErrorHandlerFile error_handler(Location::Type::Binary);
    interp::DefinedModule* module = nullptr;
    ReadBinaryOptions options;
    Result result = ReadBinaryInterp(&env_, data.data(), data.size(), options,
                                     &error_handler, &module);
    EXPECT_EQ(Result::Ok, result);

    if (result == Result::Ok) {
      interp::Executor executor(&env_);
      return executor.RunStartFunction(module);
    } else {
      return {};
    }
  }

  interp::Environment env_;
};

}  // end of anonymous namespace

TEST_F(HostTrapTest, Call) {
  // (import "host" "a" (func $0))
  // (func $1 call $0)
  // (start $1)
  std::vector<uint8_t> data = {
      0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01,
      0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74,
      0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01,
      0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b,
  };
  ASSERT_EQ(interp::Result::TrapHostTrapped,
            LoadModuleAndRunStartFunction(data).result);
}

TEST_F(HostTrapTest, CallIndirect) {
  // (import "host" "a" (func $0))
  // (table anyfunc (elem $0))
  // (func $1 i32.const 0 call_indirect)
  // (start $1)
  std::vector<uint8_t> data = {
      0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60,
      0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x01, 0x61,
      0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01,
      0x01, 0x08, 0x01, 0x01, 0x09, 0x07, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x01,
      0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x41, 0x00, 0x11, 0x00, 0x00, 0x0b,
  };
  ASSERT_EQ(interp::Result::TrapHostTrapped,
            LoadModuleAndRunStartFunction(data).result);
}

namespace {

class HostMemoryTest : public ::testing::Test {
 protected:
  virtual void SetUp() {
    interp::HostModule* host_module = env_.AppendHostModule("host");
    executor_ = MakeUnique<interp::Executor>(&env_);
    std::pair<interp::Memory*, Index> pair =
        host_module->AppendMemoryExport("mem", Limits(1));

    using namespace std::placeholders;

    host_module->AppendFuncExport(
        "fill_buf", {{Type::I32, Type::I32}, {Type::I32}},
        std::bind(&HostMemoryTest::FillBufCallback, this, _1, _2, _3, _4));
    host_module->AppendFuncExport(
        "buf_done", {{Type::I32, Type::I32}, {}},
        std::bind(&HostMemoryTest::BufDoneCallback, this, _1, _2, _3, _4));
    memory_ = pair.first;
  }

  virtual void TearDown() {
    executor_.reset();
  }

  Result LoadModule(const std::vector<uint8_t>& data) {
    ErrorHandlerFile error_handler(Location::Type::Binary);
    ReadBinaryOptions options;
    return ReadBinaryInterp(&env_, data.data(), data.size(), options,
                            &error_handler, &module_);
  }

  std::string string_data;

  interp::Result FillBufCallback(const interp::HostFunc* func,
                                 const interp::FuncSignature* sig,
                                 const interp::TypedValues& args,
                                 interp::TypedValues& results) {
    // (param $ptr i32) (param $max_size i32) (result $size i32)
    EXPECT_EQ(2u, args.size());
    EXPECT_EQ(Type::I32, args[0].type);
    EXPECT_EQ(Type::I32, args[1].type);
    EXPECT_EQ(1u, results.size());
    EXPECT_EQ(Type::I32, results[0].type);

    uint32_t ptr = args[0].get_i32();
    uint32_t max_size = args[1].get_i32();
    uint32_t size = std::min(max_size, uint32_t(string_data.size()));

    EXPECT_LT(ptr + size, memory_->data.size());

    std::copy(string_data.begin(), string_data.begin() + size,
              memory_->data.begin() + ptr);

    results[0].set_i32(size);
    return interp::Result::Ok;
  }

  interp::Result BufDoneCallback(const interp::HostFunc* func,
                                 const interp::FuncSignature* sig,
                                 const interp::TypedValues& args,
                                 interp::TypedValues& results) {
    // (param $ptr i32) (param $size i32)
    EXPECT_EQ(2u, args.size());
    EXPECT_EQ(Type::I32, args[0].type);
    EXPECT_EQ(Type::I32, args[1].type);
    EXPECT_EQ(0u, results.size());

    uint32_t ptr = args[0].get_i32();
    uint32_t size = args[1].get_i32();

    EXPECT_LT(ptr + size, memory_->data.size());

    string_data.resize(size);
    std::copy(memory_->data.begin() + ptr, memory_->data.begin() + ptr + size,
              string_data.begin());

    return interp::Result::Ok;
  }

  interp::Environment env_;
  interp::Memory* memory_;
  interp::DefinedModule* module_;
  std::unique_ptr<interp::Executor> executor_;
};

}  // end of anonymous namespace

TEST_F(HostMemoryTest, Rot13) {
  // (import "host" "mem" (memory $mem 1))
  // (import "host" "fill_buf" (func $fill_buf (param i32 i32) (result i32)))
  // (import "host" "buf_done" (func $buf_done (param i32 i32)))
  //
  // (func $rot13c (param $c i32) (result i32)
  //   (local $uc i32)
  //
  //   ;; No change if < 'A'.
  //   (if (i32.lt_u (get_local $c) (i32.const 65))
  //     (return (get_local $c)))
  //
  //   ;; Clear 5th bit of c, to force uppercase. 0xdf = 0b11011111
  //   (set_local $uc (i32.and (get_local $c) (i32.const 0xdf)))
  //
  //   ;; In range ['A', 'M'] return |c| + 13.
  //   (if (i32.le_u (get_local $uc) (i32.const 77))
  //     (return (i32.add (get_local $c) (i32.const 13))))
  //
  //   ;; In range ['N', 'Z'] return |c| - 13.
  //   (if (i32.le_u (get_local $uc) (i32.const 90))
  //     (return (i32.sub (get_local $c) (i32.const 13))))
  //
  //   ;; No change for everything else.
  //   (return (get_local $c))
  // )
  //
  // (func (export "rot13")
  //   (local $size i32)
  //   (local $i i32)
  //
  //   ;; Ask host to fill memory [0, 1024) with data.
  //   (call $fill_buf (i32.const 0) (i32.const 1024))
  //
  //   ;; The host returns the size filled.
  //   (set_local $size)
  //
  //   ;; Loop over all bytes and rot13 them.
  //   (block $exit
  //     (loop $top
  //       ;; if (i >= size) break
  //       (if (i32.ge_u (get_local $i) (get_local $size)) (br $exit))
  //
  //       ;; mem[i] = rot13c(mem[i])
  //       (i32.store8
  //         (get_local $i)
  //         (call $rot13c
  //           (i32.load8_u (get_local $i))))
  //
  //       ;; i++
  //       (set_local $i (i32.add (get_local $i) (i32.const 1)))
  //       (br $top)
  //     )
  //   )
  //
  //   (call $buf_done (i32.const 0) (get_local $size))
  // )
  std::vector<uint8_t> data = {
      0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60,
      0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01,
      0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x2d, 0x03, 0x04, 0x68, 0x6f,
      0x73, 0x74, 0x03, 0x6d, 0x65, 0x6d, 0x02, 0x00, 0x01, 0x04, 0x68, 0x6f,
      0x73, 0x74, 0x08, 0x66, 0x69, 0x6c, 0x6c, 0x5f, 0x62, 0x75, 0x66, 0x00,
      0x00, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x08, 0x62, 0x75, 0x66, 0x5f, 0x64,
      0x6f, 0x6e, 0x65, 0x00, 0x01, 0x03, 0x03, 0x02, 0x02, 0x03, 0x07, 0x09,
      0x01, 0x05, 0x72, 0x6f, 0x74, 0x31, 0x33, 0x00, 0x03, 0x0a, 0x74, 0x02,
      0x39, 0x01, 0x01, 0x7f, 0x20, 0x00, 0x41, 0xc1, 0x00, 0x49, 0x04, 0x40,
      0x20, 0x00, 0x0f, 0x0b, 0x20, 0x00, 0x41, 0xdf, 0x01, 0x71, 0x21, 0x01,
      0x20, 0x01, 0x41, 0xcd, 0x00, 0x4d, 0x04, 0x40, 0x20, 0x00, 0x41, 0x0d,
      0x6a, 0x0f, 0x0b, 0x20, 0x01, 0x41, 0xda, 0x00, 0x4d, 0x04, 0x40, 0x20,
      0x00, 0x41, 0x0d, 0x6b, 0x0f, 0x0b, 0x20, 0x00, 0x0f, 0x0b, 0x38, 0x01,
      0x02, 0x7f, 0x41, 0x00, 0x41, 0x80, 0x08, 0x10, 0x00, 0x21, 0x00, 0x02,
      0x40, 0x03, 0x40, 0x20, 0x01, 0x20, 0x00, 0x4f, 0x04, 0x40, 0x0c, 0x02,
      0x0b, 0x20, 0x01, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x10, 0x02, 0x3a, 0x00,
      0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b,
      0x41, 0x00, 0x20, 0x00, 0x10, 0x01, 0x0b,
  };

  ASSERT_EQ(Result::Ok, LoadModule(data));

  string_data = "Hello, WebAssembly!";

  ASSERT_EQ(interp::Result::Ok,
            executor_->RunExportByName(module_, "rot13", {}).result);

  ASSERT_EQ("Uryyb, JroNffrzoyl!", string_data);

  ASSERT_EQ(interp::Result::Ok,
            executor_->RunExportByName(module_, "rot13", {}).result);

  ASSERT_EQ("Hello, WebAssembly!", string_data);
}
<|endoftext|>