<commit_before>#include <map>
#include "unittest/gtest.hpp"

#include "btree/loof_node.hpp"

struct short_value_t;

template <>
class value_sizer_t<short_value_t> {
public:
    value_sizer_t<short_value_t>(block_size_t bs) : block_size_(bs) { }

    int size(const short_value_t *value) const {
        int x = *reinterpret_cast<const uint8_t *>(value);
        return 1 + x;
    }

    bool fits(const short_value_t *value, int length_available) const {
        return length_available > 0 && size(value) <= length_available;
    }

    int max_possible_size() const {
        return 256;
    }

    static block_magic_t btree_leaf_magic() {
        block_magic_t magic = { { 's', 'h', 'L', 'F' } };
        return magic;
    }

    block_size_t block_size() const { return block_size_; }

private:
    block_size_t block_size_;

    DISABLE_COPYING(value_sizer_t<short_value_t>);
};

using loof::loof_t;

namespace unittest {

class short_value_buffer_t {
public:
    short_value_buffer_t(const std::string& v) {
        rassert(v.size() < 255);
        data_[0] = v.size();
        memcpy(data_ + 1, v.data(), v.size());
    }

    short_value_t *data() {
        return reinterpret_cast<short_value_t *>(data_);
    }

private:
    uint8_t data_[256];
};

class LoofTracker {
public:
    LoofTracker() : bs_(block_size_t::unsafe_make(4096)), sizer_(bs_), node_(reinterpret_cast<loof_t *>(new char[bs_.value()])),
                    tstamp_counter_(0) {
        loof::init(&sizer_, node_);
        Print();
    }
    ~LoofTracker() { delete[] node_; }

    bool Insert(const std::string& key, const std::string& value) {
        // printf("\n\nInserting %s\n\n", key.c_str());
        btree_key_buffer_t k(key.begin(), key.end());
        short_value_buffer_t v(value);

        if (loof::is_full(&sizer_, node_, k.key(), v.data())) {
            Print();

            Verify();
            return false;
        }

        repli_timestamp_t tstamp = NextTimestamp();
        loof::insert(&sizer_, node_, k.key(), v.data(), tstamp);

        kv_[key] = value;

        Print();

        Verify();
        return true;
    }

    void Remove(const std::string& key) {
        // printf("\n\nRemoving %s\n\n", key.c_str());
        btree_key_buffer_t k(key.begin(), key.end());

        ASSERT_TRUE(ShouldHave(key));

        kv_.erase(key);

        repli_timestamp_t tstamp = NextTimestamp();
        loof::remove(&sizer_, node_, k.key(), tstamp);

        Verify();

        Print();
    }

    void Merge(LoofTracker& lnode) {
        ASSERT_EQ(bs_.ser_value(), lnode.bs_.ser_value());

        btree_key_buffer_t buf;
        loof::merge(&sizer_, lnode.node_, node_, buf.key());

        int old_kv_size = kv_.size();
        for (std::map<std::string, std::string>::iterator p = lnode.kv_.begin(), e = lnode.kv_.end(); p != e; ++p) {
            kv_[p->first] = p->second;
        }

        ASSERT_EQ(kv_.size(), old_kv_size + lnode.kv_.size());

        lnode.kv_.clear();

        Verify();
        lnode.Verify();
    }

    bool ShouldHave(const std::string& key) {
        return kv_.end() != kv_.find(key);
    }

    repli_timestamp_t NextTimestamp() {
        tstamp_counter_ ++;
        repli_timestamp_t ret;
        ret.time = tstamp_counter_;
        return ret;
    }

    // This only prints if we enable printing.
    void Print() {
        // loof::print(stdout, &sizer_, node_);
    }

    void Verify() {
        if (!kv_.empty()) {
            ASSERT_EQ(tstamp_counter_, reinterpret_cast<repli_timestamp_t *>(loof::get_at_offset(node_, node_->frontmost))->time);
        }

        // Of course, this will fail with rassert, not a gtest assertion.
        loof::validate(&sizer_, node_);
    }

public:
    block_size_t bs_;
    value_sizer_t<short_value_t> sizer_;
    loof_t *node_;

    int tstamp_counter_;

    std::map<std::string, std::string> kv_;

    DISABLE_COPYING(LoofTracker);
};

TEST(LoofTest, Offsets) {
    ASSERT_EQ(0, offsetof(loof_t, magic));
    ASSERT_EQ(4, offsetof(loof_t, num_pairs));
    ASSERT_EQ(6, offsetof(loof_t, live_size));
    ASSERT_EQ(8, offsetof(loof_t, frontmost));
    ASSERT_EQ(10, offsetof(loof_t, tstamp_cutpoint));
    ASSERT_EQ(12, offsetof(loof_t, pair_offsets));
    ASSERT_EQ(12, sizeof(loof_t));
}

TEST(LoofTest, Reinserts) {
    LoofTracker tracker;
    std::string v = "aa";
    std::string k = "key";
    for (; v[0] <= 'z'; ++v[0]) {
        v[1] = 'a';
        for (; v[1] <= 'z'; ++v[1]) {
            // printf("inserting %s\n", v.c_str());
            tracker.Insert(k, v);
        }
    }
}

TEST(LoofTest, TenInserts) {
    LoofTracker tracker;

    ASSERT_LT(loof::MANDATORY_TIMESTAMPS, 10);

    const int num_keys = 10;
    std::string ks[num_keys] = { "the_relatively_long_key_that_is_relatively_long,_eh?__or_even_longer",
                           "some_other_relatively_long_key_that_...whatever.",
                           "another_relatively_long_key",
                           "a_short_key",
                           "", /* an empty string key */
                           "grohl",
                           "cobain",
                           "reznor",
                           "marley",
                           "domino" };

    for (int i = 0; i < 26 * 26; ++i) {
        std::string v;
        v += ('a' + (i / 26));
        v += ('a' + (i % 26));

        for (int j = 0; j < num_keys; ++j) {
            tracker.Insert(ks[j], v);
        }
    }
}

TEST(LoofTest, InsertRemove) {
    LoofTracker tracker;

    srand(12345);

    const int num_keys = 10;
    std::string ks[num_keys] = { "the_relatively_long_key_that_is_relatively_long,_eh?__or_even_longer",
                           "some_other_relatively_long_key_that_...whatever.",
                           "another_relatively_long_key",
                           "a_short_key",
                           "", /* an empty string key */
                           "grohl",
                           "cobain",
                           "reznor",
                           "marley",
                           "domino" };

    for (int i = 0; i < 26 * 26; ++i) {
        std::string v;
        v += ('a' + (i / 26));
        v += ('a' + (i % 26));

        for (int j = 0; j < num_keys; ++j) {
            if (rand() % 2 == 1) {
                tracker.Insert(ks[j], v);
            } else {
                if (tracker.ShouldHave(ks[j])) {
                    tracker.Remove(ks[j]);
                }
            }
        }
    }
}

TEST(LoofTest, MinimalMerging) {
    LoofTracker left;
    LoofTracker right;

    left.Insert("a", "A");
    right.Insert("b", "B");

    right.Merge(left);
}

TEST(LoofTest, SimpleMerging) {

    LoofTracker left;
    LoofTracker right;

    // We use the largest value that will underflow.
    //
    // key_cost = 251, max_possible_size() = 256, sizeof(uint16_t) = 2, sizeof(repli_timestamp) = 4.
    //
    // 4084 - 12 = 4072.  4072 / 2 = 2036.  2036 - (251 + 256 + 2
    // + 4) = 2036 - 513 = 1523.  So 1522 is the max possible
    // mandatory_cost.  (See the is_underfull implementation.)
    //
    // With 5*4 mandatory timestamp bytes and 12 bytes per entry,
    // that gives us 1502 / 12 as the loop boundary value that
    // will underflow.  We get 12 byte entries if entries run from
    // a000 to a999.  But if we allow two-digit entries, that
    // frees up 2 bytes per entry, so add 200, giving 1702.  If we
    // allow one-digit entries, that gives us 20 more bytes to
    // use, giving 1722 / 12 as the loop boundary.  That's an odd
    // way to look at the arithmetic, but if you don't like that,
    // you can go cry to your mommy.

    for (int i = 0; i < 1722 / 12; ++i) {
        left.Insert(strprintf("a%d", i), strprintf("A%d", i));
        right.Insert(strprintf("b%d", i), strprintf("B%d", i));
    }

    right.Merge(left);
}

TEST(LoofTest, MergingWithRemoves) {
    LoofTracker left;
    LoofTracker right;

    for (int i = 0; i < 21 /* 1000 / 12 */; ++i) {
        left.Insert(strprintf("a%d", i), strprintf("A%d", i));
        right.Insert(strprintf("b%d", i), strprintf("B%d", i));
        if (i % 5 == 0) {
            left.Remove(strprintf("a%d", i / 5));
            right.Remove(strprintf("b%d", i / 5));
        }
    }

    right.Merge(left);
}

}  // namespace unittest
<commit_msg>Made LoofTest.MergingWithRemoves use its original large for loop parameter.<commit_after>#include <map>
#include "unittest/gtest.hpp"

#include "btree/loof_node.hpp"

struct short_value_t;

template <>
class value_sizer_t<short_value_t> {
public:
    value_sizer_t<short_value_t>(block_size_t bs) : block_size_(bs) { }

    int size(const short_value_t *value) const {
        int x = *reinterpret_cast<const uint8_t *>(value);
        return 1 + x;
    }

    bool fits(const short_value_t *value, int length_available) const {
        return length_available > 0 && size(value) <= length_available;
    }

    int max_possible_size() const {
        return 256;
    }

    static block_magic_t btree_leaf_magic() {
        block_magic_t magic = { { 's', 'h', 'L', 'F' } };
        return magic;
    }

    block_size_t block_size() const { return block_size_; }

private:
    block_size_t block_size_;

    DISABLE_COPYING(value_sizer_t<short_value_t>);
};

using loof::loof_t;

namespace unittest {

class short_value_buffer_t {
public:
    short_value_buffer_t(const std::string& v) {
        rassert(v.size() < 255);
        data_[0] = v.size();
        memcpy(data_ + 1, v.data(), v.size());
    }

    short_value_t *data() {
        return reinterpret_cast<short_value_t *>(data_);
    }

private:
    uint8_t data_[256];
};

class LoofTracker {
public:
    LoofTracker() : bs_(block_size_t::unsafe_make(4096)), sizer_(bs_), node_(reinterpret_cast<loof_t *>(new char[bs_.value()])),
                    tstamp_counter_(0) {
        loof::init(&sizer_, node_);
        Print();
    }
    ~LoofTracker() { delete[] node_; }

    bool Insert(const std::string& key, const std::string& value) {
        // printf("\n\nInserting %s\n\n", key.c_str());
        btree_key_buffer_t k(key.begin(), key.end());
        short_value_buffer_t v(value);

        if (loof::is_full(&sizer_, node_, k.key(), v.data())) {
            Print();

            Verify();
            return false;
        }

        repli_timestamp_t tstamp = NextTimestamp();
        loof::insert(&sizer_, node_, k.key(), v.data(), tstamp);

        kv_[key] = value;

        Print();

        Verify();
        return true;
    }

    void Remove(const std::string& key) {
        // printf("\n\nRemoving %s\n\n", key.c_str());
        btree_key_buffer_t k(key.begin(), key.end());

        ASSERT_TRUE(ShouldHave(key));

        kv_.erase(key);

        repli_timestamp_t tstamp = NextTimestamp();
        loof::remove(&sizer_, node_, k.key(), tstamp);

        Verify();

        Print();
    }

