<commit_before>/*
* Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
*
* 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.
*
*/

#include "appshell_node_process.h"
#include "appshell_node_process_internal.h"

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>

#ifndef OS_LINUX
#define OS_LINUX 1
#endif
#include "config.h"

#define BRACKETS_NODE_BUFFER_SIZE 4096
#define MAX_PATH 128

// init mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// write & read references to subprocess
FILE* streamTo;
FILE* streamFrom;

// Threads should hold mutex before using these
static int nodeState = BRACKETS_NODE_NOT_YET_STARTED;
static int nodeStartTime = 0;

// Forward declarations
void* nodeThread(void*);
void* nodeReadThread(void*);
void restartNode(bool);

// Creates the thread that starts Node and then monitors the state
// of the node process.
void startNodeProcess() {

    pthread_t thread_id;
    if (pthread_create(&thread_id, NULL, &nodeThread, NULL) != 0)
        nodeState = BRACKETS_NODE_FAILED;
}

// Thread function for the thread that starts the node process
// and monitors that process's state
void* nodeThread(void* unused) {

    // get mutex
    if (pthread_mutex_lock(&mutex)) {
        fprintf(stderr,
                "failed to acquire mutex for Node subprocess start: %s\n",
                strerror(errno));
        return NULL;
    }

    // TODO nodeStartTime = get time();

    char executablePath[MAX_PATH];
    char bracketsDirPath[MAX_PATH];
    char nodeExecutablePath[MAX_PATH];
    char nodecorePath[MAX_PATH];

    // get path to Brackets
    if (readlink("/proc/self/exe", executablePath, MAX_PATH) == -1) {
        fprintf(stderr, "cannot find Brackets path: %s\n", strerror(errno));
        pthread_mutex_unlock(&mutex);
        return NULL;
    }

    // strip off trailing executable name
    char* lastIndexOf = strrchr(executablePath, '/');
    memcpy(bracketsDirPath, executablePath, lastIndexOf - executablePath + 1);

    // create node exec and node-core paths
    strcpy(nodeExecutablePath, bracketsDirPath);
    strcat(nodeExecutablePath, NODE_EXECUTABLE_PATH);
    strcpy(nodecorePath, bracketsDirPath);
    strcat(nodecorePath, NODE_CORE_PATH);

    // create pipes for node process stdin/stdout
    int toNode[2];
    int fromNode[2];

    pipe(toNode);
    pipe(fromNode);

    // create the Node process
    pid_t child_pid = fork();
    if (child_pid == 0) { // child (node) process
        // close our copy of write end and
        // connect read end to stdin
        close(toNode[1]);
        dup2(toNode[0], STDIN_FILENO);

        // close our copy of read end and
        // connect write end to stdout
        close(fromNode[0]);
        dup2(fromNode[1], STDOUT_FILENO);

        // run node executable
        char* arg_list[] = { nodeExecutablePath, nodecorePath, NULL};
        execvp(arg_list[0], arg_list);

        fprintf(stderr, "the Node process failed to start: %s\n", strerror(errno));
        abort();
    }
    else { // parent

        // close our reference of toNode's read end
        // and fromNode's write end
        close(toNode[0]);
        close(fromNode[1]);

        // convert subprocess write & read to FILE objects
        streamTo = fdopen(toNode[1], "w");
        streamFrom = fdopen(fromNode[0], "r");

        nodeState = BRACKETS_NODE_PORT_NOT_YET_SET;

        // done launching process so release mutex
        if (pthread_mutex_unlock(&mutex)) {
            fprintf(stderr,
                "failed to release mutex for Node subprocess startup: %s\n",
                strerror(errno));
        }

        // start pipe read thread
        pthread_t readthread_id;
        if (pthread_create(&readthread_id, NULL, &nodeReadThread, NULL) != 0)
            nodeState = BRACKETS_NODE_FAILED;
            // ugly - need to think more about what to do if read thread fails

    }

    return NULL;
}

// Thread function for the thread that reads from the Node pipe
// Reads on anonymous pipes are always blocking (OVERLAPPED reads
// are not possible) So, we need to do this in a separate thread

// TODO: This code first reads to a character buffer, and then
// copies it to a std::string. The code could be optimized to avoid
// this double-copy
void* nodeReadThread(void* unused) {

    char charBuf[BRACKETS_NODE_BUFFER_SIZE];
    std::string strBuf("");
    while (fgets(charBuf, BRACKETS_NODE_BUFFER_SIZE, streamFrom) != NULL) {
        strBuf.assign(charBuf);
        processIncomingData(strBuf);
    }
}

// Determines whether the current node process has run long
// enough that a restart is warranted, and initiates the startup
// if so. Can also optionally terminate the running process.
void restartNode(bool terminateCurrentProcess) {

}

// Sends data to the node process. If the write fails completely,
// calls restartNode.
void sendData(const std::string &data) {

    if (pthread_mutex_lock(&mutex)) {
        fprintf(stderr,
            "failed to acquire mutex for write to Node subprocess: %s\n",
            strerror(errno));
        return;
    }

    // write to pipe
    fprintf(streamTo, "%s", data.c_str());

    if (pthread_mutex_unlock(&mutex)) {
            fprintf(stderr,
            "failed to release mutex for write to Node subprocess: %s\n",
            strerror(errno));
    }
}

// Returns nodeState variable
int getNodeState() {

    return nodeState;
}

// Thread-safe way to set nodeState variable
void setNodeState(int newState) {

    if (pthread_mutex_lock(&mutex)) {
            fprintf(stderr,
            "failed to acquire mutex for setting Node state: %s\n",
            strerror(errno));
            return;
    }
    nodeState = newState;
    if (pthread_mutex_unlock(&mutex)) {
            fprintf(stderr,
            "failed to release mutex for Node set state: %s\n",
            strerror(errno));
    }
}
<commit_msg>restart node process in linux<commit_after>/*
* Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
*
* 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.
*
*/

#include "appshell_node_process.h"
#include "appshell_node_process_internal.h"

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>

#ifndef OS_LINUX
#define OS_LINUX 1
#endif
#include "config.h"

#define BRACKETS_NODE_BUFFER_SIZE 4096
#define MAX_PATH 128

// init mutex
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// write & read references to subprocess
FILE* streamTo;
FILE* streamFrom;

// Threads should hold mutex before using these
static int nodeState = BRACKETS_NODE_NOT_YET_STARTED;
static int nodeStartTime = 0;

// Forward declarations
void* nodeThread(void*);
void* nodeReadThread(void*);
void restartNode(bool);

// Creates the thread that starts Node and then monitors the state
// of the node process.
void startNodeProcess() {

    pthread_t thread_id;
    if (pthread_create(&thread_id, NULL, &nodeThread, NULL) != 0)
        nodeState = BRACKETS_NODE_FAILED;
}

// Thread function for the thread that starts the node process
// and monitors that process's state
void* nodeThread(void* unused) {
    while(1){
        fprintf(stderr, "starting child\n");

        // get mutex
        if (pthread_mutex_lock(&mutex)) {
            fprintf(stderr,
                    "failed to acquire mutex for Node subprocess start: %s\n",
                    strerror(errno));
            return NULL;
        }

        // TODO nodeStartTime = get time();

        char executablePath[MAX_PATH];
        char bracketsDirPath[MAX_PATH];
        char nodeExecutablePath[MAX_PATH];
        char nodecorePath[MAX_PATH];

        // get path to Brackets
        if (readlink("/proc/self/exe", executablePath, MAX_PATH) == -1) {
            fprintf(stderr, "cannot find Brackets path: %s\n", strerror(errno));
            pthread_mutex_unlock(&mutex);
            return NULL;
        }

        // strip off trailing executable name
        char* lastIndexOf = strrchr(executablePath, '/');
        memcpy(bracketsDirPath, executablePath, lastIndexOf - executablePath + 1);

        // create node exec and node-core paths
        strcpy(nodeExecutablePath, bracketsDirPath);
        strcat(nodeExecutablePath, NODE_EXECUTABLE_PATH);
        strcpy(nodecorePath, bracketsDirPath);
        strcat(nodecorePath, NODE_CORE_PATH);

        // create pipes for node process stdin/stdout
        int toNode[2];
        int fromNode[2];

        pipe(toNode);
        pipe(fromNode);

        // create the Node process
        pid_t child_pid = fork();
        if (child_pid == 0) { // child (node) process
            // close our copy of write end and
            // connect read end to stdin
            close(toNode[1]);
            dup2(toNode[0], STDIN_FILENO);

            // close our copy of read end and
            // connect write end to stdout
            close(fromNode[0]);
            dup2(fromNode[1], STDOUT_FILENO);

            // run node executable
            char* arg_list[] = { nodeExecutablePath, nodecorePath, NULL};
            execvp(arg_list[0], arg_list);

            fprintf(stderr, "the Node process failed to start: %s\n", strerror(errno));
            abort();
        }
        else { // parent

            // close our reference of toNode's read end
            // and fromNode's write end
            close(toNode[0]);
            close(fromNode[1]);

            // convert subprocess write & read to FILE objects
            streamTo = fdopen(toNode[1], "w");
            streamFrom = fdopen(fromNode[0], "r");

            nodeState = BRACKETS_NODE_PORT_NOT_YET_SET;

            // done launching process so release mutex
            if (pthread_mutex_unlock(&mutex)) {
                fprintf(stderr,
                    "failed to release mutex for Node subprocess startup: %s\n",
                    strerror(errno));
            }

            // start pipe read thread
            pthread_t readthread_id;
            if (pthread_create(&readthread_id, NULL, &nodeReadThread, NULL) != 0)
                nodeState = BRACKETS_NODE_FAILED;
                // ugly - need to think more about what to do if read thread fails
            int status;
            waitpid(child_pid, &status, 0);
            fprintf(stderr,
                    "child exited : %d\n",
                    status);
        }
    }
    return NULL;
}

