<commit_before>/*
Copyright (C) 2011-2012 Yubico AB.  All rights reserved.

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

   1. Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

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

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

#include "yubikeyfinder.h"

YubiKeyFinder* YubiKeyFinder::_instance = 0;

// first version is inclusive, second is exclusive
const unsigned int YubiKeyFinder::FEATURE_MATRIX[][2] = {
    { YK_VERSION(2,0,0), 0 },   //Feature_MultipleConfigurations
    { YK_VERSION(2,0,0), 0 },   //Feature_ProtectConfiguration2
    { YK_VERSION(1,3,0), 0 },   //Feature_StaticPassword
    { YK_VERSION(2,0,0), 0 },   //Feature_ScanCodeMode
    { YK_VERSION(2,0,0), 0 },   //Feature_ShortTicket
    { YK_VERSION(2,0,0), 0 },   //Feature_StrongPwd
    { YK_VERSION(2,1,0), 0 },   //Feature_OathHotp
    { YK_VERSION(2,2,0), 0 },   //Feature_ChallengeResponse
    { YK_VERSION(2,1,4), 0 },   //Feature_SerialNumber
    { YK_VERSION(2,1,7), 0 },   //Feature_MovingFactor
    { YK_VERSION(2,3,0), 0 },   //Feature_ChallengeResponseFixed
    { YK_VERSION(2,3,0), 0 },   //Feature_Updatable
    { YK_VERSION(2,1,4), YK_VERSION(2,2,0)}, //Feature_Ndef
};

// when a featureset should be excluded from versions (NEO, I'm looking at you.)
const unsigned int YubiKeyFinder::FEATURE_MATRIX_EXCLUDE[][2] = {
    { YK_VERSION(2,1,4), YK_VERSION(2,2,0) }, //Feature_MultipleConfigurations
    { YK_VERSION(2,1,4), YK_VERSION(2,2,0) }, //Feature_ProtectConfiguration2
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_StaticPassword
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_ScanCodeMode
    { 0, 0 },                                 //Feature_ShortTicket
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_StrongPwd
    { 0, 0 },                                 //Feature_OathHotp
    { 0, 0 },                                 //Feature_ChallengeResponse
    { 0, 0 },                                 //Feature_SerialNumber
    { 0, 0 },                                 //Feature_MovingFactor
    { 0, 0 },                                 //Feature_ChallengeResponseFixed
    { 0, 0 },                                 //Feature_Updatable
    { 0, 0 },                                 //Feature_Ndef
};

YubiKeyFinder::YubiKeyFinder() {
    // init the ykpers library
    yk_init();

    //Initialize fields
    init();

    //Create timer
    m_timer = new QTimer( this );
    connect(m_timer, SIGNAL(timeout()),
            this, SLOT(findKey()));
}

YubiKeyFinder::~YubiKeyFinder() {
    yk_release();

    if(m_timer != 0) {
        delete m_timer;
        m_timer = 0;
    }

    if(_instance) {
        delete _instance;
    }
}

YubiKeyFinder* YubiKeyFinder::getInstance() {
    if(_instance == NULL) {
        _instance = new YubiKeyFinder();
    }
    return _instance;
}

QString YubiKeyFinder::versionStr() {
    if(m_version > 0) {
        return tr("%1.%2.%3").
                arg(m_versionMajor).
                arg(m_versionMinor).
                arg(m_versionBuild);
    }
    return "";
}

void YubiKeyFinder::reportError() {
    if (ykp_errno) {
        //qDebug("Yubikey personalization error: %s\n", ykp_strerror(ykp_errno));
        ykp_errno = 0;
    } else if (yk_errno) {
        if (yk_errno == YK_EUSBERR) {
            //qDebug("USB error: %s\n", yk_usb_strerror());
        } else {
            //qDebug("Yubikey core error: %s\n", yk_strerror(yk_errno));
        }

        yk_errno = 0;
    }

    emit errorOccurred(ERR_KEY_NOT_FOUND);
}

bool YubiKeyFinder::checkFeatureSupport(Feature feature) {
    if(m_version > 0 &&
       (unsigned int) feature < sizeof(FEATURE_MATRIX)/sizeof(FEATURE_MATRIX[0])) {
        bool supported = (
                m_version >= FEATURE_MATRIX[feature][0] &&
                (FEATURE_MATRIX[feature][1] == 0 || m_version < FEATURE_MATRIX[feature][1])
                );
        if(supported)
            if(FEATURE_MATRIX_EXCLUDE[feature][0] != 0)
                if(m_version >= FEATURE_MATRIX_EXCLUDE[feature][0])
                    if(m_version < FEATURE_MATRIX_EXCLUDE[feature][1])
                        return false;
        return supported;
    }
    return false;
}

void YubiKeyFinder::init() {
    m_state = State_Absent;
    m_yk = 0;
    m_version = 0;
    m_versionMajor = 0;
    m_versionMinor = 0;
    m_versionBuild = 0;
    m_serial = 0;
}

void YubiKeyFinder::start() {
    //Start timer
    init();
    if(m_timer && !m_timer->isActive()) {
        m_timer->start(TIMEOUT_FINDER);
    }
}

void YubiKeyFinder::stop() {
    //Stop timer
    if(m_timer && m_timer->isActive()) {
        m_timer->stop();
    }
}

bool YubiKeyFinder::openKey() {
    bool flag = true;
    if (!(m_yk = yk_open_first_key())) {
        flag = false;
    }

    return flag;
}

bool YubiKeyFinder::closeKey() {
    bool flag = true;
    if(m_yk != 0) {
        if (!yk_close_key(m_yk)) {
            flag = false;
        }
        m_yk = 0;
    }

    return flag;
}

void YubiKeyFinder::findKey() {
    YK_STATUS *ykst = ykds_alloc();
    bool error = false;

    //qDebug() << "-------------------------";
    //qDebug() << "Starting key search";
    //qDebug() << "-------------------------";

    try {
        if(m_yk == 0 && !openKey()) {
            throw 0;
        }

        if (!yk_get_status(m_yk, ykst)) {
            throw 0;
        }

        //qDebug() << "Key found";

        //Check pervious state
        if(m_state == State_Absent) {

            m_state = State_Present;

            //Get version
            m_versionMajor = ykds_version_major(ykst);
            m_versionMinor = ykds_version_minor(ykst);
            m_versionBuild = ykds_version_build(ykst);
            m_version = YK_VERSION(m_versionMajor,
                                   m_versionMinor,
                                   m_versionBuild);

            m_touchLevel = ykds_touch_level(ykst);

            //Get serial number
            if(checkFeatureSupport(Feature_SerialNumber)) {
                if (!yk_get_serial(m_yk, 0, 0, &m_serial)) {
                    qDebug() << "Failed to read serial number (serial-api-visible disabled?).";
                } else {
                    qDebug() << "Serial number: " << m_serial;
                }
            }

            //Get supported features
            size_t featuresCount = sizeof(FEATURE_MATRIX)/sizeof(FEATURE_MATRIX[0]);
            bool featuresMatrix[featuresCount];
            for(size_t i = 0; i < featuresCount; i++) {
                featuresMatrix[i] = checkFeatureSupport((Feature)i);
            }

            emit keyFound(true, featuresMatrix);
        }
    }
    catch(...) {
        error = true;
    }

    if(ykst) {
        ykds_free(ykst);
    }

    closeKey();

    if(error) {
        init();
        m_state = State_Absent;
        reportError();
        emit keyFound(false, NULL);
    }

    //qDebug() << "-------------------------";
    //qDebug() << "Stopping key search";
    //qDebug() << "-------------------------";
}
<commit_msg>features for yubikey 3.0 (neo)<commit_after>/*
Copyright (C) 2011-2012 Yubico AB.  All rights reserved.

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

   1. Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.

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

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

#include "yubikeyfinder.h"

YubiKeyFinder* YubiKeyFinder::_instance = 0;

// first version is inclusive, second is exclusive
const unsigned int YubiKeyFinder::FEATURE_MATRIX[][2] = {
    { YK_VERSION(2,0,0), 0 },   //Feature_MultipleConfigurations
    { YK_VERSION(2,0,0), 0 },   //Feature_ProtectConfiguration2
    { YK_VERSION(1,3,0), 0 },   //Feature_StaticPassword
    { YK_VERSION(2,0,0), 0 },   //Feature_ScanCodeMode
    { YK_VERSION(2,0,0), 0 },   //Feature_ShortTicket
    { YK_VERSION(2,0,0), 0 },   //Feature_StrongPwd
    { YK_VERSION(2,1,0), 0 },   //Feature_OathHotp
    { YK_VERSION(2,2,0), 0 },   //Feature_ChallengeResponse
    { YK_VERSION(2,1,4), 0 },   //Feature_SerialNumber
    { YK_VERSION(2,1,7), 0 },   //Feature_MovingFactor
    { YK_VERSION(2,3,0), 0 },   //Feature_ChallengeResponseFixed
    { YK_VERSION(2,3,0), 0 },   //Feature_Updatable
    { YK_VERSION(2,1,4), 0 },   //Feature_Ndef
};

// when a featureset should be excluded from versions (NEO, I'm looking at you.)
const unsigned int YubiKeyFinder::FEATURE_MATRIX_EXCLUDE[][2] = {
    { YK_VERSION(2,1,4), YK_VERSION(2,2,0) }, //Feature_MultipleConfigurations
    { YK_VERSION(2,1,4), YK_VERSION(2,2,0) }, //Feature_ProtectConfiguration2
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_StaticPassword
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_ScanCodeMode
    { 0, 0 },                                 //Feature_ShortTicket
    { YK_VERSION(2,1,4), YK_VERSION(2,1,8) }, //Feature_StrongPwd
    { 0, 0 },                                 //Feature_OathHotp
    { 0, 0 },                                 //Feature_ChallengeResponse
    { 0, 0 },                                 //Feature_SerialNumber
    { 0, 0 },                                 //Feature_MovingFactor
    { 0, 0 },                                 //Feature_ChallengeResponseFixed
    { 0, 0 },                                 //Feature_Updatable
    { YK_VERSION(2,2,0), YK_VERSION(3,0,0) }, //Feature_Ndef
};

YubiKeyFinder::YubiKeyFinder() {
    // init the ykpers library
    yk_init();

    //Initialize fields
    init();

    //Create timer
    m_timer = new QTimer( this );
    connect(m_timer, SIGNAL(timeout()),
            this, SLOT(findKey()));
}

YubiKeyFinder::~YubiKeyFinder() {
    yk_release();

    if(m_timer != 0) {
        delete m_timer;
        m_timer = 0;
    }

    if(_instance) {
        delete _instance;
    }
}

YubiKeyFinder* YubiKeyFinder::getInstance() {
    if(_instance == NULL) {
        _instance = new YubiKeyFinder();
    }
    return _instance;
}

QString YubiKeyFinder::versionStr() {
    if(m_version > 0) {
        return tr("%1.%2.%3").
                arg(m_versionMajor).
                arg(m_versionMinor).
                arg(m_versionBuild);
    }
    return "";
}

void YubiKeyFinder::reportError() {
    if (ykp_errno) {
        //qDebug("Yubikey personalization error: %s\n", ykp_strerror(ykp_errno));
        ykp_errno = 0;
    } else if (yk_errno) {
        if (yk_errno == YK_EUSBERR) {
            //qDebug("USB error: %s\n", yk_usb_strerror());
        } else {
            //qDebug("Yubikey core error: %s\n", yk_strerror(yk_errno));
        }

        yk_errno = 0;
    }

    emit errorOccurred(ERR_KEY_NOT_FOUND);
}

bool YubiKeyFinder::checkFeatureSupport(Feature feature) {
    if(m_version > 0 &&
       (unsigned int) feature < sizeof(FEATURE_MATRIX)/sizeof(FEATURE_MATRIX[0])) {
        bool supported = (
                m_version >= FEATURE_MATRIX[feature][0] &&
                (FEATURE_MATRIX[feature][1] == 0 || m_version < FEATURE_MATRIX[feature][1])
                );
        if(supported)
            if(FEATURE_MATRIX_EXCLUDE[feature][0] != 0)
                if(m_version >= FEATURE_MATRIX_EXCLUDE[feature][0])
                    if(m_version < FEATURE_MATRIX_EXCLUDE[feature][1])
                        return false;
        return supported;
    }
    return false;
}

void YubiKeyFinder::init() {
    m_state = State_Absent;
    m_yk = 0;
    m_version = 0;
    m_versionMajor = 0;
    m_versionMinor = 0;
    m_versionBuild = 0;
    m_serial = 0;
}

void YubiKeyFinder::start() {
    //Start timer
    init();
    if(m_timer && !m_timer->isActive()) {
        m_timer->start(TIMEOUT_FINDER);
    }
}

void YubiKeyFinder::stop() {
    //Stop timer
    if(m_timer && m_timer->isActive()) {
        m_timer->stop();
    }
}

bool YubiKeyFinder::openKey() {
    bool flag = true;
    if (!(m_yk = yk_open_first_key())) {
        flag = false;
    }

    return flag;
}

bool YubiKeyFinder::closeKey() {
    bool flag = true;
    if(m_yk != 0) {
        if (!yk_close_key(m_yk)) {
            flag = false;
        }
        m_yk = 0;
    }

    return flag;
}

void YubiKeyFinder::findKey() {
    YK_STATUS *ykst = ykds_alloc();
    bool error = false;

    //qDebug() << "-------------------------";
    //qDebug() << "Starting key search";
    //qDebug() << "-------------------------";

    try {
        if(m_yk == 0 && !openKey()) {
            throw 0;
        }

        if (!yk_get_status(m_yk, ykst)) {
            throw 0;
        }

        //qDebug() << "Key found";

        //Check pervious state
        if(m_state == State_Absent) {

            m_state = State_Present;

            //Get version
            m_versionMajor = ykds_version_major(ykst);
            m_versionMinor = ykds_version_minor(ykst);
            m_versionBuild = ykds_version_build(ykst);
            m_version = YK_VERSION(m_versionMajor,
                                   m_versionMinor,
                                   m_versionBuild);

            m_touchLevel = ykds_touch_level(ykst);

            //Get serial number
            if(checkFeatureSupport(Feature_SerialNumber)) {
                if (!yk_get_serial(m_yk, 0, 0, &m_serial)) {
                    qDebug() << "Failed to read serial number (serial-api-visible disabled?).";
                } else {
                    qDebug() << "Serial number: " << m_serial;
                }
            }

            //Get supported features
            size_t featuresCount = sizeof(FEATURE_MATRIX)/sizeof(FEATURE_MATRIX[0]);
            bool featuresMatrix[featuresCount];
            for(size_t i = 0; i < featuresCount; i++) {
                featuresMatrix[i] = checkFeatureSupport((Feature)i);
            }

            emit keyFound(true, featuresMatrix);
        }
    }
    catch(...) {
        error = true;
    }

    if(ykst) {
        ykds_free(ykst);
    }

    closeKey();

    if(error) {
        init();
        m_state = State_Absent;
        reportError();
        emit keyFound(false, NULL);
    }

    //qDebug() << "-------------------------";
    //qDebug() << "Stopping key search";
    //qDebug() << "-------------------------";
}
<|endoftext|>


<commit_before>/*
 * Lda.cpp
 *
 * Copyright (c) 2012 Tsukasa OMOTO <henry0312@gmail.com>
 *
 * 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.
*/