    void Merge(LoofTracker& lnode) {
        ASSERT_EQ(bs_.ser_value(), lnode.bs_.ser_value());

        btree_key_buffer_t buf;
        loof::merge(&sizer_, lnode.node_, node_, buf.key());

        int old_kv_size = kv_.size();
        for (std::map<std::string, std::string>::iterator p = lnode.kv_.begin(), e = lnode.kv_.end(); p != e; ++p) {
            kv_[p->first] = p->second;
        }

        ASSERT_EQ(kv_.size(), old_kv_size + lnode.kv_.size());

        lnode.kv_.clear();

        Verify();
        lnode.Verify();
    }

    bool ShouldHave(const std::string& key) {
        return kv_.end() != kv_.find(key);
    }

    repli_timestamp_t NextTimestamp() {
        tstamp_counter_ ++;
        repli_timestamp_t ret;
        ret.time = tstamp_counter_;
        return ret;
    }

    // This only prints if we enable printing.
    void Print() {
        // loof::print(stdout, &sizer_, node_);
    }

    void Verify() {
        if (!kv_.empty()) {
            ASSERT_EQ(tstamp_counter_, reinterpret_cast<repli_timestamp_t *>(loof::get_at_offset(node_, node_->frontmost))->time);
        }

        // Of course, this will fail with rassert, not a gtest assertion.
        loof::validate(&sizer_, node_);
    }

public:
    block_size_t bs_;
    value_sizer_t<short_value_t> sizer_;
    loof_t *node_;

    int tstamp_counter_;

    std::map<std::string, std::string> kv_;

    DISABLE_COPYING(LoofTracker);
};

TEST(LoofTest, Offsets) {
    ASSERT_EQ(0, offsetof(loof_t, magic));
    ASSERT_EQ(4, offsetof(loof_t, num_pairs));
    ASSERT_EQ(6, offsetof(loof_t, live_size));
    ASSERT_EQ(8, offsetof(loof_t, frontmost));
    ASSERT_EQ(10, offsetof(loof_t, tstamp_cutpoint));
    ASSERT_EQ(12, offsetof(loof_t, pair_offsets));
    ASSERT_EQ(12, sizeof(loof_t));
}

TEST(LoofTest, Reinserts) {
    LoofTracker tracker;
    std::string v = "aa";
    std::string k = "key";
    for (; v[0] <= 'z'; ++v[0]) {
        v[1] = 'a';
        for (; v[1] <= 'z'; ++v[1]) {
            // printf("inserting %s\n", v.c_str());
            tracker.Insert(k, v);
        }
    }
}

TEST(LoofTest, TenInserts) {
    LoofTracker tracker;

    ASSERT_LT(loof::MANDATORY_TIMESTAMPS, 10);

    const int num_keys = 10;
    std::string ks[num_keys] = { "the_relatively_long_key_that_is_relatively_long,_eh?__or_even_longer",
                           "some_other_relatively_long_key_that_...whatever.",
                           "another_relatively_long_key",
                           "a_short_key",
                           "", /* an empty string key */
                           "grohl",
                           "cobain",
                           "reznor",
                           "marley",
                           "domino" };

    for (int i = 0; i < 26 * 26; ++i) {
        std::string v;
        v += ('a' + (i / 26));
        v += ('a' + (i % 26));

        for (int j = 0; j < num_keys; ++j) {
            tracker.Insert(ks[j], v);
        }
    }
}

TEST(LoofTest, InsertRemove) {
    LoofTracker tracker;

    srand(12345);

    const int num_keys = 10;
    std::string ks[num_keys] = { "the_relatively_long_key_that_is_relatively_long,_eh?__or_even_longer",
                           "some_other_relatively_long_key_that_...whatever.",
                           "another_relatively_long_key",
                           "a_short_key",
                           "", /* an empty string key */
                           "grohl",
                           "cobain",
                           "reznor",
                           "marley",
                           "domino" };

    for (int i = 0; i < 26 * 26; ++i) {
        std::string v;
        v += ('a' + (i / 26));
        v += ('a' + (i % 26));

        for (int j = 0; j < num_keys; ++j) {
            if (rand() % 2 == 1) {
                tracker.Insert(ks[j], v);
            } else {
                if (tracker.ShouldHave(ks[j])) {
                    tracker.Remove(ks[j]);
                }
            }
        }
    }
}

TEST(LoofTest, MinimalMerging) {
    LoofTracker left;
    LoofTracker right;

    left.Insert("a", "A");
    right.Insert("b", "B");

    right.Merge(left);
}

TEST(LoofTest, SimpleMerging) {

    LoofTracker left;
    LoofTracker right;

    // We use the largest value that will underflow.
    //
    // key_cost = 251, max_possible_size() = 256, sizeof(uint16_t) = 2, sizeof(repli_timestamp) = 4.
    //
    // 4084 - 12 = 4072.  4072 / 2 = 2036.  2036 - (251 + 256 + 2
    // + 4) = 2036 - 513 = 1523.  So 1522 is the max possible
    // mandatory_cost.  (See the is_underfull implementation.)
    //
    // With 5*4 mandatory timestamp bytes and 12 bytes per entry,
    // that gives us 1502 / 12 as the loop boundary value that
    // will underflow.  We get 12 byte entries if entries run from
    // a000 to a999.  But if we allow two-digit entries, that
    // frees up 2 bytes per entry, so add 200, giving 1702.  If we
    // allow one-digit entries, that gives us 20 more bytes to
    // use, giving 1722 / 12 as the loop boundary.  That's an odd
    // way to look at the arithmetic, but if you don't like that,
    // you can go cry to your mommy.

    for (int i = 0; i < 1722 / 12; ++i) {
        left.Insert(strprintf("a%d", i), strprintf("A%d", i));
        right.Insert(strprintf("b%d", i), strprintf("B%d", i));
    }

    right.Merge(left);
}

TEST(LoofTest, MergingWithRemoves) {
    LoofTracker left;
    LoofTracker right;

    for (int i = 0; i < 1000 / 12; ++i) {
        left.Insert(strprintf("a%d", i), strprintf("A%d", i));
        right.Insert(strprintf("b%d", i), strprintf("B%d", i));
        if (i % 5 == 0) {
            left.Remove(strprintf("a%d", i / 5));
            right.Remove(strprintf("b%d", i / 5));
        }
    }

    right.Merge(left);
}

}  // namespace unittest
<|endoftext|>


<commit_before>#include "appconfig.h"

#include <QApplication>
#include <QSettings>
#include <QDir>
#include <QFileInfo>
#include <QDebug>

static const QString STYLESHEET_PATH = ":/qss/app.qss";

QSettings& AppConfig::config() {
    static QSettings cfg(configFileName(), QSettings::IniFormat);
    return cfg;
}

QString AppConfig::configFileName() {
    return qApp->applicationDirPath() + QDir::separator() + "app.conf";
}

QString AppConfig::styleSheet() {
    const QString path = STYLESHEET_PATH;
    QFile file(path);
    if (!file.exists()) {
        qWarning() << "Stylesheet file not found: " << path;
        return "";
    }
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "unable to open stylesheet file: " << file.errorString();
        return "";
    }
    return QTextStream(&file).readAll();
}

QString AppConfig::ckReposPath() {
    return config().value("ck_repos_path").toString();
}

QString AppConfig::ckBinPath() {
    return config().value("ck_bin_path").toString();
}

QString AppConfig::ckExeName() {
    return config().value("ck_exe_name").toString();
}

int AppConfig::configValueInt(const QString& key, int defaultValue) {
    bool ok;
    int value = config().value(key, defaultValue).toInt(&ok);
    return ok? value: defaultValue;
}

QString AppConfig::configValueStr(const QString& key, const QString& defaultValue){
    auto value = config().value(key, defaultValue).toString();
    return value.isEmpty()? defaultValue: value;
}

QString AppConfig::localSubdir(const QString& key, const QString& defaultDir) {
    auto defaultPath = qApp->applicationDirPath() + QDir::separator() + defaultDir;
    auto path = configValueStr(key, defaultPath);
    QDir d(path);
    if (!d.exists()) {
        d.mkpath(path);
    }
    return d.absolutePath();
}

int AppConfig::sectionCount(const QString& sectionName) {
    return configValueInt(sectionName + "/count", 0);
}

QString AppConfig::sectionValue(const QString& sectionName, int index, const QString& suffix) {
    return configValueStr(sectionName + "/" + QString::number(index) + "_" + suffix, "");
}

bool isCompiled(const Program& p) {
    QFileInfo out(p.outputFile);
    QDir dir = out.absoluteDir();
    return dir.exists(p.exe);
}

QList<Program> AppConfig::programs() {
    int programCount = sectionCount("Programs");
    QList<Program> ret;
    for (int i = 0; i < programCount; ++i) {
        Program p;
        p.uoa = sectionValue("Programs", i, "uoa");
        p.name = sectionValue("Programs", i, "name");
        p.outputFile = sectionValue("Programs", i, "output_file");
        p.exe = sectionValue("Programs", i, "exe");
        if (isCompiled(p)) {
            ret.append(p);
        }
    }
    return ret;
}

