<commit_before>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2022 Vaclav Slavik
 *
 *  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.
 *
 */

#include "syntaxhighlighter.h"

#include "catalog.h"
#include "str_helpers.h"

#include <unicode/uchar.h>

#include <regex>

namespace
{

class BasicSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        if (s.empty())
            return;

        const int length = int(s.length());

        // Leading whitespace:
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.begin());
                if (wlen)
                    highlight(0, wlen, LeadingWhitespace);
                break;
            }
        }

        // Trailing whitespace:
        for (auto i = s.rbegin(); i != s.rend(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.rbegin());
                if (wlen)
                    highlight(length - wlen, length, LeadingWhitespace);
                break;
            }
        }

        int blank_block_pos = -1;

        for (auto i = s.begin(); i != s.end(); ++i)
        {
            // Some special whitespace characters should always be highlighted:
            if (*i == 0x00A0 /*non-breakable space*/)
            {
                int pos = int(i - s.begin());
                highlight(pos, pos + 1, LeadingWhitespace);
            }

            // Duplicate whitespace (2+ spaces etc.):
            else if (u_isblank(*i))
            {
                if (blank_block_pos == -1)
                    blank_block_pos = int(i - s.begin());
            }
            else if (blank_block_pos != -1)
            {
                int endpos = int(i - s.begin());
                if (endpos - blank_block_pos >= 2)
                    highlight(blank_block_pos, endpos, LeadingWhitespace);
                blank_block_pos = -1;
            }

            // Escape sequences:
            if (*i == '\\')
            {
                int pos = int(i - s.begin());
                if (++i == s.end())
                    break;
                // Note: this must match AnyTranslatableTextCtrl::EscapePlainText()
                switch (*i)
                {
                    case '0':
                    case 'a':
                    case 'b':
                    case 'f':
                    case 'n':
                    case 'r':
                    case 't':
                    case 'v':
                    case '\\':
                        highlight(pos, pos + 2, Escape);
                        break;
                    default:
                        break;
                }
            }
        }
    }
};

/// Highlighter that runs multiple sub-highlighters
class CompositeSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Add(std::shared_ptr<SyntaxHighlighter> h) { m_sub.push_back(h); }

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        for (auto h : m_sub)
            h->Highlight(s, highlight);
    }

private:
    std::vector<std::shared_ptr<SyntaxHighlighter>> m_sub;
};

/// Match regular expressions for highlighting
class RegexSyntaxHighlighter : public SyntaxHighlighter
{
public:
    /// Ctor. Notice that @a re is a reference and must outlive the highlighter!
    RegexSyntaxHighlighter(std::wregex& re, TextKind kind) : m_re(re), m_kind(kind) {}

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        try
        {
            std::wsregex_iterator next(s.begin(), s.end(), m_re);
            std::wsregex_iterator end;
            while (next != end)
            {
                auto match = *next++;
                if (match.empty())
                    continue;
                int pos = static_cast<int>(match.position());
                highlight(pos, pos + static_cast<int>(match.length()), m_kind);
            }
        }
        catch (std::regex_error& e)
        {
            switch (e.code())
            {
                case std::regex_constants::error_complexity:
                case std::regex_constants::error_stack:
                    // MSVC version of std::regex in particular can fail to match
                    // e.g. HTML regex with backreferences on insanely large strings;
                    // in that case, don't highlight instead of failing outright.
                    return;
                default:
                    throw;
            }
        }
    }

private:
    std::wregex& m_re;
    TextKind m_kind;
};

std::wregex RE_HTML_MARKUP(LR"((<\/?[a-zA-Z0-9:-]+(\s+[-:\w]+(=([-:\w+]|"[^"]*"|'[^']*'))?)*\s*\/?>)|(&[^ ;]+;))",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// php-format per http://php.net/manual/en/function.sprintf.php plus positionals
std::wregex RE_PHP_FORMAT(LR"(%(\d+\$)?[-+]{0,2}([ 0]|'.)?-?\d*(\..?\d+)?[%bcdeEfFgGosuxX])",
                          std::regex_constants::ECMAScript | std::regex_constants::optimize);

// c-format per http://en.cppreference.com/w/cpp/io/c/fprintf,
//              http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
std::wregex RE_C_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                        std::regex_constants::ECMAScript | std::regex_constants::optimize);

// python-format old style https://docs.python.org/2/library/stdtypes.html#string-formatting
//               new style https://docs.python.org/3/library/string.html#format-string-syntax
std::wregex RE_PYTHON_FORMAT(LR"((%(\(\w+\))?[-+ #0]?(\d+|\*)?(\.(\d+|\*))?[hlL]?[diouxXeEfFgGcrs%]))" // old style
                             "|"
                             LR"((\{([^{}])*\}))", // new style, being permissive
                             std::regex_constants::ECMAScript | std::regex_constants::optimize);

// ruby-format per https://ruby-doc.org/core-2.7.1/Kernel.html#method-i-sprintf
std::wregex RE_RUBY_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// variables expansion for various template languages
std::wregex RE_COMMON_PLACEHOLDERS(
                    //
                    //           |             |               |
                    LR"(%[\w.-]+%|%?\{[\w.-]+\}|\{\{[\w.-]+\}\}|[@%:][\w-]+)",
                    //      |    |      |      |        |      |      |
                    //      |           |               |             +--------- Drupal: non-terminated @var, %var or :var
                    //      |           |               |                        !MUST be last in regex because of lack of termination character!
                    //      |           |               |
                    //      |           |               +----------------------- {{var}}
                    //      |           |
                    //      |           +--------------------------------------- %{var} (Ruby) and {var}
                    //      |
                    //      +--------------------------------------------------- %var% (Twig)
                    //
                    std::regex_constants::ECMAScript | std::regex_constants::optimize);

} // anonymous namespace

SyntaxHighlighterPtr SyntaxHighlighter::ForItem(const CatalogItem& item)
{
    auto formatFlag = item.GetFormatFlag();
    bool needsHTML = std::regex_search(str::to_wstring(item.GetString()), RE_HTML_MARKUP);
    bool needsPlaceholders = std::regex_search(str::to_wstring(item.GetString()), RE_COMMON_PLACEHOLDERS);

    static auto basic = std::make_shared<BasicSyntaxHighlighter>();
    if (!needsHTML && !needsPlaceholders && formatFlag.empty())
        return basic;

    auto all = std::make_shared<CompositeSyntaxHighlighter>();

    // HTML goes first, has lowest priority than special-purpose stuff like format strings:
    if (needsHTML)
    {
        static auto html = std::make_shared<RegexSyntaxHighlighter>(RE_HTML_MARKUP, TextKind::Markup);
        all->Add(html);
    }

    if (needsPlaceholders)
    {
        // If no format specified, heuristically apply highlighting of common variable markers
        static auto placeholders = std::make_shared<RegexSyntaxHighlighter>(RE_COMMON_PLACEHOLDERS, TextKind::Format);
        all->Add(placeholders);
    }

    // TODO: more/all languages
    if (formatFlag == "php")
    {
        static auto php_format = std::make_shared<RegexSyntaxHighlighter>(RE_PHP_FORMAT, TextKind::Format);
        all->Add(php_format);
    }
    else if (formatFlag == "c")
    {
        static auto c_format = std::make_shared<RegexSyntaxHighlighter>(RE_C_FORMAT, TextKind::Format);
        all->Add(c_format);
    }
    else if (formatFlag == "python")
    {
        static auto python_format = std::make_shared<RegexSyntaxHighlighter>(RE_PYTHON_FORMAT, TextKind::Format);
        all->Add(python_format);
    }
    else if (formatFlag == "ruby")
    {
        static auto ruby_format = std::make_shared<RegexSyntaxHighlighter>(RE_RUBY_FORMAT, TextKind::Format);
        all->Add(ruby_format);
    }

    // basic highlighting has highest priority, so should come last in the order:
    all->Add(basic);

    return all;
}
<commit_msg>Be more strict in matching Drupal :var placeholders<commit_after>/*
 *  This file is part of Poedit (https://poedit.net)
 *
 *  Copyright (C) 2014-2022 Vaclav Slavik
 *
 *  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.
 *
 */

#include "syntaxhighlighter.h"

#include "catalog.h"
#include "str_helpers.h"

#include <unicode/uchar.h>

#include <regex>

namespace
{

class BasicSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        if (s.empty())
            return;

        const int length = int(s.length());

        // Leading whitespace:
        for (auto i = s.begin(); i != s.end(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.begin());
                if (wlen)
                    highlight(0, wlen, LeadingWhitespace);
                break;
            }
        }

        // Trailing whitespace:
        for (auto i = s.rbegin(); i != s.rend(); ++i)
        {
            if (!u_isblank(*i))
            {
                int wlen = int(i - s.rbegin());
                if (wlen)
                    highlight(length - wlen, length, LeadingWhitespace);
                break;
            }
        }

        int blank_block_pos = -1;

        for (auto i = s.begin(); i != s.end(); ++i)
        {
            // Some special whitespace characters should always be highlighted:
            if (*i == 0x00A0 /*non-breakable space*/)
            {
                int pos = int(i - s.begin());
                highlight(pos, pos + 1, LeadingWhitespace);
            }

            // Duplicate whitespace (2+ spaces etc.):
            else if (u_isblank(*i))
            {
                if (blank_block_pos == -1)
                    blank_block_pos = int(i - s.begin());
            }
            else if (blank_block_pos != -1)
            {
                int endpos = int(i - s.begin());
                if (endpos - blank_block_pos >= 2)
                    highlight(blank_block_pos, endpos, LeadingWhitespace);
                blank_block_pos = -1;
            }

            // Escape sequences:
            if (*i == '\\')
            {
                int pos = int(i - s.begin());
                if (++i == s.end())
                    break;
                // Note: this must match AnyTranslatableTextCtrl::EscapePlainText()
                switch (*i)
                {
                    case '0':
                    case 'a':
                    case 'b':
                    case 'f':
                    case 'n':
                    case 'r':
                    case 't':
                    case 'v':
                    case '\\':
                        highlight(pos, pos + 2, Escape);
                        break;
                    default:
                        break;
                }
            }
        }
    }
};

/// Highlighter that runs multiple sub-highlighters
class CompositeSyntaxHighlighter : public SyntaxHighlighter
{
public:
    void Add(std::shared_ptr<SyntaxHighlighter> h) { m_sub.push_back(h); }

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        for (auto h : m_sub)
            h->Highlight(s, highlight);
    }

private:
    std::vector<std::shared_ptr<SyntaxHighlighter>> m_sub;
};

/// Match regular expressions for highlighting
class RegexSyntaxHighlighter : public SyntaxHighlighter
{
public:
    /// Ctor. Notice that @a re is a reference and must outlive the highlighter!
    RegexSyntaxHighlighter(std::wregex& re, TextKind kind) : m_re(re), m_kind(kind) {}