/* This file is available under an MIT license. */

#include "Lda.hpp"

/**
 * Constructor
 *
 * @param const int _K the number of Topics
 * @param double _alpha alpha
 * @param double _beta beta
 * @param const char *train Training set
 * @param const char *test Test set
 * @param const char *vocab Vocabulary
 * @param bool _asymmetry If true, use Asymmetry Dirichlet distribution"
 */
Lda::Lda(const int _K, double _alpha, double _beta,
        const char *train, const char *test, const char *vocab, bool _asymmetry=false)
    :dataset(train, vocab), testset(test), K(_K), alpha_z(_K, _alpha),
    beta(_beta), asymmetry(_asymmetry), gen(rd())
{
    init();
}

/**
 * Initialization
 */
void Lda::init() {
    // n_mz
    n_m_z.resize(dataset.M);
    for (auto& n_z : n_m_z) {
        n_z.resize(K, 0);
    }

    // n_zt
    n_z_t.resize(K);
    for (auto& n_t : n_z_t) {
        n_t.resize(dataset.V, 0);
    }

    // n_z
    n_z.resize(K, 0);

    /*
     * Topics
     */
    z_m_n.resize(dataset.M);
    std::uniform_int_distribution<> dis(0, K-1);
    for (int m = 0; m < dataset.M; ++m) {
        z_m_n[m].resize(dataset.n_m[m]);
        for (int n = 0; n < dataset.n_m[m]; ++n) {
            auto z = dis(gen);
            z_m_n[m][n] = z;
            ++n_m_z[m][z];
            ++n_z_t[z][dataset.docs[m][n] - 1];
            ++n_z[z];
        }
    }

    // phi
    phi_z_t.resize(K);
    for (auto& phi_t : phi_z_t) {
        phi_t.resize(dataset.V);
    }

    // theta
    theta_m_z.resize(dataset.M);
    for (auto& theta_z : theta_m_z) {
        theta_z.resize(K);
    }
}

/**
 * Inference
 */
void Lda::inference() {
    /*
     * Sampling z_mn
     */
    for (int m = 0; m < dataset.M; ++m) {
        for (int n = 0; n < dataset.n_m[m]; ++n) {
            sampling_z(m, n);
        }
    }

    /*
     * Update hyperparameters
     */
    if (asymmetry) {
        update_alpha();
    }
}

/**
 * Sampling z_mn
 *
 * Perform Gibbs sampling once
 *
 * @param const int m the mth doc
 * @param const int n the nth word
 */
void Lda::sampling_z(const int m, const int n) {
    // word
    const int t = dataset.docs[m][n];
    // old topic
    const int old_z = z_m_n[m][n];

    /*
     * Delete old topic
     */
    --n_m_z[m][old_z];
    --n_z_t[old_z][t - 1];
    --n_z[old_z];

    /*
     * Gibbs sampling
     */
    std::vector<double> p_z(K);
    for (int z = 0; z < K; ++z) {
        p_z[z] = (alpha_z[z] + n_m_z[m][z]) * (beta + n_z_t[z][t - 1]) / (n_z[z] + dataset.V * beta);
    }
    std::discrete_distribution<> dis(begin(p_z), end(p_z));
    int new_z = dis(gen);

    /*
     * Update topic
     */
    z_m_n[m][n] = new_z;
    ++n_m_z[m][new_z];
    ++n_z_t[new_z][t - 1];
    ++n_z[new_z];
}

/**
 * Compute Perplexity
 */
double Lda::perplexity() {
    /*
     * phi
     */
    for (int z = 0; z < K; ++z) {
        for (int t = 0; t < dataset.V; ++t) {
            phi_z_t[z][t] = (beta + n_z_t[z][t]) / (n_z[z] + dataset.V * beta);
        }
    }

    /*
     * theta
     */
    for (int m = 0; m < dataset.M; ++m) {
        for (int z = 0; z < K; ++z) {
            theta_m_z[m][z] = (alpha_z[z] + n_m_z[m][z]) / (dataset.n_m[m] + K * alpha_z[z]);
        }
    }

    /*
     * Perplexity
     */
    double log_per = 0.0;
    for (int m = 0; m < testset.M; ++m) {
        for (int n = 0; n < testset.n_m[m]; ++n) {
            double sum = 0.0;
            for (int z = 0; z < K; ++z) {
                sum += theta_m_z[m][z] * phi_z_t[z][testset.docs[m][n] - 1];
            }
            log_per -= log(sum);
        }
    }
    return exp( log_per / testset.N );
}

/**
 * Learning
 *
 * Perform Gibbs sampling specified number of times and Calculate perplexity with each cycle
 *
 * @param const int iteration the number of times of inference
 */
void Lda::learn(const int iteration) {
    using namespace std;

    cout.precision(3);
    cout.setf(ios::fixed);

    /*
     * Show Initial parameters
     */
    cout << "K = " << K << endl;
    if (asymmetry) {
        cout << "alpha_z = " << alpha_z[0] << endl;
    } else {
        cout << "alpha = " << alpha_z[0] << endl;
    }
    cout << "beta = " << beta << endl;

    // Start time
    auto start = std::chrono::system_clock::now();

    // Inference
    cout << "iter\tperplexity\n";
    for (int i = 0; i < iteration; ++i) {
        cout << i << "\t" << perplexity() << endl;
        inference();
    }
    cout << iteration << "\t" << perplexity() << endl;

    // End time
    auto end = std::chrono::system_clock::now();

    // Elapsed time
    auto ms = std::chrono::duration_cast< std::chrono::milliseconds >(end - start).count();
    int s = ms * 0.001; ms -= s * 1000;
    int m = s / 60; s %= 60;
    int h = m / 60; m %= 60;
    cout << "Elapsed time: " << h << "h " << m << "m " << s << "." << ms << "s\n" << endl;

    /*
     * Dump
     */
    // topic-word distribution
    dump();
    // alpha_z
    if (asymmetry) {
        for (int z = 0; z < K; ++z) {
            cout << "alpha_z[" << z << "] = " << alpha_z[z] << endl;
        }
    }
}

/**
 * Dump
 *
 * Print topic-word distribution
 */
void Lda::dump() {
    // Insert topic-word distribution to vector
    std::vector<std::vector<std::pair<int, double>>> topic_word;
    topic_word.resize(K);
    for (int z = 0; z < K; ++z) {
        topic_word[z].resize(dataset.V);
        for (int t = 0; t < dataset.V; ++t) {
            topic_word[z][t] = std::make_pair(t, phi_z_t[z][t]);
        }
    }

    // Descending sort
    for (int z = 0; z < K; ++z) {
        std::sort( begin(topic_word[z]), end(topic_word[z]),
                    [](const std::pair<int, double> &a, const std::pair<int, double> &b)
                    -> bool { return a.second > b.second; } );
    }

    // Print
    for (int z = 0; z < K; ++z) {
        printf("Topic: %d (%d words)\n", z, n_z[z]);
        for (int i = 0; i < (n_z[z] > 10 ? 10 : n_z[z]); ++i) {
            auto t = topic_word[z][i].first;
            auto phi = topic_word[z][i].second;
            printf("%s: %f (%d)\n", dataset.vocab[t].c_str(), phi, n_z_t[z][t]);
        }
        std::cout << std::endl;
    }
}

/**
 * Sampling new alpha
 */
void Lda::update_alpha() {
    using namespace boost::math;

    double sum_alpha = 0.0;
    for (auto alpha : alpha_z) {
        sum_alpha += alpha;
    }

    double numer, denom;
    for (int z = 0; z < K; ++z) {
        numer = denom = 0.0;
        for (int m = 0; m < dataset.M; ++m) {
            numer += digamma(n_m_z[m][z] + alpha_z[z]) - digamma(alpha_z[z]);
            denom += digamma(dataset.n_m[m] + sum_alpha) - digamma(sum_alpha);
        }
        alpha_z[z] = alpha_z[z] * numer / denom;
    }
}
<commit_msg>Hold words when computing perplexity in LDA.<commit_after>/*
 * Lda.cpp
 *
 * Copyright (c) 2012 Tsukasa OMOTO <henry0312@gmail.com>
 *
 * 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.
*/

/* This file is available under an MIT license. */

#include "Lda.hpp"

/**
 * Constructor
 *
 * @param const int _K the number of Topics
 * @param double _alpha alpha
 * @param double _beta beta
 * @param const char *train Training set
 * @param const char *test Test set
 * @param const char *vocab Vocabulary
 * @param bool _asymmetry If true, use Asymmetry Dirichlet distribution"
 */
Lda::Lda(const int _K, double _alpha, double _beta,
        const char *train, const char *test, const char *vocab, bool _asymmetry=false)
    :dataset(train, vocab), testset(test), K(_K), alpha_z(_K, _alpha),
    beta(_beta), asymmetry(_asymmetry), gen(rd())
{
    init();
}

/**
 * Initialization
 */
void Lda::init() {
    // n_mz
    n_m_z.resize(dataset.M);
    for (auto& n_z : n_m_z) {
        n_z.resize(K, 0);
    }

    // n_zt
    n_z_t.resize(K);
    for (auto& n_t : n_z_t) {
        n_t.resize(dataset.V, 0);
    }

    // n_z
    n_z.resize(K, 0);

    /*
     * Topics
     */
    z_m_n.resize(dataset.M);
    std::uniform_int_distribution<> dis(0, K-1);
    for (int m = 0; m < dataset.M; ++m) {
        z_m_n[m].resize(dataset.n_m[m]);
        for (int n = 0; n < dataset.n_m[m]; ++n) {
            auto z = dis(gen);
            z_m_n[m][n] = z;
            ++n_m_z[m][z];
            ++n_z_t[z][dataset.docs[m][n] - 1];
            ++n_z[z];
        }
    }

    // phi
    phi_z_t.resize(K);
    for (auto& phi_t : phi_z_t) {
        phi_t.resize(dataset.V);
    }

    // theta
    theta_m_z.resize(dataset.M);
    for (auto& theta_z : theta_m_z) {
        theta_z.resize(K);
    }
}

