<commit_before>#include "qtestlogger_p.h"
#include "qtestelement.h"
#include "qtestxunitstreamer.h"
#include "qtestxmlstreamer.h"
#include "qtestlightxmlstreamer.h"
#include "qtestfilelogger.h"

#include "QtTest/qtestcase.h"
#include "QtTest/private/qtestresult_p.h"
#include "QtTest/private/qbenchmark_p.h"

#include <string.h>

QT_BEGIN_NAMESPACE

QTestLogger::QTestLogger(int fm)
    :listOfTestcases(0), currentLogElement(0),
    logFormatter(0), format( (TestLoggerFormat)fm ), filelogger(new QTestFileLogger),
    testCounter(0), passCounter(0),
    failureCounter(0), errorCounter(0),
    warningCounter(0), skipCounter(0),
    systemCounter(0), qdebugCounter(0),
    qwarnCounter(0), qfatalCounter(0),
    infoCounter(0)
{
}

QTestLogger::~QTestLogger()
{
    if(format == TLF_XunitXml)
        delete currentLogElement;
    else
        delete listOfTestcases;

    delete logFormatter;
    delete filelogger;
}

void QTestLogger::startLogging()
{
    switch(format){
    case TLF_LightXml:{
        logFormatter = new QTestLightXmlStreamer;
        filelogger->init();
        break;
    }case TLF_XML:{
        logFormatter = new QTestXmlStreamer;
        filelogger->init();
        break;
    }case TLF_XunitXml:{
        logFormatter = new QTestXunitStreamer;
        filelogger->init();
        break;
    }
    }

    logFormatter->setLogger(this);
    logFormatter->startStreaming();
}

void QTestLogger::stopLogging()
{
    QTestElement *iterator = listOfTestcases;

    if(format == TLF_XunitXml ){
        char buf[10];

        currentLogElement = new QTestElement(QTest::LET_TestSuite);
        currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName());

        QTest::qt_snprintf(buf, sizeof(buf), "%i", testCounter);
        currentLogElement->addAttribute(QTest::AI_Tests, buf);

        QTest::qt_snprintf(buf, sizeof(buf), "%i", failureCounter);
        currentLogElement->addAttribute(QTest::AI_Failures, buf);

        QTest::qt_snprintf(buf, sizeof(buf), "%i", errorCounter);
        currentLogElement->addAttribute(QTest::AI_Errors, buf);

        QTestElement *property;
        QTestElement *properties = new QTestElement(QTest::LET_Properties);

        property = new QTestElement(QTest::LET_Property);
        property->addAttribute(QTest::AI_Name, "QTestVersion");
        property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
        properties->addLogElement(property);

        property = new QTestElement(QTest::LET_Property);
        property->addAttribute(QTest::AI_Name, "QtVersion");
        property->addAttribute(QTest::AI_PropertyValue, qVersion());
        properties->addLogElement(property);

        currentLogElement->addLogElement(properties);

        currentLogElement->addLogElement(iterator);

        /* For correct indenting, make sure every testcase knows its parent */
        QTestElement* testcase = iterator;
        while (testcase) {
            testcase->setParent(currentLogElement);
            testcase = testcase->nextElement();
        }

        QTestElement *it = currentLogElement;
        logFormatter->output(it);
    }else{
        logFormatter->output(iterator);
    }

    logFormatter->stopStreaming();
}

void QTestLogger::enterTestFunction(const char *function)
{
    char buf[1024];
    QTest::qt_snprintf(buf, sizeof(buf), "Entered test-function: %s\n", function);
    filelogger->flush(buf);

    currentLogElement = new QTestElement(QTest::LET_TestCase);
    currentLogElement->addAttribute(QTest::AI_Name, function);
    currentLogElement->addToList(&listOfTestcases);

    ++testCounter;
}

void QTestLogger::leaveTestFunction()
{
}

void QTestLogger::addIncident(IncidentTypes type, const char *description,
                     const char *file, int line)
{
    const char *typeBuf = 0;
    char buf[100];

    switch (type) {
    case QAbstractTestLogger::XPass:
        ++passCounter;
        typeBuf = "xpass";
        break;
    case QAbstractTestLogger::Pass:
        ++passCounter;
        typeBuf = "pass";
        break;
    case QAbstractTestLogger::XFail:
        ++failureCounter;
        typeBuf = "xfail";
        break;
    case QAbstractTestLogger::Fail:
        ++failureCounter;
        typeBuf = "fail";
        break;
    default:
        typeBuf = "??????";
        break;
    }

    if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XFail) {
        QTestElement *failureElement = new QTestElement(QTest::LET_Failure);
        failureElement->addAttribute(QTest::AI_Result, typeBuf);
        failureElement->addAttribute(QTest::AI_File, file);
        QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
        failureElement->addAttribute(QTest::AI_Line, buf);
        failureElement->addAttribute(QTest::AI_Description, description);
        const char* tag = QTestResult::currentDataTag();
        if (tag) {
            failureElement->addAttribute(QTest::AI_Tag, tag);
        }
        currentLogElement->addLogElement(failureElement);
    }

    /*
        Only one result can be shown for the whole testfunction.
        Check if we currently have a result, and if so, overwrite it
        iff the new result is worse.
    */
    QTestElementAttribute* resultAttr =
        const_cast<QTestElementAttribute*>(currentLogElement->attribute(QTest::AI_Result));
    if (resultAttr) {
        const char* oldResult = resultAttr->value();
        bool overwrite = false;
        if (!strcmp(oldResult, "pass")) {
            overwrite = true;
        }
        else if (!strcmp(oldResult, "xpass")) {
            overwrite = (type == QAbstractTestLogger::XFail || type == QAbstractTestLogger::Fail);
        }
        else if (!strcmp(oldResult, "xfail")) {
            overwrite = (type == QAbstractTestLogger::Fail);
        }
        if (overwrite) {
            resultAttr->setPair(QTest::AI_Result, typeBuf);
        }
    }
    else {
        currentLogElement->addAttribute(QTest::AI_Result, typeBuf);
    }

    if(file)
        currentLogElement->addAttribute(QTest::AI_File, file);
    else
        currentLogElement->addAttribute(QTest::AI_File, "");

    QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
    currentLogElement->addAttribute(QTest::AI_Line, buf);
}

void QTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
{
    QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark);
//    printf("element %i", benchmarkElement->elementType());

    benchmarkElement->addAttribute(QTest::AI_Metric, QBenchmarkGlobalData::current->measurer->metricText().toAscii().data());
    benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toAscii().data());
    benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(result.value).constData());

    char buf[100];
    QTest::qt_snprintf(buf, sizeof(buf), "%i", result.iterations);
    benchmarkElement->addAttribute(QTest::AI_Iterations, buf);
    currentLogElement->addLogElement(benchmarkElement);
}

void QTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line)
{
    QTestElement *errorElement = new QTestElement(QTest::LET_Error);
    const char *typeBuf = 0;

    switch (type) {
    case QAbstractTestLogger::Warn:
        ++warningCounter;
        typeBuf = "warn";
        break;
    case QAbstractTestLogger::QSystem:
        ++systemCounter;
        typeBuf = "system";
        break;
    case QAbstractTestLogger::QDebug:
        ++qdebugCounter;
        typeBuf = "qdebug";
        break;
    case QAbstractTestLogger::QWarning:
        ++qwarnCounter;
        typeBuf = "qwarning";
        break;
    case QAbstractTestLogger::QFatal:
        ++qfatalCounter;
        typeBuf = "qfatal";
        break;
    case QAbstractTestLogger::Skip:
        ++skipCounter;
        typeBuf = "skip";
        break;
    case QAbstractTestLogger::Info:
        ++infoCounter;
        typeBuf = "info";
        break;
    default:
        typeBuf = "??????";
        break;
    }

    errorElement->addAttribute(QTest::AI_Type, typeBuf);
    errorElement->addAttribute(QTest::AI_Description, message);

    if(file)
        errorElement->addAttribute(QTest::AI_File, file);
    else
        errorElement->addAttribute(QTest::AI_File, "");

    char buf[100];
    QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
    errorElement->addAttribute(QTest::AI_Line, buf);

    currentLogElement->addLogElement(errorElement);
    ++errorCounter;
}

void QTestLogger::setLogFormat(TestLoggerFormat fm)
{
    format = fm;
}

QTestLogger::TestLoggerFormat QTestLogger::logFormat()
{
    return format;
}

int QTestLogger::passCount() const
{
    return passCounter;
}

int QTestLogger::failureCount() const
{
    return failureCounter;
}

int QTestLogger::errorCount() const
{
    return errorCounter;
}

int QTestLogger::warningCount() const
{
    return warningCounter;
}

int QTestLogger::skipCount() const
{
    return skipCounter;
}

int QTestLogger::systemCount() const
{
    return systemCounter;
}

int QTestLogger::qdebugCount() const
{
    return qdebugCounter;
}

int QTestLogger::qwarnCount() const
{
    return qwarnCounter;
}

int QTestLogger::qfatalCount() const
{
    return qfatalCounter;
}

int QTestLogger::infoCount() const
{
    return infoCounter;
}

QT_END_NAMESPACE