    void Highlight(const std::wstring& s, const CallbackType& highlight) override
    {
        try
        {
            std::wsregex_iterator next(s.begin(), s.end(), m_re);
            std::wsregex_iterator end;
            while (next != end)
            {
                auto match = *next++;
                if (match.empty())
                    continue;
                int pos = static_cast<int>(match.position());
                highlight(pos, pos + static_cast<int>(match.length()), m_kind);
            }
        }
        catch (std::regex_error& e)
        {
            switch (e.code())
            {
                case std::regex_constants::error_complexity:
                case std::regex_constants::error_stack:
                    // MSVC version of std::regex in particular can fail to match
                    // e.g. HTML regex with backreferences on insanely large strings;
                    // in that case, don't highlight instead of failing outright.
                    return;
                default:
                    throw;
            }
        }
    }

private:
    std::wregex& m_re;
    TextKind m_kind;
};

std::wregex RE_HTML_MARKUP(LR"((<\/?[a-zA-Z0-9:-]+(\s+[-:\w]+(=([-:\w+]|"[^"]*"|'[^']*'))?)*\s*\/?>)|(&[^ ;]+;))",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// php-format per http://php.net/manual/en/function.sprintf.php plus positionals
std::wregex RE_PHP_FORMAT(LR"(%(\d+\$)?[-+]{0,2}([ 0]|'.)?-?\d*(\..?\d+)?[%bcdeEfFgGosuxX])",
                          std::regex_constants::ECMAScript | std::regex_constants::optimize);

// c-format per http://en.cppreference.com/w/cpp/io/c/fprintf,
//              http://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html
std::wregex RE_C_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                        std::regex_constants::ECMAScript | std::regex_constants::optimize);

// python-format old style https://docs.python.org/2/library/stdtypes.html#string-formatting
//               new style https://docs.python.org/3/library/string.html#format-string-syntax
std::wregex RE_PYTHON_FORMAT(LR"((%(\(\w+\))?[-+ #0]?(\d+|\*)?(\.(\d+|\*))?[hlL]?[diouxXeEfFgGcrs%]))" // old style
                             "|"
                             LR"((\{([^{}])*\}))", // new style, being permissive
                             std::regex_constants::ECMAScript | std::regex_constants::optimize);

// ruby-format per https://ruby-doc.org/core-2.7.1/Kernel.html#method-i-sprintf
std::wregex RE_RUBY_FORMAT(LR"(%(\d+\$)?[-+ #0]{0,5}(\d+|\*)?(\.(\d+|\*))?(hh|ll|[hljztL])?[%csdioxXufFeEaAgGnp])",
                           std::regex_constants::ECMAScript | std::regex_constants::optimize);

// variables expansion for various template languages
std::wregex RE_COMMON_PLACEHOLDERS(
                    //
                    //           |             |               |
                    LR"(%[\w.-]+%|%?\{[\w.-]+\}|\{\{[\w.-]+\}\}|[@%][\w-]+|":[\w-]+"|':[\w-]+')",
                    //      |    |      |      |        |      |      |
                    //      |           |               |             +--------- Drupal: non-terminated @var, %var or :var
                    //                                                           (the last one only in href, as ":var" or ':var')
                    //      |           |               |                        !MUST be last in regex because of lack of termination character!
                    //      |           |               |
                    //      |           |               +----------------------- {{var}}
                    //      |           |
                    //      |           +--------------------------------------- %{var} (Ruby) and {var}
                    //      |
                    //      +--------------------------------------------------- %var% (Twig)
                    //
                    std::regex_constants::ECMAScript | std::regex_constants::optimize);

} // anonymous namespace

SyntaxHighlighterPtr SyntaxHighlighter::ForItem(const CatalogItem& item)
{
    auto formatFlag = item.GetFormatFlag();
    bool needsHTML = std::regex_search(str::to_wstring(item.GetString()), RE_HTML_MARKUP);
    bool needsPlaceholders = std::regex_search(str::to_wstring(item.GetString()), RE_COMMON_PLACEHOLDERS);

    static auto basic = std::make_shared<BasicSyntaxHighlighter>();
    if (!needsHTML && !needsPlaceholders && formatFlag.empty())
        return basic;

    auto all = std::make_shared<CompositeSyntaxHighlighter>();

    // HTML goes first, has lowest priority than special-purpose stuff like format strings:
    if (needsHTML)
    {
        static auto html = std::make_shared<RegexSyntaxHighlighter>(RE_HTML_MARKUP, TextKind::Markup);
        all->Add(html);
    }

    if (needsPlaceholders)
    {
        // If no format specified, heuristically apply highlighting of common variable markers
        static auto placeholders = std::make_shared<RegexSyntaxHighlighter>(RE_COMMON_PLACEHOLDERS, TextKind::Format);
        all->Add(placeholders);
    }

    // TODO: more/all languages
    if (formatFlag == "php")
    {
        static auto php_format = std::make_shared<RegexSyntaxHighlighter>(RE_PHP_FORMAT, TextKind::Format);
        all->Add(php_format);
    }
    else if (formatFlag == "c")
    {
        static auto c_format = std::make_shared<RegexSyntaxHighlighter>(RE_C_FORMAT, TextKind::Format);
        all->Add(c_format);
    }
    else if (formatFlag == "python")
    {
        static auto python_format = std::make_shared<RegexSyntaxHighlighter>(RE_PYTHON_FORMAT, TextKind::Format);
        all->Add(python_format);
    }
    else if (formatFlag == "ruby")
    {
        static auto ruby_format = std::make_shared<RegexSyntaxHighlighter>(RE_RUBY_FORMAT, TextKind::Format);
        all->Add(ruby_format);
    }

    // basic highlighting has highest priority, so should come last in the order:
    all->Add(basic);

    return all;
}
<|endoftext|>


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

  Library: XNAT/Core

  Copyright (c) University College London,
    Centre for Medical Image Computing

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

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

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

=============================================================================*/

#include "ctkXnatObject.h"
#include "ctkXnatObjectPrivate.h"

#include "ctkXnatDataModel.h"
#include "ctkXnatDefaultSchemaTypes.h"
#include "ctkXnatException.h"
#include "ctkXnatResource.h"
#include "ctkXnatResourceFolder.h"
#include "ctkXnatSession.h"

#include <QDateTime>
#include <QDebug>
#include <QStringList>
#include <QVariant>

const QString ctkXnatObject::ID = "ID";
const QString ctkXnatObject::NAME = "name";
const QString ctkXnatObject::LABEL = "label";

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(const ctkXnatObject&)
{
  throw ctkRuntimeException("Copy constructor not implemented");
}

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(ctkXnatObject* parent, const QString& schemaType)
: d_ptr(new ctkXnatObjectPrivate())
{
  this->setParent(parent);
  this->setSchemaType(schemaType);
}

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(ctkXnatObjectPrivate& dd, ctkXnatObject* parent, const QString& schemaType)
: d_ptr(&dd)
{
  this->setParent(parent);
  this->setSchemaType(schemaType);
}

//----------------------------------------------------------------------------
ctkXnatObject::~ctkXnatObject()
{
  Q_D(ctkXnatObject);
  foreach (ctkXnatObject* child, d->children)
  {
    delete child;
  }
}

//----------------------------------------------------------------------------
QString ctkXnatObject::id() const
{
  return this->property(ID);
}

//----------------------------------------------------------------------------
void ctkXnatObject::setId(const QString& id)
{
  this->setProperty(ID, id);
}

//----------------------------------------------------------------------------
QString ctkXnatObject::name() const
{
  return this->property(NAME);
}

//----------------------------------------------------------------------------
void ctkXnatObject::setName(const QString& name)
{
  this->setProperty(NAME, name);
}