/**
 * Inference
 */
void Lda::inference() {
    /*
     * Sampling z_mn
     */
    for (int m = 0; m < dataset.M; ++m) {
        for (int n = 0; n < dataset.n_m[m]; ++n) {
            sampling_z(m, n);
        }
    }

    /*
     * Update hyperparameters
     */
    if (asymmetry) {
        update_alpha();
    }
}

/**
 * Sampling z_mn
 *
 * Perform Gibbs sampling once
 *
 * @param const int m the mth doc
 * @param const int n the nth word
 */
void Lda::sampling_z(const int m, const int n) {
    // word
    const int t = dataset.docs[m][n];
    // old topic
    const int old_z = z_m_n[m][n];

    /*
     * Delete old topic
     */
    --n_m_z[m][old_z];
    --n_z_t[old_z][t - 1];
    --n_z[old_z];

    /*
     * Gibbs sampling
     */
    std::vector<double> p_z(K);
    for (int z = 0; z < K; ++z) {
        p_z[z] = (alpha_z[z] + n_m_z[m][z]) * (beta + n_z_t[z][t - 1]) / (n_z[z] + dataset.V * beta);
    }
    std::discrete_distribution<> dis(begin(p_z), end(p_z));
    int new_z = dis(gen);

    /*
     * Update topic
     */
    z_m_n[m][n] = new_z;
    ++n_m_z[m][new_z];
    ++n_z_t[new_z][t - 1];
    ++n_z[new_z];
}

/**
 * Compute Perplexity
 */
double Lda::perplexity() {
    /*
     * phi
     */
    for (int z = 0; z < K; ++z) {
        for (int t = 0; t < dataset.V; ++t) {
            phi_z_t[z][t] = (beta + n_z_t[z][t]) / (n_z[z] + dataset.V * beta);
        }
    }

    /*
     * theta
     */
    for (int m = 0; m < dataset.M; ++m) {
        for (int z = 0; z < K; ++z) {
            theta_m_z[m][z] = (alpha_z[z] + n_m_z[m][z]) / (dataset.n_m[m] + K * alpha_z[z]);
        }
    }

    /*
     * Perplexity
     */
    double log_per = 0.0;
    for (int m = 0; m < testset.M; ++m) {
        for (int n = 0; n < testset.n_m[m]; ++n) {
            const int t = testset.docs[m][n] - 1;
            double sum = 0.0;
            for (int z = 0; z < K; ++z) {
                sum += theta_m_z[m][z] * phi_z_t[z][t];
            }
            log_per -= log(sum);
        }
    }
    return exp( log_per / testset.N );
}

/**
 * Learning
 *
 * Perform Gibbs sampling specified number of times and Calculate perplexity with each cycle
 *
 * @param const int iteration the number of times of inference
 */
void Lda::learn(const int iteration) {
    using namespace std;

    cout.precision(3);
    cout.setf(ios::fixed);

    /*
     * Show Initial parameters
     */
    cout << "K = " << K << endl;
    if (asymmetry) {
        cout << "alpha_z = " << alpha_z[0] << endl;
    } else {
        cout << "alpha = " << alpha_z[0] << endl;
    }
    cout << "beta = " << beta << endl;

    // Start time
    auto start = std::chrono::system_clock::now();

    // Inference
    cout << "iter\tperplexity\n";
    for (int i = 0; i < iteration; ++i) {
        cout << i << "\t" << perplexity() << endl;
        inference();
    }
    cout << iteration << "\t" << perplexity() << endl;

    // End time
    auto end = std::chrono::system_clock::now();

    // Elapsed time
    auto ms = std::chrono::duration_cast< std::chrono::milliseconds >(end - start).count();
    int s = ms * 0.001; ms -= s * 1000;
    int m = s / 60; s %= 60;
    int h = m / 60; m %= 60;
    cout << "Elapsed time: " << h << "h " << m << "m " << s << "." << ms << "s\n" << endl;

    /*
     * Dump
     */
    // topic-word distribution
    dump();
    // alpha_z
    if (asymmetry) {
        for (int z = 0; z < K; ++z) {
            cout << "alpha_z[" << z << "] = " << alpha_z[z] << endl;
        }
    }
}

/**
 * Dump
 *
 * Print topic-word distribution
 */
void Lda::dump() {
    // Insert topic-word distribution to vector
    std::vector<std::vector<std::pair<int, double>>> topic_word;
    topic_word.resize(K);
    for (int z = 0; z < K; ++z) {
        topic_word[z].resize(dataset.V);
        for (int t = 0; t < dataset.V; ++t) {
            topic_word[z][t] = std::make_pair(t, phi_z_t[z][t]);
        }
    }

    // Descending sort
    for (int z = 0; z < K; ++z) {
        std::sort( begin(topic_word[z]), end(topic_word[z]),
                    [](const std::pair<int, double> &a, const std::pair<int, double> &b)
                    -> bool { return a.second > b.second; } );
    }

    // Print
    for (int z = 0; z < K; ++z) {
        printf("Topic: %d (%d words)\n", z, n_z[z]);
        for (int i = 0; i < (n_z[z] > 10 ? 10 : n_z[z]); ++i) {
            auto t = topic_word[z][i].first;
            auto phi = topic_word[z][i].second;
            printf("%s: %f (%d)\n", dataset.vocab[t].c_str(), phi, n_z_t[z][t]);
        }
        std::cout << std::endl;
    }
}

/**
 * Sampling new alpha
 */
