<commit_before>/** \brief Utility for validating and fixing up records harvested by zts_harvester
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <map>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "EmailSender.h"
#include "FileUtil.h"
#include "IniFile.h"
#include "MARC.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]]
void Usage() {
   ::Usage("input_file output_file missed_expectations_file email_address");
}

enum FieldPresence { ALWAYS, SOMETIMES, IGNORE };

struct FieldInfo {
    std::string name_;
    FieldPresence presence_;
public:
    FieldInfo(const std::string &name, const FieldPresence presence): name_(name), presence_(presence) { }
};

class JournalInfo {
    bool not_in_database_yet_;
    std::vector<FieldInfo> field_infos_;
public:
    using const_iterator = std::vector<FieldInfo>::const_iterator;
    using iterator = std::vector<FieldInfo>::iterator;
public:
    explicit JournalInfo(const bool not_in_database_yet): not_in_database_yet_(not_in_database_yet) { }
    JournalInfo() = default;
    JournalInfo(const JournalInfo &rhs) = default;

    size_t size() const { return field_infos_.size(); }
    bool isInDatabase() const { return not not_in_database_yet_; }
    void addField(const std::string &field_name, const FieldPresence field_presence)
        { field_infos_.emplace_back(field_name, field_presence); }
    const_iterator begin() const { return field_infos_.cbegin(); }
    const_iterator end() const { return field_infos_.cend(); }
    iterator begin() { return field_infos_.begin(); }
    iterator end() { return field_infos_.end(); }
    iterator find(const std::string &field_name) {
        return std::find_if(field_infos_.begin(), field_infos_.end(),
                            [&field_name](const FieldInfo &field_info){ return field_name == field_info.name_; });
    }
};

std::string GetJournalNameOrDie(const MARC::Record &record) {
    const auto journal_name(record.getSuperiorTitle());
    if (unlikely(journal_name.empty()))
        LOG_ERROR("the record w/ control number \"" + record.getControlNumber() + "\" is missing a superior title!");

    return journal_name;
}

FieldPresence StringToFieldPresence(const std::string &s) {
    if (s == "always")
        return ALWAYS;
    if (s == "sometimes")
        return SOMETIMES;
    if (s == "ignore")
        return IGNORE;
    LOG_ERROR("unknown enumerated value \"" + s + "\"!");
}

std::string FieldPresenceToString(const FieldPresence field_presence) {
    switch (field_presence) {
    case ALWAYS:
        return "always";
    case SOMETIMES:
        return "sometimes";
    case IGNORE:
        return "ignore";
    default:
        LOG_ERROR("we should *never get here!");
    }
}

void LoadFromDatabaseOrCreateFromScratch(DbConnection * const db_connection, const std::string &journal_name,
                                         JournalInfo * const journal_info)
{
    db_connection->queryOrDie("SELECT metadata_field_name,field_presence FROM metadata_presence_tracer WHERE journal_name='"
                              + db_connection->escapeString(journal_name) + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (result_set.empty()) {
        LOG_INFO("\"" + journal_name + "\" was not yet in the database.");
        *journal_info = JournalInfo(/* not_in_database_yet = */true);
        return;
    }

    *journal_info = JournalInfo(/* not_in_database_yet = */false);
    while (auto row = result_set.getNextRow())
        journal_info->addField(row["metadata_field_name"], StringToFieldPresence(row["field_presence"]));
    LOG_INFO("Loadad " + std::to_string(journal_info->size()) + " entries for \"" + journal_name + "\" from the database.");
}

// Two-way mapping required as the map is uni-directional
const std::map<std::string, std::string> EQUIVALENT_TAGS_MAP{
    { "700", "100" }, { "100", "700" }
};

void AnalyseNewJournalRecord(const MARC::Record &record, const bool first_record, JournalInfo * const journal_info) {
    std::unordered_set<std::string> seen_tags;
    MARC::Tag last_tag;
    for (const auto &field : record) {
        auto current_tag(field.getTag());
        if (current_tag == last_tag)
            continue;

        seen_tags.emplace(current_tag.toString());

        if (first_record)
            journal_info->addField(current_tag.toString(), ALWAYS);
        else if (journal_info->find(current_tag.toString()) == journal_info->end())
            journal_info->addField(current_tag.toString(), SOMETIMES);

        last_tag = current_tag;
    }

    for (auto &field_info : *journal_info) {
        if (seen_tags.find(field_info.name_) == seen_tags.end())
            field_info.presence_ = SOMETIMES;
    }
}

bool RecordMeetsExpectations(const MARC::Record &record, const std::string &journal_name, const JournalInfo &journal_info) {
    std::unordered_set<std::string> seen_tags;
    MARC::Tag last_tag;
    for (const auto &field : record) {
        const auto current_tag(field.getTag());
        if (current_tag == last_tag)
            continue;
        seen_tags.emplace(current_tag.toString());
        last_tag = current_tag;
    }

    bool missed_at_least_one_expectation(false);
    for (const auto &field_info : journal_info) {
        if (field_info.presence_ != ALWAYS)
            continue;   // we only care about required fields that are missing

        const auto equivalent_tag(EQUIVALENT_TAGS_MAP.find(field_info.name_));
        if (seen_tags.find(field_info.name_) != seen_tags.end())
            ;// required tag found
        else if (equivalent_tag != EQUIVALENT_TAGS_MAP.end() and seen_tags.find(equivalent_tag->second) != seen_tags.end())
            ;// equivalent tag found
        else {
            LOG_WARNING("Record w/ control number " + record.getControlNumber() + " in \"" + journal_name
                     + "\" is missing the always expected " + field_info.name_ + " field.");
            missed_at_least_one_expectation = true;
        }
    }

    return not missed_at_least_one_expectation;
}

void WriteToDatabase(DbConnection * const db_connection, const std::string &journal_name, const JournalInfo &journal_info) {
    for (const auto &field_info : journal_info)
        db_connection->queryOrDie("INSERT INTO metadata_presence_tracer SET journal_name='" + journal_name
                                  + "', metadata_field_name='" + db_connection->escapeString(field_info.name_)
                                  + "', field_presence='" + FieldPresenceToString(field_info.presence_) + "'");
}

void SendEmail(const std::string &email_address, const std::string &message_body) {
    const auto reply_code(EmailSender::SendEmail("zts_harvester_delivery_pipeline@uni-tuebingen.de",
                          email_address, "validate_harvested_records encountered problems", message_body,
                          EmailSender::MEDIUM));

    if (reply_code >= 300)
        LOG_ERROR("failed to send email, the response code was: " + std::to_string(reply_code));
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 5)
        Usage();

    DbConnection db_connection;
    auto reader(MARC::Reader::Factory(argv[1]));
    auto valid_records_writer(MARC::Writer::Factory(argv[2]));
    auto delinquent_records_writer(MARC::Writer::Factory(argv[3]));
    std::map<std::string, JournalInfo> journal_name_to_info_map;
    const std::string email_address(argv[4]);

    unsigned total_record_count(0), new_record_count(0), missed_expectation_count(0);
    while (const auto record = reader->read()) {
        ++total_record_count;
        const auto journal_name(GetJournalNameOrDie(record));

        auto journal_name_and_info(journal_name_to_info_map.find(journal_name));
        bool first_record(false); // True if the current record is the first encounter of a journal
        if (journal_name_and_info == journal_name_to_info_map.end()) {
            first_record = true;
            JournalInfo new_journal_info;
            LoadFromDatabaseOrCreateFromScratch(&db_connection, journal_name, &new_journal_info);
            journal_name_to_info_map[journal_name] = new_journal_info;
            journal_name_and_info = journal_name_to_info_map.find(journal_name);
        }

        bool missed_expectation(false);
        if (journal_name_and_info->second.isInDatabase()) {
            if (not RecordMeetsExpectations(record, journal_name_and_info->first, journal_name_and_info->second)) {
                missed_expectation = true;
                ++missed_expectation_count;
            }
        } else {
            AnalyseNewJournalRecord(record, first_record, &journal_name_and_info->second);
            ++new_record_count;
        }

        if (missed_expectation)
            delinquent_records_writer->write(record);
        else
            valid_records_writer->write(record);
    }

    for (const auto &journal_name_and_info : journal_name_to_info_map) {
        if (not journal_name_and_info.second.isInDatabase())
            WriteToDatabase(&db_connection, journal_name_and_info.first, journal_name_and_info.second);
    }

    if (missed_expectation_count > 0) {
        // send notification to the email address
        SendEmail(email_address, "Some records missed expectations with respect to MARC fields. Check "
                  "/usr/local/var/log/tuefind/zts_harvester_delivery_pipeline.log for details.");
    }

    LOG_INFO("Processed " + std::to_string(total_record_count) + " record(s) of which " + std::to_string(new_record_count)
             + " was/were (a) record(s) of new journals and " + std::to_string(missed_expectation_count)
             + " record(s) missed expectations.");

    return EXIT_SUCCESS;
}
<commit_msg>Removed unused header include<commit_after>/** \brief Utility for validating and fixing up records harvested by zts_harvester
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2018,2019 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <algorithm>
#include <iostream>
#include <map>
#include <unordered_set>
#include <cstdio>
#include <cstdlib>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "EmailSender.h"
#include "IniFile.h"
#include "MARC.h"
#include "UBTools.h"
#include "util.h"

namespace {

[[noreturn]]
void Usage() {
   ::Usage("marc_input marc_output missed_expectations_file email_address");
}

enum FieldPresence { ALWAYS, SOMETIMES, IGNORE };

struct FieldInfo {
    std::string name_;
    FieldPresence presence_;
public:
    FieldInfo(const std::string &name, const FieldPresence presence): name_(name), presence_(presence) { }
};

class JournalInfo {
    bool not_in_database_yet_;
    std::vector<FieldInfo> field_infos_;
public:
    using const_iterator = std::vector<FieldInfo>::const_iterator;
    using iterator = std::vector<FieldInfo>::iterator;
public:
    explicit JournalInfo(const bool not_in_database_yet): not_in_database_yet_(not_in_database_yet) { }
    JournalInfo() = default;
    JournalInfo(const JournalInfo &rhs) = default;

    size_t size() const { return field_infos_.size(); }
    bool isInDatabase() const { return not not_in_database_yet_; }
    void addField(const std::string &field_name, const FieldPresence field_presence)
        { field_infos_.emplace_back(field_name, field_presence); }
    const_iterator begin() const { return field_infos_.cbegin(); }
    const_iterator end() const { return field_infos_.cend(); }
    iterator begin() { return field_infos_.begin(); }
    iterator end() { return field_infos_.end(); }
    iterator find(const std::string &field_name) {
        return std::find_if(field_infos_.begin(), field_infos_.end(),
                            [&field_name](const FieldInfo &field_info){ return field_name == field_info.name_; });
    }
};

std::string GetJournalNameOrDie(const MARC::Record &record) {
    const auto journal_name(record.getSuperiorTitle());
    if (unlikely(journal_name.empty()))
        LOG_ERROR("the record w/ control number \"" + record.getControlNumber() + "\" is missing a superior title!");

    return journal_name;
}

FieldPresence StringToFieldPresence(const std::string &s) {
    if (s == "always")
        return ALWAYS;
    if (s == "sometimes")
        return SOMETIMES;
    if (s == "ignore")
        return IGNORE;
    LOG_ERROR("unknown enumerated value \"" + s + "\"!");
}

std::string FieldPresenceToString(const FieldPresence field_presence) {
    switch (field_presence) {
    case ALWAYS:
        return "always";
    case SOMETIMES:
        return "sometimes";
    case IGNORE:
        return "ignore";
    default:
        LOG_ERROR("we should *never get here!");
    }
}

void LoadFromDatabaseOrCreateFromScratch(DbConnection * const db_connection, const std::string &journal_name,
                                         JournalInfo * const journal_info)
{
    db_connection->queryOrDie("SELECT metadata_field_name,field_presence FROM metadata_presence_tracer WHERE journal_name='"
                              + db_connection->escapeString(journal_name) + "'");
    DbResultSet result_set(db_connection->getLastResultSet());
    if (result_set.empty()) {
        LOG_INFO("\"" + journal_name + "\" was not yet in the database.");
        *journal_info = JournalInfo(/* not_in_database_yet = */true);
        return;
    }

    *journal_info = JournalInfo(/* not_in_database_yet = */false);
    while (auto row = result_set.getNextRow())
        journal_info->addField(row["metadata_field_name"], StringToFieldPresence(row["field_presence"]));
    LOG_INFO("Loadad " + std::to_string(journal_info->size()) + " entries for \"" + journal_name + "\" from the database.");
}