//----------------------------------------------------------------------------
QString ctkXnatObject::description() const
{
  Q_D(const ctkXnatObject);
  return d->description;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setDescription(const QString& description)
{
  Q_D(ctkXnatObject);
  d->description = description;
}

//----------------------------------------------------------------------------
QString ctkXnatObject::childDataType() const
{
  return "Resources";
}

QDateTime ctkXnatObject::lastModifiedTimeOnServer()
{
  Q_D(ctkXnatObject);
  QUuid queryId = this->session()->httpHead(this->resourceUri());
  QMap<QByteArray, QByteArray> header = this->session()->httpHeadSync(queryId);
  QVariant lastModifiedHeader = header.value("Last-Modified");
  QDateTime lastModifiedTime;

  if (lastModifiedHeader.isValid())
  {
    QStringList dateformates;
    // In case http date formate RFC 822 ( "Sun, 06 Nov 1994 08:49:37 GMT" )
    dateformates<<"ddd, dd MMM yyyy HH:mm:ss";
    // In case http date formate ANSI ( "Sun Nov  6 08:49:37 1994" )
    dateformates<<"ddd MMM  d HH:mm:ss yyyy";
    // In case http date formate RFC 850 ( "Sunday, 06-Nov-94 08:49:37 GMT" )
    dateformates<<"dddd, dd-MMM-yy HH:mm:ss";

    QString dateText = lastModifiedHeader.toString();
    // Remove "GMT" addition at the end of the http timestamp
    if (dateText.indexOf("GMT") != -1)
    {
      dateText = dateText.left(dateText.length()-4);
    }

    foreach (QString format, dateformates)
    {
      lastModifiedTime = QDateTime::fromString(dateText, format);
      if (lastModifiedTime.isValid())
        break;
    }
  }
  return lastModifiedTime;
}

void ctkXnatObject::setLastModifiedTime(const QDateTime &lastModifiedTime)
{
  Q_D(ctkXnatObject);
  if (d->lastModifiedTime < lastModifiedTime)
  {
    d->lastModifiedTime = lastModifiedTime;
  }
}

//----------------------------------------------------------------------------
QString ctkXnatObject::property(const QString& name) const
{
  Q_D(const ctkXnatObject);
  ctkXnatObjectPrivate::PropertyMapConstInterator iter = d->properties.find(name);
  if (iter != d->properties.end())
  {
    return iter.value();
  }
  return QString::null;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setProperty(const QString& name, const QVariant& value)
{
  Q_D(ctkXnatObject);
  if (d->properties[name] != value)
  {
    d->properties.insert(name, value.toString());
  }
}

//----------------------------------------------------------------------------
const QMap<QString, QString>& ctkXnatObject::properties() const
{
  Q_D(const ctkXnatObject);
  return d->properties;
}

//----------------------------------------------------------------------------
ctkXnatObject* ctkXnatObject::parent() const
{
  Q_D(const ctkXnatObject);
  return d->parent;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setParent(ctkXnatObject* parent)
{
  Q_D(ctkXnatObject);
  if (d->parent != parent)
  {
    if (d->parent)
    {
      d->parent->remove(this);
    }
    if (parent)
    {
      parent->add(this);
    }
  }
}

//----------------------------------------------------------------------------
QList<ctkXnatObject*> ctkXnatObject::children() const
{
  Q_D(const ctkXnatObject);
  return d->children;
}

//----------------------------------------------------------------------------
void ctkXnatObject::add(ctkXnatObject* child)
{
  Q_D(ctkXnatObject);
  if (child->parent() != this)
  {
    child->d_func()->parent = this;
  }

  bool childExists (false);
  foreach (ctkXnatObject* existingChild, d->children)
  {
    if ((existingChild->id().length() != 0 && existingChild->id() == child->id()) ||
        (existingChild->id().length() == 0 && existingChild->name() == child->name()))
    {
      d->children.replace(d->children.indexOf(existingChild), child);
      childExists = true;
    }
  }
  if (!childExists)
  {
    d->children.push_back(child);
  }
}

//----------------------------------------------------------------------------
void ctkXnatObject::remove(ctkXnatObject* child)
{
  Q_D(ctkXnatObject);
  if (!d->children.removeOne(child))
  {
    qWarning() << "ctkXnatObject::remove(): Child does not exist";
  }
}

//----------------------------------------------------------------------------
void ctkXnatObject::reset()
{
  Q_D(ctkXnatObject);
  // d->properties.clear();
  d->children.clear();
  d->fetched = false;
}

//----------------------------------------------------------------------------
bool ctkXnatObject::isFetched() const
{
  Q_D(const ctkXnatObject);
  return d->fetched;
}

//----------------------------------------------------------------------------
QString ctkXnatObject::schemaType() const
{
  return this->property("xsiType");
}

//----------------------------------------------------------------------------
void ctkXnatObject::setSchemaType(const QString& schemaType)
{
  this->setProperty("xsiType", schemaType);
}

//----------------------------------------------------------------------------
void ctkXnatObject::fetch()
{
  Q_D(ctkXnatObject);
  if (!d->fetched)
  {
    this->fetchImpl();
    d->fetched = true;
  }
}

//----------------------------------------------------------------------------
ctkXnatSession* ctkXnatObject::session() const
{
  const ctkXnatObject* xnatObject = this;
  while (ctkXnatObject* parent = xnatObject->parent())
  {
    xnatObject = parent;
  }
  const ctkXnatDataModel* dataModel = dynamic_cast<const ctkXnatDataModel*>(xnatObject);
  return dataModel ? dataModel->session() : NULL;
}

//----------------------------------------------------------------------------
void ctkXnatObject::download(const QString& filename)
{
  this->downloadImpl(filename);
}

//----------------------------------------------------------------------------
void ctkXnatObject::save(bool overwrite)
{
  Q_D(ctkXnatObject);
  this->saveImpl(overwrite);
}

//----------------------------------------------------------------------------
ctkXnatResource* ctkXnatObject::addResourceFolder(QString foldername, QString format,
                                   QString content, QString tags)
{
  if (foldername.size() == 0)
  {
    throw ctkXnatException("Error creating resource! Foldername must not be empty!");
  }

  ctkXnatResourceFolder* resFolder = 0;
  QList<ctkXnatObject*> children = this->children();
  for (unsigned int i = 0; i < children.size(); ++i)
  {
    resFolder = dynamic_cast<ctkXnatResourceFolder*>(children.at(i));
    if (resFolder)
    {
      break;
    }
  }

  if (!resFolder)
  {
    resFolder = new ctkXnatResourceFolder();
    this->add(resFolder);
  }

  ctkXnatResource* resource = new ctkXnatResource();
  resource->setName(foldername);
  if (format.size() != 0)
    resource->setFormat(format);
  if (content.size() != 0)
    resource->setContent(content);
  if (tags.size() != 0)
    resource->setTags(tags);

  resFolder->add(resource);
  resource->save();

  return resource;
}

//----------------------------------------------------------------------------
bool ctkXnatObject::exists() const
{
  return this->session()->exists(this);
}

//----------------------------------------------------------------------------
void ctkXnatObject::saveImpl(bool /*overwrite*/)
{
  Q_D(ctkXnatObject);
  ctkXnatSession::UrlParameters urlParams;
  urlParams["xsi:type"] = this->schemaType();

  // Just do this if there is already a valid last-modification-time,
  // otherwise the object is not yet on the server!
  QDateTime remoteModTime;
  if (d->lastModifiedTime.isValid())
  {
    // TODO Overwrite this for e.g. project and subject which already support modification time!
    remoteModTime = this->lastModifiedTimeOnServer();
    // If the object has been modified on the server, perform an update
    if (d->lastModifiedTime < remoteModTime)
    {
      qWarning()<<"Uploaded object maybe overwritten on server!";
      // TODO update from server, since modification time is not really supported
      // by xnat right now this is not of high priority
      // something like this->updateImpl + setLastModifiedTime()
    }
  }

  const QMap<QString, QString>& properties = this->properties();
  QMapIterator<QString, QString> itProperties(properties);
  while (itProperties.hasNext())
  {
    itProperties.next();
    if (itProperties.key() == "ID")
      continue;

    urlParams[itProperties.key()] = itProperties.value();
  }

  // Execute the update
  QUuid queryID = this->session()->httpPut(this->resourceUri(), urlParams);
  const QList<QVariantMap> results = this->session()->httpSync(queryID);

  // If this xnat object did not exist before on the server set the ID returned by Xnat
  if (results.size() == 1 && results[0].size() == 1)
  {
    QVariant id = results[0][ID];
    if (!id.isNull())
    {
      this->setId(id.toString());
    }
  }

  // Finally update the modification time on the server
  remoteModTime = this->lastModifiedTimeOnServer();
  d->lastModifiedTime = remoteModTime;
}

//----------------------------------------------------------------------------
void ctkXnatObject::fetchResources(const QString& path)
{
  ctkXnatResourceFolder* resFolder = new ctkXnatResourceFolder();
  this->add(resFolder);
}

//----------------------------------------------------------------------------
void ctkXnatObject::erase()
{
  this->session()->remove(this);
  this->parent()->remove(this);
}
<commit_msg>Replaced for loop with iterators.<commit_after>/*=============================================================================

  Library: XNAT/Core

  Copyright (c) University College London,
    Centre for Medical Image Computing

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

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

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

=============================================================================*/

#include "ctkXnatObject.h"
#include "ctkXnatObjectPrivate.h"

#include "ctkXnatDataModel.h"
#include "ctkXnatDefaultSchemaTypes.h"
#include "ctkXnatException.h"
#include "ctkXnatResource.h"
#include "ctkXnatResourceFolder.h"
#include "ctkXnatSession.h"

#include <QDateTime>
#include <QDebug>
#include <QStringList>
#include <QVariant>

const QString ctkXnatObject::ID = "ID";
const QString ctkXnatObject::NAME = "name";
const QString ctkXnatObject::LABEL = "label";

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(const ctkXnatObject&)
{
  throw ctkRuntimeException("Copy constructor not implemented");
}

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(ctkXnatObject* parent, const QString& schemaType)
: d_ptr(new ctkXnatObjectPrivate())
{
  this->setParent(parent);
  this->setSchemaType(schemaType);
}

//----------------------------------------------------------------------------
ctkXnatObject::ctkXnatObject(ctkXnatObjectPrivate& dd, ctkXnatObject* parent, const QString& schemaType)
: d_ptr(&dd)
{
  this->setParent(parent);
  this->setSchemaType(schemaType);
}

//----------------------------------------------------------------------------
ctkXnatObject::~ctkXnatObject()
{
  Q_D(ctkXnatObject);
  foreach (ctkXnatObject* child, d->children)
  {
    delete child;
  }
}

//----------------------------------------------------------------------------
QString ctkXnatObject::id() const
{
  return this->property(ID);
}

//----------------------------------------------------------------------------
void ctkXnatObject::setId(const QString& id)
{
  this->setProperty(ID, id);
}

//----------------------------------------------------------------------------
QString ctkXnatObject::name() const
{
  return this->property(NAME);
}

//----------------------------------------------------------------------------
void ctkXnatObject::setName(const QString& name)
{
  this->setProperty(NAME, name);
}

//----------------------------------------------------------------------------
QString ctkXnatObject::description() const
{
  Q_D(const ctkXnatObject);
  return d->description;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setDescription(const QString& description)
{
  Q_D(ctkXnatObject);
  d->description = description;
}

//----------------------------------------------------------------------------
QString ctkXnatObject::childDataType() const
{
  return "Resources";
}

QDateTime ctkXnatObject::lastModifiedTimeOnServer()
{
  Q_D(ctkXnatObject);
  QUuid queryId = this->session()->httpHead(this->resourceUri());
  QMap<QByteArray, QByteArray> header = this->session()->httpHeadSync(queryId);
  QVariant lastModifiedHeader = header.value("Last-Modified");
  QDateTime lastModifiedTime;

  if (lastModifiedHeader.isValid())
  {
    QStringList dateformates;
    // In case http date formate RFC 822 ( "Sun, 06 Nov 1994 08:49:37 GMT" )
    dateformates<<"ddd, dd MMM yyyy HH:mm:ss";
    // In case http date formate ANSI ( "Sun Nov  6 08:49:37 1994" )
    dateformates<<"ddd MMM  d HH:mm:ss yyyy";
    // In case http date formate RFC 850 ( "Sunday, 06-Nov-94 08:49:37 GMT" )
    dateformates<<"dddd, dd-MMM-yy HH:mm:ss";

    QString dateText = lastModifiedHeader.toString();
    // Remove "GMT" addition at the end of the http timestamp
    if (dateText.indexOf("GMT") != -1)
    {
      dateText = dateText.left(dateText.length()-4);
    }

    foreach (QString format, dateformates)
    {
      lastModifiedTime = QDateTime::fromString(dateText, format);
      if (lastModifiedTime.isValid())
        break;
    }
  }
  return lastModifiedTime;
}

void ctkXnatObject::setLastModifiedTime(const QDateTime &lastModifiedTime)
{
  Q_D(ctkXnatObject);
  if (d->lastModifiedTime < lastModifiedTime)
  {
    d->lastModifiedTime = lastModifiedTime;
  }
}

//----------------------------------------------------------------------------
QString ctkXnatObject::property(const QString& name) const
{
  Q_D(const ctkXnatObject);
  ctkXnatObjectPrivate::PropertyMapConstInterator iter = d->properties.find(name);
  if (iter != d->properties.end())
  {
    return iter.value();
  }
  return QString::null;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setProperty(const QString& name, const QVariant& value)
{
  Q_D(ctkXnatObject);
  if (d->properties[name] != value)
  {
    d->properties.insert(name, value.toString());
  }
}

//----------------------------------------------------------------------------
const QMap<QString, QString>& ctkXnatObject::properties() const
{
  Q_D(const ctkXnatObject);
  return d->properties;
}

//----------------------------------------------------------------------------
ctkXnatObject* ctkXnatObject::parent() const
{
  Q_D(const ctkXnatObject);
  return d->parent;
}

//----------------------------------------------------------------------------
void ctkXnatObject::setParent(ctkXnatObject* parent)
{
  Q_D(ctkXnatObject);
  if (d->parent != parent)
  {
    if (d->parent)
    {
      d->parent->remove(this);
    }
    if (parent)
    {
      parent->add(this);
    }
  }
}

//----------------------------------------------------------------------------
QList<ctkXnatObject*> ctkXnatObject::children() const
{
  Q_D(const ctkXnatObject);
  return d->children;
}

//----------------------------------------------------------------------------
void ctkXnatObject::add(ctkXnatObject* child)
{
  Q_D(ctkXnatObject);
  if (child->parent() != this)
  {
    child->d_func()->parent = this;
  }

  bool childExists (false);

  QList<ctkXnatObject*>::iterator iter;
  for (iter = d->children.begin(); iter != d->children.end(); ++iter)
  {
    if (((*iter)->id().length() != 0 && (*iter)->id() == child->id()) ||
        ((*iter)->id().length() == 0 && (*iter)->name() == child->name()))
    {
      *iter = child;
      childExists = true;
    }
  }

  if (!childExists)
  {
    d->children.push_back(child);
  }
}

//----------------------------------------------------------------------------
void ctkXnatObject::remove(ctkXnatObject* child)
{
  Q_D(ctkXnatObject);
  if (!d->children.removeOne(child))
  {
    qWarning() << "ctkXnatObject::remove(): Child does not exist";
  }
}

//----------------------------------------------------------------------------
void ctkXnatObject::reset()
{
  Q_D(ctkXnatObject);
  // d->properties.clear();
  d->children.clear();
  d->fetched = false;
}

//----------------------------------------------------------------------------
bool ctkXnatObject::isFetched() const
{
  Q_D(const ctkXnatObject);
  return d->fetched;
}

//----------------------------------------------------------------------------
QString ctkXnatObject::schemaType() const
{
  return this->property("xsiType");
}

//----------------------------------------------------------------------------
void ctkXnatObject::setSchemaType(const QString& schemaType)
{
  this->setProperty("xsiType", schemaType);
}

//----------------------------------------------------------------------------
void ctkXnatObject::fetch()
{
  Q_D(ctkXnatObject);
  if (!d->fetched)
  {
    this->fetchImpl();
    d->fetched = true;
  }
}

//----------------------------------------------------------------------------
ctkXnatSession* ctkXnatObject::session() const
{
  const ctkXnatObject* xnatObject = this;
  while (ctkXnatObject* parent = xnatObject->parent())
  {
    xnatObject = parent;
  }
  const ctkXnatDataModel* dataModel = dynamic_cast<const ctkXnatDataModel*>(xnatObject);
  return dataModel ? dataModel->session() : NULL;
}

//----------------------------------------------------------------------------
void ctkXnatObject::download(const QString& filename)
{
  this->downloadImpl(filename);
}

//----------------------------------------------------------------------------
void ctkXnatObject::save(bool overwrite)
{
  Q_D(ctkXnatObject);
  this->saveImpl(overwrite);
}

//----------------------------------------------------------------------------
ctkXnatResource* ctkXnatObject::addResourceFolder(QString foldername, QString format,
                                   QString content, QString tags)
{
  if (foldername.size() == 0)
  {
    throw ctkXnatException("Error creating resource! Foldername must not be empty!");
  }

  ctkXnatResourceFolder* resFolder = 0;
  QList<ctkXnatObject*> children = this->children();
  for (unsigned int i = 0; i < children.size(); ++i)
  {
    resFolder = dynamic_cast<ctkXnatResourceFolder*>(children.at(i));
    if (resFolder)
    {
      break;
    }
  }

  if (!resFolder)
  {
    resFolder = new ctkXnatResourceFolder();
    this->add(resFolder);
  }

  ctkXnatResource* resource = new ctkXnatResource();
  resource->setName(foldername);
  if (format.size() != 0)
    resource->setFormat(format);
  if (content.size() != 0)
    resource->setContent(content);
  if (tags.size() != 0)
    resource->setTags(tags);

  resFolder->add(resource);
  resource->save();

  return resource;
}

//----------------------------------------------------------------------------
bool ctkXnatObject::exists() const
{
  return this->session()->exists(this);
}

//----------------------------------------------------------------------------
void ctkXnatObject::saveImpl(bool /*overwrite*/)
{
  Q_D(ctkXnatObject);
  ctkXnatSession::UrlParameters urlParams;
  urlParams["xsi:type"] = this->schemaType();

  // Just do this if there is already a valid last-modification-time,
  // otherwise the object is not yet on the server!
  QDateTime remoteModTime;
  if (d->lastModifiedTime.isValid())
  {
    // TODO Overwrite this for e.g. project and subject which already support modification time!
    remoteModTime = this->lastModifiedTimeOnServer();
    // If the object has been modified on the server, perform an update
    if (d->lastModifiedTime < remoteModTime)
    {
      qWarning()<<"Uploaded object maybe overwritten on server!";
      // TODO update from server, since modification time is not really supported
      // by xnat right now this is not of high priority
      // something like this->updateImpl + setLastModifiedTime()
    }
  }

  const QMap<QString, QString>& properties = this->properties();
  QMapIterator<QString, QString> itProperties(properties);
  while (itProperties.hasNext())
  {
    itProperties.next();
    if (itProperties.key() == "ID")
      continue;

    urlParams[itProperties.key()] = itProperties.value();
  }

  // Execute the update
  QUuid queryID = this->session()->httpPut(this->resourceUri(), urlParams);
  const QList<QVariantMap> results = this->session()->httpSync(queryID);

  // If this xnat object did not exist before on the server set the ID returned by Xnat
  if (results.size() == 1 && results[0].size() == 1)
  {
    QVariant id = results[0][ID];
    if (!id.isNull())
    {
      this->setId(id.toString());
    }
  }

  // Finally update the modification time on the server
  remoteModTime = this->lastModifiedTimeOnServer();
  d->lastModifiedTime = remoteModTime;
}

//----------------------------------------------------------------------------
void ctkXnatObject::fetchResources(const QString& path)
{
  ctkXnatResourceFolder* resFolder = new ctkXnatResourceFolder();
  this->add(resFolder);
}

//----------------------------------------------------------------------------
void ctkXnatObject::erase()
{
  this->session()->remove(this);
  this->parent()->remove(this);
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/foreach.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_writer_template.h"
#include "json/json_spirit_utils.h"

#include "main.h"
#include "wallet.h"

using namespace std;
using namespace json_spirit;
using namespace boost::algorithm;

extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
                         bool fValidatePayToScriptHash, int nHashType);

CScript
ParseScript(string s)
{
    CScript result;

    static map<string, opcodetype> mapOpNames;

    if (mapOpNames.size() == 0)
    {
        for (int op = OP_NOP; op <= OP_NOP10; op++)
        {
            const char* name = GetOpName((opcodetype)op);
            if (strcmp(name, "OP_UNKNOWN") == 0)
                continue;
            string strName(name);
            mapOpNames[strName] = (opcodetype)op;
            // Convenience: OP_ADD and just ADD are both recognized:
            replace_first(strName, "OP_", "");
            mapOpNames[strName] = (opcodetype)op;
        }
    }

    vector<string> words;
    split(words, s, is_any_of(" \t\n"), token_compress_on);

    BOOST_FOREACH(string w, words)
    {
        if (all(w, is_digit()) ||
            (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit())))
        {
            // Number
            int64 n = atoi64(w);
            result << n;
        }
        else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end())))
        {
            // Hex data:
            result << ParseHex(string(w.begin()+2, w.end()));
        }
        else if (s.size() >= 2 && starts_with(w, "'") && ends_with(w, "'"))
        {
            // Single-quoted string, pushed as data:
            std::vector<unsigned char> value(s.begin()+1, s.end()-1);
            result << value;
        }
        else if (mapOpNames.count(w))
        {
            // opcode, e.g. OP_ADD or OP_1:
            result << mapOpNames[w];
        }
        else
        {
            BOOST_ERROR("Parse error: " << s);
            return CScript();
        }
    }

    return result;
}

Array
read_json(const std::string& filename)
{
    namespace fs = boost::filesystem;
    fs::path testFile = fs::current_path() / "test" / "data" / filename;

#ifdef TEST_DATA_DIR
    if (!fs::exists(testFile))
    {
        testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename;
    }
#endif

    ifstream ifs(testFile.string().c_str(), ifstream::in);
    Value v;
    if (!read_stream(ifs, v))
    {
        BOOST_ERROR("Cound not find/open " << filename);
        return Array();
    }
    if (v.type() != array_type)
    {
        BOOST_ERROR(filename << " does not contain a json array");
        return Array();
    }

    return v.get_array();
}

BOOST_AUTO_TEST_SUITE(script_tests)

BOOST_AUTO_TEST_CASE(script_valid)
{
    // Read tests from test/data/script_valid.json
    // Format is an array of arrays
    // Inner arrays are [ "scriptSig", "scriptPubKey" ]
    // ... where scriptSig and scriptPubKey are stringified
    // scripts.
    Array tests = read_json("script_valid.json");

    BOOST_FOREACH(Value& tv, tests)
    {
        Array test = tv.get_array();
        string strTest = write_string(tv, false);
        if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        string scriptSigString = test[0].get_str();
        CScript scriptSig = ParseScript(scriptSigString);
        string scriptPubKeyString = test[1].get_str();
        CScript scriptPubKey = ParseScript(scriptPubKeyString);

        CTransaction tx;
        BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest);
    }
}