void Lda::update_alpha() {
    using namespace boost::math;

    double sum_alpha = 0.0;
    for (auto alpha : alpha_z) {
        sum_alpha += alpha;
    }

    double numer, denom;
    for (int z = 0; z < K; ++z) {
        numer = denom = 0.0;
        for (int m = 0; m < dataset.M; ++m) {
            numer += digamma(n_m_z[m][z] + alpha_z[z]) - digamma(alpha_z[z]);
            denom += digamma(dataset.n_m[m] + sum_alpha) - digamma(sum_alpha);
        }
        alpha_z[z] = alpha_z[z] * numer / denom;
    }
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
using namespace std;

struct node {
	int i;
	int j;
	int uc;
	double hc;
	double tc;
	node *p;
	node *c1;
	node *c2;
	node *c3;
	node *c4;

	node()
	{
		i = 1;
		j = 1;
		uc = 0;
		hc = 0;
		tc = 0;
		p = NULL;
		c1 = NULL;
		c2 = NULL;
		c3 = NULL;
		c4 = NULL;
	}
	node(int a, int b)
	{
		node();
		i = a;
		j = b;
	}
	node(const node &x)
	{
		i = x.i;
		j = x.j;
		uc = x.uc;
		hc = x.hc;
		tc = x.tc;
		p = x.p;
		c1 = x.c1;
		c2 = x.c2;
		c3 = x.c3;
		c4 = x.c4;
	}
	void operator=(const node &x)
	{
		i = x.i;
		j = x.j;
		uc = x.uc;
		hc = x.hc;
		tc = x.tc;
		p = x.p;
		c1 = x.c1;
		c2 = x.c2;
		c3 = x.c3;
		c4 = x.c4;
	}
	void print()
	{
		cout << "(" << i << ", " << j << ")" << endl;
	}
};

struct compare  {
	bool operator()(const node& l,const node& r)
	{
		return l.tc > r.tc;
	}
};

vector <node> VISITED;
priority_queue <node, vector<node>, compare> SEARCH;

void find_start(vector <vector <char> > MAZE, node &start)
{
	for (int i = 0; i < MAZE.size(); i++)
	{
		if (MAZE.at(i).at(0) == ' ')
		{
			start.i = i;
			start.j = 0;
			return;
		}
	}
	for (int j = 0; j < MAZE.at(0).size(); j++)
	{
		if (MAZE.at(0).at(j) == ' ')
		{
			start.i = 0;
			start.j = j;
			return;
		}
	}
}

void find_end(vector < vector <char> > MAZE, node &end)
{
	for (int i = 0; i < MAZE.size(); i++)
	{
		if (MAZE.at(i).at(MAZE.at(i).size() - 1) == ' ')
		{
			end.i = i;
			end.j = MAZE.at(0).size() - 1;
			return;
		}
	}
	for (int j = 0; j < MAZE.at(0).size(); j++)
	{
		if (MAZE.at(MAZE.size() - 1).at(j) == ' ')
		{
			end.i = MAZE.size();
			end.j = j;
			return;
		}
	}
}

bool already_visited(node n)
{
	for (int i = 0; i < VISITED.size(); i++)
	{
		if (VISITED.at(i).i == n.i && VISITED.at(i).j == n.j)
		{
			return true;
		}
	}
	return false;
}

double heuristic(node n, node end)
{
	int x = abs(n.i - end.i);
	int y = abs(n.j - end.j);
	return x + y;
}

void print(vector < vector <char> > MAZE, node n)
{
	node curr = n;
	stack <node> TO_PRINT;
	TO_PRINT.push(curr);
	while (curr.p != NULL)
	{
		curr = *curr.p;
		TO_PRINT.push(curr);
	}
	while (!TO_PRINT.empty())
	{
		int a = TO_PRINT.top().i;
		int b = TO_PRINT.top().j;
		MAZE.at(a).at(b) = 'x';
		TO_PRINT.pop();
	}
	for (int i = 0; i < MAZE.size(); i++)
	{
		for (int j = 0; j < MAZE.at(i).size(); j++)
		{
			cout << MAZE.at(i).at(j);
		}
		cout << endl;
	}
}

void search(vector < vector <char> > &MAZE, node start, node end)
{
	SEARCH.push(start);

	while (!SEARCH.empty())
	{
		node *curr = new node(SEARCH.top());
		if (curr->i == end.i && curr->j == end.j)
		{
			print(MAZE, SEARCH.top());
			return;
		}
		SEARCH.pop();
		VISITED.push_back(*curr);

		node *n1 = new node(curr->i + 1, curr->j);		//up
		node *n2 = new node(curr->i - 1, curr->j);		//down
		node *n3 = new node(curr->i, curr->j + 1);		//right
		node *n4 = new node(curr->i, curr->j - 1);		//left

		n1->uc = curr->uc + 1;
		n2->uc = curr->uc + 1;
		n3->uc = curr->uc + 1;
		n4->uc = curr->uc + 1;
		n1->hc = heuristic(*n1, end);
		n2->hc = heuristic(*n2, end);
		n3->hc = heuristic(*n3, end);
		n4->hc = heuristic(*n4, end);
		n1->tc = n1->uc + n1->hc;
		n2->tc = n2->uc + n2->hc;
		n3->tc = n3->uc + n3->hc;
		n4->tc = n4->uc + n4->hc;

		if (n1->i < MAZE.size())
		{
			if (MAZE.at(n1->i).at(n1->j) == ' ' && !already_visited(*n1))
			{
				curr->c1 = n1;
				n1->p = curr;
				SEARCH.push(*n1);
			}
		}
		if (n2->i > -1)
		{
			if (MAZE.at(n2->i).at(n2->j) == ' ' && !already_visited(*n2))
			{
				curr->c2 = n2;
				n2->p = curr;
				SEARCH.push(*n2);
			}
		}
		if (n3->j < MAZE.at(0).size())
		{
			if (MAZE.at(n3->i).at(n3->j) == ' ' && !already_visited(*n3))
			{
				curr->c3 = n3;
				n3->p = curr;
				SEARCH.push(*n3);
			}
		}
		if (n4->j > -1)
		{
			if (MAZE.at(n4->i).at(n4->j) == ' ' && !already_visited(*n4))
			{
				curr->c4 = n4;
				n4->p = curr;
				SEARCH.push(*n4);
			}
		}
	}
	cout << "NO SOLUTION" << endl;
}

int main(int argc, char *argv[])
{
	string filename;
	if (argc == 2)
	{
		filename = argv[2];
	}
	if (argc == 1)
	{
		cout << "Enter the file name: " << endl;
		cin >> filename;
	}

	string line;
	vector < vector <char> > MAZE;
	ifstream file(filename.c_str());
	if (file.is_open())
	{
		while (getline(file, line))
		{
			vector <char> lineData;
			stringstream lineStream(line);

			char value;
			while (lineStream >> noskipws >> value)
			{
				lineData.push_back(value);
			}
			MAZE.push_back(lineData);
		}
		file.close();
	}
	else
	{
		cout << "Unable to open file" << endl;
		return 0;
	}

	node start;
	node end;

	find_start(MAZE, start);
	find_end(MAZE, end);

	search(MAZE, start, end);

	return 0;
}
<commit_msg>fixed bug. Solver complete<commit_after>#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
using namespace std;

struct node {
	int i;
	int j;
	int uc;
	double hc;
	double tc;
	node *p;
	node *c1;
	node *c2;
	node *c3;
	node *c4;

	node()
	{
		i = 1;
		j = 1;
		uc = 0;
		hc = 0;
		tc = 0;
		p = NULL;
		c1 = NULL;
		c2 = NULL;
		c3 = NULL;
		c4 = NULL;
	}
	node(int a, int b)
	{
		node();
		i = a;
		j = b;
	}
	node(const node &x)
	{
		i = x.i;
		j = x.j;
		uc = x.uc;
		hc = x.hc;
		tc = x.tc;
		p = x.p;
		c1 = x.c1;
		c2 = x.c2;
		c3 = x.c3;
		c4 = x.c4;
	}
	void operator=(const node &x)
	{
		i = x.i;
		j = x.j;
		uc = x.uc;
		hc = x.hc;
		tc = x.tc;
		p = x.p;
		c1 = x.c1;
		c2 = x.c2;
		c3 = x.c3;
		c4 = x.c4;
	}
	void print()
	{
		cout << "(" << i << ", " << j << ")" << endl;
	}
};

struct compare  {
	bool operator()(const node& l,const node& r)
	{
		return l.tc > r.tc;
	}
};

vector <node> VISITED;
priority_queue <node, vector<node>, compare> SEARCH;

void find_start(vector <vector <char> > MAZE, node &start)
{
	for (int i = 0; i < MAZE.size(); i++)
	{
		if (MAZE.at(i).at(0) == ' ')
		{
			start.i = i;
			start.j = 0;
			return;
		}
	}
	for (int j = 0; j < MAZE.at(0).size(); j++)
	{
		if (MAZE.at(0).at(j) == ' ')
		{
			start.i = 0;
			start.j = j;
			return;
		}
	}
}

void find_end(vector < vector <char> > MAZE, node &end)
{
	for (int i = 0; i < MAZE.size(); i++)
	{
		if (MAZE.at(i).at(MAZE.at(i).size() - 1) == ' ')
		{
			end.i = i;
			end.j = MAZE.at(0).size() - 1;
			return;
		}
	}
	for (int j = 0; j < MAZE.at(0).size(); j++)
	{
		if (MAZE.at(MAZE.size() - 1).at(j) == ' ')
		{
			end.i = MAZE.size() - 1;
			end.j = j;
			return;
		}
	}
}

bool already_visited(node n)
{
	for (int i = 0; i < VISITED.size(); i++)
	{
		if (VISITED.at(i).i == n.i && VISITED.at(i).j == n.j)
		{
			return true;
		}
	}
	return false;
}

double heuristic(node n, node end)
{
	int x = abs(n.i - end.i);
	int y = abs(n.j - end.j);
	return x + y;
}

void print(vector < vector <char> > MAZE, node n)
{
	node curr = n;
	stack <node> TO_PRINT;
	TO_PRINT.push(curr);
	while (curr.p != NULL)
	{
		curr = *curr.p;
		TO_PRINT.push(curr);
	}
	while (!TO_PRINT.empty())
	{
		int a = TO_PRINT.top().i;
		int b = TO_PRINT.top().j;
		MAZE.at(a).at(b) = 'x';
		TO_PRINT.pop();
	}
	for (int i = 0; i < MAZE.size(); i++)
	{
		for (int j = 0; j < MAZE.at(i).size(); j++)
		{
			cout << MAZE.at(i).at(j);
		}
		cout << endl;
	}
}

void search(vector < vector <char> > &MAZE, node start, node end)
{
	SEARCH.push(start);

	while (!SEARCH.empty())
	{
		node *curr = new node(SEARCH.top());
		if (curr->i == end.i && curr->j == end.j)
		{
			print(MAZE, SEARCH.top());
			return;
		}
		SEARCH.pop();
		VISITED.push_back(*curr);

		node *n1 = new node(curr->i + 1, curr->j);		//up
		node *n2 = new node(curr->i - 1, curr->j);		//down
		node *n3 = new node(curr->i, curr->j + 1);		//right
		node *n4 = new node(curr->i, curr->j - 1);		//left

		n1->uc = curr->uc + 1;
		n2->uc = curr->uc + 1;
		n3->uc = curr->uc + 1;
		n4->uc = curr->uc + 1;
		n1->hc = heuristic(*n1, end);
		n2->hc = heuristic(*n2, end);
		n3->hc = heuristic(*n3, end);
		n4->hc = heuristic(*n4, end);
		n1->tc = n1->uc + n1->hc;
		n2->tc = n2->uc + n2->hc;
		n3->tc = n3->uc + n3->hc;
		n4->tc = n4->uc + n4->hc;

		if (n1->i < MAZE.size())
		{
			if (MAZE.at(n1->i).at(n1->j) == ' ' && !already_visited(*n1))
			{
				curr->c1 = n1;
				n1->p = curr;
				SEARCH.push(*n1);
			}
		}
		if (n2->i > -1)
		{
			if (MAZE.at(n2->i).at(n2->j) == ' ' && !already_visited(*n2))
			{
				curr->c2 = n2;
				n2->p = curr;
				SEARCH.push(*n2);
			}
		}
		if (n3->j < MAZE.at(0).size())
		{
			if (MAZE.at(n3->i).at(n3->j) == ' ' && !already_visited(*n3))
			{
				curr->c3 = n3;
				n3->p = curr;
				SEARCH.push(*n3);
			}
		}
		if (n4->j > -1)
		{
			if (MAZE.at(n4->i).at(n4->j) == ' ' && !already_visited(*n4))
			{
				curr->c4 = n4;
				n4->p = curr;
				SEARCH.push(*n4);
			}
		}
	}
	cout << "NO SOLUTION" << endl;
	for (int i = 0; i < VISITED.size(); i++)
	{
		int a = VISITED.at(i).i;
		int b = VISITED.at(i).j;
		MAZE.at(a).at(b) = 'x';
	}
	for (int i = 0; i < MAZE.size(); i++)
	{
		for (int j = 0; j < MAZE.at(i).size(); j++)
		{
			cout << MAZE.at(i).at(j);
		}
		cout << endl;
	}
}

int main(int argc, char *argv[])
{
	string filename;
	if (argc == 2)
	{
		filename = argv[2];
	}
	if (argc == 1)
	{
		cout << "Enter the file name: " << endl;
		cin >> filename;
	}

	string line;
	vector < vector <char> > MAZE;
	ifstream file(filename.c_str());
	if (file.is_open())
	{
		while (getline(file, line))
		{
			vector <char> lineData;
			stringstream lineStream(line);

			char value;
			while (lineStream >> noskipws >> value)
			{
				lineData.push_back(value);
			}
			MAZE.push_back(lineData);
		}
		file.close();
	}
	else
	{
		cout << "Unable to open file" << endl;
		return 0;
	}

	node start;
	node end;

	find_start(MAZE, start);
	find_end(MAZE, end);

	search(MAZE, start, end);

	return 0;
}
<|endoftext|>


<commit_before>/**************************************************************************
 *
 * Copyright 2011 Zack Rusin
 * 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.
 *
 **************************************************************************/

/*
 * Snappy file format.
 * -------------------
 *
 * Snappy at its core is just a compressoin algorithm so we're
 * creating a new file format which uses snappy compression
 * to hold the trace data.
 *
 * The file is composed of a number of chunks, they are:
 * chunk {
 *     uint32 - specifying the length of the compressed data
 *     compressed data, in little endian
 * }
 * File can contain any number of such chunks.
 * The default size of an uncompressed chunk is specified in
 * SNAPPY_CHUNK_SIZE.
 *
 * Note:
 * Currently the default size for a a to-be-compressed data is
 * 1mb, meaning that the compressed data will be <= 1mb.
 * The reason it's 1mb is because it seems
 * to offer a pretty good compression/disk io speed ratio
 * but that might change.
 *
 */

#include <snappy.h>

#include <iostream>
#include <algorithm>

#include <assert.h>
#include <string.h>

#include "trace_file.hpp"
#include "trace_snappy.hpp"

#define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)

using namespace trace;

class SnappyFile : public File {
public:
    SnappyFile(const std::string &filename = std::string());
    virtual ~SnappyFile();

    virtual bool supportsOffsets() const override;
    virtual File::Offset currentOffset() override;
    virtual void setCurrentOffset(const File::Offset &offset) override;
protected:
    virtual bool rawOpen(const char *filename) override;
    virtual size_t rawRead(void *buffer, size_t length) override;
    virtual int rawGetc() override;
    virtual void rawClose() override;
    virtual bool rawSkip(size_t length) override;
    virtual int rawPercentRead() override;

private:
    inline size_t usedCacheSize() const
    {
        assert(m_cachePtr >= m_cache);
        return m_cachePtr - m_cache;
    }
    inline size_t freeCacheSize() const
    {
        assert(m_cacheSize >= usedCacheSize());
        if (m_cacheSize > 0) {
            return m_cacheSize - usedCacheSize();
        } else {
            return 0;
        }
    }
    inline bool endOfData() const
    {
        return m_stream.eof() && freeCacheSize() == 0;
    }
    void flushWriteCache();
    void flushReadCache(size_t skipLength = 0);
    void createCache(size_t size);
    size_t readCompressedLength();
private:
    std::ifstream m_stream;
    size_t m_cacheMaxSize;
    size_t m_cacheSize;
    char *m_cache;
    char *m_cachePtr;

    char *m_compressedCache;

    File::Offset m_currentOffset;
    std::streampos m_endPos;
};

SnappyFile::SnappyFile(const std::string &filename)
    : File(),
      m_cacheMaxSize(SNAPPY_CHUNK_SIZE),
      m_cacheSize(m_cacheMaxSize),
      m_cache(new char [m_cacheMaxSize]),
      m_cachePtr(m_cache)
{
    size_t maxCompressedLength =
        snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
    m_compressedCache = new char[maxCompressedLength];
}

SnappyFile::~SnappyFile()
{
    close();
    delete [] m_compressedCache;
    delete [] m_cache;
}

bool SnappyFile::rawOpen(const char *filename)
{
    std::ios_base::openmode fmode = std::fstream::binary
                                  | std::fstream::in;

    m_stream.open(filename, fmode);

    //read in the initial buffer if we're reading
    if (m_stream.is_open()) {
        m_stream.seekg(0, std::ios::end);
        m_endPos = m_stream.tellg();
        m_stream.seekg(0, std::ios::beg);

        // read the snappy file identifier
        unsigned char byte1, byte2;
        m_stream >> byte1;
        m_stream >> byte2;
        assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);

        flushReadCache();
    }
    return m_stream.is_open();
}

size_t SnappyFile::rawRead(void *buffer, size_t length)
{
    if (endOfData()) {
        return 0;
    }

    if (freeCacheSize() >= length) {
        memcpy(buffer, m_cachePtr, length);
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        size_t offset = 0;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            offset = length - sizeToRead;
            memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0) {
                flushReadCache();
            }
            if (!m_cacheSize) {
                return length - sizeToRead;
            }
        }
    }

    return length;
}

int SnappyFile::rawGetc()
{
    unsigned char c = 0;
    if (rawRead(&c, 1) != 1)
        return -1;
    return c;
}

void SnappyFile::rawClose()
{
    m_stream.close();
    delete [] m_cache;
    m_cache = NULL;
    m_cachePtr = NULL;
}

void SnappyFile::flushReadCache(size_t skipLength)
{
    //assert(m_cachePtr == m_cache + m_cacheSize);
    m_currentOffset.chunk = m_stream.tellg();
    size_t compressedLength;
    compressedLength = readCompressedLength();
    if (!compressedLength) {
        // Reached end of file
        createCache(0);
        return;
    }

    m_stream.read((char*)m_compressedCache, compressedLength);
    if (m_stream.fail()) {
        // XXX: Unforunately Snappy's interface is not expressive enough
        // to allow recovering part of the uncompressed bytes.
        std::cerr << "warning: unexpected end of file while reading trace\n";
        createCache(0);
        return;
    }
    ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                    &m_cacheSize);
    createCache(m_cacheSize);
    if (skipLength < m_cacheSize) {
        ::snappy::RawUncompress(m_compressedCache, compressedLength,
                                m_cache);
    }
}

