<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtGui/QColor>
#include <QtGui/QApplication>

namespace QmlJS {
namespace Messages {
static const char *invalid_property_name =  QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a valid property name");
static const char *unknown_type = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown type");
static const char *has_no_members = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' does not have members");
static const char *is_not_a_member = QT_TRANSLATE_NOOP("QmlJS::Check", "'%1' is not a member of '%2'");
static const char *easing_curve_not_a_string = QT_TRANSLATE_NOOP("QmlJS::Check", "easing-curve name is not a string");
static const char *unknown_easing_curve_name = QT_TRANSLATE_NOOP("QmlJS::Check", "unknown easing-curve name");
static const char *value_might_be_undefined = QT_TRANSLATE_NOOP("QmlJS::Check", "value might be 'undefined'");
} // namespace Messages

static inline QString tr(const char *msg)
{ return qApp->translate("QmlJS::Check", msg); }

} // namespace QmlJS

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

namespace {

class AssignmentCheck : public ValueVisitor
{
public:
    DiagnosticMessage operator()(
            const SourceLocation &location,
            const Interpreter::Value *lhsValue,
            const Interpreter::Value *rhsValue,
            ExpressionNode *ast)
    {
        _message = DiagnosticMessage(DiagnosticMessage::Error, location, QString());
        _rhsValue = rhsValue;
        _ast = ast;

        if (lhsValue)
            lhsValue->accept(this);

        return _message;
    }

    virtual void visit(const NumberValue *)
    {
        // ### Consider enums: elide: "ElideLeft" is valid, but currently elide is a NumberValue.
        if (/*cast<StringLiteral *>(_ast)
                ||*/ _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "numerical value expected");
        }
    }

    virtual void visit(const BooleanValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<StringLiteral *>(_ast)
                || cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "boolean value expected");
        }
    }

    virtual void visit(const StringValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
                || _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "string value expected");
        }
    }

    virtual void visit(const EasingCurveNameValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString curveName = stringLiteral->value->asString();

            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
                _message.message = tr(Messages::unknown_easing_curve_name);
            }
        } else if (_rhsValue->asUndefinedValue()) {
            _message.kind = DiagnosticMessage::Warning;
            _message.message = tr(Messages::value_might_be_undefined);
        } else if (! _rhsValue->asStringValue()) {
            _message.message = tr(Messages::easing_curve_not_a_string);
        }
    }

    virtual void visit(const ColorValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString colorString = stringLiteral->value->asString();

            bool ok = true;
            if (colorString.size() == 9 && colorString.at(0) == QLatin1Char('#')) {
                // #rgba
                for (int i = 1; i < 9; ++i) {
                    const QChar c = colorString.at(i);
                    if ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
                        || (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
                        || (c >= QLatin1Char('A') && c <= QLatin1Char('F')))
                        continue;
                    ok = false;
                    break;
                }
            } else {
                ok = QColor::isValidColor(colorString);
            }
            if (!ok)
                _message.message = QCoreApplication::translate("QmlJS::Check", "not a valid color");
        } else {
            visit((StringValue *)0);
        }
    }

    virtual void visit(const AnchorLineValue *)
    {
        if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
            _message.message = QCoreApplication::translate("QmlJS::Check", "expected anchor line");
    }

    DiagnosticMessage _message;
    const Value *_rhsValue;
    ExpressionNode *_ast;
};

} // end of anonymous namespace

Check::Check(Document::Ptr doc, const Snapshot &snapshot, const QStringList &importPaths)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot, importPaths)
    , _scopeBuilder(doc, &_context)
    , _ignoreTypeErrors(_context.documentImportsPlugins(_doc.data()))
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    _messages.append(_link.diagnosticMessages());
    return _messages;
}

bool Check::visit(UiProgram *)
{
    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    _scopeBuilder.push(ast);

    if (! _context.lookupType(_doc.data(), typeId)) {
        if (! _ignoreTypeErrors)
            error(typeId->identifierToken, tr(Messages::unknown_type));
        // suppress subsequent errors about scope object lookup by clearing
        // the scope object list
        // ### todo: better way?
        _context.scopeChain().qmlScopeObjects.clear();
        _context.scopeChain().update();
    }

    Node::accept(initializer, this);

    _scopeBuilder.pop();
}

bool Check::visit(UiScriptBinding *ast)
{
    // special case for id property
    if (ast->qualifiedId->name->asString() == QLatin1String("id") && ! ast->qualifiedId->next) {
        if (! ast->statement)
            return false;

        const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
                                                     ast->statement->lastSourceLocation());

        ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
        if (!expStmt) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        QString id;
        if (IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression)) {
            id = idExp->name->asString();
        } else if (StringLiteral *strExp = cast<StringLiteral *>(expStmt->expression)) {
            id = strExp->value->asString();
            warning(loc, QCoreApplication::translate("QmlJS::Check", "using string literals for ids is discouraged"));
        } else {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        if (id.isEmpty() || ! id[0].isLower()) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "ids must be lower case"));
            return false;
        }
    }

    const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId);
    if (lhsValue) {
        // ### Fix the evaluator to accept statements!
        if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) {
            ExpressionNode *expr = expStmt->expression;

            Evaluate evaluator(&_context);
            const Value *rhsValue = evaluator(expr);

            const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
                                                         expStmt->lastSourceLocation());
            AssignmentCheck assignmentCheck;
            DiagnosticMessage message = assignmentCheck(loc, lhsValue, rhsValue, expr);
            if (! message.message.isEmpty())
                _messages += message;
        }

    }

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

/// When something is changed here, also change ReadingContext::lookupProperty in
/// texttomodelmerger.cpp
/// ### Maybe put this into the context as a helper method.
const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{
    QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects;
    if (scopeObjects.isEmpty())
        return 0;

    if (! id)
        return 0; // ### error?

    if (! id->name) // possible after error recovery
        return 0;

    QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return 0; // ### should probably be a special value

    // attached properties
    bool isAttachedProperty = false;
    if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
        isAttachedProperty = true;
        scopeObjects += _context.scopeChain().qmlTypes;
    }

    if (scopeObjects.isEmpty())
        return 0;

    // global lookup for first part of id
    const Value *value = 0;
    for (int i = scopeObjects.size() - 1; i >= 0; --i) {
        value = scopeObjects[i]->lookupMember(propertyName, &_context);
        if (value)
            break;
    }
    if (!value) {
        error(id->identifierToken,
              tr(Messages::invalid_property_name).arg(propertyName));
    }

    // can't look up members for attached properties
    if (isAttachedProperty)
        return 0;

    // member lookup
    const UiQualifiedId *idPart = id;
    while (idPart->next) {
        const ObjectValue *objectValue = value_cast<const ObjectValue *>(value);
        if (! objectValue) {
            error(idPart->identifierToken,
                  tr(Messages::has_no_members).arg(propertyName));
            return 0;
        }

        if (! idPart->next->name) {
            // somebody typed "id." and error recovery still gave us a valid tree,
            // so just bail out here.
            return 0;
        }

        idPart = idPart->next;
        propertyName = idPart->name->asString();

        value = objectValue->lookupMember(propertyName, &_context);
        if (! value) {
            error(idPart->identifierToken,
                  tr(Messages::is_not_a_member).arg(propertyName,
                                               objectValue->className()));
            return 0;
        }
    }

    return value;
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}

SourceLocation Check::locationFromRange(const SourceLocation &start,
                                        const SourceLocation &end)
{
    return SourceLocation(start.offset,
                          end.end() - start.begin(),
                          start.startLine,
                          start.startColumn);
}
<commit_msg>QmlJS: Change way to translate strings to nicer one.<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "qmljscheck.h"
#include "qmljsbind.h"
#include "qmljsinterpreter.h"
#include "qmljsevaluate.h"
#include "parser/qmljsast_p.h"

#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
#include <QtGui/QColor>
#include <QtGui/QApplication>

using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJS::Interpreter;

namespace {

class AssignmentCheck : public ValueVisitor
{
public:
    DiagnosticMessage operator()(
            const SourceLocation &location,
            const Interpreter::Value *lhsValue,
            const Interpreter::Value *rhsValue,
            ExpressionNode *ast)
    {
        _message = DiagnosticMessage(DiagnosticMessage::Error, location, QString());
        _rhsValue = rhsValue;
        _ast = ast;

        if (lhsValue)
            lhsValue->accept(this);

        return _message;
    }

    virtual void visit(const NumberValue *)
    {
        // ### Consider enums: elide: "ElideLeft" is valid, but currently elide is a NumberValue.
        if (/*cast<StringLiteral *>(_ast)
                ||*/ _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "numerical value expected");
        }
    }