BOOST_AUTO_TEST_CASE(script_invalid)
{
    // Scripts that should evaluate as invalid
    Array tests = read_json("script_invalid.json");

    BOOST_FOREACH(Value& tv, tests)
    {
        Array test = tv.get_array();
        string strTest = write_string(tv, false);
        if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        string scriptSigString = test[0].get_str();
        CScript scriptSig = ParseScript(scriptSigString);
        string scriptPubKeyString = test[1].get_str();
        CScript scriptPubKey = ParseScript(scriptPubKeyString);

        CTransaction tx;
        BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest);
    }
}

BOOST_AUTO_TEST_CASE(script_PushData)
{
    // Check that PUSHDATA1, PUSHDATA2, and PUSHDATA4 create the same value on
    // the stack as the 1-75 opcodes do.
    static const unsigned char direct[] = { 1, 0x5a };
    static const unsigned char pushdata1[] = { OP_PUSHDATA1, 1, 0x5a };
    static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a };
    static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };

    vector<vector<unsigned char> > directStack;
    BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0));

    vector<vector<unsigned char> > pushdata1Stack;
    BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata1Stack == directStack);

    vector<vector<unsigned char> > pushdata2Stack;
    BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata2Stack == directStack);

    vector<vector<unsigned char> > pushdata4Stack;
    BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata4Stack == directStack);
}