<commit_msg>Fixed invalid XML from testlib: `<Incident type="fail" (null)="" ...'<commit_after>#include "qtestlogger_p.h"
#include "qtestelement.h"
#include "qtestxunitstreamer.h"
#include "qtestxmlstreamer.h"
#include "qtestlightxmlstreamer.h"
#include "qtestfilelogger.h"

#include "QtTest/qtestcase.h"
#include "QtTest/private/qtestresult_p.h"
#include "QtTest/private/qbenchmark_p.h"

#include <string.h>

QT_BEGIN_NAMESPACE

QTestLogger::QTestLogger(int fm)
    :listOfTestcases(0), currentLogElement(0),
    logFormatter(0), format( (TestLoggerFormat)fm ), filelogger(new QTestFileLogger),
    testCounter(0), passCounter(0),
    failureCounter(0), errorCounter(0),
    warningCounter(0), skipCounter(0),
    systemCounter(0), qdebugCounter(0),
    qwarnCounter(0), qfatalCounter(0),
    infoCounter(0)
{
}

QTestLogger::~QTestLogger()
{
    if(format == TLF_XunitXml)
        delete currentLogElement;
    else
        delete listOfTestcases;

    delete logFormatter;
    delete filelogger;
}

void QTestLogger::startLogging()
{
    switch(format){
    case TLF_LightXml:{
        logFormatter = new QTestLightXmlStreamer;
        filelogger->init();
        break;
    }case TLF_XML:{
        logFormatter = new QTestXmlStreamer;
        filelogger->init();
        break;
    }case TLF_XunitXml:{
        logFormatter = new QTestXunitStreamer;
        filelogger->init();
        break;
    }
    }

    logFormatter->setLogger(this);
    logFormatter->startStreaming();
}

void QTestLogger::stopLogging()
{
    QTestElement *iterator = listOfTestcases;

    if(format == TLF_XunitXml ){
        char buf[10];

        currentLogElement = new QTestElement(QTest::LET_TestSuite);
        currentLogElement->addAttribute(QTest::AI_Name, QTestResult::currentTestObjectName());

        QTest::qt_snprintf(buf, sizeof(buf), "%i", testCounter);
        currentLogElement->addAttribute(QTest::AI_Tests, buf);

        QTest::qt_snprintf(buf, sizeof(buf), "%i", failureCounter);
        currentLogElement->addAttribute(QTest::AI_Failures, buf);

        QTest::qt_snprintf(buf, sizeof(buf), "%i", errorCounter);
        currentLogElement->addAttribute(QTest::AI_Errors, buf);

        QTestElement *property;
        QTestElement *properties = new QTestElement(QTest::LET_Properties);

        property = new QTestElement(QTest::LET_Property);
        property->addAttribute(QTest::AI_Name, "QTestVersion");
        property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
        properties->addLogElement(property);

        property = new QTestElement(QTest::LET_Property);
        property->addAttribute(QTest::AI_Name, "QtVersion");
        property->addAttribute(QTest::AI_PropertyValue, qVersion());
        properties->addLogElement(property);

        currentLogElement->addLogElement(properties);

        currentLogElement->addLogElement(iterator);

        /* For correct indenting, make sure every testcase knows its parent */
        QTestElement* testcase = iterator;
        while (testcase) {
            testcase->setParent(currentLogElement);
            testcase = testcase->nextElement();
        }

        QTestElement *it = currentLogElement;
        logFormatter->output(it);
    }else{
        logFormatter->output(iterator);
    }

    logFormatter->stopStreaming();
}

void QTestLogger::enterTestFunction(const char *function)
{
    char buf[1024];
    QTest::qt_snprintf(buf, sizeof(buf), "Entered test-function: %s\n", function);
    filelogger->flush(buf);

    currentLogElement = new QTestElement(QTest::LET_TestCase);
    currentLogElement->addAttribute(QTest::AI_Name, function);
    currentLogElement->addToList(&listOfTestcases);

    ++testCounter;
}

void QTestLogger::leaveTestFunction()
{
}

void QTestLogger::addIncident(IncidentTypes type, const char *description,
                     const char *file, int line)
{
    const char *typeBuf = 0;
    char buf[100];

    switch (type) {
    case QAbstractTestLogger::XPass:
        ++passCounter;
        typeBuf = "xpass";
        break;
    case QAbstractTestLogger::Pass:
        ++passCounter;
        typeBuf = "pass";
        break;
    case QAbstractTestLogger::XFail:
        ++failureCounter;
        typeBuf = "xfail";
        break;
    case QAbstractTestLogger::Fail:
        ++failureCounter;
        typeBuf = "fail";
        break;
    default:
        typeBuf = "??????";
        break;
    }

    if (type == QAbstractTestLogger::Fail || type == QAbstractTestLogger::XFail) {
        QTestElement *failureElement = new QTestElement(QTest::LET_Failure);
        failureElement->addAttribute(QTest::AI_Result, typeBuf);
        if(file)
            failureElement->addAttribute(QTest::AI_File, file);
        else
            failureElement->addAttribute(QTest::AI_File, "");
        QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
        failureElement->addAttribute(QTest::AI_Line, buf);
        failureElement->addAttribute(QTest::AI_Description, description);
        const char* tag = QTestResult::currentDataTag();
        if (tag) {
            failureElement->addAttribute(QTest::AI_Tag, tag);
        }
        currentLogElement->addLogElement(failureElement);
    }

    /*
        Only one result can be shown for the whole testfunction.
        Check if we currently have a result, and if so, overwrite it
        iff the new result is worse.
    */
    QTestElementAttribute* resultAttr =
        const_cast<QTestElementAttribute*>(currentLogElement->attribute(QTest::AI_Result));
    if (resultAttr) {
        const char* oldResult = resultAttr->value();
        bool overwrite = false;
        if (!strcmp(oldResult, "pass")) {
            overwrite = true;
        }
        else if (!strcmp(oldResult, "xpass")) {
            overwrite = (type == QAbstractTestLogger::XFail || type == QAbstractTestLogger::Fail);
        }
        else if (!strcmp(oldResult, "xfail")) {
            overwrite = (type == QAbstractTestLogger::Fail);
        }
        if (overwrite) {
            resultAttr->setPair(QTest::AI_Result, typeBuf);
        }
    }
    else {
        currentLogElement->addAttribute(QTest::AI_Result, typeBuf);
    }

    if(file)
        currentLogElement->addAttribute(QTest::AI_File, file);
    else
        currentLogElement->addAttribute(QTest::AI_File, "");

    QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
    currentLogElement->addAttribute(QTest::AI_Line, buf);
}

void QTestLogger::addBenchmarkResult(const QBenchmarkResult &result)
{
    QTestElement *benchmarkElement = new QTestElement(QTest::LET_Benchmark);
//    printf("element %i", benchmarkElement->elementType());

    benchmarkElement->addAttribute(QTest::AI_Metric, QBenchmarkGlobalData::current->measurer->metricText().toAscii().data());
    benchmarkElement->addAttribute(QTest::AI_Tag, result.context.tag.toAscii().data());
    benchmarkElement->addAttribute(QTest::AI_Value, QByteArray::number(result.value).constData());

    char buf[100];
    QTest::qt_snprintf(buf, sizeof(buf), "%i", result.iterations);
    benchmarkElement->addAttribute(QTest::AI_Iterations, buf);
    currentLogElement->addLogElement(benchmarkElement);
}

void QTestLogger::addMessage(MessageTypes type, const char *message, const char *file, int line)
{
    QTestElement *errorElement = new QTestElement(QTest::LET_Error);
    const char *typeBuf = 0;

    switch (type) {
    case QAbstractTestLogger::Warn:
        ++warningCounter;
        typeBuf = "warn";
        break;
    case QAbstractTestLogger::QSystem:
        ++systemCounter;
        typeBuf = "system";
        break;
    case QAbstractTestLogger::QDebug:
        ++qdebugCounter;
        typeBuf = "qdebug";
        break;
    case QAbstractTestLogger::QWarning:
        ++qwarnCounter;
        typeBuf = "qwarning";
        break;
    case QAbstractTestLogger::QFatal:
        ++qfatalCounter;
        typeBuf = "qfatal";
        break;
    case QAbstractTestLogger::Skip:
        ++skipCounter;
        typeBuf = "skip";
        break;
    case QAbstractTestLogger::Info:
        ++infoCounter;
        typeBuf = "info";
        break;
    default:
        typeBuf = "??????";
        break;
    }

    errorElement->addAttribute(QTest::AI_Type, typeBuf);
    errorElement->addAttribute(QTest::AI_Description, message);

    if(file)
        errorElement->addAttribute(QTest::AI_File, file);
    else
        errorElement->addAttribute(QTest::AI_File, "");

    char buf[100];
    QTest::qt_snprintf(buf, sizeof(buf), "%i", line);
    errorElement->addAttribute(QTest::AI_Line, buf);

    currentLogElement->addLogElement(errorElement);
    ++errorCounter;
}

void QTestLogger::setLogFormat(TestLoggerFormat fm)
{
    format = fm;
}

QTestLogger::TestLoggerFormat QTestLogger::logFormat()
{
    return format;
}

int QTestLogger::passCount() const
{
    return passCounter;
}

int QTestLogger::failureCount() const
{
    return failureCounter;
}

int QTestLogger::errorCount() const
{
    return errorCounter;
}

int QTestLogger::warningCount() const
{
    return warningCounter;
}

int QTestLogger::skipCount() const
{
    return skipCounter;
}

int QTestLogger::systemCount() const
{
    return systemCounter;
}

int QTestLogger::qdebugCount() const
{
    return qdebugCounter;
}

int QTestLogger::qwarnCount() const
{
    return qwarnCounter;
}

int QTestLogger::qfatalCount() const
{
    return qfatalCounter;
}

int QTestLogger::infoCount() const
{
    return infoCounter;
}

QT_END_NAMESPACE

<|endoftext|>


<commit_before>#pragma once

#include <queue>

#include <fc/filesystem.hpp>
#include <fc/io/fstream.hpp>
#include <fc/io/datastream.hpp>
#include <fc/io/raw.hpp>
#include <fstream>
#include <b1/session/session.hpp>
#include <b1/session/session_variant.hpp>
#include <eosio/chain/exceptions.hpp>

namespace eosio::session {
   constexpr uint32_t undo_stack_magic_number = 0x30510ABC;
   constexpr uint32_t undo_stack_min_supported_version = 1;
   constexpr uint32_t undo_stack_max_supported_version = 1;
   constexpr auto undo_stack_filename = "undo_stack.dat";

/// \brief Represents a container of pending sessions to be committed.
template <typename Session>
class undo_stack {
 public:
   using root_type          = Session;
   using session_type       = session<Session>;
   using variant_type       = session_variant<root_type, session_type>;
   using const_variant_type = session_variant<const root_type, const session_type>;

   /// \brief Constructor.
   /// \param head The session that the changes are merged into when commit is called.
   undo_stack(Session& head, const fc::path& datadir = {});
   undo_stack(const undo_stack&) = delete;
   undo_stack(undo_stack&&)      = default;
   ~undo_stack();

   undo_stack& operator=(const undo_stack&) = delete;
   undo_stack& operator=(undo_stack&&) = default;

   /// \brief Adds a new session to the top of the stack.
   void push();

   /// \brief Merges the changes of the top session into the session below it in the stack.
   void squash();

   /// \brief Pops the top session off the stack and discards the changes in that session.
   void undo();

   /// \brief Commits the sessions at the bottom of the stack up to and including the provided revision.
   /// \param revision The revision number to commit up to.
   /// \remarks Each time a session is push onto the stack, a revision is assigned to it.
   void commit(int64_t revision);

   bool   empty() const;
   size_t size() const;

   /// \brief The starting revision number of the stack.
   int64_t revision() const;

   /// \brief Sets the starting revision number of the stack.
   /// \remarks This can only be set when the stack is empty and it can't be set
   /// to value lower than the current revision.
   void revision(int64_t revision);

   /// \brief Returns the head session (the session at the top of the stack.
   variant_type top();

   /// \brief Returns the head session (the session at the top of the stack.
   const_variant_type top() const;

   /// \brief Returns the session at the bottom of the stack.
   /// \remarks This is the next session to be committed.
   variant_type bottom();

   /// \brief Returns the session at the bottom of the stack.
   /// \remarks This is the next session to be committed.
   const_variant_type bottom() const;

   void open();
   void close();

 private:
   int64_t                  m_revision{ 0 };
   Session*                 m_head;
   std::deque<session_type> m_sessions; // Need a deque so pointers don't become invalidated.  The session holds a
                                        // pointer to the parent internally.
   fc::path                 m_datadir;
};

template <typename Session>
undo_stack<Session>::undo_stack(Session& head, const fc::path& datadir) : m_head{ &head }, m_datadir{ datadir } {
   open();
}

template <typename Session>
undo_stack<Session>::~undo_stack() {
   close();
}

template <typename Session>
void undo_stack<Session>::push() {
   if (m_sessions.empty()) {
      m_sessions.emplace_back(*m_head);
   } else {
      m_sessions.emplace_back(m_sessions.back(), nullptr);
   }
   ++m_revision;
}

template <typename Session>
void undo_stack<Session>::squash() {
   if (m_sessions.empty()) {
      return;
   }
   m_sessions.back().commit();
   m_sessions.back().detach();
   m_sessions.pop_back();
   --m_revision;
}

template <typename Session>
void undo_stack<Session>::undo() {
   if (m_sessions.empty()) {
      return;
   }
   m_sessions.back().detach();
   m_sessions.pop_back();
   --m_revision;
}

template <typename Session>
void undo_stack<Session>::commit(int64_t revision) {
   if (m_sessions.empty()) {
      return;
   }

   revision              = std::min(revision, m_revision);
   auto initial_revision = static_cast<int64_t>(m_revision - m_sessions.size() + 1);
   if (initial_revision > revision) {
      return;
   }

   const auto start_index = revision - initial_revision;

   for (int64_t i = start_index; i >= 0; --i) { m_sessions[i].commit(); }
   m_sessions.erase(std::begin(m_sessions), std::begin(m_sessions) + start_index + 1);
   if (!m_sessions.empty()) {
      m_sessions.front().attach(*m_head);
   }
}

template <typename Session>
bool undo_stack<Session>::empty() const {
   return m_sessions.empty();
}

template <typename Session>
size_t undo_stack<Session>::size() const {
   return m_sessions.size();
}

template <typename Session>
int64_t undo_stack<Session>::revision() const {
   return m_revision;
}

template <typename Session>
void undo_stack<Session>::revision(int64_t revision) {
   if (!empty()) {
      return;
   }

   if (revision <= m_revision) {
      return;
   }

   m_revision = revision;
}

template <typename Session>
typename undo_stack<Session>::variant_type undo_stack<Session>::top() {
   if (!m_sessions.empty()) {
      auto& back = m_sessions.back();
      return { back, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::const_variant_type undo_stack<Session>::top() const {
   if (!m_sessions.empty()) {
      auto& back = m_sessions.back();
      return { back, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::variant_type undo_stack<Session>::bottom() {
   if (!m_sessions.empty()) {
      auto& front = m_sessions.front();
      return { front, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::const_variant_type undo_stack<Session>::bottom() const {
   if (!m_sessions.empty()) {
      auto& front = m_sessions.front();
      return { front, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
void undo_stack<Session>::open() {
   if (m_datadir.empty())
      return;

   if (!fc::is_directory(m_datadir))
      fc::create_directories(m_datadir);

   auto undo_stack_dat = m_datadir / undo_stack_filename;
   if( fc::exists( undo_stack_dat ) ) {
      try {
         std::string content;
         fc::read_file_contents( undo_stack_dat, content );

         fc::datastream<const char*> ds( content.data(), content.size() );

         // validate totem
         uint32_t totem = 0;
         fc::raw::unpack( ds, totem );
         EOS_ASSERT( totem == undo_stack_magic_number, eosio::chain::chain_exception,
                     "Undo stack data file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${expected_totem}",
                     ("filename", undo_stack_dat.generic_string())
                     ("actual_totem", totem)
                     ("expected_totem", undo_stack_magic_number)
         );

         // validate version
         uint32_t version = 0;
         fc::raw::unpack( ds, version );
         EOS_ASSERT( version >= undo_stack_min_supported_version && version <= undo_stack_max_supported_version,
                    eosio::chain::chain_exception,
                    "Unsupported version of Undo stack data file '${filename}'. "
                    "Undo stack data version is ${version} while code supports version(s) [${min},${max}]",
                    ("filename", undo_stack_dat.generic_string())
                    ("version", version)
                    ("min", undo_stack_min_supported_version)
                    ("max", undo_stack_max_supported_version)
         );

         int64_t rev; fc::raw::unpack( ds, rev );

         size_t num_sessions; fc::raw::unpack( ds, num_sessions );
         for( size_t i = 0; i < num_sessions; ++i ) {
            push();
            auto& session = m_sessions.back();

            size_t num_updated_keys; fc::raw::unpack( ds, num_updated_keys );
            for( size_t j = 0; j < num_updated_keys; ++j ) {
               shared_bytes key; ds >> key;
               shared_bytes value; ds >> value;
               session.write(key, value);
            }

            size_t num_deleted_keys; fc::raw::unpack( ds, num_deleted_keys );
            for( size_t j = 0; j < num_deleted_keys; ++j ) {
               shared_bytes key; ds >> key;
               session.erase(key);
            }
         }
         m_revision = rev; // restore head revision
      } FC_CAPTURE_AND_RETHROW( (undo_stack_dat) )

      fc::remove( undo_stack_dat );
   }
}

template <typename Session>
void undo_stack<Session>::close() {
   if (m_datadir.empty())
      return;

   auto undo_stack_dat = m_datadir / undo_stack_filename;

   std::ofstream out( undo_stack_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc );
   fc::raw::pack( out, undo_stack_magic_number );
   fc::raw::pack( out, undo_stack_max_supported_version );

   fc::raw::pack( out, revision() );
   fc::raw::pack( out, size() ); // number of sessions

   while ( !m_sessions.empty() ) {
      auto& session = m_sessions.front();
      auto updated_keys = session.updated_keys();
      fc::raw::pack( out, updated_keys.size() ); // number of updated keys

      for (const auto& key: updated_keys) {
        auto value = session.read(key);

        if ( value ) {
           out << key;
           out << *value;
        }
      }

      auto deleted_keys = session.deleted_keys();
      fc::raw::pack( out, deleted_keys.size() ); // number of deleted keys

      for (const auto& key: deleted_keys) {
         fc::raw::pack( out, key );
      }

      session.detach();
      m_sessions.pop_front();
   }
}
} // namespace eosio::session
<commit_msg>Do not keep undo_stack.dat when error happens<commit_after>#pragma once

#include <queue>

#include <fc/filesystem.hpp>
#include <fc/io/fstream.hpp>
#include <fc/io/datastream.hpp>
#include <fc/io/raw.hpp>
#include <fstream>
#include <b1/session/session.hpp>
#include <b1/session/session_variant.hpp>
#include <eosio/chain/exceptions.hpp>

namespace eosio::session {
   constexpr uint32_t undo_stack_magic_number = 0x30510ABC;
   constexpr uint32_t undo_stack_min_supported_version = 1;
   constexpr uint32_t undo_stack_max_supported_version = 1;
   constexpr auto undo_stack_filename = "undo_stack.dat";

/// \brief Represents a container of pending sessions to be committed.
template <typename Session>
class undo_stack {
 public:
   using root_type          = Session;
   using session_type       = session<Session>;
   using variant_type       = session_variant<root_type, session_type>;
   using const_variant_type = session_variant<const root_type, const session_type>;

   /// \brief Constructor.
   /// \param head The session that the changes are merged into when commit is called.
   undo_stack(Session& head, const fc::path& datadir = {});
   undo_stack(const undo_stack&) = delete;
   undo_stack(undo_stack&&)      = default;
   ~undo_stack();

   undo_stack& operator=(const undo_stack&) = delete;
   undo_stack& operator=(undo_stack&&) = default;

   /// \brief Adds a new session to the top of the stack.
   void push();

   /// \brief Merges the changes of the top session into the session below it in the stack.
   void squash();

   /// \brief Pops the top session off the stack and discards the changes in that session.
   void undo();

   /// \brief Commits the sessions at the bottom of the stack up to and including the provided revision.
   /// \param revision The revision number to commit up to.
   /// \remarks Each time a session is push onto the stack, a revision is assigned to it.
   void commit(int64_t revision);

   bool   empty() const;
   size_t size() const;

   /// \brief The starting revision number of the stack.
   int64_t revision() const;

   /// \brief Sets the starting revision number of the stack.
   /// \remarks This can only be set when the stack is empty and it can't be set
   /// to value lower than the current revision.
   void revision(int64_t revision);

   /// \brief Returns the head session (the session at the top of the stack.
   variant_type top();

   /// \brief Returns the head session (the session at the top of the stack.
   const_variant_type top() const;

   /// \brief Returns the session at the bottom of the stack.
   /// \remarks This is the next session to be committed.
   variant_type bottom();

   /// \brief Returns the session at the bottom of the stack.
   /// \remarks This is the next session to be committed.
   const_variant_type bottom() const;

   void open();
   void close();

 private:
   int64_t                  m_revision{ 0 };
   Session*                 m_head;
   std::deque<session_type> m_sessions; // Need a deque so pointers don't become invalidated.  The session holds a
                                        // pointer to the parent internally.
   fc::path                 m_datadir;
};

template <typename Session>
undo_stack<Session>::undo_stack(Session& head, const fc::path& datadir) : m_head{ &head }, m_datadir{ datadir } {
   open();
}

template <typename Session>
undo_stack<Session>::~undo_stack() {
   close();
}

template <typename Session>
void undo_stack<Session>::push() {
   if (m_sessions.empty()) {
      m_sessions.emplace_back(*m_head);
   } else {
      m_sessions.emplace_back(m_sessions.back(), nullptr);
   }
   ++m_revision;
}

template <typename Session>
void undo_stack<Session>::squash() {
   if (m_sessions.empty()) {
      return;
   }
   m_sessions.back().commit();
   m_sessions.back().detach();
   m_sessions.pop_back();
   --m_revision;
}

template <typename Session>
void undo_stack<Session>::undo() {
   if (m_sessions.empty()) {
      return;
   }
   m_sessions.back().detach();
   m_sessions.pop_back();
   --m_revision;
}

template <typename Session>
void undo_stack<Session>::commit(int64_t revision) {
   if (m_sessions.empty()) {
      return;
   }

   revision              = std::min(revision, m_revision);
   auto initial_revision = static_cast<int64_t>(m_revision - m_sessions.size() + 1);
   if (initial_revision > revision) {
      return;
   }

   const auto start_index = revision - initial_revision;

   for (int64_t i = start_index; i >= 0; --i) { m_sessions[i].commit(); }
   m_sessions.erase(std::begin(m_sessions), std::begin(m_sessions) + start_index + 1);
   if (!m_sessions.empty()) {
      m_sessions.front().attach(*m_head);
   }
}

template <typename Session>
bool undo_stack<Session>::empty() const {
   return m_sessions.empty();
}

template <typename Session>
size_t undo_stack<Session>::size() const {
   return m_sessions.size();
}

template <typename Session>
int64_t undo_stack<Session>::revision() const {
   return m_revision;
}

template <typename Session>
void undo_stack<Session>::revision(int64_t revision) {
   if (!empty()) {
      return;
   }

   if (revision <= m_revision) {
      return;
   }

   m_revision = revision;
}

template <typename Session>
typename undo_stack<Session>::variant_type undo_stack<Session>::top() {
   if (!m_sessions.empty()) {
      auto& back = m_sessions.back();
      return { back, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::const_variant_type undo_stack<Session>::top() const {
   if (!m_sessions.empty()) {
      auto& back = m_sessions.back();
      return { back, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::variant_type undo_stack<Session>::bottom() {
   if (!m_sessions.empty()) {
      auto& front = m_sessions.front();
      return { front, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
typename undo_stack<Session>::const_variant_type undo_stack<Session>::bottom() const {
   if (!m_sessions.empty()) {
      auto& front = m_sessions.front();
      return { front, nullptr };
   }
   return { *m_head, nullptr };
}

template <typename Session>
void undo_stack<Session>::open() {
   if (m_datadir.empty())
      return;

   if (!fc::is_directory(m_datadir))
      fc::create_directories(m_datadir);

   auto undo_stack_dat = m_datadir / undo_stack_filename;
   if( fc::exists( undo_stack_dat ) ) {
      try {
         std::string content;
         fc::read_file_contents( undo_stack_dat, content );

         fc::datastream<const char*> ds( content.data(), content.size() );

         // validate totem
         uint32_t totem = 0;
         fc::raw::unpack( ds, totem );
         EOS_ASSERT( totem == undo_stack_magic_number, eosio::chain::chain_exception,
                     "Undo stack data file '${filename}' has unexpected magic number: ${actual_totem}. Expected ${expected_totem}",
                     ("filename", undo_stack_dat.generic_string())
                     ("actual_totem", totem)
                     ("expected_totem", undo_stack_magic_number)
         );

         // validate version
         uint32_t version = 0;
         fc::raw::unpack( ds, version );
         EOS_ASSERT( version >= undo_stack_min_supported_version && version <= undo_stack_max_supported_version,
                    eosio::chain::chain_exception,
                    "Unsupported version of Undo stack data file '${filename}'. "
                    "Undo stack data version is ${version} while code supports version(s) [${min},${max}]",
                    ("filename", undo_stack_dat.generic_string())
                    ("version", version)
                    ("min", undo_stack_min_supported_version)
                    ("max", undo_stack_max_supported_version)
         );

         int64_t rev; fc::raw::unpack( ds, rev );

         size_t num_sessions; fc::raw::unpack( ds, num_sessions );
         for( size_t i = 0; i < num_sessions; ++i ) {
            push();
            auto& session = m_sessions.back();

            size_t num_updated_keys; fc::raw::unpack( ds, num_updated_keys );
            for( size_t j = 0; j < num_updated_keys; ++j ) {
               shared_bytes key; ds >> key;
               shared_bytes value; ds >> value;
               session.write(key, value);
            }

            size_t num_deleted_keys; fc::raw::unpack( ds, num_deleted_keys );
            for( size_t j = 0; j < num_deleted_keys; ++j ) {
               shared_bytes key; ds >> key;
               session.erase(key);
            }
         }
         m_revision = rev; // restore head revision
      } FC_CAPTURE_AND_RETHROW( (undo_stack_dat) )

      fc::remove( undo_stack_dat );
   }
}

template <typename Session>
void undo_stack<Session>::close() {
   if (m_datadir.empty())
      return;

   auto undo_stack_dat = m_datadir / undo_stack_filename;

   std::ofstream out( undo_stack_dat.generic_string().c_str(), std::ios::out | std::ios::binary | std::ofstream::trunc );
   fc::raw::pack( out, undo_stack_magic_number );
   fc::raw::pack( out, undo_stack_max_supported_version );

   fc::raw::pack( out, revision() );
   fc::raw::pack( out, size() ); // number of sessions

   while ( !m_sessions.empty() ) {
      auto& session = m_sessions.front();
      auto updated_keys = session.updated_keys();
      fc::raw::pack( out, updated_keys.size() ); // number of updated keys

      for (const auto& key: updated_keys) {
         auto value = session.read(key);

         if ( value ) {
            out << key;
            out << *value;
         } else {
            fc::remove( undo_stack_dat ); // May not be used by next startup
            elog( "Did not find value for ${k}", ("k", key.data() ) );
            return; // Do not assert as we are during shutdown
         }
      }

      auto deleted_keys = session.deleted_keys();
      fc::raw::pack( out, deleted_keys.size() ); // number of deleted keys

      for (const auto& key: deleted_keys) {
         fc::raw::pack( out, key );
      }

      session.detach();
      m_sessions.pop_front();
   }
}
} // namespace eosio::session
<|endoftext|>


<commit_before>// bslim_printer.cpp                                                  -*-C++-*-
#include <bslim_printer.h>

#include <bsls_ident.h>
BSLS_IDENT("$Id$ $CSID$")

#include <bslim_testutil.h>  // for testing only

#include <bsls_assert.h>

#include <bsl_cctype.h>
#include <bsl_iomanip.h>
#include <bsl_ios.h>

namespace BloombergLP {

namespace {

static
void putSpaces(bsl::ostream& stream, int numSpaces)
    // Efficiently insert the specified 'numSpaces' spaces into the specified
    // 'stream'.  This function has no effect on 'stream' if 'numSpaces < 0'.
{
    // Algorithm: Write spaces in chunks.  The chunk size is large enough so
    // that most times only a single call to the 'write' method is needed.

    // Define the largest chunk of spaces:
    static const char k_SPACES[]    = "                                      ";
           const int  k_SPACES_SIZE = sizeof(k_SPACES) - 1;

    while (k_SPACES_SIZE < numSpaces) {
        stream.write(k_SPACES, k_SPACES_SIZE);
        numSpaces -= k_SPACES_SIZE;
    }

    if (0 < numSpaces) {
        stream.write(k_SPACES, numSpaces);
    }
}

class FormatGuard {
    // Class that saves the format flags from a stream.  Note 'ios_base' is a
    // base class that both 'ostream' and 'istream' inherit from.

    // DATA
    bsl::ios_base           *d_stream;
    bsl::ios_base::fmtflags  d_flags;

  public:
    // CREATORS
    explicit
    FormatGuard(bsl::ios_base *stream);
        // Save a pointer to the specified 'stream', and save its format flags,
        // to be restored upon this object's destruction.

    ~FormatGuard();
        // Restore the format flags that were saved at construction to the
        // stream whose pointer we saved at construction.
};

// CREATORS
inline
FormatGuard::FormatGuard(bsl::ios_base *stream)
{
    d_stream = stream;
    d_flags  = stream->flags();
}

inline
FormatGuard::~FormatGuard()
{
    d_stream->flags(d_flags);
}

}  // close unnamed namespace

namespace bslim {

                        // -------------
                        // class Printer
                        // -------------

// PRIVATE ACCESSORS
void Printer::printEndIndentation() const
{
    putSpaces(*d_stream_p, d_spacesPerLevel < 0
                           ? 1
                           : d_spacesPerLevel * d_level);
}

void Printer::printIndentation() const
{
    putSpaces(*d_stream_p, d_spacesPerLevel < 0
                           ? 1
                           : d_spacesPerLevel * d_levelPlusOne);
}
// CREATORS
Printer::Printer(bsl::ostream *stream, int level, int spacesPerLevel)
: d_stream_p(stream)
, d_spacesPerLevel(spacesPerLevel)
{
    BSLS_ASSERT(stream);

    d_suppressInitialIndentFlag = level < 0;
    d_level                     = level < 0 ? -level : level;
    d_levelPlusOne              = d_level + 1;
}

Printer::~Printer()
{
}

// ACCESSORS
int Printer::absLevel() const
{
    return d_level;
}

void Printer::end(bool suppressBracket) const
{
    if (!suppressBracket) {
        printEndIndentation();
        *d_stream_p << ']';
    }

    if (d_spacesPerLevel >= 0) {
        *d_stream_p << '\n';
    }
}

void Printer::printHexAddr(const void *address, const char *name) const
{
    printIndentation();

    if (name != NULL) {
        *d_stream_p << name << " = ";
    }

    Printer_Helper::print(*d_stream_p,
                          address,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

int Printer::spacesPerLevel() const
{
    return d_spacesPerLevel;
}

void Printer::start(bool suppressBracket) const
{
    if (!suppressInitialIndentFlag()) {
        const int absSpacesPerLevel = d_spacesPerLevel < 0
                                      ? -d_spacesPerLevel
                                      :  d_spacesPerLevel;
        putSpaces(*d_stream_p, absSpacesPerLevel * d_level);
    }

    if (!suppressBracket) {
        *d_stream_p << '[';
        if (d_spacesPerLevel >= 0) {
            *d_stream_p << '\n';
        }
    }
}

bool Printer::suppressInitialIndentFlag() const
{
    return d_suppressInitialIndentFlag;
}

                        // ---------------------
                        // struct Printer_Helper
                        // ---------------------

// CLASS METHODS
void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              char                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_fundamental>)
{
#define HANDLE_CONTROL_CHAR(value) case value: stream << #value; break;
    if (bsl::isprint(static_cast<unsigned char>(data))) {
        // print within quotes

        stream << "'" << data << "'";
    }
    else {
        switch(data) {
          HANDLE_CONTROL_CHAR('\n');
          HANDLE_CONTROL_CHAR('\t');
          HANDLE_CONTROL_CHAR('\0');

          default: {
            // Print as hex.

            FormatGuard guard(&stream);
            stream << bsl::hex
                   << bsl::showbase
                   << static_cast<bsls::Types::UintPtr>(
                                             static_cast<unsigned char>(data));
          }
        }
    }
#undef HANDLE_CONTROL_CHAR

    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              bool                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_fundamental>)
{
    {
        FormatGuard guard(&stream);
        stream << bsl::boolalpha
               << data;
    }

    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const char                *data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_pointer>)
{
    if (0 == data) {
        stream << "NULL";
    }
    else {
        stream << '"' << data << '"';
    }
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const void                *data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_pointer>)
{
    if (0 == data) {
        stream << "NULL";
    }
    else {
        FormatGuard guard(&stream);
        stream << bsl::hex
               << bsl::showbase
               << reinterpret_cast<bsls::Types::UintPtr>(data);
    }
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const bslstl::StringRef&   data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    // Use the defined 'operator<<' for 'bslstl::StringRef' because it does
    // not define a 'print' method and also does not define the
    // 'bslalg::HasStlIterators' type trait due to only having defined a
    // 'const_iterator' interface.

    stream << '"' << data << '"';
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

}  // close package namespace
}  // close enterprise namespace

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<commit_msg>bslim_printer: bdeflag cleanup<commit_after>// bslim_printer.cpp                                                  -*-C++-*-
#include <bslim_printer.h>

#include <bsls_ident.h>
BSLS_IDENT("$Id$ $CSID$")

#include <bslim_testutil.h>  // for testing only

#include <bsls_assert.h>

#include <bsl_cctype.h>
#include <bsl_iomanip.h>
#include <bsl_ios.h>

namespace BloombergLP {

namespace {

static
void putSpaces(bsl::ostream& stream, int numSpaces)
    // Efficiently insert the specified 'numSpaces' spaces into the specified
    // 'stream'.  This function has no effect on 'stream' if 'numSpaces < 0'.
{
    // Algorithm: Write spaces in chunks.  The chunk size is large enough so
    // that most times only a single call to the 'write' method is needed.

    // Define the largest chunk of spaces:

    static const char k_SPACES[]    = "                                      ";
    const        int  k_SPACES_SIZE = sizeof(k_SPACES) - 1;

    while (k_SPACES_SIZE < numSpaces) {
        stream.write(k_SPACES, k_SPACES_SIZE);
        numSpaces -= k_SPACES_SIZE;
    }

    if (0 < numSpaces) {
        stream.write(k_SPACES, numSpaces);
    }
}

class FormatGuard {
    // Class that saves the format flags from a stream.  Note 'ios_base' is a
    // base class that both 'ostream' and 'istream' inherit from.

    // DATA
    bsl::ios_base           *d_stream;
    bsl::ios_base::fmtflags  d_flags;

  public:
    // CREATORS
    explicit
    FormatGuard(bsl::ios_base *stream);
        // Save a pointer to the specified 'stream', and save its format flags,
        // to be restored upon this object's destruction.

    ~FormatGuard();
        // Restore the format flags that were saved at construction to the
        // stream whose pointer we saved at construction.
};

// CREATORS
inline
FormatGuard::FormatGuard(bsl::ios_base *stream)
{
    d_stream = stream;
    d_flags  = stream->flags();
}

inline
FormatGuard::~FormatGuard()
{
    d_stream->flags(d_flags);
}

}  // close unnamed namespace

namespace bslim {

                        // -------------
                        // class Printer
                        // -------------

// PRIVATE ACCESSORS
void Printer::printEndIndentation() const
{
    putSpaces(*d_stream_p, d_spacesPerLevel < 0
                           ? 1
                           : d_spacesPerLevel * d_level);
}

void Printer::printIndentation() const
{
    putSpaces(*d_stream_p, d_spacesPerLevel < 0
                           ? 1
                           : d_spacesPerLevel * d_levelPlusOne);
}
// CREATORS
Printer::Printer(bsl::ostream *stream, int level, int spacesPerLevel)
: d_stream_p(stream)
, d_spacesPerLevel(spacesPerLevel)
{
    BSLS_ASSERT(stream);

    d_suppressInitialIndentFlag = level < 0;
    d_level                     = level < 0 ? -level : level;
    d_levelPlusOne              = d_level + 1;
}

Printer::~Printer()
{
}

// ACCESSORS
int Printer::absLevel() const
{
    return d_level;
}

void Printer::end(bool suppressBracket) const
{
    if (!suppressBracket) {
        printEndIndentation();
        *d_stream_p << ']';
    }

    if (d_spacesPerLevel >= 0) {
        *d_stream_p << '\n';
    }
}

void Printer::printHexAddr(const void *address, const char *name) const
{
    printIndentation();

    if (name != NULL) {
        *d_stream_p << name << " = ";
    }

    Printer_Helper::print(*d_stream_p,
                          address,
                          -d_levelPlusOne,
                          d_spacesPerLevel);
}

int Printer::spacesPerLevel() const
{
    return d_spacesPerLevel;
}

void Printer::start(bool suppressBracket) const
{
    if (!suppressInitialIndentFlag()) {
        const int absSpacesPerLevel = d_spacesPerLevel < 0
                                      ? -d_spacesPerLevel
                                      :  d_spacesPerLevel;
        putSpaces(*d_stream_p, absSpacesPerLevel * d_level);
    }

    if (!suppressBracket) {
        *d_stream_p << '[';
        if (d_spacesPerLevel >= 0) {
            *d_stream_p << '\n';
        }
    }
}

bool Printer::suppressInitialIndentFlag() const
{
    return d_suppressInitialIndentFlag;
}

                        // ---------------------
                        // struct Printer_Helper
                        // ---------------------

// CLASS METHODS
void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              char                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_fundamental>)
{
#define HANDLE_CONTROL_CHAR(value) case value: stream << #value; break;
    if (bsl::isprint(static_cast<unsigned char>(data))) {
        // print within quotes

        stream << "'" << data << "'";
    }
    else {
        switch(data) {
          HANDLE_CONTROL_CHAR('\n');
          HANDLE_CONTROL_CHAR('\t');
          HANDLE_CONTROL_CHAR('\0');

          default: {
            // Print as hex.

            FormatGuard guard(&stream);
            stream << bsl::hex
                   << bsl::showbase
                   << static_cast<bsls::Types::UintPtr>(
                                             static_cast<unsigned char>(data));
          }
        }
    }
#undef HANDLE_CONTROL_CHAR

    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&                  stream,
                              bool                           data,
                              int                            ,
                              int                            spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_fundamental>)
{
    {
        FormatGuard guard(&stream);
        stream << bsl::boolalpha
               << data;
    }

    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const char                *data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_pointer>)
{
    if (0 == data) {
        stream << "NULL";
    }
    else {
        stream << '"' << data << '"';
    }
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const void                *data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<bsl::is_pointer>)
{
    if (0 == data) {
        stream << "NULL";
    }
    else {
        FormatGuard guard(&stream);
        stream << bsl::hex
               << bsl::showbase
               << reinterpret_cast<bsls::Types::UintPtr>(data);
    }
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

void Printer_Helper::printRaw(bsl::ostream&              stream,
                              const bslstl::StringRef&   data,
                              int                        ,
                              int                        spacesPerLevel,
                              bslmf::SelectTraitCase<>)
{
    // Use the defined 'operator<<' for 'bslstl::StringRef' because it does
    // not define a 'print' method and also does not define the
    // 'bslalg::HasStlIterators' type trait due to only having defined a
    // 'const_iterator' interface.

    stream << '"' << data << '"';
    if (spacesPerLevel >= 0) {
        stream << '\n';
    }
}

}  // close package namespace
}  // close enterprise namespace

// ----------------------------------------------------------------------------
// Copyright 2013 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
<|endoftext|>


<commit_before>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->selectedPage = -1;
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->selectedPage = -1;
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
	delay(10); //Avoid Bugs on Arduino UNO
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, uint32_t value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	uint8_t buffer;
	//Select page and address
	if(this->selectedPage != page){
		buffer = (pageByte | (uint8_t)page);
		cSerial->write(buffer);
		this->selectedPage = page;
	}
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	this->clearSerialBuffer();

	uint8_t buffer;
	//Select page and address
	if(this->selectedPage != page){
		buffer = (pageByte | (uint8_t)page);
		cSerial->write(buffer);
		this->selectedPage = page;
	}
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){
	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/******* Clears cSerial Buffer *******/
void CS5490::clearSerialBuffer(){
	while (cSerial->available()) cSerial->read();
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Exponent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

	https://repl.it/@tiagolobao/toDouble-CS5490
*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	uint32_t buffer = 0;
	double output = 0.0;
	bool MSB;

	//Concat bytes in a 32 bit word
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;

  switch(MSBoption){

    case MSBnull:
			buffer &= 0x7FFFFF; //Clear MSB
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			buffer = buffer & 0x00FFFFFF; //Clearing the first 8 bits
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/*
  Function: toBinary
  Transforms a double number to a 24 bit number for writing registers

  Param:
  LSBpow => Expoent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.
  input => (double) value to be sent to CS5490
*/
uint32_t CS5490::toBinary(int LSBpow, int MSBoption, double input){

	uint32_t output;

  switch(MSBoption){
    case MSBnull:
      input *= pow(2,LSBpow);
      output = (uint32_t)input;
      output &= 0x7FFFFF; //Clear Don't care bits
    break;
    case MSBsigned:
      if(input <= 0){ //- (2 complement conversion)
        input *= -pow(2,LSBpow);
        output = (uint32_t)input;
        output = ~output;
        output = (output+1) & 0xFFFFFF; //Clearing the first 8 bits
      }
      else{           //+
        input *= (pow(2,LSBpow)-1.0);
        output = (uint32_t)input;
      }
    break;
    default:
    case MSBunsigned:
  		input *= pow(2,LSBpow);
      output = (uint32_t)input;
    break;
  }

  return output;
}

/******* Concatenation of the incomming data from CS5490 *******/
uint32_t CS5490::concatData(){
	uint32_t buffer;
	buffer += this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	return buffer;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	delay(50); //Avoid bugs from Arduino MEGA
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->data[0];
	buffer += this->data[1] << 8;
	buffer += this->data[2] << 16;
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 61
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

uint32_t CS5490::readReg(int page, int address){
	uint32_t value = 0;
	this->read(page, address);
	value = value + data[2] << 8;
	value = value + data[1] << 8;
	value = value + data[0];
	return value;
}
<commit_msg>Using concatData method<commit_after>/******************************************

	Author: Tiago Britto Lobão
	tiago.blobao@gmail.com
*/

/*
	Purpose: Control an integrated circuit
	Cirrus Logic - CS5490

	Used to measure electrical quantities

	MIT License

******************************************/

#include "CS5490.h"

/******* Init CS5490 *******/

//For Arduino & ESP8622
#if !(defined ARDUINO_NodeMCU_32S ) && !defined(__AVR_ATmega1280__) && !defined(__AVR_ATmega2560__) && !defined(ARDUINO_Node32s)
	CS5490::CS5490(float mclk, int rx, int tx){
		this->selectedPage = -1;
		this->MCLK = mclk;
		this->cSerial = new SoftwareSerial(rx,tx);
	}
//For ESP32 AND MEGA
#else
	CS5490::CS5490(float mclk){
		this->selectedPage = -1;
		this->MCLK = mclk;
		this->cSerial = &Serial2;
	}
#endif

void CS5490::begin(int baudRate){
	cSerial->begin(baudRate);
	delay(10); //Avoid Bugs on Arduino UNO
}

/**************************************************************/
/*                     PRIVATE METHODS                        */
/**************************************************************/

/******* Write a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::write(int page, int address, uint32_t value){

	uint8_t checksum = 0;
	for(int i=0; i<3; i++)
		checksum += 0xFF - checksum;

	uint8_t buffer;
	//Select page and address
	if(this->selectedPage != page){
		buffer = (pageByte | (uint8_t)page);
		cSerial->write(buffer);
		this->selectedPage = page;
	}
	buffer = (writeByte | (uint8_t)address);
	cSerial->write(buffer);

	//Send information
	for(int i=0; i<3 ; i++){
		data[i] = value & 0x000000FF;
		cSerial->write(this->data[i]);
		value >>= 8;
	}
	//Calculate and send checksum
	buffer = 0xFF - data[0] - data[1] - data[2];
	cSerial->write(buffer);
}

/******* Read a register by the serial communication *******/
/* data bytes pass by data variable from this class */

void CS5490::read(int page, int address){

	this->clearSerialBuffer();

	uint8_t buffer;
	//Select page and address
	if(this->selectedPage != page){
		buffer = (pageByte | (uint8_t)page);
		cSerial->write(buffer);
		this->selectedPage = page;
	}
	buffer = (readByte | (uint8_t)address);
	cSerial->write(buffer);

	//Wait for 3 bytes to arrive
	while(cSerial->available() < 3);
	for(int i=0; i<3; i++){
		data[i] = cSerial->read();
	}
}

/******* Give an instruction by the serial communication *******/

void CS5490::instruct(int value){
	uint8_t buffer = (instructionByte | (uint8_t)value);
	cSerial->write(buffer);
}

/******* Clears cSerial Buffer *******/
void CS5490::clearSerialBuffer(){
	while (cSerial->available()) cSerial->read();
}

/*
  Function: toDouble
  Transforms a 24 bit number to a double number for easy processing data

  Param:
  data[] => Array with size 3. Each uint8_t is an 8 byte number received from CS5490
  LSBpow => Exponent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.

	https://repl.it/@tiagolobao/toDouble-CS5490
*/
double CS5490::toDouble(int LSBpow, int MSBoption){

	double output = 0.0;
	bool MSB;

	uint32_t buffer = this->concatData();

  switch(MSBoption){

    case MSBnull:
			buffer &= 0x7FFFFF; //Clear MSB
      output = (double)buffer;
      output /= pow(2,LSBpow);
    break;

    case MSBsigned:
      MSB = data[2] & 0x80;
  		if(MSB){  //- (2 complement conversion)
  			buffer = ~buffer;
  			buffer = buffer & 0x00FFFFFF; //Clearing the first 8 bits
  			output = (double)buffer + 1.0;
  			output /= -pow(2,LSBpow);
  		}
  		else{     //+
  		  output = (double)buffer;
  			output /= (pow(2,LSBpow)-1.0);
  		}
    break;

    default:
    case MSBunsigned:
      output = (double)buffer;
  		output /= pow(2,LSBpow);
    break;

  }

	return output;
}

/*
  Function: toBinary
  Transforms a double number to a 24 bit number for writing registers

  Param:
  LSBpow => Expoent specified from datasheet of the less significant bit
  MSBoption => Information of most significant bit case. It can be only three values:
    MSBnull (1)  The MSB is a Don't Care bit
    MSBsigned (2) the MSB is a negative value, requiring a 2 complement conversion
    MSBunsigned (3) The MSB is a positive value, the default case.
  input => (double) value to be sent to CS5490
*/
uint32_t CS5490::toBinary(int LSBpow, int MSBoption, double input){

	uint32_t output;

  switch(MSBoption){
    case MSBnull:
      input *= pow(2,LSBpow);
      output = (uint32_t)input;
      output &= 0x7FFFFF; //Clear Don't care bits
    break;
    case MSBsigned:
      if(input <= 0){ //- (2 complement conversion)
        input *= -pow(2,LSBpow);
        output = (uint32_t)input;
        output = ~output;
        output = (output+1) & 0xFFFFFF; //Clearing the first 8 bits
      }
      else{           //+
        input *= (pow(2,LSBpow)-1.0);
        output = (uint32_t)input;
      }
    break;
    default:
    case MSBunsigned:
  		input *= pow(2,LSBpow);
      output = (uint32_t)input;
    break;
  }

  return output;
}

/******* Concatenation of the incomming data from CS5490 *******/
uint32_t CS5490::concatData(){
	uint32_t output;
	output = output + data[2] << 8;
	output = output + data[1] << 8;
	output = output + data[0];
	return output;
}

/**************************************************************/
/*              PUBLIC METHODS - Instructions                 */
/**************************************************************/
void CS5490::reset(){
	this->instruct(1);
}

void CS5490::standby(){
	this->instruct(2);
}

void CS5490::wakeUp(){
	this->instruct(3);
}

void CS5490::singConv(){
	this->instruct(20);
}

void CS5490::contConv(){
	this->instruct(21);
}

void CS5490::haltConv(){
	this->instruct(24);
}

/**************************************************************/
/*       PUBLIC METHODS - Calibration and Configuration       */
/**************************************************************/

/* SET */
void CS5490::setBaudRate(long value){

	//Calculate the correct binary value
	uint32_t hexBR = ceil(value*0.5242880/MCLK);
	if (hexBR > 65535) hexBR = 65535;
	hexBR += 0x020000;

	this->write(0x80,0x07,hexBR);
	delay(100); //To avoid bugs from ESP32

	//Reset Serial communication from controller
	cSerial->end();
	cSerial->begin(value);
	delay(50); //Avoid bugs from Arduino MEGA
	return;
}

/* GET */
int CS5490::getGainI(){
	//Page 16, Address 33
	this->read(16,33);
	return this->toDouble(22,MSBunsigned);
}

long CS5490::getBaudRate(){
	this->read(0,7);
	uint32_t buffer = this->concatData();
	buffer -= 0x020000;
	return ( (buffer/0.5242880)*MCLK );
}

/**************************************************************/
/*              PUBLIC METHODS - Measurements                 */
/**************************************************************/

double CS5490::getPeakV(){
	//Page 0, Address 36
	this->read(0,36);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPeakI(){
	//Page 0, Address 37
	this->read(0,37);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstI(){
	//Page 16, Address 2
	this->read(16,2);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstV(){
	//Page 16, Address 3
	this->read(16,3);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstP(){
	//Page 16, Address 4
	this->read(16,4);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getRmsI(){
	//Page 16, Address 6
	this->read(16,6);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getRmsV(){
	//Page 16, Address 7
	this->read(16,7);
	return this->toDouble(24, MSBunsigned);
}

double CS5490::getAvgP(){
	//Page 16, Address 5
	this->read(16,5);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgQ(){
	//Page 16, Address 14
	this->read(16,14);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getAvgS(){
	//Page 16, Address 20
	this->read(16,20);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getInstQ(){
	//Page 16, Address 15
	this->read(16,15);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getPF(){
	//Page 16, Address 21
	this->read(16,21);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalP(){
	//Page 16, Address 29
	this->read(16,29);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalS(){
	//Page 16, Address 30
	this->read(16,30);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTotalQ(){
	//Page 16, Address 31
	this->read(16,31);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getFreq(){
	//Page 16, Address 49
	this->read(16,49);
	return this->toDouble(23, MSBsigned);
}

double CS5490::getTime(){
	//Page 16, Address 61
	this->read(16,61);
	return this->toDouble(0, MSBunsigned);
}

/**************************************************************/
/*              PUBLIC METHODS - Read Register                */
/**************************************************************/

uint32_t CS5490::readReg(int page, int address){
	this->read(page, address);
	return this->concatData();
}
<|endoftext|>


<commit_before>/*
 * Canvas.cpp
 *
 *  Created on: Oct 14, 2014
 *      Author: Carsten Uphoff (uphoff@mytum.de)
 */
#include "Canvas.hpp"
#include "GL/glld.h"
#include <cmath>

namespace hpl
{
Canvas::Canvas()
{
}

Canvas::~Canvas()
{
}

CoordinateSystem& Canvas::addCoordinateSystem()
{
    CoordinateSystem* cs = new CoordinateSystem(rawData, dataRevisions);
    cs->changed.template bind<Canvas, &Canvas::processUpdate>(this);

    csystems.add(cs);

    processUpdate();

    return *cs;
}

Canvas::Slot* Canvas::findSlot(Slot const& slot)
{
	for (auto it = racks.begin(); it != racks.end(); ++it) {
		auto f = std::find(it->second.slots.begin(), it->second.slots.end(), slot);
		if (f != it->second.slots.end()) {
			return &(*f);
		}
	}

	return nullptr;
}

void Canvas::addSlotToLayout(Slot const& slot, Layout::ID to)
{
	Slot* last = findSlot(slot);
	if (last != nullptr) {
		last->invalidate();
	}
	Rack& rack = racks[to];
	rack.slots.push_back(slot);
	rack.geometries.push_back(Geometry());

	recalculateLayout(to);
}

void Canvas::recalculateLayout(Layout::ID layout)
{
	Rack& rack = racks[layout];
	layouts.lookup(layout).get(rack.geometries);

	auto s = rack.slots.cbegin();
	auto g = rack.geometries.cbegin();
	for (; s != rack.slots.cend() && g != rack.geometries.cend(); ++s, ++g) {
		if (s->cs.valid()) {
			csystems.lookup(s->cs).setGeometry(*g);
		} else if (s->layout.valid()) {
			layouts.lookup(s->layout).setGeometry(*g);
			recalculateLayout(s->layout);
		}
	}

    processUpdate();
}

void Canvas::processUpdate(Drawable::ID id) {
    if (id != Drawable::ID()) {
        ++ dataRevisions[id];
    }

    for (auto it = connectedPlotters.begin(); it != connectedPlotters.end(); it++) {
        (*it)->update();
    }
}

CoordinateSystem& Canvas::setUpEasySystem(AbstractPlotter* plotter)
{
    connectToPlotter(plotter);
    Layout& layout = addLayout<GridLayout>();
    CoordinateSystem& cosy = addCoordinateSystem();
    addCoordinateSystemToLayout(cosy.id(), layout.id());
    return cosy;
}
}
<commit_msg>Little change<commit_after>/*
 * Canvas.cpp
 *
 *  Created on: Oct 14, 2014
 *      Author: Carsten Uphoff (uphoff@mytum.de)
 */
#include "Canvas.hpp"
#include "GL/glld.h"
#include <cmath>

namespace hpl
{
Canvas::Canvas()
{
}

Canvas::~Canvas()
{
}

CoordinateSystem& Canvas::addCoordinateSystem()
{
    CoordinateSystem* cs = new CoordinateSystem(rawData, dataRevisions);
    cs->changed.template bind<Canvas, &Canvas::processUpdate>(this);

    csystems.add(cs);

    processUpdate();

    return *cs;
}

Canvas::Slot* Canvas::findSlot(Slot const& slot)
{
	for (auto it = racks.begin(); it != racks.end(); ++it) {
		auto f = std::find(it->second.slots.begin(), it->second.slots.end(), slot);
		if (f != it->second.slots.end()) {
			return &(*f);
		}
	}

	return nullptr;
}

void Canvas::addSlotToLayout(Slot const& slot, Layout::ID to)
{
	Slot* last = findSlot(slot);
	if (last != nullptr) {
		last->invalidate();
	}
	Rack& rack = racks[to];
	rack.slots.push_back(slot);
	rack.geometries.push_back(Geometry());

	recalculateLayout(to);
}

void Canvas::recalculateLayout(Layout::ID layout)
{
	Rack& rack = racks[layout];
	layouts.lookup(layout).get(rack.geometries);

	auto s = rack.slots.cbegin();
	auto g = rack.geometries.cbegin();
	for (; s != rack.slots.cend() && g != rack.geometries.cend(); ++s, ++g) {
		if (s->cs.valid()) {
			csystems.lookup(s->cs).setGeometry(*g);
		} else if (s->layout.valid()) {
			layouts.lookup(s->layout).setGeometry(*g);
			recalculateLayout(s->layout);
		}
	}

    processUpdate();
}

void Canvas::processUpdate(Drawable::ID id) {
    if (id != Drawable::ID()) {
        ++ dataRevisions[id];
    }

    for (auto it = connectedPlotters.begin(); it != connectedPlotters.end(); it++) {
        (*it)->update();
    }
}

CoordinateSystem& Canvas::setUpEasySystem(AbstractPlotter* plotter)
{
    connectToPlotter(plotter);
    GridLayout& layout = addLayout<GridLayout>();
    CoordinateSystem& cosy = addCoordinateSystem();
    addCoordinateSystemToLayout(cosy.id(), layout.id());
    return cosy;
}
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2005 Tommi Maekitalo
 *
 * 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.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tntdb/postgresql/impl/connection.h>
#include <tntdb/postgresql/impl/result.h>
#include <tntdb/postgresql/impl/statement.h>
#include <tntdb/postgresql/error.h>
#include <tntdb/result.h>
#include <tntdb/statement.h>
#include <cxxtools/log.h>
#include <new>
#include <sstream>

log_define("tntdb.postgresql.connection")

namespace tntdb
{
  namespace postgresql
  {
    Connection::Connection(const char* conninfo)
    {
      log_debug("PQconnectdb(\"" << conninfo << "\")");

      conn = PQconnectdb(conninfo);
      if (conn == 0)
        throw std::bad_alloc();

      if (PQstatus(conn) == CONNECTION_BAD )
        throw PgConnError("PQconnect", conn);
    }

    Connection::~Connection()
    {
      if (conn)
      {
        clearStatementCache();

        log_debug("PQfinish(" << conn << ")");
        PQfinish(conn);
      }
    }

    void Connection::beginTransaction()
    {
      execute("BEGIN");
    }

    void Connection::commitTransaction()
    {
      execute("COMMIT");
    }

    void Connection::rollbackTransaction()
    {
      execute("ROLLBACK");
    }

    Connection::size_type Connection::execute(const std::string& query)
    {
      log_debug("execute(\"" << query << "\")");

      log_debug("PQexec(" << conn << ", \"" << query << "\")");
      PGresult* result = PQexec(conn, query.c_str());
      if (isError(result))
      {
        log_error(PQresultErrorMessage(result));
        throw PgSqlError(query, "PQexec", result, true);
      }

      std::istringstream tuples(PQcmdTuples(result));
      Connection::size_type ret = 0;
      tuples >> ret;

      log_debug("PQclear(" << result << ')');
      PQclear(result);

      return ret;
    }

    tntdb::Result Connection::select(const std::string& query)
    {
      log_debug("select(\"" << query << "\")");

      log_debug("PQexec(" << conn << ", \"" << query << "\")");
      PGresult* result = PQexec(conn, query.c_str());
      if (isError(result))
      {
        log_error(PQresultErrorMessage(result));
        throw PgSqlError(query, "PQexec", result, true);
      }

      return tntdb::Result(new Result(tntdb::Connection(this), result));
    }

    Row Connection::selectRow(const std::string& query)
    {
      log_debug("selectRow(\"" << query << "\")");
      tntdb::Result result = select(query);
      if (result.empty())
        throw NotFound();

      return result.getRow(0);
    }

    Value Connection::selectValue(const std::string& query)
    {
      log_debug("selectValue(\"" << query << "\")");
      Row t = selectRow(query);
      if (t.empty())
        throw NotFound();

      return t.getValue(0);
    }

    tntdb::Statement Connection::prepare(const std::string& query)
    {
      log_debug("prepare(\"" << query << "\")");
      return tntdb::Statement(new Statement(this, query));
    }

    bool Connection::ping()
    {
      log_debug("ping()");
      try
      {
        select("select 1");
        return true;
      }
      catch (const PgError&)
      {
        return false;
      }
    }

    long Connection::lastInsertId(const std::string& name)
    {
      if (!currvalStmt)
        currvalStmt = prepare("select currval(:name)");

      long ret;
      currvalStmt.set("name", name)
        .selectValue()
        .get(ret);

      return ret;
    }

  }
}
<commit_msg>fix function name in connection exception of postgresql driver<commit_after>/*
 * Copyright (C) 2005 Tommi Maekitalo
 *
 * 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.
 *
 * As a special exception, you may use this file as part of a free
 * software library without restriction. Specifically, if other files
 * instantiate templates or use macros or inline functions from this
 * file, or you compile this file and link it with other files to
 * produce an executable, this file does not by itself cause the
 * resulting executable to be covered by the GNU General Public
 * License. This exception does not however invalidate any other
 * reasons why the executable file might be covered by the GNU Library
 * General Public License.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <tntdb/postgresql/impl/connection.h>
#include <tntdb/postgresql/impl/result.h>
#include <tntdb/postgresql/impl/statement.h>
#include <tntdb/postgresql/error.h>
#include <tntdb/result.h>
#include <tntdb/statement.h>
#include <cxxtools/log.h>
#include <new>
#include <sstream>

log_define("tntdb.postgresql.connection")

namespace tntdb
{
  namespace postgresql
  {
    Connection::Connection(const char* conninfo)
    {
      log_debug("PQconnectdb(\"" << conninfo << "\")");

      conn = PQconnectdb(conninfo);
      if (conn == 0)
        throw std::bad_alloc();

      if (PQstatus(conn) == CONNECTION_BAD )
        throw PgConnError("PQconnectdb", conn);
    }

    Connection::~Connection()
    {
      if (conn)
      {
        clearStatementCache();

        log_debug("PQfinish(" << conn << ")");
        PQfinish(conn);
      }
    }

    void Connection::beginTransaction()
    {
      execute("BEGIN");
    }

    void Connection::commitTransaction()
    {
      execute("COMMIT");
    }

    void Connection::rollbackTransaction()
    {
      execute("ROLLBACK");
    }

    Connection::size_type Connection::execute(const std::string& query)
    {
      log_debug("execute(\"" << query << "\")");

      log_debug("PQexec(" << conn << ", \"" << query << "\")");
      PGresult* result = PQexec(conn, query.c_str());
      if (isError(result))
      {
        log_error(PQresultErrorMessage(result));
        throw PgSqlError(query, "PQexec", result, true);
      }

      std::istringstream tuples(PQcmdTuples(result));
      Connection::size_type ret = 0;
      tuples >> ret;

      log_debug("PQclear(" << result << ')');
      PQclear(result);

      return ret;
    }

    tntdb::Result Connection::select(const std::string& query)
    {
      log_debug("select(\"" << query << "\")");

      log_debug("PQexec(" << conn << ", \"" << query << "\")");
      PGresult* result = PQexec(conn, query.c_str());
      if (isError(result))
      {
        log_error(PQresultErrorMessage(result));
        throw PgSqlError(query, "PQexec", result, true);
      }

      return tntdb::Result(new Result(tntdb::Connection(this), result));
    }

    Row Connection::selectRow(const std::string& query)
    {
      log_debug("selectRow(\"" << query << "\")");
      tntdb::Result result = select(query);
      if (result.empty())
        throw NotFound();

      return result.getRow(0);
    }

    Value Connection::selectValue(const std::string& query)
    {
      log_debug("selectValue(\"" << query << "\")");
      Row t = selectRow(query);
      if (t.empty())
        throw NotFound();

      return t.getValue(0);
    }

    tntdb::Statement Connection::prepare(const std::string& query)
    {
      log_debug("prepare(\"" << query << "\")");
      return tntdb::Statement(new Statement(this, query));
    }

    bool Connection::ping()
    {
      log_debug("ping()");
      try
      {
        select("select 1");
        return true;
      }
      catch (const PgError&)
      {
        return false;
      }
    }

    long Connection::lastInsertId(const std::string& name)
    {
      if (!currvalStmt)
        currvalStmt = prepare("select currval(:name)");

      long ret;
      currvalStmt.set("name", name)
        .selectValue()
        .get(ret);

      return ret;
    }

  }
}
<|endoftext|>