    virtual void visit(const BooleanValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<StringLiteral *>(_ast)
                || cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "boolean value expected");
        }
    }

    virtual void visit(const StringValue *)
    {
        UnaryMinusExpression *unaryMinus = cast<UnaryMinusExpression *>(_ast);

        if (cast<NumericLiteral *>(_ast)
                || (unaryMinus && cast<NumericLiteral *>(unaryMinus->expression))
                || _ast->kind == Node::Kind_TrueLiteral
                || _ast->kind == Node::Kind_FalseLiteral) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "string value expected");
        }
    }

    virtual void visit(const EasingCurveNameValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString curveName = stringLiteral->value->asString();

            if (!EasingCurveNameValue::curveNames().contains(curveName)) {
                _message.message = QCoreApplication::translate("QmlJS::Check", "unknown easing-curve name");
            }
        } else if (_rhsValue->asUndefinedValue()) {
            _message.kind = DiagnosticMessage::Warning;
            _message.message = QCoreApplication::translate("QmlJS::Check", "value might be 'undefined'");
        } else if (! _rhsValue->asStringValue()) {
            _message.message = QCoreApplication::translate("QmlJS::Check", "easing-curve name is not a string");
        }
    }

    virtual void visit(const ColorValue *)
    {
        if (StringLiteral *stringLiteral = cast<StringLiteral *>(_ast)) {
            const QString colorString = stringLiteral->value->asString();

            bool ok = true;
            if (colorString.size() == 9 && colorString.at(0) == QLatin1Char('#')) {
                // #rgba
                for (int i = 1; i < 9; ++i) {
                    const QChar c = colorString.at(i);
                    if ((c >= QLatin1Char('0') && c <= QLatin1Char('9'))
                        || (c >= QLatin1Char('a') && c <= QLatin1Char('f'))
                        || (c >= QLatin1Char('A') && c <= QLatin1Char('F')))
                        continue;
                    ok = false;
                    break;
                }
            } else {
                ok = QColor::isValidColor(colorString);
            }
            if (!ok)
                _message.message = QCoreApplication::translate("QmlJS::Check", "not a valid color");
        } else {
            visit((StringValue *)0);
        }
    }

    virtual void visit(const AnchorLineValue *)
    {
        if (! (_rhsValue->asAnchorLineValue() || _rhsValue->asUndefinedValue()))
            _message.message = QCoreApplication::translate("QmlJS::Check", "expected anchor line");
    }

    DiagnosticMessage _message;
    const Value *_rhsValue;
    ExpressionNode *_ast;
};

} // end of anonymous namespace

Check::Check(Document::Ptr doc, const Snapshot &snapshot, const QStringList &importPaths)
    : _doc(doc)
    , _snapshot(snapshot)
    , _context(&_engine)
    , _link(&_context, doc, snapshot, importPaths)
    , _scopeBuilder(doc, &_context)
    , _ignoreTypeErrors(_context.documentImportsPlugins(_doc.data()))
{
}

Check::~Check()
{
}

QList<DiagnosticMessage> Check::operator()()
{
    _messages.clear();
    Node::accept(_doc->ast(), this);
    _messages.append(_link.diagnosticMessages());
    return _messages;
}

bool Check::visit(UiProgram *)
{
    return true;
}

bool Check::visit(UiObjectDefinition *ast)
{
    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

bool Check::visit(UiObjectBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    visitQmlObject(ast, ast->qualifiedTypeNameId, ast->initializer);
    return false;
}

void Check::visitQmlObject(Node *ast, UiQualifiedId *typeId,
                           UiObjectInitializer *initializer)
{
    // If the 'typeId' starts with a lower-case letter, it doesn't define
    // a new object instance. For instance: anchors { ... }
    if (typeId->name->asString().at(0).isLower() && ! typeId->next) {
        checkScopeObjectMember(typeId);
        // ### don't give up!
        return;
    }

    _scopeBuilder.push(ast);

    if (! _context.lookupType(_doc.data(), typeId)) {
        if (! _ignoreTypeErrors)
            error(typeId->identifierToken,
                  QCoreApplication::translate("QmlJS::Check", "unknown type"));
        // suppress subsequent errors about scope object lookup by clearing
        // the scope object list
        // ### todo: better way?
        _context.scopeChain().qmlScopeObjects.clear();
        _context.scopeChain().update();
    }

    Node::accept(initializer, this);

    _scopeBuilder.pop();
}

bool Check::visit(UiScriptBinding *ast)
{
    // special case for id property
    if (ast->qualifiedId->name->asString() == QLatin1String("id") && ! ast->qualifiedId->next) {
        if (! ast->statement)
            return false;

        const SourceLocation loc = locationFromRange(ast->statement->firstSourceLocation(),
                                                     ast->statement->lastSourceLocation());

        ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement);
        if (!expStmt) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        QString id;
        if (IdentifierExpression *idExp = cast<IdentifierExpression *>(expStmt->expression)) {
            id = idExp->name->asString();
        } else if (StringLiteral *strExp = cast<StringLiteral *>(expStmt->expression)) {
            id = strExp->value->asString();
            warning(loc, QCoreApplication::translate("QmlJS::Check", "using string literals for ids is discouraged"));
        } else {
            error(loc, QCoreApplication::translate("QmlJS::Check", "expected id"));
            return false;
        }

        if (id.isEmpty() || ! id[0].isLower()) {
            error(loc, QCoreApplication::translate("QmlJS::Check", "ids must be lower case"));
            return false;
        }
    }

    const Value *lhsValue = checkScopeObjectMember(ast->qualifiedId);
    if (lhsValue) {
        // ### Fix the evaluator to accept statements!
        if (ExpressionStatement *expStmt = cast<ExpressionStatement *>(ast->statement)) {
            ExpressionNode *expr = expStmt->expression;

            Evaluate evaluator(&_context);
            const Value *rhsValue = evaluator(expr);

            const SourceLocation loc = locationFromRange(expStmt->firstSourceLocation(),
                                                         expStmt->lastSourceLocation());
            AssignmentCheck assignmentCheck;
            DiagnosticMessage message = assignmentCheck(loc, lhsValue, rhsValue, expr);
            if (! message.message.isEmpty())
                _messages += message;
        }

    }

    return true;
}

bool Check::visit(UiArrayBinding *ast)
{
    checkScopeObjectMember(ast->qualifiedId);

    return true;
}

/// When something is changed here, also change ReadingContext::lookupProperty in
/// texttomodelmerger.cpp
/// ### Maybe put this into the context as a helper method.
const Value *Check::checkScopeObjectMember(const UiQualifiedId *id)
{
    QList<const ObjectValue *> scopeObjects = _context.scopeChain().qmlScopeObjects;
    if (scopeObjects.isEmpty())
        return 0;

    if (! id)
        return 0; // ### error?

    if (! id->name) // possible after error recovery
        return 0;

    QString propertyName = id->name->asString();

    if (propertyName == QLatin1String("id") && ! id->next)
        return 0; // ### should probably be a special value

    // attached properties
    bool isAttachedProperty = false;
    if (! propertyName.isEmpty() && propertyName[0].isUpper()) {
        isAttachedProperty = true;
        scopeObjects += _context.scopeChain().qmlTypes;
    }

    if (scopeObjects.isEmpty())
        return 0;

    // global lookup for first part of id
    const Value *value = 0;
    for (int i = scopeObjects.size() - 1; i >= 0; --i) {
        value = scopeObjects[i]->lookupMember(propertyName, &_context);
        if (value)
            break;
    }
    if (!value) {
        error(id->identifierToken,
              QCoreApplication::translate("QmlJS::Check", "'%1' is not a valid property name").arg(propertyName));
    }

    // can't look up members for attached properties
    if (isAttachedProperty)
        return 0;

    // member lookup
    const UiQualifiedId *idPart = id;
    while (idPart->next) {
        const ObjectValue *objectValue = value_cast<const ObjectValue *>(value);
        if (! objectValue) {
            error(idPart->identifierToken,
                  QCoreApplication::translate("QmlJS::Check", "'%1' does not have members").arg(propertyName));
            return 0;
        }

        if (! idPart->next->name) {
            // somebody typed "id." and error recovery still gave us a valid tree,
            // so just bail out here.
            return 0;
        }

        idPart = idPart->next;
        propertyName = idPart->name->asString();

        value = objectValue->lookupMember(propertyName, &_context);
        if (! value) {
            error(idPart->identifierToken,
                  QCoreApplication::translate("QmlJS::Check", "'%1' is not a member of '%2'").arg(
                          propertyName, objectValue->className()));
            return 0;
        }
    }

    return value;
}

void Check::error(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Error, loc, message));
}

void Check::warning(const AST::SourceLocation &loc, const QString &message)
{
    _messages.append(DiagnosticMessage(DiagnosticMessage::Warning, loc, message));
}

SourceLocation Check::locationFromRange(const SourceLocation &start,
                                        const SourceLocation &end)
{
    return SourceLocation(start.offset,
                          end.end() - start.begin(),
                          start.startLine,
                          start.startColumn);
}
<|endoftext|>


<commit_before>
#ifndef __CONFIG_ARGS_H__
#define __CONFIG_ARGS_H__

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.2"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                64

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     64

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// Currently, each cache uses two IO accounts:
// one account for writes, and one account for reads.
// By adjusting the priorities of these accounts, reads
// can be prioritized over writes or the other way around.
//
// This is a one-per-serializer/file priority.
// The per-cache priorities are dynamically derived by dividing these priorities
// by the number of slices on a specific file.
#define CACHE_READS_IO_PRIORITY                   2048
#define CACHE_WRITES_IO_PRIORITY                  128

// Garbage Colletion uses its own two IO accounts.
// There is one low-priority account that is meant to guarantee
// (performance-wise) unintrusive garbage collection.
// If the garbage ratio keeps growing,
// GC starts using the high priority account instead, which
// might have a negative influence on database performance
// under i/o heavy workloads but guarantees that the database
// doesn't grow indefinitely.
//
// This is a one-per-serializer/file priority.
#define GC_IO_PRIORITY_NICE                       16
#define GC_IO_PRIORITY_HIGH                       (2 * CACHE_WRITES_IO_PRIORITY)

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (8 * MEGABYTE)

// Max number of blocks which can be read ahead in one i/o transaction (if enabled)
#define MAX_READ_AHEAD_BLOCKS 32

// Max size of log file name
#define MAX_LOG_FILE_NAME                         1024

// Max length of log message, including terminating \0
#define MAX_LOG_MSGLEN                            1024

// Queue ID of logging worker
#define LOG_WORKER 0

// Ratio of free ram to use for the cache by default
#define DEFAULT_MAX_CACHE_RATIO                   0.7f