CScript
sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction)
{
    uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL);

    CScript result;
    //
    // NOTE: CHECKMULTISIG has an unfortunate bug; it requires
    // one extra item on the stack, before the signatures.
    // Putting OP_0 on the stack is the workaround;
    // fixing the bug would mean splitting the blockchain (old
    // clients would not accept new CHECKMULTISIG transactions,
    // and vice-versa)
    //
    result << OP_0;
    BOOST_FOREACH(CKey key, keys)
    {
        vector<unsigned char> vchSig;
        BOOST_CHECK(key.Sign(hash, vchSig));
        vchSig.push_back((unsigned char)SIGHASH_ALL);
        result << vchSig;
    }
    return result;
}
CScript
sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
{
    std::vector<CKey> keys;
    keys.push_back(key);
    return sign_multisig(scriptPubKey, keys, transaction);
}

BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
{
    CKey key1, key2, key3;
    key1.MakeNewKey(true);
    key2.MakeNewKey(false);
    key3.MakeNewKey(true);

    CScript scriptPubKey12;
    scriptPubKey12 << OP_1 << key1.GetPubKey() << key2.GetPubKey() << OP_2 << OP_CHECKMULTISIG;

    CTransaction txFrom12;
    txFrom12.vout.resize(1);
    txFrom12.vout[0].scriptPubKey = scriptPubKey12;

    CTransaction txTo12;
    txTo12.vin.resize(1);
    txTo12.vout.resize(1);
    txTo12.vin[0].prevout.n = 0;
    txTo12.vin[0].prevout.hash = txFrom12.GetHash();
    txTo12.vout[0].nValue = 1;

    CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
    BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0));
    txTo12.vout[0].nValue = 2;
    BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0));

    CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
    BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, 0));

    CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
    BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, 0));
}

BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
{
    CKey key1, key2, key3, key4;
    key1.MakeNewKey(true);
    key2.MakeNewKey(false);
    key3.MakeNewKey(true);
    key4.MakeNewKey(false);

    CScript scriptPubKey23;
    scriptPubKey23 << OP_2 << key1.GetPubKey() << key2.GetPubKey() << key3.GetPubKey() << OP_3 << OP_CHECKMULTISIG;

    CTransaction txFrom23;
    txFrom23.vout.resize(1);
    txFrom23.vout[0].scriptPubKey = scriptPubKey23;

    CTransaction txTo23;
    txTo23.vin.resize(1);
    txTo23.vout.resize(1);
    txTo23.vin[0].prevout.n = 0;
    txTo23.vin[0].prevout.hash = txFrom23.GetHash();
    txTo23.vout[0].nValue = 1;

    std::vector<CKey> keys;
    keys.push_back(key1); keys.push_back(key2);
    CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key1); keys.push_back(key3);
    CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key3);
    CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
    CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
    CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
    CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
    CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
    CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear(); // Must have signatures
    CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, 0));
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Added some park extraction tests<commit_after>#include <iostream>
#include <fstream>
#include <vector>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/foreach.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/test/unit_test.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_writer_template.h"
#include "json/json_spirit_utils.h"

#include "main.h"
#include "wallet.h"

using namespace std;
using namespace json_spirit;
using namespace boost::algorithm;

extern uint256 SignatureHash(CScript scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType);
extern bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn,
                         bool fValidatePayToScriptHash, int nHashType);

CScript
ParseScript(string s)
{
    CScript result;

    static map<string, opcodetype> mapOpNames;

    if (mapOpNames.size() == 0)
    {
        for (int op = OP_NOP; op <= OP_NOP10; op++)
        {
            const char* name = GetOpName((opcodetype)op);
            if (strcmp(name, "OP_UNKNOWN") == 0)
                continue;
            string strName(name);
            mapOpNames[strName] = (opcodetype)op;
            // Convenience: OP_ADD and just ADD are both recognized:
            replace_first(strName, "OP_", "");
            mapOpNames[strName] = (opcodetype)op;
        }
    }

    vector<string> words;
    split(words, s, is_any_of(" \t\n"), token_compress_on);

    BOOST_FOREACH(string w, words)
    {
        if (all(w, is_digit()) ||
            (starts_with(w, "-") && all(string(w.begin()+1, w.end()), is_digit())))
        {
            // Number
            int64 n = atoi64(w);
            result << n;
        }
        else if (starts_with(w, "0x") && IsHex(string(w.begin()+2, w.end())))
        {
            // Hex data:
            result << ParseHex(string(w.begin()+2, w.end()));
        }
        else if (s.size() >= 2 && starts_with(w, "'") && ends_with(w, "'"))
        {
            // Single-quoted string, pushed as data:
            std::vector<unsigned char> value(s.begin()+1, s.end()-1);
            result << value;
        }
        else if (mapOpNames.count(w))
        {
            // opcode, e.g. OP_ADD or OP_1:
            result << mapOpNames[w];
        }
        else
        {
            BOOST_ERROR("Parse error: " << s);
            return CScript();
        }
    }

    return result;
}

Array
read_json(const std::string& filename)
{
    namespace fs = boost::filesystem;
    fs::path testFile = fs::current_path() / "test" / "data" / filename;

#ifdef TEST_DATA_DIR
    if (!fs::exists(testFile))
    {
        testFile = fs::path(BOOST_PP_STRINGIZE(TEST_DATA_DIR)) / filename;
    }
#endif

    ifstream ifs(testFile.string().c_str(), ifstream::in);
    Value v;
    if (!read_stream(ifs, v))
    {
        BOOST_ERROR("Cound not find/open " << filename);
        return Array();
    }
    if (v.type() != array_type)
    {
        BOOST_ERROR(filename << " does not contain a json array");
        return Array();
    }

    return v.get_array();
}

BOOST_AUTO_TEST_SUITE(script_tests)

BOOST_AUTO_TEST_CASE(script_valid)
{
    // Read tests from test/data/script_valid.json
    // Format is an array of arrays
    // Inner arrays are [ "scriptSig", "scriptPubKey" ]
    // ... where scriptSig and scriptPubKey are stringified
    // scripts.
    Array tests = read_json("script_valid.json");

    BOOST_FOREACH(Value& tv, tests)
    {
        Array test = tv.get_array();
        string strTest = write_string(tv, false);
        if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        string scriptSigString = test[0].get_str();
        CScript scriptSig = ParseScript(scriptSigString);
        string scriptPubKeyString = test[1].get_str();
        CScript scriptPubKey = ParseScript(scriptPubKeyString);

        CTransaction tx;
        BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest);
    }
}

BOOST_AUTO_TEST_CASE(script_invalid)
{
    // Scripts that should evaluate as invalid
    Array tests = read_json("script_invalid.json");

    BOOST_FOREACH(Value& tv, tests)
    {
        Array test = tv.get_array();
        string strTest = write_string(tv, false);
        if (test.size() < 2) // Allow size > 2; extra stuff ignored (useful for comments)
        {
            BOOST_ERROR("Bad test: " << strTest);
            continue;
        }
        string scriptSigString = test[0].get_str();
        CScript scriptSig = ParseScript(scriptSigString);
        string scriptPubKeyString = test[1].get_str();
        CScript scriptPubKey = ParseScript(scriptPubKeyString);

        CTransaction tx;
        BOOST_CHECK_MESSAGE(!VerifyScript(scriptSig, scriptPubKey, tx, 0, true, SIGHASH_NONE), strTest);
    }
}

BOOST_AUTO_TEST_CASE(script_PushData)
{
    // Check that PUSHDATA1, PUSHDATA2, and PUSHDATA4 create the same value on
    // the stack as the 1-75 opcodes do.
    static const unsigned char direct[] = { 1, 0x5a };
    static const unsigned char pushdata1[] = { OP_PUSHDATA1, 1, 0x5a };
    static const unsigned char pushdata2[] = { OP_PUSHDATA2, 1, 0, 0x5a };
    static const unsigned char pushdata4[] = { OP_PUSHDATA4, 1, 0, 0, 0, 0x5a };

    vector<vector<unsigned char> > directStack;
    BOOST_CHECK(EvalScript(directStack, CScript(&direct[0], &direct[sizeof(direct)]), CTransaction(), 0, 0));

    vector<vector<unsigned char> > pushdata1Stack;
    BOOST_CHECK(EvalScript(pushdata1Stack, CScript(&pushdata1[0], &pushdata1[sizeof(pushdata1)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata1Stack == directStack);

    vector<vector<unsigned char> > pushdata2Stack;
    BOOST_CHECK(EvalScript(pushdata2Stack, CScript(&pushdata2[0], &pushdata2[sizeof(pushdata2)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata2Stack == directStack);

    vector<vector<unsigned char> > pushdata4Stack;
    BOOST_CHECK(EvalScript(pushdata4Stack, CScript(&pushdata4[0], &pushdata4[sizeof(pushdata4)]), CTransaction(), 0, 0));
    BOOST_CHECK(pushdata4Stack == directStack);
}

CScript
sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction)
{
    uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL);

    CScript result;
    //
    // NOTE: CHECKMULTISIG has an unfortunate bug; it requires
    // one extra item on the stack, before the signatures.
    // Putting OP_0 on the stack is the workaround;
    // fixing the bug would mean splitting the blockchain (old
    // clients would not accept new CHECKMULTISIG transactions,
    // and vice-versa)
    //
    result << OP_0;
    BOOST_FOREACH(CKey key, keys)
    {
        vector<unsigned char> vchSig;
        BOOST_CHECK(key.Sign(hash, vchSig));
        vchSig.push_back((unsigned char)SIGHASH_ALL);
        result << vchSig;
    }
    return result;
}
CScript
sign_multisig(CScript scriptPubKey, CKey key, CTransaction transaction)
{
    std::vector<CKey> keys;
    keys.push_back(key);
    return sign_multisig(scriptPubKey, keys, transaction);
}

BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12)
{
    CKey key1, key2, key3;
    key1.MakeNewKey(true);
    key2.MakeNewKey(false);
    key3.MakeNewKey(true);

    CScript scriptPubKey12;
    scriptPubKey12 << OP_1 << key1.GetPubKey() << key2.GetPubKey() << OP_2 << OP_CHECKMULTISIG;

    CTransaction txFrom12;
    txFrom12.vout.resize(1);
    txFrom12.vout[0].scriptPubKey = scriptPubKey12;

    CTransaction txTo12;
    txTo12.vin.resize(1);
    txTo12.vout.resize(1);
    txTo12.vin[0].prevout.n = 0;
    txTo12.vin[0].prevout.hash = txFrom12.GetHash();
    txTo12.vout[0].nValue = 1;

    CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12);
    BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0));
    txTo12.vout[0].nValue = 2;
    BOOST_CHECK(!VerifyScript(goodsig1, scriptPubKey12, txTo12, 0, true, 0));

    CScript goodsig2 = sign_multisig(scriptPubKey12, key2, txTo12);
    BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey12, txTo12, 0, true, 0));

    CScript badsig1 = sign_multisig(scriptPubKey12, key3, txTo12);
    BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey12, txTo12, 0, true, 0));
}

BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
{
    CKey key1, key2, key3, key4;
    key1.MakeNewKey(true);
    key2.MakeNewKey(false);
    key3.MakeNewKey(true);
    key4.MakeNewKey(false);

    CScript scriptPubKey23;
    scriptPubKey23 << OP_2 << key1.GetPubKey() << key2.GetPubKey() << key3.GetPubKey() << OP_3 << OP_CHECKMULTISIG;

    CTransaction txFrom23;
    txFrom23.vout.resize(1);
    txFrom23.vout[0].scriptPubKey = scriptPubKey23;

    CTransaction txTo23;
    txTo23.vin.resize(1);
    txTo23.vout.resize(1);
    txTo23.vin[0].prevout.n = 0;
    txTo23.vin[0].prevout.hash = txFrom23.GetHash();
    txTo23.vout[0].nValue = 1;

    std::vector<CKey> keys;
    keys.push_back(key1); keys.push_back(key2);
    CScript goodsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig1, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key1); keys.push_back(key3);
    CScript goodsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig2, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key3);
    CScript goodsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(VerifyScript(goodsig3, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key2); // Can't re-use sig
    CScript badsig1 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig1, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key2); keys.push_back(key1); // sigs must be in correct order
    CScript badsig2 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig2, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key3); keys.push_back(key2); // sigs must be in correct order
    CScript badsig3 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig3, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key4); keys.push_back(key2); // sigs must match pubkeys
    CScript badsig4 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig4, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear();
    keys.push_back(key1); keys.push_back(key4); // sigs must match pubkeys
    CScript badsig5 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig5, scriptPubKey23, txTo23, 0, true, 0));

    keys.clear(); // Must have signatures
    CScript badsig6 = sign_multisig(scriptPubKey23, keys, txTo23);
    BOOST_CHECK(!VerifyScript(badsig6, scriptPubKey23, txTo23, 0, true, 0));
}

void CheckPark(const CScript& script, int64 nExpectedDuration, const uint160& expectedUnparkAddress)
{
    int64 nDuration;
    CTxDestination unparkDestination;
    BOOST_CHECK(ExtractPark(script, nDuration, unparkDestination));
    BOOST_CHECK_EQUAL(nExpectedDuration, nDuration);
    BOOST_CHECK(expectedUnparkAddress == boost::get<CKeyID>(unparkDestination));
}

void CheckNoPark(const CScript& script)
{
    int64 nDuration;
    CTxDestination unparkDestination;
    BOOST_CHECK(!ExtractPark(script, nDuration, unparkDestination));
}

