<commit_before>#include <iostream>
#include <map>
#include <memory>
#include <vector>
#include <utility>

#include <rematch/compile.h>
#include <rematch/execute.h>
#include <rematch/rematch.h>

#include "db_setup.h"
#include "../Rule.h"

using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::vector;

using regexbench::Rule;

static void usage()
{
  cerr << "arguments too few" << endl;
  cerr << "command line should look like :" << endl;
  cerr << "$ pcre_checker {db_file}" << endl;
}

int main(int argc, char **argv)
{
  if (argc < 2) {
    usage();
    return -1;
  }

  string dbFile(argv[1]);
  cout << "db file " << dbFile << endl;

  try {
    dbFile = "database=" + dbFile;
    PcreCheckDb db("sqlite3", dbFile);
    // db.verbose = true; // turn on when debugging

    if (db.needsUpgrade()) // TODO : revisit!!
      db.upgrade();

    db.begin();

    vector<Rule> rules; // to be used for engine eval
    vector<DbRule> dbRules = select<DbRule>(db).orderBy(DbRule::Id).all();
    for (const auto &dbRule : dbRules) {
      auto blob = dbRule.content.value();
      size_t len = blob.length();
      auto temp = std::make_unique<char[]>(len);
      blob.getData(reinterpret_cast<unsigned char*>(temp.get()), len, 0);
      std::string line(temp.get(), len);
      rules.emplace_back(Rule(line, static_cast<size_t>(dbRule.id.value())));
    }
    // for debugging
    for (const auto &r : rules) {
      cout << "rule " << r.getID() << ": " << r.getRegexp() << endl;
    }

    int engineId;
    int resMatchId =
        select<Result>(db, Result::Name == "match").one().id.value();
    int resNomatchId =
        select<Result>(db, Result::Name == "nomatch").one().id.value();

    int resErrorId =
        select<Result>(db, Result::Name == "error").one().id.value();

    // rematch test first (TODO)
    engineId = select<Engine>(db, Engine::Name == "rematch").one().id.value();
    rematch2_t* matcher;
    rematch_match_context_t* context;
    vector<const char*> rematchExps;
    vector<unsigned> rematchMods;
    vector<unsigned> rematchIds;
    for (const auto& rule : rules) {
      rematchExps.push_back(rule.getRegexp().data());
      rematchIds.push_back(static_cast<unsigned>(rule.getID()));
      uint32_t opt = 0;
      if (rule.isSet(regexbench::MOD_CASELESS))
        opt |= REMATCH_MOD_CASELESS;
      if (rule.isSet(regexbench::MOD_MULTILINE))
        opt |= REMATCH_MOD_MULTILINE;
      if (rule.isSet(regexbench::MOD_DOTALL))
        opt |= REMATCH_MOD_DOTALL;
      rematchMods.push_back(opt);
    }
    matcher = rematch2_compile(rematchIds.data(), rematchExps.data(),
                            rematchMods.data(), rematchIds.size(),
                            true /* reduce */);
    if (!matcher)
      throw std::runtime_error("Could not build REmatch2 matcher.");
    context = rematch2ContextInit(matcher, 10); // nmatch = 10 (TODO)
    if (context == nullptr)
      throw std::runtime_error("Could not initialize context.");

    // prepare data (only need the data specified in Test table)
    int lastPid = -1;
    vector<Test> tests = select<Test>(db).orderBy(Test::Patternid).all();
    for (const auto &t : tests) {
      if (t.patternid.value() == lastPid)
        continue;
      lastPid = t.patternid.value();
      // we can get excption below
      // (which should not happen with a db correctly set up)
      const auto& pattern = select<Pattern>(db, Pattern::Id == lastPid).one();
      auto blob = pattern.content.value();
      size_t len = blob.length();
      auto temp = std::make_unique<char[]>(len);
      blob.getData(reinterpret_cast<unsigned char*>(temp.get()), len, 0);

      // for debugging
      //cout << "pattern " << lastPid << " content : " << string(temp.get(), len)
      //     << endl;

      // set up Rule-id to (Test-id, result) mapping to be used for TestResult update
      std::map<int, std::pair<int, bool>> rule2TestMap;
      auto curTest = select<Test>(db, Test::Patternid == lastPid).cursor();
      for (; curTest.rowsLeft(); curTest++) {
        rule2TestMap[(*curTest).ruleid.value()] =
            std::make_pair((*curTest).id.value(), false);
      }

      // do match
      int ret = rematch2_exec(matcher, temp.get(), len, context);
      if (ret == MREG_FINISHED) { // this means we need to adjust 'nmatch'
                                  // parameter used for rematch2ContextInit
        cerr << "rematch2 returned MREG_FINISHED" << endl;
      }
      if (context->num_matches > 0) { // match
        // for debugging
        //cout << "pattern " << lastPid;
        //cout << " matched rules :" << endl;
        for (size_t i = 0; i < context->num_matches; ++i) {
          // for debugging
          //cout << " " << context->matchlist[i].fid;
          stateid_t mid = context->matchlist[i].fid;
          if (rule2TestMap.count(static_cast<int>(mid)) > 0)
            rule2TestMap[mid].second = true;
        }
        //cout << endl;
      } else { // nomatch
        cout << "pattern " << lastPid << " has no match" << endl;
      }
      rematch2ContextClear(context, true);

      // for debugging
      //cout << "Matched rule and test id for pattern id " << lastPid << endl;
      //for (const auto& p : rule2TestMap) {
      //  cout << " rule id " << p.first << " test id " << p.second.first
      //       << " matched? " << p.second.second << endl;
      //}
      for (const auto& p : rule2TestMap) {
        try {
          auto cur =
              select<TestResult>(db, TestResult::Testid == p.second.first &&
                                         TestResult::Engineid == engineId)
                  .cursor();
          (*cur).resultid = (p.second.second ? resMatchId : resNomatchId);
          (*cur).update();
        } catch (NotFound) {
          TestResult res(db);
          res.testid = p.second.first;
          res.engineid = engineId;
          res.resultid = (p.second.second ? resMatchId : resNomatchId);
          res.update();
        }
      }
    } // for loop for Test table entries

    // clean-up of REmatch related objects
    rematch2ContextFree(context);
    rematch2Free(matcher);

    db.commit(); // commit changes (mostly about result)

  } catch (const std::exception &e) {
    cerr << e.what() << endl;
    return -1;
  } catch (Except& e) { // litesql exception
    cerr << e << endl;
    return -1;
  }
  return 0;
}

<commit_msg>Add support for hyperscan check<commit_after>#include <iostream>
#include <map>
#include <memory>
#include <vector>
#include <utility>

#include <rematch/compile.h>
#include <rematch/execute.h>
#include <rematch/rematch.h>
#include <hs/hs_compile.h>
#include <hs/hs_runtime.h>

#include "db_setup.h"
#include "../Rule.h"

using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::vector;

using regexbench::Rule;

static void usage()
{
  cerr << "arguments too few" << endl;
  cerr << "command line should look like :" << endl;
  cerr << "$ pcre_checker {db_file}" << endl;
}

struct AuxInfo {
  int resMatchId;
  int resNomatchId;
  int resErrorId;
  std::map<string, int> str2EngineId;
  uint32_t nmatch; // this is rematch only parameter

  vector<Rule> rules;
};

static void checkRematch(PcreCheckDb& db, const struct AuxInfo& aux);
static void checkHyperscan(PcreCheckDb& db, struct AuxInfo& aux);
//static void checkPcre(PcreCheckDb& db, struct AuxInfo& aux);

int main(int argc, char **argv)
{
  if (argc < 2) {
    usage();
    return -1;
  }

  string dbFile(argv[1]);

  try {
    dbFile = "database=" + dbFile;
    PcreCheckDb db("sqlite3", dbFile);
    // db.verbose = true; // turn on when debugging

    if (db.needsUpgrade()) // TODO : revisit!!
      db.upgrade();

    db.begin();

    AuxInfo aux;
    aux.resMatchId =
        select<Result>(db, Result::Name == "match").one().id.value();
    aux.resNomatchId =
        select<Result>(db, Result::Name == "nomatch").one().id.value();
    aux.resErrorId =
        select<Result>(db, Result::Name == "error").one().id.value();
    aux.str2EngineId["rematch"] =
        select<Engine>(db, Engine::Name == "rematch").one().id.value();
    aux.str2EngineId["hyperscan"] =
        select<Engine>(db, Engine::Name == "hyperscan").one().id.value();
    aux.str2EngineId["pcre"] =
        select<Engine>(db, Engine::Name == "pcre").one().id.value();
    aux.nmatch = 10; // TODO
    auto& rules = aux.rules;
    vector<DbRule> dbRules = select<DbRule>(db).orderBy(DbRule::Id).all();
    for (const auto &dbRule : dbRules) {
      auto blob = dbRule.content.value();
      size_t len = blob.length();
      auto temp = std::make_unique<char[]>(len);
      blob.getData(reinterpret_cast<unsigned char*>(temp.get()), len, 0);
      std::string line(temp.get(), len);
      rules.emplace_back(Rule(line, static_cast<size_t>(dbRule.id.value())));
    }
    // for debugging
    //for (const auto &r : rules) {
    //  cout << "rule " << r.getID() << ": " << r.getRegexp() << endl;
    //}

    checkRematch(db, aux);
    checkHyperscan(db, aux);

    db.commit(); // commit changes (mostly about result)

  } catch (const std::exception &e) {
    cerr << e.what() << endl;
    return -1;
  } catch (Except& e) { // litesql exception
    cerr << e << endl;
    return -1;
  }
  return 0;
}