void SnappyFile::createCache(size_t size)
{
    if (size > m_cacheMaxSize) {
        do {
            m_cacheMaxSize <<= 1;
        } while (size > m_cacheMaxSize);

        delete [] m_cache;
        m_cache = new char[size];
        m_cacheMaxSize = size;
    }

    m_cachePtr = m_cache;
    m_cacheSize = size;
}

size_t SnappyFile::readCompressedLength()
{
    unsigned char buf[4];
    size_t length;
    m_stream.read((char *)buf, sizeof buf);
    if (m_stream.fail()) {
        length = 0;
    } else {
        length  =  (size_t)buf[0];
        length |= ((size_t)buf[1] <<  8);
        length |= ((size_t)buf[2] << 16);
        length |= ((size_t)buf[3] << 24);
    }
    return length;
}

bool SnappyFile::supportsOffsets() const
{
    return true;
}

File::Offset SnappyFile::currentOffset()
{
    m_currentOffset.offsetInChunk = m_cachePtr - m_cache;
    return m_currentOffset;
}

void SnappyFile::setCurrentOffset(const File::Offset &offset)
{
    // to remove eof bit
    m_stream.clear();
    // seek to the start of a chunk
    m_stream.seekg(offset.chunk, std::ios::beg);
    // load the chunk
    flushReadCache();
    assert(m_cacheSize >= offset.offsetInChunk);
    // seek within our cache to the correct location within the chunk
    m_cachePtr = m_cache + offset.offsetInChunk;

}

bool SnappyFile::rawSkip(size_t length)
{
    if (endOfData()) {
        return false;
    }

    if (freeCacheSize() >= length) {
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0) {
                flushReadCache(sizeToRead);
            }
            if (!m_cacheSize) {
                break;
            }
        }
    }

    return true;
}

int SnappyFile::rawPercentRead()
{
    return int(100 * (double(m_stream.tellg()) / double(m_endPos)));
}

File* File::createSnappy(void) {
    return new SnappyFile;
}
<commit_msg>common: Read incomplete Snappy compressed blocks.<commit_after>/**************************************************************************
 *
 * Copyright 2011 Zack Rusin
 * 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.
 *
 **************************************************************************/

/*
 * Snappy file format.
 * -------------------
 *
 * Snappy at its core is just a compressoin algorithm so we're
 * creating a new file format which uses snappy compression
 * to hold the trace data.
 *
 * The file is composed of a number of chunks, they are:
 * chunk {
 *     uint32 - specifying the length of the compressed data
 *     compressed data, in little endian
 * }
 * File can contain any number of such chunks.
 * The default size of an uncompressed chunk is specified in
 * SNAPPY_CHUNK_SIZE.
 *
 * Note:
 * Currently the default size for a a to-be-compressed data is
 * 1mb, meaning that the compressed data will be <= 1mb.
 * The reason it's 1mb is because it seems
 * to offer a pretty good compression/disk io speed ratio
 * but that might change.
 *
 */

#include <snappy.h>
#include <snappy-sinksource.h>

#include <iostream>
#include <algorithm>

#include <assert.h>
#include <string.h>

#include "trace_file.hpp"
#include "trace_snappy.hpp"

#define SNAPPY_CHUNK_SIZE (1 * 1024 * 1024)

using namespace trace;

class SnappyFile : public File {
public:
    SnappyFile(const std::string &filename = std::string());
    virtual ~SnappyFile();

    virtual bool supportsOffsets() const override;
    virtual File::Offset currentOffset() override;
    virtual void setCurrentOffset(const File::Offset &offset) override;
protected:
    virtual bool rawOpen(const char *filename) override;
    virtual size_t rawRead(void *buffer, size_t length) override;
    virtual int rawGetc() override;
    virtual void rawClose() override;
    virtual bool rawSkip(size_t length) override;
    virtual int rawPercentRead() override;

private:
    inline size_t usedCacheSize() const
    {
        assert(m_cachePtr >= m_cache);
        return m_cachePtr - m_cache;
    }
    inline size_t freeCacheSize() const
    {
        assert(m_cacheSize >= usedCacheSize());
        if (m_cacheSize > 0) {
            return m_cacheSize - usedCacheSize();
        } else {
            return 0;
        }
    }
    inline bool endOfData() const
    {
        return m_stream.eof() && freeCacheSize() == 0;
    }
    void flushWriteCache();
    void flushReadCache(size_t skipLength = 0);
    void createCache(size_t size);
    size_t readCompressedLength();
private:
    std::ifstream m_stream;
    size_t m_cacheMaxSize;
    size_t m_cacheSize;
    char *m_cache;
    char *m_cachePtr;

    char *m_compressedCache;

    File::Offset m_currentOffset;
    std::streampos m_endPos;
};

SnappyFile::SnappyFile(const std::string &filename)
    : File(),
      m_cacheMaxSize(SNAPPY_CHUNK_SIZE),
      m_cacheSize(m_cacheMaxSize),
      m_cache(new char [m_cacheMaxSize]),
      m_cachePtr(m_cache)
{
    size_t maxCompressedLength =
        snappy::MaxCompressedLength(SNAPPY_CHUNK_SIZE);
    m_compressedCache = new char[maxCompressedLength];
}

SnappyFile::~SnappyFile()
{
    close();
    delete [] m_compressedCache;
    delete [] m_cache;
}

bool SnappyFile::rawOpen(const char *filename)
{
    std::ios_base::openmode fmode = std::fstream::binary
                                  | std::fstream::in;

    m_stream.open(filename, fmode);

    //read in the initial buffer if we're reading
    if (m_stream.is_open()) {
        m_stream.seekg(0, std::ios::end);
        m_endPos = m_stream.tellg();
        m_stream.seekg(0, std::ios::beg);

        // read the snappy file identifier
        unsigned char byte1, byte2;
        m_stream >> byte1;
        m_stream >> byte2;
        assert(byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);

        flushReadCache();
    }
    return m_stream.is_open();
}

size_t SnappyFile::rawRead(void *buffer, size_t length)
{
    if (endOfData()) {
        return 0;
    }

    if (freeCacheSize() >= length) {
        memcpy(buffer, m_cachePtr, length);
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        size_t offset = 0;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            offset = length - sizeToRead;
            memcpy((char*)buffer + offset, m_cachePtr, chunkSize);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0) {
                flushReadCache();
            }
            if (!m_cacheSize) {
                return length - sizeToRead;
            }
        }
    }

    return length;
}

int SnappyFile::rawGetc()
{
    unsigned char c = 0;
    if (rawRead(&c, 1) != 1)
        return -1;
    return c;
}

void SnappyFile::rawClose()
{
    m_stream.close();
    delete [] m_cache;
    m_cache = NULL;
    m_cachePtr = NULL;
}

void SnappyFile::flushReadCache(size_t skipLength)
{
    //assert(m_cachePtr == m_cache + m_cacheSize);
    m_currentOffset.chunk = m_stream.tellg();
    size_t compressedLength;
    compressedLength = readCompressedLength();
    if (!compressedLength) {
        // Reached end of file
        createCache(0);
        return;
    }

    m_stream.read((char*)m_compressedCache, compressedLength);
    if (m_stream.fail()) {
        std::cerr << "warning: unexpected end of file while reading trace\n";

        compressedLength = m_stream.gcount();
        if (!snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                           &m_cacheSize)) {
            createCache(0);
            return;
        }

        createCache(m_cacheSize);
        snappy::ByteArraySource source(m_compressedCache, compressedLength);

        snappy::UncheckedByteArraySink sink(m_cache);
        m_cacheSize = snappy::UncompressAsMuchAsPossible(&source, &sink);

        return;
    }

    if (!snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                       &m_cacheSize)) {
        createCache(0);
        return;
    }

    createCache(m_cacheSize);
    if (skipLength < m_cacheSize) {
        snappy::RawUncompress(m_compressedCache, compressedLength,
                              m_cache);
    }
}

void SnappyFile::createCache(size_t size)
{
    if (size > m_cacheMaxSize) {
        do {
            m_cacheMaxSize <<= 1;
        } while (size > m_cacheMaxSize);

        delete [] m_cache;
        m_cache = new char[size];
        m_cacheMaxSize = size;
    }

    m_cachePtr = m_cache;
    m_cacheSize = size;
}

size_t SnappyFile::readCompressedLength()
{
    unsigned char buf[4];
    size_t length;
    m_stream.read((char *)buf, sizeof buf);
    if (m_stream.fail()) {
        length = 0;
    } else {
        length  =  (size_t)buf[0];
        length |= ((size_t)buf[1] <<  8);
        length |= ((size_t)buf[2] << 16);
        length |= ((size_t)buf[3] << 24);
    }
    return length;
}

bool SnappyFile::supportsOffsets() const
{
    return true;
}

File::Offset SnappyFile::currentOffset()
{
    m_currentOffset.offsetInChunk = m_cachePtr - m_cache;
    return m_currentOffset;
}

void SnappyFile::setCurrentOffset(const File::Offset &offset)
{
    // to remove eof bit
    m_stream.clear();
    // seek to the start of a chunk
    m_stream.seekg(offset.chunk, std::ios::beg);
    // load the chunk
    flushReadCache();
    assert(m_cacheSize >= offset.offsetInChunk);
    // seek within our cache to the correct location within the chunk
    m_cachePtr = m_cache + offset.offsetInChunk;

}

bool SnappyFile::rawSkip(size_t length)
{
    if (endOfData()) {
        return false;
    }

    if (freeCacheSize() >= length) {
        m_cachePtr += length;
    } else {
        size_t sizeToRead = length;
        while (sizeToRead) {
            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
            m_cachePtr += chunkSize;
            sizeToRead -= chunkSize;
            if (sizeToRead > 0) {
                flushReadCache(sizeToRead);
            }
            if (!m_cacheSize) {
                break;
            }
        }
    }

    return true;
}

int SnappyFile::rawPercentRead()
{
    return int(100 * (double(m_stream.tellg()) / double(m_endPos)));
}

File* File::createSnappy(void) {
    return new SnappyFile;
}
<|endoftext|>


<commit_before>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sky/viewer/content_handler_impl.h"

#include "base/bind.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/utility/run_loop.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "sky/viewer/document_view.h"

namespace sky {

class SkyApplication : public mojo::Application {
 public:
  SkyApplication(mojo::InterfaceRequest<mojo::Application> application,
                 mojo::URLResponsePtr response)
      : url_(response->url),
        binding_(this, application.Pass()),
        initial_response_(response.Pass()) {}

  void Initialize(mojo::ShellPtr shell,
                  mojo::Array<mojo::String> args,
                  const mojo::String& url) override {
    shell_ = shell.Pass();
    mojo::ServiceProviderPtr service_provider;
    shell_->ConnectToApplication("mojo:network_service",
                                 mojo::GetProxy(&service_provider), nullptr);
    mojo::ConnectToService(service_provider.get(), &network_service_);
  }

  void AcceptConnection(const mojo::String& requestor_url,
                        mojo::InterfaceRequest<mojo::ServiceProvider> services,
                        mojo::ServiceProviderPtr exposed_services) override {
    if (initial_response_) {
      OnResponseReceived(mojo::URLLoaderPtr(), services.Pass(),
                         exposed_services.Pass(), initial_response_.Pass());
    } else {
      mojo::URLLoaderPtr loader;
      network_service_->CreateURLLoader(mojo::GetProxy(&loader));
      mojo::URLRequestPtr request(mojo::URLRequest::New());
      request->url = url_;
      request->auto_follow_redirects = true;

      // |loader| will be pass to the OnResponseReceived method through a
      // callback. Because order of evaluation is undefined, a reference to the
      // raw pointer is needed.
      mojo::URLLoader* raw_loader = loader.get();
      raw_loader->Start(
          request.Pass(),
          base::Bind(&SkyApplication::OnResponseReceived,
                     base::Unretained(this), base::Passed(&loader),
                     base::Passed(&services), base::Passed(&exposed_services)));
    }
  }

  void RequestQuit() override {
    mojo::RunLoop::current()->Quit();
  }

 private:
  void OnResponseReceived(
      mojo::URLLoaderPtr loader,
      mojo::InterfaceRequest<mojo::ServiceProvider> services,
      mojo::ServiceProviderPtr exposed_services,
      mojo::URLResponsePtr response) {
    new DocumentView(services.Pass(), exposed_services.Pass(), response.Pass(),
                     shell_.get());
  }