QVariant AppConfig::currentProgram() {
    QString uoa = configValueStr("program_uoa", "");
    QList<Program> progs = programs();
    QVariant ret;
    for (auto i : progs) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.uoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentProgram(QString uoa) {
    config().setValue("program_uoa", uoa);
    config().sync();
}

QVariant AppConfig::currentSqueezeDetProgram() {
    static const QString& SECTION = "SqueezeDet";
    int programCount = sectionCount(SECTION);
    if (programCount < 1) {
        return QVariant();
    }
    Program p;
    p.uoa = sectionValue(SECTION, 0, "uoa");
    p.name = sectionValue(SECTION, 0, "name");
    p.outputFile = sectionValue(SECTION, 0, "output_file");
    p.exe = sectionValue(SECTION, 0, "exe");
    QVariant ret;
    ret.setValue(p);
    return ret;
}

QList<Model> AppConfig::models() {
    int count = sectionCount("Models");
    QList<Model> ret;
    for (int i = 0; i < count; ++i) {
        Model m;
        m.uoa = sectionValue("Models", i, "uoa");
        m.name = sectionValue("Models", i, "name");
        ret.append(m);
    }
    return ret;
}

QVariant AppConfig::currentModel() {
    QString uoa = configValueStr("model_uoa", "");
    QVariant ret;
    for (auto i : models()) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.uoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentModel(QString uoa) {
    config().setValue("model_uoa", uoa);
    config().sync();
}

QList<Dataset> AppConfig::datasets() {
    int valCount = sectionCount("VAL");
    int auxCount = sectionCount("AUX");
    QList<Dataset> ret;
    if (0 >= valCount || 0 >= auxCount) {
        return ret;
    }
    for (int i = 0; i < valCount; ++i) {
        Dataset m;
        m.valUoa = sectionValue("VAL", i, "uoa");
        m.valName = sectionValue("VAL", i, "name");

        QString auxPackageUoa = sectionValue("VAL", i, "aux_package_uoa");
        for (int j = 0; j < auxCount; ++j) {
            if (auxPackageUoa == sectionValue("AUX", j, "package_uoa")) {
                m.auxUoa = sectionValue("AUX", j, "uoa");
                m.auxName = sectionValue("AUX", j, "name");
                break;
            }
        }

        if (m.auxUoa.isEmpty()) {
            qWarning() << "AUX not found for VAL " + m.valName + " (" + m.valUoa + "), skipping";
        } else {
            ret.append(m);
        }
    }
    return ret;
}

QVariant AppConfig::currentDataset() {
    QString uoa = configValueStr("val_uoa", "");
    QVariant ret;
    for (auto i : datasets()) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.valUoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentDataset(QString uoa) {
    config().setValue("val_uoa", uoa);
    config().sync();
}

QList<Mode> AppConfig::modes() {
#ifdef Q_OS_WIN
    return QList<Mode>({ Mode(Mode::Type::CLASSIFICATION) });
#else
    return QList<Mode>({ Mode(Mode::Type::CLASSIFICATION), Mode(Mode::Type::RECOGNITION) });
#endif
}

QVariant AppConfig::currentMode() {
    Mode m(static_cast<Mode::Type>(configValueInt("demo_mode", Mode::Type::CLASSIFICATION)));
    QVariant ret;
    ret.setValue(Mode());
    for (auto m1 : modes()) {
        if (m.type == m1.type) {
            ret.setValue(m);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentMode(Mode::Type type) {
    for (auto m : modes()) {
        if (m.type == type) {
            config().setValue("demo_mode", type);
            config().sync();
            return;
        }
    }
}

int AppConfig::classificationStartupTimeoutSeconds() {
    return config().value("startup_timeout_seconds", 40).toInt();
}

qint64 AppConfig::fpsUpdateIntervalMs() {
    return config().value("fps_update_interval_ms", 500).toInt();
}

qint64 AppConfig::recognitionUpdateIntervalMs() {
    return config().value("recognition_update_interval_ms", 1000).toInt();
}

int AppConfig::batchSize() {
    int ret = config().value("batch_size", 1).toInt();
    if (0 >= ret) {
        qWarning() << "Non-positive batch size is specified in the config file. Defaulting to 1. Batch size from config: " << ret;
        ret = 1;
    }
    return ret;
}

void AppConfig::setBatchSize(int batchSize) {
    if (0 >= batchSize) {
        qWarning() << "Trying to set non-positive batch size, which is not allowed: " << batchSize;
        return;
    }
    config().setValue("batch_size", batchSize);
    config().sync();
}
<commit_msg>Enable object detection on Windows<commit_after>#include "appconfig.h"

#include <QApplication>
#include <QSettings>
#include <QDir>
#include <QFileInfo>
#include <QDebug>

static const QString STYLESHEET_PATH = ":/qss/app.qss";

QSettings& AppConfig::config() {
    static QSettings cfg(configFileName(), QSettings::IniFormat);
    return cfg;
}

QString AppConfig::configFileName() {
    return qApp->applicationDirPath() + QDir::separator() + "app.conf";
}

QString AppConfig::styleSheet() {
    const QString path = STYLESHEET_PATH;
    QFile file(path);
    if (!file.exists()) {
        qWarning() << "Stylesheet file not found: " << path;
        return "";
    }
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "unable to open stylesheet file: " << file.errorString();
        return "";
    }
    return QTextStream(&file).readAll();
}

QString AppConfig::ckReposPath() {
    return config().value("ck_repos_path").toString();
}

QString AppConfig::ckBinPath() {
    return config().value("ck_bin_path").toString();
}

QString AppConfig::ckExeName() {
    return config().value("ck_exe_name").toString();
}

int AppConfig::configValueInt(const QString& key, int defaultValue) {
    bool ok;
    int value = config().value(key, defaultValue).toInt(&ok);
    return ok? value: defaultValue;
}

QString AppConfig::configValueStr(const QString& key, const QString& defaultValue){
    auto value = config().value(key, defaultValue).toString();
    return value.isEmpty()? defaultValue: value;
}

QString AppConfig::localSubdir(const QString& key, const QString& defaultDir) {
    auto defaultPath = qApp->applicationDirPath() + QDir::separator() + defaultDir;
    auto path = configValueStr(key, defaultPath);
    QDir d(path);
    if (!d.exists()) {
        d.mkpath(path);
    }
    return d.absolutePath();
}

int AppConfig::sectionCount(const QString& sectionName) {
    return configValueInt(sectionName + "/count", 0);
}

QString AppConfig::sectionValue(const QString& sectionName, int index, const QString& suffix) {
    return configValueStr(sectionName + "/" + QString::number(index) + "_" + suffix, "");
}

bool isCompiled(const Program& p) {
    QFileInfo out(p.outputFile);
    QDir dir = out.absoluteDir();
    return dir.exists(p.exe);
}

QList<Program> AppConfig::programs() {
    int programCount = sectionCount("Programs");
    QList<Program> ret;
    for (int i = 0; i < programCount; ++i) {
        Program p;
        p.uoa = sectionValue("Programs", i, "uoa");
        p.name = sectionValue("Programs", i, "name");
        p.outputFile = sectionValue("Programs", i, "output_file");
        p.exe = sectionValue("Programs", i, "exe");
        if (isCompiled(p)) {
            ret.append(p);
        }
    }
    return ret;
}

QVariant AppConfig::currentProgram() {
    QString uoa = configValueStr("program_uoa", "");
    QList<Program> progs = programs();
    QVariant ret;
    for (auto i : progs) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.uoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentProgram(QString uoa) {
    config().setValue("program_uoa", uoa);
    config().sync();
}

QVariant AppConfig::currentSqueezeDetProgram() {
    static const QString& SECTION = "SqueezeDet";
    int programCount = sectionCount(SECTION);
    if (programCount < 1) {
        return QVariant();
    }
    Program p;
    p.uoa = sectionValue(SECTION, 0, "uoa");
    p.name = sectionValue(SECTION, 0, "name");
    p.outputFile = sectionValue(SECTION, 0, "output_file");
    p.exe = sectionValue(SECTION, 0, "exe");
    QVariant ret;
    ret.setValue(p);
    return ret;
}

QList<Model> AppConfig::models() {
    int count = sectionCount("Models");
    QList<Model> ret;
    for (int i = 0; i < count; ++i) {
        Model m;
        m.uoa = sectionValue("Models", i, "uoa");
        m.name = sectionValue("Models", i, "name");
        ret.append(m);
    }
    return ret;
}

QVariant AppConfig::currentModel() {
    QString uoa = configValueStr("model_uoa", "");
    QVariant ret;
    for (auto i : models()) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.uoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentModel(QString uoa) {
    config().setValue("model_uoa", uoa);
    config().sync();
}

QList<Dataset> AppConfig::datasets() {
    int valCount = sectionCount("VAL");
    int auxCount = sectionCount("AUX");
    QList<Dataset> ret;
    if (0 >= valCount || 0 >= auxCount) {
        return ret;
    }
    for (int i = 0; i < valCount; ++i) {
        Dataset m;
        m.valUoa = sectionValue("VAL", i, "uoa");
        m.valName = sectionValue("VAL", i, "name");

        QString auxPackageUoa = sectionValue("VAL", i, "aux_package_uoa");
        for (int j = 0; j < auxCount; ++j) {
            if (auxPackageUoa == sectionValue("AUX", j, "package_uoa")) {
                m.auxUoa = sectionValue("AUX", j, "uoa");
                m.auxName = sectionValue("AUX", j, "name");
                break;
            }
        }

        if (m.auxUoa.isEmpty()) {
            qWarning() << "AUX not found for VAL " + m.valName + " (" + m.valUoa + "), skipping";
        } else {
            ret.append(m);
        }
    }
    return ret;
}

QVariant AppConfig::currentDataset() {
    QString uoa = configValueStr("val_uoa", "");
    QVariant ret;
    for (auto i : datasets()) {
        if (!ret.isValid()) {
            ret.setValue(i);
        }
        if (i.valUoa == uoa) {
            ret.setValue(i);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentDataset(QString uoa) {
    config().setValue("val_uoa", uoa);
    config().sync();
}

QList<Mode> AppConfig::modes() {
    return QList<Mode>({ Mode(Mode::Type::CLASSIFICATION), Mode(Mode::Type::RECOGNITION) });
}

QVariant AppConfig::currentMode() {
    Mode m(static_cast<Mode::Type>(configValueInt("demo_mode", Mode::Type::CLASSIFICATION)));
    QVariant ret;
    ret.setValue(Mode());
    for (auto m1 : modes()) {
        if (m.type == m1.type) {
            ret.setValue(m);
            break;
        }
    }
    return ret;
}

void AppConfig::setCurrentMode(Mode::Type type) {
    for (auto m : modes()) {
        if (m.type == type) {
            config().setValue("demo_mode", type);
            config().sync();
            return;
        }
    }
}

int AppConfig::classificationStartupTimeoutSeconds() {
    return config().value("startup_timeout_seconds", 40).toInt();
}

qint64 AppConfig::fpsUpdateIntervalMs() {
    return config().value("fps_update_interval_ms", 500).toInt();
}

qint64 AppConfig::recognitionUpdateIntervalMs() {
    return config().value("recognition_update_interval_ms", 1000).toInt();
}

int AppConfig::batchSize() {
    int ret = config().value("batch_size", 1).toInt();
    if (0 >= ret) {
        qWarning() << "Non-positive batch size is specified in the config file. Defaulting to 1. Batch size from config: " << ret;
        ret = 1;
    }
    return ret;
}

void AppConfig::setBatchSize(int batchSize) {
    if (0 >= batchSize) {
        qWarning() << "Trying to set non-positive batch size, which is not allowed: " << batchSize;
        return;
    }
    config().setValue("batch_size", batchSize);
    config().sync();
}
<|endoftext|>


<commit_before>//===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include "llvm/Object/ELF.h"
#include "llvm/Support/ScopedPrinter.h"

#include "lldb/Utility/Log.h"
#include "lldb/Utility/ProcessInfo.h"
#include "lldb/Utility/Status.h"

#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/linux/Support.h"
#include "lldb/Utility/DataExtractor.h"

using namespace lldb;
using namespace lldb_private;

namespace {
enum class ProcessState {
  Unknown,
  DiskSleep,
  Paging,
  Running,
  Sleeping,
  TracedOrStopped,
  Zombie,
};
}

namespace lldb_private {
class ProcessLaunchInfo;
}

static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
                          ProcessState &State, ::pid_t &TracerPid) {
  auto BufferOrError = getProcFile(Pid, "status");
  if (!BufferOrError)
    return false;

  llvm::StringRef Rest = BufferOrError.get()->getBuffer();
  while(!Rest.empty()) {
    llvm::StringRef Line;
    std::tie(Line, Rest) = Rest.split('\n');

    if (Line.consume_front("Gid:")) {
      // Real, effective, saved set, and file system GIDs. Read the first two.
      Line = Line.ltrim();
      uint32_t RGid, EGid;
      Line.consumeInteger(10, RGid);
      Line = Line.ltrim();
      Line.consumeInteger(10, EGid);

      ProcessInfo.SetGroupID(RGid);
      ProcessInfo.SetEffectiveGroupID(EGid);
    } else if (Line.consume_front("Uid:")) {
      // Real, effective, saved set, and file system UIDs. Read the first two.
      Line = Line.ltrim();
      uint32_t RUid, EUid;
      Line.consumeInteger(10, RUid);
      Line = Line.ltrim();
      Line.consumeInteger(10, EUid);

      ProcessInfo.SetUserID(RUid);
      ProcessInfo.SetEffectiveUserID(EUid);
    } else if (Line.consume_front("PPid:")) {
      ::pid_t PPid;
      Line.ltrim().consumeInteger(10, PPid);
      ProcessInfo.SetParentProcessID(PPid);
    } else if (Line.consume_front("State:")) {
      char S = Line.ltrim().front();
      switch (S) {
      case 'R':
        State = ProcessState::Running;
        break;
      case 'S':
        State = ProcessState::Sleeping;
        break;
      case 'D':
        State = ProcessState::DiskSleep;
        break;
      case 'Z':
        State = ProcessState::Zombie;
        break;
      case 'T':
        State = ProcessState::TracedOrStopped;
        break;
      case 'W':
        State = ProcessState::Paging;
        break;
      }
    } else if (Line.consume_front("TracerPid:")) {
      Line = Line.ltrim();
      Line.consumeInteger(10, TracerPid);
    }
  }
  return true;
}

static bool IsDirNumeric(const char *dname) {
  for (; *dname; dname++) {
    if (!isdigit(*dname))
      return false;
  }
  return true;
}

static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {
  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);

  auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);
  if (!buffer_sp)
    return ArchSpec();

  uint8_t exe_class =
      llvm::object::getElfArchType(
          {buffer_sp->GetChars(), size_t(buffer_sp->GetByteSize())})
          .first;

  switch (exe_class) {
  case llvm::ELF::ELFCLASS32:
    return HostInfo::GetArchitecture(HostInfo::eArchKind32);
  case llvm::ELF::ELFCLASS64:
    return HostInfo::GetArchitecture(HostInfo::eArchKind64);
  default:
    LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);
    return ArchSpec();
  }
}

static bool GetProcessAndStatInfo(::pid_t pid,
                                  ProcessInstanceInfo &process_info,
                                  ProcessState &State, ::pid_t &tracerpid) {
  tracerpid = 0;
  process_info.Clear();

  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));

  // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
  llvm::SmallString<64> ProcExe;
  (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);
  std::string ExePath(PATH_MAX, '\0');

  ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
  if (len <= 0) {
    LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
             Status(errno, eErrorTypePOSIX));
    return false;
  }
  ExePath.resize(len);

  // If the binary has been deleted, the link name has " (deleted)" appended.
  // Remove if there.
  llvm::StringRef PathRef = ExePath;
  PathRef.consume_back(" (deleted)");

  process_info.SetArchitecture(GetELFProcessCPUType(PathRef));

  // Get the process environment.
  auto BufferOrError = getProcFile(pid, "environ");
  if (!BufferOrError)
    return false;
  std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);

  // Get the command line used to start the process.
  BufferOrError = getProcFile(pid, "cmdline");
  if (!BufferOrError)
    return false;
  std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);

  // Get User and Group IDs and get tracer pid.
  if (!GetStatusInfo(pid, process_info, State, tracerpid))
    return false;

  process_info.SetProcessID(pid);
  process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);

  llvm::StringRef Rest = Environ->getBuffer();
  while (!Rest.empty()) {
    llvm::StringRef Var;
    std::tie(Var, Rest) = Rest.split('\0');
    process_info.GetEnvironment().insert(Var);
  }

  llvm::StringRef Arg0;
  std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
  process_info.SetArg0(Arg0);
  while (!Rest.empty()) {
    llvm::StringRef Arg;
    std::tie(Arg, Rest) = Rest.split('\0');
    process_info.GetArguments().AppendArgument(Arg);
  }

  return true;
}

uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
                             ProcessInstanceInfoList &process_infos) {
  static const char procdir[] = "/proc/";

  DIR *dirproc = opendir(procdir);
  if (dirproc) {
    struct dirent *direntry = nullptr;
    const uid_t our_uid = getuid();
    const lldb::pid_t our_pid = getpid();
    bool all_users = match_info.GetMatchAllUsers();

    while ((direntry = readdir(dirproc)) != nullptr) {
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;

      lldb::pid_t pid = atoi(direntry->d_name);

      // Skip this process.
      if (pid == our_pid)
        continue;

      ::pid_t tracerpid;
      ProcessState State;
      ProcessInstanceInfo process_info;

      if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
        continue;

      // Skip if process is being debugged.
      if (tracerpid != 0)
        continue;

      if (State == ProcessState::Zombie)
        continue;

      // Check for user match if we're not matching all users and not running
      // as root.
      if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
        continue;

      if (match_info.Matches(process_info)) {
        process_infos.Append(process_info);
      }
    }

    closedir(dirproc);
  }

  return process_infos.GetSize();
}

bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
  bool tids_changed = false;
  static const char procdir[] = "/proc/";
  static const char taskdir[] = "/task/";
  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
  DIR *dirproc = opendir(process_task_dir.c_str());

  if (dirproc) {
    struct dirent *direntry = nullptr;
    while ((direntry = readdir(dirproc)) != nullptr) {
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;

      lldb::tid_t tid = atoi(direntry->d_name);
      TidMap::iterator it = tids_to_attach.find(tid);
      if (it == tids_to_attach.end()) {
        tids_to_attach.insert(TidPair(tid, false));
        tids_changed = true;
      }
    }
    closedir(dirproc);
  }

  return tids_changed;
}

bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
  ::pid_t tracerpid;
  ProcessState State;
  return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
}

Environment Host::GetEnvironment() { return Environment(environ); }

Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
  return Status("unimplemented");
}
<commit_msg>[lldb-server/android] Show more processes by relaxing some checks<commit_after>//===-- source/Host/linux/Host.cpp ------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>

#include "llvm/Object/ELF.h"
#include "llvm/Support/ScopedPrinter.h"

#include "lldb/Utility/Log.h"
#include "lldb/Utility/ProcessInfo.h"
#include "lldb/Utility/Status.h"

#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/linux/Support.h"
#include "lldb/Utility/DataExtractor.h"

using namespace lldb;
using namespace lldb_private;

namespace {
enum class ProcessState {
  Unknown,
  DiskSleep,
  Paging,
  Running,
  Sleeping,
  TracedOrStopped,
  Zombie,
};
}

namespace lldb_private {
class ProcessLaunchInfo;
}

static bool GetStatusInfo(::pid_t Pid, ProcessInstanceInfo &ProcessInfo,
                          ProcessState &State, ::pid_t &TracerPid) {
  auto BufferOrError = getProcFile(Pid, "status");
  if (!BufferOrError)
    return false;

  llvm::StringRef Rest = BufferOrError.get()->getBuffer();
  while(!Rest.empty()) {
    llvm::StringRef Line;
    std::tie(Line, Rest) = Rest.split('\n');

    if (Line.consume_front("Gid:")) {
      // Real, effective, saved set, and file system GIDs. Read the first two.
      Line = Line.ltrim();
      uint32_t RGid, EGid;
      Line.consumeInteger(10, RGid);
      Line = Line.ltrim();
      Line.consumeInteger(10, EGid);

      ProcessInfo.SetGroupID(RGid);
      ProcessInfo.SetEffectiveGroupID(EGid);
    } else if (Line.consume_front("Uid:")) {
      // Real, effective, saved set, and file system UIDs. Read the first two.
      Line = Line.ltrim();
      uint32_t RUid, EUid;
      Line.consumeInteger(10, RUid);
      Line = Line.ltrim();
      Line.consumeInteger(10, EUid);

      ProcessInfo.SetUserID(RUid);
      ProcessInfo.SetEffectiveUserID(EUid);
    } else if (Line.consume_front("PPid:")) {
      ::pid_t PPid;
      Line.ltrim().consumeInteger(10, PPid);
      ProcessInfo.SetParentProcessID(PPid);
    } else if (Line.consume_front("State:")) {
      char S = Line.ltrim().front();
      switch (S) {
      case 'R':
        State = ProcessState::Running;
        break;
      case 'S':
        State = ProcessState::Sleeping;
        break;
      case 'D':
        State = ProcessState::DiskSleep;
        break;
      case 'Z':
        State = ProcessState::Zombie;
        break;
      case 'T':
        State = ProcessState::TracedOrStopped;
        break;
      case 'W':
        State = ProcessState::Paging;
        break;
      }
    } else if (Line.consume_front("TracerPid:")) {
      Line = Line.ltrim();
      Line.consumeInteger(10, TracerPid);
    }
  }
  return true;
}

static bool IsDirNumeric(const char *dname) {
  for (; *dname; dname++) {
    if (!isdigit(*dname))
      return false;
  }
  return true;
}

static ArchSpec GetELFProcessCPUType(llvm::StringRef exe_path) {
  Log *log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_HOST);

  auto buffer_sp = FileSystem::Instance().CreateDataBuffer(exe_path, 0x20, 0);
  if (!buffer_sp)
    return ArchSpec();

  uint8_t exe_class =
      llvm::object::getElfArchType(
          {buffer_sp->GetChars(), size_t(buffer_sp->GetByteSize())})
          .first;

  switch (exe_class) {
  case llvm::ELF::ELFCLASS32:
    return HostInfo::GetArchitecture(HostInfo::eArchKind32);
  case llvm::ELF::ELFCLASS64:
    return HostInfo::GetArchitecture(HostInfo::eArchKind64);
  default:
    LLDB_LOG(log, "Unknown elf class ({0}) in file {1}", exe_class, exe_path);
    return ArchSpec();
  }
}

static void GetProcessArgs(::pid_t pid, ProcessInstanceInfo &process_info) {
  auto BufferOrError = getProcFile(pid, "cmdline");
  if (!BufferOrError)
    return;
  std::unique_ptr<llvm::MemoryBuffer> Cmdline = std::move(*BufferOrError);

  llvm::StringRef Arg0, Rest;
  std::tie(Arg0, Rest) = Cmdline->getBuffer().split('\0');
  process_info.SetArg0(Arg0);
  while (!Rest.empty()) {
    llvm::StringRef Arg;
    std::tie(Arg, Rest) = Rest.split('\0');
    process_info.GetArguments().AppendArgument(Arg);
  }
}

static void GetExePathAndArch(::pid_t pid, ProcessInstanceInfo &process_info) {
  Log *log(GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
  std::string ExePath(PATH_MAX, '\0');

  // We can't use getProcFile here because proc/[pid]/exe is a symbolic link.
  llvm::SmallString<64> ProcExe;
  (llvm::Twine("/proc/") + llvm::Twine(pid) + "/exe").toVector(ProcExe);

  ssize_t len = readlink(ProcExe.c_str(), &ExePath[0], PATH_MAX);
  if (len > 0) {
    ExePath.resize(len);
  } else {
    LLDB_LOG(log, "failed to read link exe link for {0}: {1}", pid,
             Status(errno, eErrorTypePOSIX));
    ExePath.resize(0);
  }
  // If the binary has been deleted, the link name has " (deleted)" appended.
  // Remove if there.
  llvm::StringRef PathRef = ExePath;
  PathRef.consume_back(" (deleted)");

  if (!PathRef.empty()) {
    process_info.GetExecutableFile().SetFile(PathRef, FileSpec::Style::native);
    process_info.SetArchitecture(GetELFProcessCPUType(PathRef));
  }
}

static void GetProcessEnviron(::pid_t pid, ProcessInstanceInfo &process_info) {
  // Get the process environment.
  auto BufferOrError = getProcFile(pid, "environ");
  if (!BufferOrError)
    return;

  std::unique_ptr<llvm::MemoryBuffer> Environ = std::move(*BufferOrError);
  llvm::StringRef Rest = Environ->getBuffer();
  while (!Rest.empty()) {
    llvm::StringRef Var;
    std::tie(Var, Rest) = Rest.split('\0');
    process_info.GetEnvironment().insert(Var);
  }
}

static bool GetProcessAndStatInfo(::pid_t pid,
                                  ProcessInstanceInfo &process_info,
                                  ProcessState &State, ::pid_t &tracerpid) {
  tracerpid = 0;
  process_info.Clear();

  process_info.SetProcessID(pid);

  GetExePathAndArch(pid, process_info);
  GetProcessArgs(pid, process_info);
  GetProcessEnviron(pid, process_info);

  // Get User and Group IDs and get tracer pid.
  if (!GetStatusInfo(pid, process_info, State, tracerpid))
    return false;

  return true;
}

uint32_t Host::FindProcesses(const ProcessInstanceInfoMatch &match_info,
                             ProcessInstanceInfoList &process_infos) {
  static const char procdir[] = "/proc/";

  DIR *dirproc = opendir(procdir);
  if (dirproc) {
    struct dirent *direntry = nullptr;
    const uid_t our_uid = getuid();
    const lldb::pid_t our_pid = getpid();
    bool all_users = match_info.GetMatchAllUsers();

    while ((direntry = readdir(dirproc)) != nullptr) {
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;

      lldb::pid_t pid = atoi(direntry->d_name);

      // Skip this process.
      if (pid == our_pid)
        continue;

      ::pid_t tracerpid;
      ProcessState State;
      ProcessInstanceInfo process_info;

      if (!GetProcessAndStatInfo(pid, process_info, State, tracerpid))
        continue;

      // Skip if process is being debugged.
      if (tracerpid != 0)
        continue;

      if (State == ProcessState::Zombie)
        continue;

      // Check for user match if we're not matching all users and not running
      // as root.
      if (!all_users && (our_uid != 0) && (process_info.GetUserID() != our_uid))
        continue;

      if (match_info.Matches(process_info)) {
        process_infos.Append(process_info);
      }
    }

    closedir(dirproc);
  }

  return process_infos.GetSize();
}

bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
  bool tids_changed = false;
  static const char procdir[] = "/proc/";
  static const char taskdir[] = "/task/";
  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
  DIR *dirproc = opendir(process_task_dir.c_str());

  if (dirproc) {
    struct dirent *direntry = nullptr;
    while ((direntry = readdir(dirproc)) != nullptr) {
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;

      lldb::tid_t tid = atoi(direntry->d_name);
      TidMap::iterator it = tids_to_attach.find(tid);
      if (it == tids_to_attach.end()) {
        tids_to_attach.insert(TidPair(tid, false));
        tids_changed = true;
      }
    }
    closedir(dirproc);
  }

  return tids_changed;
}