void checkRematch(PcreCheckDb& db, const struct AuxInfo& aux)
{
  int engineId = aux.str2EngineId.at("rematch");
  int resMatchId = aux.resMatchId;
  int resNomatchId = aux.resNomatchId;
  const auto& rules = aux.rules;

  rematch2_t* matcher;
  rematch_match_context_t* context;
  vector<const char*> rematchExps;
  vector<unsigned> rematchMods;
  vector<unsigned> rematchIds;
  for (const auto& rule : rules) {
    rematchExps.push_back(rule.getRegexp().data());
    rematchIds.push_back(static_cast<unsigned>(rule.getID()));
    uint32_t opt = 0;
    if (rule.isSet(regexbench::MOD_CASELESS))
      opt |= REMATCH_MOD_CASELESS;
    if (rule.isSet(regexbench::MOD_MULTILINE))
      opt |= REMATCH_MOD_MULTILINE;
    if (rule.isSet(regexbench::MOD_DOTALL))
      opt |= REMATCH_MOD_DOTALL;
    rematchMods.push_back(opt);
  }
  matcher = rematch2_compile(rematchIds.data(), rematchExps.data(),
                             rematchMods.data(), rematchIds.size(),
                             true /* reduce */);
  if (!matcher)
    throw std::runtime_error("Could not build REmatch2 matcher.");
  context = rematch2ContextInit(matcher, aux.nmatch);
  if (context == nullptr)
    throw std::runtime_error("Could not initialize context.");

  // prepare data (only need the data specified in Test table)
  int lastPid = -1;
  vector<Test> tests = select<Test>(db).orderBy(Test::Patternid).all();
  for (const auto& t : tests) {
    if (t.patternid.value() == lastPid)
      continue;
    lastPid = t.patternid.value();
    // we can get excption below
    // (which should not happen with a db correctly set up)
    const auto& pattern = select<Pattern>(db, Pattern::Id == lastPid).one();
    auto blob = pattern.content.value();
    size_t len = blob.length();
    auto temp = std::make_unique<char[]>(len);
    blob.getData(reinterpret_cast<unsigned char*>(temp.get()), len, 0);

    // for debugging
    // cout << "pattern " << lastPid << " content : " << string(temp.get(), len)
    //     << endl;

    // set up Rule-id to (Test-id, result) mapping to be used for TestResult
    // update
    std::map<int, std::pair<int, bool>> rule2TestMap;
    auto curTest = select<Test>(db, Test::Patternid == lastPid).cursor();
    for (; curTest.rowsLeft(); curTest++) {
      rule2TestMap[(*curTest).ruleid.value()] =
          std::make_pair((*curTest).id.value(), false);
    }

    // do match
    int ret = rematch2_exec(matcher, temp.get(), len, context);
    if (ret == MREG_FINISHED) { // this means we need to adjust 'nmatch'
                                // parameter used for rematch2ContextInit
      cerr << "rematch2 returned MREG_FINISHED" << endl;
    }
    if (context->num_matches > 0) { // match
      // for debugging
      // cout << "pattern " << lastPid;
      // cout << " matched rules :" << endl;
      for (size_t i = 0; i < context->num_matches; ++i) {
        // for debugging
        // cout << " " << context->matchlist[i].fid;
        stateid_t mid = context->matchlist[i].fid;
        if (rule2TestMap.count(static_cast<int>(mid)) > 0)
          rule2TestMap[static_cast<int>(mid)].second = true;
      }
      // cout << endl;
    } else { // nomatch
      cout << "pattern " << lastPid << " has no match" << endl;
    }
    rematch2ContextClear(context, true);

    // for debugging
    // cout << "Matched rule and test id for pattern id " << lastPid << endl;
    // for (const auto& p : rule2TestMap) {
    //  cout << " rule id " << p.first << " test id " << p.second.first
    //       << " matched? " << p.second.second << endl;
    //}
    for (const auto& p : rule2TestMap) {
      try {
        auto cur =
            select<TestResult>(db, TestResult::Testid == p.second.first &&
                                       TestResult::Engineid == engineId)
                .cursor();
        (*cur).resultid = (p.second.second ? resMatchId : resNomatchId);
        (*cur).update();
      } catch (NotFound) {
        TestResult res(db);
        res.testid = p.second.first;
        res.engineid = engineId;
        res.resultid = (p.second.second ? resMatchId : resNomatchId);
        res.update();
      }
    }
  } // for loop for Test table entries

  // clean-up of REmatch related objects
  rematch2ContextFree(context);
  rematch2Free(matcher);
}

static int hsOnMatch(unsigned int, unsigned long long, unsigned long long,
                   unsigned int, void* ctx)
{
  size_t& nmatches = *static_cast<size_t*>(ctx);
  nmatches++;
  return 0;
}

void checkHyperscan(PcreCheckDb& db, struct AuxInfo& aux)
{
  int engineId = aux.str2EngineId.at("hyperscan");
  int resMatchId = aux.resMatchId;
  int resNomatchId = aux.resNomatchId;
  int resErrorId = aux.resErrorId;
  const auto& rules = aux.rules;

  hs_database_t* hsDb = nullptr;
  hs_scratch_t* hsScratch = nullptr;
  //hs_platform_info_t hsPlatform;
  hs_compile_error_t* hsErr = nullptr;

  auto cur = select<Test>(db).orderBy(Test::Ruleid).cursor();
  if (!cur.rowsLeft()) // nothing to do
    return;

  // map of Test-id to Result-id
  std::map<int, int> test2ResMap;

  // Entering into this loop, please make sure that
  // rules and cur are all sorted w.r.t rule id
  // (only, cur can have multiple occurrences of rule id's)
  // and also rules be super set of the iteration cur points to
  // in terms of containing rule id's.
  // Below outer and inner loop is assuming the above constraints
  // to prevent multiple rule compile for a same rule
  for (const auto& rule : rules) {
    if (!cur.rowsLeft())
      break;
    if (rule.getID() != (*cur).ruleid.value())
      continue;

    unsigned flag = HS_FLAG_ALLOWEMPTY;
    if (rule.isSet(regexbench::MOD_CASELESS))
      flag |= HS_FLAG_CASELESS;
    if (rule.isSet(regexbench::MOD_MULTILINE))
      flag |= HS_FLAG_MULTILINE;
    if (rule.isSet(regexbench::MOD_DOTALL))
      flag |= HS_FLAG_DOTALL;

    hsDb = nullptr;
    hsScratch = nullptr;
    hsErr = nullptr;
    auto resCompile = hs_compile(rule.getRegexp().data(), flag, HS_MODE_BLOCK,
                             nullptr, &hsDb, &hsErr);
    if (resCompile == HS_SUCCESS) {
      auto resAlloc = hs_alloc_scratch(hsDb, &hsScratch);
      if (resAlloc != HS_SUCCESS) {
        hs_free_database(hsDb);
        throw std::bad_alloc();
      }
    } else
      hs_free_compile_error(hsErr);

    for (; cur.rowsLeft() && rule.getID() == (*cur).ruleid.value(); cur++) {

      if (resCompile != HS_SUCCESS) {
        test2ResMap[(*cur).id.value()] = resErrorId;
        continue;
      }

      const auto& pattern =
          select<Pattern>(db, Pattern::Id == (*cur).patternid).one();
      auto blob = pattern.content.value();
      size_t len = blob.length();
      auto temp = std::make_unique<char[]>(len);
      blob.getData(reinterpret_cast<unsigned char*>(temp.get()), len, 0);

      size_t nmatches = 0;
      hs_scan(hsDb, temp.get(), static_cast<unsigned>(len), 0, hsScratch,
              hsOnMatch, &nmatches);
      if (nmatches)
        test2ResMap[(*cur).id.value()] =
            (nmatches > 0) ? resMatchId : resNomatchId;

    }

    hs_free_scratch(hsScratch);
    hs_free_database(hsDb);

  }

  //cout << "Hyper scan result" << endl << endl;
  for (const auto& p : test2ResMap) {
    try {
      auto curT = select<TestResult>(db, TestResult::Testid == p.first &&
                                            TestResult::Engineid == engineId)
                     .cursor();
      (*curT).resultid = p.second;
      (*curT).update();
    } catch (NotFound) {
      TestResult res(db);
      res.testid = p.first;
      res.engineid = engineId;
      res.resultid = p.second;
      res.update();
    }
    // for debugging
    //const auto& test = select<Test>(db, Test::Id == p.first).one();
    //cout << "test " << test.id.value() << " (rule id " << test.ruleid.value()
    //     << ", pattern id " << test.patternid.value()
    //     << ") => result : " << p.second << endl;
  }
}

<|endoftext|>


<commit_before>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "EC_DynamicComponent.h"
#include "IModule.h"
#include "ModuleManager.h"
#include "Entity.h"
#include "LoggingFunctions.h"

#include <QScriptEngine>
#include <QScriptValueIterator>

DEFINE_POCO_LOGGING_FUNCTIONS("EC_DynamicComponent")

#include <QDomDocument>

namespace
{
    struct DeserializeData
    {
        DeserializeData(const std::string name = std::string(""),
                        const std::string type = std::string(""),
                        const std::string value = std::string("")):
            name_(name),
            type_(type),
            value_(value)
        {
        }

        //! Checks if any of data structure's values are null.
        bool isNull() const
        {
            return name_ == "" || type_ == "" || value_ == "";
        }

        std::string name_;
        std::string type_;
        std::string value_;
    };

    //! Function that is used by std::sort algorithm to sort attributes by their name.
    bool CmpAttributeByName(const IAttribute *a, const IAttribute *b)
    {
        return a->GetNameString() < b->GetNameString();
    }

    //! Function that is used by std::sort algorithm to sort DeserializeData by their name.
    bool CmpAttributeDataByName(const DeserializeData &a, const DeserializeData &b)
    {
        return a.name_ < b.name_;
    }
}

EC_DynamicComponent::EC_DynamicComponent(IModule *module):
    IComponent(module->GetFramework())
{
}

EC_DynamicComponent::~EC_DynamicComponent()
{
    foreach(IAttribute *a, attributes_)
        SAFE_DELETE(a);
}

void EC_DynamicComponent::SerializeTo(QDomDocument& doc, QDomElement& base_element) const
{
    QDomElement comp_element = BeginSerialization(doc, base_element);

    AttributeVector::const_iterator iter = attributes_.begin();
    while(iter != attributes_.end())
    {
        WriteAttribute(doc, comp_element, (*iter)->GetNameString().c_str(), (*iter)->ToString().c_str(), (*iter)->TypenameToString().c_str());
        iter++;
    }
}