// Two-way mapping required as the map is uni-directional
const std::map<std::string, std::string> EQUIVALENT_TAGS_MAP{
    { "700", "100" }, { "100", "700" }
};

void AnalyseNewJournalRecord(const MARC::Record &record, const bool first_record, JournalInfo * const journal_info) {
    std::unordered_set<std::string> seen_tags;
    MARC::Tag last_tag;
    for (const auto &field : record) {
        auto current_tag(field.getTag());
        if (current_tag == last_tag)
            continue;

        seen_tags.emplace(current_tag.toString());

        if (first_record)
            journal_info->addField(current_tag.toString(), ALWAYS);
        else if (journal_info->find(current_tag.toString()) == journal_info->end())
            journal_info->addField(current_tag.toString(), SOMETIMES);

        last_tag = current_tag;
    }

    for (auto &field_info : *journal_info) {
        if (seen_tags.find(field_info.name_) == seen_tags.end())
            field_info.presence_ = SOMETIMES;
    }
}

bool RecordMeetsExpectations(const MARC::Record &record, const std::string &journal_name, const JournalInfo &journal_info) {
    std::unordered_set<std::string> seen_tags;
    MARC::Tag last_tag;
    for (const auto &field : record) {
        const auto current_tag(field.getTag());
        if (current_tag == last_tag)
            continue;
        seen_tags.emplace(current_tag.toString());
        last_tag = current_tag;
    }

    bool missed_at_least_one_expectation(false);
    for (const auto &field_info : journal_info) {
        if (field_info.presence_ != ALWAYS)
            continue;   // we only care about required fields that are missing

        const auto equivalent_tag(EQUIVALENT_TAGS_MAP.find(field_info.name_));
        if (seen_tags.find(field_info.name_) != seen_tags.end())
            ;// required tag found
        else if (equivalent_tag != EQUIVALENT_TAGS_MAP.end() and seen_tags.find(equivalent_tag->second) != seen_tags.end())
            ;// equivalent tag found
        else {
            LOG_WARNING("Record w/ control number " + record.getControlNumber() + " in \"" + journal_name
                     + "\" is missing the always expected " + field_info.name_ + " field.");
            missed_at_least_one_expectation = true;
        }
    }

    return not missed_at_least_one_expectation;
}

void WriteToDatabase(DbConnection * const db_connection, const std::string &journal_name, const JournalInfo &journal_info) {
    for (const auto &field_info : journal_info)
        db_connection->queryOrDie("INSERT INTO metadata_presence_tracer SET journal_name='" + journal_name
                                  + "', metadata_field_name='" + db_connection->escapeString(field_info.name_)
                                  + "', field_presence='" + FieldPresenceToString(field_info.presence_) + "'");
}

void SendEmail(const std::string &email_address, const std::string &message_body) {
    const auto reply_code(EmailSender::SendEmail("zts_harvester_delivery_pipeline@uni-tuebingen.de",
                          email_address, "validate_harvested_records encountered problems", message_body,
                          EmailSender::MEDIUM));

    if (reply_code >= 300)
        LOG_ERROR("failed to send email, the response code was: " + std::to_string(reply_code));
}

} // unnamed namespace

int Main(int argc, char *argv[]) {
    if (argc != 5)
        Usage();

    DbConnection db_connection;
    auto reader(MARC::Reader::Factory(argv[1]));
    auto valid_records_writer(MARC::Writer::Factory(argv[2]));
    auto delinquent_records_writer(MARC::Writer::Factory(argv[3]));
    std::map<std::string, JournalInfo> journal_name_to_info_map;
    const std::string email_address(argv[4]);

    unsigned total_record_count(0), new_record_count(0), missed_expectation_count(0);
    while (const auto record = reader->read()) {
        ++total_record_count;
        const auto journal_name(GetJournalNameOrDie(record));

        auto journal_name_and_info(journal_name_to_info_map.find(journal_name));
        bool first_record(false); // True if the current record is the first encounter of a journal
        if (journal_name_and_info == journal_name_to_info_map.end()) {
            first_record = true;
            JournalInfo new_journal_info;
            LoadFromDatabaseOrCreateFromScratch(&db_connection, journal_name, &new_journal_info);
            journal_name_to_info_map[journal_name] = new_journal_info;
            journal_name_and_info = journal_name_to_info_map.find(journal_name);
        }

        bool missed_expectation(false);
        if (journal_name_and_info->second.isInDatabase()) {
            if (not RecordMeetsExpectations(record, journal_name_and_info->first, journal_name_and_info->second)) {
                missed_expectation = true;
                ++missed_expectation_count;
            }
        } else {
            AnalyseNewJournalRecord(record, first_record, &journal_name_and_info->second);
            ++new_record_count;
        }

        if (missed_expectation)
            delinquent_records_writer->write(record);
        else
            valid_records_writer->write(record);
    }

    for (const auto &journal_name_and_info : journal_name_to_info_map) {
        if (not journal_name_and_info.second.isInDatabase())
            WriteToDatabase(&db_connection, journal_name_and_info.first, journal_name_and_info.second);
    }

    if (missed_expectation_count > 0) {
        // send notification to the email address
        SendEmail(email_address, "Some records missed expectations with respect to MARC fields. Check "
                  "/usr/local/var/log/tuefind/zts_harvester_delivery_pipeline.log for details.");
    }

    LOG_INFO("Processed " + std::to_string(total_record_count) + " record(s) of which " + std::to_string(new_record_count)
             + " was/were (a) record(s) of new journals and " + std::to_string(missed_expectation_count)
             + " record(s) missed expectations.");

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>// Author: Enrico Guiraud, Danilo Piparo CERN  03/2017

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h" // R__USE_IMT
#include "ROOT/TDFNodes.hxx"
#include "ROOT/TSpinMutex.hxx"
#include "ROOT/TTreeProcessorMT.hxx"
#include "RtypesCore.h" // Long64_t
#include "TROOT.h"      // IsImplicitMTEnabled
#include "TTreeReader.h"

#include <mutex>
#include <numeric> // std::accumulate
#include <string>
class TDirectory;
class TTree;

namespace ROOT {
namespace Internal {

TDataFrameActionBase::TDataFrameActionBase(ROOT::Detail::TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches)
{
}

void TDataFrameActionBase::CreateSlots(unsigned int nSlots)
{
   fReaderValues.resize(nSlots);
}

} // end NS Internal

namespace Detail {

TDataFrameBranchBase::TDataFrameBranchBase(TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches,
                                           const std::string &name)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches), fName(name){};

BranchNames_t TDataFrameBranchBase::GetTmpBranches() const
{
   return fTmpBranches;
}

std::string TDataFrameBranchBase::GetName() const
{
   return fName;
}

TDataFrameImpl *TDataFrameBranchBase::GetImplPtr() const
{
   return fImplPtr;
}

TDataFrameFilterBase::TDataFrameFilterBase(TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches,
                                           const std::string &name)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches), fName(name){};

TDataFrameImpl *TDataFrameFilterBase::GetImplPtr() const
{
   return fImplPtr;
}

BranchNames_t TDataFrameFilterBase::GetTmpBranches() const
{
   return fTmpBranches;
}

bool TDataFrameFilterBase::HasName() const
{
   return !fName.empty();
};

void TDataFrameFilterBase::CreateSlots(unsigned int nSlots)
{
   fReaderValues.resize(nSlots);
   fLastCheckedEntry.resize(nSlots, -1);
   fLastResult.resize(nSlots);
   fAccepted.resize(nSlots);
   fRejected.resize(nSlots);
   // fAccepted and fRejected could be different than 0 if this is not the
   // first event-loop run using this filter
   std::fill(fAccepted.begin(), fAccepted.end(), 0);
   std::fill(fRejected.begin(), fRejected.end(), 0);
}

void TDataFrameFilterBase::PrintReport() const
{
   if (fName.empty()) // PrintReport is no-op for unnamed filters
      return;
   const auto accepted = std::accumulate(fAccepted.begin(), fAccepted.end(), 0ULL);
   const auto all      = accepted + std::accumulate(fRejected.begin(), fRejected.end(), 0ULL);
   double     perc     = accepted;
   if (all > 0) perc /= all;
   perc *= 100.;
   Printf("%-10s: pass=%-10lld all=%-10lld -- %8.3f %%", fName.c_str(), accepted, all, perc);
}

TDataFrameImpl::TDataFrameImpl(TTree *tree, const BranchNames_t &defaultBranches)
   : fTree(tree), fDefaultBranches(defaultBranches), fNSlots(ROOT::Internal::GetNSlots())
{
}

// This is an helper class to allow to pick a slot without resorting to a map
// indexed by thread ids.
// WARNING: this class does not work as a regular stack. The size is
// fixed at construction time and no blocking is foreseen.
// TODO move into TDFUtils
class TSlotStack {
private:
   unsigned int              fCursor;
   std::vector<unsigned int> fBuf;
   ROOT::TSpinMutex          fMutex;

public:
   TSlotStack() = delete;
   TSlotStack(unsigned int size) : fCursor(size), fBuf(size) { std::iota(fBuf.begin(), fBuf.end(), 0U); }
   void Push(unsigned int slotNumber)
   {
      std::lock_guard<ROOT::TSpinMutex> guard(fMutex);
      fBuf[fCursor++] = slotNumber;
   };
   unsigned int Pop()
   {
      std::lock_guard<ROOT::TSpinMutex> guard(fMutex);
      return fBuf[--fCursor];
   }
};

void TDataFrameImpl::Run()
{
#ifdef R__USE_IMT
   if (ROOT::IsImplicitMTEnabled()) {
      using ttpmt_t = ROOT::TTreeProcessorMT;
      std::unique_ptr<ttpmt_t> tp;
      tp.reset(new ttpmt_t(*fTree));

      TSlotStack slotStack(fNSlots);
      CreateSlots(fNSlots);
      tp->Process([this, &slotStack](TTreeReader &r) -> void {
         auto slot = slotStack.Pop();
         BuildAllReaderValues(r, slot);
         // recursive call to check filters and conditionally execute actions
         while (r.Next()) {
            const auto currEntry = r.GetCurrentEntry();
            for (auto &actionPtr : fBookedActions) actionPtr->Run(slot, currEntry);
            for (auto &namedFilterPtr : fBookedNamedFilters) namedFilterPtr->CheckFilters(slot, currEntry);
         }
         slotStack.Push(slot);
      });
   } else {
#endif // R__USE_IMT
      TTreeReader r(fTree);

      CreateSlots(1);
      BuildAllReaderValues(r, 0);

      // recursive call to check filters and conditionally execute actions
      while (r.Next()) {
         const auto currEntry = r.GetCurrentEntry();
         for (auto &actionPtr : fBookedActions) actionPtr->Run(0, currEntry);
         for (auto &namedFilterPtr : fBookedNamedFilters) namedFilterPtr->CheckFilters(0, currEntry);
      }
#ifdef R__USE_IMT
   }
#endif // R__USE_IMT

   fHasRunAtLeastOnce = true;
   // forget actions and "detach" the action result pointers marking them ready
   // and forget them too
   fBookedActions.clear();
   for (auto readiness : fResProxyReadiness) {
      *readiness.get() = true;
   }
   fResProxyReadiness.clear();
}

/// Build TTreeReaderValues for all nodes
///
/// This method loops over all filters, actions and other booked objects and
/// calls their `BuildReaderValues` methods. It is called once per node per slot, before
/// running the event loop. It also informs each node of the TTreeReader that
/// a particular slot will be using.
void TDataFrameImpl::BuildAllReaderValues(TTreeReader &r, unsigned int slot)
{
   for (auto &ptr : fBookedActions) ptr->BuildReaderValues(r, slot);
   for (auto &ptr : fBookedFilters) ptr->BuildReaderValues(r, slot);
   for (auto &bookedBranch : fBookedBranches) bookedBranch.second->BuildReaderValues(r, slot);
}

/// Initialize all nodes of the functional graph before running the event loop
///
/// This method loops over all filters, actions and other booked objects and
/// calls their `CreateSlots` methods. It is called once per node before running the
/// event loop. The main effect is to inform all nodes of the number of slots
/// (i.e. workers) that will be used to perform the event loop.
void TDataFrameImpl::CreateSlots(unsigned int nSlots)
{
   for (auto &ptr : fBookedActions) ptr->CreateSlots(nSlots);
   for (auto &ptr : fBookedFilters) ptr->CreateSlots(nSlots);
   for (auto &bookedBranch : fBookedBranches) bookedBranch.second->CreateSlots(nSlots);
}

TDataFrameImpl *TDataFrameImpl::GetImplPtr()
{
   return this;
}

const BranchNames_t &TDataFrameImpl::GetDefaultBranches() const
{
   return fDefaultBranches;
}

TTree *TDataFrameImpl::GetTree() const
{
   return fTree;
}

const TDataFrameBranchBase &TDataFrameImpl::GetBookedBranch(const std::string &name) const
{
   return *fBookedBranches.find(name)->second.get();
}

void *TDataFrameImpl::GetTmpBranchValue(const std::string &branch, unsigned int slot, Long64_t entry)
{
   return fBookedBranches.at(branch)->GetValue(slot, entry);
}

TDirectory *TDataFrameImpl::GetDirectory() const
{
   return fDirPtr;
}

std::string TDataFrameImpl::GetTreeName() const
{
   return fTree->GetName();
}

void TDataFrameImpl::Book(const ROOT::Internal::ActionBasePtr_t &actionPtr)
{
   fBookedActions.emplace_back(actionPtr);
}

void TDataFrameImpl::Book(const ROOT::Detail::FilterBasePtr_t &filterPtr)
{
   fBookedFilters.emplace_back(filterPtr);
   if (filterPtr->HasName()) {
      fBookedNamedFilters.emplace_back(filterPtr);
   }
}

void TDataFrameImpl::Book(const ROOT::Detail::TmpBranchBasePtr_t &branchPtr)
{
   fBookedBranches[branchPtr->GetName()] = branchPtr;
}

// dummy call, end of recursive chain of calls
bool TDataFrameImpl::CheckFilters(int, unsigned int)
{
   return true;
}

unsigned int TDataFrameImpl::GetNSlots() const
{
   return fNSlots;
}

/// Call `PrintReport` on all booked filters
void TDataFrameImpl::Report() const
{
   for (const auto &fPtr : fBookedNamedFilters) fPtr->PrintReport();
}

} // end NS Detail
} // end NS ROOT
<commit_msg>[TDF] Make TSlotStack helper sturdier against error condtions.<commit_after>// Author: Enrico Guiraud, Danilo Piparo CERN  03/2017