// Thread function for the thread that reads from the Node pipe
// Reads on anonymous pipes are always blocking (OVERLAPPED reads
// are not possible) So, we need to do this in a separate thread

// TODO: This code first reads to a character buffer, and then
// copies it to a std::string. The code could be optimized to avoid
// this double-copy
void* nodeReadThread(void* unused) {

    char charBuf[BRACKETS_NODE_BUFFER_SIZE];
    std::string strBuf("");
    while (fgets(charBuf, BRACKETS_NODE_BUFFER_SIZE, streamFrom) != NULL) {
        strBuf.assign(charBuf);
        processIncomingData(strBuf);
    }
}

// Determines whether the current node process has run long
// enough that a restart is warranted, and initiates the startup
// if so. Can also optionally terminate the running process.
void restartNode(bool terminateCurrentProcess) {

}

// Sends data to the node process. If the write fails completely,
// calls restartNode.
void sendData(const std::string &data) {

    if (pthread_mutex_lock(&mutex)) {
        fprintf(stderr,
            "failed to acquire mutex for write to Node subprocess: %s\n",
            strerror(errno));
        return;
    }

    // write to pipe
    fprintf(streamTo, "%s", data.c_str());

    if (pthread_mutex_unlock(&mutex)) {
            fprintf(stderr,
            "failed to release mutex for write to Node subprocess: %s\n",
            strerror(errno));
    }
}

// Returns nodeState variable
int getNodeState() {

    return nodeState;
}

// Thread-safe way to set nodeState variable
void setNodeState(int newState) {

    if (pthread_mutex_lock(&mutex)) {
            fprintf(stderr,
            "failed to acquire mutex for setting Node state: %s\n",
            strerror(errno));
            return;
    }
    nodeState = newState;
    if (pthread_mutex_unlock(&mutex)) {
            fprintf(stderr,
            "failed to release mutex for Node set state: %s\n",
            strerror(errno));
    }
}
<|endoftext|>


<commit_before><commit_msg>comment change<commit_after><|endoftext|>


<commit_before><commit_msg>suite-tolower: Added tests for two-byte codepoints.<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (c) 2003 The Regents of The University of Michigan
 * All rights reserved.
 *
 * 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;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * 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
 * OWNER 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 <sys/types.h>
#include <sys/stat.h>

#include <cstring>
#include <fstream>
#include <list>
#include <string>
#include <vector>

#include "base/files.hh"
#include "base/misc.hh"
#include "sim/universe.hh"
#include "sim/host.hh"
#include "sim/param.hh"

using namespace std;

Tick curTick = 0;
Tick ticksPerSecond;
double __ticksPerMS;
double __ticksPerUS;
double __ticksPerNS;

string outputDirectory;
ostream *outputStream;

class UniverseParamContext : public ParamContext
{
  private:
    ofstream outputFile;

  public:
    UniverseParamContext(const string &is) : ParamContext(is) {}
    void checkParams();
};

UniverseParamContext universe("Universe");

Param<Tick> universe_freq(&universe, "frequency", "tick frequency",
                          200000000);

Param<string> universe_output_dir(&universe, "output_dir",
                                  "directory to output data to");
Param<string> universe_output_file(&universe, "output_file",
                                   "file to dump simulator output to");

void
UniverseParamContext::checkParams()
{
    ticksPerSecond = universe_freq;
    double freq = double(ticksPerSecond);
    __ticksPerMS = freq / 1.0e3;
    __ticksPerUS = freq / 1.0e6;
    __ticksPerNS = freq / 1.0e9;

    if (universe_output_dir.isValid()) {
        outputDirectory = universe_output_dir;

        // guarantee that directory ends with a '/'
        if (outputDirectory[outputDirectory.size() - 1] != '/')
            outputDirectory += "/";

        if (mkdir(outputDirectory.c_str(), 0777) < 0) {
            if (errno != EEXIST) {
                panic("%s\ncould not make output directory: %s\n",
                      strerror(errno), outputDirectory);
            }
        }
    }

    string filename;
    if (universe_output_file.isValid()) {
        string f = universe_output_file;
        if (f != "stdout" && f != "cout" && f != "stderr" && f != "cerr")
            filename = outputDirectory + f;
        else
            filename = f;
    } else {
        if (outputDirectory.empty())
            filename = "stdout";
        else
            filename = outputDirectory + "output.txt";
    }

    if (filename == "stdout" || filename == "cout")
        outputStream = &cout;
    else if (filename == "stderr" || filename == "cerr")
        outputStream = &cerr;
    else {
        outputFile.open(filename.c_str(), ios::trunc);
        outputStream = &outputFile;
    }
}
<commit_msg>remove #include of nonexistent file<commit_after>/*
 * Copyright (c) 2003 The Regents of The University of Michigan
 * All rights reserved.
 *
 * 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;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * 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
 * OWNER 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 <sys/types.h>
#include <sys/stat.h>

#include <cstring>
#include <fstream>
#include <list>
#include <string>
#include <vector>

#include "base/misc.hh"
#include "sim/universe.hh"
#include "sim/host.hh"
#include "sim/param.hh"

using namespace std;

Tick curTick = 0;
Tick ticksPerSecond;
double __ticksPerMS;
double __ticksPerUS;
double __ticksPerNS;

string outputDirectory;
ostream *outputStream;

class UniverseParamContext : public ParamContext
{
  private:
    ofstream outputFile;

  public:
    UniverseParamContext(const string &is) : ParamContext(is) {}
    void checkParams();
};

UniverseParamContext universe("Universe");

Param<Tick> universe_freq(&universe, "frequency", "tick frequency",
                          200000000);

Param<string> universe_output_dir(&universe, "output_dir",
                                  "directory to output data to");
Param<string> universe_output_file(&universe, "output_file",
                                   "file to dump simulator output to");

void
UniverseParamContext::checkParams()
{
    ticksPerSecond = universe_freq;
    double freq = double(ticksPerSecond);
    __ticksPerMS = freq / 1.0e3;
    __ticksPerUS = freq / 1.0e6;
    __ticksPerNS = freq / 1.0e9;

    if (universe_output_dir.isValid()) {
        outputDirectory = universe_output_dir;

        // guarantee that directory ends with a '/'
        if (outputDirectory[outputDirectory.size() - 1] != '/')
            outputDirectory += "/";

        if (mkdir(outputDirectory.c_str(), 0777) < 0) {
            if (errno != EEXIST) {
                panic("%s\ncould not make output directory: %s\n",
                      strerror(errno), outputDirectory);
            }
        }
    }

    string filename;
    if (universe_output_file.isValid()) {
        string f = universe_output_file;
        if (f != "stdout" && f != "cout" && f != "stderr" && f != "cerr")
            filename = outputDirectory + f;
        else
            filename = f;
    } else {
        if (outputDirectory.empty())
            filename = "stdout";
        else
            filename = outputDirectory + "output.txt";
    }

    if (filename == "stdout" || filename == "cout")
        outputStream = &cout;
    else if (filename == "stderr" || filename == "cerr")
        outputStream = &cerr;
    else {
        outputFile.open(filename.c_str(), ios::trunc);
        outputStream = &outputFile;
    }
}
<|endoftext|>