void EC_DynamicComponent::DeserializeFrom(QDomElement& element, AttributeChange::Type change)
{
    if (!BeginDeserialization(element))
        return;

    std::vector<DeserializeData> deserializedAttributes;
    QDomElement child = element.firstChildElement("attribute");
    while(!child.isNull())
    {
        QString name = child.attribute("name");
        QString type = child.attribute("type");
        QString value = child.attribute("value");
        DeserializeData attributeData(name.toStdString(), type.toStdString(), value.toStdString());
        deserializedAttributes.push_back(attributeData);

        child = child.nextSiblingElement("attribute");
    }

    // Sort both lists in alphabetical order.
    AttributeVector oldAttributes = attributes_;
    std::stable_sort(oldAttributes.begin(), oldAttributes.end(), &CmpAttributeByName);
    std::stable_sort(deserializedAttributes.begin(), deserializedAttributes.end(), &CmpAttributeDataByName);

    std::vector<DeserializeData> addAttributes;
    std::vector<DeserializeData> remAttributes;
    AttributeVector::iterator iter1 = oldAttributes.begin();
    std::vector<DeserializeData>::iterator iter2 = deserializedAttributes.begin();
    // Check what attributes we need to add or remove from the dynamic component (done by comparing two list differences).
    while(iter1 != oldAttributes.end() || iter2 != deserializedAttributes.end())
    {
        // No point to continue the iteration if other list is empty. We can just push all new attributes into the dynamic component.
        if(iter1 == oldAttributes.end())
        {
            for(;iter2 != deserializedAttributes.end(); iter2++)
            {
                addAttributes.push_back(*iter2);
            }
            break;
        }
        // Only old attributes are left and they can be removed from the dynamic component.
        else if(iter2 == deserializedAttributes.end())
        {
            for(;iter1 != oldAttributes.end(); iter1++)
                remAttributes.push_back(DeserializeData((*iter1)->GetNameString().c_str()));
            break;
        }

        // Attribute has already created and we only need to update it's value.
        if((*iter1)->GetNameString() == (*iter2).name_)
        {
            SetAttribute(QString::fromStdString(iter2->name_), QString::fromStdString(iter2->value_), change);
            iter2++;
            iter1++;
        }
        // Found a new attribute that need to be created and added to the component.
        else if((*iter1)->GetNameString() > (*iter2).name_)
        {
            addAttributes.push_back(*iter2);
            iter2++;
        }
        // Couldn't find the attribute in a new list so it need to be removed from the component.
        else
        {
            remAttributes.push_back(DeserializeData((*iter1)->GetNameString().c_str()));
            iter1++;
        }
    }

    while(!addAttributes.empty())
    {
        DeserializeData attributeData = addAttributes.back();
        IAttribute *attribute = CreateAttribute(attributeData.type_.c_str(), attributeData.name_.c_str());
        if (attribute)
            attribute->FromString(attributeData.value_, change);
        addAttributes.pop_back();
    }
    while(!remAttributes.empty())
    {
        DeserializeData attributeData = remAttributes.back();
        RemoveAttribute(QString::fromStdString(attributeData.name_));
        remAttributes.pop_back();
    }
}

IAttribute *EC_DynamicComponent::CreateAttribute(const QString &typeName, const QString &name, AttributeChange::Type change)
{
    IAttribute *attribute = 0;
    if(ContainsAttribute(name))
        return attribute;
    attribute = framework_->GetComponentManager()->CreateAttribute(this, typeName.toStdString(), name.toStdString());
    if(attribute)
        emit AttributeAdded(name);

    AttributeChanged(attribute, change);

    return attribute;
}

void EC_DynamicComponent::RemoveAttribute(const QString &name, AttributeChange::Type change)
{
    for(AttributeVector::iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            //! /todo Make sure that component removal is replicated to the server if change type is Replicate.
            AttributeChanged(*iter, change);
            SAFE_DELETE(*iter);
            attributes_.erase(iter);
            emit AttributeRemoved(name);
            break;
        }
    }
}

void EC_DynamicComponent::AddQVariantAttribute(const QString &name, AttributeChange::Type change)
{
    //Check if the attribute has already been created.
    if(!ContainsAttribute(name))
    {
        Attribute<QVariant> *attribute = new Attribute<QVariant>(this, name.toStdString().c_str());
        AttributeChanged(attribute, change);
        emit AttributeAdded(name);
    }
    LogWarning("Failed to add a new QVariant in name of " + name.toStdString() + ", cause there already is an attribute in that name.");
}

QVariant EC_DynamicComponent::GetAttribute(int index) const
{
    if(index < attributes_.size() && index >= 0)
    {
        return attributes_[index]->ToQVariant();
    }
    return QVariant();
}

QVariant EC_DynamicComponent::GetAttribute(const QString &name) const
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); ++iter)
        if ((*iter)->GetNameString() == name.toStdString())
            return (*iter)->ToQVariant();

    return QVariant();
}

void EC_DynamicComponent::SetAttribute(int index, const QVariant &value, AttributeChange::Type change)
{
    if(index < attributes_.size() && index >= 0)
    {
        attributes_[index]->FromQVariant(value, change);
    }
    LogWarning("Cannot get attribute name, cause index is out of range.");
}

void EC_DynamicComponent::SetAttributeQScript(const QString &name, const QScriptValue &value, AttributeChange::Type change)
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            (*iter)->FromScriptValue(value, change);
            break;
        }
    }
}

void EC_DynamicComponent::SetAttribute(const QString &name, const QVariant &value, AttributeChange::Type change)
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            (*iter)->FromQVariant(value, change);
            break;
        }
    }
}

QString EC_DynamicComponent::GetAttributeName(int index) const
{
    if(index < attributes_.size() && index >= 0)
    {
        return attributes_[index]->GetName();
    }
    LogWarning("Cannot get attribute name, cause index is out of range.");
    return QString();
}

bool EC_DynamicComponent::ContainSameAttributes(const EC_DynamicComponent &comp) const
{
    AttributeVector myAttributeVector = GetAttributes();
    AttributeVector attributeVector = comp.GetAttributes();
    if(attributeVector.size() != myAttributeVector.size())
        return false;
    if(attributeVector.empty() && myAttributeVector.empty())
        return true;

    std::sort(myAttributeVector.begin(), myAttributeVector.end(), &CmpAttributeByName);
    std::sort(attributeVector.begin(), attributeVector.end(), &CmpAttributeByName);

    AttributeVector::const_iterator iter1 = myAttributeVector.begin();
    AttributeVector::const_iterator iter2 = attributeVector.begin();
    while(iter1 != myAttributeVector.end() && iter2 != attributeVector.end())
    {
        // Compare attribute names and type and if they mach continue iteration if not components aren't exatly the same.
        if((*iter1)->GetNameString() == (*iter2)->GetNameString() &&
           (*iter1)->TypenameToString() == (*iter2)->TypenameToString())
        {
            if(iter1 != myAttributeVector.end())
                iter1++;
            if(iter2 != attributeVector.end())
                iter2++;
        }
        else
        {
            return false;
        }
    }
    return true;

    /*// Get both attributes and check if they are holding exact number of attributes.
    AttributeVector myAttributeVector = GetAttributes();
    AttributeVector attributeVector = comp.GetAttributes();
    if(attributeVector.size() != myAttributeVector.size())
        return false;

    // Compare that every attribute is same in both components.
    QSet<IAttribute*> myAttributeSet;
    QSet<IAttribute*> attributeSet;
    for(uint i = 0; i < myAttributeSet.size(); i++)
    {
        attributeSet.insert(myAttributeVector[i]);
        myAttributeSet.insert(attributeVector[i]);
    }
    if(attributeSet != myAttributeSet)
        return false;
    return true;*/
}

bool EC_DynamicComponent::ContainsAttribute(const QString &name) const
{
    AttributeVector::const_iterator iter = attributes_.begin();
    while(iter != attributes_.end())
    {
        if((*iter)->GetName() == name.toStdString())
        {
            return true;
        }
        iter++;
    }
    return false;
}
<commit_msg>Fixed issue that caused deserialize code to fail in EC_DynamicComponent.<commit_after>// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"
#include "EC_DynamicComponent.h"
#include "IModule.h"
#include "ModuleManager.h"
#include "Entity.h"
#include "LoggingFunctions.h"

#include <QScriptEngine>
#include <QScriptValueIterator>

DEFINE_POCO_LOGGING_FUNCTIONS("EC_DynamicComponent")

#include <QDomDocument>

namespace
{
    struct DeserializeData
    {
        DeserializeData(const std::string name = std::string(""),
                        const std::string type = std::string(""),
                        const std::string value = std::string("")):
            name_(name),
            type_(type),
            value_(value)
        {
        }

        //! Checks if any of data structure's values are null.
        bool isNull() const
        {
            return name_ == "" || type_ == "" || value_ == "";
        }

        std::string name_;
        std::string type_;
        std::string value_;
    };

    //! Function that is used by std::sort algorithm to sort attributes by their name.
    bool CmpAttributeByName(const IAttribute *a, const IAttribute *b)
    {
        return a->GetNameString() < b->GetNameString();
    }

    //! Function that is used by std::sort algorithm to sort DeserializeData by their name.
    bool CmpAttributeDataByName(const DeserializeData &a, const DeserializeData &b)
    {
        return a.name_ < b.name_;
    }
}

EC_DynamicComponent::EC_DynamicComponent(IModule *module):
    IComponent(module->GetFramework())
{
}

EC_DynamicComponent::~EC_DynamicComponent()
{
    foreach(IAttribute *a, attributes_)
        SAFE_DELETE(a);
}

void EC_DynamicComponent::SerializeTo(QDomDocument& doc, QDomElement& base_element) const
{
    QDomElement comp_element = BeginSerialization(doc, base_element);

    AttributeVector::const_iterator iter = attributes_.begin();
    while(iter != attributes_.end())
    {
        WriteAttribute(doc, comp_element, (*iter)->GetNameString().c_str(), (*iter)->ToString().c_str(), (*iter)->TypenameToString().c_str());
        iter++;
    }
}

void EC_DynamicComponent::DeserializeFrom(QDomElement& element, AttributeChange::Type change)
{
    if (!BeginDeserialization(element))
        return;

    std::vector<DeserializeData> deserializedAttributes;
    QDomElement child = element.firstChildElement("attribute");
    while(!child.isNull())
    {
        QString name = child.attribute("name");
        QString type = child.attribute("type");
        QString value = child.attribute("value");
        DeserializeData attributeData(name.toStdString(), type.toStdString(), value.toStdString());
        deserializedAttributes.push_back(attributeData);

        child = child.nextSiblingElement("attribute");
    }

    // Sort both lists in alphabetical order.
    AttributeVector oldAttributes = attributes_;
    std::stable_sort(oldAttributes.begin(), oldAttributes.end(), &CmpAttributeByName);
    std::stable_sort(deserializedAttributes.begin(), deserializedAttributes.end(), &CmpAttributeDataByName);

    std::vector<DeserializeData> addAttributes;
    std::vector<DeserializeData> remAttributes;
    AttributeVector::iterator iter1 = oldAttributes.begin();
    std::vector<DeserializeData>::iterator iter2 = deserializedAttributes.begin();
    // Check what attributes we need to add or remove from the dynamic component (done by comparing two list differences).
    while(iter1 != oldAttributes.end() || iter2 != deserializedAttributes.end())
    {
        // No point to continue the iteration if other list is empty. We can just push all new attributes into the dynamic component.
        if(iter1 == oldAttributes.end())
        {
            for(;iter2 != deserializedAttributes.end(); iter2++)
            {
                addAttributes.push_back(*iter2);
            }
            break;
        }
        // Only old attributes are left and they can be removed from the dynamic component.
        else if(iter2 == deserializedAttributes.end())
        {
            for(;iter1 != oldAttributes.end(); iter1++)
                remAttributes.push_back(DeserializeData((*iter1)->GetNameString().c_str()));
            break;
        }

        // Attribute has already created and we only need to update it's value.
        if((*iter1)->GetNameString() == (*iter2).name_)
        {
            //SetAttribute(QString::fromStdString(iter2->name_), QString::fromStdString(iter2->value_), change);
            for(AttributeVector::const_iterator attr_iter = attributes_.begin(); attr_iter != attributes_.end(); attr_iter++)
            {
                if((*attr_iter)->GetNameString() == iter2->name_)
                {
                    (*attr_iter)->FromString(iter2->value_, change);
                }
            }
            iter2++;
            iter1++;
        }
        // Found a new attribute that need to be created and added to the component.
        else if((*iter1)->GetNameString() > (*iter2).name_)
        {
            addAttributes.push_back(*iter2);
            iter2++;
        }
        // Couldn't find the attribute in a new list so it need to be removed from the component.
        else
        {
            remAttributes.push_back(DeserializeData((*iter1)->GetNameString().c_str()));
            iter1++;
        }
    }

    while(!addAttributes.empty())
    {
        DeserializeData attributeData = addAttributes.back();
        IAttribute *attribute = CreateAttribute(attributeData.type_.c_str(), attributeData.name_.c_str());
        if (attribute)
            attribute->FromString(attributeData.value_, change);
        addAttributes.pop_back();
    }
    while(!remAttributes.empty())
    {
        DeserializeData attributeData = remAttributes.back();
        RemoveAttribute(QString::fromStdString(attributeData.name_));
        remAttributes.pop_back();
    }
}