  mojo::String url_;
  mojo::StrongBinding<mojo::Application> binding_;
  mojo::ShellPtr shell_;
  mojo::NetworkServicePtr network_service_;
  mojo::URLResponsePtr initial_response_;
};

ContentHandlerImpl::ContentHandlerImpl(
    mojo::InterfaceRequest<mojo::ContentHandler> request)
    : binding_(this, request.Pass()) {
}

ContentHandlerImpl::~ContentHandlerImpl() {
}

void ContentHandlerImpl::StartApplication(
    mojo::InterfaceRequest<mojo::Application> application,
    mojo::URLResponsePtr response) {
  new SkyApplication(application.Pass(), response.Pass());
}

}  // namespace sky
<commit_msg>Simple multi-url support for mojo apps.<commit_after>// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sky/viewer/content_handler_impl.h"

#include "base/bind.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/cpp/utility/run_loop.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "sky/viewer/document_view.h"

namespace sky {

class SkyApplication : public mojo::Application {
 public:
  SkyApplication(mojo::InterfaceRequest<mojo::Application> application,
                 mojo::URLResponsePtr response)
      : binding_(this, application.Pass()),
        initial_response_(response.Pass()) {}

  void Initialize(mojo::ShellPtr shell,
                  mojo::Array<mojo::String> args,
                  const mojo::String& url) override {
    shell_ = shell.Pass();
    mojo::ServiceProviderPtr service_provider;
    shell_->ConnectToApplication("mojo:network_service",
                                 mojo::GetProxy(&service_provider), nullptr);
    mojo::ConnectToService(service_provider.get(), &network_service_);
  }

  void AcceptConnection(const mojo::String& requestor_url,
                        mojo::InterfaceRequest<mojo::ServiceProvider> services,
                        mojo::ServiceProviderPtr exposed_services,
                        const mojo::String& url) override {
    if (initial_response_) {
      OnResponseReceived(mojo::URLLoaderPtr(), services.Pass(),
                         exposed_services.Pass(), initial_response_.Pass());
    } else {
      mojo::URLLoaderPtr loader;
      network_service_->CreateURLLoader(mojo::GetProxy(&loader));
      mojo::URLRequestPtr request(mojo::URLRequest::New());
      request->url = url;
      request->auto_follow_redirects = true;

      // |loader| will be pass to the OnResponseReceived method through a
      // callback. Because order of evaluation is undefined, a reference to the
      // raw pointer is needed.
      mojo::URLLoader* raw_loader = loader.get();
      raw_loader->Start(
          request.Pass(),
          base::Bind(&SkyApplication::OnResponseReceived,
                     base::Unretained(this), base::Passed(&loader),
                     base::Passed(&services), base::Passed(&exposed_services)));
    }
  }

  void RequestQuit() override {
    mojo::RunLoop::current()->Quit();
  }

 private:
  void OnResponseReceived(
      mojo::URLLoaderPtr loader,
      mojo::InterfaceRequest<mojo::ServiceProvider> services,
      mojo::ServiceProviderPtr exposed_services,
      mojo::URLResponsePtr response) {
    new DocumentView(services.Pass(), exposed_services.Pass(), response.Pass(),
                     shell_.get());
  }

  mojo::StrongBinding<mojo::Application> binding_;
  mojo::ShellPtr shell_;
  mojo::NetworkServicePtr network_service_;
  mojo::URLResponsePtr initial_response_;
};

ContentHandlerImpl::ContentHandlerImpl(
    mojo::InterfaceRequest<mojo::ContentHandler> request)
    : binding_(this, request.Pass()) {
}

ContentHandlerImpl::~ContentHandlerImpl() {
}

void ContentHandlerImpl::StartApplication(
    mojo::InterfaceRequest<mojo::Application> application,
    mojo::URLResponsePtr response) {
  new SkyApplication(application.Pass(), response.Pass());
}

}  // namespace sky
<|endoftext|>


<commit_before>#include <GarrysMod/Lua/Interface.h>
#include <GarrysMod/Lua/Helpers.hpp>
#include <GarrysMod/Interfaces.hpp>

#include <scanning/symbolfinder.hpp>
#include <detouring/classproxy.hpp>

#include <cstdint>
#include <string>
#include <unordered_set>
#include <algorithm>
#include <cctype>

#include <interface.h>
#include <filesystem.h>
#include <eiface.h>

class GModDataPack;

namespace Bootil
{

class Buffer
{
public:
	void **vtable;
	void *data;
	uint32_t size;
	uint32_t pos;
	uint32_t written;
};

class AutoBuffer : public Buffer
{ };

}

struct LuaFile // 116 bytes
{
	void **vtable;
	std::string path;
	std::string parent;
	std::string content;
	Bootil::AutoBuffer buffer;
	bool unknown1;
	bool unknown2;
};

namespace luapack
{

#if defined _WIN32

static const char FileSystemFactory_dedicated_sym[] = "\x55\x8B\xEC\x68\x2A\x2A\x2A\x2A\xFF\x75\x08\xE8\x2A\x2A\x2A\x2A\x83\xC4\x08";
static const size_t FileSystemFactory_dedicated_symlen = sizeof( FileSystemFactory_dedicated_sym ) - 1;

static const char AddOrUpdateFile_sym[] = "\x55\x8B\xEC\x83\xEC\x18\x53\x56\x57\x8B\x7D\x08\x8B\xD9\x83\x7F\x18\x10";
static const size_t AddOrUpdateFile_symlen = sizeof( AddOrUpdateFile_sym ) - 1;

#elif defined __linux || defined __APPLE__

static const char FileSystemFactory_dedicated_sym[] = "@_Z17FileSystemFactoryPKcPi";
static const size_t FileSystemFactory_dedicated_symlen = 0;

static const char AddOrUpdateFile_sym[] = "@_ZN12GModDataPack15AddOrUpdateFileEP7LuaFileb";
static const size_t AddOrUpdateFile_symlen = 0;

#endif

static const std::string filesystem_dedicated_lib = Helpers::GetBinaryFileName( "dedicated", false );
static const std::string filesystem_lib = Helpers::GetBinaryFileName( "filesystem_stdio", false, false );
static IFileSystem *filesystem = nullptr;

static const std::string server_lib = Helpers::GetBinaryFileName( "server", false, true, "garrysmod/bin/" );

static const char FileSystemFactory_sym[] = "@CreateInterface";
static const size_t FileSystemFactory_symlen = 0;

static std::unordered_set<std::string> whitelist_extensions = { "lua", "txt", "dat" };
static std::unordered_set<std::string> whitelist_pathid = { "lsv", "lua", "data" };

class GModDataPackProxy : public Detouring::ClassProxy<GModDataPack, GModDataPackProxy>
{
private:

#if defined _WIN32

	typedef void( __thiscall *AddOrUpdateFile_t ) ( GModDataPack *self, LuaFile *file, bool force );

#elif defined __linux || defined __APPLE__

	typedef void( *AddOrUpdateFile_t ) ( GModDataPack *self, LuaFile *file, bool force );

#endif

	static AddOrUpdateFile_t AddOrUpdateFile_original;
	static const char hook_name[];
	static GarrysMod::Lua::ILuaBase *lua;

public:
	static void Initialize( GarrysMod::Lua::ILuaBase *LUA )
	{
		lua = LUA;

		SymbolFinder symfinder;

		AddOrUpdateFile_original = reinterpret_cast<AddOrUpdateFile_t>(
			symfinder.ResolveOnBinary( server_lib.c_str( ), AddOrUpdateFile_sym, AddOrUpdateFile_symlen )
		);
		if( AddOrUpdateFile_original == nullptr )
			LUA->ThrowError( "failed to find GModDataPack::AddOrUpdateFile" );

		if( !Hook( AddOrUpdateFile_original, &GModDataPackProxy::AddOrUpdateFile ) )
			LUA->ThrowError( "failed to hook GModDataPack::AddOrUpdateFile" );
	}

	static void Deinitialize( GarrysMod::Lua::ILuaBase *LUA )
	{
		UnHook( AddOrUpdateFile_original );
	}

	void AddOrUpdateFile( LuaFile *file, bool reload )
	{
		LuaHelpers::PushHookRun( lua, hook_name );

		lua->PushString( file->path.c_str( ) );
		lua->PushBool( reload );

		bool shouldcall = true;
		if( LuaHelpers::CallHookRun( lua, 2, 1 ) )
			shouldcall = !lua->IsType( -1, GarrysMod::Lua::Type::BOOL ) || lua->GetBool( -1 );

		lua->Pop( 1 );

		if( shouldcall )
			return Call( AddOrUpdateFile_original, file, reload );
	}

	static GModDataPackProxy Singleton;
};

GModDataPackProxy::AddOrUpdateFile_t GModDataPackProxy::AddOrUpdateFile_original = nullptr;
const char GModDataPackProxy::hook_name[] = "AddOrUpdateCSLuaFile";
GarrysMod::Lua::ILuaBase *GModDataPackProxy::lua = nullptr;

static bool IsPathAllowed( std::string &filename )
{
	if( !V_RemoveDotSlashes( &filename[0], CORRECT_PATH_SEPARATOR, true ) )
		return false;

	filename.resize( std::strlen( filename.c_str( ) ) );

	const char *extension = V_GetFileExtension( filename.c_str( ) );
	if( extension == nullptr )
		return false;

	std::string ext = extension;
	std::transform( ext.begin( ), ext.end( ), ext.begin( ), [] ( uint8_t c )
	{
		return static_cast<char>( std::tolower( c ) );
	} );
	return whitelist_extensions.find( ext ) != whitelist_extensions.end( );
}

inline bool IsPathIDAllowed( std::string &pathid )
{
	std::transform( pathid.begin( ), pathid.end( ), pathid.begin( ), [] ( uint8_t c )
	{
		return static_cast<char>( std::tolower( c ) );
	} );
	return whitelist_pathid.find( pathid ) != whitelist_pathid.end( );
}

LUA_FUNCTION_STATIC( Rename )
{
	LUA->CheckType( 1, GarrysMod::Lua::Type::STRING );
	LUA->CheckType( 2, GarrysMod::Lua::Type::STRING );
	LUA->CheckType( 3, GarrysMod::Lua::Type::STRING );

	std::string fname = LUA->GetString( 1 ), fnew = LUA->GetString( 2 ), pathid = LUA->GetString( 3 );

	if( !IsPathAllowed( fname ) || !IsPathAllowed( fnew ) || !IsPathIDAllowed( pathid ) )
	{
		LUA->PushBool( false );
		return 1;
	}

	LUA->PushBool( filesystem->RenameFile( fname.c_str( ), fnew.c_str( ), pathid.c_str( ) ) );
	return 1;
}

static void Initialize( GarrysMod::Lua::ILuaBase *LUA )
{
	SourceSDK::FactoryLoader engine_loader( "engine", false, false, "bin/" );

	IVEngineServer *engine_server = engine_loader.GetInterface<IVEngineServer>(
		INTERFACEVERSION_VENGINESERVER
	);
	if( engine_server == nullptr )
		LUA->ThrowError( "failed to retrieve server engine interface" );

	bool isdedicated = engine_server->IsDedicatedServer( );

	SymbolFinder symfinder;

	CreateInterfaceFn factory = reinterpret_cast<CreateInterfaceFn>( symfinder.ResolveOnBinary(
		( isdedicated ? filesystem_dedicated_lib : filesystem_lib ).c_str( ),
		isdedicated ? FileSystemFactory_dedicated_sym : FileSystemFactory_sym,
		isdedicated ? FileSystemFactory_dedicated_symlen : FileSystemFactory_symlen
	) );
	if( factory == nullptr )
		LUA->ThrowError( "unable to retrieve filesystem factory" );

	filesystem = static_cast<IFileSystem *>( factory( FILESYSTEM_INTERFACE_VERSION, nullptr ) );
	if( filesystem == nullptr )
		LUA->ThrowError( "failed to initialize IFileSystem" );

	GModDataPackProxy::Singleton.Initialize( LUA );

	LUA->GetField( GarrysMod::Lua::INDEX_GLOBAL, "luapack" );
	if( !LUA->IsType( -1, GarrysMod::Lua::Type::TABLE ) )
		LUA->ThrowError( "luapack table not found" );

	LUA->PushCFunction( Rename );
	LUA->SetField( -2, "Rename" );

	LUA->Pop( 1 );
}

static void Deinitialize( GarrysMod::Lua::ILuaBase *LUA )
{
	LUA->GetField( GarrysMod::Lua::INDEX_GLOBAL, "luapack" );
	if( !LUA->IsType( -1, GarrysMod::Lua::Type::TABLE ) )
		LUA->ThrowError( "luapack table not found" );

	LUA->PushNil( );
	LUA->SetField( -2, "Rename" );

	LUA->Pop( 1 );

	GModDataPackProxy::Singleton.Deinitialize( LUA );
}

}