BOOST_AUTO_TEST_CASE(park_solver)
{
    CheckPark(CScript() << OP_RETURN << OP_3 << OP_1  << uint160( 123),   1, uint160( 123));
    CheckPark(CScript() << OP_RETURN << OP_3 << OP_16 << uint160(5555),  16, uint160(5555));
    CheckPark(CScript() << OP_RETURN << OP_3 << 123   << uint160(5555), 123, uint160(5555));

    CheckPark(  CScript() << OP_RETURN << OP_3 << (CBigNum(1) << 10) << uint160(5555), 1 << 10, uint160(5555));
    CheckNoPark(CScript() << OP_RETURN << OP_3 << (CBigNum(1) << 31) << uint160(5555));

    CScript script;
    script.SetPark(-1, uint160(12));
    CheckNoPark(script);
    script.SetPark(0, uint160(12));
    CheckNoPark(script);
    script.SetPark(1, uint160(12));
    CheckPark(script, 1, uint160(12));
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2007-2010 Digital Bazaar, Inc. All rights reserved.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "monarch/io/FileInputStream.h"

#include "monarch/io/FileFunctions.h"
#include "monarch/rt/DynamicObject.h"

#include <cstring>
#include <cstdlib>

using namespace std;
using namespace monarch::io;
using namespace monarch::rt;

// setup some aliases depending on fseeko/ftello support

#ifdef HAVE_FSEEKO
#define mo_fseek fseeko
#define mo_ftell ftello
#define mo_fseek_off_t off_t
#else
#define mo_fseek fseek
#define mo_ftell ftell
#define mo_fseek_off_t long
#endif

FileInputStream::FileInputStream(File& file) :
   mFile(file),
   mHandle(NULL)
{
}

FileInputStream::FileInputStream(StdInput in) :
   mFile((FileImpl*)NULL),
   mHandle(stdin)
{
}

FileInputStream::~FileInputStream()
{
   // close the handle if it is open
   FileInputStream::close();
}

bool FileInputStream::ensureOpen()
{
   bool rval = true;

   // try to open the file (mFile is null when using stdin)
   if(mHandle == NULL && !mFile.isNull())
   {
      if(!mFile->exists())
      {
         ExceptionRef e = new Exception(
            "Could not open file.",
            "monarch.io.File.NotFound");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         Exception::set(e);
         rval = false;
      }
      else if(!mFile->isReadable())
      {
         ExceptionRef e = new Exception(
            "Could not open file.",
            "monarch.io.File.AccessDenied");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         Exception::set(e);
         rval = false;
      }
      else
      {
         mHandle = fopen(mFile->getAbsolutePath(), "rb");
         if(mHandle == NULL)
         {
            ExceptionRef e = new Exception(
               "Could not open file stream.",
               "monarch.io.File.OpenFailed");
            e->getDetails()["path"] = mFile->getAbsolutePath();
            e->getDetails()["error"] = strerror(errno);
            Exception::set(e);
            rval = false;
         }
      }
   }

   return rval;
}

int FileInputStream::read(char* b, int length)
{
   int rval = -1;

   if(ensureOpen())
   {
      rval = 0;

      // do read
      int count = fread(b, 1, length, mHandle);
      if(count != length)
      {
         // check for an error
         if(ferror(mHandle) != 0)
         {
            ExceptionRef e = new Exception(
               "Could not read file.",
               "monarch.io.File.ReadError");
            e->getDetails()["path"] = mFile->getAbsolutePath();
            e->getDetails()["error"] = strerror(errno);
            Exception::set(e);
            rval = -1;
         }
      }

      if(rval != -1)
      {
         // return number of bytes read
         rval = count;
      }
   }

   return rval;
}

int64_t FileInputStream::skip(int64_t count)
{
   int64_t rval = -1;

   if(ensureOpen())
   {
      bool success;

      // store current position and the end position
      mo_fseek_off_t curr = mo_ftell(mHandle);
      success = (mo_fseek(mHandle, 0, SEEK_END) == 0);
      mo_fseek_off_t end = mo_ftell(mHandle);
      success = success && (mo_fseek(mHandle, curr, SEEK_SET) == 0);

      rval = count;
      if(rval > 0 && curr < end)
      {
         // do not skip past EOF
         if(rval > (end - curr))
         {
            rval = end - curr;
         }

         // skip from current offset
         success = success && (mo_fseek(mHandle, rval, SEEK_CUR) == 0);
      }

      if(!success)
      {
         ExceptionRef e = new Exception(
            "Could not read file.",
            "monarch.io.File.ReadError");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         e->getDetails()["error"] = strerror(errno);
         Exception::set(e);
         rval = -1;
      }
   }

   return rval;
}

int FileInputStream::readLine(string& line, char delimiter)
{
   int rval = -1;

   if(ensureOpen())
   {
      rval = 0;

      // feof returns non-zero when EOF
      if(feof(mHandle) == 0)
      {
         // get line
         char* data = NULL;
         size_t size = 0;
         ssize_t length = getdelim(&data, &size, delimiter, mHandle);
         if(length == -1)
         {
            ExceptionRef e = new Exception(
               "Could not read file.",
               "monarch.io.File.ReadError");
            e->getDetails()["path"] = mFile->getAbsolutePath();
            e->getDetails()["error"] = strerror(errno);
            Exception::set(e);
            rval = -1;
         }
         else
         {
            // line was read
            rval = 1;
            if(data[length - 1] == delimiter)
            {
               // do not include delimiter
               line.assign(data, length - 1);
            }
            else
            {
               line.assign(data, length);
            }
            free(data);
         }
      }
   }

   return rval;
}

void FileInputStream::close()
{
   // (mFile is null when using stdin)
   if(mHandle != NULL && !mFile.isNull())
   {
      // close the stream
      fclose(mHandle);
      mHandle = NULL;
   }
}
<commit_msg>Added extra eof check.<commit_after>/*
 * Copyright (c) 2007-2010 Digital Bazaar, Inc. All rights reserved.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "monarch/io/FileInputStream.h"

#include "monarch/io/FileFunctions.h"
#include "monarch/rt/DynamicObject.h"

#include <cstring>
#include <cstdlib>

using namespace std;
using namespace monarch::io;
using namespace monarch::rt;

// setup some aliases depending on fseeko/ftello support

#ifdef HAVE_FSEEKO
#define mo_fseek fseeko
#define mo_ftell ftello
#define mo_fseek_off_t off_t
#else
#define mo_fseek fseek
#define mo_ftell ftell
#define mo_fseek_off_t long
#endif

FileInputStream::FileInputStream(File& file) :
   mFile(file),
   mHandle(NULL)
{
}

FileInputStream::FileInputStream(StdInput in) :
   mFile((FileImpl*)NULL),
   mHandle(stdin)
{
}

FileInputStream::~FileInputStream()
{
   // close the handle if it is open
   FileInputStream::close();
}

bool FileInputStream::ensureOpen()
{
   bool rval = true;

   // try to open the file (mFile is null when using stdin)
   if(mHandle == NULL && !mFile.isNull())
   {
      if(!mFile->exists())
      {
         ExceptionRef e = new Exception(
            "Could not open file.",
            "monarch.io.File.NotFound");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         Exception::set(e);
         rval = false;
      }
      else if(!mFile->isReadable())
      {
         ExceptionRef e = new Exception(
            "Could not open file.",
            "monarch.io.File.AccessDenied");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         Exception::set(e);
         rval = false;
      }
      else
      {
         mHandle = fopen(mFile->getAbsolutePath(), "rb");
         if(mHandle == NULL)
         {
            ExceptionRef e = new Exception(
               "Could not open file stream.",
               "monarch.io.File.OpenFailed");
            e->getDetails()["path"] = mFile->getAbsolutePath();
            e->getDetails()["error"] = strerror(errno);
            Exception::set(e);
            rval = false;
         }
      }
   }

   return rval;
}

int FileInputStream::read(char* b, int length)
{
   int rval = -1;

   if(ensureOpen())
   {
      rval = 0;

      // do read
      int count = fread(b, 1, length, mHandle);
      if(count != length)
      {
         // check for an error
         if(ferror(mHandle) != 0)
         {
            ExceptionRef e = new Exception(
               "Could not read file.",
               "monarch.io.File.ReadError");
            e->getDetails()["path"] = mFile->getAbsolutePath();
            e->getDetails()["error"] = strerror(errno);
            Exception::set(e);
            rval = -1;
         }
      }

      if(rval != -1)
      {
         // return number of bytes read
         rval = count;
      }
   }

   return rval;
}

int64_t FileInputStream::skip(int64_t count)
{
   int64_t rval = -1;

   if(ensureOpen())
   {
      bool success;

      // store current position and the end position
      mo_fseek_off_t curr = mo_ftell(mHandle);
      success = (mo_fseek(mHandle, 0, SEEK_END) == 0);
      mo_fseek_off_t end = mo_ftell(mHandle);
      success = success && (mo_fseek(mHandle, curr, SEEK_SET) == 0);

      rval = count;
      if(rval > 0 && curr < end)
      {
         // do not skip past EOF
         if(rval > (end - curr))
         {
            rval = end - curr;
         }

         // skip from current offset
         success = success && (mo_fseek(mHandle, rval, SEEK_CUR) == 0);
      }

      if(!success)
      {
         ExceptionRef e = new Exception(
            "Could not read file.",
            "monarch.io.File.ReadError");
         e->getDetails()["path"] = mFile->getAbsolutePath();
         e->getDetails()["error"] = strerror(errno);
         Exception::set(e);
         rval = -1;
      }
   }

   return rval;
}

int FileInputStream::readLine(string& line, char delimiter)
{
   int rval = -1;

   if(ensureOpen())
   {
      rval = 0;

      // feof returns non-zero when EOF
      if(feof(mHandle) == 0)
      {
         // get line
         char* data = NULL;
         size_t size = 0;
         ssize_t length = getdelim(&data, &size, delimiter, mHandle);
         if(length == -1)
         {
            if(feof(mHandle) != 0)
            {
               // end of file
            }
            else
            {
               ExceptionRef e = new Exception(
                  "Could not read file.",
                  "monarch.io.File.ReadError");
               e->getDetails()["path"] = mFile->getAbsolutePath();
               e->getDetails()["error"] = strerror(errno);
               Exception::set(e);
               rval = -1;
            }
         }
         else
         {
            // line was read
            rval = 1;
            if(data[length - 1] == delimiter)
            {
               // do not include delimiter
               line.assign(data, length - 1);
            }
            else
            {
               line.assign(data, length);
            }
            free(data);
         }
      }
   }

   return rval;
}

void FileInputStream::close()
{
   // (mFile is null when using stdin)
   if(mHandle != NULL && !mFile.isNull())
   {
      // close the stream
      fclose(mHandle);
      mHandle = NULL;
   }
}
<|endoftext|>


<commit_before>/*******************************************************************************
    Taichi - Physically based Computer Graphics Library

    Copyright (c) 2017 Yuanming Hu <yuanmhu@gmail.com>

    All rights reserved. Use of this source code is governed by
    the MIT license as written in the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/math.h>
#include <taichi/math/qr_svd.h>

TC_NAMESPACE_BEGIN

class TestLinAlg : public Task {
  template <int DIM>
  void test_matrix_inverse() {
    using Matrix = MatrixND<DIM, real>;
    for (int i = 0; i < 10000; i++) {
      Matrix M = Matrix::rand();
      P(M);
      P(M * inverse(M));
      assert((M * inverse(M) - Matrix(1.0_f)).frobenius_norm2() < 1e-3_f);
    }
  }

  void test_qr() {
    using Matrix = MatrixND<2, real>;
    for (int i = 0; i < 100; i++) {
      Matrix m = Matrix::rand(), q, r;
      qr_decomp(m, q, r);
      assert((m - q * r).frobenius_norm() < 1e-5_f);
      assert((q * q.transposed() - Matrix(1.0_f)).frobenius_norm() < 1e-6_f);
      assert(abs(r[0][1]) < 1e-6_f);
      assert(r[0][0] > -1e-6_f);
      assert(r[1][1] > -1e-6_f);
      P(q);
      P(r);
    }
  }

  void run() override {
    Vector3 a(1, 2, 3), b(4, 2, 5);
    assert(a + b == Vector3(5, 4, 8));
    assert(b - a == Vector3(3, 0, 2));
    assert(b * a == Vector3(4, 4, 15));
    assert(b / a == Vector3(4, 1, 5.0f / 3.0f));
    a += b;
    assert(a == Vector3(5, 4, 8));
    a -= b;
    assert(a == Vector3(1, 2, 3));
    a *= b;
    assert(a == Vector3(4, 4, 15));
    a /= b;
    assert(a == Vector3(1, 2, 3));
    a = Vector3({7.0f, 8.0f, 9.0f});
    assert(a == Vector3(7, 8, 9));

#ifndef TC_USE_DOUBLE
    auto t = __m128(a);
#endif

    Vector2 c(1, 2), d(2, 5);
    assert(c + d == Vector2(3, 7));

    assert(Vector4(1, 2, 3, 1).length2() == 15.0f);
#ifndef TC_USE_DOUBLE
    assert(Vector3(1, 2, 3, 1).length2() == 14.0f);
#endif
    assert(dot(Vector2(1, 2), Vector2(3, 2)) == 7.0f);
    assert(dot(Vector2i(1, 2), Vector2i(3, 2)) == 7);
    assert((fract(Vector2(1.3f, 2.7f)) - Vector2(0.3f, 0.7f)).length2() <
           1e-10f);
    assert(Vector2(1.3f, 2.7f).sin() == Vector2(sin(1.3f), sin(2.7f)));

    assert(Matrix3(3.0f) + Matrix3(4.0f) == Matrix3(7.0f));
    assert(Matrix3(3.0f) + Matrix3(Vector3(1, 2, 3)) ==
           Matrix3(Vector3(4, 5, 6)));

    assert(Matrix2(Vector2(1, 2)) * Vector2(2, 3) == Vector2(2, 6));
    assert(Matrix3(Vector3(1, 2, 3)) * Vector3(2, 3, 4) == Vector3(2, 6, 12));
    assert(Matrix4(Vector4(1, 2, 3, 4)) * Vector4(2, 3, 4, 5) ==
           Vector4(2, 6, 12, 20));

    test_matrix_inverse<2>();
    test_matrix_inverse<3>();
    test_matrix_inverse<4>();

    test_qr();

    std::cout << "Passed." << std::endl;
  }
};

TC_IMPLEMENTATION(Task, TestLinAlg, "test_linalg")

TC_NAMESPACE_END
<commit_msg>Test linalg crashes with certain prob. due to matrix singularity<commit_after>/*******************************************************************************
    Taichi - Physically based Computer Graphics Library

    Copyright (c) 2017 Yuanming Hu <yuanmhu@gmail.com>

    All rights reserved. Use of this source code is governed by
    the MIT license as written in the LICENSE file.
*******************************************************************************/

#include <taichi/util.h>
#include <taichi/math.h>
#include <taichi/math/qr_svd.h>

TC_NAMESPACE_BEGIN

class TestLinAlg : public Task {
  template <int DIM>
  void test_matrix_inverse() {
    using Matrix = MatrixND<DIM, real>;
    for (int i = 0; i < 100; i++) {
      Matrix M = Matrix::rand();
      P(M);
      P(M * inverse(M));
      assert((M * inverse(M) - Matrix(1.0_f)).frobenius_norm2() < 1e-3_f);
    }
  }

  void test_qr() {
    using Matrix = MatrixND<2, real>;
    for (int i = 0; i < 100; i++) {
      Matrix m = Matrix::rand(), q, r;
      qr_decomp(m, q, r);
      assert((m - q * r).frobenius_norm() < 1e-5_f);
      assert((q * q.transposed() - Matrix(1.0_f)).frobenius_norm() < 1e-6_f);
      assert(abs(r[0][1]) < 1e-6_f);
      assert(r[0][0] > -1e-6_f);
      assert(r[1][1] > -1e-6_f);
      P(q);
      P(r);
    }
  }

  void run() override {
    Vector3 a(1, 2, 3), b(4, 2, 5);
    assert(a + b == Vector3(5, 4, 8));
    assert(b - a == Vector3(3, 0, 2));
    assert(b * a == Vector3(4, 4, 15));
    assert(b / a == Vector3(4, 1, 5.0f / 3.0f));
    a += b;
    assert(a == Vector3(5, 4, 8));
    a -= b;
    assert(a == Vector3(1, 2, 3));
    a *= b;
    assert(a == Vector3(4, 4, 15));
    a /= b;
    assert(a == Vector3(1, 2, 3));
    a = Vector3({7.0f, 8.0f, 9.0f});
    assert(a == Vector3(7, 8, 9));

#ifndef TC_USE_DOUBLE
    auto t = __m128(a);
#endif

    Vector2 c(1, 2), d(2, 5);
    assert(c + d == Vector2(3, 7));

    assert(Vector4(1, 2, 3, 1).length2() == 15.0f);
#ifndef TC_USE_DOUBLE
    assert(Vector3(1, 2, 3, 1).length2() == 14.0f);
#endif
    assert(dot(Vector2(1, 2), Vector2(3, 2)) == 7.0f);
    assert(dot(Vector2i(1, 2), Vector2i(3, 2)) == 7);
    assert((fract(Vector2(1.3f, 2.7f)) - Vector2(0.3f, 0.7f)).length2() <
           1e-10f);
    assert(Vector2(1.3f, 2.7f).sin() == Vector2(sin(1.3f), sin(2.7f)));

    assert(Matrix3(3.0f) + Matrix3(4.0f) == Matrix3(7.0f));
    assert(Matrix3(3.0f) + Matrix3(Vector3(1, 2, 3)) ==
           Matrix3(Vector3(4, 5, 6)));

    assert(Matrix2(Vector2(1, 2)) * Vector2(2, 3) == Vector2(2, 6));
    assert(Matrix3(Vector3(1, 2, 3)) * Vector3(2, 3, 4) == Vector3(2, 6, 12));
    assert(Matrix4(Vector4(1, 2, 3, 4)) * Vector4(2, 3, 4, 5) ==
           Vector4(2, 6, 12, 20));

    test_matrix_inverse<2>();
    test_matrix_inverse<3>();
    test_matrix_inverse<4>();

    test_qr();

    std::cout << "Passed." << std::endl;
  }
};

TC_IMPLEMENTATION(Task, TestLinAlg, "test_linalg")

TC_NAMESPACE_END
<|endoftext|>


<commit_before>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "translation/Handler.hxx"
#include "Client.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "stock/GetHandler.hxx"
#include "lease.hxx"
#include "pool.hxx"
#include "system/Error.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "event/SocketEvent.hxx"

#include <daemon/log.h>

#include <stdexcept>

#include <string.h>
#include <errno.h>

class TranslateConnection final : public StockItem {
    UniqueSocketDescriptor s;

    SocketEvent event;

public:
    explicit TranslateConnection(CreateStockItem c)
        :StockItem(c),
         event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)) {}

    ~TranslateConnection() override {
        if (s.IsDefined())
            event.Delete();
    }