/*************************************************************************
 * Copyright (C) 1995-2016, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "RConfigure.h" // R__USE_IMT
#include "ROOT/TDFNodes.hxx"
#include "ROOT/TSpinMutex.hxx"
#include "ROOT/TTreeProcessorMT.hxx"
#include "RtypesCore.h" // Long64_t
#include "TROOT.h"      // IsImplicitMTEnabled
#include "TTreeReader.h"

#include <cassert>
#include <mutex>
#include <numeric> // std::accumulate
#include <string>
class TDirectory;
class TTree;

namespace ROOT {
namespace Internal {

TDataFrameActionBase::TDataFrameActionBase(ROOT::Detail::TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches)
{
}

void TDataFrameActionBase::CreateSlots(unsigned int nSlots)
{
   fReaderValues.resize(nSlots);
}

} // end NS Internal

namespace Detail {

TDataFrameBranchBase::TDataFrameBranchBase(TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches,
                                           const std::string &name)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches), fName(name){};

BranchNames_t TDataFrameBranchBase::GetTmpBranches() const
{
   return fTmpBranches;
}

std::string TDataFrameBranchBase::GetName() const
{
   return fName;
}

TDataFrameImpl *TDataFrameBranchBase::GetImplPtr() const
{
   return fImplPtr;
}

TDataFrameFilterBase::TDataFrameFilterBase(TDataFrameImpl *implPtr, const BranchNames_t &tmpBranches,
                                           const std::string &name)
   : fImplPtr(implPtr), fTmpBranches(tmpBranches), fName(name){};

TDataFrameImpl *TDataFrameFilterBase::GetImplPtr() const
{
   return fImplPtr;
}

BranchNames_t TDataFrameFilterBase::GetTmpBranches() const
{
   return fTmpBranches;
}

bool TDataFrameFilterBase::HasName() const
{
   return !fName.empty();
};

void TDataFrameFilterBase::CreateSlots(unsigned int nSlots)
{
   fReaderValues.resize(nSlots);
   fLastCheckedEntry.resize(nSlots, -1);
   fLastResult.resize(nSlots);
   fAccepted.resize(nSlots);
   fRejected.resize(nSlots);
   // fAccepted and fRejected could be different than 0 if this is not the
   // first event-loop run using this filter
   std::fill(fAccepted.begin(), fAccepted.end(), 0);
   std::fill(fRejected.begin(), fRejected.end(), 0);
}

void TDataFrameFilterBase::PrintReport() const
{
   if (fName.empty()) // PrintReport is no-op for unnamed filters
      return;
   const auto accepted = std::accumulate(fAccepted.begin(), fAccepted.end(), 0ULL);
   const auto all      = accepted + std::accumulate(fRejected.begin(), fRejected.end(), 0ULL);
   double     perc     = accepted;
   if (all > 0) perc /= all;
   perc *= 100.;
   Printf("%-10s: pass=%-10lld all=%-10lld -- %8.3f %%", fName.c_str(), accepted, all, perc);
}

TDataFrameImpl::TDataFrameImpl(TTree *tree, const BranchNames_t &defaultBranches)
   : fTree(tree), fDefaultBranches(defaultBranches), fNSlots(ROOT::Internal::GetNSlots())
{
}

// This is an helper class to allow to pick a slot without resorting to a map
// indexed by thread ids.
// WARNING: this class does not work as a regular stack. The size is
// fixed at construction time and no blocking is foreseen.
// TODO move into TDFUtils
class TSlotStack {
private:
   unsigned int              fCursor;
   std::vector<unsigned int> fBuf;
   ROOT::TSpinMutex          fMutex;

public:
   TSlotStack() = delete;
   TSlotStack(unsigned int size) : fCursor(size), fBuf(size) { std::iota(fBuf.begin(), fBuf.end(), 0U); }
   void Push(unsigned int slotNumber)
   {
      std::lock_guard<ROOT::TSpinMutex> guard(fMutex);
      fBuf[fCursor++] = slotNumber;
      assert(fCursor <= fBuf.size() && "TSlotStack assumes that at most a fixed number of values can be present in the stack. fCursor is greater than the size of the internal buffer. This violates such assumption.");
   };
   unsigned int Pop()
   {
      assert(fCursor > 0 && "TSlotStack assumes that a value can be always popped. fCursor is <=0 and this violates such assumption.");
      std::lock_guard<ROOT::TSpinMutex> guard(fMutex);
      return fBuf[--fCursor];
   }
};

void TDataFrameImpl::Run()
{
#ifdef R__USE_IMT
   if (ROOT::IsImplicitMTEnabled()) {
      using ttpmt_t = ROOT::TTreeProcessorMT;
      std::unique_ptr<ttpmt_t> tp;
      tp.reset(new ttpmt_t(*fTree));

      TSlotStack slotStack(fNSlots);
      CreateSlots(fNSlots);
      tp->Process([this, &slotStack](TTreeReader &r) -> void {
         auto slot = slotStack.Pop();
         BuildAllReaderValues(r, slot);
         // recursive call to check filters and conditionally execute actions
         while (r.Next()) {
            const auto currEntry = r.GetCurrentEntry();
            for (auto &actionPtr : fBookedActions) actionPtr->Run(slot, currEntry);
            for (auto &namedFilterPtr : fBookedNamedFilters) namedFilterPtr->CheckFilters(slot, currEntry);
         }
         slotStack.Push(slot);
      });
   } else {
#endif // R__USE_IMT
      TTreeReader r(fTree);

      CreateSlots(1);
      BuildAllReaderValues(r, 0);

      // recursive call to check filters and conditionally execute actions
      while (r.Next()) {
         const auto currEntry = r.GetCurrentEntry();
         for (auto &actionPtr : fBookedActions) actionPtr->Run(0, currEntry);
         for (auto &namedFilterPtr : fBookedNamedFilters) namedFilterPtr->CheckFilters(0, currEntry);
      }
#ifdef R__USE_IMT
   }
#endif // R__USE_IMT

   fHasRunAtLeastOnce = true;
   // forget actions and "detach" the action result pointers marking them ready
   // and forget them too
   fBookedActions.clear();
   for (auto readiness : fResProxyReadiness) {
      *readiness.get() = true;
   }
   fResProxyReadiness.clear();
}

/// Build TTreeReaderValues for all nodes
///
/// This method loops over all filters, actions and other booked objects and
/// calls their `BuildReaderValues` methods. It is called once per node per slot, before
/// running the event loop. It also informs each node of the TTreeReader that
/// a particular slot will be using.
void TDataFrameImpl::BuildAllReaderValues(TTreeReader &r, unsigned int slot)
{
   for (auto &ptr : fBookedActions) ptr->BuildReaderValues(r, slot);
   for (auto &ptr : fBookedFilters) ptr->BuildReaderValues(r, slot);
   for (auto &bookedBranch : fBookedBranches) bookedBranch.second->BuildReaderValues(r, slot);
}

/// Initialize all nodes of the functional graph before running the event loop
///
/// This method loops over all filters, actions and other booked objects and
/// calls their `CreateSlots` methods. It is called once per node before running the
/// event loop. The main effect is to inform all nodes of the number of slots
/// (i.e. workers) that will be used to perform the event loop.
void TDataFrameImpl::CreateSlots(unsigned int nSlots)
{
   for (auto &ptr : fBookedActions) ptr->CreateSlots(nSlots);
   for (auto &ptr : fBookedFilters) ptr->CreateSlots(nSlots);
   for (auto &bookedBranch : fBookedBranches) bookedBranch.second->CreateSlots(nSlots);
}

TDataFrameImpl *TDataFrameImpl::GetImplPtr()
{
   return this;
}

const BranchNames_t &TDataFrameImpl::GetDefaultBranches() const
{
   return fDefaultBranches;
}

TTree *TDataFrameImpl::GetTree() const
{
   return fTree;
}

const TDataFrameBranchBase &TDataFrameImpl::GetBookedBranch(const std::string &name) const
{
   return *fBookedBranches.find(name)->second.get();
}

void *TDataFrameImpl::GetTmpBranchValue(const std::string &branch, unsigned int slot, Long64_t entry)
{
   return fBookedBranches.at(branch)->GetValue(slot, entry);
}

TDirectory *TDataFrameImpl::GetDirectory() const
{
   return fDirPtr;
}

std::string TDataFrameImpl::GetTreeName() const
{
   return fTree->GetName();
}

void TDataFrameImpl::Book(const ROOT::Internal::ActionBasePtr_t &actionPtr)
{
   fBookedActions.emplace_back(actionPtr);
}

void TDataFrameImpl::Book(const ROOT::Detail::FilterBasePtr_t &filterPtr)
{
   fBookedFilters.emplace_back(filterPtr);
   if (filterPtr->HasName()) {
      fBookedNamedFilters.emplace_back(filterPtr);
   }
}

void TDataFrameImpl::Book(const ROOT::Detail::TmpBranchBasePtr_t &branchPtr)
{
   fBookedBranches[branchPtr->GetName()] = branchPtr;
}

// dummy call, end of recursive chain of calls
bool TDataFrameImpl::CheckFilters(int, unsigned int)
{
   return true;
}

unsigned int TDataFrameImpl::GetNSlots() const
{
   return fNSlots;
}

/// Call `PrintReport` on all booked filters
void TDataFrameImpl::Report() const
{
   for (const auto &fPtr : fBookedNamedFilters) fPtr->PrintReport();
}

} // end NS Detail
} // end NS ROOT
<|endoftext|>


<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright © 2018 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * make_element
 * Convenience function to create element_ptr's.
 */