// Maximum number of threads we support
// TODO: make this dynamic where possible
#define MAX_THREADS                                  128

// Maximum slices total
#define MAX_SLICES                                128

// Maximum number of files we use
#define MAX_SERIALIZERS                           32

// The number of ways we split a BTree (the most optimal is the number
// of cores, but we use a higher split factor to allow upgrading to
// more cores without migrating the database file).
#define DEFAULT_BTREE_SHARD_FACTOR                64

// If --diff-log-size is not specified, then the patch log size will default to the
// smaller of DEFAULT_PATCH_LOG_SIZE and (DEFAULT_PATCH_LOG_FRACTION * cache size).
#ifdef NDEBUG
#define DEFAULT_PATCH_LOG_SIZE                     (512 * MEGABYTE)
#else
#define DEFAULT_PATCH_LOG_SIZE                     (4 * MEGABYTE)
#endif
#define DEFAULT_PATCH_LOG_FRACTION                 0.2

// Default port to listen on
#define DEFAULT_LISTEN_PORT                       11211

// Default port to do replication on
#define DEFAULT_REPLICATION_PORT                  11319

#define DEFAULT_TOTAL_DELETE_QUEUE_LIMIT          GIGABYTE

// Heartbeat configuration...
// The interval at which heartbeats are sent (ms)
#define REPLICATION_HEARTBEAT_INTERVAL            800 // (so we can allow a timeout of 1000 if users like it risky)
// The default timeout. This is the time after which replication
// connections get terminated, if no activity has been observed.
#define DEFAULT_REPLICATION_HEARTBEAT_TIMEOUT     10000

// Default extension for the semantic file which is appended to the database name
#define DEFAULT_SEMANTIC_EXTENSION                ".semantic"

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    1000

// flush_waiting_threshold is the maximal number of transactions which can wait
// for a sync before a flush gets triggered on any single slice. As transactions only wait for
// sync with wait_for_flush enabled, this option plays a role only then.
#define DEFAULT_FLUSH_WAITING_THRESHOLD           8

// If wait_for_flush is true, concurrent flushing can be used to reduce the latency
// of each single flush. max_concurrent_flushes controls how many flushes can be active
// on a specific slice at any given time.
#define DEFAULT_MAX_CONCURRENT_FLUSHES            1

// If the size of the data affected by the current set of patches in a block is larger than
// block size / MAX_PATCHES_SIZE_RATIO, we flush the block instead of waiting for
// more patches to come.
// Note: An average write transaction under canonical workload leads to patches of about 75
// bytes of affected data.
// The actual value is continuously adjusted between MAX_PATCHES_SIZE_RATIO_MIN and
// MAX_PATCHES_SIZE_RATIO_MAX depending on how much the system is i/o bound
#define MAX_PATCHES_SIZE_RATIO_MIN                100
#define MAX_PATCHES_SIZE_RATIO_MAX                2
#define MAX_PATCHES_SIZE_RATIO_DURABILITY         5
#define RAISE_PATCHES_RATIO_AT_FRACTION_OF_UNSAVED_DATA_LIMIT 0.6

// If more than this many bytes of dirty data accumulate in the cache, then write
// transactions will be throttled.
// A value of 0 means that it will automatically be set to MAX_UNSAVED_DATA_LIMIT_FRACTION
// times the max cache size
#define DEFAULT_UNSAVED_DATA_LIMIT                4096 * MEGABYTE

// The unsaved data limit cannot exceed this fraction of the max cache size
#define MAX_UNSAVED_DATA_LIMIT_FRACTION           0.9

// We start flushing dirty pages as soon as we hit this fraction of the unsaved data limit
#define FLUSH_AT_FRACTION_OF_UNSAVED_DATA_LIMIT   0.2

// How many times the page replacement algorithm tries to find an eligible page before giving up.
// Note that (MAX_UNSAVED_DATA_LIMIT_FRACTION ** PAGE_REPL_NUM_TRIES) is the probability that the
// page replacement algorithm will succeed on a given try, and if that probability is less than 1/2
// then the page replacement algorithm will on average be unable to evict pages from the cache.
#define PAGE_REPL_NUM_TRIES                       10

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// In addition to the value itself we could potentially store
// memcached flags, exptime, and a CAS value in the value contents, so
// we reserve space for that.
#define MAX_BTREE_VALUE_AUXILIARY_SIZE            (sizeof(btree_value) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint64_t))
#define MAX_BTREE_VALUE_SIZE                      (MAX_BTREE_VALUE_AUXILIARY_SIZE + MAX_IN_NODE_VALUE_SIZE)

// memcached specifies the maximum value size to be 1MB, but customers asked this to be much higher
#define MAX_VALUE_SIZE                            10 * MEGABYTE

// Values larger than this will be streamed in a set operation.
#define MAX_BUFFERED_SET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// Values larger than this will be streamed in a get operation
#define MAX_BUFFERED_GET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// If a single connection sends this many 'noreply' commands, the next command will
// have to wait until the first one finishes
#define MAX_CONCURRENT_QUERIES_PER_CONNECTION     500

//
#define MAX_CONCURRENT_QUEURIES_ON_IMPORT 1000

// How many timestamps we store in a leaf node.  We store the
// NUM_LEAF_NODE_EARLIER_TIMES+1 most-recent timestamps.
#define NUM_LEAF_NODE_EARLIER_TIMES               2

// Perform allocator GC every N milliseconds (the resolution is limited to TIMER_TICKS_IN_MS)
#define ALLOC_GC_INTERVAL_MS                      3000

//filenames for the database
#define DEFAULT_DB_FILE_NAME                      "rethinkdb_data"

// We assume there will never be more than this many blocks. The value
// is computed by dividing 1 TB by the smallest reasonable block size.
// This value currently fits in 32 bits, and so block_id_t and
// ser_block_id_t is a uint32_t.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// Special block IDs.  These don't really belong here because they're
// more magic constants than tunable parameters.

// The btree superblock, which has a reference to the root node block
// id.
#define SUPERBLOCK_ID                             0
// HEY: This is kind of fragile because some patch disk storage code
// expects this value to be 1 (since the free list returns 1 the first
// time a block id is generated, or something).
#define MC_CONFIGBLOCK_ID                         (SUPERBLOCK_ID + 1)

// The ratio at which we should start GCing.  (HEY: What's the extra
// 0.000001 in MAX_GC_HIGH_RATIO for?  Is it because we told the user
// that 0.99 was too high?)
#define DEFAULT_GC_HIGH_RATIO                     0.65
#define MAX_GC_HIGH_RATIO                         0.990001

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.5
#define MIN_GC_LOW_RATIO                          0.099999

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// If the size of the LBA on a given disk exceeds LBA_MIN_SIZE_FOR_GC, then the fraction of the
// entries that are live and not garbage should be at least LBA_MIN_UNGARBAGE_FRACTION.
#define LBA_MIN_SIZE_FOR_GC                       (MEGABYTE * 20)
#define LBA_MIN_UNGARBAGE_FRACTION                0.15

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many bytes of buffering space we can use per disk when reading the LBA. If it's set
// too high, then RethinkDB will eat a lot of memory at startup. This is bad because tcmalloc
// doesn't return memory to the OS. If it's set too low, startup will take a longer time.
#define LBA_READ_BUFFER_SIZE                      GIGABYTE

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               1

// How many zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE
#define MAX_FILE_ZONES                            (TERABYTE / DEFAULT_FILE_ZONE_SIZE)

// XXX: I increased this from 65536; make sure it's actually needed.
#define COROUTINE_STACK_SIZE                      131072

#define MAX_COROS_PER_THREAD                      10000

// TODO: It would be nice if we didn't need MAX_HOSTNAME_LEN and
// MAX_PATH_LEN.. just because we're storing stuff in the database.

// Maximum length of a hostname we communicate with
#define MAX_HOSTNAME_LEN                          100

//max length of a path that we have to store during run time
#define MAX_PATH_LEN                              200

// Size of a cache line (used in cache_line_padded_t).
#define CACHE_LINE_SIZE                           64

#endif // __CONFIG_ARGS_H__

<commit_msg>Change of default parameter value: heartbeat timeout is now 30s<commit_after>
#ifndef __CONFIG_ARGS_H__
#define __CONFIG_ARGS_H__

#define KILOBYTE 1024L
#define MEGABYTE (KILOBYTE*1024L)
#define GIGABYTE (MEGABYTE*1024L)
#define TERABYTE (GIGABYTE*1024L)

/*!
 * Version strings
 */

#define SOFTWARE_NAME_STRING "RethinkDB"
#define VERSION_STRING "0.2"

/**
 * Basic configuration parameters.
 * TODO: Many of these should be runtime switches.
 */
// Max concurrent IO requests per event queue
#define MAX_CONCURRENT_IO_REQUESTS                64

// Don't send more IO requests to the system until the per-thread
// queue of IO requests is higher than this depth
#define TARGET_IO_QUEUE_DEPTH                     64

// Defines the maximum size of the batch of IO events to process on
// each loop iteration. A larger number will increase throughput but
// decrease concurrency
#define MAX_IO_EVENT_PROCESSING_BATCH_SIZE        50

// Currently, each cache uses two IO accounts:
// one account for writes, and one account for reads.
// By adjusting the priorities of these accounts, reads
// can be prioritized over writes or the other way around.
//
// This is a one-per-serializer/file priority.
// The per-cache priorities are dynamically derived by dividing these priorities
// by the number of slices on a specific file.
#define CACHE_READS_IO_PRIORITY                   2048
#define CACHE_WRITES_IO_PRIORITY                  128