<commit_before>/* LICENSE NOTICE
	Copyright (c) 2009, Frederick Emmott <mail@fredemmott.co.uk>

	Permission to use, copy, modify, and/or distribute this software for any
	purpose with or without fee is hereby granted, provided that the above
	copyright notice and this permission notice appear in all copies.

	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "Responder.h"

#include "OutputDevice.h"

#include <QDebug>
#include <QIODevice>

namespace FastCgiQt
{
	Responder::Responder(const Request& request, QIODevice* outputDevice, QIODevice* inputDevice, QObject* parent)
		:
			ClientIOInterface(request, outputDevice, inputDevice, parent)
	{
	}

	void Responder::finished()
	{
		emit finished(this, request);
	}

	Responder::~Responder()
	{
	}
}
<commit_msg>remove unused OutputDevice header from Responder<commit_after>/* LICENSE NOTICE
	Copyright (c) 2009, Frederick Emmott <mail@fredemmott.co.uk>

	Permission to use, copy, modify, and/or distribute this software for any
	purpose with or without fee is hereby granted, provided that the above
	copyright notice and this permission notice appear in all copies.

	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "Responder.h"

#include <QDebug>
#include <QIODevice>

namespace FastCgiQt
{
	Responder::Responder(const Request& request, QIODevice* outputDevice, QIODevice* inputDevice, QObject* parent)
		:
			ClientIOInterface(request, outputDevice, inputDevice, parent)
	{
	}

	void Responder::finished()
	{
		emit finished(this, request);
	}

	Responder::~Responder()
	{
	}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <map>

#include "IRVisitor.h"
#include "IRMatch.h"
#include "IREquality.h"
#include "IROperator.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::map;
using std::string;

void expr_match_test() {
    vector<Expr> matches;
    Expr w = Variable::make(Int(32), "*");
    Expr fw = Variable::make(Float(32), "*");
    Expr x = Variable::make(Int(32), "x");
    Expr y = Variable::make(Int(32), "y");
    Expr fx = Variable::make(Float(32), "fx");
    Expr fy = Variable::make(Float(32), "fy");

    Expr vec_wild = Variable::make(Int(32, 4), "*");

    internal_assert(expr_match(w, 3, matches) &&
                    equal(matches[0], 3));

    internal_assert(expr_match(w + 3, (y*2) + 3, matches) &&
                    equal(matches[0], y*2));

    internal_assert(expr_match(fw * 17 + cast<float>(w + cast<int>(fw)),
                               (81.0f * fy) * 17 + cast<float>(x/2 + cast<int>(x + 4.5f)), matches) &&
                    equal(matches[0], 81.0f * fy) &&
                    equal(matches[1], x/2) &&
                    equal(matches[2], x + 4.5f)) << matches[0] << ", " << matches[1] << ", " << matches[2] << "\n";

    internal_assert(!expr_match(fw + 17, fx + 18, matches) &&
                    matches.empty());
    internal_assert(!expr_match((w*2) + 17, fx + 17, matches) &&
                    matches.empty());
    internal_assert(!expr_match(w * 3, 3 * x, matches) &&
                    matches.empty());

    internal_assert(expr_match(vec_wild * 3, Ramp::make(x, y, 4) * 3, matches));

    std::cout << "expr_match test passed" << std::endl;
}

class IRMatch : public IRVisitor {
public:
    bool result;
    vector<Expr> *matches;
    map<string, Expr> *var_matches;
    Expr expr;

    IRMatch(Expr e, vector<Expr> &m) : result(true), matches(&m), var_matches(NULL), expr(e) {
    }
    IRMatch(Expr e, map<string, Expr> &m) : result(true), matches(NULL), var_matches(&m), expr(e) {
    }

    using IRVisitor::visit;

    bool types_match(Type pattern_type, Type expr_type) {
        bool bits_matches  = (pattern_type.bits  == -1) || (pattern_type.bits  == expr_type.bits);
        bool width_matches = (pattern_type.width == -1) || (pattern_type.width == expr_type.width);
        bool code_matches  = (pattern_type.code  == expr_type.code);
        return bits_matches && width_matches && code_matches;
    }

    void visit(const IntImm *op) {
        const IntImm *e = expr.as<IntImm>();
        if (!e ||
            e->value != op->value ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const UIntImm *op) {
        const UIntImm *e = expr.as<UIntImm>();
        if (!e ||
            e->value != op->value ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const FloatImm *op) {
        const FloatImm *e = expr.as<FloatImm>();
        if (!e ||
            e->value != op->value ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const Cast *op) {
        const Cast *e = expr.as<Cast>();
        if (result && e && types_match(op->type, e->type)) {
            expr = e->value;
            op->value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Variable *op) {
        if (!result) {
            return;
        }

        if (!types_match(op->type, expr.type())) {
            result = false;
        } else if (matches) {
            if (op->name == "*") {
                matches->push_back(expr);
            } else {
                const Variable *e = expr.as<Variable>();
                result = e && (e->name == op->name);
            }
        } else if (var_matches) {
            Expr &match = (*var_matches)[op->name];
            if (match.defined()) {
                result = equal(match, expr);
            } else {
                match = expr;
            }
        }
    }

    template<typename T>
    void visit_binary_operator(const T *op) {
        const T *e = expr.as<T>();
        if (result && e) {
            expr = e->a;
            op->a.accept(this);
            expr = e->b;
            op->b.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Add *op) {visit_binary_operator(op);}
    void visit(const Sub *op) {visit_binary_operator(op);}
    void visit(const Mul *op) {visit_binary_operator(op);}
    void visit(const Div *op) {visit_binary_operator(op);}
    void visit(const Mod *op) {visit_binary_operator(op);}
    void visit(const Min *op) {visit_binary_operator(op);}
    void visit(const Max *op) {visit_binary_operator(op);}
    void visit(const EQ *op) {visit_binary_operator(op);}
    void visit(const NE *op) {visit_binary_operator(op);}
    void visit(const LT *op) {visit_binary_operator(op);}
    void visit(const LE *op) {visit_binary_operator(op);}
    void visit(const GT *op) {visit_binary_operator(op);}
    void visit(const GE *op) {visit_binary_operator(op);}
    void visit(const And *op) {visit_binary_operator(op);}
    void visit(const Or *op) {visit_binary_operator(op);}

    void visit(const Not *op) {
        const Not *e = expr.as<Not>();
        if (result && e) {
            expr = e->a;
            op->a.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Select *op) {
        const Select *e = expr.as<Select>();
        if (result && e) {
            expr = e->condition;
            op->condition.accept(this);
            expr = e->true_value;
            op->true_value.accept(this);
            expr = e->false_value;
            op->false_value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Load *op) {
        const Load *e = expr.as<Load>();
        if (result && e && types_match(op->type, e->type) && e->name == op->name) {
            expr = e->index;
            op->index.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Ramp *op) {
        const Ramp *e = expr.as<Ramp>();
        if (result && e && e->width == op->width) {
            expr = e->base;
            op->base.accept(this);
            expr = e->stride;
            op->stride.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Broadcast *op) {
        const Broadcast *e = expr.as<Broadcast>();
        if (result && e && types_match(op->type, e->type)) {
            expr = e->value;
            op->value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Call *op) {
        const Call *e = expr.as<Call>();
        if (result && e &&
            types_match(op->type, e->type) &&
            e->name == op->name &&
            e->value_index == op->value_index &&
            e->call_type == op->call_type &&
            e->args.size() == op->args.size()) {
            for (size_t i = 0; result && (i < e->args.size()); i++) {
                expr = e->args[i];
                op->args[i].accept(this);
            }
        } else {
            result = false;
        }
    }

    void visit(const Let *op) {
        const Let *e = expr.as<Let>();
        if (result && e && e->name == op->name) {
            expr = e->value;
            op->value.accept(this);
            expr = e->body;
            op->body.accept(this);
        } else {
            result = false;
        }
    }
};

bool expr_match(Expr pattern, Expr expr, vector<Expr> &matches) {
    matches.clear();
    if (!pattern.defined() && !expr.defined()) return true;
    if (!pattern.defined() || !expr.defined()) return false;

    IRMatch eq(expr, matches);
    pattern.accept(&eq);
    if (eq.result) {
        return true;
    } else {
        matches.clear();
        return false;
    }
}

bool expr_match(Expr pattern, Expr expr, map<string, Expr> &matches) {
    // Explicitly don't clear matches. This allows usages to pre-match
    // some variables.

    if (!pattern.defined() && !expr.defined()) return true;
    if (!pattern.defined() || !expr.defined()) return false;

    IRMatch eq(expr, matches);
    pattern.accept(&eq);
    if (eq.result) {
        return true;
    } else {
        matches.clear();
        return false;
    }
}

}}
<commit_msg>IR match uses bit-equality instead of double equality<commit_after>#include <iostream>
#include <map>

#include "IRVisitor.h"
#include "IRMatch.h"
#include "IREquality.h"
#include "IROperator.h"

namespace Halide {
namespace Internal {

using std::vector;
using std::map;
using std::string;

void expr_match_test() {
    vector<Expr> matches;
    Expr w = Variable::make(Int(32), "*");
    Expr fw = Variable::make(Float(32), "*");
    Expr x = Variable::make(Int(32), "x");
    Expr y = Variable::make(Int(32), "y");
    Expr fx = Variable::make(Float(32), "fx");
    Expr fy = Variable::make(Float(32), "fy");

    Expr vec_wild = Variable::make(Int(32, 4), "*");

    internal_assert(expr_match(w, 3, matches) &&
                    equal(matches[0], 3));

    internal_assert(expr_match(w + 3, (y*2) + 3, matches) &&
                    equal(matches[0], y*2));

    internal_assert(expr_match(fw * 17 + cast<float>(w + cast<int>(fw)),
                               (81.0f * fy) * 17 + cast<float>(x/2 + cast<int>(x + 4.5f)), matches) &&
                    equal(matches[0], 81.0f * fy) &&
                    equal(matches[1], x/2) &&
                    equal(matches[2], x + 4.5f)) << matches[0] << ", " << matches[1] << ", " << matches[2] << "\n";

    internal_assert(!expr_match(fw + 17, fx + 18, matches) &&
                    matches.empty());
    internal_assert(!expr_match((w*2) + 17, fx + 17, matches) &&
                    matches.empty());
    internal_assert(!expr_match(w * 3, 3 * x, matches) &&
                    matches.empty());

    internal_assert(expr_match(vec_wild * 3, Ramp::make(x, y, 4) * 3, matches));

    std::cout << "expr_match test passed" << std::endl;
}

class IRMatch : public IRVisitor {
public:
    bool result;
    vector<Expr> *matches;
    map<string, Expr> *var_matches;
    Expr expr;

    IRMatch(Expr e, vector<Expr> &m) : result(true), matches(&m), var_matches(NULL), expr(e) {
    }
    IRMatch(Expr e, map<string, Expr> &m) : result(true), matches(NULL), var_matches(&m), expr(e) {
    }

    using IRVisitor::visit;

    bool types_match(Type pattern_type, Type expr_type) {
        bool bits_matches  = (pattern_type.bits  == -1) || (pattern_type.bits  == expr_type.bits);
        bool width_matches = (pattern_type.width == -1) || (pattern_type.width == expr_type.width);
        bool code_matches  = (pattern_type.code  == expr_type.code);
        return bits_matches && width_matches && code_matches;
    }

    void visit(const IntImm *op) {
        const IntImm *e = expr.as<IntImm>();
        if (!e ||
            e->value != op->value ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const UIntImm *op) {
        const UIntImm *e = expr.as<UIntImm>();
        if (!e ||
            e->value != op->value ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const FloatImm *op) {
        const FloatImm *e = expr.as<FloatImm>();
        // Note we use uint64_t equality instead of double equality to
        // catch NaNs. We're checking for the same bits.
        if (!e ||
            reinterpret_bits<uint64_t>(e->value) !=
            reinterpret_bits<uint64_t>(op->value) ||
            !types_match(op->type, e->type)) {
            result = false;
        }
    }

    void visit(const Cast *op) {
        const Cast *e = expr.as<Cast>();
        if (result && e && types_match(op->type, e->type)) {
            expr = e->value;
            op->value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Variable *op) {
        if (!result) {
            return;
        }

        if (!types_match(op->type, expr.type())) {
            result = false;
        } else if (matches) {
            if (op->name == "*") {
                matches->push_back(expr);
            } else {
                const Variable *e = expr.as<Variable>();
                result = e && (e->name == op->name);
            }
        } else if (var_matches) {
            Expr &match = (*var_matches)[op->name];
            if (match.defined()) {
                result = equal(match, expr);
            } else {
                match = expr;
            }
        }
    }

    template<typename T>
    void visit_binary_operator(const T *op) {
        const T *e = expr.as<T>();
        if (result && e) {
            expr = e->a;
            op->a.accept(this);
            expr = e->b;
            op->b.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Add *op) {visit_binary_operator(op);}
    void visit(const Sub *op) {visit_binary_operator(op);}
    void visit(const Mul *op) {visit_binary_operator(op);}
    void visit(const Div *op) {visit_binary_operator(op);}
    void visit(const Mod *op) {visit_binary_operator(op);}
    void visit(const Min *op) {visit_binary_operator(op);}
    void visit(const Max *op) {visit_binary_operator(op);}
    void visit(const EQ *op) {visit_binary_operator(op);}
    void visit(const NE *op) {visit_binary_operator(op);}
    void visit(const LT *op) {visit_binary_operator(op);}
    void visit(const LE *op) {visit_binary_operator(op);}
    void visit(const GT *op) {visit_binary_operator(op);}
    void visit(const GE *op) {visit_binary_operator(op);}
    void visit(const And *op) {visit_binary_operator(op);}
    void visit(const Or *op) {visit_binary_operator(op);}

    void visit(const Not *op) {
        const Not *e = expr.as<Not>();
        if (result && e) {
            expr = e->a;
            op->a.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Select *op) {
        const Select *e = expr.as<Select>();
        if (result && e) {
            expr = e->condition;
            op->condition.accept(this);
            expr = e->true_value;
            op->true_value.accept(this);
            expr = e->false_value;
            op->false_value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Load *op) {
        const Load *e = expr.as<Load>();
        if (result && e && types_match(op->type, e->type) && e->name == op->name) {
            expr = e->index;
            op->index.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Ramp *op) {
        const Ramp *e = expr.as<Ramp>();
        if (result && e && e->width == op->width) {
            expr = e->base;
            op->base.accept(this);
            expr = e->stride;
            op->stride.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Broadcast *op) {
        const Broadcast *e = expr.as<Broadcast>();
        if (result && e && types_match(op->type, e->type)) {
            expr = e->value;
            op->value.accept(this);
        } else {
            result = false;
        }
    }

    void visit(const Call *op) {
        const Call *e = expr.as<Call>();
        if (result && e &&
            types_match(op->type, e->type) &&
            e->name == op->name &&
            e->value_index == op->value_index &&
            e->call_type == op->call_type &&
            e->args.size() == op->args.size()) {
            for (size_t i = 0; result && (i < e->args.size()); i++) {
                expr = e->args[i];
                op->args[i].accept(this);
            }
        } else {
            result = false;
        }
    }

    void visit(const Let *op) {
        const Let *e = expr.as<Let>();
        if (result && e && e->name == op->name) {
            expr = e->value;
            op->value.accept(this);
            expr = e->body;
            op->body.accept(this);
        } else {
            result = false;
        }
    }
};

bool expr_match(Expr pattern, Expr expr, vector<Expr> &matches) {
    matches.clear();
    if (!pattern.defined() && !expr.defined()) return true;
    if (!pattern.defined() || !expr.defined()) return false;

    IRMatch eq(expr, matches);
    pattern.accept(&eq);
    if (eq.result) {
        return true;
    } else {
        matches.clear();
        return false;
    }
}

bool expr_match(Expr pattern, Expr expr, map<string, Expr> &matches) {
    // Explicitly don't clear matches. This allows usages to pre-match
    // some variables.

    if (!pattern.defined() && !expr.defined()) return true;
    if (!pattern.defined() || !expr.defined()) return false;

    IRMatch eq(expr, matches);
    pattern.accept(&eq);
    if (eq.result) {
        return true;
    } else {
        matches.clear();
        return false;
    }
}

}}
<|endoftext|>


<commit_before>/*
 *  MapView.cpp
 *  RunParticles
 *
 *  Created by Doug Letterman on 2/21/13.
 *  Copyright 2013 Doug Letterman. All rights reserved.
 *
 */