#ifndef SKUI_GUI_MAKE_ELEMENT_H
#define SKUI_GUI_MAKE_ELEMENT_H

#include "gui/element.h++"

#include <core/utility.h++>

#include <memory>
#include <utility>

namespace skui
{
  namespace gui
  {
    template<typename ElementType, typename... ArgumentTypes>
    std::unique_ptr<ElementType> make(ArgumentTypes&&... arguments)
    {
      return std::make_unique<ElementType>(std::forward<ArgumentTypes>(arguments)...);
    }

    template<typename... ElementTypes>
    element_ptrs make_element_ptrs(std::unique_ptr<ElementTypes>&&... elements)
    {
      element_ptrs result;
      (result.emplace_back(std::move(elements)), ...);
      return result;
    }
  }
}

#endif
<commit_msg>Remove silly std::make_unique wrapper.<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright © 2018 Ruben Van Boxem
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 **/

/*
 * make_element
 * Convenience function to create element_ptr's.
 */

#ifndef SKUI_GUI_MAKE_ELEMENT_H
#define SKUI_GUI_MAKE_ELEMENT_H

#include "gui/element.h++"

#include <core/utility.h++>

#include <memory>
#include <utility>

namespace skui
{
  namespace gui
  {
    template<typename... ElementTypes>
    element_ptrs make_element_ptrs(std::unique_ptr<ElementTypes>&&... elements)
    {
      element_ptrs result;
      (result.emplace_back(std::move(elements)), ...);
      return result;
    }
  }
}

#endif
<|endoftext|>


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

#include <map>
#include <set>

#include "build/build_config.h"
// Need to include this before most other files because it defines
// IPC_MESSAGE_LOG_ENABLED. We need to use it to define
// IPC_MESSAGE_MACROS_LOG_ENABLED so ppapi_messages.h will generate the
// ViewMsgLog et al. functions.

#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "components/tracing/child_trace_message_filter.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_message.h"
#include "native_client/src/shared/srpc/nacl_srpc.h"
#include "native_client/src/untrusted/irt/irt_ppapi.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/native_client/src/shared/ppapi_proxy/ppruntime.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/plugin_proxy_delegate.h"
#include "ppapi/shared_impl/ppb_audio_shared.h"

#if defined(IPC_MESSAGE_LOG_ENABLED)
#include "base/containers/hash_tables.h"

LogFunctionMap g_log_function_mapping;

#define IPC_MESSAGE_MACROS_LOG_ENABLED
#define IPC_LOG_TABLE_ADD_ENTRY(msg_id, logger) \
  g_log_function_mapping[msg_id] = logger

#endif
#include "ppapi/proxy/ppapi_messages.h"

// This must match up with NACL_CHROME_INITIAL_IPC_DESC,
// defined in sel_main_chrome.h
#define NACL_IPC_FD 6

using ppapi::proxy::PluginDispatcher;
using ppapi::proxy::PluginGlobals;
using ppapi::proxy::PluginProxyDelegate;
using ppapi::proxy::ProxyChannel;
using ppapi::proxy::SerializedHandle;

namespace {

// This class manages communication between the plugin and the browser, and
// manages the PluginDispatcher instances for communication between the plugin
// and the renderer.
class PpapiDispatcher : public ProxyChannel,
                        public PluginDispatcher::PluginDelegate,
                        public PluginProxyDelegate {
 public:
  explicit PpapiDispatcher(scoped_refptr<base::MessageLoopProxy> io_loop);

  // PluginDispatcher::PluginDelegate implementation.
  virtual base::MessageLoopProxy* GetIPCMessageLoop() OVERRIDE;
  virtual base::WaitableEvent* GetShutdownEvent() OVERRIDE;
  virtual IPC::PlatformFileForTransit ShareHandleWithRemote(
      base::PlatformFile handle,
      base::ProcessId peer_pid,
      bool should_close_source) OVERRIDE;
  virtual std::set<PP_Instance>* GetGloballySeenInstanceIDSet() OVERRIDE;
  virtual uint32 Register(PluginDispatcher* plugin_dispatcher) OVERRIDE;
  virtual void Unregister(uint32 plugin_dispatcher_id) OVERRIDE;

  // PluginProxyDelegate implementation.
  virtual IPC::Sender* GetBrowserSender() OVERRIDE;
  virtual std::string GetUILanguage() OVERRIDE;
  virtual void PreCacheFont(const void* logfontw) OVERRIDE;
  virtual void SetActiveURL(const std::string& url) OVERRIDE;
  virtual PP_Resource CreateBrowserFont(
      ppapi::proxy::Connection connection,
      PP_Instance instance,
      const PP_BrowserFont_Trusted_Description& desc,
      const ppapi::Preferences& prefs) OVERRIDE;

  // IPC::Listener implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

 private:
  void OnMsgCreateNaClChannel(int renderer_id,
                              const ppapi::PpapiNaClChannelArgs& args,
                              SerializedHandle handle);
  void OnMsgResourceReply(
      const ppapi::proxy::ResourceMessageReplyParams& reply_params,
      const IPC::Message& nested_msg);
  void OnPluginDispatcherMessageReceived(const IPC::Message& msg);

  std::set<PP_Instance> instances_;
  std::map<uint32, PluginDispatcher*> plugin_dispatchers_;
  uint32 next_plugin_dispatcher_id_;
  scoped_refptr<base::MessageLoopProxy> message_loop_;
  base::WaitableEvent shutdown_event_;
};

PpapiDispatcher::PpapiDispatcher(scoped_refptr<base::MessageLoopProxy> io_loop)
    : next_plugin_dispatcher_id_(0),
      message_loop_(io_loop),
      shutdown_event_(true, false) {
  IPC::ChannelHandle channel_handle(
      "NaCl IPC", base::FileDescriptor(NACL_IPC_FD, false));
  // We don't have/need a PID since handle sharing happens outside of the
  // NaCl sandbox.
  InitWithChannel(this, base::kNullProcessId, channel_handle,
                  false);  // Channel is server.
  channel()->AddFilter(
      new tracing::ChildTraceMessageFilter(message_loop_.get()));
}

base::MessageLoopProxy* PpapiDispatcher::GetIPCMessageLoop() {
  return message_loop_.get();
}

base::WaitableEvent* PpapiDispatcher::GetShutdownEvent() {
  return &shutdown_event_;
}

IPC::PlatformFileForTransit PpapiDispatcher::ShareHandleWithRemote(
    base::PlatformFile handle,
    base::ProcessId peer_pid,
    bool should_close_source) {
  return IPC::InvalidPlatformFileForTransit();
}

std::set<PP_Instance>* PpapiDispatcher::GetGloballySeenInstanceIDSet() {
  return &instances_;
}

uint32 PpapiDispatcher::Register(PluginDispatcher* plugin_dispatcher) {
  if (!plugin_dispatcher ||
      plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
    return 0;
  }

  uint32 id = 0;
  do {
    // Although it is unlikely, make sure that we won't cause any trouble
    // when the counter overflows.
    id = next_plugin_dispatcher_id_++;
  } while (id == 0 ||
           plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
  plugin_dispatchers_[id] = plugin_dispatcher;
  return id;
}

void PpapiDispatcher::Unregister(uint32 plugin_dispatcher_id) {
  plugin_dispatchers_.erase(plugin_dispatcher_id);
}

IPC::Sender* PpapiDispatcher::GetBrowserSender() {
  return this;
}

std::string PpapiDispatcher::GetUILanguage() {
  NOTIMPLEMENTED();
  return std::string();
}

void PpapiDispatcher::PreCacheFont(const void* logfontw) {
  NOTIMPLEMENTED();
}

void PpapiDispatcher::SetActiveURL(const std::string& url) {
  NOTIMPLEMENTED();
}

PP_Resource PpapiDispatcher::CreateBrowserFont(
    ppapi::proxy::Connection connection,
    PP_Instance instance,
    const PP_BrowserFont_Trusted_Description& desc,
    const ppapi::Preferences& prefs) {
  NOTIMPLEMENTED();
  return 0;
}

bool PpapiDispatcher::OnMessageReceived(const IPC::Message& msg) {
  IPC_BEGIN_MESSAGE_MAP(PpapiDispatcher, msg)
    IPC_MESSAGE_HANDLER(PpapiMsg_CreateNaClChannel, OnMsgCreateNaClChannel)
    IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnMsgResourceReply)
    // All other messages are simply forwarded to a PluginDispatcher.
    IPC_MESSAGE_UNHANDLED(OnPluginDispatcherMessageReceived(msg))
  IPC_END_MESSAGE_MAP()
  return true;
}

void PpapiDispatcher::OnMsgCreateNaClChannel(
    int renderer_id,
    const ppapi::PpapiNaClChannelArgs& args,
    SerializedHandle handle) {
  static bool command_line_and_logging_initialized = false;
  if (!command_line_and_logging_initialized) {
    CommandLine::Init(0, NULL);
    for (size_t i = 0; i < args.switch_names.size(); ++i) {
      DCHECK(i < args.switch_values.size());
      CommandLine::ForCurrentProcess()->AppendSwitchASCII(
          args.switch_names[i], args.switch_values[i]);
    }
    logging::LoggingSettings settings;
    settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
    logging::InitLogging(settings);
    command_line_and_logging_initialized = true;
  }
  // Tell the process-global GetInterface which interfaces it can return to the
  // plugin.
  ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(
      args.permissions);

  PluginDispatcher* dispatcher =
      new PluginDispatcher(::PPP_GetInterface, args.permissions,
                           args.off_the_record);
  // The channel handle's true name is not revealed here.
  IPC::ChannelHandle channel_handle("nacl", handle.descriptor());
  if (!dispatcher->InitPluginWithChannel(this, base::kNullProcessId,
                                         channel_handle, false)) {
    delete dispatcher;
    return;
  }
  // From here, the dispatcher will manage its own lifetime according to the
  // lifetime of the attached channel.
}

void PpapiDispatcher::OnMsgResourceReply(
    const ppapi::proxy::ResourceMessageReplyParams& reply_params,
    const IPC::Message& nested_msg) {
  ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
                                                        nested_msg);
}

void PpapiDispatcher::OnPluginDispatcherMessageReceived(
    const IPC::Message& msg) {
  // The first parameter should be a plugin dispatcher ID.
  PickleIterator iter(msg);
  uint32 id = 0;
  if (!msg.ReadUInt32(&iter, &id)) {
    NOTREACHED();
    return;
  }
  std::map<uint32, ppapi::proxy::PluginDispatcher*>::iterator dispatcher =
      plugin_dispatchers_.find(id);
  if (dispatcher != plugin_dispatchers_.end())
    dispatcher->second->OnMessageReceived(msg);
}

}  // namespace