GMOD_MODULE_OPEN( )
{
	luapack::Initialize( LUA );
	return 0;
}

GMOD_MODULE_CLOSE( )
{
	luapack::Deinitialize( LUA );
	return 0;
}
<commit_msg>Add _srv suffix to engine binary path<commit_after>#include <GarrysMod/Lua/Interface.h>
#include <GarrysMod/Lua/Helpers.hpp>
#include <GarrysMod/Interfaces.hpp>

#include <scanning/symbolfinder.hpp>
#include <detouring/classproxy.hpp>

#include <cstdint>
#include <string>
#include <unordered_set>
#include <algorithm>
#include <cctype>

#include <interface.h>
#include <filesystem.h>
#include <eiface.h>

class GModDataPack;

namespace Bootil
{

class Buffer
{
public:
	virtual ~Buffer() = default; // For the VTable

	void *data;
	uint32_t size;
	uint32_t pos;
	uint32_t written;
};

class AutoBuffer : public Buffer
{
public:
	virtual ~AutoBuffer() = default; // For the VTable
};

}

class LuaFile // 116 bytes
{
public:
	virtual ~LuaFile() = default; // For the VTable

	std::string path;
	std::string parent;
	std::string content;
	Bootil::AutoBuffer buffer;
	bool unknown1;
	bool unknown2;
};

namespace luapack
{

#if defined _WIN32

static const char FileSystemFactory_dedicated_sym[] = "\x55\x8B\xEC\x68\x2A\x2A\x2A\x2A\xFF\x75\x08\xE8\x2A\x2A\x2A\x2A\x83\xC4\x08";
static const size_t FileSystemFactory_dedicated_symlen = sizeof( FileSystemFactory_dedicated_sym ) - 1;

static const char AddOrUpdateFile_sym[] = "\x55\x8B\xEC\x83\xEC\x18\x53\x56\x57\x8B\x7D\x08\x8B\xD9\x83\x7F\x18\x10";
static const size_t AddOrUpdateFile_symlen = sizeof( AddOrUpdateFile_sym ) - 1;

#elif defined __linux || defined __APPLE__

static const char FileSystemFactory_dedicated_sym[] = "@_Z17FileSystemFactoryPKcPi";
static const size_t FileSystemFactory_dedicated_symlen = 0;

static const char AddOrUpdateFile_sym[] = "@_ZN12GModDataPack15AddOrUpdateFileEP7LuaFileb";
static const size_t AddOrUpdateFile_symlen = 0;

#endif

static const std::string filesystem_dedicated_lib = Helpers::GetBinaryFileName( "dedicated", false );
static const std::string filesystem_lib = Helpers::GetBinaryFileName( "filesystem_stdio", false, false );
static IFileSystem *filesystem = nullptr;

static const std::string server_lib = Helpers::GetBinaryFileName( "server", false, true, "garrysmod/bin/" );

static const char FileSystemFactory_sym[] = "@CreateInterface";
static const size_t FileSystemFactory_symlen = 0;

static std::unordered_set<std::string> whitelist_extensions = { "lua", "txt", "dat" };
static std::unordered_set<std::string> whitelist_pathid = { "lsv", "lua", "data" };

class GModDataPackProxy : public Detouring::ClassProxy<GModDataPack, GModDataPackProxy>
{
private:

#if defined _WIN32

	typedef void( __thiscall *AddOrUpdateFile_t ) ( GModDataPack *self, LuaFile *file, bool force );

#elif defined __linux || defined __APPLE__

	typedef void( *AddOrUpdateFile_t ) ( GModDataPack *self, LuaFile *file, bool force );

#endif

	static AddOrUpdateFile_t AddOrUpdateFile_original;
	static const char hook_name[];
	static GarrysMod::Lua::ILuaBase *lua;

public:
	static void Initialize( GarrysMod::Lua::ILuaBase *LUA )
	{
		lua = LUA;

		SymbolFinder symfinder;

		AddOrUpdateFile_original = reinterpret_cast<AddOrUpdateFile_t>(
			symfinder.ResolveOnBinary( server_lib.c_str( ), AddOrUpdateFile_sym, AddOrUpdateFile_symlen )
		);
		if( AddOrUpdateFile_original == nullptr )
			LUA->ThrowError( "failed to find GModDataPack::AddOrUpdateFile" );

		if( !Hook( AddOrUpdateFile_original, &GModDataPackProxy::AddOrUpdateFile ) )
			LUA->ThrowError( "failed to hook GModDataPack::AddOrUpdateFile" );
	}

	static void Deinitialize( GarrysMod::Lua::ILuaBase *LUA )
	{
		UnHook( AddOrUpdateFile_original );
	}

	void AddOrUpdateFile( LuaFile *file, bool reload )
	{
		LuaHelpers::PushHookRun( lua, hook_name );

		lua->PushString( file->path.c_str( ) );
		lua->PushBool( reload );

		bool shouldcall = true;
		if( LuaHelpers::CallHookRun( lua, 2, 1 ) )
			shouldcall = !lua->IsType( -1, GarrysMod::Lua::Type::BOOL ) || lua->GetBool( -1 );

		lua->Pop( 1 );

		if( shouldcall )
			return Call( AddOrUpdateFile_original, file, reload );
	}

	static GModDataPackProxy Singleton;
};

GModDataPackProxy::AddOrUpdateFile_t GModDataPackProxy::AddOrUpdateFile_original = nullptr;
const char GModDataPackProxy::hook_name[] = "AddOrUpdateCSLuaFile";
GarrysMod::Lua::ILuaBase *GModDataPackProxy::lua = nullptr;

static bool IsPathAllowed( std::string &filename )
{
	if( !V_RemoveDotSlashes( &filename[0], CORRECT_PATH_SEPARATOR, true ) )
		return false;

	filename.resize( std::strlen( filename.c_str( ) ) );

	const char *extension = V_GetFileExtension( filename.c_str( ) );
	if( extension == nullptr )
		return false;

	std::string ext = extension;
	std::transform( ext.begin( ), ext.end( ), ext.begin( ), [] ( uint8_t c )
	{
		return static_cast<char>( std::tolower( c ) );
	} );
	return whitelist_extensions.find( ext ) != whitelist_extensions.end( );
}

inline bool IsPathIDAllowed( std::string &pathid )
{
	std::transform( pathid.begin( ), pathid.end( ), pathid.begin( ), [] ( uint8_t c )
	{
		return static_cast<char>( std::tolower( c ) );
	} );
	return whitelist_pathid.find( pathid ) != whitelist_pathid.end( );
}

LUA_FUNCTION_STATIC( Rename )
{
	LUA->CheckType( 1, GarrysMod::Lua::Type::STRING );
	LUA->CheckType( 2, GarrysMod::Lua::Type::STRING );
	LUA->CheckType( 3, GarrysMod::Lua::Type::STRING );

	std::string fname = LUA->GetString( 1 ), fnew = LUA->GetString( 2 ), pathid = LUA->GetString( 3 );

	if( !IsPathAllowed( fname ) || !IsPathAllowed( fnew ) || !IsPathIDAllowed( pathid ) )
	{
		LUA->PushBool( false );
		return 1;
	}

	LUA->PushBool( filesystem->RenameFile( fname.c_str( ), fnew.c_str( ), pathid.c_str( ) ) );
	return 1;
}

static void Initialize( GarrysMod::Lua::ILuaBase *LUA )
{
	SourceSDK::FactoryLoader engine_loader( "engine", false, true, "bin/" );

	IVEngineServer *engine_server = engine_loader.GetInterface<IVEngineServer>(
		INTERFACEVERSION_VENGINESERVER
	);
	if( engine_server == nullptr )
		LUA->ThrowError( "failed to retrieve server engine interface" );

	bool isdedicated = engine_server->IsDedicatedServer( );

	SymbolFinder symfinder;

	CreateInterfaceFn factory = reinterpret_cast<CreateInterfaceFn>( symfinder.ResolveOnBinary(
		( isdedicated ? filesystem_dedicated_lib : filesystem_lib ).c_str( ),
		isdedicated ? FileSystemFactory_dedicated_sym : FileSystemFactory_sym,
		isdedicated ? FileSystemFactory_dedicated_symlen : FileSystemFactory_symlen
	) );
	if( factory == nullptr )
		LUA->ThrowError( "unable to retrieve filesystem factory" );

	filesystem = static_cast<IFileSystem *>( factory( FILESYSTEM_INTERFACE_VERSION, nullptr ) );
	if( filesystem == nullptr )
		LUA->ThrowError( "failed to initialize IFileSystem" );

	GModDataPackProxy::Singleton.Initialize( LUA );

	LUA->GetField( GarrysMod::Lua::INDEX_GLOBAL, "luapack" );
	if( !LUA->IsType( -1, GarrysMod::Lua::Type::TABLE ) )
		LUA->ThrowError( "luapack table not found" );

	LUA->PushCFunction( Rename );
	LUA->SetField( -2, "Rename" );

	LUA->Pop( 1 );
}

static void Deinitialize( GarrysMod::Lua::ILuaBase *LUA )
{
	LUA->GetField( GarrysMod::Lua::INDEX_GLOBAL, "luapack" );
	if( !LUA->IsType( -1, GarrysMod::Lua::Type::TABLE ) )
		LUA->ThrowError( "luapack table not found" );

	LUA->PushNil( );
	LUA->SetField( -2, "Rename" );

	LUA->Pop( 1 );

	GModDataPackProxy::Singleton.Deinitialize( LUA );
}

}

GMOD_MODULE_OPEN( )
{
	luapack::Initialize( LUA );
	return 0;
}

GMOD_MODULE_CLOSE( )
{
	luapack::Deinitialize( LUA );
	return 0;
}
<|endoftext|>


<commit_before>#include "buildClassConstructorsEtc.h"
#include "symtab.h"
#include "symscope.h"
#include "symbol.h"
#include "type.h"
#include "expr.h"
#include "stmt.h"
#include "stringutil.h"
#include "../traversals/fixup.h"

//#define CONSTRUCTOR_WITH_PARAMETERS 1

static void build_constructor(ClassType* class_type) {
#ifndef CONSTRUCTOR_WITH_PARAMETERS
  SymScope* saveScope = Symboltable::setCurrentScope(class_type->classScope);
#endif

  char* name = glomstrings(2, "_construct_", class_type->symbol->name);
  FnSymbol* fn = Symboltable::startFnDef(new FnSymbol(name));
  fn->cname = glomstrings(2, "_construct_", class_type->symbol->cname);

  ParamSymbol* args = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
#if CONSTRUCTOR_WITH_PARAMETERS
    args = appendLink(args, new ParamSymbol(PARAM_BLANK, tmp->name, tmp->type, tmp->init ? tmp->init->copy() : tmp->type->defaultVal->copy()));
#else
#endif
  }

  BlockStmt* body = Symboltable::startCompoundStmt();
  Stmt* stmts = NULL;
  VarSymbol* _this = new VarSymbol("this", class_type);
  DefExpr* def_expr = new DefExpr(_this);
  _this->setDefPoint(def_expr);
  stmts = new DefStmt(def_expr);
  if (!(class_type->value || class_type->union_value)) {
    Expr* alloc_args = new IntLiteral("1", 1);
    alloc_args = appendLink(alloc_args, new SizeofExpr(class_type));
    alloc_args = appendLink(alloc_args, new StringLiteral("string"));
    Symbol* alloc_sym = Symboltable::lookupInternal("_chpl_malloc");
    Expr* alloc_call = new FnCall(new Variable(alloc_sym), alloc_args);
    Expr* alloc_lhs = new Variable(_this);
    Expr* alloc_rhs = new CastExpr(class_type, alloc_call);
    Expr* alloc_expr = new AssignOp(GETS_NORM, alloc_lhs, alloc_rhs);
    Stmt* alloc_stmt = new ExprStmt(alloc_expr);
    stmts = appendLink(stmts, alloc_stmt);
  }
#if CONSTRUCTOR_WITH_PARAMETERS
  ParamSymbol* ptmp = args;
#endif
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    Expr* lhs = new MemberAccess(new Variable(_this), tmp);
#if CONSTRUCTOR_WITH_PARAMETERS
    Expr* rhs = new Variable(ptmp);