#include "MapView.h"

#include <QDebug>

#define MAX_WIDTH 1.6e+07

MapView::MapView() :
    _left(0.),
    _top(0.),
    _right(0.),
    _bottom(0.),
    _aspectRatio(1.0)
{

}

void
MapView::resize(int oldWidth, int /*oldHeight*/, int newWidth, int newHeight)
{
    _aspectRatio = (float)newWidth / (float)newHeight;
    double mapWidth = _right - _left;
    double newMapWidth = mapWidth*(float(newWidth)/float(oldWidth));
    if (newMapWidth > MAX_WIDTH)
        newMapWidth = MAX_WIDTH;
    double newMapHeight = newMapWidth / _aspectRatio;
    _right = _left+newMapWidth;
    _bottom = _top-newMapHeight;
}

void
MapView::move(const Vec2d &moveVector)
{
    _left += moveVector.x;
    _right +=moveVector.x;
    _bottom += moveVector.y;
    _top += moveVector.y;
}

void
MapView::setCurrentCam( CameraOrtho &aCurrentCam )
{
    float left, top, right, bottom, near, far;
    aCurrentCam.getFrustum(&left,
                           &top,
                           &right,
                           &bottom,
                           &near,
                           &far);
    setFrustum(left, top, right, bottom);
    _aspectRatio = (right - left) / (top - bottom);
}

void
MapView::zoom(const float size)
{
    double oldLeft, oldTop, oldRight, oldBottom;
    getFrustum(oldLeft, oldTop, oldRight, oldBottom);
    float width = fabsf(oldRight - oldLeft);
    MapPoint ctr((oldLeft+oldRight)*0.5, (oldBottom+oldTop)*0.5);
    float hsize = width * size * 0.5;
    hsize = (hsize*2. > MAX_WIDTH) ? MAX_WIDTH/2. : hsize;
    float vsize = hsize / _aspectRatio;
    setFrustum(ctr.x - hsize,
               ctr.y + vsize,
               ctr.x + hsize,
               ctr.y - vsize);
}

void
MapView::recenter(const MapPoint &position)
{
    double oldLeft, oldTop, oldRight, oldBottom;
    getFrustum(oldLeft, oldTop, oldRight, oldBottom);
    float width = (oldRight - oldLeft) * 0.5;
    float height = (oldTop - oldBottom) * 0.5;
    float newLeft = position.x - width;
    float newRight = position.x + width;
    float newTop = position.y + height;
    float newBottom = position.y - height;
    setFrustum(newLeft, newTop, newRight, newBottom);
}

void
MapView::frameBoundingBox(const BoundingBox &bbox)
{
    MapPoint center = bbox.center();
    float bboxWidth = bbox.width();
    if (bboxWidth > MAX_WIDTH)
        bboxWidth = MAX_WIDTH;
    double bboxRatio = bboxWidth / bbox.height();
    if (bboxRatio > _aspectRatio) {
        double viewheight = bbox.width() / _aspectRatio;
        setFrustum(bbox.upperLeft.x,
                   center.y + viewheight * 0.5,
                   bbox.lowerRight.x,
                   center.y - viewheight * 0.5);
    } else {
        double viewWidth = bbox.height() * _aspectRatio;
        setFrustum(center.x - viewWidth * 0.5,
                   bbox.upperLeft.y,
                   center.x + viewWidth * 0.5,
                   bbox.lowerRight.y);
    }
}

void
MapView::getFrustum(double &left, double &top, double &right, double &bottom) const
{
    left = _left;
    top = _top;
    right = _right;
    bottom = _bottom;
}

void
MapView::setFrustum(double left, double top, double right, double bottom)
{
    _left = left;
    _top = top;
    _right = right;
    _bottom = bottom;
}

<commit_msg>Do the reentering calculations in double. Lock view to layer is now smooth as silk.<commit_after>/*
 *  MapView.cpp
 *  RunParticles
 *
 *  Created by Doug Letterman on 2/21/13.
 *  Copyright 2013 Doug Letterman. All rights reserved.
 *
 */

#include "MapView.h"

#include <QDebug>

#define MAX_WIDTH 1.6e+07

MapView::MapView() :
    _left(0.),
    _top(0.),
    _right(0.),
    _bottom(0.),
    _aspectRatio(1.0)
{

}

void
MapView::resize(int oldWidth, int /*oldHeight*/, int newWidth, int newHeight)
{
    _aspectRatio = (float)newWidth / (float)newHeight;
    double mapWidth = _right - _left;
    double newMapWidth = mapWidth*(float(newWidth)/float(oldWidth));
    if (newMapWidth > MAX_WIDTH)
        newMapWidth = MAX_WIDTH;
    double newMapHeight = newMapWidth / _aspectRatio;
    _right = _left+newMapWidth;
    _bottom = _top-newMapHeight;
}

void
MapView::move(const Vec2d &moveVector)
{
    _left += moveVector.x;
    _right +=moveVector.x;
    _bottom += moveVector.y;
    _top += moveVector.y;
}

void
MapView::setCurrentCam( CameraOrtho &aCurrentCam )
{
    float left, top, right, bottom, near, far;
    aCurrentCam.getFrustum(&left,
                           &top,
                           &right,
                           &bottom,
                           &near,
                           &far);
    setFrustum(left, top, right, bottom);
    _aspectRatio = (right - left) / (top - bottom);
}

void
MapView::zoom(const float size)
{
    double oldLeft, oldTop, oldRight, oldBottom;
    getFrustum(oldLeft, oldTop, oldRight, oldBottom);
    float width = fabsf(oldRight - oldLeft);
    MapPoint ctr((oldLeft+oldRight)*0.5, (oldBottom+oldTop)*0.5);
    float hsize = width * size * 0.5;
    hsize = (hsize*2. > MAX_WIDTH) ? MAX_WIDTH/2. : hsize;
    float vsize = hsize / _aspectRatio;
    setFrustum(ctr.x - hsize,
               ctr.y + vsize,
               ctr.x + hsize,
               ctr.y - vsize);
}

void
MapView::recenter(const MapPoint &position)
{
    double width = (_right - _left) * 0.5;
    double height = (_top - _bottom) * 0.5;
    _left = position.x - width;
    _right = position.x + width;
    _top = position.y + height;
    _bottom = position.y - height;
}