void PpapiPluginRegisterThreadCreator(
    const struct PP_ThreadFunctions* thread_functions) {
  // Initialize all classes that need to create threads that call back into
  // user code.
  ppapi::PPB_Audio_Shared::SetThreadFunctions(thread_functions);
}

int PpapiPluginMain() {
  // Though it isn't referenced here, we must instantiate an AtExitManager.
  base::AtExitManager exit_manager;
  base::MessageLoop loop;
  IPC::Logging::set_log_function_map(&g_log_function_mapping);
  ppapi::proxy::PluginGlobals plugin_globals;
  base::Thread io_thread("Chrome_NaClIOThread");
  base::Thread::Options options;
  options.message_loop_type = base::MessageLoop::TYPE_IO;
  io_thread.StartWithOptions(options);

  // Start up the SRPC server on another thread. Otherwise, when it blocks
  // on an RPC, the PPAPI proxy will hang. Do this before we initialize the
  // module and start the PPAPI proxy so that the NaCl plugin can continue
  // loading the app.
  static struct NaClSrpcHandlerDesc srpc_methods[] = { { NULL, NULL } };
  if (!NaClSrpcAcceptClientOnThread(srpc_methods)) {
    return 1;
  }

  int32_t error = ::PPP_InitializeModule(
      0 /* module */,
      &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
  // TODO(dmichael): Handle other error conditions, like failure to connect?
  if (error)
    return error;

  PpapiDispatcher ppapi_dispatcher(io_thread.message_loop_proxy());
  plugin_globals.set_plugin_proxy_delegate(&ppapi_dispatcher);

  loop.Run();

  return 0;
}
<commit_msg>PPAPI/NaCl: Delay calling PPP_InitializeModule<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>
#include <set>

#include "build/build_config.h"
// Need to include this before most other files because it defines
// IPC_MESSAGE_LOG_ENABLED. We need to use it to define
// IPC_MESSAGE_MACROS_LOG_ENABLED so ppapi_messages.h will generate the
// ViewMsgLog et al. functions.

#include "base/command_line.h"
#include "base/message_loop/message_loop.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "components/tracing/child_trace_message_filter.h"
#include "ipc/ipc_channel_handle.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_message.h"
#include "native_client/src/shared/srpc/nacl_srpc.h"
#include "native_client/src/untrusted/irt/irt_ppapi.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/native_client/src/shared/ppapi_proxy/ppruntime.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/plugin_proxy_delegate.h"
#include "ppapi/shared_impl/ppb_audio_shared.h"

#if defined(IPC_MESSAGE_LOG_ENABLED)
#include "base/containers/hash_tables.h"

LogFunctionMap g_log_function_mapping;

#define IPC_MESSAGE_MACROS_LOG_ENABLED
#define IPC_LOG_TABLE_ADD_ENTRY(msg_id, logger) \
  g_log_function_mapping[msg_id] = logger

#endif
#include "ppapi/proxy/ppapi_messages.h"

// This must match up with NACL_CHROME_INITIAL_IPC_DESC,
// defined in sel_main_chrome.h
#define NACL_IPC_FD 6

using ppapi::proxy::PluginDispatcher;
using ppapi::proxy::PluginGlobals;
using ppapi::proxy::PluginProxyDelegate;
using ppapi::proxy::ProxyChannel;
using ppapi::proxy::SerializedHandle;

namespace {

// This class manages communication between the plugin and the browser, and
// manages the PluginDispatcher instances for communication between the plugin
// and the renderer.
class PpapiDispatcher : public ProxyChannel,
                        public PluginDispatcher::PluginDelegate,
                        public PluginProxyDelegate {
 public:
  explicit PpapiDispatcher(scoped_refptr<base::MessageLoopProxy> io_loop);

  // PluginDispatcher::PluginDelegate implementation.
  virtual base::MessageLoopProxy* GetIPCMessageLoop() OVERRIDE;
  virtual base::WaitableEvent* GetShutdownEvent() OVERRIDE;
  virtual IPC::PlatformFileForTransit ShareHandleWithRemote(
      base::PlatformFile handle,
      base::ProcessId peer_pid,
      bool should_close_source) OVERRIDE;
  virtual std::set<PP_Instance>* GetGloballySeenInstanceIDSet() OVERRIDE;
  virtual uint32 Register(PluginDispatcher* plugin_dispatcher) OVERRIDE;
  virtual void Unregister(uint32 plugin_dispatcher_id) OVERRIDE;

  // PluginProxyDelegate implementation.
  virtual IPC::Sender* GetBrowserSender() OVERRIDE;
  virtual std::string GetUILanguage() OVERRIDE;
  virtual void PreCacheFont(const void* logfontw) OVERRIDE;
  virtual void SetActiveURL(const std::string& url) OVERRIDE;
  virtual PP_Resource CreateBrowserFont(
      ppapi::proxy::Connection connection,
      PP_Instance instance,
      const PP_BrowserFont_Trusted_Description& desc,
      const ppapi::Preferences& prefs) OVERRIDE;

  // IPC::Listener implementation.
  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;

 private:
  void OnMsgCreateNaClChannel(int renderer_id,
                              const ppapi::PpapiNaClChannelArgs& args,
                              SerializedHandle handle);
  void OnMsgResourceReply(
      const ppapi::proxy::ResourceMessageReplyParams& reply_params,
      const IPC::Message& nested_msg);
  void OnPluginDispatcherMessageReceived(const IPC::Message& msg);

  std::set<PP_Instance> instances_;
  std::map<uint32, PluginDispatcher*> plugin_dispatchers_;
  uint32 next_plugin_dispatcher_id_;
  scoped_refptr<base::MessageLoopProxy> message_loop_;
  base::WaitableEvent shutdown_event_;
};

PpapiDispatcher::PpapiDispatcher(scoped_refptr<base::MessageLoopProxy> io_loop)
    : next_plugin_dispatcher_id_(0),
      message_loop_(io_loop),
      shutdown_event_(true, false) {
  IPC::ChannelHandle channel_handle(
      "NaCl IPC", base::FileDescriptor(NACL_IPC_FD, false));
  // We don't have/need a PID since handle sharing happens outside of the
  // NaCl sandbox.
  InitWithChannel(this, base::kNullProcessId, channel_handle,
                  false);  // Channel is server.
  channel()->AddFilter(
      new tracing::ChildTraceMessageFilter(message_loop_.get()));
}

base::MessageLoopProxy* PpapiDispatcher::GetIPCMessageLoop() {
  return message_loop_.get();
}

base::WaitableEvent* PpapiDispatcher::GetShutdownEvent() {
  return &shutdown_event_;
}

IPC::PlatformFileForTransit PpapiDispatcher::ShareHandleWithRemote(
    base::PlatformFile handle,
    base::ProcessId peer_pid,
    bool should_close_source) {
  return IPC::InvalidPlatformFileForTransit();
}

std::set<PP_Instance>* PpapiDispatcher::GetGloballySeenInstanceIDSet() {
  return &instances_;
}

uint32 PpapiDispatcher::Register(PluginDispatcher* plugin_dispatcher) {
  if (!plugin_dispatcher ||
      plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
    return 0;
  }

  uint32 id = 0;
  do {
    // Although it is unlikely, make sure that we won't cause any trouble
    // when the counter overflows.
    id = next_plugin_dispatcher_id_++;
  } while (id == 0 ||
           plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
  plugin_dispatchers_[id] = plugin_dispatcher;
  return id;
}

void PpapiDispatcher::Unregister(uint32 plugin_dispatcher_id) {
  plugin_dispatchers_.erase(plugin_dispatcher_id);
}

IPC::Sender* PpapiDispatcher::GetBrowserSender() {
  return this;
}

std::string PpapiDispatcher::GetUILanguage() {
  NOTIMPLEMENTED();
  return std::string();
}

void PpapiDispatcher::PreCacheFont(const void* logfontw) {
  NOTIMPLEMENTED();
}

void PpapiDispatcher::SetActiveURL(const std::string& url) {
  NOTIMPLEMENTED();
}

PP_Resource PpapiDispatcher::CreateBrowserFont(
    ppapi::proxy::Connection connection,
    PP_Instance instance,
    const PP_BrowserFont_Trusted_Description& desc,
    const ppapi::Preferences& prefs) {
  NOTIMPLEMENTED();
  return 0;
}

bool PpapiDispatcher::OnMessageReceived(const IPC::Message& msg) {
  IPC_BEGIN_MESSAGE_MAP(PpapiDispatcher, msg)
    IPC_MESSAGE_HANDLER(PpapiMsg_CreateNaClChannel, OnMsgCreateNaClChannel)
    IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnMsgResourceReply)
    // All other messages are simply forwarded to a PluginDispatcher.
    IPC_MESSAGE_UNHANDLED(OnPluginDispatcherMessageReceived(msg))
  IPC_END_MESSAGE_MAP()
  return true;
}

void PpapiDispatcher::OnMsgCreateNaClChannel(
    int renderer_id,
    const ppapi::PpapiNaClChannelArgs& args,
    SerializedHandle handle) {
  static bool command_line_and_logging_initialized = false;
  if (!command_line_and_logging_initialized) {
    CommandLine::Init(0, NULL);
    for (size_t i = 0; i < args.switch_names.size(); ++i) {
      DCHECK(i < args.switch_values.size());
      CommandLine::ForCurrentProcess()->AppendSwitchASCII(
          args.switch_names[i], args.switch_values[i]);
    }
    logging::LoggingSettings settings;
    settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
    logging::InitLogging(settings);
    command_line_and_logging_initialized = true;
  }
  // Tell the process-global GetInterface which interfaces it can return to the
  // plugin.
  ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(
      args.permissions);

  int32_t error = ::PPP_InitializeModule(
      0 /* module */,
      &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
  if (error)
    ::exit(error);

  PluginDispatcher* dispatcher =
      new PluginDispatcher(::PPP_GetInterface, args.permissions,
                           args.off_the_record);
  // The channel handle's true name is not revealed here.
  IPC::ChannelHandle channel_handle("nacl", handle.descriptor());
  if (!dispatcher->InitPluginWithChannel(this, base::kNullProcessId,
                                         channel_handle, false)) {
    delete dispatcher;
    return;
  }
  // From here, the dispatcher will manage its own lifetime according to the
  // lifetime of the attached channel.
}

void PpapiDispatcher::OnMsgResourceReply(
    const ppapi::proxy::ResourceMessageReplyParams& reply_params,
    const IPC::Message& nested_msg) {
  ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
                                                        nested_msg);
}

void PpapiDispatcher::OnPluginDispatcherMessageReceived(
    const IPC::Message& msg) {
  // The first parameter should be a plugin dispatcher ID.
  PickleIterator iter(msg);
  uint32 id = 0;
  if (!msg.ReadUInt32(&iter, &id)) {
    NOTREACHED();
    return;
  }
  std::map<uint32, ppapi::proxy::PluginDispatcher*>::iterator dispatcher =
      plugin_dispatchers_.find(id);
  if (dispatcher != plugin_dispatchers_.end())
    dispatcher->second->OnMessageReceived(msg);
}

}  // namespace

void PpapiPluginRegisterThreadCreator(
    const struct PP_ThreadFunctions* thread_functions) {
  // Initialize all classes that need to create threads that call back into
  // user code.
  ppapi::PPB_Audio_Shared::SetThreadFunctions(thread_functions);
}

int PpapiPluginMain() {
  // Though it isn't referenced here, we must instantiate an AtExitManager.
  base::AtExitManager exit_manager;
  base::MessageLoop loop;
  IPC::Logging::set_log_function_map(&g_log_function_mapping);
  ppapi::proxy::PluginGlobals plugin_globals;
  base::Thread io_thread("Chrome_NaClIOThread");
  base::Thread::Options options;
  options.message_loop_type = base::MessageLoop::TYPE_IO;
  io_thread.StartWithOptions(options);

  // Start up the SRPC server on another thread. Otherwise, when it blocks
  // on an RPC, the PPAPI proxy will hang. Do this before we initialize the
  // module and start the PPAPI proxy so that the NaCl plugin can continue
  // loading the app.
  static struct NaClSrpcHandlerDesc srpc_methods[] = { { NULL, NULL } };
  if (!NaClSrpcAcceptClientOnThread(srpc_methods)) {
    return 1;
  }

  PpapiDispatcher ppapi_dispatcher(io_thread.message_loop_proxy());
  plugin_globals.set_plugin_proxy_delegate(&ppapi_dispatcher);

  loop.Run();

  return 0;
}
<|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2017 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/diff_system.h"
#include "libmesh/dof_map.h"
#include "libmesh/libmesh_logging.h"
#include "libmesh/petsc_diff_solver.h"
#include "libmesh/petsc_matrix.h"
#include "libmesh/petsc_vector.h"
#include "libmesh/petsc_auto_fieldsplit.h"

#ifdef LIBMESH_HAVE_PETSC

namespace libMesh
{

//--------------------------------------------------------------------
// Functions with C linkage to pass to PETSc.  PETSc will call these
// methods as needed.
//
// Since they must have C linkage they have no knowledge of a namespace.
// Give them an obscure name to avoid namespace pollution.
extern "C"
{
  // Function to hand to PETSc's SNES,
  // which monitors convergence at X
  PetscErrorCode
  __libmesh_petsc_diff_solver_monitor (SNES snes,
                                       PetscInt its,
                                       PetscReal fnorm,
                                       void * ctx)
  {
    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver *> (ctx));

    if (solver.verbose)
      libMesh::out << "  PetscDiffSolver step " << its
                   << ", |residual|_2 = " << fnorm << std::endl;
    if (solver.linear_solution_monitor.get()) {
      int ierr = 0;

      Vec petsc_delta_u;
      ierr = SNESGetSolutionUpdate(snes, &petsc_delta_u);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> delta_u(petsc_delta_u, solver.comm());
      delta_u.close();

      Vec petsc_u;
      ierr = SNESGetSolution(snes, &petsc_u);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> u(petsc_u, solver.comm());
      u.close();

      Vec petsc_res;
      ierr = SNESGetFunction(snes, &petsc_res, libmesh_nullptr, libmesh_nullptr);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> res(petsc_res, solver.comm());
      res.close();

      (*solver.linear_solution_monitor)(
                                        delta_u, delta_u.l2_norm(),
                                        u, u.l2_norm(),
                                        res, res.l2_norm(), its);
    }
    return 0;
  }

  // Functions to hand to PETSc's SNES,
  // which compute the residual or jacobian at X
  PetscErrorCode
  __libmesh_petsc_diff_solver_residual (SNES, Vec x, Vec r, void * ctx)
  {
    libmesh_assert(x);
    libmesh_assert(r);
    libmesh_assert(ctx);

    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver*> (ctx));
    ImplicitSystem & sys = solver.system();

    if (solver.verbose)
      libMesh::out << "Assembling the residual" << std::endl;

    PetscVector<Number> & X_system =
      *cast_ptr<PetscVector<Number> *>(sys.solution.get());
    PetscVector<Number> & R_system =
      *cast_ptr<PetscVector<Number> *>(sys.rhs);
    PetscVector<Number> X_input(x, sys.comm()), R_input(r, sys.comm());

    // DiffSystem assembles from the solution and into the rhs, so swap
    // those with our input vectors before assembling.  They'll probably
    // already be references to the same vectors, but PETSc might do
    // something tricky.
    X_input.swap(X_system);
    R_input.swap(R_system);

    // We may need to correct a non-conforming solution
    sys.get_dof_map().enforce_constraints_exactly(sys);

    // We may need to localize a parallel solution
    sys.update();

    // Do DiffSystem assembly
    sys.assembly(true, false);
    R_system.close();

    // Swap back
    X_input.swap(X_system);
    R_input.swap(R_system);

    // No errors, we hope
    return 0;
  }

#if PETSC_RELEASE_LESS_THAN(3,5,0)
  PetscErrorCode
  __libmesh_petsc_diff_solver_jacobian (SNES,
                                        Vec x,
                                        Mat * libmesh_dbg_var(j),
                                        Mat * pc,
                                        MatStructure * msflag,
                                        void * ctx)
#else
    PetscErrorCode
    __libmesh_petsc_diff_solver_jacobian (SNES,
                                          Vec x,
                                          Mat libmesh_dbg_var(j),
                                          Mat pc,
                                          void * ctx)
#endif
  {
    libmesh_assert(x);
    libmesh_assert(j);
    //  libmesh_assert_equal_to (pc, j);  // We don't use separate preconditioners yet
    libmesh_assert(ctx);

    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver*> (ctx));
    ImplicitSystem & sys = solver.system();

    if (solver.verbose)
      libMesh::out << "Assembling the Jacobian" << std::endl;

    PetscVector<Number> & X_system =
      *cast_ptr<PetscVector<Number> *>(sys.solution.get());
    PetscVector<Number> X_input(x, sys.comm());

#if PETSC_RELEASE_LESS_THAN(3,5,0)
    PetscMatrix<Number> J_input(*pc, sys.comm());
#else
    PetscMatrix<Number> J_input(pc, sys.comm());
#endif
    PetscMatrix<Number> & J_system =
      *cast_ptr<PetscMatrix<Number> *>(sys.matrix);

    // DiffSystem assembles from the solution and into the jacobian, so
    // swap those with our input vectors before assembling.  They'll
    // probably already be references to the same vectors, but PETSc
    // might do something tricky.
    X_input.swap(X_system);
    J_input.swap(J_system);

    // We may need to correct a non-conforming solution
    sys.get_dof_map().enforce_constraints_exactly(sys);

    // We may need to localize a parallel solution
    sys.update();

    // Do DiffSystem assembly
    sys.assembly(false, true);
    J_system.close();

    // Swap back
    X_input.swap(X_system);
    J_input.swap(J_system);

#if PETSC_RELEASE_LESS_THAN(3,5,0)
    *msflag = SAME_NONZERO_PATTERN;
#endif
    // No errors, we hope
    return 0;
  }

} // extern "C"