IAttribute *EC_DynamicComponent::CreateAttribute(const QString &typeName, const QString &name, AttributeChange::Type change)
{
    IAttribute *attribute = 0;
    if(ContainsAttribute(name))
        return attribute;
    attribute = framework_->GetComponentManager()->CreateAttribute(this, typeName.toStdString(), name.toStdString());
    if(attribute)
        emit AttributeAdded(name);

    AttributeChanged(attribute, change);

    return attribute;
}

void EC_DynamicComponent::RemoveAttribute(const QString &name, AttributeChange::Type change)
{
    for(AttributeVector::iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            //! /todo Make sure that component removal is replicated to the server if change type is Replicate.
            AttributeChanged(*iter, change);
            SAFE_DELETE(*iter);
            attributes_.erase(iter);
            emit AttributeRemoved(name);
            break;
        }
    }
}

void EC_DynamicComponent::AddQVariantAttribute(const QString &name, AttributeChange::Type change)
{
    //Check if the attribute has already been created.
    if(!ContainsAttribute(name))
    {
        Attribute<QVariant> *attribute = new Attribute<QVariant>(this, name.toStdString().c_str());
        AttributeChanged(attribute, change);
        emit AttributeAdded(name);
    }
    LogWarning("Failed to add a new QVariant in name of " + name.toStdString() + ", cause there already is an attribute in that name.");
}

QVariant EC_DynamicComponent::GetAttribute(int index) const
{
    if(index < attributes_.size() && index >= 0)
    {
        return attributes_[index]->ToQVariant();
    }
    return QVariant();
}

QVariant EC_DynamicComponent::GetAttribute(const QString &name) const
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); ++iter)
        if ((*iter)->GetNameString() == name.toStdString())
            return (*iter)->ToQVariant();

    return QVariant();
}

void EC_DynamicComponent::SetAttribute(int index, const QVariant &value, AttributeChange::Type change)
{
    if(index < attributes_.size() && index >= 0)
    {
        attributes_[index]->FromQVariant(value, change);
    }
    LogWarning("Cannot get attribute name, cause index is out of range.");
}

void EC_DynamicComponent::SetAttributeQScript(const QString &name, const QScriptValue &value, AttributeChange::Type change)
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            (*iter)->FromScriptValue(value, change);
            break;
        }
    }
}

void EC_DynamicComponent::SetAttribute(const QString &name, const QVariant &value, AttributeChange::Type change)
{
    for(AttributeVector::const_iterator iter = attributes_.begin(); iter != attributes_.end(); iter++)
    {
        if((*iter)->GetNameString() == name.toStdString())
        {
            (*iter)->FromQVariant(value, change);
            break;
        }
    }
}

QString EC_DynamicComponent::GetAttributeName(int index) const
{
    if(index < attributes_.size() && index >= 0)
    {
        return attributes_[index]->GetName();
    }
    LogWarning("Cannot get attribute name, cause index is out of range.");
    return QString();
}

bool EC_DynamicComponent::ContainSameAttributes(const EC_DynamicComponent &comp) const
{
    AttributeVector myAttributeVector = GetAttributes();
    AttributeVector attributeVector = comp.GetAttributes();
    if(attributeVector.size() != myAttributeVector.size())
        return false;
    if(attributeVector.empty() && myAttributeVector.empty())
        return true;

    std::sort(myAttributeVector.begin(), myAttributeVector.end(), &CmpAttributeByName);
    std::sort(attributeVector.begin(), attributeVector.end(), &CmpAttributeByName);

    AttributeVector::const_iterator iter1 = myAttributeVector.begin();
    AttributeVector::const_iterator iter2 = attributeVector.begin();
    while(iter1 != myAttributeVector.end() && iter2 != attributeVector.end())
    {
        // Compare attribute names and type and if they mach continue iteration if not components aren't exatly the same.
        if((*iter1)->GetNameString() == (*iter2)->GetNameString() &&
           (*iter1)->TypenameToString() == (*iter2)->TypenameToString())
        {
            if(iter1 != myAttributeVector.end())
                iter1++;
            if(iter2 != attributeVector.end())
                iter2++;
        }
        else
        {
            return false;
        }
    }
    return true;

    /*// Get both attributes and check if they are holding exact number of attributes.
    AttributeVector myAttributeVector = GetAttributes();
    AttributeVector attributeVector = comp.GetAttributes();
    if(attributeVector.size() != myAttributeVector.size())
        return false;

    // Compare that every attribute is same in both components.
    QSet<IAttribute*> myAttributeSet;
    QSet<IAttribute*> attributeSet;
    for(uint i = 0; i < myAttributeSet.size(); i++)
    {
        attributeSet.insert(myAttributeVector[i]);
        myAttributeSet.insert(attributeVector[i]);
    }
    if(attributeSet != myAttributeSet)
        return false;
    return true;*/
}

bool EC_DynamicComponent::ContainsAttribute(const QString &name) const
{
    AttributeVector::const_iterator iter = attributes_.begin();
    while(iter != attributes_.end())
    {
        if((*iter)->GetName() == name.toStdString())
        {
            return true;
        }
        iter++;
    }
    return false;
}
<|endoftext|>


<commit_before>/* ttn_base_netjoin.cpp	Tue Nov  1 2016 06:19:25 tmm */

/*

Module:  ttn_base_netjoin.cpp

Function:
	Arduino_LoRaWAN_ttn_base::NetJoin()

Version:
	V0.2.0	Tue Nov  1 2016 06:19:25 tmm	Edit level 1

Copyright notice:
	This file copyright (C) 2016 by

		MCCI Corporation
		3520 Krums Corners Road
		Ithaca, NY  14850

	An unpublished work.  All rights reserved.

	This file is proprietary information, and may not be disclosed or
	copied without the prior permission of MCCI Corporation.

Author:
	Terry Moore, MCCI Corporation	November 2016

Revision history:
   0.2.0  Tue Nov  1 2016 06:19:25  tmm
	Module created.

*/

#include <Arduino_LoRaWAN_ttn.h>
#include <Arduino_LoRaWAN_lmic.h>

/****************************************************************************\
|
|		Manifest constants & typedefs.
|
\****************************************************************************/

/****************************************************************************\
|
|	Read-only data.
|
\****************************************************************************/

/****************************************************************************\
|
|	VARIABLES:
|
\****************************************************************************/

void Arduino_LoRaWAN_ttn_base::NetJoin()
	{
        LMIC_setLinkCheckMode(0);
	}
<commit_msg>Fix #5: don't disable link-check-mode by default<commit_after>/* ttn_base_netjoin.cpp	Tue Nov  1 2016 06:19:25 tmm */

/*

Module:  ttn_base_netjoin.cpp

Function:
	Arduino_LoRaWAN_ttn_base::NetJoin()

Version:
	V0.2.0	Tue Nov  1 2016 06:19:25 tmm	Edit level 1

Copyright notice:
	This file copyright (C) 2016 by

		MCCI Corporation
		3520 Krums Corners Road
		Ithaca, NY  14850

	An unpublished work.  All rights reserved.

	This file is proprietary information, and may not be disclosed or
	copied without the prior permission of MCCI Corporation.

Author:
	Terry Moore, MCCI Corporation	November 2016

Revision history:
   0.2.0  Tue Nov  1 2016 06:19:25  tmm
	Module created.

*/

#include <Arduino_LoRaWAN_ttn.h>
#include <Arduino_LoRaWAN_lmic.h>

/****************************************************************************\
|
|		Manifest constants & typedefs.
|
\****************************************************************************/

/****************************************************************************\
|
|	Read-only data.
|
\****************************************************************************/

/****************************************************************************\
|
|	VARIABLES:
|
\****************************************************************************/

void Arduino_LoRaWAN_ttn_base::NetJoin()
	{
	// we don't want to disable link-check mode on
	// current TTN networks with the current LMIC.
	// LMIC_setLinkCheckMode(0);
	}
<|endoftext|>


<commit_before>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/lite/delegates/gpu/cl/selectors/special_selector.h"

#include "absl/types/any.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/kernels/special/depthwise_conv_plus_1x1_conv.h"
#include "tensorflow/lite/delegates/gpu/cl/kernels/special/fc_fc_add.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/shape.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
#include "tensorflow/lite/delegates/gpu/common/tensor.h"