// Garbage Colletion uses its own two IO accounts.
// There is one low-priority account that is meant to guarantee
// (performance-wise) unintrusive garbage collection.
// If the garbage ratio keeps growing,
// GC starts using the high priority account instead, which
// might have a negative influence on database performance
// under i/o heavy workloads but guarantees that the database
// doesn't grow indefinitely.
//
// This is a one-per-serializer/file priority.
#define GC_IO_PRIORITY_NICE                       16
#define GC_IO_PRIORITY_HIGH                       (2 * CACHE_WRITES_IO_PRIORITY)

// Size of the buffer used to perform IO operations (in bytes).
#define IO_BUFFER_SIZE                            (4 * KILOBYTE)

// Size of the device block size (in bytes)
#define DEVICE_BLOCK_SIZE                         (4 * KILOBYTE)

// Size of each btree node (in bytes) on disk
#define DEFAULT_BTREE_BLOCK_SIZE                  (4 * KILOBYTE)

// Maximum number of data blocks
#define MAX_DATA_EXTENTS                          (TERABYTE / (16 * KILOBYTE))

// Size of each extent (in bytes)
#define DEFAULT_EXTENT_SIZE                       (8 * MEGABYTE)

// Max number of blocks which can be read ahead in one i/o transaction (if enabled)
#define MAX_READ_AHEAD_BLOCKS 32

// Max size of log file name
#define MAX_LOG_FILE_NAME                         1024

// Max length of log message, including terminating \0
#define MAX_LOG_MSGLEN                            1024

// Queue ID of logging worker
#define LOG_WORKER 0

// Ratio of free ram to use for the cache by default
#define DEFAULT_MAX_CACHE_RATIO                   0.7f

// Maximum number of threads we support
// TODO: make this dynamic where possible
#define MAX_THREADS                                  128

// Maximum slices total
#define MAX_SLICES                                128

// Maximum number of files we use
#define MAX_SERIALIZERS                           32

// The number of ways we split a BTree (the most optimal is the number
// of cores, but we use a higher split factor to allow upgrading to
// more cores without migrating the database file).
#define DEFAULT_BTREE_SHARD_FACTOR                64

// If --diff-log-size is not specified, then the patch log size will default to the
// smaller of DEFAULT_PATCH_LOG_SIZE and (DEFAULT_PATCH_LOG_FRACTION * cache size).
#ifdef NDEBUG
#define DEFAULT_PATCH_LOG_SIZE                     (512 * MEGABYTE)
#else
#define DEFAULT_PATCH_LOG_SIZE                     (4 * MEGABYTE)
#endif
#define DEFAULT_PATCH_LOG_FRACTION                 0.2

// Default port to listen on
#define DEFAULT_LISTEN_PORT                       11211

// Default port to do replication on
#define DEFAULT_REPLICATION_PORT                  11319

#define DEFAULT_TOTAL_DELETE_QUEUE_LIMIT          GIGABYTE

// Heartbeat configuration...
// The interval at which heartbeats are sent (ms)
#define REPLICATION_HEARTBEAT_INTERVAL            800 // (so we can allow a timeout of 1000 if users like it risky)
// The default timeout. This is the time after which replication
// connections get terminated, if no activity has been observed.
#define DEFAULT_REPLICATION_HEARTBEAT_TIMEOUT     30000

// Default extension for the semantic file which is appended to the database name
#define DEFAULT_SEMANTIC_EXTENSION                ".semantic"

// Ticks (in milliseconds) the internal timed tasks are performed at
#define TIMER_TICKS_IN_MS                         5

// How many milliseconds to allow changes to sit in memory before flushing to disk
#define DEFAULT_FLUSH_TIMER_MS                    1000

// flush_waiting_threshold is the maximal number of transactions which can wait
// for a sync before a flush gets triggered on any single slice. As transactions only wait for
// sync with wait_for_flush enabled, this option plays a role only then.
#define DEFAULT_FLUSH_WAITING_THRESHOLD           8

// If wait_for_flush is true, concurrent flushing can be used to reduce the latency
// of each single flush. max_concurrent_flushes controls how many flushes can be active
// on a specific slice at any given time.
#define DEFAULT_MAX_CONCURRENT_FLUSHES            1

// If the size of the data affected by the current set of patches in a block is larger than
// block size / MAX_PATCHES_SIZE_RATIO, we flush the block instead of waiting for
// more patches to come.
// Note: An average write transaction under canonical workload leads to patches of about 75
// bytes of affected data.
// The actual value is continuously adjusted between MAX_PATCHES_SIZE_RATIO_MIN and
// MAX_PATCHES_SIZE_RATIO_MAX depending on how much the system is i/o bound
#define MAX_PATCHES_SIZE_RATIO_MIN                100
#define MAX_PATCHES_SIZE_RATIO_MAX                2
#define MAX_PATCHES_SIZE_RATIO_DURABILITY         5
#define RAISE_PATCHES_RATIO_AT_FRACTION_OF_UNSAVED_DATA_LIMIT 0.6

// If more than this many bytes of dirty data accumulate in the cache, then write
// transactions will be throttled.
// A value of 0 means that it will automatically be set to MAX_UNSAVED_DATA_LIMIT_FRACTION
// times the max cache size
#define DEFAULT_UNSAVED_DATA_LIMIT                4096 * MEGABYTE

// The unsaved data limit cannot exceed this fraction of the max cache size
#define MAX_UNSAVED_DATA_LIMIT_FRACTION           0.9

// We start flushing dirty pages as soon as we hit this fraction of the unsaved data limit
#define FLUSH_AT_FRACTION_OF_UNSAVED_DATA_LIMIT   0.2

// How many times the page replacement algorithm tries to find an eligible page before giving up.
// Note that (MAX_UNSAVED_DATA_LIMIT_FRACTION ** PAGE_REPL_NUM_TRIES) is the probability that the
// page replacement algorithm will succeed on a given try, and if that probability is less than 1/2
// then the page replacement algorithm will on average be unable to evict pages from the cache.
#define PAGE_REPL_NUM_TRIES                       10

// How large can the key be, in bytes?  This value needs to fit in a byte.
#define MAX_KEY_SIZE                              250

// Any values of this size or less will be directly stored in btree leaf nodes.
// Values greater than this size will be stored in overflow blocks. This value
// needs to fit in a byte.
#define MAX_IN_NODE_VALUE_SIZE                    250

// In addition to the value itself we could potentially store
// memcached flags, exptime, and a CAS value in the value contents, so
// we reserve space for that.
#define MAX_BTREE_VALUE_AUXILIARY_SIZE            (sizeof(btree_value) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint64_t))
#define MAX_BTREE_VALUE_SIZE                      (MAX_BTREE_VALUE_AUXILIARY_SIZE + MAX_IN_NODE_VALUE_SIZE)

// memcached specifies the maximum value size to be 1MB, but customers asked this to be much higher
#define MAX_VALUE_SIZE                            10 * MEGABYTE

// Values larger than this will be streamed in a set operation.
#define MAX_BUFFERED_SET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// Values larger than this will be streamed in a get operation
#define MAX_BUFFERED_GET_SIZE                     MAX_VALUE_SIZE // streaming is too slow for now, so we disable it completely

// If a single connection sends this many 'noreply' commands, the next command will
// have to wait until the first one finishes
#define MAX_CONCURRENT_QUERIES_PER_CONNECTION     500

//
#define MAX_CONCURRENT_QUEURIES_ON_IMPORT 1000

// How many timestamps we store in a leaf node.  We store the
// NUM_LEAF_NODE_EARLIER_TIMES+1 most-recent timestamps.
#define NUM_LEAF_NODE_EARLIER_TIMES               2

// Perform allocator GC every N milliseconds (the resolution is limited to TIMER_TICKS_IN_MS)
#define ALLOC_GC_INTERVAL_MS                      3000

//filenames for the database
#define DEFAULT_DB_FILE_NAME                      "rethinkdb_data"

// We assume there will never be more than this many blocks. The value
// is computed by dividing 1 TB by the smallest reasonable block size.
// This value currently fits in 32 bits, and so block_id_t and
// ser_block_id_t is a uint32_t.
#define MAX_BLOCK_ID                              (TERABYTE / KILOBYTE)

// We assume that there will never be more than this many blocks held in memory by the cache at
// any one time. The value is computed by dividing 50 GB by the smallest reasonable block size.
#define MAX_BLOCKS_IN_MEMORY                      (50 * GIGABYTE / KILOBYTE)

// Special block IDs.  These don't really belong here because they're
// more magic constants than tunable parameters.

// The btree superblock, which has a reference to the root node block
// id.
#define SUPERBLOCK_ID                             0
// HEY: This is kind of fragile because some patch disk storage code
// expects this value to be 1 (since the free list returns 1 the first
// time a block id is generated, or something).
#define MC_CONFIGBLOCK_ID                         (SUPERBLOCK_ID + 1)

// The ratio at which we should start GCing.  (HEY: What's the extra
// 0.000001 in MAX_GC_HIGH_RATIO for?  Is it because we told the user
// that 0.99 was too high?)
#define DEFAULT_GC_HIGH_RATIO                     0.65
#define MAX_GC_HIGH_RATIO                         0.990001

// The ratio at which we don't want to keep GC'ing.
#define DEFAULT_GC_LOW_RATIO                      0.5
#define MIN_GC_LOW_RATIO                          0.099999

// What's the maximum number of "young" extents we can have?
#define GC_YOUNG_EXTENT_MAX_SIZE                  50
// What's the definition of a "young" extent in microseconds?
#define GC_YOUNG_EXTENT_TIMELIMIT_MICROS          50000

// If the size of the LBA on a given disk exceeds LBA_MIN_SIZE_FOR_GC, then the fraction of the
// entries that are live and not garbage should be at least LBA_MIN_UNGARBAGE_FRACTION.
#define LBA_MIN_SIZE_FOR_GC                       (MEGABYTE * 20)
#define LBA_MIN_UNGARBAGE_FRACTION                0.15