PetscDiffSolver::PetscDiffSolver (sys_type & s)
  : Parent(s)
{
}

void PetscDiffSolver::init ()
{
  LOG_SCOPE("init()", "PetscDiffSolver");

  Parent::init();

  int ierr=0;

  ierr = SNESCreate(this->comm().get(),&_snes);
  LIBMESH_CHKERR(ierr);

  ierr = SNESMonitorSet (_snes, __libmesh_petsc_diff_solver_monitor,
                         this, PETSC_NULL);
  LIBMESH_CHKERR(ierr);

  if (libMesh::on_command_line("--solver_system_names"))
    {
      ierr = SNESSetOptionsPrefix(_snes, (_system.name()+"_").c_str());
      LIBMESH_CHKERR(ierr);
    }

  ierr = SNESSetFromOptions(_snes);
  LIBMESH_CHKERR(ierr);

  KSP my_ksp;
  ierr = SNESGetKSP(_snes, &my_ksp);
  LIBMESH_CHKERR(ierr);

  PC my_pc;
  ierr = KSPGetPC(my_ksp, &my_pc);
  LIBMESH_CHKERR(ierr);

  petsc_auto_fieldsplit(my_pc, _system);
}

PetscDiffSolver::~PetscDiffSolver ()
{
}

void PetscDiffSolver::clear()
{
  LOG_SCOPE("clear()", "PetscDiffSolver");

  int ierr = LibMeshSNESDestroy(&_snes);
  LIBMESH_CHKERR(ierr);
}

void PetscDiffSolver::reinit()
{
  Parent::reinit();

  KSP my_ksp;
  int ierr = SNESGetKSP(_snes, &my_ksp);
  LIBMESH_CHKERR(ierr);

  PC my_pc;
  ierr = KSPGetPC(my_ksp, &my_pc);
  LIBMESH_CHKERR(ierr);

  petsc_auto_fieldsplit(my_pc, _system);
}

DiffSolver::SolveResult convert_solve_result(SNESConvergedReason r)
{
  switch (r)
    {
    case SNES_CONVERGED_FNORM_ABS:
      return DiffSolver::CONVERGED_ABSOLUTE_RESIDUAL;
    case SNES_CONVERGED_FNORM_RELATIVE:
      return DiffSolver::CONVERGED_RELATIVE_RESIDUAL;
#if PETSC_VERSION_LESS_THAN(3,2,1)
    case SNES_CONVERGED_PNORM_RELATIVE:
#else
    case SNES_CONVERGED_SNORM_RELATIVE:
#endif
      return DiffSolver::CONVERGED_RELATIVE_STEP;
    case SNES_CONVERGED_ITS:
    case SNES_CONVERGED_TR_DELTA:
      return DiffSolver::CONVERGED_NO_REASON;
    case SNES_DIVERGED_FUNCTION_DOMAIN:
    case SNES_DIVERGED_FUNCTION_COUNT:
    case SNES_DIVERGED_FNORM_NAN:
#if !PETSC_VERSION_LESS_THAN(3,3,0)
    case SNES_DIVERGED_INNER:
#endif
    case SNES_DIVERGED_LINEAR_SOLVE:
    case SNES_DIVERGED_LOCAL_MIN:
      return DiffSolver::DIVERGED_NO_REASON;
    case SNES_DIVERGED_MAX_IT:
      return DiffSolver::DIVERGED_MAX_NONLINEAR_ITERATIONS;
#if PETSC_VERSION_LESS_THAN(3,2,0)
    case SNES_DIVERGED_LS_FAILURE:
#else
    case SNES_DIVERGED_LINE_SEARCH:
#endif
      return DiffSolver::DIVERGED_BACKTRACKING_FAILURE;
      // In PETSc, SNES_CONVERGED_ITERATING means
      // the solve is still iterating, but by the
      // time we get here, we must have either
      // converged or diverged, so
      // SNES_CONVERGED_ITERATING is invalid.
    case SNES_CONVERGED_ITERATING:
      return DiffSolver::INVALID_SOLVE_RESULT;
    default:
      break;
    }
  return DiffSolver::INVALID_SOLVE_RESULT;
}

unsigned int PetscDiffSolver::solve()
{
  this->init();

  LOG_SCOPE("solve()", "PetscDiffSolver");

  PetscVector<Number> & x =
    *(cast_ptr<PetscVector<Number> *>(_system.solution.get()));
  PetscMatrix<Number> & jac =
    *(cast_ptr<PetscMatrix<Number> *>(_system.matrix));
  PetscVector<Number> & r =
    *(cast_ptr<PetscVector<Number> *>(_system.rhs));

#ifdef LIBMESH_ENABLE_CONSTRAINTS
  _system.get_dof_map().enforce_constraints_exactly(_system);
#endif

  int ierr = 0;

  ierr = SNESSetFunction (_snes, r.vec(),
                          __libmesh_petsc_diff_solver_residual, this);
  LIBMESH_CHKERR(ierr);

  ierr = SNESSetJacobian (_snes, jac.mat(), jac.mat(),
                          __libmesh_petsc_diff_solver_jacobian, this);
  LIBMESH_CHKERR(ierr);

  ierr = SNESSolve (_snes, PETSC_NULL, x.vec());
  LIBMESH_CHKERR(ierr);

  SNESConvergedReason reason;
  SNESGetConvergedReason(_snes, &reason);

  this->clear();

  return convert_solve_result(reason);
}

} // namespace libMesh

#endif // LIBMESH_HAVE_PETSC
<commit_msg>solve PETSc lock<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2017 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

#include "libmesh/diff_system.h"
#include "libmesh/dof_map.h"
#include "libmesh/libmesh_logging.h"
#include "libmesh/petsc_diff_solver.h"
#include "libmesh/petsc_matrix.h"
#include "libmesh/petsc_vector.h"
#include "libmesh/petsc_auto_fieldsplit.h"

#ifdef LIBMESH_HAVE_PETSC