bool Host::GetProcessInfo(lldb::pid_t pid, ProcessInstanceInfo &process_info) {
  ::pid_t tracerpid;
  ProcessState State;
  return GetProcessAndStatInfo(pid, process_info, State, tracerpid);
}

Environment Host::GetEnvironment() { return Environment(environ); }

Status Host::ShellExpandArguments(ProcessLaunchInfo &launch_info) {
  return Status("unimplemented");
}
<|endoftext|>


<commit_before>/**
 * @author: Jeff Thompson
 * See COPYING for copyright and distribution information.
 */

#include <cstdlib>
#include <sstream>
#include <iostream>
#include <ndn-cpp/interest.hpp>
#include <ndn-cpp/data.hpp>
#include <ndn-cpp/transport/udp-transport.hpp>
#include <ndn-cpp/face.hpp>

using namespace std;
using namespace ndn;
using namespace ptr_lib;

class MyClosure : public Closure {
public:
  MyClosure()
  : gotContent_(false)
  {
  }

  virtual UpcallResult upcall(UpcallKind kind, UpcallInfo &upcallInfo)
  {
    if (kind == UPCALL_DATA || kind == UPCALL_DATA_UNVERIFIED) {
      gotContent_ = true;
      cout << "Got data packet with name " << upcallInfo.getData()->getName().to_uri() << endl;
      for (unsigned int i = 0; i < upcallInfo.getData()->getContent().size(); ++i)
        cout << upcallInfo.getData()->getContent()[i];
      cout << endl;

      return CLOSURE_RESULT_OK;
    }
    else
      return CLOSURE_RESULT_OK;
  }

  bool gotContent_;
};

int main(int argc, char** argv)
{
  try {
    MyClosure closure;
    Face face("E.hub.ndn.ucla.edu", 9695, shared_ptr<UdpTransport>(new UdpTransport()));
    face.expressInterest(Name("/ndn/ucla.edu/apps/ndn-js-test/hello.txt/level2/%FD%05%0B%16%7D%95%0E"), &closure);

    // The main event loop.
    while (!closure.gotContent_) {
      face.processEvents();
      // We need to sleep for a few milliseconds so we don't use 100% of the CPU.
      usleep(10000);
    }
  } catch (std::exception &e) {
    cout << "exception: " << e.what() << endl;
  }
  return 0;
}
<commit_msg>Use the simplified constructor for Face.<commit_after>/**
 * @author: Jeff Thompson
 * See COPYING for copyright and distribution information.
 */

#include <cstdlib>
#include <sstream>
#include <iostream>
#include <ndn-cpp/interest.hpp>
#include <ndn-cpp/data.hpp>
#include <ndn-cpp/face.hpp>

using namespace std;
using namespace ndn;
using namespace ptr_lib;

class MyClosure : public Closure {
public:
  MyClosure()
  : gotContent_(false)
  {
  }

  virtual UpcallResult upcall(UpcallKind kind, UpcallInfo &upcallInfo)
  {
    if (kind == UPCALL_DATA || kind == UPCALL_DATA_UNVERIFIED) {
      gotContent_ = true;
      cout << "Got data packet with name " << upcallInfo.getData()->getName().to_uri() << endl;
      for (unsigned int i = 0; i < upcallInfo.getData()->getContent().size(); ++i)
        cout << upcallInfo.getData()->getContent()[i];
      cout << endl;

      return CLOSURE_RESULT_OK;
    }
    else
      return CLOSURE_RESULT_OK;
  }

  bool gotContent_;
};

int main(int argc, char** argv)
{
  try {
    MyClosure closure;
    Face face("E.hub.ndn.ucla.edu");
    face.expressInterest(Name("/ndn/ucla.edu/apps/ndn-js-test/hello.txt/level2/%FD%05%0B%16%7D%95%0E"), &closure);

    // The main event loop.
    while (!closure.gotContent_) {
      face.processEvents();
      // We need to sleep for a few milliseconds so we don't use 100% of the CPU.
      usleep(10000);
    }
  } catch (std::exception &e) {
    cout << "exception: " << e.what() << endl;
  }
  return 0;
}
<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
#include <utility>

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>

#include "image.h"
#include "obj_loader.h"

namespace visionaray
{

typedef basic_triangle<3, float> triangle_type;

namespace detail
{

//-------------------------------------------------------------------------------------------------
// aabb of a list of triangles
//

aabb bounds(detail::triangle_list const& tris)
{
    aabb result( vec3(std::numeric_limits<float>::max()), -vec3(std::numeric_limits<float>::max()) );

    for (auto const& tri : tris)
    {
        auto v1 = tri.v1;
        auto v2 = tri.v1 + tri.e1;
        auto v3 = tri.v1 + tri.e2;

        result = combine(result, aabb(v1, v1));
        result = combine(result, aabb(v2, v2));
        result = combine(result, aabb(v3, v3));
    }

    return result;
}

std::pair<triangle_type, triangle_type> tesselate_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4)
{

    triangle_type t1;
    t1.v1 = v1;
    t1.e1 = v2 - t1.v1;
    t1.e2 = v3 - t1.v1;

    triangle_type t2;
    t2.v1 = v3;
    t2.e1 = v4 - t2.v1;
    t2.e2 = v1 - t2.v1;

    return std::make_pair(t1, t2);

}

} // detail

struct mtl
{
    vec3 ka;
    vec3 kd;
    vec3 ks;
    float ns;
    std::string map_kd;
};

void parse_mtl(std::string const& filename, std::map<std::string, mtl>* matlib)
{
    std::ifstream ifstr(filename.c_str(), std::ifstream::in);
    std::string line;
    std::map<std::string, mtl>::iterator it;

    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {
        boost::algorithm::trim(line);
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier == "newmtl")
        {
            std::string name;
            str >> name;
            auto mtl_pair = std::make_pair(name, mtl());
            matlib->insert(mtl_pair);
            it = matlib->find(name);
        }
        else if (identifier == "Ka")
        {
            str >> (*it).second.ka.x >> std::ws >> (*it).second.ka.y >> std::ws >> (*it).second.ka.z >> std::ws;
        }
        else if (identifier == "Kd")
        {
            str >> (*it).second.kd.x >> std::ws >> (*it).second.kd.y >> std::ws >> (*it).second.kd.z >> std::ws;
        }
        else if (identifier == "Ks")
        {
            str >> (*it).second.ks.x >> std::ws >> (*it).second.ks.y >> std::ws >> (*it).second.ks.z >> std::ws;
        }
        else if (identifier == "Ns")
        {
            str >> (*it).second.ns >> std::ws;
        }
        else if (identifier == "map_Kd")
        {
            str >> (*it).second.map_kd;
        }
    }
}

detail::obj_scene load_obj(std::string const& filename)
{
    std::map<std::string, mtl> matlib;

    std::ifstream ifstr(filename.c_str(), std::ifstream::in);

    unsigned prim_id = 0;
    unsigned geom_id = 0;

    using namespace detail;
    obj_scene result;
    std::vector<vec3> vertices;
    std::vector<vec2> tex_coords;

    std::string line;
    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {

        // comments

        if (line.length() > 0 && line[0] == '#')
        {
            continue;
        }

        // v, vn, vt, f, ...
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier.length() <= 0)
        {
            continue;
        }

        if (identifier == "mtllib")
        {
            boost::filesystem::path p(filename);
            std::string mtl_dir = p.parent_path().string();

            std::string mtl_file;
            str >> mtl_file >> std::ws;

            std::string mtl_path = mtl_dir + "/" + mtl_file;

            parse_mtl(mtl_path, &matlib);
        }

        if (identifier == "usemtl")
        {
            std::string mtl_name;
            str >> mtl_name >> std::ws;

            auto mat_it = matlib.find(mtl_name);
            if (mat_it != matlib.end())
            {
                phong<float> mat;
                mat.set_cd( mat_it->second.kd );
                mat.set_kd( 1.0f );
                mat.set_ks( 1.0f );
                mat.set_specular_exp( mat_it->second.ns );
                result.materials.push_back(mat);

                typedef tex_list::value_type tex_type;
                boost::filesystem::path p(filename);
                std::string tex_filename = p.parent_path().string() + "/" + mat_it->second.map_kd;

                static const std::string extensions[] = { ".jpg", ".jpeg", ".JPG", ".JPEG" };
                auto tex_path = boost::filesystem::path(tex_filename);
                auto has_jpg_ext = ( std::find(extensions, extensions + 4, tex_path.extension()) != extensions + 4 );

                if (!mat_it->second.map_kd.empty() && boost::filesystem::exists(tex_filename) && has_jpg_ext)
                {
#if defined(VSNRAY_HAVE_JPEG)
                    jpeg_image jpg(tex_filename);

                    tex_type tex(jpg.width(), jpg.height());

                    // TODO: we need textures with managed storage to fix this memory leak
                    tex_type::value_type* tmp = new tex_type::value_type[jpg.width() * jpg.height()];
                    auto eval_ptr = reinterpret_cast<tex_type::value_type const*>(jpg.data().data());
                    std::memcpy(tmp, eval_ptr, sizeof(tex_type::value_type) * tex.size());

                    tex.data = tmp;

                    result.textures.push_back(std::move(tex));
                    // TODO: here we leak tmp..
#endif
                }
                else
                {
                    result.textures.push_back(tex_type(0, 0));
                }
            }
            geom_id = result.materials.size() == 0 ? 0 : static_cast<unsigned>(result.materials.size() - 1);
        }

        if (identifier == "v")
        {
            vec3 v;
            str >> v[0] >> std::ws >> v[1] >> std::ws >> v[2] >> std::ws;
            vertices.push_back(v);
        }
        else if (identifier == "vn")
        {
            vec3 n;
            str >> n[0] >> std::ws >> n[1] >> std::ws >> n[2] >> std::ws;
            result.normals.push_back(n);
        }
        else if (identifier == "vt")
        {
            vec2 t;
            str >> t[0] >> std::ws >> t[1] >> std::ws;
            tex_coords.push_back(t);
        }
        else if (identifier == "f")
        {
            int cnt = 0;
            unsigned indices[4];
            unsigned tc_indices[4];

            bool has_tc = false;

            while (!str.eof())
            {
                int i;
                str >> i;

                // indices are either 1-based or negative
                if (i < 0)
                {
                    indices[cnt] = vertices.size() + i;
                }
                else
                {
                    indices[cnt] = i - 1;
                }

                if (str.get() == '/')
                {
                    if (str.peek() != '/')
                    {
                        has_tc = true;

                        int ti;
                        str >> ti;
                        tc_indices[cnt] = ti - 1;
                    }

                    if (str.get() == '/')
                    {
                        int ni;
                        str >> ni;
                        // TODO: handle normal indices
                    }
                }

                str >> std::ws;

                if (++cnt > 4)
                {
                    throw std::exception();
                    break;
                }
            }

            if (cnt == 4)
            {
                auto tris = detail::tesselate_quad(vertices[indices[0]], vertices[indices[1]], vertices[indices[2]], vertices[indices[3]]);

                tris.first.prim_id  = prim_id;
                tris.first.geom_id  = geom_id;
                ++prim_id;

                tris.second.prim_id = prim_id;
                tris.second.geom_id = geom_id;
                ++prim_id;

                result.primitives.push_back(tris.first);
                result.primitives.push_back(tris.second);

                if (has_tc)
                {
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[1]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);

                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[3]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                }
            }
            else
            {
                triangle_type tri;
                tri.prim_id = prim_id;
                tri.geom_id = geom_id;
                tri.v1 = vertices[indices[0]];
                tri.e1 = vertices[indices[1]] - tri.v1;
                tri.e2 = vertices[indices[2]] - tri.v1;
                result.primitives.push_back(tri);
                ++prim_id;

                if (has_tc)
                {
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[1]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);
                }
            }
        }
    }