void
MapView::frameBoundingBox(const BoundingBox &bbox)
{
    MapPoint center = bbox.center();
    float bboxWidth = bbox.width();
    if (bboxWidth > MAX_WIDTH)
        bboxWidth = MAX_WIDTH;
    double bboxRatio = bboxWidth / bbox.height();
    if (bboxRatio > _aspectRatio) {
        double viewheight = bbox.width() / _aspectRatio;
        setFrustum(bbox.upperLeft.x,
                   center.y + viewheight * 0.5,
                   bbox.lowerRight.x,
                   center.y - viewheight * 0.5);
    } else {
        double viewWidth = bbox.height() * _aspectRatio;
        setFrustum(center.x - viewWidth * 0.5,
                   bbox.upperLeft.y,
                   center.x + viewWidth * 0.5,
                   bbox.lowerRight.y);
    }
}

void
MapView::getFrustum(double &left, double &top, double &right, double &bottom) const
{
    left = _left;
    top = _top;
    right = _right;
    bottom = _bottom;
}

void
MapView::setFrustum(double left, double top, double right, double bottom)
{
    _left = left;
    _top = top;
    _right = right;
    _bottom = bottom;
}

<|endoftext|>


<commit_before>#include "treeface/scene/MaterialManager.h"

#include "treeface/Config.h"

#include "treeface/base/Enums.h"
#include "treeface/base/PackageManager.h"

#include "treeface/gl/Program.h"

#include "treeface/graphics/VectorGraphicsMaterial.h"

#include "treeface/misc/Errors.h"
#include "treeface/misc/PropertyValidator.h"
#include "treeface/misc/StringCast.h"

#include "treeface/scene/Material.h"
#include "treeface/scene/SceneGraphMaterial.h"
#include "treeface/scene/guts/Material_guts.h"

#include <treecore/DynamicObject.h>
#include <treecore/HashMap.h>
#include <treecore/MemoryBlock.h>
#include <treecore/JSON.h>
#include <treecore/RefCountHolder.h>
#include <treecore/Singleton.h>
#include <treecore/Variant.h>

using namespace treecore;

namespace treeface {

struct MaterialManager::Impl
{
    HashMap<Identifier, RefCountHolder<Material> > materials;
};

MaterialManager::MaterialManager()
    : m_impl( new Impl )
{}

MaterialManager::~MaterialManager()
{
    if (m_impl)
        delete m_impl;
}

#define KEY_PROGRAM      "program"
#define KEY_TYPE         "type"
#define KEY_PROJ_SHADOW  "project_shadow"
#define KEY_RECV_SHADOW  "receive_shadow"
#define KEY_TRANSLUSCENT "transluscent"
#define KEY_TEXTURE      "textures"

class MaterialPropertyValidator: public PropertyValidator
{
public:
    MaterialPropertyValidator()
    {
        add_item( KEY_PROGRAM,      PropertyValidator::ITEM_ARRAY,  true );
        add_item( KEY_TYPE,         PropertyValidator::ITEM_SCALAR, true );
        add_item( KEY_PROJ_SHADOW,  PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_RECV_SHADOW,  PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_TRANSLUSCENT, PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_TEXTURE,      PropertyValidator::ITEM_ARRAY,  false );
    }

    virtual ~MaterialPropertyValidator() {}

    juce_DeclareSingleton( MaterialPropertyValidator, false )
};
juce_ImplementSingleton( MaterialPropertyValidator )

Material * MaterialManager::build_material( const treecore::Identifier & name, const treecore::var & data )
{
    // validate data
    if ( !data.isObject() )
        throw ConfigParseError( "Material root node is not KV" );

    NamedValueSet& data_kv = data.getDynamicObject()->getProperties();
    {
        Result re = MaterialPropertyValidator::getInstance()->validate( data_kv );
        if (!re)
            throw ConfigParseError( "Invalid material JSON node: " + re.getErrorMessage() );
    }

    //
    // create material by type
    //

    // get material type
    MaterialType mat_type;
    if ( !fromString<MaterialType>( data_kv[KEY_TYPE], mat_type ) )
        throw ConfigParseError( "Invalid material type: " + data_kv[KEY_TYPE].toString() );

    // create material object by type
    Material* mat = nullptr;

    switch (mat_type)
    {
    case MATERIAL_RAW:             mat = new Material(); break;
    case MATERIAL_SCENE_GRAPH:     mat = new SceneGraphMaterial(); break;
    case MATERIAL_VECTOR_GRAPHICS: mat = new VectorGraphicsMaterial(); break;
    case MATERIAL_SCREEN_SPACE:    die( "material for screen space not implemented" ); break;
    default:
        die( "unsupported material type enum: %d", mat_type );
    }

    //
    // build program
    //

    // get shader source
    const var&  node_program  = data_kv[KEY_PROGRAM];
    Array<var>* program_names = node_program.getArray();
    if (program_names->size() != 2)
        throw ConfigParseError( "Invalid program specification: " + node_program.toString() + ".\nExpect an array of two strings specifying vertex and fragment shader name." );

    String name_vertex = (*program_names)[0].toString();
    String name_frag   = (*program_names)[1].toString();

    MemoryBlock src_vert_raw;
    MemoryBlock src_frag_raw;
    {
        if ( !PackageManager::getInstance()->get_item_data( name_vertex, src_vert_raw, true ) )
            throw ConfigParseError( "MaterialManager: no vertex shader resource named \"" + name_vertex + "\"" );

        if ( !PackageManager::getInstance()->get_item_data( name_frag, src_frag_raw, true ) )
            throw ConfigParseError( "MaterialManager: no fragment shader resource named \"" + name_frag + "\"" );
    }

    // preprocess shader source
    String src_vert = mat->get_shader_source_addition() + (const char*) src_vert_raw.getData();
    String src_frag = mat->get_shader_source_addition() + (const char*) src_frag_raw.getData();

    // create and build shader program
    mat->m_program = new Program( src_vert.toRawUTF8(), src_frag.toRawUTF8() );

    //
    // load properties
    // get scene additive uniforms
    //
    if (mat_type == MATERIAL_SCENE_GRAPH)
    {
        // scene material defined uniforms
        const Program* prog = mat->m_program;

        SceneGraphMaterial* sgmat = dynamic_cast<SceneGraphMaterial*>( mat );
        sgmat->m_uni_model_view      = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_MODEL_VIEW );
        sgmat->m_uni_proj            = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_PROJECT );
        sgmat->m_uni_model_view_proj = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_MODEL_VIEW_PROJECT );
        sgmat->m_uni_norm            = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_NORMAL );
        sgmat->m_uni_light_direct    = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_DIRECTION );
        sgmat->m_uni_light_color     = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_COLOR );
        sgmat->m_uni_light_ambient   = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_AMBIENT );

        // scene properties
        if ( data_kv.contains( KEY_PROJ_SHADOW ) )
            sgmat->m_project_shadow = bool(data_kv[KEY_PROJ_SHADOW]);
        if ( data_kv.contains( KEY_RECV_SHADOW ) )
            sgmat->m_receive_shadow = bool(data_kv[KEY_RECV_SHADOW]);
        if ( data_kv.contains( KEY_TRANSLUSCENT ) )
            sgmat->m_translucent = bool(data_kv[KEY_TRANSLUSCENT]);
    }
    else if (mat_type == MATERIAL_SCREEN_SPACE)
    {
        die( "no implemented" );
    }

    //
    // load textures
    //
    if ( data_kv.contains( KEY_TEXTURE ) )
    {
        const var& tex_list_node   = data_kv[KEY_TEXTURE];
        const Array<var>* tex_list = tex_list_node.getArray();

        for (int i_tex = 0; i_tex < tex_list->size(); i_tex++)
        {
            const var& tex_node = (*tex_list)[i_tex];

            Texture* tex_obj = new Texture( tex_node );

            // create texture layer
            // the "name" property should has been checked inside build method of Texture object
            String tex_name = tex_node.getProperty( Identifier( "name" ), var::null ).toString();
            int    uni_idx  = mat->m_program->get_uniform_location( tex_name );

            if (uni_idx >= 0)
                mat->m_impl->layers.add( { tex_name, tex_obj, uni_idx } );
            else
                warn( "program don't have texture unit named %s", tex_name.toRawUTF8() );
        }
    }

    // store material
    m_impl->materials.set( name, mat );

    return mat;
}

Material* MaterialManager::get_material( const Identifier& name )
{
    if ( m_impl->materials.contains( name ) )
        return m_impl->materials[name];

    // get item JSON
    var mat_root_node = PackageManager::getInstance()->get_item_json( name );
    if (!mat_root_node)
        throw ConfigParseError( "no material named \"" + name.toString() + "\"" );

    // build material from JSON structure
    return build_material( name, mat_root_node );
}

bool MaterialManager::material_is_cached( const treecore::Identifier& name )
{
    return m_impl->materials.contains( name );
}

bool MaterialManager::release_material_hold( const treecore::Identifier& name )
{
    if ( m_impl->materials.contains( name ) )
    {
        m_impl->materials.remove( name );
        return true;
    }
    else
    {
        return false;
    }
}

} // namespace treeface
<commit_msg>fixed a bug that VectorGraphicsMaterial don't preload fixed uniform locations<commit_after>#include "treeface/scene/MaterialManager.h"

#include "treeface/Config.h"

#include "treeface/base/Enums.h"
#include "treeface/base/PackageManager.h"

#include "treeface/gl/Program.h"

#include "treeface/graphics/VectorGraphicsMaterial.h"

#include "treeface/misc/Errors.h"
#include "treeface/misc/PropertyValidator.h"
#include "treeface/misc/StringCast.h"

#include "treeface/scene/Material.h"
#include "treeface/scene/SceneGraphMaterial.h"
#include "treeface/scene/guts/Material_guts.h"