namespace libMesh
{

//--------------------------------------------------------------------
// Functions with C linkage to pass to PETSc.  PETSc will call these
// methods as needed.
//
// Since they must have C linkage they have no knowledge of a namespace.
// Give them an obscure name to avoid namespace pollution.
extern "C"
{
  // Function to hand to PETSc's SNES,
  // which monitors convergence at X
  PetscErrorCode
  __libmesh_petsc_diff_solver_monitor (SNES snes,
                                       PetscInt its,
                                       PetscReal fnorm,
                                       void * ctx)
  {
    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver *> (ctx));

    if (solver.verbose)
      libMesh::out << "  PetscDiffSolver step " << its
                   << ", |residual|_2 = " << fnorm << std::endl;
    if (solver.linear_solution_monitor.get()) {
      int ierr = 0;

      Vec petsc_delta_u;
      ierr = SNESGetSolutionUpdate(snes, &petsc_delta_u);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> delta_u(petsc_delta_u, solver.comm());
      delta_u.close();

      Vec petsc_u;
      ierr = SNESGetSolution(snes, &petsc_u);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> u(petsc_u, solver.comm());
      u.close();

      Vec petsc_res;
      ierr = SNESGetFunction(snes, &petsc_res, libmesh_nullptr, libmesh_nullptr);
      CHKERRABORT(solver.comm().get(), ierr);
      PetscVector<Number> res(petsc_res, solver.comm());
      res.close();

      (*solver.linear_solution_monitor)(
                                        delta_u, delta_u.l2_norm(),
                                        u, u.l2_norm(),
                                        res, res.l2_norm(), its);
    }
    return 0;
  }

  // Functions to hand to PETSc's SNES,
  // which compute the residual or jacobian at X
  PetscErrorCode
  __libmesh_petsc_diff_solver_residual (SNES, Vec x, Vec r, void * ctx)
  {
    libmesh_assert(x);
    libmesh_assert(r);
    libmesh_assert(ctx);

    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver*> (ctx));
    ImplicitSystem & sys = solver.system();

    if (solver.verbose)
      libMesh::out << "Assembling the residual" << std::endl;

    PetscVector<Number> & X_system =
      *cast_ptr<PetscVector<Number> *>(sys.solution.get());
    PetscVector<Number> & R_system =
      *cast_ptr<PetscVector<Number> *>(sys.rhs);
    PetscVector<Number> X_input(x, sys.comm()), R_input(r, sys.comm());

    // DiffSystem assembles from the solution and into the rhs, so swap
    // those with our input vectors before assembling.  They'll probably
    // already be references to the same vectors, but PETSc might do
    // something tricky.
    X_input.swap(X_system);
    R_input.swap(R_system);

    // We may need to correct a non-conforming solution
    sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());

    // We may need to localize a parallel solution
    sys.update();

    // Do DiffSystem assembly
    sys.assembly(true, false);
    R_system.close();

    // Swap back
    X_input.swap(X_system);
    R_input.swap(R_system);

    // No errors, we hope
    return 0;
  }

#if PETSC_RELEASE_LESS_THAN(3,5,0)
  PetscErrorCode
  __libmesh_petsc_diff_solver_jacobian (SNES,
                                        Vec x,
                                        Mat * libmesh_dbg_var(j),
                                        Mat * pc,
                                        MatStructure * msflag,
                                        void * ctx)
#else
    PetscErrorCode
    __libmesh_petsc_diff_solver_jacobian (SNES,
                                          Vec x,
                                          Mat libmesh_dbg_var(j),
                                          Mat pc,
                                          void * ctx)
#endif
  {
    libmesh_assert(x);
    libmesh_assert(j);
    //  libmesh_assert_equal_to (pc, j);  // We don't use separate preconditioners yet
    libmesh_assert(ctx);

    PetscDiffSolver & solver =
      *(static_cast<PetscDiffSolver*> (ctx));
    ImplicitSystem & sys = solver.system();

    if (solver.verbose)
      libMesh::out << "Assembling the Jacobian" << std::endl;

    PetscVector<Number> & X_system =
      *cast_ptr<PetscVector<Number> *>(sys.solution.get());
    PetscVector<Number> X_input(x, sys.comm());

#if PETSC_RELEASE_LESS_THAN(3,5,0)
    PetscMatrix<Number> J_input(*pc, sys.comm());
#else
    PetscMatrix<Number> J_input(pc, sys.comm());
#endif
    PetscMatrix<Number> & J_system =
      *cast_ptr<PetscMatrix<Number> *>(sys.matrix);

    // DiffSystem assembles from the solution and into the jacobian, so
    // swap those with our input vectors before assembling.  They'll
    // probably already be references to the same vectors, but PETSc
    // might do something tricky.
    X_input.swap(X_system);
    J_input.swap(J_system);

    // We may need to correct a non-conforming solution
    sys.get_dof_map().enforce_constraints_exactly(sys, sys.current_local_solution.get());

    // We may need to localize a parallel solution
    sys.update();

    // Do DiffSystem assembly
    sys.assembly(false, true);
    J_system.close();

    // Swap back
    X_input.swap(X_system);
    J_input.swap(J_system);

#if PETSC_RELEASE_LESS_THAN(3,5,0)
    *msflag = SAME_NONZERO_PATTERN;
#endif
    // No errors, we hope
    return 0;
  }

} // extern "C"

PetscDiffSolver::PetscDiffSolver (sys_type & s)
  : Parent(s)
{
}

void PetscDiffSolver::init ()
{
  LOG_SCOPE("init()", "PetscDiffSolver");

  Parent::init();

  int ierr=0;

  ierr = SNESCreate(this->comm().get(),&_snes);
  LIBMESH_CHKERR(ierr);

  ierr = SNESMonitorSet (_snes, __libmesh_petsc_diff_solver_monitor,
                         this, PETSC_NULL);
  LIBMESH_CHKERR(ierr);

  if (libMesh::on_command_line("--solver_system_names"))
    {
      ierr = SNESSetOptionsPrefix(_snes, (_system.name()+"_").c_str());
      LIBMESH_CHKERR(ierr);
    }

  ierr = SNESSetFromOptions(_snes);
  LIBMESH_CHKERR(ierr);

  KSP my_ksp;
  ierr = SNESGetKSP(_snes, &my_ksp);
  LIBMESH_CHKERR(ierr);

  PC my_pc;
  ierr = KSPGetPC(my_ksp, &my_pc);
  LIBMESH_CHKERR(ierr);

  petsc_auto_fieldsplit(my_pc, _system);
}

PetscDiffSolver::~PetscDiffSolver ()
{
}

void PetscDiffSolver::clear()
{
  LOG_SCOPE("clear()", "PetscDiffSolver");

  int ierr = LibMeshSNESDestroy(&_snes);
  LIBMESH_CHKERR(ierr);
}

void PetscDiffSolver::reinit()
{
  Parent::reinit();

  KSP my_ksp;
  int ierr = SNESGetKSP(_snes, &my_ksp);
  LIBMESH_CHKERR(ierr);

  PC my_pc;
  ierr = KSPGetPC(my_ksp, &my_pc);
  LIBMESH_CHKERR(ierr);

  petsc_auto_fieldsplit(my_pc, _system);
}

DiffSolver::SolveResult convert_solve_result(SNESConvergedReason r)
{
  switch (r)
    {
    case SNES_CONVERGED_FNORM_ABS:
      return DiffSolver::CONVERGED_ABSOLUTE_RESIDUAL;
    case SNES_CONVERGED_FNORM_RELATIVE:
      return DiffSolver::CONVERGED_RELATIVE_RESIDUAL;
#if PETSC_VERSION_LESS_THAN(3,2,1)
    case SNES_CONVERGED_PNORM_RELATIVE:
#else
    case SNES_CONVERGED_SNORM_RELATIVE:
#endif
      return DiffSolver::CONVERGED_RELATIVE_STEP;
    case SNES_CONVERGED_ITS:
    case SNES_CONVERGED_TR_DELTA:
      return DiffSolver::CONVERGED_NO_REASON;
    case SNES_DIVERGED_FUNCTION_DOMAIN:
    case SNES_DIVERGED_FUNCTION_COUNT:
    case SNES_DIVERGED_FNORM_NAN:
#if !PETSC_VERSION_LESS_THAN(3,3,0)
    case SNES_DIVERGED_INNER:
#endif
    case SNES_DIVERGED_LINEAR_SOLVE:
    case SNES_DIVERGED_LOCAL_MIN:
      return DiffSolver::DIVERGED_NO_REASON;
    case SNES_DIVERGED_MAX_IT:
      return DiffSolver::DIVERGED_MAX_NONLINEAR_ITERATIONS;
#if PETSC_VERSION_LESS_THAN(3,2,0)
    case SNES_DIVERGED_LS_FAILURE:
#else
    case SNES_DIVERGED_LINE_SEARCH:
#endif
      return DiffSolver::DIVERGED_BACKTRACKING_FAILURE;
      // In PETSc, SNES_CONVERGED_ITERATING means
      // the solve is still iterating, but by the
      // time we get here, we must have either
      // converged or diverged, so
      // SNES_CONVERGED_ITERATING is invalid.
    case SNES_CONVERGED_ITERATING:
      return DiffSolver::INVALID_SOLVE_RESULT;
    default:
      break;
    }
  return DiffSolver::INVALID_SOLVE_RESULT;
}

unsigned int PetscDiffSolver::solve()
{
  this->init();

  LOG_SCOPE("solve()", "PetscDiffSolver");

  PetscVector<Number> & x =
    *(cast_ptr<PetscVector<Number> *>(_system.solution.get()));
  PetscMatrix<Number> & jac =
    *(cast_ptr<PetscMatrix<Number> *>(_system.matrix));
  PetscVector<Number> & r =
    *(cast_ptr<PetscVector<Number> *>(_system.rhs));

#ifdef LIBMESH_ENABLE_CONSTRAINTS
  _system.get_dof_map().enforce_constraints_exactly(_system);
#endif

  int ierr = 0;

  ierr = SNESSetFunction (_snes, r.vec(),
                          __libmesh_petsc_diff_solver_residual, this);
  LIBMESH_CHKERR(ierr);

  ierr = SNESSetJacobian (_snes, jac.mat(), jac.mat(),
                          __libmesh_petsc_diff_solver_jacobian, this);
  LIBMESH_CHKERR(ierr);

  ierr = SNESSolve (_snes, PETSC_NULL, x.vec());
  LIBMESH_CHKERR(ierr);

  SNESConvergedReason reason;
  SNESGetConvergedReason(_snes, &reason);

  this->clear();

  return convert_solve_result(reason);
}

} // namespace libMesh

#endif // LIBMESH_HAVE_PETSC
<|endoftext|>


<commit_before>#include "storage/write/node_batch.h"

#include "storage/id/edge_id.h"
#include "storage/id/node_property_id.h"

namespace GraphDB {

NodeBatch::NodeBatch(BasicIterator* const iter, bool events):
	iterator_(iter),
	events_(events) { }

uint32_t NodeBatch::getCurrent() {
	return this->iterator_->getCurrent();
}

void NodeBatch::create() {
	EdgeId edge(0, 1, EdgeDirection::Outbound, this->getCurrent());

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::discard() {
	EdgeId edge(0, 1, EdgeDirection::Outbound, this->getCurrent());

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);

	edge.typeId = 2;

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::connectTo(uint32_t toId, uint16_t typeId) {
	EdgeId edge(this->getCurrent(), typeId, EdgeDirection::Outbound, toId);

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);

	edge.fromId    = toId;
	edge.direction = EdgeDirection::Inbound;
	edge.nodeId    = this->getCurrent();

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::disconnectFrom(uint32_t toId, uint16_t typeId) {
	EdgeId edge(this->getCurrent(), typeId, EdgeDirection::Outbound, toId);

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);

	edge.fromId    = toId;
	edge.direction = EdgeDirection::Inbound;
	edge.nodeId    = this->getCurrent();

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);
}