// TODO

    result.normals.resize(0);
//    if (result.normals.size() == 0) // have no default normals
    {
        for (auto const& tri : result.primitives)
        {
            vec3 n = normalize( cross(tri.e1, tri.e2) );
            result.normals.push_back(n);
        }
    }

    if (result.materials.size() == 0)
    {
        for (unsigned i = 0; i <= geom_id; ++i)
        {
            phong<float> m;
            m.set_cd( vec3(0.8f, 0.8f, 0.8f) );
            m.set_kd( 1.0f );
            m.set_ks( 1.0f );
            m.set_specular_exp( 32.0f );
            result.materials.push_back(m);
        }
    }
// TODO

    result.bbox = bounds(result.primitives);
    return result;
}

} // visionaray

<commit_msg>Raw pointers and wild casts are evil...<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
#include <utility>

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>

#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>

#include "image.h"
#include "obj_loader.h"

namespace visionaray
{

typedef basic_triangle<3, float> triangle_type;

namespace detail
{

//-------------------------------------------------------------------------------------------------
// aabb of a list of triangles
//

aabb bounds(detail::triangle_list const& tris)
{
    aabb result( vec3(std::numeric_limits<float>::max()), -vec3(std::numeric_limits<float>::max()) );

    for (auto const& tri : tris)
    {
        auto v1 = tri.v1;
        auto v2 = tri.v1 + tri.e1;
        auto v3 = tri.v1 + tri.e2;

        result = combine(result, aabb(v1, v1));
        result = combine(result, aabb(v2, v2));
        result = combine(result, aabb(v3, v3));
    }

    return result;
}

std::pair<triangle_type, triangle_type> tesselate_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4)
{

    triangle_type t1;
    t1.v1 = v1;
    t1.e1 = v2 - t1.v1;
    t1.e2 = v3 - t1.v1;

    triangle_type t2;
    t2.v1 = v3;
    t2.e1 = v4 - t2.v1;
    t2.e2 = v1 - t2.v1;

    return std::make_pair(t1, t2);

}

} // detail

struct mtl
{
    vec3 ka;
    vec3 kd;
    vec3 ks;
    float ns;
    std::string map_kd;
};

void parse_mtl(std::string const& filename, std::map<std::string, mtl>* matlib)
{
    std::ifstream ifstr(filename.c_str(), std::ifstream::in);
    std::string line;
    std::map<std::string, mtl>::iterator it;

    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {
        boost::algorithm::trim(line);
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier == "newmtl")
        {
            std::string name;
            str >> name;
            auto mtl_pair = std::make_pair(name, mtl());
            matlib->insert(mtl_pair);
            it = matlib->find(name);
        }
        else if (identifier == "Ka")
        {
            str >> (*it).second.ka.x >> std::ws >> (*it).second.ka.y >> std::ws >> (*it).second.ka.z >> std::ws;
        }
        else if (identifier == "Kd")
        {
            str >> (*it).second.kd.x >> std::ws >> (*it).second.kd.y >> std::ws >> (*it).second.kd.z >> std::ws;
        }
        else if (identifier == "Ks")
        {
            str >> (*it).second.ks.x >> std::ws >> (*it).second.ks.y >> std::ws >> (*it).second.ks.z >> std::ws;
        }
        else if (identifier == "Ns")
        {
            str >> (*it).second.ns >> std::ws;
        }
        else if (identifier == "map_Kd")
        {
            str >> (*it).second.map_kd;
        }
    }
}

detail::obj_scene load_obj(std::string const& filename)
{
    std::map<std::string, mtl> matlib;

    std::ifstream ifstr(filename.c_str(), std::ifstream::in);

    unsigned prim_id = 0;
    unsigned geom_id = 0;

    using namespace detail;
    obj_scene result;
    std::vector<vec3> vertices;
    std::vector<vec2> tex_coords;

    std::string line;
    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {

        // comments

        if (line.length() > 0 && line[0] == '#')
        {
            continue;
        }

        // v, vn, vt, f, ...
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier.length() <= 0)
        {
            continue;
        }

        if (identifier == "mtllib")
        {
            boost::filesystem::path p(filename);
            std::string mtl_dir = p.parent_path().string();

            std::string mtl_file;
            str >> mtl_file >> std::ws;

            std::string mtl_path = mtl_dir + "/" + mtl_file;

            parse_mtl(mtl_path, &matlib);
        }

        if (identifier == "usemtl")
        {
            std::string mtl_name;
            str >> mtl_name >> std::ws;

            auto mat_it = matlib.find(mtl_name);
            if (mat_it != matlib.end())
            {
                phong<float> mat;
                mat.set_cd( mat_it->second.kd );
                mat.set_kd( 1.0f );
                mat.set_ks( 1.0f );
                mat.set_specular_exp( mat_it->second.ns );
                result.materials.push_back(mat);

                typedef tex_list::value_type tex_type;
                boost::filesystem::path p(filename);
                std::string tex_filename = p.parent_path().string() + "/" + mat_it->second.map_kd;

                static const std::string extensions[] = { ".jpg", ".jpeg", ".JPG", ".JPEG" };
                auto tex_path = boost::filesystem::path(tex_filename);
                auto has_jpg_ext = ( std::find(extensions, extensions + 4, tex_path.extension()) != extensions + 4 );

                if (!mat_it->second.map_kd.empty() && boost::filesystem::exists(tex_filename) && has_jpg_ext)
                {
#if defined(VSNRAY_HAVE_JPEG)
                    jpeg_image jpg(tex_filename);

                    tex_type tex(jpg.width(), jpg.height());

                    // TODO: we need textures with managed storage to fix this memory leak
                    tex_type::value_type* tmp = new tex_type::value_type[jpg.width() * jpg.height()];
                    auto evil_ptr = reinterpret_cast<tex_type::value_type const*>(jpg.data().data());
                    std::memcpy(tmp, evil_ptr, sizeof(tex_type::value_type) * tex.size());

                    tex.data = tmp;

                    result.textures.push_back(std::move(tex));
                    // TODO: here we leak tmp..
#endif
                }
                else
                {
                    result.textures.push_back(tex_type(0, 0));
                }
            }
            geom_id = result.materials.size() == 0 ? 0 : static_cast<unsigned>(result.materials.size() - 1);
        }

        if (identifier == "v")
        {
            vec3 v;
            str >> v[0] >> std::ws >> v[1] >> std::ws >> v[2] >> std::ws;
            vertices.push_back(v);
        }
        else if (identifier == "vn")
        {
            vec3 n;
            str >> n[0] >> std::ws >> n[1] >> std::ws >> n[2] >> std::ws;
            result.normals.push_back(n);
        }
        else if (identifier == "vt")
        {
            vec2 t;
            str >> t[0] >> std::ws >> t[1] >> std::ws;
            tex_coords.push_back(t);
        }
        else if (identifier == "f")
        {
            int cnt = 0;
            unsigned indices[4];
            unsigned tc_indices[4];

            bool has_tc = false;

            while (!str.eof())
            {
                int i;
                str >> i;

                // indices are either 1-based or negative
                if (i < 0)
                {
                    indices[cnt] = vertices.size() + i;
                }
                else
                {
                    indices[cnt] = i - 1;
                }

                if (str.get() == '/')
                {
                    if (str.peek() != '/')
                    {
                        has_tc = true;

                        int ti;
                        str >> ti;
                        tc_indices[cnt] = ti - 1;
                    }

                    if (str.get() == '/')
                    {
                        int ni;
                        str >> ni;
                        // TODO: handle normal indices
                    }
                }

                str >> std::ws;

                if (++cnt > 4)
                {
                    throw std::exception();
                    break;
                }
            }

            if (cnt == 4)
            {
                auto tris = detail::tesselate_quad(vertices[indices[0]], vertices[indices[1]], vertices[indices[2]], vertices[indices[3]]);

                tris.first.prim_id  = prim_id;
                tris.first.geom_id  = geom_id;
                ++prim_id;

                tris.second.prim_id = prim_id;
                tris.second.geom_id = geom_id;
                ++prim_id;

                result.primitives.push_back(tris.first);
                result.primitives.push_back(tris.second);

                if (has_tc)
                {
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[1]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);

                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[3]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                }
            }
            else
            {
                triangle_type tri;
                tri.prim_id = prim_id;
                tri.geom_id = geom_id;
                tri.v1 = vertices[indices[0]];
                tri.e1 = vertices[indices[1]] - tri.v1;
                tri.e2 = vertices[indices[2]] - tri.v1;
                result.primitives.push_back(tri);
                ++prim_id;

                if (has_tc)
                {
                    result.tex_coords.push_back(tex_coords[tc_indices[0]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[1]]);
                    result.tex_coords.push_back(tex_coords[tc_indices[2]]);
                }
            }
        }
    }

// TODO

    result.normals.resize(0);
//    if (result.normals.size() == 0) // have no default normals
    {
        for (auto const& tri : result.primitives)
        {
            vec3 n = normalize( cross(tri.e1, tri.e2) );
            result.normals.push_back(n);
        }
    }

    if (result.materials.size() == 0)
    {
        for (unsigned i = 0; i <= geom_id; ++i)
        {
            phong<float> m;
            m.set_cd( vec3(0.8f, 0.8f, 0.8f) );
            m.set_kd( 1.0f );
            m.set_ks( 1.0f );
            m.set_specular_exp( 32.0f );
            result.materials.push_back(m);
        }
    }
// TODO

    result.bbox = bounds(result.primitives);
    return result;
}

} // visionaray

<|endoftext|>


<commit_before>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <memory>
#include <sstream>
#include <utility>

#include <boost/algorithm/string.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>

#include <visionaray/detail/macros.h>
#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>

#include "image.h"
#include "obj_loader.h"

namespace qi = boost::spirit::qi;

//-------------------------------------------------------------------------------------------------
// boost::fusion-adapt some structs for parsing
//

namespace visionaray
{
namespace detail
{

struct face_index_t
{
    int vertex_index;
    boost::optional<int> tex_coord_index;
    boost::optional<int> normal_index;
};

} // detail
} // visionaray

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::detail::face_index_t,
    (int, vertex_index)
    (boost::optional<int>, tex_coord_index)
    (boost::optional<int>, normal_index)
)

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::vec2,
    (float, x)
    (float, y)
)

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::vec3,
    (float, x)
    (float, y)
    (float, z)
)

using boost::string_ref;

namespace boost
{
namespace spirit
{
namespace traits
{

template <typename Iterator, typename Enable>
struct assign_to_attribute_from_iterators<string_ref, Iterator, Enable>
{
    static void call(Iterator const& first, Iterator const& last, string_ref& attr)
    {
        attr = { first, static_cast<size_t>(last - first) };
    }
};

} // traits
} // spirit
}