#include <treecore/DynamicObject.h>
#include <treecore/HashMap.h>
#include <treecore/MemoryBlock.h>
#include <treecore/JSON.h>
#include <treecore/RefCountHolder.h>
#include <treecore/Singleton.h>
#include <treecore/Variant.h>

using namespace treecore;

namespace treeface {

struct MaterialManager::Impl
{
    HashMap<Identifier, RefCountHolder<Material> > materials;
};

MaterialManager::MaterialManager()
    : m_impl( new Impl )
{}

MaterialManager::~MaterialManager()
{
    if (m_impl)
        delete m_impl;
}

#define KEY_PROGRAM      "program"
#define KEY_TYPE         "type"
#define KEY_PROJ_SHADOW  "project_shadow"
#define KEY_RECV_SHADOW  "receive_shadow"
#define KEY_TRANSLUSCENT "transluscent"
#define KEY_TEXTURE      "textures"

class MaterialPropertyValidator: public PropertyValidator
{
public:
    MaterialPropertyValidator()
    {
        add_item( KEY_PROGRAM,      PropertyValidator::ITEM_ARRAY,  true );
        add_item( KEY_TYPE,         PropertyValidator::ITEM_SCALAR, true );
        add_item( KEY_PROJ_SHADOW,  PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_RECV_SHADOW,  PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_TRANSLUSCENT, PropertyValidator::ITEM_SCALAR, false );
        add_item( KEY_TEXTURE,      PropertyValidator::ITEM_ARRAY,  false );
    }

    virtual ~MaterialPropertyValidator() {}

    juce_DeclareSingleton( MaterialPropertyValidator, false )
};
juce_ImplementSingleton( MaterialPropertyValidator )

Material * MaterialManager::build_material( const treecore::Identifier & name, const treecore::var & data )
{
    // validate data
    if ( !data.isObject() )
        throw ConfigParseError( "Material root node is not KV" );

    NamedValueSet& data_kv = data.getDynamicObject()->getProperties();
    {
        Result re = MaterialPropertyValidator::getInstance()->validate( data_kv );
        if (!re)
            throw ConfigParseError( "Invalid material JSON node: " + re.getErrorMessage() );
    }

    //
    // create material by type
    //

    // get material type
    MaterialType mat_type;
    if ( !fromString<MaterialType>( data_kv[KEY_TYPE], mat_type ) )
        throw ConfigParseError( "Invalid material type: " + data_kv[KEY_TYPE].toString() );

    // create material object by type
    Material* mat = nullptr;

    switch (mat_type)
    {
    case MATERIAL_RAW:             mat = new Material(); break;
    case MATERIAL_SCENE_GRAPH:     mat = new SceneGraphMaterial(); break;
    case MATERIAL_VECTOR_GRAPHICS: mat = new VectorGraphicsMaterial(); break;
    case MATERIAL_SCREEN_SPACE:    die( "material for screen space not implemented" ); break;
    default:
        die( "unsupported material type enum: %d", mat_type );
    }

    //
    // build program
    //

    // get shader source
    const var&  node_program  = data_kv[KEY_PROGRAM];
    Array<var>* program_names = node_program.getArray();
    if (program_names->size() != 2)
        throw ConfigParseError( "Invalid program specification: " + node_program.toString() + ".\nExpect an array of two strings specifying vertex and fragment shader name." );

    String name_vertex = (*program_names)[0].toString();
    String name_frag   = (*program_names)[1].toString();

    MemoryBlock src_vert_raw;
    MemoryBlock src_frag_raw;
    {
        if ( !PackageManager::getInstance()->get_item_data( name_vertex, src_vert_raw, true ) )
            throw ConfigParseError( "MaterialManager: no vertex shader resource named \"" + name_vertex + "\"" );

        if ( !PackageManager::getInstance()->get_item_data( name_frag, src_frag_raw, true ) )
            throw ConfigParseError( "MaterialManager: no fragment shader resource named \"" + name_frag + "\"" );
    }

    // preprocess shader source
    String src_vert = mat->get_shader_source_addition() + (const char*) src_vert_raw.getData();
    String src_frag = mat->get_shader_source_addition() + (const char*) src_frag_raw.getData();

    // create and build shader program
    mat->m_program = new Program( src_vert.toRawUTF8(), src_frag.toRawUTF8() );

    //
    // load properties
    // get scene additive uniforms
    //
    SceneGraphMaterial* sgmat = dynamic_cast<SceneGraphMaterial*>(mat);
    if ( sgmat != nullptr )
    {
        // scene material defined uniforms
        const Program* prog = mat->m_program;

        sgmat->m_uni_model_view      = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_MODEL_VIEW );
        sgmat->m_uni_proj            = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_PROJECT );
        sgmat->m_uni_model_view_proj = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_MODEL_VIEW_PROJECT );
        sgmat->m_uni_norm            = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_MATRIX_NORMAL );
        sgmat->m_uni_light_direct    = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_DIRECTION );
        sgmat->m_uni_light_color     = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_COLOR );
        sgmat->m_uni_light_ambient   = prog->get_uniform_location( SceneGraphMaterial::UNIFORM_GLOBAL_LIGHT_AMBIENT );

        // scene properties
        if ( data_kv.contains( KEY_PROJ_SHADOW ) )
            sgmat->m_project_shadow = bool(data_kv[KEY_PROJ_SHADOW]);
        if ( data_kv.contains( KEY_RECV_SHADOW ) )
            sgmat->m_receive_shadow = bool(data_kv[KEY_RECV_SHADOW]);
        if ( data_kv.contains( KEY_TRANSLUSCENT ) )
            sgmat->m_translucent = bool(data_kv[KEY_TRANSLUSCENT]);
    }

    //
    // load textures
    //
    if ( data_kv.contains( KEY_TEXTURE ) )
    {
        const var& tex_list_node   = data_kv[KEY_TEXTURE];
        const Array<var>* tex_list = tex_list_node.getArray();

        for (int i_tex = 0; i_tex < tex_list->size(); i_tex++)
        {
            const var& tex_node = (*tex_list)[i_tex];

            Texture* tex_obj = new Texture( tex_node );

            // create texture layer
            // the "name" property should has been checked inside build method of Texture object
            String tex_name = tex_node.getProperty( Identifier( "name" ), var::null ).toString();
            int    uni_idx  = mat->m_program->get_uniform_location( tex_name );

            if (uni_idx >= 0)
                mat->m_impl->layers.add( { tex_name, tex_obj, uni_idx } );
            else
                warn( "program don't have texture unit named %s", tex_name.toRawUTF8() );
        }
    }

    // store material
    m_impl->materials.set( name, mat );

    return mat;
}

Material* MaterialManager::get_material( const Identifier& name )
{
    if ( m_impl->materials.contains( name ) )
        return m_impl->materials[name];

    // get item JSON
    var mat_root_node = PackageManager::getInstance()->get_item_json( name );
    if (!mat_root_node)
        throw ConfigParseError( "no material named \"" + name.toString() + "\"" );

    // build material from JSON structure
    return build_material( name, mat_root_node );
}

bool MaterialManager::material_is_cached( const treecore::Identifier& name )
{
    return m_impl->materials.contains( name );
}

bool MaterialManager::release_material_hold( const treecore::Identifier& name )
{
    if ( m_impl->materials.contains( name ) )
    {
        m_impl->materials.remove( name );
        return true;
    }
    else
    {
        return false;
    }
}

} // namespace treeface
<|endoftext|>


<commit_before>#include "catch.hpp"
#include "broker.hpp"
#include "span.h"
#include <vector>
#include <iostream>

using namespace std;
using namespace gsl;

using Payload = vector<ubyte>;

struct TestMqttSubscriber {

    void newMessage(span<ubyte> bytes) {
        messages.emplace_back(bytes.begin(), bytes.end());
        assert(messages.size() != 0);
        assert((long)messages[messages.size() - 1].size() == bytes.size());
        //debug();
    }

    void debug() {
        cout << "Messages: " << endl;
        for(const auto& msg: messages) {
            for(const auto b: msg) {
                printf("%#x, ", b);
            }
            cout << endl;
        }
    }

    vector<Payload> messages;
};

TEST_CASE("no subscriptions") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.publish("topics/foo", as_span(msg1));
        broker.publish("topics/bar", as_span(msg2));

        REQUIRE(subscriber.messages == vector<Payload>{});
    }
}

TEST_CASE("subscribe") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.subscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1});

        broker.subscribe(subscriber, {"topics/bar"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg1, msg2}));
    }
}

TEST_CASE("unsubscribe all") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.subscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1});

        broker.unsubscribe(subscriber);
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1}); //shouldn't have changed
    }
}

TEST_CASE("unsubscribe one") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};
        vector<ubyte> msg3{9, 8, 7, 6, 5};

        broker.subscribe(subscriber, vector<string>{"topics/foo", "topics/bar"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        broker.publish(ensure_z("topics/baz"), msg3);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg2}));

        broker.unsubscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        broker.publish(ensure_z("topics/baz"), msg3);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg2, msg2}));
    }
}
<commit_msg>Wildcards<commit_after>#include "catch.hpp"
#include "broker.hpp"
#include "span.h"
#include <vector>
#include <iostream>

using namespace std;
using namespace gsl;

using Payload = vector<ubyte>;

struct TestMqttSubscriber {

    void newMessage(span<ubyte> bytes) {
        messages.emplace_back(bytes.begin(), bytes.end());
        assert(messages.size() != 0);
        assert((long)messages[messages.size() - 1].size() == bytes.size());
        //debug();
    }

    void debug() {
        cout << "Messages: " << endl;
        for(const auto& msg: messages) {
            for(const auto b: msg) {
                printf("%#x, ", b);
            }
            cout << endl;
        }
    }

    vector<Payload> messages;
};

TEST_CASE("no subscriptions") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.publish("topics/foo", as_span(msg1));
        broker.publish("topics/bar", as_span(msg2));

        REQUIRE(subscriber.messages == vector<Payload>{});
    }
}