// How many LBA structures to have for each file
#define LBA_SHARD_FACTOR                          16

// How many bytes of buffering space we can use per disk when reading the LBA. If it's set
// too high, then RethinkDB will eat a lot of memory at startup. This is bad because tcmalloc
// doesn't return memory to the OS. If it's set too low, startup will take a longer time.
#define LBA_READ_BUFFER_SIZE                      GIGABYTE

// How many different places in each file we should be writing to at once, not counting the
// metablock or LBA
#define MAX_ACTIVE_DATA_EXTENTS                   64
#define DEFAULT_ACTIVE_DATA_EXTENTS               1

// How many zones the serializer will divide a block device into
#define DEFAULT_FILE_ZONE_SIZE                    GIGABYTE
#define MAX_FILE_ZONES                            (TERABYTE / DEFAULT_FILE_ZONE_SIZE)

// XXX: I increased this from 65536; make sure it's actually needed.
#define COROUTINE_STACK_SIZE                      131072

#define MAX_COROS_PER_THREAD                      10000

// TODO: It would be nice if we didn't need MAX_HOSTNAME_LEN and
// MAX_PATH_LEN.. just because we're storing stuff in the database.

// Maximum length of a hostname we communicate with
#define MAX_HOSTNAME_LEN                          100

//max length of a path that we have to store during run time
#define MAX_PATH_LEN                              200

// Size of a cache line (used in cache_line_padded_t).
#define CACHE_LINE_SIZE                           64

#endif // __CONFIG_ARGS_H__

<|endoftext|>


<commit_before>/*
 * Copyright 2016 Andrei Pangin
 *
 * 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 <fstream>
#include <dlfcn.h>
#include "vmEntry.h"
#include "arguments.h"
#include "profiler.h"
#include "perfEvents.h"
#include "lockTracer.h"

JavaVM* VM::_vm;
jvmtiEnv* VM::_jvmti = NULL;
AsyncGetCallTrace VM::_asyncGetCallTrace;

void VM::init(JavaVM* vm, bool attach) {
    if (_jvmti != NULL) return;

    _vm = vm;
    _vm->GetEnv((void**)&_jvmti, JVMTI_VERSION_1_0);

    jvmtiCapabilities capabilities = {0};
    capabilities.can_generate_all_class_hook_events = 1;
    capabilities.can_get_bytecodes = 1;
    capabilities.can_get_constant_pool = 1;
    capabilities.can_get_source_file_name = 1;
    capabilities.can_get_line_numbers = 1;
    capabilities.can_generate_compiled_method_load_events = 1;
    capabilities.can_generate_monitor_events = 1;
    capabilities.can_tag_objects = 1;
    _jvmti->AddCapabilities(&capabilities);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.VMInit = VMInit;
    callbacks.VMDeath = Profiler::VMDeath;
    callbacks.ClassLoad = ClassLoad;
    callbacks.ClassPrepare = ClassPrepare;
    callbacks.CompiledMethodLoad = Profiler::CompiledMethodLoad;
    callbacks.CompiledMethodUnload = Profiler::CompiledMethodUnload;
    callbacks.DynamicCodeGenerated = Profiler::DynamicCodeGenerated;
    callbacks.ThreadStart = PerfEvents::ThreadStart;
    callbacks.ThreadEnd = PerfEvents::ThreadEnd;
    callbacks.MonitorContendedEnter = LockTracer::MonitorContendedEnter;
    callbacks.MonitorContendedEntered = LockTracer::MonitorContendedEntered;
    _jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);

    PerfEvents::init();

    _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(RTLD_DEFAULT, "AsyncGetCallTrace");
    if (_asyncGetCallTrace == NULL) {
        void* libjvm_handle = dlopen("libjvm.so", RTLD_NOW);
        if (!libjvm_handle) {
            std::cerr << "Failed to load libjvm.so: " << dlerror() << std::endl;
        }
	// try loading AsyncGetCallTrace after opening libjvm.so
        _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(libjvm_handle, "AsyncGetCallTrace");
    }

    if (attach) {
        loadAllMethodIDs(_jvmti);
        _jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
        _jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
    }
}

void VM::loadMethodIDs(jvmtiEnv* jvmti, jclass klass) {
    jint method_count;
    jmethodID* methods;
    if (jvmti->GetClassMethods(klass, &method_count, &methods) == 0) {
        jvmti->Deallocate((unsigned char*)methods);
    }
}

void VM::loadAllMethodIDs(jvmtiEnv* jvmti) {
    jint class_count;
    jclass* classes;
    if (jvmti->GetLoadedClasses(&class_count, &classes) == 0) {
        for (int i = 0; i < class_count; i++) {
            loadMethodIDs(jvmti, classes[i]);
        }
        jvmti->Deallocate((unsigned char*)classes);
    }
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    VM::init(vm, false);

    Arguments args(options);
    if (args.error()) {
        std::cerr << args.error().message() << std::endl;
        return -1;
    }

    Profiler::_instance.run(args);
    return 0;
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    VM::init(vm, true);

    Arguments args(options);
    if (args.error()) {
        std::cerr << args.error().message() << std::endl;
        return -1;
    }

    Profiler::_instance.run(args);
    return 0;
}

extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
    VM::init(vm, true);
    return JNI_VERSION_1_6;
}
<commit_msg>Document why trying to AsyncGetCallTrace from libjvm.so is needed<commit_after>/*
 * Copyright 2016 Andrei Pangin
 *
 * 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 <fstream>
#include <dlfcn.h>
#include "vmEntry.h"
#include "arguments.h"
#include "profiler.h"
#include "perfEvents.h"
#include "lockTracer.h"

JavaVM* VM::_vm;
jvmtiEnv* VM::_jvmti = NULL;
AsyncGetCallTrace VM::_asyncGetCallTrace;

void VM::init(JavaVM* vm, bool attach) {
    if (_jvmti != NULL) return;

    _vm = vm;
    _vm->GetEnv((void**)&_jvmti, JVMTI_VERSION_1_0);

    jvmtiCapabilities capabilities = {0};
    capabilities.can_generate_all_class_hook_events = 1;
    capabilities.can_get_bytecodes = 1;
    capabilities.can_get_constant_pool = 1;
    capabilities.can_get_source_file_name = 1;
    capabilities.can_get_line_numbers = 1;
    capabilities.can_generate_compiled_method_load_events = 1;
    capabilities.can_generate_monitor_events = 1;
    capabilities.can_tag_objects = 1;
    _jvmti->AddCapabilities(&capabilities);

    jvmtiEventCallbacks callbacks = {0};
    callbacks.VMInit = VMInit;
    callbacks.VMDeath = Profiler::VMDeath;
    callbacks.ClassLoad = ClassLoad;
    callbacks.ClassPrepare = ClassPrepare;
    callbacks.CompiledMethodLoad = Profiler::CompiledMethodLoad;
    callbacks.CompiledMethodUnload = Profiler::CompiledMethodUnload;
    callbacks.DynamicCodeGenerated = Profiler::DynamicCodeGenerated;
    callbacks.ThreadStart = PerfEvents::ThreadStart;
    callbacks.ThreadEnd = PerfEvents::ThreadEnd;
    callbacks.MonitorContendedEnter = LockTracer::MonitorContendedEnter;
    callbacks.MonitorContendedEntered = LockTracer::MonitorContendedEntered;
    _jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));

    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    _jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);

    PerfEvents::init();

    _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(RTLD_DEFAULT, "AsyncGetCallTrace");
    if (_asyncGetCallTrace == NULL) {
	// Unable to locate AsyncGetCallTrace, it is likely that JVM has been started
	// by JNI_CreateJavaVM() via dynamically loaded libjvm.so from a C/C++ program
        void* libjvm_handle = dlopen("libjvm.so", RTLD_NOW);
        if (!libjvm_handle) {
            std::cerr << "Failed to load libjvm.so: " << dlerror() << std::endl;
        }
	// Try loading AGCT after opening libjvm.so
        _asyncGetCallTrace = (AsyncGetCallTrace)dlsym(libjvm_handle, "AsyncGetCallTrace");
    }

    if (attach) {
        loadAllMethodIDs(_jvmti);
        _jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
        _jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
    }
}

void VM::loadMethodIDs(jvmtiEnv* jvmti, jclass klass) {
    jint method_count;
    jmethodID* methods;
    if (jvmti->GetClassMethods(klass, &method_count, &methods) == 0) {
        jvmti->Deallocate((unsigned char*)methods);
    }
}

void VM::loadAllMethodIDs(jvmtiEnv* jvmti) {
    jint class_count;
    jclass* classes;
    if (jvmti->GetLoadedClasses(&class_count, &classes) == 0) {
        for (int i = 0; i < class_count; i++) {
            loadMethodIDs(jvmti, classes[i]);
        }
        jvmti->Deallocate((unsigned char*)classes);
    }
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
    VM::init(vm, false);

    Arguments args(options);
    if (args.error()) {
        std::cerr << args.error().message() << std::endl;
        return -1;
    }

    Profiler::_instance.run(args);
    return 0;
}

extern "C" JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
    VM::init(vm, true);

    Arguments args(options);
    if (args.error()) {
        std::cerr << args.error().message() << std::endl;
        return -1;
    }

    Profiler::_instance.run(args);
    return 0;
}

extern "C" JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM* vm, void* reserved) {
    VM::init(vm, true);
    return JNI_VERSION_1_6;
}
<|endoftext|>


<commit_before>/* Copyright (C) 2005-2008 Damien Stehle.
   Copyright (C) 2007 David Cade.
   Copyright (C) 2011 Xavier Pujol.

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

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

   You should have received a copy of the GNU Lesser General Public License
   along with fplll. If not, see <http://www.gnu.org/licenses/>. */