namespace tflite {
namespace gpu {
namespace cl {
namespace {
absl::Status TryDepthwiseConvPlus1x1Conv(
    CalculationsPrecision precision, const GraphFloat32& graph,
    NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph) {
  auto* dw_node = graph.GetNode(first_node_id);
  if (OperationTypeFromString(dw_node->operation.type) !=
      OperationType::DEPTHWISE_CONVOLUTION) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_inputs = graph.FindInputs(dw_node->id);
  if (dw_inputs.size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_outputs = graph.FindOutputs(dw_node->id);
  auto consumers = graph.FindConsumers(dw_outputs[0]->id);
  if (consumers.size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto* conv_node = consumers[0];
  if (consumed_nodes->find(conv_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (OperationTypeFromString(conv_node->operation.type) !=
      OperationType::CONVOLUTION_2D) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (graph.FindInputs(conv_node->id).size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_attr = absl::any_cast<DepthwiseConvolution2DAttributes>(
      dw_node->operation.attributes);
  auto conv_attr =
      absl::any_cast<Convolution2DAttributes>(conv_node->operation.attributes);
  auto conv_outputs = graph.FindOutputs(conv_node->id);
  OperationDef op_def;
  op_def.precision = precision;
  auto it = tensor_descriptors.find(dw_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(conv_outputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.dst_tensors.push_back(it->second);
  }
  if (!IsDepthwiseConvPlus1x1ConvSupported(op_def, dw_attr, conv_attr)) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  std::unique_ptr<GPUOperation>* gpu_op =
      InitSingleOpSubgraph(dw_inputs, conv_outputs, gpu_subgraph);
  auto operation = CreateDepthwiseConvPlus1x1Conv(op_def, dw_attr, conv_attr);
  *gpu_op = absl::make_unique<GPUOperation>(std::move(operation));
  consumed_nodes->insert(dw_node->id);
  consumed_nodes->insert(conv_node->id);
  return absl::OkStatus();
}

// fully connected + fully connected + add
absl::Status TryFCFCAdd(
    const GpuInfo& gpu_info, CalculationsPrecision precision,
    const GraphFloat32& graph, NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph) {
  auto* fc0_node = graph.GetNode(first_node_id);
  if (OperationTypeFromString(fc0_node->operation.type) !=
      OperationType::FULLY_CONNECTED) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_inputs = graph.FindInputs(fc0_node->id);
  if (fc0_inputs.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_output_id = graph.FindOutputs(fc0_node->id)[0]->id;
  auto consumers = graph.FindConsumers(fc0_output_id);
  if (consumers.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto* add_node = consumers[0];
  if (consumed_nodes->find(add_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (OperationTypeFromString(add_node->operation.type) != OperationType::ADD) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto add_inputs = graph.FindInputs(add_node->id);
  if (add_inputs.size() != 2) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc1_output_id = add_inputs[0]->id + add_inputs[1]->id - fc0_output_id;
  auto* fc1_node = graph.FindProducer(fc1_output_id);
  if (OperationTypeFromString(fc1_node->operation.type) !=
      OperationType::FULLY_CONNECTED) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (consumed_nodes->find(fc1_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc1_inputs = graph.FindInputs(fc1_node->id);
  if (fc1_inputs.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_attr =
      absl::any_cast<FullyConnectedAttributes>(fc0_node->operation.attributes);
  auto fc1_attr =
      absl::any_cast<FullyConnectedAttributes>(fc1_node->operation.attributes);
  if (fc0_attr.weights.shape.o != fc1_attr.weights.shape.o) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto add_outputs = graph.FindOutputs(add_node->id);

  OperationDef op_def;
  op_def.precision = precision;
  auto it = tensor_descriptors.find(fc0_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(fc1_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(add_outputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.dst_tensors.push_back(it->second);
  }

  for (int i = 0; i < fc1_inputs.size(); ++i) {
    fc0_inputs.push_back(fc1_inputs[i]);
  }
  std::unique_ptr<GPUOperation>* gpu_op =
      InitSingleOpSubgraph(fc0_inputs, add_outputs, gpu_subgraph);
  FCFCAdd fc = CreateFCFCAdd(gpu_info, op_def, fc0_attr, fc1_attr);
  *gpu_op = absl::make_unique<FCFCAdd>(std::move(fc));
  consumed_nodes->insert(fc0_node->id);
  consumed_nodes->insert(fc1_node->id);
  consumed_nodes->insert(add_node->id);
  return absl::OkStatus();
}
}  // namespace

absl::Status GPUSubgraphFromGraph(
    const GpuInfo& gpu_info, CalculationsPrecision precision,
    const GraphFloat32& graph, NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph,
    std::string* name) {
  if ((gpu_info.IsAdreno() || gpu_info.IsNvidia()) &&
      TryDepthwiseConvPlus1x1Conv(precision, graph, first_node_id,
                                  tensor_descriptors, consumed_nodes,
                                  gpu_subgraph)
          .ok()) {
    *name = "depthwise_conv_plus_1x1_conv";
    return absl::OkStatus();
  }
  if ((gpu_info.IsIntel() || gpu_info.IsNvidia()) &&
      TryFCFCAdd(gpu_info, precision, graph, first_node_id, tensor_descriptors,
                 consumed_nodes, gpu_subgraph)
          .ok()) {
    *name = "fully_connected_x2_and_add";
    return absl::OkStatus();
  }
  return absl::NotFoundError("No special combination.");
}

}  // namespace cl
}  // namespace gpu
}  // namespace tflite
<commit_msg>Fix potential nullptr dereferences.<commit_after>/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.

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

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

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

#include "tensorflow/lite/delegates/gpu/cl/selectors/special_selector.h"

#include "absl/types/any.h"
#include "tensorflow/lite/delegates/gpu/cl/cl_device.h"
#include "tensorflow/lite/delegates/gpu/cl/kernels/special/depthwise_conv_plus_1x1_conv.h"
#include "tensorflow/lite/delegates/gpu/cl/kernels/special/fc_fc_add.h"
#include "tensorflow/lite/delegates/gpu/common/data_type.h"
#include "tensorflow/lite/delegates/gpu/common/operations.h"
#include "tensorflow/lite/delegates/gpu/common/shape.h"
#include "tensorflow/lite/delegates/gpu/common/status.h"
#include "tensorflow/lite/delegates/gpu/common/task/tensor_desc.h"
#include "tensorflow/lite/delegates/gpu/common/tensor.h"

namespace tflite {
namespace gpu {
namespace cl {
namespace {
absl::Status TryDepthwiseConvPlus1x1Conv(
    CalculationsPrecision precision, const GraphFloat32& graph,
    NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph) {
  auto* dw_node = graph.GetNode(first_node_id);
  if (dw_node == nullptr) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (OperationTypeFromString(dw_node->operation.type) !=
      OperationType::DEPTHWISE_CONVOLUTION) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_inputs = graph.FindInputs(dw_node->id);
  if (dw_inputs.size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_outputs = graph.FindOutputs(dw_node->id);
  auto consumers = graph.FindConsumers(dw_outputs[0]->id);
  if (consumers.size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto* conv_node = consumers[0];
  if (conv_node == nullptr) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (consumed_nodes->find(conv_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (OperationTypeFromString(conv_node->operation.type) !=
      OperationType::CONVOLUTION_2D) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  if (graph.FindInputs(conv_node->id).size() != 1) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  auto dw_attr = absl::any_cast<DepthwiseConvolution2DAttributes>(
      dw_node->operation.attributes);
  auto conv_attr =
      absl::any_cast<Convolution2DAttributes>(conv_node->operation.attributes);
  auto conv_outputs = graph.FindOutputs(conv_node->id);
  OperationDef op_def;
  op_def.precision = precision;
  auto it = tensor_descriptors.find(dw_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(conv_outputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.dst_tensors.push_back(it->second);
  }
  if (!IsDepthwiseConvPlus1x1ConvSupported(op_def, dw_attr, conv_attr)) {
    return absl::NotFoundError("DepthwiseConvPlus1x1Conv not suitable.");
  }
  std::unique_ptr<GPUOperation>* gpu_op =
      InitSingleOpSubgraph(dw_inputs, conv_outputs, gpu_subgraph);
  auto operation = CreateDepthwiseConvPlus1x1Conv(op_def, dw_attr, conv_attr);
  *gpu_op = absl::make_unique<GPUOperation>(std::move(operation));
  consumed_nodes->insert(dw_node->id);
  consumed_nodes->insert(conv_node->id);
  return absl::OkStatus();
}

// fully connected + fully connected + add
absl::Status TryFCFCAdd(
    const GpuInfo& gpu_info, CalculationsPrecision precision,
    const GraphFloat32& graph, NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph) {
  auto* fc0_node = graph.GetNode(first_node_id);
  if (fc0_node == nullptr) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (OperationTypeFromString(fc0_node->operation.type) !=
      OperationType::FULLY_CONNECTED) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_inputs = graph.FindInputs(fc0_node->id);
  if (fc0_inputs.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_output_id = graph.FindOutputs(fc0_node->id)[0]->id;
  auto consumers = graph.FindConsumers(fc0_output_id);
  if (consumers.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto* add_node = consumers[0];
  if (add_node == nullptr) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (consumed_nodes->find(add_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (OperationTypeFromString(add_node->operation.type) != OperationType::ADD) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto add_inputs = graph.FindInputs(add_node->id);
  if (add_inputs.size() != 2) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc1_output_id = add_inputs[0]->id + add_inputs[1]->id - fc0_output_id;
  auto* fc1_node = graph.FindProducer(fc1_output_id);
  if (fc1_node == nullptr) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (OperationTypeFromString(fc1_node->operation.type) !=
      OperationType::FULLY_CONNECTED) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  if (consumed_nodes->find(fc1_node->id) != consumed_nodes->end()) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc1_inputs = graph.FindInputs(fc1_node->id);
  if (fc1_inputs.size() != 1) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto fc0_attr =
      absl::any_cast<FullyConnectedAttributes>(fc0_node->operation.attributes);
  auto fc1_attr =
      absl::any_cast<FullyConnectedAttributes>(fc1_node->operation.attributes);
  if (fc0_attr.weights.shape.o != fc1_attr.weights.shape.o) {
    return absl::NotFoundError("FCFCAdd not suitable.");
  }
  auto add_outputs = graph.FindOutputs(add_node->id);

  OperationDef op_def;
  op_def.precision = precision;
  auto it = tensor_descriptors.find(fc0_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(fc1_inputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.src_tensors.push_back(it->second);
  }
  it = tensor_descriptors.find(add_outputs[0]->id);
  if (it != tensor_descriptors.end()) {
    op_def.dst_tensors.push_back(it->second);
  }

  for (int i = 0; i < fc1_inputs.size(); ++i) {
    fc0_inputs.push_back(fc1_inputs[i]);
  }
  std::unique_ptr<GPUOperation>* gpu_op =
      InitSingleOpSubgraph(fc0_inputs, add_outputs, gpu_subgraph);
  FCFCAdd fc = CreateFCFCAdd(gpu_info, op_def, fc0_attr, fc1_attr);
  *gpu_op = absl::make_unique<FCFCAdd>(std::move(fc));
  consumed_nodes->insert(fc0_node->id);
  consumed_nodes->insert(fc1_node->id);
  consumed_nodes->insert(add_node->id);
  return absl::OkStatus();
}
}  // namespace

absl::Status GPUSubgraphFromGraph(
    const GpuInfo& gpu_info, CalculationsPrecision precision,
    const GraphFloat32& graph, NodeId first_node_id,
    const std::map<ValueId, TensorDescriptor>& tensor_descriptors,
    std::set<NodeId>* consumed_nodes, GPUOperationsSubgraph* gpu_subgraph,
    std::string* name) {
  if ((gpu_info.IsAdreno() || gpu_info.IsNvidia()) &&
      TryDepthwiseConvPlus1x1Conv(precision, graph, first_node_id,
                                  tensor_descriptors, consumed_nodes,
                                  gpu_subgraph)
          .ok()) {
    *name = "depthwise_conv_plus_1x1_conv";
    return absl::OkStatus();
  }
  if ((gpu_info.IsIntel() || gpu_info.IsNvidia()) &&
      TryFCFCAdd(gpu_info, precision, graph, first_node_id, tensor_descriptors,
                 consumed_nodes, gpu_subgraph)
          .ok()) {
    *name = "fully_connected_x2_and_add";
    return absl::OkStatus();
  }
  return absl::NotFoundError("No special combination.");
}

}  // namespace cl
}  // namespace gpu
}  // namespace tflite
<|endoftext|>


<commit_before>/**
 * @file device_properties.hxx
 * @author Muhammad Osama (mosama@ucdavis.edu)
 * @brief
 * @version 0.1
 * @date 2020-10-05
 *
 * @copyright Copyright (c) 2020
 *
 */
#pragma once
#include <iostream>
#include <gunrock/cuda/device.hxx>

namespace gunrock {
namespace cuda {

typedef cudaDeviceProp device_properties_t;
typedef int architecture_t;

/**
 * @namespace properties
 * C++ based CUDA device properties.
 */
namespace properties {

void print(device_properties_t& prop) {
  device_id_t ordinal;
  cudaGetDevice(&ordinal);

  size_t freeMem, totalMem;
  error::error_t status = cudaMemGetInfo(&freeMem, &totalMem);
  error::throw_if_exception(status);

  double memBandwidth =
      (prop.memoryClockRate * 1000.0) * (prop.memoryBusWidth / 8 * 2) / 1.0e9;

  std::cout << prop.name << " : " << prop.clockRate / 1000.0 << " Mhz "
            << "(Ordinal " << ordinal << ")" << std::endl;
  std::cout << "FreeMem: " << (int)(freeMem / (1 << 20)) << " MB "
            << "TotalMem: " << (int)(totalMem / (1 << 20)) << " MB "
            << ((int)8 * sizeof(int*)) << "-bit pointers." << std::endl;
  std::cout << prop.multiProcessorCount
            << " SMs enabled, Compute Capability sm_" << prop.major
            << prop.minor << std::endl;
  std::cout << "Mem Clock: " << prop.memoryClockRate / 1000.0 << " Mhz x "
            << prop.memoryBusWidth << " bits (" << memBandwidth << " GB/s)"
            << std::endl;
  std::cout << "ECC " << (prop.ECCEnabled ? "Enabled" : "Disabled")
            << std::endl;
}

inline constexpr unsigned shared_memory_banks() {
  return 1 << 5;  // 32 memory banks per SM
}

inline constexpr unsigned shared_memory_bank_stride() {
  return 1 << 2;  // 4 byte words
}

inline constexpr unsigned maximum_threads_per_warp() {
  return 1 << 5;  // 32 threads per warp
}

}  // namespace properties

}  // namespace cuda
}  // namespace gunrock<commit_msg>Include error.hxx for print()<commit_after>/**
 * @file device_properties.hxx
 * @author Muhammad Osama (mosama@ucdavis.edu)
 * @brief
 * @version 0.1
 * @date 2020-10-05
 *
 * @copyright Copyright (c) 2020
 *
 */
#pragma once
#include <iostream>
#include <gunrock/cuda/device.hxx>
#include <gunrock/error.hxx>

namespace gunrock {
namespace cuda {

typedef cudaDeviceProp device_properties_t;
typedef int architecture_t;

/**
 * @namespace properties
 * C++ based CUDA device properties.
 */
namespace properties {

void print(device_properties_t& prop) {
  device_id_t ordinal;
  cudaGetDevice(&ordinal);

  size_t freeMem, totalMem;
  error::error_t status = cudaMemGetInfo(&freeMem, &totalMem);
  error::throw_if_exception(status);

  double memBandwidth =
      (prop.memoryClockRate * 1000.0) * (prop.memoryBusWidth / 8 * 2) / 1.0e9;

  std::cout << prop.name << " : " << prop.clockRate / 1000.0 << " Mhz "
            << "(Ordinal " << ordinal << ")" << std::endl;
  std::cout << "FreeMem: " << (int)(freeMem / (1 << 20)) << " MB "
            << "TotalMem: " << (int)(totalMem / (1 << 20)) << " MB "
            << ((int)8 * sizeof(int*)) << "-bit pointers." << std::endl;
  std::cout << prop.multiProcessorCount
            << " SMs enabled, Compute Capability sm_" << prop.major
            << prop.minor << std::endl;
  std::cout << "Mem Clock: " << prop.memoryClockRate / 1000.0 << " Mhz x "
            << prop.memoryBusWidth << " bits (" << memBandwidth << " GB/s)"
            << std::endl;
  std::cout << "ECC " << (prop.ECCEnabled ? "Enabled" : "Disabled")
            << std::endl;
}

inline constexpr unsigned shared_memory_banks() {
  return 1 << 5;  // 32 memory banks per SM
}

inline constexpr unsigned shared_memory_bank_stride() {
  return 1 << 2;  // 4 byte words
}

inline constexpr unsigned maximum_threads_per_warp() {
  return 1 << 5;  // 32 threads per warp
}

}  // namespace properties

}  // namespace cuda
}  // namespace gunrock<|endoftext|>


<commit_before>#include "material/ggx.h"

namespace AT_NAME
{
	// NOTE
	// http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
	// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

	// NOTE
	// http://qiita.com/_Pheema_/items/f1ffb2e38cc766e6e668

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto ret = pdf(roughness.r, normal, wi, wo);
		return ret;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v) const
	{
		return pdf(&m_param, normal, wi, wo, u, v);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		real u, real v,
		aten::sampler* sampler)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		aten::vec3 dir = sampleDirection(roughness.r, normal, wi, sampler);

		return std::move(dir);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		const aten::ray& ray,
		const aten::vec3& normal,
		real u, real v,
		aten::sampler* sampler) const
	{
		return std::move(sampleDirection(&m_param, normal, ray.dir, u, v, sampler));
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto albedo = param->baseColor;
		albedo *= material::sampleTexture(param->albedoMap, u, v, real(1));

		real fresnel = 1;
		real ior = param->ior;

		aten::vec3 ret = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, wo, u, v);
		return std::move(ret);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v,
		const aten::vec3& externalAlbedo)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto albedo = param->baseColor;
		albedo *= externalAlbedo;

		real fresnel = 1;
		real ior = param->ior;

		aten::vec3 ret = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, wo, u, v);
		return std::move(ret);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v) const
	{
		return std::move(bsdf(&m_param, normal, wi, wo, u, v));
	}

	static AT_DEVICE_MTRL_API real sampleGGX_D(
		const aten::vec3& wh,	// half
		const aten::vec3& n,	// normal
		real roughness)
	{
		// NOTE
		// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

		// NOTE
		// ((a^2 - 1) * cos^2 + 1)^2
		// (-> a^2 = a2, cos^2 = cos2)
		// ((a2 - 1) * cos2 + 1)^2
		//  = (a2cos2 + 1 - cos2)^2 = (a2cos2 + sin2)^2
		// (-> sin = sqrt(1 - cos2), sin2 = 1 - cos2)
		//  = a4 * cos4 + 2 * a2 * cos2 * sin2 + sin4
		//  = cos4 * (a4 + 2 * a2 * (sin2 / cos2) + (sin4 / cos4))
		//  = cos4 * (a4 + 2 * a2 * tan2 + tan4)
		//  = cos4 * (a2 + tan2) ^ 2

		real a = roughness;
		auto a2 = a * a;

		auto costheta = aten::abs(dot(wh, n));
		auto cos2 = costheta * costheta;

		auto denom = aten::pow((a2 - 1) * cos2 + 1, 2);

		auto D = denom > 0 ? a2 / (AT_MATH_PI * denom) : 0;

		return D;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::computeGGXSmithG1(real roughness, const aten::vec3& v, const aten::vec3& n)
	{
		// NOTE
		// http://computergraphics.stackexchange.com/questions/2489/correct-form-of-the-ggx-geometry-term
		// http://gregory-igehy.hatenadiary.com/entry/2015/02/26/154142

		real a = roughness;

		real costheta = aten::abs(dot(v, n));

		real sintheta = aten::sqrt(1 - aten::clamp<real>(costheta * costheta, 0, 1));
		real tan = costheta > 0 ? sintheta / costheta : 0;

		real denom = aten::sqrt(1 + a * a * tan * tan);

		real ret = 2 / (1 + denom);

		return ret;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		real roughness,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo)
	{
		// NOTE
		// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

		auto wh = normalize(-wi + wo);

		auto costheta = aten::abs(dot(wh, normal));

		auto D = sampleGGX_D(wh, normal, roughness);

		auto denom = 4 * aten::abs(dot(wo, wh));

		auto pdf = denom > 0 ? (D * costheta) / denom : 0;

		return pdf;
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		real roughness,
		const aten::vec3& in,
		const aten::vec3& normal,
		aten::sampler* sampler)
	{
		auto r1 = sampler->nextSample();
		auto r2 = sampler->nextSample();

		auto a = roughness;

		auto theta = aten::atan(a * aten::sqrt(r1 / (1 - r1)));
		theta = ((theta >= 0) ? theta : (theta + 2 * AT_MATH_PI));

		auto phi = 2 * AT_MATH_PI * r2;

		auto costheta = aten::cos(theta);
		auto sintheta = aten::sqrt(1 - costheta * costheta);

		auto cosphi = aten::cos(phi);
		auto sinphi = aten::sqrt(1 - cosphi * cosphi);

		// Ortho normal base.
		auto n = normal;
		auto t = aten::getOrthoVector(normal);
		auto b = normalize(cross(n, t));

		auto w = t * sintheta * cosphi + b * sintheta * sinphi + n * costheta;
		w = normalize(w);

		auto dir = in - 2 * dot(in, w) * w;

		return std::move(dir);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::vec3& albedo,
		const real roughness,
		const real ior,
		real& fresnel,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		// C˂Ă鑤̂̋̕ܗ.
		real ni = real(1);	// ^

		real nt = ior;		// ̓̋ܗ.

		aten::vec3 V = -wi;
		aten::vec3 L = wo;
		aten::vec3 N = normal;
		aten::vec3 H = normalize(L + V);

		// TODO
		// DesneyabsĂȂAAMD̂͂Ă....
		auto NdotH = aten::abs(dot(N, H));
		auto VdotH = aten::abs(dot(V, H));
		auto NdotL = aten::abs(dot(N, L));
		auto NdotV = aten::abs(dot(N, V));

		// Compute D.
		real D = sampleGGX_D(H, N, roughness);

		// Compute G.
		real G(1);
		{
			auto G1_lh = computeGGXSmithG1(roughness, L, N);
			auto G1_vh = computeGGXSmithG1(roughness, V, N);

			G = G1_lh * G1_vh;
		}

		real F(1);
		{
			// http://d.hatena.ne.jp/hanecci/20130525/p3

			// NOTE
			// Fschlick(v,h)  R0 + (1 - R0)(1 - cos)^5
			// R0 = ((n1 - n2) / (n1 + n2))^2

			auto r0 = (ni - nt) / (ni + nt);
			r0 = r0 * r0;

			auto LdotH = aten::abs(dot(L, H));

			F = r0 + (1 - r0) * aten::pow((1 - LdotH), 5);
		}

		auto denom = 4 * NdotL * NdotV;

		auto bsdf = denom > AT_MATH_EPSILON ? albedo * F * G * D / denom : aten::vec3(0);

		fresnel = F;

		return std::move(bsdf);
	}

	AT_DEVICE_MTRL_API void MicrofacetGGX::sample(
		MaterialSampling* result,
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		bool isLightPath/*= false*/)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		result->dir = sampleDirection(roughness.r, wi, normal, sampler);
		result->pdf = pdf(roughness.r, normal, wi, result->dir);

		auto albedo = param->baseColor;
		albedo *= material::sampleTexture(param->albedoMap, u, v, real(1));

		real fresnel = 1;
		real ior = param->ior;

		result->bsdf = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, result->dir, u, v);
		result->fresnel = fresnel;
	}

	AT_DEVICE_MTRL_API void MicrofacetGGX::sample(
		MaterialSampling* result,
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		const aten::vec3& externalAlbedo,
		bool isLightPath/*= false*/)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		result->dir = sampleDirection(roughness.r, wi, normal, sampler);
		result->pdf = pdf(roughness.r, normal, wi, result->dir);

		auto albedo = param->baseColor;
		albedo *= externalAlbedo;

		real fresnel = 1;
		real ior = param->ior;

		result->bsdf = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, result->dir, u, v);
		result->fresnel = fresnel;
	}

	AT_DEVICE_MTRL_API MaterialSampling MicrofacetGGX::sample(
		const aten::ray& ray,
		const aten::vec3& normal,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		bool isLightPath/*= false*/) const
	{
		MaterialSampling ret;

		sample(
			&ret,
			&m_param,
			normal,
			ray.dir,
			orgnormal,
			sampler,
			u, v,
			isLightPath);

		return std::move(ret);
	}

	bool MicrofacetGGX::edit(aten::IMaterialParamEditor* editor)
	{
		auto b0 = AT_EDIT_MATERIAL_PARAM(editor, m_param, roughness);
		auto b1 = AT_EDIT_MATERIAL_PARAM_RANGE(editor, m_param, ior, real(0.01), real(10));
		auto b2 = AT_EDIT_MATERIAL_PARAM(editor, m_param, baseColor);

		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, albedoMap);
		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, normalMap);
		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, roughnessMap);

		return b0 || b1 || b2;
	}
}
<commit_msg>Add comments.<commit_after>#include "material/ggx.h"

namespace AT_NAME
{
	// NOTE
	// http://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
	// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

	// NOTE
	// http://qiita.com/_Pheema_/items/f1ffb2e38cc766e6e668

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto ret = pdf(roughness.r, normal, wi, wo);
		return ret;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v) const
	{
		return pdf(&m_param, normal, wi, wo, u, v);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		real u, real v,
		aten::sampler* sampler)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		aten::vec3 dir = sampleDirection(roughness.r, normal, wi, sampler);

		return std::move(dir);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		const aten::ray& ray,
		const aten::vec3& normal,
		real u, real v,
		aten::sampler* sampler) const
	{
		return std::move(sampleDirection(&m_param, normal, ray.dir, u, v, sampler));
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto albedo = param->baseColor;
		albedo *= material::sampleTexture(param->albedoMap, u, v, real(1));

		real fresnel = 1;
		real ior = param->ior;

		aten::vec3 ret = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, wo, u, v);
		return std::move(ret);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v,
		const aten::vec3& externalAlbedo)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		auto albedo = param->baseColor;
		albedo *= externalAlbedo;

		real fresnel = 1;
		real ior = param->ior;

		aten::vec3 ret = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, wo, u, v);
		return std::move(ret);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v) const
	{
		return std::move(bsdf(&m_param, normal, wi, wo, u, v));
	}

	static AT_DEVICE_MTRL_API real sampleGGX_D(
		const aten::vec3& wh,	// half
		const aten::vec3& n,	// normal
		real roughness)
	{
		// NOTE
		// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

		// NOTE
		// ((a^2 - 1) * cos^2 + 1)^2
		// (-> a^2 = a2, cos^2 = cos2)
		// ((a2 - 1) * cos2 + 1)^2
		//  = (a2cos2 + 1 - cos2)^2 = (a2cos2 + sin2)^2
		// (-> sin = sqrt(1 - cos2), sin2 = 1 - cos2)
		//  = a4 * cos4 + 2 * a2 * cos2 * sin2 + sin4
		//  = cos4 * (a4 + 2 * a2 * (sin2 / cos2) + (sin4 / cos4))
		//  = cos4 * (a4 + 2 * a2 * tan2 + tan4)
		//  = cos4 * (a2 + tan2)^2
		//  = (cos2 * (a2 + tan2))^2
		// (tan = sin / cos -> tan2 = sin2 / cos2)
		//  = (a2 * cos2 + sin2)^2
		//  = (a2 * cos2 + (1 - cos2))^2
		//  = ((a2 - 1) * cos2 + 1)^2

		real a = roughness;
		auto a2 = a * a;

		auto costheta = aten::abs(dot(wh, n));
		auto cos2 = costheta * costheta;

		auto denom = aten::pow((a2 - 1) * cos2 + 1, 2);

		auto D = denom > 0 ? a2 / (AT_MATH_PI * denom) : 0;

		return D;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::computeGGXSmithG1(real roughness, const aten::vec3& v, const aten::vec3& n)
	{
		// NOTE
		// http://computergraphics.stackexchange.com/questions/2489/correct-form-of-the-ggx-geometry-term
		// http://gregory-igehy.hatenadiary.com/entry/2015/02/26/154142

		real a = roughness;

		real costheta = aten::abs(dot(v, n));

		real sintheta = aten::sqrt(1 - aten::clamp<real>(costheta * costheta, 0, 1));
		real tan = costheta > 0 ? sintheta / costheta : 0;

		real denom = aten::sqrt(1 + a * a * tan * tan);

		real ret = 2 / (1 + denom);

		return ret;
	}

	AT_DEVICE_MTRL_API real MicrofacetGGX::pdf(
		real roughness,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo)
	{
		// NOTE
		// https://agraphicsguy.wordpress.com/2015/11/01/sampling-microfacet-brdf/

		auto wh = normalize(-wi + wo);

		auto costheta = aten::abs(dot(wh, normal));

		auto D = sampleGGX_D(wh, normal, roughness);

		auto denom = 4 * aten::abs(dot(wo, wh));

		auto pdf = denom > 0 ? (D * costheta) / denom : 0;

		return pdf;
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::sampleDirection(
		real roughness,
		const aten::vec3& in,
		const aten::vec3& normal,
		aten::sampler* sampler)
	{
		auto r1 = sampler->nextSample();
		auto r2 = sampler->nextSample();

		auto a = roughness;

		auto theta = aten::atan(a * aten::sqrt(r1 / (1 - r1)));
		theta = ((theta >= 0) ? theta : (theta + 2 * AT_MATH_PI));

		auto phi = 2 * AT_MATH_PI * r2;

		auto costheta = aten::cos(theta);
		auto sintheta = aten::sqrt(1 - costheta * costheta);

		auto cosphi = aten::cos(phi);
		auto sinphi = aten::sqrt(1 - cosphi * cosphi);

		// Ortho normal base.
		auto n = normal;
		auto t = aten::getOrthoVector(normal);
		auto b = normalize(cross(n, t));

		auto w = t * sintheta * cosphi + b * sintheta * sinphi + n * costheta;
		w = normalize(w);

		auto dir = in - 2 * dot(in, w) * w;

		return std::move(dir);
	}

	AT_DEVICE_MTRL_API aten::vec3 MicrofacetGGX::bsdf(
		const aten::vec3& albedo,
		const real roughness,
		const real ior,
		real& fresnel,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& wo,
		real u, real v)
	{
		// C˂Ă鑤̂̋̕ܗ.
		real ni = real(1);	// ^

		real nt = ior;		// ̓̋ܗ.

		aten::vec3 V = -wi;
		aten::vec3 L = wo;
		aten::vec3 N = normal;
		aten::vec3 H = normalize(L + V);

		// TODO
		// DesneyabsĂȂAAMD̂͂Ă....
		auto NdotH = aten::abs(dot(N, H));
		auto VdotH = aten::abs(dot(V, H));
		auto NdotL = aten::abs(dot(N, L));
		auto NdotV = aten::abs(dot(N, V));

		// Compute D.
		real D = sampleGGX_D(H, N, roughness);

		// Compute G.
		real G(1);
		{
			auto G1_lh = computeGGXSmithG1(roughness, L, N);
			auto G1_vh = computeGGXSmithG1(roughness, V, N);

			G = G1_lh * G1_vh;
		}

		real F(1);
		{
			// http://d.hatena.ne.jp/hanecci/20130525/p3

			// NOTE
			// Fschlick(v,h)  R0 + (1 - R0)(1 - cos)^5
			// R0 = ((n1 - n2) / (n1 + n2))^2

			auto r0 = (ni - nt) / (ni + nt);
			r0 = r0 * r0;

			auto LdotH = aten::abs(dot(L, H));

			F = r0 + (1 - r0) * aten::pow((1 - LdotH), 5);
		}

		auto denom = 4 * NdotL * NdotV;

		auto bsdf = denom > AT_MATH_EPSILON ? albedo * F * G * D / denom : aten::vec3(0);

		fresnel = F;

		return std::move(bsdf);
	}

	AT_DEVICE_MTRL_API void MicrofacetGGX::sample(
		MaterialSampling* result,
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		bool isLightPath/*= false*/)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		result->dir = sampleDirection(roughness.r, wi, normal, sampler);
		result->pdf = pdf(roughness.r, normal, wi, result->dir);

		auto albedo = param->baseColor;
		albedo *= material::sampleTexture(param->albedoMap, u, v, real(1));

		real fresnel = 1;
		real ior = param->ior;

		result->bsdf = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, result->dir, u, v);
		result->fresnel = fresnel;
	}

	AT_DEVICE_MTRL_API void MicrofacetGGX::sample(
		MaterialSampling* result,
		const aten::MaterialParameter* param,
		const aten::vec3& normal,
		const aten::vec3& wi,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		const aten::vec3& externalAlbedo,
		bool isLightPath/*= false*/)
	{
		auto roughness = material::sampleTexture(
			param->roughnessMap,
			u, v,
			param->roughness);

		result->dir = sampleDirection(roughness.r, wi, normal, sampler);
		result->pdf = pdf(roughness.r, normal, wi, result->dir);

		auto albedo = param->baseColor;
		albedo *= externalAlbedo;

		real fresnel = 1;
		real ior = param->ior;

		result->bsdf = bsdf(albedo, roughness.r, ior, fresnel, normal, wi, result->dir, u, v);
		result->fresnel = fresnel;
	}

	AT_DEVICE_MTRL_API MaterialSampling MicrofacetGGX::sample(
		const aten::ray& ray,
		const aten::vec3& normal,
		const aten::vec3& orgnormal,
		aten::sampler* sampler,
		real u, real v,
		bool isLightPath/*= false*/) const
	{
		MaterialSampling ret;

		sample(
			&ret,
			&m_param,
			normal,
			ray.dir,
			orgnormal,
			sampler,
			u, v,
			isLightPath);

		return std::move(ret);
	}

	bool MicrofacetGGX::edit(aten::IMaterialParamEditor* editor)
	{
		auto b0 = AT_EDIT_MATERIAL_PARAM(editor, m_param, roughness);
		auto b1 = AT_EDIT_MATERIAL_PARAM_RANGE(editor, m_param, ior, real(0.01), real(10));
		auto b2 = AT_EDIT_MATERIAL_PARAM(editor, m_param, baseColor);

		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, albedoMap);
		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, normalMap);
		AT_EDIT_MATERIAL_PARAM_TEXTURE(editor, m_param, roughnessMap);

		return b0 || b1 || b2;
	}
}
<|endoftext|>


<commit_before>#include "../test.h"

extern "C" {

#include "udp-json-parser/json.h"
#include "udp-json-builder/json-builder.h"

} // extern "C"

static void GenStat(Stat* s, const json_value* v) {
    switch (v->type) {
    case json_object:
        for (size_t i = 0; i < v->u.object.length; i++) {
            const json_object_entry* e = v->u.object.values + i;
            GenStat(s, e->value);
            s->stringLength += e->name_length;
        }
        s->stringCount += v->u.object.length;
        s->memberCount += v->u.object.length;
        s->objectCount++;
        break;

    case json_array:
        for (size_t i = 0; i < v->u.array.length; i++)
            GenStat(s, v->u.array.values[i]);
        s->arrayCount++;
        s->elementCount += v->u.array.length;
        break;

    case json_string:
        s->stringCount++;
        s->stringLength += v->u.string.length;
        break;

    case json_integer:
    case json_double:
        s->numberCount++;
        break;

    case json_boolean:
        if (v->u.boolean)
            s->trueCount++;
        else
            s->falseCount++;
        break;

    case json_null:
        s->nullCount++;
        break;

    default:
        break;
    }
}

class UdpjsonParseResult : public ParseResultBase {
public:
    UdpjsonParseResult() : root() {}
    ~UdpjsonParseResult() { json_value_free(root); }

    json_value *root;
};

class UdpjsonStringResult : public StringResultBase {
public:
    UdpjsonStringResult() : s() {}
    ~UdpjsonStringResult() { free(s); }

    virtual const char* c_str() const { return s; }

    char* s;
};

class UdpjsonTest : public TestBase {
public:
#if TEST_INFO
    virtual const char* GetName() const { return "udp/json-parser (C)"; }
    virtual const char* GetFilename() const { return __FILE__; }
#endif

#if TEST_PARSE
    virtual ParseResultBase* Parse(const char* json, size_t length) const {
        UdpjsonParseResult* pr = new UdpjsonParseResult;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr->root = json_parse_ex(&settings, json, length, error);
        if (!pr->root) {
            delete pr;
            return 0;
        }
    	return pr;
    }
#endif

    // Very slow in the current version.
#if TEST_STRINGIFY
    virtual StringResultBase* Stringify(const ParseResultBase* parseResult) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        UdpjsonStringResult* sr = new UdpjsonStringResult;
        json_serialize_opts opts = { json_serialize_mode_packed, 0, 0 };
        sr->s = (char*)malloc(json_measure_ex(pr->root, opts));
        json_serialize_ex(sr->s, pr->root, opts);
        return sr;
    }
#endif

#if TEST_PRETTIFY
    virtual StringResultBase* Prettify(const ParseResultBase* parseResult) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        UdpjsonStringResult* sr = new UdpjsonStringResult;
        json_serialize_opts opts = { json_serialize_mode_multiline, 0, 4 };
        sr->s = (char*)malloc(json_measure_ex(pr->root, opts));
        json_serialize_ex(sr->s, pr->root, opts);
        return sr;
    }
#endif

#if TEST_STATISTICS
    virtual bool Statistics(const ParseResultBase* parseResult, Stat* stat) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        memset(stat, 0, sizeof(Stat));
        GenStat(stat, pr->root);
        return true;
    }
#endif

#if TEST_CONFORMANCE
    virtual bool ParseDouble(const char* json, double* d) const {
        UdpjsonParseResult pr;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr.root = json_parse_ex(&settings, json, strlen(json), error);
        if (pr.root &&
            pr.root->type == json_array &&
            pr.root->u.array.length == 1 &&
            pr.root->u.array.values[0]->type == json_double)
        {
            *d = pr.root->u.array.values[0]->u.dbl;
            return true;
        }
        return false;
    }

    virtual bool ParseString(const char* json, std::string& s) const {
        UdpjsonParseResult pr;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr.root = json_parse_ex(&settings, json, strlen(json), error);
        if (pr.root &&
            pr.root->type == json_array &&
            pr.root->u.array.length == 1 &&
            pr.root->u.array.values[0]->type == json_string)
        {
            s = std::string(
                pr.root->u.array.values[0]->u.string.ptr,
                pr.root->u.array.values[0]->u.string.length);
            return true;
        }
        return false;
    }
#endif
};

REGISTER_TEST(UdpjsonTest);
<commit_msg>Temp disable udpjson on gcc<commit_after>#include "../test.h"

#if !defined(__GNUC__) || defined(__clang__) // gcc crash in Travis https://github.com/udp/json-builder/issues/7

extern "C" {

#include "udp-json-parser/json.h"
#include "udp-json-builder/json-builder.h"

} // extern "C"

static void GenStat(Stat* s, const json_value* v) {
    switch (v->type) {
    case json_object:
        for (size_t i = 0; i < v->u.object.length; i++) {
            const json_object_entry* e = v->u.object.values + i;
            GenStat(s, e->value);
            s->stringLength += e->name_length;
        }
        s->stringCount += v->u.object.length;
        s->memberCount += v->u.object.length;
        s->objectCount++;
        break;

    case json_array:
        for (size_t i = 0; i < v->u.array.length; i++)
            GenStat(s, v->u.array.values[i]);
        s->arrayCount++;
        s->elementCount += v->u.array.length;
        break;

    case json_string:
        s->stringCount++;
        s->stringLength += v->u.string.length;
        break;

    case json_integer:
    case json_double:
        s->numberCount++;
        break;

    case json_boolean:
        if (v->u.boolean)
            s->trueCount++;
        else
            s->falseCount++;
        break;

    case json_null:
        s->nullCount++;
        break;

    default:
        break;
    }
}

class UdpjsonParseResult : public ParseResultBase {
public:
    UdpjsonParseResult() : root() {}
    ~UdpjsonParseResult() { json_value_free(root); }