TEST_CASE("subscribe") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.subscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1});

        broker.subscribe(subscriber, {"topics/bar"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg1, msg2}));
    }
}

TEST_CASE("unsubscribe all") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};

        broker.subscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1});

        broker.unsubscribe(subscriber);
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        REQUIRE(subscriber.messages == vector<Payload>{msg1}); //shouldn't have changed
    }
}

TEST_CASE("unsubscribe one") {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};
        vector<ubyte> msg2{1, 3, 5, 7};
        vector<ubyte> msg3{9, 8, 7, 6, 5};

        broker.subscribe(subscriber, vector<string>{"topics/foo", "topics/bar"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        broker.publish(ensure_z("topics/baz"), msg3);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg2}));

        broker.unsubscribe(subscriber, {"topics/foo"});
        broker.publish(ensure_z("topics/foo"), msg1);
        broker.publish(ensure_z("topics/bar"), msg2);
        broker.publish(ensure_z("topics/baz"), msg3);
        REQUIRE(subscriber.messages == (vector<Payload>{msg1, msg2, msg2}));
    }
}

static void checkMatches(const std::string& pubTopic, const std::string& subTopic, bool matches) {
    for(const auto useCache: {false, true}) {
        MqttBroker<TestMqttSubscriber> broker{useCache};
        TestMqttSubscriber subscriber;

        vector<ubyte> msg1{2, 4, 6};

        broker.subscribe(subscriber, {subTopic});
        broker.publish(pubTopic, msg1);
        if(matches)
            REQUIRE(subscriber.messages == vector<Payload>{msg1});
        else
            REQUIRE(subscriber.messages == vector<Payload>{});
    }
}

TEST_CASE("wildcards") {
    checkMatches("foo/bar/baz", "foo/bar/baz", true);
    checkMatches("foo/bar", "foo/+", true);
    checkMatches("foo/baz", "foo/+", true);
    checkMatches("foo/bar/baz", "foo/+", false);
    checkMatches("foo/bar", "foo/#", true);
    checkMatches("foo/bar/baz", "foo/#", true);
    checkMatches("foo/bar/baz/boo", "foo/#", true);
    checkMatches("foo/bla/bar/baz/boo/bogadog", "foo/+/bar/baz/#", true);
    checkMatches("finance", "finance/#", true);
    checkMatches("finance", "finance#", false);
    checkMatches("finance", "#", true);
    checkMatches("finance/stock", "#", true);
    checkMatches("finance/stock", "finance/stock/ibm", false);
    checkMatches("topics/foo/bar", "topics/foo/#", true);
    checkMatches("topics/bar/baz/boo", "topics/foo/#", false);
}
<|endoftext|>


<commit_before>#include <unordered_map>
#include <algorithm>
#include "zs_memory.hh"
#include "vm.hh"
#include "cons.hh"
#include "delay.hh"
#include "env.hh"
#include "procedure.hh"
#include "s_closure.hh"
#include "symbol.hh"
#include "s_rules.hh"
#include "zs_error.hh"
#include "rational.hh"

#include <iostream>
#include "printer.hh"

// Assumes 'delete' is not called frequently.

using namespace std;

namespace {

enum MarkBit {
  unmarked = 0,
  marked = 1,
  fixed = 2
};

struct MarkObj {
  Ptr_tag tag;
  MarkBit mark;
};

static unordered_map<void*, MarkObj> arena;

}

void* zs_m_in(void* p, Ptr_tag tag){
  arena[p] = {tag, MarkBit::unmarked};
  return p;
}

void zs_m_out(void* p){
  auto i = arena.find(p);
  if(i != end(arena)){
    arena.erase(i);
  }
}

// GC

namespace {

bool gc_is_marked_ptr(void* p){
  auto i = arena.find(p);
  return (i == end(arena)) || i->second.mark;
}

void gc_mark_ptr(void* p){
  auto i = arena.find(p);

  if(i != end(arena) && !i->second.mark){
    i->second.mark = MarkBit::marked;
  }
}

void gc_mark_lp(Lisp_ptr p);

void gc_mark(Cons* c){
  if(gc_is_marked_ptr(c)) return;
  gc_mark_ptr(c);

  if(!c) return;
  gc_mark_lp(c->car());
  gc_mark_lp(c->cdr());
}

void gc_mark(IProcedure* iproc){
  if(gc_is_marked_ptr(iproc)) return;
  gc_mark_ptr(iproc);

  gc_mark_lp(iproc->arg_list());
  gc_mark_lp(iproc->get());
  gc_mark(iproc->closure());
  gc_mark_lp(iproc->name());
}

void gc_mark(const VM& v){
  for(auto i : v.code){
    gc_mark_lp(i);
  }

  for(auto i : v.stack){
    gc_mark_lp(i);
  }

  for(auto i : v.return_value){
    gc_mark_lp(i);
  }

  for(auto i : v.extent){
    gc_mark_lp(i.before);
    gc_mark_lp(i.thunk);
    gc_mark_lp(i.after);
  }

  gc_mark(v.frame());
}

void gc_mark(Continuation* c){
  if(gc_is_marked_ptr(c)) return;
  gc_mark_ptr(c);

  gc_mark(c->get());
}

void gc_mark(Vector* v){
  if(gc_is_marked_ptr(v)) return;
  gc_mark_ptr(v);

  for(auto i : *v){
    gc_mark_lp(i);
  }
}

void gc_mark(Delay* d){
  if(gc_is_marked_ptr(d)) return;
  gc_mark_ptr(d);

  gc_mark_lp(d->get());
  gc_mark(d->env());
}

void gc_mark(SyntacticClosure* sc){
  if(gc_is_marked_ptr(sc)) return;
  gc_mark_ptr(sc);

  gc_mark(sc->env());
  gc_mark(sc->free_names());
  gc_mark_lp(sc->expr());
}

void gc_mark(SyntaxRules* sr){
  if(gc_is_marked_ptr(sr)) return;
  gc_mark_ptr(sr);

  gc_mark(sr->env());
  gc_mark_lp(sr->literals());
  gc_mark_lp(sr->rules());
}

} // namespace

void gc_mark(Env* e){
  if(gc_is_marked_ptr(e)) return;
  gc_mark_ptr(e);

  if(!e) return;
  for(auto i : e->map_){
    gc_mark_lp(i.first);
    gc_mark_lp(i.second);
  }
  gc_mark(e->next_);
}

namespace {

void gc_mark_lp(Lisp_ptr p){
  auto tag = p.tag();

  if(gc_is_marked_ptr(p.get<void*>()))
    return;

  switch(tag){
    // included in Lisp_ptr
  case Ptr_tag::undefined:
  case Ptr_tag::boolean:
  case Ptr_tag::character:
  case Ptr_tag::integer:
  case Ptr_tag::vm_argcount:
    break;

    // no dynamic allocation
  case Ptr_tag::n_procedure:
  case Ptr_tag::vm_op:
    break;

    // not container
  case Ptr_tag::symbol:
  case Ptr_tag::rational:
  case Ptr_tag::real:
  case Ptr_tag::complex:
  case Ptr_tag::string:
  case Ptr_tag::input_port:
  case Ptr_tag::output_port:
    gc_mark_ptr(p.get<void*>());
    break;

    // container
  case Ptr_tag::cons:
    gc_mark(p.get<Cons*>());
    break;
  case Ptr_tag::i_procedure:
    gc_mark(p.get<IProcedure*>());
    break;
  case Ptr_tag::continuation:
    gc_mark(p.get<Continuation*>());
    break;
  case Ptr_tag::vector:
    gc_mark(p.get<Vector*>());
    break;
  case Ptr_tag::env:
    gc_mark(p.get<Env*>());
    break;
  case Ptr_tag::delay:
    gc_mark(p.get<Delay*>());
    break;
  case Ptr_tag::syntactic_closure:
    gc_mark(p.get<SyntacticClosure*>());
    break;
  case Ptr_tag::syntax_rules:
    gc_mark(p.get<SyntaxRules*>());
    break;

  default:
    break;
  }
}

template<Ptr_tag tag>
void gc_tagged_delete(void* p){
  typedef typename to_type<Ptr_tag, tag>::type TargetT;
  delete static_cast<TargetT>(p);
}

void gc_sweep(){
  auto i = begin(arena), e = end(arena);
  while(i != e){
    auto ii = next(i);
    switch(i->second.mark){
    case MarkBit::unmarked:
      switch(i->second.tag){
      case Ptr_tag::undefined:
        break;
      case Ptr_tag::boolean:
        break;
      case Ptr_tag::character:
        break;
      case Ptr_tag::cons:
        gc_tagged_delete<Ptr_tag::cons>(i->first);
        break;
      case Ptr_tag::symbol:
        gc_tagged_delete<Ptr_tag::symbol>(i->first);
        break;
      case Ptr_tag::i_procedure:
        gc_tagged_delete<Ptr_tag::i_procedure>(i->first);
        break;
      case Ptr_tag::n_procedure:
        break;
      case Ptr_tag::continuation:
        gc_tagged_delete<Ptr_tag::continuation>(i->first);
        break;
      case Ptr_tag::integer:
        break;
      case Ptr_tag::rational:
        gc_tagged_delete<Ptr_tag::rational>(i->first);
        break;
      case Ptr_tag::real:
        gc_tagged_delete<Ptr_tag::real>(i->first);
        break;
      case Ptr_tag::complex:
        gc_tagged_delete<Ptr_tag::complex>(i->first);
        break;
      case Ptr_tag::string:
        gc_tagged_delete<Ptr_tag::string>(i->first);
        break;
      case Ptr_tag::vector:
        gc_tagged_delete<Ptr_tag::vector>(i->first);
        break;
      case Ptr_tag::input_port:
        gc_tagged_delete<Ptr_tag::input_port>(i->first);
        break;
      case Ptr_tag::output_port:
        gc_tagged_delete<Ptr_tag::output_port>(i->first);
        break;
      case Ptr_tag::env:
        gc_tagged_delete<Ptr_tag::env>(i->first);
        break;
      case Ptr_tag::delay:
        gc_tagged_delete<Ptr_tag::delay>(i->first);
        break;
      case Ptr_tag::syntactic_closure:
        gc_tagged_delete<Ptr_tag::syntactic_closure>(i->first);
        break;
      case Ptr_tag::syntax_rules:
        gc_tagged_delete<Ptr_tag::syntax_rules>(i->first);
        break;
      case Ptr_tag::vm_op:
        break;
      case Ptr_tag::vm_argcount:
        break;
      default:
        break;
      }
      arena.erase(i);
      break;
    case MarkBit::marked:
      i->second.mark = MarkBit::unmarked;
      break;
    case MarkBit::fixed:
      break;
    default:
      break;
    }
    i = ii;
  }
}

void gc_marked_to_fixed(){
  auto i = begin(arena), e = end(arena);
  while(i != e){
    auto ii = next(i);
    switch(i->second.mark){
    case MarkBit::unmarked:
      break;
    case MarkBit::marked:
      if(i->second.tag != Ptr_tag::env){
        i->second.mark = MarkBit::fixed;
      }
      break;
    case MarkBit::fixed:
      break;
    default:
      break;
    }
    i = ii;
  }
}

} // namespace