namespace visionaray
{

typedef basic_triangle<3, float> triangle_type;

namespace detail
{

//-------------------------------------------------------------------------------------------------
// aabb of a list of triangles
//

aabb bounds(detail::triangle_list const& tris)
{
    aabb result( vec3(std::numeric_limits<float>::max()), -vec3(std::numeric_limits<float>::max()) );

    for (auto const& tri : tris)
    {
        auto v1 = tri.v1;
        auto v2 = tri.v1 + tri.e1;
        auto v3 = tri.v1 + tri.e2;

        result = combine(result, aabb(v1, v1));
        result = combine(result, aabb(v2, v2));
        result = combine(result, aabb(v3, v3));
    }

    return result;
}

} // detail

struct mtl
{
    vec3 ka;
    vec3 kd;
    vec3 ks;
    float ns;
    std::string map_kd;
};

void parse_mtl(std::string const& filename, std::map<std::string, mtl>* matlib)
{
    std::ifstream ifstr(filename.c_str(), std::ifstream::in);
    std::string line;
    std::map<std::string, mtl>::iterator it;

    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {
        boost::algorithm::trim(line);
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier == "newmtl")
        {
            std::string name;
            str >> name;
            auto mtl_pair = std::make_pair(name, mtl());
            matlib->insert(mtl_pair);
            it = matlib->find(name);
        }
        else if (identifier == "Ka")
        {
            str >> (*it).second.ka.x >> std::ws >> (*it).second.ka.y >> std::ws >> (*it).second.ka.z >> std::ws;
        }
        else if (identifier == "Kd")
        {
            str >> (*it).second.kd.x >> std::ws >> (*it).second.kd.y >> std::ws >> (*it).second.kd.z >> std::ws;
        }
        else if (identifier == "Ks")
        {
            str >> (*it).second.ks.x >> std::ws >> (*it).second.ks.y >> std::ws >> (*it).second.ks.z >> std::ws;
        }
        else if (identifier == "Ns")
        {
            str >> (*it).second.ns >> std::ws;
        }
        else if (identifier == "map_Kd")
        {
            str >> (*it).second.map_kd;
        }
    }
}

detail::obj_scene load_obj(std::string const& filename)
{
    std::map<std::string, mtl> matlib;

    std::ifstream ifstr(filename.c_str(), std::ifstream::in);
    boost::iostreams::mapped_file_source file(filename);

    unsigned geom_id = 0;

    using namespace detail;

    obj_scene result;

    using vertex_vector     = aligned_vector<vec3>;
    using tex_coord_vector  = aligned_vector<vec2>;
    using normal_vector     = aligned_vector<vec3>;
    using face_vector       = aligned_vector<face_index_t>;

    vertex_vector       vertices;
    tex_coord_vector    tex_coords;
    normal_vector       normals;
    face_vector         faces;

    // intermediate containers

    string_ref comment;
    string_ref mtl_file;
    string_ref mtl_name;

    // helper functions

    auto remap_index = [](int idx, int size) -> int
    {
        return idx < 0 ? static_cast<int>(size) + idx : idx - 1;
    };

    auto store_triangle = [&](int i1, int i2, int i3)
    {
        triangle_type tri;
        tri.prim_id = static_cast<unsigned>(result.primitives.size());
        tri.geom_id = geom_id;
        tri.v1 = vertices[i1];
        tri.e1 = vertices[i2] - tri.v1;
        tri.e2 = vertices[i3] - tri.v1;
        result.primitives.push_back(tri);
    };

    auto store_faces = [&](int vertices_size, int tex_coords_size, int normals_size)
    {
        size_t last = 2;
        auto i1 = remap_index(faces[0].vertex_index, vertices_size);

        while (last != faces.size())
        {
            // triangle
            auto i2 = remap_index(faces[last - 1].vertex_index, vertices_size);
            auto i3 = remap_index(faces[last].vertex_index, vertices_size);
            store_triangle(i1, i2, i3);

            // texture coordinates
            if (faces[0].tex_coord_index && faces[last - 1].tex_coord_index && faces[last].tex_coord_index)
            {
                auto ti1 = remap_index(*faces[0].tex_coord_index, tex_coords_size);
                auto ti2 = remap_index(*faces[last - 1].tex_coord_index, tex_coords_size);
                auto ti3 = remap_index(*faces[last].tex_coord_index, tex_coords_size);

                result.tex_coords.push_back( tex_coords[ti1] );
                result.tex_coords.push_back( tex_coords[ti2] );
                result.tex_coords.push_back( tex_coords[ti3] );
            }

            // normals
            if (faces[0].normal_index && faces[last - 1].normal_index && faces[last].normal_index)
            {
                auto ni1 = remap_index(*faces[0].normal_index, normals_size);
                auto ni2 = remap_index(*faces[last - 1].normal_index, normals_size);
                auto ni3 = remap_index(*faces[last].normal_index, normals_size);

                result.normals.push_back( normals[ni1] );
                result.normals.push_back( normals[ni2] );
                result.normals.push_back( normals[ni3] );
            }

            ++last;
        }
    };

    // obj grammar

    using It = string_ref::const_iterator;
    using space_type = decltype(qi::blank);

    qi::rule<It> r_unhandled                                    = *(qi::char_ - qi::eol)                                                    >> qi::eol;
    qi::rule<It, string_ref()> r_text_to_eol                    = qi::raw[*(qi::char_ - qi::eol)];

    qi::rule<It, string_ref()> r_comment                        = "#" >> r_text_to_eol                                                      >> qi::eol;
    qi::rule<It, string_ref(), space_type> r_mtllib             = "mtllib" >> r_text_to_eol                                                 >> qi::eol;
    qi::rule<It, string_ref(), space_type> r_usemtl             = "usemtl" >> r_text_to_eol                                                 >> qi::eol;

    qi::rule<It, vec3(), space_type> r_v                        = "v" >> qi::float_ >> qi::float_ >> qi::float_ >> -qi::float_              >> qi::eol  // TODO: mind w
                                                                | "v" >> qi::float_ >> qi::float_ >> qi::float_
                                                                      >> qi::float_ >> qi::float_ >> qi::float_                             >> qi::eol; // RGB color (extension)
    qi::rule<It, vec2(), space_type> r_vt                       = "vt" >> qi::float_ >> qi::float_ >> -qi::float_                           >> qi::eol; // TODO: mind w
    qi::rule<It, vec3(), space_type> r_vn                       = "vn" >> qi::float_ >> qi::float_ >> qi::float_                            >> qi::eol;

    qi::rule<It, vertex_vector(), space_type> r_vertices        = r_v >> *r_v;
    qi::rule<It, tex_coord_vector(), space_type> r_tex_coords   = r_vt >> *r_vt;
    qi::rule<It, normal_vector(), space_type> r_normals         = r_vn >> *r_vn;

    qi::rule<It, detail::face_index_t()> r_face_vertex          = qi::int_ >> -qi::lit("/") >> -qi::int_ >> -qi::lit("/") >> -qi::int_;
    qi::rule<It, face_vector(), space_type> r_face              = "f" >> r_face_vertex >> r_face_vertex >> r_face_vertex >> *r_face_vertex  >> qi::eol;

    string_ref text(file.data(), file.size());
    auto it = text.cbegin();

    while (it != text.cend())
    {
        faces.clear();

        if ( qi::phrase_parse(it, text.cend(), r_comment, qi::blank, comment) )
        {
            VSNRAY_UNUSED(comment);
            continue;
        }
        else if ( qi::phrase_parse(it, text.cend(), r_mtllib, qi::blank, mtl_file) )
        {
            boost::filesystem::path p(filename);
            std::string mtl_dir = p.parent_path().string();

            std::string mtl_path = mtl_dir + "/" + std::string(mtl_file);

            parse_mtl(mtl_path, &matlib);
        }
        else if ( qi::phrase_parse(it, text.cend(), r_usemtl, qi::blank, mtl_name) )
        {
            auto mat_it = matlib.find(std::string(mtl_name));
            if (mat_it != matlib.end())
            {
                phong<float> mat;
                mat.set_cd( mat_it->second.kd );
                mat.set_kd( 1.0f );
                mat.set_ks( 1.0f );
                mat.set_specular_exp( mat_it->second.ns );
                result.materials.push_back(mat);

                typedef tex_list::value_type tex_type;
                boost::filesystem::path p(filename);
                std::string tex_filename = p.parent_path().string() + "/" + mat_it->second.map_kd;

                static const std::string extensions[] = { ".jpg", ".jpeg", ".JPG", ".JPEG" };
                auto tex_path = boost::filesystem::path(tex_filename);
                auto has_jpg_ext = ( std::find(extensions, extensions + 4, tex_path.extension()) != extensions + 4 );

                if (!mat_it->second.map_kd.empty() && boost::filesystem::exists(tex_filename) && has_jpg_ext)
                {
#if defined(VSNRAY_HAVE_JPEG)
                    jpeg_image jpg(tex_filename);

                    tex_type tex(jpg.width(), jpg.height());
                    tex.set_address_mode( Clamp );
                    tex.set_filter_mode( Linear );

                    auto data_ptr = reinterpret_cast<tex_type::value_type const*>(jpg.data());
                    tex.set_data(data_ptr);

                    result.textures.push_back(std::move(tex));
#endif
                }
                else
                {
                    result.textures.push_back(tex_type(0, 0));
                }
            }
            geom_id = result.materials.size() == 0 ? 0 : static_cast<unsigned>(result.materials.size() - 1);
        }
        else if ( qi::phrase_parse(it, text.cend(), r_vertices, qi::blank, vertices) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_tex_coords, qi::blank, tex_coords) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_normals, qi::blank, normals) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_face, qi::blank, faces) )
        {
            store_faces(static_cast<int>(vertices.size()), static_cast<int>(tex_coords.size()), static_cast<int>(normals.size()));
        }
        else if ( qi::phrase_parse(it, text.cend(), r_unhandled, qi::blank) )
        {
            continue;
        }
    }

// TODO

    result.normals.resize(0); // TODO: support for vertex normals
    if (result.normals.size() == 0) // have no default normals
    {
        for (auto const& tri : result.primitives)
        {
            vec3 n = normalize( cross(tri.e1, tri.e2) );
            result.normals.push_back(n);
        }
    }

    if (result.materials.size() == 0)
    {
        for (unsigned i = 0; i <= geom_id; ++i)
        {
            phong<float> m;
            m.set_cd( vec3(0.8f, 0.8f, 0.8f) );
            m.set_kd( 1.0f );
            m.set_ks( 1.0f );
            m.set_specular_exp( 32.0f );
            result.materials.push_back(m);
        }
    }
// TODO

    result.bbox = bounds(result.primitives);
    return result;
}

} // visionaray
<commit_msg>Code cleanup<commit_after>// This file is distributed under the MIT license.
// See the LICENSE file for details.

#include <algorithm>
#include <cstddef>
#include <cstring>
#include <exception>
#include <fstream>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>
#include <utility>

#include <boost/algorithm/string.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/utility/string_ref.hpp>
#include <boost/filesystem.hpp>
#include <boost/optional.hpp>

#include <visionaray/math/math.h>
#include <visionaray/texture/texture.h>

#include "image.h"
#include "obj_loader.h"

namespace qi = boost::spirit::qi;

//-------------------------------------------------------------------------------------------------
// boost::fusion-adapt some structs for parsing
//

namespace visionaray
{

struct face_index_t
{
    int vertex_index;
    boost::optional<int> tex_coord_index;
    boost::optional<int> normal_index;
};

} // visionaray

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::face_index_t,
    (int, vertex_index)
    (boost::optional<int>, tex_coord_index)
    (boost::optional<int>, normal_index)
)

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::vec2,
    (float, x)
    (float, y)
)