#include "util.h"
#include "lll.h"
#include "wrapper.h"

FPLLL_BEGIN_NAMESPACE

/* prec=53, eta=0.501, dim < dim_double_max [ (delta / 100.0) + 25 ] */
const double dim_double_max[75]=
  {0,26,29.6,28.1,31.1,32.6,34.6,34,37.7,38.8,39.6,41.8,40.9,43.6,44.2,47,46.8,
   50.6,49.1,51.5,52.5,54.8,54.6,57.4,57.6,59.9,61.8,62.3,64.5,67.1,68.8,68.3,
   69.9,73.1,74,76.1,76.8,80.9,81.8,83,85.3,87.9,89,90.1,89,94.6,94.8,98.7,99,
   101.6,104.9,106.8,108.2,107.4,110,112.7,114.6,118.1,119.7,121.8,122.9,126.6,
   128.6,129,133.6,126.9,135.9,139.5,135.2,137.2,139.3,142.8,142.4,142.5,145.4};

const double eta_dep[10]=
  {1.,//0.5
   1.,//0.55
   1.0521,//0.6
   1.1254,//0.65
   1.2535,//0.7
   1.3957,//0.75
   1.6231,//0.8
   1.8189,//0.85
   2.1025,//0.9
   2.5117};//0.95

Wrapper::Wrapper(IntMatrix& b, IntMatrix& u, IntMatrix& uInv,
                 double delta, double eta, int flags) :
  status(RED_SUCCESS), b(b), u(u), uInv(uInv), delta(delta), eta(eta),
  useLong(false), lastEarlyRed(0)
{
  n = b.getCols();
  d = b.getRows();
  this->flags = flags;
  maxExponent = b.getMaxExp() + (int) ceil(0.5 * log2((double) d * n));

  // Computes the parameters required for the proved version
  goodPrec = l2MinPrec(d, delta, eta, LLL_DEF_EPSILON);
}

bool Wrapper::little(int kappa, int precision) {
  /*one may add here dimension arguments with respect to eta and delta */
  int dm=(int)(delta*100.-25.);
  if (dm<0) dm=0;
  if (dm>74) dm=74;

  int em=(int) ((eta-0.5)*20);
  if (em<0) em=0;
  if (em>9) em=9;

  double p = max(1.0, precision / 53.0);

  p *= eta_dep[em]; /* eta dependance */
  p *= dim_double_max[dm];
  //cerr << kappa << " compared to " << p << endl;
  return kappa < p;
}

template<class Z, class F>
int Wrapper::callLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz, ZZ_mat<Z>& uInvZ,
        LLLMethod method, int precision, double delta, double eta) {
  typedef Z_NR<Z> ZT;
  typedef FP_NR<F> FT;

  if (flags & LLL_VERBOSE) {
    cerr << "====== Wrapper: calling " << LLL_METHOD_STR[method] << "<"
         << numTypeStr<Z>() << "," << numTypeStr<F>() << "> method";
    if (precision > 0) {
      cerr << " (precision=" << precision << ")";
    }
    cerr << " ======" << endl;
  }

  int gsoFlags = 0;
  if (method == LM_PROVED) gsoFlags |= GSO_INT_GRAM;
  if (method == LM_FAST) gsoFlags |= GSO_ROW_EXPO;
  if (method != LM_PROVED && precision == 0) gsoFlags |= GSO_OP_FORCE_LONG;

  int oldprec = Float::getprec();
  if (precision > 0) {
    Float::setprec(precision);
  }
  MatGSO<ZT, FT> mGSO(bz, uz, uInvZ, gsoFlags);
  LLLReduction<ZT, FT> lllObj(mGSO, delta, eta, flags);
  lllObj.lastEarlyRed = lastEarlyRed;
  lllObj.lll();
  status = lllObj.status;
  lastEarlyRed = max(lastEarlyRed, lllObj.lastEarlyRed);
  if (precision > 0) {
    Float::setprec(oldprec);
  }

  if (flags & LLL_VERBOSE) {
    cerr << "====== Wrapper: end of " << LLL_METHOD_STR[method]
        << " method ======\n" << endl;
  }

  if (lllObj.status == RED_SUCCESS)
    return 0;
  else if (lllObj.status == RED_GSO_FAILURE
            || lllObj.status == RED_BABAI_FAILURE)
    return lllObj.finalKappa;
  else
    return -1;
}

template<class F>
int Wrapper::fastLLL(double delta, double eta) {
  return callLLL<mpz_t, F>(b, u, uInv, LM_FAST, 0, delta, eta);
}

template<class Z, class F>
int Wrapper::heuristicLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz,
                          ZZ_mat<Z>& uInvZ, int precision,
                          double delta, double eta) {
  return callLLL<Z, F>(bz, uz, uInvZ, LM_HEURISTIC, precision, delta, eta);
}

template<class Z, class F>
int Wrapper::provedLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz,
                       ZZ_mat<Z>& uInvZ, int precision,
                       double delta, double eta) {
  return callLLL<Z, F>(bz, uz, uInvZ, LM_PROVED, precision, delta, eta);
}

int Wrapper::heuristicLoop(int precision) {
  int kappa;

  if (precision > numeric_limits<double>::digits)
    kappa = heuristicLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
  else {
#ifdef FPLLL_WITH_DPE
    kappa = heuristicLLL<mpz_t, dpe_t>(b, u, uInv, 0, delta, eta);
#else
    kappa = heuristicLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
#endif
  }

  if (kappa == 0)
    return 0; // Success
  else if (precision < goodPrec && !little(kappa, precision))
    return heuristicLoop(increasePrec(precision));
  else
    return provedLoop(precision);
}

int Wrapper::provedLoop(int precision) {
  int kappa;

  if (precision > numeric_limits<double>::digits)
    kappa = provedLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
  else if (maxExponent * 2 > MAX_EXP_DOUBLE) {
#ifdef FPLLL_WITH_DPE
    kappa = provedLLL<mpz_t, dpe_t>(b, u, uInv, 0, delta, eta);
#else
    kappa = provedLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
#endif
  }
  else
    kappa = provedLLL<mpz_t, double>(b, u, uInv, 0, delta, eta);

  if (kappa == 0)
    return 0; // Success
  else if (precision < goodPrec)
    return provedLoop(increasePrec(precision));
  else
    return -1; // This point should never be reached
}

int Wrapper::lastLLL() {
#ifdef FPLLL_WITH_ZLONG
  if (useLong) {
    int kappa;
    if (goodPrec <= numeric_limits<double>::digits)
      kappa = provedLLL<long, double>(bLong, uLong, uInvLong, goodPrec, delta, eta);
    else
      kappa = provedLLL<long, mpfr_t>(bLong, uLong, uInvLong, goodPrec, delta, eta);
    return kappa;
  }
#endif
#ifdef FPLLL_WITH_DPE
  if (goodPrec <= numeric_limits<double>::digits) {
    return provedLLL<mpz_t, dpe_t>(b, u, uInv, goodPrec, delta, eta);
  }
#endif
  return provedLLL<mpz_t, mpfr_t>(b, u, uInv, goodPrec, delta, eta);
}

bool Wrapper::lll() {
  if (b.getRows() == 0 || b.getCols() == 0) return RED_SUCCESS;

#ifdef FPLLL_WITH_ZLONG
  bool heuristicWithLong = maxExponent < numeric_limits<long>::digits - 2
            && u.empty() && uInv.empty();
  bool provedWithLong = 2 * maxExponent < numeric_limits<long>::digits - 2
            && u.empty() && uInv.empty();
#else
  bool heuristicWithLong = false, provedWithLong = false;
#endif

  int kappa;

  if (heuristicWithLong) {
#ifdef FPLLL_WITH_ZLONG
    setUseLong(true);
    heuristicLLL<long, double>(bLong, uLong, uInvLong, 0, delta, eta);
#endif
  }
  else {
    kappa = fastLLL<double>(delta, eta);
    bool lllFailure = (kappa != 0);

#ifdef FPLLL_WITH_LONG_DOUBLE
    if (lllFailure) {
      kappa = fastLLL<long double>(delta, eta);
      lllFailure = kappa != 0;
    }
    int lastPrec = numeric_limits<long double>::digits;
#else
    int lastPrec = numeric_limits<double>::digits;
#endif

    if (lllFailure) {
      int precD = numeric_limits<double>::digits;
      if (little(kappa, lastPrec))
        kappa = provedLoop(precD);
      else
        kappa = heuristicLoop(increasePrec(precD));
    }
  }

  setUseLong(provedWithLong);
  kappa = lastLLL();
  setUseLong(false);
  return kappa == 0;
}

void Wrapper::setUseLong(bool value) {
#ifdef FPLLL_WITH_ZLONG
  if (!useLong && value) {
    if (bLong.empty()) {
      bLong.resize(d, n);
    }
    for (int i = 0; i < d; i++)
      for (int j = 0; j < n; j++)
        bLong(i, j) = b(i, j).get_si();
  }
  else if (useLong && !value) {
    for (int i = 0; i < d; i++)
      for (int j = 0; j < n; j++)
        b(i, j) = bLong(i, j).get_si();
  }
  useLong = value;
#endif
}

int Wrapper::increasePrec(int precision) {
  return min(precision * 2, goodPrec);
}

FPLLL_END_NAMESPACE
<commit_msg>documentation for wrapper.cpp<commit_after>/* Copyright (C) 2005-2008 Damien Stehle.
   Copyright (C) 2007 David Cade.
   Copyright (C) 2011 Xavier Pujol.

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

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

   You should have received a copy of the GNU Lesser General Public License
   along with fplll. If not, see <http://www.gnu.org/licenses/>. */