#else
    Expr* rhs = tmp->init ? tmp->init->copy() : tmp->type->defaultVal->copy();
    if (!rhs) {
      if (ClassType* nested_class_type = dynamic_cast<ClassType*>(tmp->type)) {
	rhs = new ParenOpExpr(new Variable(nested_class_type->symbol), NULL);
      } else continue; // hack for classes that are cloned; we don't
                       // actually want to build the constructor for
                       // cloned classes until they are cloned
    }
#endif
    Expr* assign_expr = new AssignOp(GETS_NORM, lhs, rhs);
    Stmt* assign_stmt = new ExprStmt(assign_expr);
    stmts = appendLink(stmts, assign_stmt);
#if CONSTRUCTOR_WITH_PARAMETERS
    ptmp = nextLink(ParamSymbol, ptmp);
#endif
  }

  stmts = appendLink(stmts, new ReturnStmt(new Variable(_this)));
  body = Symboltable::finishCompoundStmt(body, stmts);
  Expr* fn_def = Symboltable::finishFnDef(fn, args, class_type, body);
  class_type->constructor = new DefStmt(fn_def);

  SET_BACK(class_type->constructor);
#ifndef CONSTRUCTOR_WITH_PARAMETERS
  Symboltable::setCurrentScope(saveScope);
#endif
  TRAVERSE(dynamic_cast<Expr*>(class_type->symbol->defPoint)->stmt, new Fixup(), true);
}

static void build_union_id_enum(ClassType* class_type) {
  if (!class_type->union_value) {
    return;
  }

  EnumSymbol* id_list = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    char* id_name =
      glomstrings(4, "_", class_type->symbol->name, "_union_id_", tmp->name);
    EnumSymbol* id_symbol = new EnumSymbol(id_name, NULL);
    id_list = appendLink(id_list, id_symbol);
  }
  id_list->set_values();
  EnumType* enum_type = new EnumType(id_list);
  char* enum_name =
    glomstrings(3, "_", class_type->symbol->name, "_union_id");
  TypeSymbol* enum_symbol = new TypeSymbol(enum_name, enum_type);
  enum_type->addSymbol(enum_symbol);
  DefExpr* def_expr = new DefExpr(enum_symbol);
  enum_symbol->setDefPoint(def_expr);
  id_list->setDefPoint(def_expr);
  DefStmt* def_stmt = new DefStmt(def_expr);
  dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
}

static void build_record_equality_testing_functions(ClassType* class_type) {
  if (!class_type->value) {
    return;
  }

  FnSymbol* fn = Symboltable::startFnDef(new FnSymbol("=="));
  {
    ParamSymbol* arg1 = new ParamSymbol(PARAM_BLANK, "_arg1", class_type);
    ParamSymbol* arg2 = new ParamSymbol(PARAM_BLANK, "_arg2", class_type);
    arg1->append(arg2);
    Expr* cond = NULL;
    forv_Vec(VarSymbol, tmp, class_type->fields) {
      Expr* left = new MemberAccess(new Variable(arg1), tmp);
      Expr* right = new MemberAccess(new Variable(arg2), tmp);
      cond = (cond)
	? new BinOp(BINOP_LOGAND, cond, new BinOp(BINOP_EQUAL, left, right))
	: new BinOp(BINOP_EQUAL, left, right);
    }
    Stmt* body = new ReturnStmt(cond);
    DefStmt* def_stmt = new DefStmt(Symboltable::finishFnDef(fn, arg1, dtBoolean, body));
    dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
  }

  FnSymbol* fn2 = Symboltable::startFnDef(new FnSymbol("!="));
  {
    ParamSymbol* arg1 = new ParamSymbol(PARAM_BLANK, "__arg1", class_type);
    ParamSymbol* arg2 = new ParamSymbol(PARAM_BLANK, "__arg2", class_type);
    arg1->append(arg2);
    Expr* actuals = new Variable(arg1);
    actuals->append(new Variable(arg2));
    Expr* call_equal = new FnCall(new Variable(fn), actuals);
    Stmt* body = new ReturnStmt(new UnOp(UNOP_LOGNOT, call_equal));
    DefStmt* def_stmt = new DefStmt(Symboltable::finishFnDef(fn2, arg1, dtBoolean, body));
    dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
  }
}

BuildClassConstructorsEtc::BuildClassConstructorsEtc(void) {
  processInternalModules = false;
}

void BuildClassConstructorsEtc::postProcessExpr(Expr* expr) {
  DefExpr* def_expr = dynamic_cast<DefExpr*>(expr);

  if (!def_expr) {
    return;
  }

  if (TypeSymbol* type_symbol = dynamic_cast<TypeSymbol*>(def_expr->sym)) {
    if (ClassType* class_type = dynamic_cast<ClassType*>(type_symbol->type)) {
      build_constructor(class_type);
      build_union_id_enum(class_type);
      build_record_equality_testing_functions(class_type);
    }
  }
}
<commit_msg>Decoupled the creation of the != record function from the == record function.  It no longer negates the results of a call to == but rather creates a different function from scratch.<commit_after>#include "buildClassConstructorsEtc.h"
#include "symtab.h"
#include "symscope.h"
#include "symbol.h"
#include "type.h"
#include "expr.h"
#include "stmt.h"
#include "stringutil.h"
#include "../traversals/fixup.h"

//#define CONSTRUCTOR_WITH_PARAMETERS 1

static void build_constructor(ClassType* class_type) {
#ifndef CONSTRUCTOR_WITH_PARAMETERS
  SymScope* saveScope = Symboltable::setCurrentScope(class_type->classScope);
#endif

  char* name = glomstrings(2, "_construct_", class_type->symbol->name);
  FnSymbol* fn = Symboltable::startFnDef(new FnSymbol(name));
  fn->cname = glomstrings(2, "_construct_", class_type->symbol->cname);

  ParamSymbol* args = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
#if CONSTRUCTOR_WITH_PARAMETERS
    args = appendLink(args, new ParamSymbol(PARAM_BLANK, tmp->name, tmp->type, tmp->init ? tmp->init->copy() : tmp->type->defaultVal->copy()));
#else
#endif
  }

  BlockStmt* body = Symboltable::startCompoundStmt();
  Stmt* stmts = NULL;
  VarSymbol* _this = new VarSymbol("this", class_type);
  DefExpr* def_expr = new DefExpr(_this);
  _this->setDefPoint(def_expr);
  stmts = new DefStmt(def_expr);
  if (!(class_type->value || class_type->union_value)) {
    Expr* alloc_args = new IntLiteral("1", 1);
    alloc_args = appendLink(alloc_args, new SizeofExpr(class_type));
    alloc_args = appendLink(alloc_args, new StringLiteral("string"));
    Symbol* alloc_sym = Symboltable::lookupInternal("_chpl_malloc");
    Expr* alloc_call = new FnCall(new Variable(alloc_sym), alloc_args);
    Expr* alloc_lhs = new Variable(_this);
    Expr* alloc_rhs = new CastExpr(class_type, alloc_call);
    Expr* alloc_expr = new AssignOp(GETS_NORM, alloc_lhs, alloc_rhs);
    Stmt* alloc_stmt = new ExprStmt(alloc_expr);
    stmts = appendLink(stmts, alloc_stmt);
  }
#if CONSTRUCTOR_WITH_PARAMETERS
  ParamSymbol* ptmp = args;
#endif
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    Expr* lhs = new MemberAccess(new Variable(_this), tmp);
#if CONSTRUCTOR_WITH_PARAMETERS
    Expr* rhs = new Variable(ptmp);
#else
    Expr* rhs = tmp->init ? tmp->init->copy() : tmp->type->defaultVal->copy();
    if (!rhs) {
      if (ClassType* nested_class_type = dynamic_cast<ClassType*>(tmp->type)) {
	rhs = new ParenOpExpr(new Variable(nested_class_type->symbol), NULL);
      } else continue; // hack for classes that are cloned; we don't
                       // actually want to build the constructor for
                       // cloned classes until they are cloned
    }
#endif
    Expr* assign_expr = new AssignOp(GETS_NORM, lhs, rhs);
    Stmt* assign_stmt = new ExprStmt(assign_expr);
    stmts = appendLink(stmts, assign_stmt);
#if CONSTRUCTOR_WITH_PARAMETERS
    ptmp = nextLink(ParamSymbol, ptmp);
#endif
  }

  stmts = appendLink(stmts, new ReturnStmt(new Variable(_this)));
  body = Symboltable::finishCompoundStmt(body, stmts);
  Expr* fn_def = Symboltable::finishFnDef(fn, args, class_type, body);
  class_type->constructor = new DefStmt(fn_def);

  SET_BACK(class_type->constructor);
#ifndef CONSTRUCTOR_WITH_PARAMETERS
  Symboltable::setCurrentScope(saveScope);
#endif
  TRAVERSE(dynamic_cast<Expr*>(class_type->symbol->defPoint)->stmt, new Fixup(), true);
}

static void build_union_id_enum(ClassType* class_type) {
  if (!class_type->union_value) {
    return;
  }

  EnumSymbol* id_list = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    char* id_name =
      glomstrings(4, "_", class_type->symbol->name, "_union_id_", tmp->name);
    EnumSymbol* id_symbol = new EnumSymbol(id_name, NULL);
    id_list = appendLink(id_list, id_symbol);
  }
  id_list->set_values();
  EnumType* enum_type = new EnumType(id_list);
  char* enum_name =
    glomstrings(3, "_", class_type->symbol->name, "_union_id");
  TypeSymbol* enum_symbol = new TypeSymbol(enum_name, enum_type);
  enum_type->addSymbol(enum_symbol);
  DefExpr* def_expr = new DefExpr(enum_symbol);
  enum_symbol->setDefPoint(def_expr);
  id_list->setDefPoint(def_expr);
  DefStmt* def_stmt = new DefStmt(def_expr);
  dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
}

static void build_record_equality_function(ClassType* class_type) {
  if (!class_type->value) {
    return;
  }

  FnSymbol* fn = Symboltable::startFnDef(new FnSymbol("=="));
  ParamSymbol* arg1 = new ParamSymbol(PARAM_BLANK, "_arg1", class_type);
  ParamSymbol* arg2 = new ParamSymbol(PARAM_BLANK, "_arg2", class_type);
  arg1->append(arg2);
  Expr* cond = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    Expr* left = new MemberAccess(new Variable(arg1), tmp);
    Expr* right = new MemberAccess(new Variable(arg2), tmp);
    cond = (cond)
      ? new BinOp(BINOP_LOGAND, cond, new BinOp(BINOP_EQUAL, left, right))
      : new BinOp(BINOP_EQUAL, left, right);
  }
  Stmt* body = new ReturnStmt(cond);
  DefStmt* def_stmt = new DefStmt(Symboltable::finishFnDef(fn, arg1, dtBoolean, body));
  dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
}

static void build_record_inequality_function(ClassType* class_type) {
  if (!class_type->value) {
    return;
  }

  FnSymbol* fn = Symboltable::startFnDef(new FnSymbol("!="));

  ParamSymbol* arg1 = new ParamSymbol(PARAM_BLANK, "_arg1", class_type);
  ParamSymbol* arg2 = new ParamSymbol(PARAM_BLANK, "_arg2", class_type);
  arg1->append(arg2);
  Expr* cond = NULL;
  forv_Vec(VarSymbol, tmp, class_type->fields) {
    Expr* left = new MemberAccess(new Variable(arg1), tmp);
    Expr* right = new MemberAccess(new Variable(arg2), tmp);
    cond = (cond)
      ? new BinOp(BINOP_LOGOR, cond, new BinOp(BINOP_EQUAL, left, right))
      : new BinOp(BINOP_NEQUAL, left, right);
  }
  Stmt* body = new ReturnStmt(cond);
  DefStmt* def_stmt = new DefStmt(Symboltable::finishFnDef(fn, arg1, dtBoolean, body));
  dynamic_cast<DefExpr*>(class_type->symbol->defPoint)->stmt->insertBefore(def_stmt);
}

BuildClassConstructorsEtc::BuildClassConstructorsEtc(void) {
  processInternalModules = false;
}

void BuildClassConstructorsEtc::postProcessExpr(Expr* expr) {
  DefExpr* def_expr = dynamic_cast<DefExpr*>(expr);

  if (!def_expr) {
    return;
  }

  if (TypeSymbol* type_symbol = dynamic_cast<TypeSymbol*>(def_expr->sym)) {
    if (ClassType* class_type = dynamic_cast<ClassType*>(type_symbol->type)) {
      build_constructor(class_type);
      build_union_id_enum(class_type);
      build_record_equality_function(class_type);
      build_record_inequality_function(class_type);
    }
  }
}
<|endoftext|>