    json_value *root;
};

class UdpjsonStringResult : public StringResultBase {
public:
    UdpjsonStringResult() : s() {}
    ~UdpjsonStringResult() { free(s); }

    virtual const char* c_str() const { return s; }

    char* s;
};

class UdpjsonTest : public TestBase {
public:
#if TEST_INFO
    virtual const char* GetName() const { return "udp/json-parser (C)"; }
    virtual const char* GetFilename() const { return __FILE__; }
#endif

#if TEST_PARSE
    virtual ParseResultBase* Parse(const char* json, size_t length) const {
        UdpjsonParseResult* pr = new UdpjsonParseResult;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr->root = json_parse_ex(&settings, json, length, error);
        if (!pr->root) {
            delete pr;
            return 0;
        }
    	return pr;
    }
#endif

    // Very slow in the current version.
#if TEST_STRINGIFY
    virtual StringResultBase* Stringify(const ParseResultBase* parseResult) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        UdpjsonStringResult* sr = new UdpjsonStringResult;
        json_serialize_opts opts = { json_serialize_mode_packed, 0, 0 };
        sr->s = (char*)malloc(json_measure_ex(pr->root, opts));
        json_serialize_ex(sr->s, pr->root, opts);
        return sr;
    }
#endif

#if TEST_PRETTIFY
    virtual StringResultBase* Prettify(const ParseResultBase* parseResult) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        UdpjsonStringResult* sr = new UdpjsonStringResult;
        json_serialize_opts opts = { json_serialize_mode_multiline, 0, 4 };
        sr->s = (char*)malloc(json_measure_ex(pr->root, opts));
        json_serialize_ex(sr->s, pr->root, opts);
        return sr;
    }
#endif

#if TEST_STATISTICS
    virtual bool Statistics(const ParseResultBase* parseResult, Stat* stat) const {
        const UdpjsonParseResult* pr = static_cast<const UdpjsonParseResult*>(parseResult);
        memset(stat, 0, sizeof(Stat));
        GenStat(stat, pr->root);
        return true;
    }
#endif

#if TEST_CONFORMANCE
    virtual bool ParseDouble(const char* json, double* d) const {
        UdpjsonParseResult pr;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr.root = json_parse_ex(&settings, json, strlen(json), error);
        if (pr.root &&
            pr.root->type == json_array &&
            pr.root->u.array.length == 1 &&
            pr.root->u.array.values[0]->type == json_double)
        {
            *d = pr.root->u.array.values[0]->u.dbl;
            return true;
        }
        return false;
    }

    virtual bool ParseString(const char* json, std::string& s) const {
        UdpjsonParseResult pr;
        json_settings settings = json_settings();
        settings.value_extra = json_builder_extra;  /* space for json-builder state */
        char error[128];
        pr.root = json_parse_ex(&settings, json, strlen(json), error);
        if (pr.root &&
            pr.root->type == json_array &&
            pr.root->u.array.length == 1 &&
            pr.root->u.array.values[0]->type == json_string)
        {
            s = std::string(
                pr.root->u.array.values[0]->u.string.ptr,
                pr.root->u.array.values[0]->u.string.length);
            return true;
        }
        return false;
    }
#endif
};

REGISTER_TEST(UdpjsonTest);

#endif
<|endoftext|>