#include "util.h"
#include "lll.h"
#include "wrapper.h"

FPLLL_BEGIN_NAMESPACE

/* prec=53, eta=0.501, dim < dim_double_max [ (delta / 100.0) + 25 ] */
const double dim_double_max[75]=
  {0,26,29.6,28.1,31.1,32.6,34.6,34,37.7,38.8,39.6,41.8,40.9,43.6,44.2,47,46.8,
   50.6,49.1,51.5,52.5,54.8,54.6,57.4,57.6,59.9,61.8,62.3,64.5,67.1,68.8,68.3,
   69.9,73.1,74,76.1,76.8,80.9,81.8,83,85.3,87.9,89,90.1,89,94.6,94.8,98.7,99,
   101.6,104.9,106.8,108.2,107.4,110,112.7,114.6,118.1,119.7,121.8,122.9,126.6,
   128.6,129,133.6,126.9,135.9,139.5,135.2,137.2,139.3,142.8,142.4,142.5,145.4};

const double eta_dep[10]=
  {1.,//0.5
   1.,//0.55
   1.0521,//0.6
   1.1254,//0.65
   1.2535,//0.7
   1.3957,//0.75
   1.6231,//0.8
   1.8189,//0.85
   2.1025,//0.9
   2.5117};//0.95

Wrapper::Wrapper(IntMatrix& b, IntMatrix& u, IntMatrix& uInv,
                 double delta, double eta, int flags) :
  status(RED_SUCCESS), b(b), u(u), uInv(uInv), delta(delta), eta(eta),
  useLong(false), lastEarlyRed(0)
{
  n = b.getCols();
  d = b.getRows();
  this->flags = flags;
  maxExponent = b.getMaxExp() + (int) ceil(0.5 * log2((double) d * n));

  // Computes the parameters required for the proved version
  goodPrec = l2MinPrec(d, delta, eta, LLL_DEF_EPSILON);
}

bool Wrapper::little(int kappa, int precision) {
  /*one may add here dimension arguments with respect to eta and delta */
  int dm=(int)(delta*100.-25.);
  if (dm<0) dm=0;
  if (dm>74) dm=74;

  int em=(int) ((eta-0.5)*20);
  if (em<0) em=0;
  if (em>9) em=9;

  double p = max(1.0, precision / 53.0);

  p *= eta_dep[em]; /* eta dependance */
  p *= dim_double_max[dm];
  //cerr << kappa << " compared to " << p << endl;
  return kappa < p;
}

/**
 * main function. Method determines whether heuristic, fast or proved
 */
template<class Z, class F>
int Wrapper::callLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz, ZZ_mat<Z>& uInvZ,
        LLLMethod method, int precision, double delta, double eta) {
  typedef Z_NR<Z> ZT;
  typedef FP_NR<F> FT;

  if (flags & LLL_VERBOSE) {
    cerr << "====== Wrapper: calling " << LLL_METHOD_STR[method] << "<"
         << numTypeStr<Z>() << "," << numTypeStr<F>() << "> method";
    if (precision > 0) {
      cerr << " (precision=" << precision << ")";
    }
    cerr << " ======" << endl;
  }

  int gsoFlags = 0;
  if (method == LM_PROVED) gsoFlags |= GSO_INT_GRAM;
  if (method == LM_FAST) gsoFlags |= GSO_ROW_EXPO;
  if (method != LM_PROVED && precision == 0) gsoFlags |= GSO_OP_FORCE_LONG;

  int oldprec = Float::getprec();
  if (precision > 0) {
    Float::setprec(precision);
  }
  MatGSO<ZT, FT> mGSO(bz, uz, uInvZ, gsoFlags);
  LLLReduction<ZT, FT> lllObj(mGSO, delta, eta, flags);
  lllObj.lastEarlyRed = lastEarlyRed;
  lllObj.lll();
  status = lllObj.status;
  lastEarlyRed = max(lastEarlyRed, lllObj.lastEarlyRed);
  if (precision > 0) {
    Float::setprec(oldprec);
  }

  if (flags & LLL_VERBOSE) {
    cerr << "====== Wrapper: end of " << LLL_METHOD_STR[method]
        << " method ======\n" << endl;
  }

  if (lllObj.status == RED_SUCCESS)
    return 0;
  else if (lllObj.status == RED_GSO_FAILURE
            || lllObj.status == RED_BABAI_FAILURE)
    return lllObj.finalKappa;
  else
    return -1;
}

/**
 * pass the method to callLLL()
 */
template<class F>
int Wrapper::fastLLL(double delta, double eta) {
  return callLLL<mpz_t, F>(b, u, uInv, LM_FAST, 0, delta, eta);
}

template<class Z, class F>
int Wrapper::heuristicLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz,
                          ZZ_mat<Z>& uInvZ, int precision,
                          double delta, double eta) {
  return callLLL<Z, F>(bz, uz, uInvZ, LM_HEURISTIC, precision, delta, eta);
}

template<class Z, class F>
int Wrapper::provedLLL(ZZ_mat<Z>& bz, ZZ_mat<Z>& uz,
                       ZZ_mat<Z>& uInvZ, int precision,
                       double delta, double eta) {
  return callLLL<Z, F>(bz, uz, uInvZ, LM_PROVED, precision, delta, eta);
}

/**
 * In heuristicLoop(), we only use double or dpe_t or mpfr_t.
 */
int Wrapper::heuristicLoop(int precision) {
  int kappa;

  if (precision > numeric_limits<double>::digits)
    kappa = heuristicLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
  else {
#ifdef FPLLL_WITH_DPE
    kappa = heuristicLLL<mpz_t, dpe_t>(b, u, uInv, 0, delta, eta);
#else
    kappa = heuristicLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
#endif
  }

  if (kappa == 0)
    return 0; // Success
  else if (precision < goodPrec && !little(kappa, precision))
    return heuristicLoop(increasePrec(precision));
  else
    return provedLoop(precision);
}

int Wrapper::provedLoop(int precision) {
  int kappa;

  if (precision > numeric_limits<double>::digits)
    kappa = provedLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
  else if (maxExponent * 2 > MAX_EXP_DOUBLE) {
#ifdef FPLLL_WITH_DPE
    kappa = provedLLL<mpz_t, dpe_t>(b, u, uInv, 0, delta, eta);
#else
    kappa = provedLLL<mpz_t, mpfr_t>(b, u, uInv, precision, delta, eta);
#endif
  }
  else
    kappa = provedLLL<mpz_t, double>(b, u, uInv, 0, delta, eta);

  if (kappa == 0)
    return 0; // Success
  else if (precision < goodPrec)
    return provedLoop(increasePrec(precision));
  else
    return -1; // This point should never be reached
}

/**
 * last call to LLL. Need to be provedLLL.
 */
int Wrapper::lastLLL() {

  /* <long, FT> */
#ifdef FPLLL_WITH_ZLONG
  if (useLong) {
    int kappa;
    if (goodPrec <= numeric_limits<double>::digits)
      kappa = provedLLL<long, double>(bLong, uLong, uInvLong, goodPrec, delta, eta);
    else
      kappa = provedLLL<long, mpfr_t>(bLong, uLong, uInvLong, goodPrec, delta, eta);
    return kappa;
  }
#endif

  /* <mpfr, FT> */
#ifdef FPLLL_WITH_DPE
  if (goodPrec <= numeric_limits<double>::digits) {
    return provedLLL<mpz_t, dpe_t>(b, u, uInv, goodPrec, delta, eta);
  }
#endif
  return provedLLL<mpz_t, mpfr_t>(b, u, uInv, goodPrec, delta, eta);
}

/**
 * Wrapper.lll() calls
 *  - heuristicLLL()
 *  - fastLLL()
 *  - provedLLL()
 */
bool Wrapper::lll() {
  if (b.getRows() == 0 || b.getCols() == 0)
    return RED_SUCCESS;

#ifdef FPLLL_WITH_ZLONG
  bool heuristicWithLong = maxExponent < numeric_limits<long>::digits - 2
            && u.empty() && uInv.empty();
  bool provedWithLong = 2 * maxExponent < numeric_limits<long>::digits - 2
            && u.empty() && uInv.empty();
#else
  bool heuristicWithLong = false, provedWithLong = false;
#endif

  int kappa;

  /* small matrix */
  if (heuristicWithLong) {
#ifdef FPLLL_WITH_ZLONG
    setUseLong(true);
    /* try heuristicLLL <long, double> */
    heuristicLLL<long, double>(bLong, uLong, uInvLong, 0, delta, eta);
#endif
  }
  /* large matrix */
  else {

    /* try fastLLL<mpz_t, double> */
    kappa = fastLLL<double>(delta, eta);
    bool lllFailure = (kappa != 0);

    /* try fastLLL<mpz_t, long double> */
#ifdef FPLLL_WITH_LONG_DOUBLE
    if (lllFailure) {
      kappa = fastLLL<long double>(delta, eta);
      lllFailure = kappa != 0;
    }
    int lastPrec = numeric_limits<long double>::digits;
#else
    int lastPrec = numeric_limits<double>::digits;
#endif

    /* loop */
    if (lllFailure) {
      int precD = numeric_limits<double>::digits;
      if (little(kappa, lastPrec))
        kappa = provedLoop(precD);
      else
        kappa = heuristicLoop(increasePrec(precD));
    }
  }

  setUseLong(provedWithLong);
  /* final LLL */
  kappa = lastLLL();
  setUseLong(false);
  return kappa == 0;
}

/**
 * set blong <-- b
 */