void NodeBatch::setProperty(uint16_t propertyId, const PropertyValue& value) {
	this->set<NodePropertyId>(
		NodePropertyId(this->getCurrent(), propertyId),
		value
	);
}

void NodeBatch::removeProperty(uint16_t propertyId) {
	this->remove<NodePropertyId>(
		NodePropertyId(this->getCurrent(), propertyId)
	);
}

void NodeBatch::addEvent(EdgeStreamEventType type, EdgeId& edge) {
	if ( this->events_ ) {
		this->event_queue_.push_back(EdgeStreamEvent(type, edge));
	}
}

}
<commit_msg>Fixed parameter sequence in NodeBatch * setProperty and removeProperty were using nodeId and propertyId in the wrong order<commit_after>#include "storage/write/node_batch.h"

#include "storage/id/edge_id.h"
#include "storage/id/node_property_id.h"

namespace GraphDB {

NodeBatch::NodeBatch(BasicIterator* const iter, bool events):
	iterator_(iter),
	events_(events) { }

uint32_t NodeBatch::getCurrent() {
	return this->iterator_->getCurrent();
}

void NodeBatch::create() {
	EdgeId edge(0, 1, EdgeDirection::Outbound, this->getCurrent());

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::discard() {
	EdgeId edge(0, 1, EdgeDirection::Outbound, this->getCurrent());

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);

	edge.typeId = 2;

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::connectTo(uint32_t toId, uint16_t typeId) {
	EdgeId edge(this->getCurrent(), typeId, EdgeDirection::Outbound, toId);

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);

	edge.fromId    = toId;
	edge.direction = EdgeDirection::Inbound;
	edge.nodeId    = this->getCurrent();

	this->set<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::INSERT, edge);
}

void NodeBatch::disconnectFrom(uint32_t toId, uint16_t typeId) {
	EdgeId edge(this->getCurrent(), typeId, EdgeDirection::Outbound, toId);

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);

	edge.fromId    = toId;
	edge.direction = EdgeDirection::Inbound;
	edge.nodeId    = this->getCurrent();

	this->remove<EdgeId>(edge);
	this->addEvent(EdgeStreamEventType::REMOVE, edge);
}

void NodeBatch::setProperty(uint16_t propertyId, const PropertyValue& value) {
	this->set<NodePropertyId>(
		NodePropertyId(propertyId, this->getCurrent()),
		value
	);
}

void NodeBatch::removeProperty(uint16_t propertyId) {
	this->remove<NodePropertyId>(
		NodePropertyId(propertyId, this->getCurrent())
	);
}

void NodeBatch::addEvent(EdgeStreamEventType type, EdgeId& edge) {
	if ( this->events_ ) {
		this->event_queue_.push_back(EdgeStreamEvent(type, edge));
	}
}

}
<|endoftext|>


<commit_before>/**
 * This file is part of the "libstx" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * libstx is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "stx/exception.h"
#include "stx/inspect.h"
#include "stx/stringutil.h"
#include "stx/json/jsoninputstream.h"

namespace stx {
namespace json {

JSONInputStream::JSONInputStream(
    std::unique_ptr<InputStream> input) :
    input_(std::move(input)),
    cur_(' ') {}

JSONInputStream::JSONInputStream(
    JSONInputStream&& other) :
    input_(std::move(other.input_)),
    cur_(other.cur_) {
  other.cur_ = 0;
}

bool JSONInputStream::readNextToken(
    kTokenType* token_type,
    std::string* token_data) {
  token_data->clear();

  for (;;) {
    switch (cur_) {
      case '{':
        *token_type = JSON_OBJECT_BEGIN;
        advanceCursor();
        return true;

      case '}':
        *token_type = JSON_OBJECT_END;
        advanceCursor();
        return true;

      case '[':
        *token_type = JSON_ARRAY_BEGIN;
        advanceCursor();
        return true;

      case ']':
        *token_type = JSON_ARRAY_END;
        advanceCursor();
        return true;

      case ':':
      case ',':
      case ' ':
      case '\n':
      case '\r':
        advanceCursor();
        break;

      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        *token_type = JSON_NUMBER;
        readNumber(token_data);
        return true;

      case '"':
        *token_type = JSON_STRING;
        readString(token_data);
        return true;

      case 't':
        *token_type = JSON_TRUE;
        expectString("true");
        return true;

      case 'f':
        *token_type = JSON_FALSE;
        expectString("false");
        return true;

      case 'n':
        *token_type = JSON_NULL;
        expectString("null");
        return true;

      case 0:
        return false;

      default:
        RAISE(kRuntimeError, "invalid json, unexpected char: %i", cur_);
        return false;

    }
  }
}

void JSONInputStream::readNumber(std::string* dst) {
  for (;;) {
    switch (cur_) {
      case '-':
      case '+':
      case '.':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'e':
      case 'E':
        *dst += cur_;
        /* fallthrough */

      case ' ':
        advanceCursor();
        break;

      default:
        return;
    }
  }
}

// FIXPAUL profiling says this is slow slow slow (90% of rpc server time spent
// in this method)
void JSONInputStream::readString(std::string* dst) {
  bool escaped = false;
  for (;;) {
    advanceCursor();

    switch (cur_) {

      case '\\':
        advanceCursor();

        switch (cur_) {

          case '"':
            *dst += "\"";
            break;

          case '\\':
            *dst += "\\";
            break;

          case '/':
            *dst += "/";
            break;

          case 'b':
            *dst += "\b";
            break;

          case 'f':
            *dst += "\f";
            break;

          case 'n':
            *dst += "\n";
            break;

          case 'r':
            *dst += "\r";
            break;

          case 't':
            *dst += "\t";
            break;

          case 'u':
            // FIXME
            advanceCursor();
            advanceCursor();
            advanceCursor();
            advanceCursor();
            break;

          default:
            RAISE(kRuntimeError, "invalid escape sequence");

        }
        break;

      default:
        *dst += cur_;
        break;

      case '"':
        advanceCursor();
        return;

      case 0:
        RAISE(kRuntimeError, "invalid json. unterminated string");
        return;

    }
  }
}

void JSONInputStream::expectString(const std::string& expect) {
  std::string str;
  str += cur_;
  input_->readNextBytes(&str, expect.length() - 1);

  if (str != expect) {
    RAISEF(kRuntimeError, "invalid json. expected '$0', got '$1'", expect, str);
  }

  advanceCursor();
}

void JSONInputStream::advanceCursor() {
  if (!input_->readNextByte(&cur_)) {
    cur_ = 0;
  }
}

} // namespace json

template <>
std::string inspect(const json::kTokenType& token) {
  switch (token) {
    case json::JSON_OBJECT_BEGIN: return "JSON_OBJECT_BEGIN";
    case json::JSON_OBJECT_END: return "JSON_OBJECT_END";
    case json::JSON_ARRAY_BEGIN: return "JSON_ARRAY_BEGIN";
    case json::JSON_ARRAY_END: return "JSON_ARRAY_END";
    case json::JSON_STRING: return "JSON_STRING";
    case json::JSON_NUMBER: return "JSON_NUMBER";
    case json::JSON_TRUE: return "JSON_TRUE";
    case json::JSON_FALSE: return "JSON_FALSE";
    case json::JSON_NULL: return "JSON_NULL";
  }
}

} // namespace stx

<commit_msg>cleaning up<commit_after>/**
 * This file is part of the "libstx" project
 *   Copyright (c) 2011-2014 Paul Asmuth, Google Inc.
 *
 * libstx is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "stx/exception.h"
#include "stx/inspect.h"
#include "stx/stringutil.h"
#include "stx/json/jsoninputstream.h"

namespace stx {
namespace json {

JSONInputStream::JSONInputStream(
    std::unique_ptr<InputStream> input) :
    input_(std::move(input)),
    cur_(' ') {}

JSONInputStream::JSONInputStream(
    JSONInputStream&& other) :
    input_(std::move(other.input_)),
    cur_(other.cur_) {
  other.cur_ = 0;
}

bool JSONInputStream::readNextToken(
    kTokenType* token_type,
    std::string* token_data) {
  token_data->clear();

  for (;;) {
    switch (cur_) {
      case '{':
        *token_type = JSON_OBJECT_BEGIN;
        advanceCursor();
        return true;

      case '}':
        *token_type = JSON_OBJECT_END;
        advanceCursor();
        return true;

      case '[':
        *token_type = JSON_ARRAY_BEGIN;
        advanceCursor();
        return true;

      case ']':
        *token_type = JSON_ARRAY_END;
        advanceCursor();
        return true;

      case ':':
      case ',':
      case ' ':
      case '\n':
      case '\r':
        advanceCursor();
        break;

      case '-':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        *token_type = JSON_NUMBER;
        readNumber(token_data);
        return true;

      case '"':
        *token_type = JSON_STRING;
        readString(token_data);
        return true;

      case 't':
        *token_type = JSON_TRUE;
        expectString("true");
        return true;

      case 'f':
        *token_type = JSON_FALSE;
        expectString("false");
        return true;

      case 'n':
        *token_type = JSON_NULL;
        expectString("null");
        return true;

      case 0:
        return false;

      default:
        RAISE(kRuntimeError, "invalid json, unexpected char: %i", cur_);
        return false;

    }
  }
}

void JSONInputStream::readNumber(std::string* dst) {
  for (;;) {
    switch (cur_) {
      case '-':
      case '+':
      case '.':
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case 'e':
      case 'E':
        *dst += cur_;
        /* fallthrough */

      case ' ':
        advanceCursor();
        break;

      default:
        return;
    }
  }
}

void JSONInputStream::readString(std::string* dst) {
  bool escaped = false;
  for (;;) {
    advanceCursor();

    switch (cur_) {

      case '\\':
        advanceCursor();

        switch (cur_) {

          case '"':
            *dst += "\"";
            break;

          case '\\':
            *dst += "\\";
            break;

          case '/':
            *dst += "/";
            break;

          case 'b':
            *dst += "\b";
            break;

          case 'f':
            *dst += "\f";
            break;

          case 'n':
            *dst += "\n";
            break;

          case 'r':
            *dst += "\r";
            break;

          case 't':
            *dst += "\t";
            break;

          case 'u':
            // FIXME
            advanceCursor();
            advanceCursor();
            advanceCursor();
            advanceCursor();
            break;

          default:
            RAISE(kRuntimeError, "invalid escape sequence");

        }
        break;

      default:
        *dst += cur_;
        break;

      case '"':
        advanceCursor();
        return;

      case 0:
        RAISE(kRuntimeError, "invalid json. unterminated string");
        return;

    }
  }
}

void JSONInputStream::expectString(const std::string& expect) {
  std::string str;
  str += cur_;
  input_->readNextBytes(&str, expect.length() - 1);

  if (str != expect) {
    RAISEF(kRuntimeError, "invalid json. expected '$0', got '$1'", expect, str);
  }

  advanceCursor();
}

void JSONInputStream::advanceCursor() {
  if (!input_->readNextByte(&cur_)) {
    cur_ = 0;
  }
}

} // namespace json

template <>
std::string inspect(const json::kTokenType& token) {
  switch (token) {
    case json::JSON_OBJECT_BEGIN: return "JSON_OBJECT_BEGIN";
    case json::JSON_OBJECT_END: return "JSON_OBJECT_END";
    case json::JSON_ARRAY_BEGIN: return "JSON_ARRAY_BEGIN";
    case json::JSON_ARRAY_END: return "JSON_ARRAY_END";
    case json::JSON_STRING: return "JSON_STRING";
    case json::JSON_NUMBER: return "JSON_NUMBER";
    case json::JSON_TRUE: return "JSON_TRUE";
    case json::JSON_FALSE: return "JSON_FALSE";
    case json::JSON_NULL: return "JSON_NULL";
  }
}

} // namespace stx

<|endoftext|>