private:
    bool CreateAndConnect(SocketAddress address) {
        assert(!s.IsDefined());

        return s.CreateNonBlock(AF_LOCAL, SOCK_STREAM, 0) &&
            s.Connect(address);
    }

public:
    void CreateAndConnectAndFinish(SocketAddress address) {
        if (CreateAndConnect(address)) {
            event.Set(s.Get(), SocketEvent::READ);
            InvokeCreateSuccess();
        } else {
            auto error = std::make_exception_ptr(MakeErrno());

            if (s.IsDefined())
                s.Close();

            InvokeCreateError(error);
        }
    }

    SocketDescriptor GetSocket() {
        return s;
    }

private:
    void EventCallback(unsigned) {
        char buffer;
        ssize_t nbytes = recv(s.Get(), &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            daemon_log(2, "error on idle translation server connection: %s\n",
                       strerror(errno));
        else if (nbytes > 0)
            daemon_log(2, "unexpected data in idle translation server connection\n");

        InvokeIdleDisconnect();
    }

public:
    /* virtual methods from class StockItem */
    bool Borrow() override {
        event.Delete();
        return true;
    }

    bool Release() override {
        event.Add();
        return true;
    }
};

class TranslateStock final : StockClass {
    Stock stock;

    AllocatedSocketAddress address;

public:
    TranslateStock(EventLoop &event_loop, SocketAddress _address,
                   unsigned limit)
        :stock(event_loop, *this, "translation", limit, 8),
         address(_address) {
    }

    EventLoop &GetEventLoop() {
        return stock.GetEventLoop();
    }

    void Get(struct pool &pool, StockGetHandler &handler,
             CancellablePointer &cancel_ptr) {
        stock.Get(pool, nullptr, handler, cancel_ptr);
    }

    void Put(StockItem &item, bool destroy) {
        stock.Put(item, destroy);
    }

private:
    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *, struct pool &,
                CancellablePointer &) override {
        auto *connection = new TranslateConnection(c);
        connection->CreateAndConnectAndFinish(address);
    }
};

class TranslateStockRequest final : public StockGetHandler, Lease {
    struct pool &pool;

    TranslateStock &stock;
    TranslateConnection *item;

    const TranslateRequest &request;

    const TranslateHandler &handler;
    void *handler_ctx;

    CancellablePointer &cancel_ptr;

public:
    TranslateStockRequest(TranslateStock &_stock, struct pool &_pool,
                          const TranslateRequest &_request,
                          const TranslateHandler &_handler, void *_ctx,
                          CancellablePointer &_cancel_ptr)
        :pool(_pool), stock(_stock),
         request(_request),
         handler(_handler), handler_ctx(_ctx),
         cancel_ptr(_cancel_ptr) {}

    /* virtual methods from class StockGetHandler */
    void OnStockItemReady(StockItem &item) override;
    void OnStockItemError(std::exception_ptr ep) override;

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) override {
        stock.Put(*item, !reuse);
    }
};

/*
 * stock callback
 *
 */

void
TranslateStockRequest::OnStockItemReady(StockItem &_item)
{
    item = &(TranslateConnection &)_item;
    translate(pool, stock.GetEventLoop(), item->GetSocket(),
              *this,
              request, handler, handler_ctx,
              cancel_ptr);
}

void
TranslateStockRequest::OnStockItemError(std::exception_ptr ep)
{
    handler.error(ep, handler_ctx);
}

/*
 * constructor
 *
 */

TranslateStock *
tstock_new(EventLoop &event_loop, SocketAddress address, unsigned limit)
{
    return new TranslateStock(event_loop, address, limit);
}

void
tstock_free(TranslateStock *stock)
{
    delete stock;
}

void
tstock_translate(TranslateStock &stock, struct pool &pool,
                 const TranslateRequest &request,
                 const TranslateHandler &handler, void *ctx,
                 CancellablePointer &cancel_ptr)
{
    auto r = NewFromPool<TranslateStockRequest>(pool, stock, pool, request,
                                                handler, ctx, cancel_ptr);
    stock.Get(pool, *r, cancel_ptr);
}
<commit_msg>translation/Stock: use the Logger library instead of libdaemon<commit_after>/*
 * Copyright 2007-2017 Content Management AG
 * All rights reserved.
 *
 * author: Max Kellermann <mk@cm4all.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the
 * distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
 * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "Stock.hxx"
#include "translation/Handler.hxx"
#include "Client.hxx"
#include "stock/Stock.hxx"
#include "stock/Class.hxx"
#include "stock/Item.hxx"
#include "stock/GetHandler.hxx"
#include "lease.hxx"
#include "pool.hxx"
#include "system/Error.hxx"
#include "net/AllocatedSocketAddress.hxx"
#include "net/UniqueSocketDescriptor.hxx"
#include "event/SocketEvent.hxx"
#include "io/Logger.hxx"

#include <stdexcept>

#include <string.h>
#include <errno.h>

class TranslateConnection final : public StockItem {
    UniqueSocketDescriptor s;

    SocketEvent event;

public:
    explicit TranslateConnection(CreateStockItem c)
        :StockItem(c),
         event(c.stock.GetEventLoop(), BIND_THIS_METHOD(EventCallback)) {}

    ~TranslateConnection() override {
        if (s.IsDefined())
            event.Delete();
    }

private:
    bool CreateAndConnect(SocketAddress address) {
        assert(!s.IsDefined());

        return s.CreateNonBlock(AF_LOCAL, SOCK_STREAM, 0) &&
            s.Connect(address);
    }

public:
    void CreateAndConnectAndFinish(SocketAddress address) {
        if (CreateAndConnect(address)) {
            event.Set(s.Get(), SocketEvent::READ);
            InvokeCreateSuccess();
        } else {
            auto error = std::make_exception_ptr(MakeErrno());

            if (s.IsDefined())
                s.Close();

            InvokeCreateError(error);
        }
    }

    SocketDescriptor GetSocket() {
        return s;
    }

private:
    void EventCallback(unsigned) {
        char buffer;
        ssize_t nbytes = recv(s.Get(), &buffer, sizeof(buffer), MSG_DONTWAIT);
        if (nbytes < 0)
            LogConcat(2, "translation",
                      "error on idle translation server connection: ",
                      strerror(errno));
        else if (nbytes > 0)
            LogConcat(2, "translation",
                      "unexpected data in idle translation server connection");

        InvokeIdleDisconnect();
    }

public:
    /* virtual methods from class StockItem */
    bool Borrow() override {
        event.Delete();
        return true;
    }

    bool Release() override {
        event.Add();
        return true;
    }
};

class TranslateStock final : StockClass {
    Stock stock;

    AllocatedSocketAddress address;

public:
    TranslateStock(EventLoop &event_loop, SocketAddress _address,
                   unsigned limit)
        :stock(event_loop, *this, "translation", limit, 8),
         address(_address) {
    }

    EventLoop &GetEventLoop() {
        return stock.GetEventLoop();
    }

    void Get(struct pool &pool, StockGetHandler &handler,
             CancellablePointer &cancel_ptr) {
        stock.Get(pool, nullptr, handler, cancel_ptr);
    }

    void Put(StockItem &item, bool destroy) {
        stock.Put(item, destroy);
    }

private:
    /* virtual methods from class StockClass */
    void Create(CreateStockItem c, void *, struct pool &,
                CancellablePointer &) override {
        auto *connection = new TranslateConnection(c);
        connection->CreateAndConnectAndFinish(address);
    }
};

class TranslateStockRequest final : public StockGetHandler, Lease {
    struct pool &pool;

    TranslateStock &stock;
    TranslateConnection *item;

    const TranslateRequest &request;

    const TranslateHandler &handler;
    void *handler_ctx;

    CancellablePointer &cancel_ptr;

public:
    TranslateStockRequest(TranslateStock &_stock, struct pool &_pool,
                          const TranslateRequest &_request,
                          const TranslateHandler &_handler, void *_ctx,
                          CancellablePointer &_cancel_ptr)
        :pool(_pool), stock(_stock),
         request(_request),
         handler(_handler), handler_ctx(_ctx),
         cancel_ptr(_cancel_ptr) {}

    /* virtual methods from class StockGetHandler */
    void OnStockItemReady(StockItem &item) override;
    void OnStockItemError(std::exception_ptr ep) override;

    /* virtual methods from class Lease */
    void ReleaseLease(bool reuse) override {
        stock.Put(*item, !reuse);
    }
};

/*
 * stock callback
 *
 */

void
TranslateStockRequest::OnStockItemReady(StockItem &_item)
{
    item = &(TranslateConnection &)_item;
    translate(pool, stock.GetEventLoop(), item->GetSocket(),
              *this,
              request, handler, handler_ctx,
              cancel_ptr);
}

void
TranslateStockRequest::OnStockItemError(std::exception_ptr ep)
{
    handler.error(ep, handler_ctx);
}

/*
 * constructor
 *
 */

TranslateStock *
tstock_new(EventLoop &event_loop, SocketAddress address, unsigned limit)
{
    return new TranslateStock(event_loop, address, limit);
}

void
tstock_free(TranslateStock *stock)
{
    delete stock;
}

void
tstock_translate(TranslateStock &stock, struct pool &pool,
                 const TranslateRequest &request,
                 const TranslateHandler &handler, void *ctx,
                 CancellablePointer &cancel_ptr)
{
    auto r = NewFromPool<TranslateStockRequest>(pool, stock, pool, request,
                                                handler, ctx, cancel_ptr);
    stock.Get(pool, *r, cancel_ptr);
}
<|endoftext|>