BOOST_FUSION_ADAPT_STRUCT
(
    visionaray::vec3,
    (float, x)
    (float, y)
    (float, z)
)

using boost::string_ref;

namespace boost
{
namespace spirit
{
namespace traits
{

template <typename Iterator, typename Enable>
struct assign_to_attribute_from_iterators<string_ref, Iterator, Enable>
{
    static void call(Iterator const& first, Iterator const& last, string_ref& attr)
    {
        attr = { first, static_cast<size_t>(last - first) };
    }
};

} // traits
} // spirit
} // boost

namespace visionaray
{

using triangle_type     = basic_triangle<3, float>;
using vertex_vector     = aligned_vector<vec3>;
using tex_coord_vector  = aligned_vector<vec2>;
using normal_vector     = aligned_vector<vec3>;
using face_vector       = aligned_vector<face_index_t>;

//-------------------------------------------------------------------------------------------------
// Map obj indices to unsigned base-0 indices
//

template <typename Int>
Int remap_index(Int idx, Int size)
{
    return idx < 0 ? size + idx : idx - 1;
};

//-------------------------------------------------------------------------------------------------
// Store a triangle and assign visionaray-internal ids
//

void store_triangle(detail::obj_scene& result, vertex_vector const& vertices, int i1, int i2, int i3)
{
    triangle_type tri;
    tri.prim_id = static_cast<unsigned>(result.primitives.size());
    tri.geom_id = result.materials.size() == 0 ? 0 : static_cast<unsigned>(result.materials.size() - 1);
    tri.v1 = vertices[i1];
    tri.e1 = vertices[i2] - tri.v1;
    tri.e2 = vertices[i3] - tri.v1;
    result.primitives.push_back(tri);
};

//-------------------------------------------------------------------------------------------------
// Store obj faces (i.e. triangle fans) in vertex|tex_coords|normals lists
//

void store_faces(detail::obj_scene& result, vertex_vector const& vertices,
    tex_coord_vector const& tex_coords, normal_vector const& normals, face_vector const& faces)
{

    auto vertices_size = static_cast<int>(vertices.size());
    size_t last = 2;
    auto i1 = remap_index(faces[0].vertex_index, vertices_size);

    while (last != faces.size())
    {
        // triangle
        auto i2 = remap_index(faces[last - 1].vertex_index, vertices_size);
        auto i3 = remap_index(faces[last].vertex_index, vertices_size);
        store_triangle(result, vertices, i1, i2, i3);

        // texture coordinates
        if (faces[0].tex_coord_index && faces[last - 1].tex_coord_index && faces[last].tex_coord_index)
        {
            auto tex_coords_size = static_cast<int>(tex_coords.size());
            auto ti1 = remap_index(*faces[0].tex_coord_index, tex_coords_size);
            auto ti2 = remap_index(*faces[last - 1].tex_coord_index, tex_coords_size);
            auto ti3 = remap_index(*faces[last].tex_coord_index, tex_coords_size);

            result.tex_coords.push_back( tex_coords[ti1] );
            result.tex_coords.push_back( tex_coords[ti2] );
            result.tex_coords.push_back( tex_coords[ti3] );
        }

        // normals
        if (faces[0].normal_index && faces[last - 1].normal_index && faces[last].normal_index)
        {
            auto normals_size = static_cast<int>(normals.size());
            auto ni1 = remap_index(*faces[0].normal_index, normals_size);
            auto ni2 = remap_index(*faces[last - 1].normal_index, normals_size);
            auto ni3 = remap_index(*faces[last].normal_index, normals_size);

            result.normals.push_back( normals[ni1] );
            result.normals.push_back( normals[ni2] );
            result.normals.push_back( normals[ni3] );
        }

        ++last;
    }
};

//-------------------------------------------------------------------------------------------------
// aabb of a list of triangles
//

aabb bounds(detail::triangle_list const& tris)
{
    aabb result( vec3(std::numeric_limits<float>::max()), -vec3(std::numeric_limits<float>::max()) );

    for (auto const& tri : tris)
    {
        auto v1 = tri.v1;
        auto v2 = tri.v1 + tri.e1;
        auto v3 = tri.v1 + tri.e2;

        result = combine(result, aabb(v1, v1));
        result = combine(result, aabb(v2, v2));
        result = combine(result, aabb(v3, v3));
    }

    return result;
}

struct mtl
{
    vec3 ka;
    vec3 kd;
    vec3 ks;
    float ns;
    std::string map_kd;
};

void parse_mtl(std::string const& filename, std::map<std::string, mtl>* matlib)
{
    std::ifstream ifstr(filename.c_str(), std::ifstream::in);
    std::string line;
    std::map<std::string, mtl>::iterator it;

    while (ifstr.good() && !ifstr.eof() && std::getline(ifstr, line))
    {
        boost::algorithm::trim(line);
        std::string identifier;
        std::istringstream str(line);
        str >> identifier >> std::ws;

        if (identifier == "newmtl")
        {
            std::string name;
            str >> name;
            auto mtl_pair = std::make_pair(name, mtl());
            matlib->insert(mtl_pair);
            it = matlib->find(name);
        }
        else if (identifier == "Ka")
        {
            str >> (*it).second.ka.x >> std::ws >> (*it).second.ka.y >> std::ws >> (*it).second.ka.z >> std::ws;
        }
        else if (identifier == "Kd")
        {
            str >> (*it).second.kd.x >> std::ws >> (*it).second.kd.y >> std::ws >> (*it).second.kd.z >> std::ws;
        }
        else if (identifier == "Ks")
        {
            str >> (*it).second.ks.x >> std::ws >> (*it).second.ks.y >> std::ws >> (*it).second.ks.z >> std::ws;
        }
        else if (identifier == "Ns")
        {
            str >> (*it).second.ns >> std::ws;
        }
        else if (identifier == "map_Kd")
        {
            str >> (*it).second.map_kd;
        }
    }
}

detail::obj_scene load_obj(std::string const& filename)
{
    detail::obj_scene result;

    std::map<std::string, mtl> matlib;

    boost::iostreams::mapped_file_source file(filename);

    size_t geom_id = 0;

    // obj grammar

    using It = string_ref::const_iterator;
    using skip_t = decltype(qi::blank);

    qi::rule<It> r_unhandled                                = *(qi::char_ - qi::eol)                                                    >> qi::eol;
    qi::rule<It, string_ref()> r_text_to_eol                = qi::raw[*(qi::char_ - qi::eol)];

    qi::rule<It, string_ref()> r_comment                    = "#" >> r_text_to_eol                                                      >> qi::eol;
    qi::rule<It, string_ref(), skip_t> r_mtllib             = "mtllib" >> r_text_to_eol                                                 >> qi::eol;
    qi::rule<It, string_ref(), skip_t> r_usemtl             = "usemtl" >> r_text_to_eol                                                 >> qi::eol;

    qi::rule<It, vec3(), skip_t> r_v                        = "v" >> qi::float_ >> qi::float_ >> qi::float_ >> -qi::float_              >> qi::eol  // TODO: mind w
                                                            | "v" >> qi::float_ >> qi::float_ >> qi::float_
                                                                  >> qi::float_ >> qi::float_ >> qi::float_                             >> qi::eol; // RGB color (extension)
    qi::rule<It, vec2(), skip_t> r_vt                       = "vt" >> qi::float_ >> qi::float_ >> -qi::float_                           >> qi::eol; // TODO: mind w
    qi::rule<It, vec3(), skip_t> r_vn                       = "vn" >> qi::float_ >> qi::float_ >> qi::float_                            >> qi::eol;

    qi::rule<It, vertex_vector(), skip_t> r_vertices        = r_v >> *r_v;
    qi::rule<It, tex_coord_vector(), skip_t> r_tex_coords   = r_vt >> *r_vt;
    qi::rule<It, normal_vector(), skip_t> r_normals         = r_vn >> *r_vn;

    qi::rule<It, face_index_t()> r_face_vertex                  = qi::int_ >> -qi::lit("/") >> -qi::int_ >> -qi::lit("/") >> -qi::int_;
    qi::rule<It, face_vector(), skip_t> r_face              = "f" >> r_face_vertex >> r_face_vertex >> r_face_vertex >> *r_face_vertex  >> qi::eol;

    string_ref text(file.data(), file.size());
    auto it = text.cbegin();

    // containers for parsing

    vertex_vector       vertices;
    tex_coord_vector    tex_coords;
    normal_vector       normals;
    face_vector         faces;

    string_ref comment;
    string_ref mtl_file;
    string_ref mtl_name;

    while (it != text.cend())
    {
        faces.clear();

        if ( qi::phrase_parse(it, text.cend(), r_comment, qi::blank, comment) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_mtllib, qi::blank, mtl_file) )
        {
            boost::filesystem::path p(filename);
            std::string mtl_dir = p.parent_path().string();

            std::string mtl_path = mtl_dir + "/" + std::string(mtl_file);

            parse_mtl(mtl_path, &matlib);
        }
        else if ( qi::phrase_parse(it, text.cend(), r_usemtl, qi::blank, mtl_name) )
        {
            auto mat_it = matlib.find(std::string(mtl_name));
            if (mat_it != matlib.end())
            {
                phong<float> mat;
                mat.set_cd( mat_it->second.kd );
                mat.set_kd( 1.0f );
                mat.set_ks( 1.0f );
                mat.set_specular_exp( mat_it->second.ns );
                result.materials.push_back(mat);

                typedef detail::tex_list::value_type tex_type;
                boost::filesystem::path p(filename);
                std::string tex_filename = p.parent_path().string() + "/" + mat_it->second.map_kd;

                static const std::string extensions[] = { ".jpg", ".jpeg", ".JPG", ".JPEG" };
                auto tex_path = boost::filesystem::path(tex_filename);
                auto has_jpg_ext = ( std::find(extensions, extensions + 4, tex_path.extension()) != extensions + 4 );

                if (!mat_it->second.map_kd.empty() && boost::filesystem::exists(tex_filename) && has_jpg_ext)
                {
#if defined(VSNRAY_HAVE_JPEG)
                    jpeg_image jpg(tex_filename);

                    tex_type tex(jpg.width(), jpg.height());
                    tex.set_address_mode( Clamp );
                    tex.set_filter_mode( Linear );

                    auto data_ptr = reinterpret_cast<tex_type::value_type const*>(jpg.data());
                    tex.set_data(data_ptr);

                    result.textures.push_back(std::move(tex));
#endif
                }
                else
                {
                    result.textures.push_back(tex_type(0, 0));
                }
            }
            geom_id = result.materials.size() == 0 ? 0 : result.materials.size() - 1;
        }
        else if ( qi::phrase_parse(it, text.cend(), r_vertices, qi::blank, vertices) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_tex_coords, qi::blank, tex_coords) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_normals, qi::blank, normals) )
        {
        }
        else if ( qi::phrase_parse(it, text.cend(), r_face, qi::blank, faces) )
        {
            store_faces(result, vertices, tex_coords, normals, faces);
        }
        else if ( qi::phrase_parse(it, text.cend(), r_unhandled, qi::blank) )
        {
        }
    }

// TODO

    result.normals.resize(0); // TODO: support for vertex normals
    if (result.normals.size() == 0) // have no default normals
    {
        for (auto const& tri : result.primitives)
        {
            vec3 n = normalize( cross(tri.e1, tri.e2) );
            result.normals.push_back(n);
        }
    }

    if (result.materials.size() == 0)
    {
        for (size_t i = 0; i <= geom_id; ++i)
        {
            phong<float> m;
            m.set_cd( vec3(0.8f, 0.8f, 0.8f) );
            m.set_kd( 1.0f );
            m.set_ks( 1.0f );
            m.set_specular_exp( 32.0f );
            result.materials.push_back(m);
        }
    }
// TODO

    result.bbox = bounds(result.primitives);
    return result;
}

} // visionaray
<|endoftext|>