void gc(){
  gc_mark(vm);
  gc_sweep();
}

void gc_fix_object(){
  gc_mark(vm);
  gc_marked_to_fixed();
}
<commit_msg>refactoring zs_memory<commit_after>#include <unordered_map>
#include <algorithm>
#include "zs_memory.hh"
#include "vm.hh"
#include "cons.hh"
#include "delay.hh"
#include "env.hh"
#include "procedure.hh"
#include "s_closure.hh"
#include "symbol.hh"
#include "s_rules.hh"
#include "zs_error.hh"
#include "rational.hh"

#include <iostream>
#include "printer.hh"

// Assumes 'delete' is not called frequently.

using namespace std;

namespace {

enum MarkBit {
  unmarked = 0,
  marked = 1,
  fixed = 2
};

struct MarkObj {
  Ptr_tag tag;
  MarkBit mark;
};

static unordered_map<void*, MarkObj> arena;

}

void* zs_m_in(void* p, Ptr_tag tag){
  arena[p] = {tag, MarkBit::unmarked};
  return p;
}

void zs_m_out(void* p){
  auto i = arena.find(p);
  if(i != end(arena)){
    arena.erase(i);
  }
}

// GC

namespace {

bool gc_is_marked_ptr(void* p){
  auto i = arena.find(p);
  return (i == end(arena)) || i->second.mark;
}

void gc_mark_ptr(void* p){
  auto i = arena.find(p);

  if(i != end(arena) && !i->second.mark){
    i->second.mark = MarkBit::marked;
  }
}

void gc_mark_lp(Lisp_ptr p);

void gc_mark(Cons* c){
  if(gc_is_marked_ptr(c)) return;
  gc_mark_ptr(c);

  if(!c) return;
  gc_mark_lp(c->car());
  gc_mark_lp(c->cdr());
}

void gc_mark(const VM& v){
  for(auto i : v.code){
    gc_mark_lp(i);
  }

  for(auto i : v.stack){
    gc_mark_lp(i);
  }

  for(auto i : v.return_value){
    gc_mark_lp(i);
  }

  for(auto i : v.extent){
    gc_mark_lp(i.before);
    gc_mark_lp(i.thunk);
    gc_mark_lp(i.after);
  }

  gc_mark(v.frame());
}

} // namespace

void gc_mark(Env* e){
  if(gc_is_marked_ptr(e)) return;
  gc_mark_ptr(e);

  if(!e) return;
  for(auto i : e->map_){
    gc_mark_lp(i.first);
    gc_mark_lp(i.second);
  }
  gc_mark(e->next_);
}

namespace {

void gc_mark_lp(Lisp_ptr p){
  auto tag = p.tag();

  if(gc_is_marked_ptr(p.get<void*>()))
    return;

  switch(tag){
    // included in Lisp_ptr
  case Ptr_tag::undefined:
  case Ptr_tag::boolean:
  case Ptr_tag::character:
  case Ptr_tag::integer:
  case Ptr_tag::vm_argcount:
    break;

    // no dynamic allocation
  case Ptr_tag::n_procedure:
  case Ptr_tag::vm_op:
    break;

    // not container
  case Ptr_tag::symbol:
  case Ptr_tag::rational:
  case Ptr_tag::real:
  case Ptr_tag::complex:
  case Ptr_tag::string:
  case Ptr_tag::input_port:
  case Ptr_tag::output_port:
    gc_mark_ptr(p.get<void*>());
    break;

    // container
  case Ptr_tag::cons:
    gc_mark(p.get<Cons*>());
    break;

  case Ptr_tag::i_procedure: {
    auto iproc = p.get<IProcedure*>();
    // TODO: this check should be replaced with assertion.
    // this is required now, but strange.
    if(!iproc) return;

    gc_mark_ptr(iproc);

    gc_mark_lp(iproc->arg_list());
    gc_mark_lp(iproc->get());
    gc_mark(iproc->closure());
    gc_mark_lp(iproc->name());
    break;
  }

  case Ptr_tag::continuation: {
    auto c = p.get<Continuation*>();
    gc_mark_ptr(c);

    gc_mark(c->get());
    break;
  }

  case Ptr_tag::vector: {
    auto v = p.get<Vector*>();
    // TODO: this check should be replaced with assertion.
    // this is required now, but strange.
    if(!v) return;

    gc_mark_ptr(v);

    for(auto i : *v){
      gc_mark_lp(i);
    }
    break;
  }

  case Ptr_tag::env:
    gc_mark(p.get<Env*>());
    break;

  case Ptr_tag::delay: {
    auto d = p.get<Delay*>();
    gc_mark_ptr(d);

    gc_mark_lp(d->get());
    gc_mark(d->env());
    break;
  }

  case Ptr_tag::syntactic_closure: {
    auto sc = p.get<SyntacticClosure*>();
    gc_mark_ptr(sc);

    gc_mark(sc->env());
    gc_mark(sc->free_names());
    gc_mark_lp(sc->expr());
    break;
  }

  case Ptr_tag::syntax_rules: {
    auto sr = p.get<SyntaxRules*>();
    gc_mark_ptr(sr);

    gc_mark(sr->env());
    gc_mark_lp(sr->literals());
    gc_mark_lp(sr->rules());
    break;
  }

  default:
    break;
  }
}

template<Ptr_tag tag>
void gc_tagged_delete(void* p){
  typedef typename to_type<Ptr_tag, tag>::type TargetT;
  delete static_cast<TargetT>(p);
}

void gc_sweep(){
  auto i = begin(arena), e = end(arena);
  while(i != e){
    auto ii = next(i);
    switch(i->second.mark){
    case MarkBit::unmarked:
      switch(i->second.tag){
      case Ptr_tag::undefined:
        break;
      case Ptr_tag::boolean:
        break;
      case Ptr_tag::character:
        break;
      case Ptr_tag::cons:
        gc_tagged_delete<Ptr_tag::cons>(i->first);
        break;
      case Ptr_tag::symbol:
        gc_tagged_delete<Ptr_tag::symbol>(i->first);
        break;
      case Ptr_tag::i_procedure:
        gc_tagged_delete<Ptr_tag::i_procedure>(i->first);
        break;
      case Ptr_tag::n_procedure:
        break;
      case Ptr_tag::continuation:
        gc_tagged_delete<Ptr_tag::continuation>(i->first);
        break;
      case Ptr_tag::integer:
        break;
      case Ptr_tag::rational:
        gc_tagged_delete<Ptr_tag::rational>(i->first);
        break;
      case Ptr_tag::real:
        gc_tagged_delete<Ptr_tag::real>(i->first);
        break;
      case Ptr_tag::complex:
        gc_tagged_delete<Ptr_tag::complex>(i->first);
        break;
      case Ptr_tag::string:
        gc_tagged_delete<Ptr_tag::string>(i->first);
        break;
      case Ptr_tag::vector:
        gc_tagged_delete<Ptr_tag::vector>(i->first);
        break;
      case Ptr_tag::input_port:
        gc_tagged_delete<Ptr_tag::input_port>(i->first);
        break;
      case Ptr_tag::output_port:
        gc_tagged_delete<Ptr_tag::output_port>(i->first);
        break;
      case Ptr_tag::env:
        gc_tagged_delete<Ptr_tag::env>(i->first);
        break;
      case Ptr_tag::delay:
        gc_tagged_delete<Ptr_tag::delay>(i->first);
        break;
      case Ptr_tag::syntactic_closure:
        gc_tagged_delete<Ptr_tag::syntactic_closure>(i->first);
        break;
      case Ptr_tag::syntax_rules:
        gc_tagged_delete<Ptr_tag::syntax_rules>(i->first);
        break;
      case Ptr_tag::vm_op:
        break;
      case Ptr_tag::vm_argcount:
        break;
      default:
        break;
      }
      arena.erase(i);
      break;
    case MarkBit::marked:
      i->second.mark = MarkBit::unmarked;
      break;
    case MarkBit::fixed:
      break;
    default:
      break;
    }
    i = ii;
  }
}

void gc_marked_to_fixed(){
  auto i = begin(arena), e = end(arena);
  while(i != e){
    auto ii = next(i);
    switch(i->second.mark){
    case MarkBit::unmarked:
      break;
    case MarkBit::marked:
      if(i->second.tag != Ptr_tag::env){
        i->second.mark = MarkBit::fixed;
      }
      break;
    case MarkBit::fixed:
      break;
    default:
      break;
    }
    i = ii;
  }
}

} // namespace

void gc(){
  gc_mark(vm);
  gc_sweep();
}

void gc_fix_object(){
  gc_mark(vm);
  gc_marked_to_fixed();
}
<|endoftext|>