void Wrapper::setUseLong(bool value) {
#ifdef FPLLL_WITH_ZLONG
  if (!useLong && value) {
    if (bLong.empty()) {
      bLong.resize(d, n);
    }
    for (int i = 0; i < d; i++)
      for (int j = 0; j < n; j++)
        bLong(i, j) = b(i, j).get_si();
  }
  else if (useLong && !value) {
    for (int i = 0; i < d; i++)
      for (int j = 0; j < n; j++)
        b(i, j) = bLong(i, j).get_si();
  }
  useLong = value;
#endif
}

int Wrapper::increasePrec(int precision) {
  return min(precision * 2, goodPrec);
}

FPLLL_END_NAMESPACE
<|endoftext|>


<commit_before>/******************************************************************************
 *
 * This file is part of Log4Qt library.
 *
 * Copyright (C) 2007 - 2020 Log4Qt contributors
 *
 * 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 "writerappender.h"

#include "layout.h"
#include "loggingevent.h"

#if QT_VERSION < 0x060000
#include <QTextCodec>
#endif

namespace Log4Qt
{

WriterAppender::WriterAppender(QObject *parent) :
    AppenderSkeleton(false, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::Utf8),
#endif
    mWriter(nullptr),
    mImmediateFlush(true)
{
}

WriterAppender::WriterAppender(const LayoutSharedPtr &layout,
                               QObject *parent) :
    AppenderSkeleton(false, layout, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::System),
#endif
    mWriter(nullptr),
    mImmediateFlush(true)
{
}

WriterAppender::WriterAppender(const LayoutSharedPtr &layout,
                               QTextStream *textStream,
                               QObject *parent) :
    AppenderSkeleton(false, layout, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::System),
#endif
    mWriter(textStream),
    mImmediateFlush(true)
{
}

WriterAppender::~WriterAppender()
{
    closeInternal();
}

#if QT_VERSION < 0x060000
void WriterAppender::setEncoding(QTextCodec *encoding)
{
    QMutexLocker locker(&mObjectGuard);

    if (mEncoding == encoding)
        return;

    mEncoding = encoding;
    if (mWriter != nullptr)
    {
        if (mEncoding != nullptr)
            mWriter->setCodec(mEncoding);

        mWriter->setCodec(QTextCodec::codecForLocale());
    }
}
#else
void WriterAppender::setEncoding(QStringConverter::Encoding encoding)
{
    QMutexLocker locker(&mObjectGuard);

    mEncoding = encoding;
    if (mWriter != nullptr)
        mWriter->setEncoding(mEncoding);
}
#endif

void WriterAppender::setWriter(QTextStream *textStream)
{
    QMutexLocker locker(&mObjectGuard);

    closeWriter();

    mWriter = textStream;
#if QT_VERSION < 0x060000
    if ((mEncoding != nullptr) && (mWriter != nullptr))
        mWriter->setCodec(mEncoding);
#else
    if (mWriter != nullptr)
        mWriter->setEncoding(mEncoding);
#endif
    writeHeader();
}

void WriterAppender::activateOptions()
{
    QMutexLocker locker(&mObjectGuard);

    if (writer() == nullptr)
    {
        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Activation of Appender '%1' that requires writer and has no writer set"),
                                         APPENDER_ACTIVATE_MISSING_WRITER_ERROR);
        e << name();
        logger()->error(e);
        return;
    }
    AppenderSkeleton::activateOptions();
}

void WriterAppender::close()
{
    closeInternal();
    AppenderSkeleton::close();
}

void WriterAppender::closeInternal()
{
    QMutexLocker locker(&mObjectGuard);

    if (isClosed())
        return;

    closeWriter();
}

bool WriterAppender::requiresLayout() const
{
    return true;
}

void WriterAppender::append(const LoggingEvent &event)
{
    Q_ASSERT_X(layout(), "WriterAppender::append()", "Layout must not be null");

    QString message(layout()->format(event));

    *mWriter << message;
    if (handleIoErrors())
        return;

    if (immediateFlush())
    {
        mWriter->flush();
        if (handleIoErrors())
            return;
    }
}

bool WriterAppender::checkEntryConditions() const
{
    if (writer() == nullptr)
    {
        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Use of appender '%1' without a writer set"),
                                         APPENDER_USE_MISSING_WRITER_ERROR);
        e << name();
        logger()->error(e);
        return false;
    }

    return AppenderSkeleton::checkEntryConditions();
}

void WriterAppender::closeWriter()
{
    if (mWriter == nullptr)
        return;

    writeFooter();
    mWriter = nullptr;
}

bool WriterAppender::handleIoErrors() const
{
    return false;
}

void WriterAppender::writeFooter() const
{
    if (!layout() || (mWriter == nullptr))
        return;

    QString footer = layout()->footer();
    if (footer.isEmpty())
        return;

    *mWriter << footer << Layout::endOfLine();
    if (handleIoErrors())
        return;
}

void WriterAppender::writeHeader() const
{
    if (!layout() || (mWriter == nullptr))
        return;

    QString header = layout()->header();
    if (header.isEmpty())
        return;

    *mWriter << header << Layout::endOfLine();
    if (handleIoErrors())
        return;
}

} // namespace Log4Qt

#include "moc_writerappender.cpp"
<commit_msg>Fix setEncoding in WriterAppender<commit_after>/******************************************************************************
 *
 * This file is part of Log4Qt library.
 *
 * Copyright (C) 2007 - 2020 Log4Qt contributors
 *
 * 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 "writerappender.h"

#include "layout.h"
#include "loggingevent.h"

#if QT_VERSION < 0x060000
#include <QTextCodec>
#endif

namespace Log4Qt
{

WriterAppender::WriterAppender(QObject *parent) :
    AppenderSkeleton(false, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::Utf8),
#endif
    mWriter(nullptr),
    mImmediateFlush(true)
{
}

WriterAppender::WriterAppender(const LayoutSharedPtr &layout,
                               QObject *parent) :
    AppenderSkeleton(false, layout, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::System),
#endif
    mWriter(nullptr),
    mImmediateFlush(true)
{
}

WriterAppender::WriterAppender(const LayoutSharedPtr &layout,
                               QTextStream *textStream,
                               QObject *parent) :
    AppenderSkeleton(false, layout, parent),
#if QT_VERSION < 0x060000
    mEncoding(nullptr),
#else
    mEncoding(QStringConverter::Encoding::System),
#endif
    mWriter(textStream),
    mImmediateFlush(true)
{
}

WriterAppender::~WriterAppender()
{
    closeInternal();
}

#if QT_VERSION < 0x060000
void WriterAppender::setEncoding(QTextCodec *encoding)
#else
void WriterAppender::setEncoding(QStringConverter::Encoding encoding)
#endif
{
    QMutexLocker locker(&mObjectGuard);
    if (mEncoding == encoding)
        return;

    mEncoding = encoding;
    if (mWriter != nullptr)
    {
#if QT_VERSION < 0x060000
        if (mEncoding != nullptr)
            mWriter->setCodec(mEncoding);
#else
        mWriter->setEncoding(mEncoding);
#endif
    }
}

void WriterAppender::setWriter(QTextStream *textStream)
{
    QMutexLocker locker(&mObjectGuard);

    closeWriter();

    mWriter = textStream;
#if QT_VERSION < 0x060000
    if ((mEncoding != nullptr) && (mWriter != nullptr))
        mWriter->setCodec(mEncoding);
#else
    if (mWriter != nullptr)
        mWriter->setEncoding(mEncoding);
#endif
    writeHeader();
}

void WriterAppender::activateOptions()
{
    QMutexLocker locker(&mObjectGuard);

    if (writer() == nullptr)
    {
        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Activation of Appender '%1' that requires writer and has no writer set"),
                                         APPENDER_ACTIVATE_MISSING_WRITER_ERROR);
        e << name();
        logger()->error(e);
        return;
    }
    AppenderSkeleton::activateOptions();
}

void WriterAppender::close()
{
    closeInternal();
    AppenderSkeleton::close();
}

void WriterAppender::closeInternal()
{
    QMutexLocker locker(&mObjectGuard);

    if (isClosed())
        return;

    closeWriter();
}

bool WriterAppender::requiresLayout() const
{
    return true;
}

void WriterAppender::append(const LoggingEvent &event)
{
    Q_ASSERT_X(layout(), "WriterAppender::append()", "Layout must not be null");

    QString message(layout()->format(event));

    *mWriter << message;
    if (handleIoErrors())
        return;

    if (immediateFlush())
    {
        mWriter->flush();
        if (handleIoErrors())
            return;
    }
}

bool WriterAppender::checkEntryConditions() const
{
    if (writer() == nullptr)
    {
        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Use of appender '%1' without a writer set"),
                                         APPENDER_USE_MISSING_WRITER_ERROR);
        e << name();
        logger()->error(e);
        return false;
    }

    return AppenderSkeleton::checkEntryConditions();
}

void WriterAppender::closeWriter()
{
    if (mWriter == nullptr)
        return;

    writeFooter();
    mWriter = nullptr;
}

bool WriterAppender::handleIoErrors() const
{
    return false;
}

void WriterAppender::writeFooter() const
{
    if (!layout() || (mWriter == nullptr))
        return;

    QString footer = layout()->footer();
    if (footer.isEmpty())
        return;

    *mWriter << footer << Layout::endOfLine();
    if (handleIoErrors())
        return;
}

void WriterAppender::writeHeader() const
{
    if (!layout() || (mWriter == nullptr))
        return;

    QString header = layout()->header();
    if (header.isEmpty())
        return;

    *mWriter << header << Layout::endOfLine();
    if (handleIoErrors())
        return;
}

} // namespace Log4Qt

#include "moc_writerappender.cpp"
<|endoftext|>