<commit_before>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2016 ETH Zurich
 ** All rights reserved.
 **
 ** 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.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** 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 COPYRIGHT HOLDER 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 "CommentedNode.h"

#include "ModelBase/src/nodes/composite/CompositeNode.h"

#include "ReviewComment.h"

#include "ModelBase/src/nodes/TypedList.hpp"

template class Model::TypedList<CodeReview::CommentedNode>;

namespace CodeReview
{
DEFINE_COMPOSITE_EMPTY_CONSTRUCTORS(CommentedNode)
DEFINE_COMPOSITE_TYPE_REGISTRATION_METHODS(CommentedNode)

DEFINE_ATTRIBUTE(CommentedNode, nodeId, Text, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, reviewComments, TypedListOfReviewComment, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, offsetX, Integer, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, offsetY, Integer, false, false, true)

CommentedNode::CommentedNode(QString associatedNodeId, QPoint offset) : Super{nullptr, CommentedNode::getMetaData()}
{
	setNodeId(new Model::Text{associatedNodeId});
	auto offsetX = new Model::Integer{};
	offsetX->set(offset.x());
	setOffsetX(offsetX);
	auto offsetY = new Model::Integer{};
	offsetY->set(offset.y());
	setOffsetY(offsetY);
}

}
<commit_msg>Remove instantiation of new Model::Integer for required attribute<commit_after>/***********************************************************************************************************************
 **
 ** Copyright (c) 2011, 2016 ETH Zurich
 ** All rights reserved.
 **
 ** 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.
 **    * Neither the name of the ETH Zurich nor the names of its contributors may be used to endorse or promote products
 **      derived from this software without specific prior written permission.
 **
 **
 ** 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 COPYRIGHT HOLDER 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 "CommentedNode.h"

#include "ModelBase/src/nodes/composite/CompositeNode.h"

#include "ReviewComment.h"

#include "ModelBase/src/nodes/TypedList.hpp"

template class Model::TypedList<CodeReview::CommentedNode>;

namespace CodeReview
{
DEFINE_COMPOSITE_EMPTY_CONSTRUCTORS(CommentedNode)
DEFINE_COMPOSITE_TYPE_REGISTRATION_METHODS(CommentedNode)

DEFINE_ATTRIBUTE(CommentedNode, nodeId, Text, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, reviewComments, TypedListOfReviewComment, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, offsetX, Integer, false, false, true)
DEFINE_ATTRIBUTE(CommentedNode, offsetY, Integer, false, false, true)

CommentedNode::CommentedNode(QString associatedNodeId, QPoint offset) : Super{nullptr, CommentedNode::getMetaData()}
{
	setNodeId(new Model::Text{associatedNodeId});
	offsetX()->set(offset.x());
	offsetY()->set(offset.y());
}

}
<|endoftext|>


<commit_before>//
// Created by Theo Weidmann on 26.02.18.
//

#include "SemanticAnalyser.hpp"
#include "Compiler.hpp"
#include "DeinitializerBuilder.hpp"
#include "FunctionAnalyser.hpp"
#include "AST/ASTExpr.hpp"
#include "Scoping/SemanticScoper.hpp"
#include "Types/TypeExpectation.hpp"
#include "Package/Package.hpp"
#include "ThunkBuilder.hpp"
#include "Types/Class.hpp"
#include "Types/Protocol.hpp"
#include "Types/TypeDefinition.hpp"
#include "Types/ValueType.hpp"

namespace EmojicodeCompiler {

Compiler* SemanticAnalyser::compiler() const {
    return package_->compiler();
}

void SemanticAnalyser::analyse(bool executable) {
    for (auto &vt : package_->valueTypes()) {
        finalizeProtocols(Type(vt.get()));
        vt->analyseConstraints(TypeContext(Type(vt.get())));
    }
    for (auto &klass : package_->classes()) {
        klass->analyseSuperType();
        finalizeProtocols(Type(klass.get()));
        klass->analyseConstraints(TypeContext(Type(klass.get())));
    }

    package_->recreateClassTypes();

    // Now all types are ready to be used with compatibleTo

    for (auto &protocol : package_->protocols()) {
        protocol->eachFunction([this](Function *function) {
            analyseFunctionDeclaration(function);
        });
    }
    for (auto &vt : package_->valueTypes()) {
        checkProtocolConformance(Type(vt.get()));
        declareInstanceVariables(Type(vt.get()));
        buildDeinitializer(vt.get());
        buildCopyRetain(vt.get());
        enqueueFunction(vt->deinitializer());
        enqueueFunction(vt->copyRetain());
        enqueueFunctionsOfTypeDefinition(vt.get());
    }
    for (auto &klass : package_->classes()) {
        for (auto init : klass->initializerList()) {
            if (!init->required()) {
                continue;
            }
            klass->addTypeMethod(buildRequiredInitThunk(klass.get(), init));
        }

        klass->inherit(this);
        checkProtocolConformance(Type(klass.get()));

        buildDeinitializer(klass.get());
        enqueueFunction(klass->deinitializer());

        enqueueFunctionsOfTypeDefinition(klass.get());

        if (!klass->hasSubclass() && !klass->exported()) {
            klass->setFinal();
        }
    }
    for (auto &function : package_->functions()) {
        enqueueFunction(function.get());
    }

    analyseQueue();
    checkStartFlagFunction(executable);
}

void SemanticAnalyser::checkStartFlagFunction(bool executable) {
    if (package_->hasStartFlagFunction()) {
        auto returnType = package_->startFlagFunction()->returnType()->type();
        if (returnType.type() != TypeType::NoReturn &&
            !returnType.compatibleTo(Type(package_->compiler()->sInteger), TypeContext())) {
            package_->compiler()->error(CompilerError(package_->startFlagFunction()->position(),
                                                      "🏁 must either have no return or return 🔢."));
        }
        if (!executable) {
            package_->startFlagFunction()->makeExternal();  // Prevent function from being included in object file
        }
    }
    else if (executable) {
        compiler()->error(CompilerError(SourcePosition(), "No 🏁 block was found."));
    }
}

void SemanticAnalyser::analyseQueue() {
    while (!queue_.empty()) {
        try {
            FunctionAnalyser(queue_.front(), this).analyse();
        }
        catch (CompilerError &ce) {
            package_->compiler()->error(ce);
        }
        queue_.pop();
    }
}

void SemanticAnalyser::enqueueFunctionsOfTypeDefinition(TypeDefinition *typeDef) {
    typeDef->eachFunction([this](Function *function) {
        enqueueFunction(function);
    });
}

void SemanticAnalyser::enqueueFunction(Function *function) {
    analyseFunctionDeclaration(function);
    if (!function->isExternal()) {
        queue_.emplace(function);
    }
}

void SemanticAnalyser::analyseFunctionDeclaration(Function *function) const {
    if (function->returnType() == nullptr) {
        function->setReturnType(std::make_unique<ASTLiteralType>(Type::noReturn()));
    }
    else if (function->returnType()->wasAnalysed()) {
        return;
    }

    auto context = function->typeContext();

    function->analyseConstraints(context);
    for (auto &param : function->parameters()) {
        param.type->analyseType(context);
        if (!function->externalName().empty() && param.type->type().type() == TypeType::ValueType &&
            !param.type->type().valueType()->isPrimitive()) {
            param.type->type().setReference();
        }
    }

    if (auto initializer = dynamic_cast<Initializer*>(function)) {
        if (initializer->errorProne() && initializer->errorType()->analyseType(context).type() != TypeType::Enum) {
            throw CompilerError(initializer->errorType()->position(), "Error type must be a non-optional 🦃.");
        }
        return;
    }
    function->returnType()->analyseType(context, true);
}

void SemanticAnalyser::declareInstanceVariables(const Type &type) {
    TypeDefinition *typeDef = type.typeDefinition();

    auto context = TypeContext(type);
    auto scoper = std::make_unique<SemanticScoper>();
    scoper->pushScope();  // For closure analysis
    ExpressionAnalyser analyser(this, context, package_, std::move(scoper));

    for (auto &var : typeDef->instanceVariables()) {
        typeDef->instanceScope().declareVariable(var.name, var.type->analyseType(context), false,
                                                 var.position);

        if (var.expr != nullptr) {
            auto type = var.expr->analyse(&analyser, TypeExpectation(var.type->type()));
            if (!type.compatibleTo(var.type->type(), context)) {
                package_->compiler()->error(CompilerError(var.expr->position(),
                                                          "Cannot initialize instance variable of type ",
                                                          var.type->type().toString(context), " with value of type ",
                                                          type.toString(context), "."));
            }
        }
    }

    if (!typeDef->instanceVariables().empty() && typeDef->initializerList().empty()) {
        package_->compiler()->warn(typeDef->position(), "Type defines ", typeDef->instanceVariables().size(),
                                   " instances variables but has no initializers.");
    }
}

bool SemanticAnalyser::checkReturnPromise(const Function *sub, const TypeContext &subContext,
                                          const Function *super, const TypeContext &superContext,
                                          const Type &superSource) const {
    auto superReturn = super->returnType()->type().resolveOn(superContext);
    auto subReturn = sub->returnType()->type().resolveOn(subContext);
    if (!subReturn.compatibleTo(superReturn, subContext)) {
        auto supername = superReturn.toString(subContext);
        auto thisname = sub->returnType()->type().toString(subContext);
        package_->compiler()->error(CompilerError(sub->position(), "Return type ",
                                                  sub->returnType()->type().toString(subContext), " of ",
                                                  utf8(sub->name()),
                                                  " is not compatible to the return type defined in ",
                                                  superSource.toString(subContext)));
    }
    return subReturn.storageType() == superReturn.storageType() && subReturn.isReference() == superReturn.isReference();
}

std::unique_ptr<Function> SemanticAnalyser::enforcePromises(Function *sub, Function *super,
                                                            const Type &superSource,
                                                            const TypeContext &subContext,
                                                            const TypeContext &superContext) {
    analyseFunctionDeclaration(sub);
    analyseFunctionDeclaration(super);
    if (super->final()) {
        package_->compiler()->error(CompilerError(sub->position(), superSource.toString(subContext),
                                                  "’s implementation of ", utf8(sub->name()), " was marked 🔏."));
    }
    if (sub->accessLevel() == AccessLevel::Private || (sub->accessLevel() == AccessLevel::Protected &&
            super->accessLevel() == AccessLevel::Public)) {
        package_->compiler()->error(CompilerError(sub->position(), "Overriding method must be as accessible or more ",
                                                  "accessible than the overridden method."));
    }

    bool isReturnOk = checkReturnPromise(sub, subContext, super, superContext, superSource);
    bool isParamsOk = checkArgumentPromise(sub, super, subContext, superContext) ;
    if (!isParamsOk || !isReturnOk) {
        return buildBoxingThunk(superContext, super, sub);
    }
    return nullptr;
}

bool SemanticAnalyser::checkArgumentPromise(const Function *sub, const Function *super, const TypeContext &subContext,
                                            const TypeContext &superContext) const {
    if (super->parameters().size() != sub->parameters().size()) {
        package_->compiler()->error(CompilerError(sub->position(), "Parameter count does not match."));
        return true;
    }

    bool compatible = true;
    for (size_t i = 0; i < super->parameters().size(); i++) { // More general arguments are OK
        auto superArgumentType = super->parameters()[i].type->type().resolveOn(superContext);
        if (!superArgumentType.compatibleTo(sub->parameters()[i].type->type().resolveOn(subContext), subContext)) {
            auto supertype = superArgumentType.toString(subContext);
            auto thisname = sub->parameters()[i].type->type().toString(subContext);
            package_->compiler()->error(CompilerError(sub->position(), "Type ", thisname, " of argument ", i + 1,
                                                      " is not compatible with its ", thisname, " argument type ",
                                                      supertype, "."));
        }
        if (sub->parameters()[i].type->type().resolveOn(subContext).storageType() != superArgumentType.storageType()) {
            compatible = false;  // Boxing Thunk required for parameter i
        }
    }
    return compatible;
}

void SemanticAnalyser::finalizeProtocol(const Type &type, const Type &protocol, const SourcePosition &p) {
    for (auto method : protocol.protocol()->methodList()) {
        auto methodImplementation = type.typeDefinition()->lookupMethod(method->name(), method->mood());
        if (methodImplementation == nullptr) {
            package_->compiler()->error(
                    CompilerError(p, type.toString(TypeContext()), " does not conform to protocol ",
                                  protocol.toString(TypeContext()), ": Method ",
                                  utf8(method->name()), " not provided."));
            continue;
        }

        if (imported_) {
            continue;
        }

        methodImplementation->createUnspecificReification();
        auto layer = enforcePromises(methodImplementation, method, protocol, TypeContext(type), TypeContext(protocol));
        if (layer != nullptr) {
            type.typeDefinition()->addMethod(std::move(layer));
        }
    }
}

void SemanticAnalyser::checkProtocolConformance(const Type &type) {
    for (auto &protocol : type.typeDefinition()->protocols()) {
        finalizeProtocol(type, protocol->type(), protocol->position());
    }
}

void SemanticAnalyser::finalizeProtocols(const Type &type) {
    std::set<Type> protocols;

    for (auto &protocol : type.typeDefinition()->protocols()) {
        auto &protocolType = protocol->analyseType(TypeContext(type));
        if (protocolType.unboxedType() != TypeType::Protocol) {
            package_->compiler()->error(CompilerError(protocol->position(), "Type is not a protocol."));
            continue;
        }
        if (protocols.find(protocolType.unboxed()) != protocols.end()) {
            package_->compiler()->error(CompilerError(protocol->position(),
                                                      "Conformance to protocol was already declared."));
            continue;
        }
        protocols.emplace(protocolType.unboxed());
    }
}

}  // namespace EmojicodeCompiler
<commit_msg>⚪ Fix SemanticAnalyser::analyseFunctionDeclaration<commit_after>//
// Created by Theo Weidmann on 26.02.18.
//

#include "SemanticAnalyser.hpp"
#include "Compiler.hpp"
#include "DeinitializerBuilder.hpp"
#include "FunctionAnalyser.hpp"
#include "AST/ASTExpr.hpp"
#include "Scoping/SemanticScoper.hpp"
#include "Types/TypeExpectation.hpp"
#include "Package/Package.hpp"
#include "ThunkBuilder.hpp"
#include "Types/Class.hpp"
#include "Types/Protocol.hpp"
#include "Types/TypeDefinition.hpp"
#include "Types/ValueType.hpp"

namespace EmojicodeCompiler {

Compiler* SemanticAnalyser::compiler() const {
    return package_->compiler();
}

void SemanticAnalyser::analyse(bool executable) {
    for (auto &vt : package_->valueTypes()) {
        finalizeProtocols(Type(vt.get()));
        vt->analyseConstraints(TypeContext(Type(vt.get())));
    }
    for (auto &klass : package_->classes()) {
        klass->analyseSuperType();
        finalizeProtocols(Type(klass.get()));
        klass->analyseConstraints(TypeContext(Type(klass.get())));
    }

    package_->recreateClassTypes();

    // Now all types are ready to be used with compatibleTo

    for (auto &protocol : package_->protocols()) {
        protocol->eachFunction([this](Function *function) {
            analyseFunctionDeclaration(function);
        });
    }
    for (auto &vt : package_->valueTypes()) {
        checkProtocolConformance(Type(vt.get()));
        declareInstanceVariables(Type(vt.get()));
        buildDeinitializer(vt.get());
        buildCopyRetain(vt.get());
        enqueueFunction(vt->deinitializer());
        enqueueFunction(vt->copyRetain());
        enqueueFunctionsOfTypeDefinition(vt.get());
    }
    for (auto &klass : package_->classes()) {
        for (auto init : klass->initializerList()) {
            if (!init->required()) {
                continue;
            }
            klass->addTypeMethod(buildRequiredInitThunk(klass.get(), init));
        }

        klass->inherit(this);
        checkProtocolConformance(Type(klass.get()));

        buildDeinitializer(klass.get());
        enqueueFunction(klass->deinitializer());

        enqueueFunctionsOfTypeDefinition(klass.get());

        if (!klass->hasSubclass() && !klass->exported()) {
            klass->setFinal();
        }
    }
    for (auto &function : package_->functions()) {
        enqueueFunction(function.get());
    }

    analyseQueue();
    checkStartFlagFunction(executable);
}

void SemanticAnalyser::checkStartFlagFunction(bool executable) {
    if (package_->hasStartFlagFunction()) {
        auto returnType = package_->startFlagFunction()->returnType()->type();
        if (returnType.type() != TypeType::NoReturn &&
            !returnType.compatibleTo(Type(package_->compiler()->sInteger), TypeContext())) {
            package_->compiler()->error(CompilerError(package_->startFlagFunction()->position(),
                                                      "🏁 must either have no return or return 🔢."));
        }
        if (!executable) {
            package_->startFlagFunction()->makeExternal();  // Prevent function from being included in object file
        }
    }
    else if (executable) {
        compiler()->error(CompilerError(SourcePosition(), "No 🏁 block was found."));
    }
}

void SemanticAnalyser::analyseQueue() {
    while (!queue_.empty()) {
        try {
            FunctionAnalyser(queue_.front(), this).analyse();
        }
        catch (CompilerError &ce) {
            package_->compiler()->error(ce);
        }
        queue_.pop();
    }
}

void SemanticAnalyser::enqueueFunctionsOfTypeDefinition(TypeDefinition *typeDef) {
    typeDef->eachFunction([this](Function *function) {
        enqueueFunction(function);
    });
}

void SemanticAnalyser::enqueueFunction(Function *function) {
    analyseFunctionDeclaration(function);
    if (!function->isExternal()) {
        queue_.emplace(function);
    }
}

void SemanticAnalyser::analyseFunctionDeclaration(Function *function) const {
    if (function->returnType() == nullptr) {
        function->setReturnType(std::make_unique<ASTLiteralType>(Type::noReturn()));
    }

    auto context = function->typeContext();

    function->analyseConstraints(context);
    for (auto &param : function->parameters()) {
        param.type->analyseType(context);
        if (!function->externalName().empty() && param.type->type().type() == TypeType::ValueType &&
            !param.type->type().valueType()->isPrimitive()) {
            param.type->type().setReference();
        }
    }

    if (auto initializer = dynamic_cast<Initializer*>(function)) {
        if (initializer->errorProne() && initializer->errorType()->analyseType(context).type() != TypeType::Enum) {
            throw CompilerError(initializer->errorType()->position(), "Error type must be a non-optional 🦃.");
        }
        return;
    }
    function->returnType()->analyseType(context, true);
}

void SemanticAnalyser::declareInstanceVariables(const Type &type) {
    TypeDefinition *typeDef = type.typeDefinition();

    auto context = TypeContext(type);
    auto scoper = std::make_unique<SemanticScoper>();
    scoper->pushScope();  // For closure analysis
    ExpressionAnalyser analyser(this, context, package_, std::move(scoper));

    for (auto &var : typeDef->instanceVariables()) {
        typeDef->instanceScope().declareVariable(var.name, var.type->analyseType(context), false,
                                                 var.position);

        if (var.expr != nullptr) {
            auto type = var.expr->analyse(&analyser, TypeExpectation(var.type->type()));
            if (!type.compatibleTo(var.type->type(), context)) {
                package_->compiler()->error(CompilerError(var.expr->position(),
                                                          "Cannot initialize instance variable of type ",
                                                          var.type->type().toString(context), " with value of type ",
                                                          type.toString(context), "."));
            }
        }
    }

    if (!typeDef->instanceVariables().empty() && typeDef->initializerList().empty()) {
        package_->compiler()->warn(typeDef->position(), "Type defines ", typeDef->instanceVariables().size(),
                                   " instances variables but has no initializers.");
    }
}

bool SemanticAnalyser::checkReturnPromise(const Function *sub, const TypeContext &subContext,
                                          const Function *super, const TypeContext &superContext,
                                          const Type &superSource) const {
    auto superReturn = super->returnType()->type().resolveOn(superContext);
    auto subReturn = sub->returnType()->type().resolveOn(subContext);
    if (!subReturn.compatibleTo(superReturn, subContext)) {
        auto supername = superReturn.toString(subContext);
        auto thisname = sub->returnType()->type().toString(subContext);
        package_->compiler()->error(CompilerError(sub->position(), "Return type ",
                                                  sub->returnType()->type().toString(subContext), " of ",
                                                  utf8(sub->name()),
                                                  " is not compatible to the return type defined in ",
                                                  superSource.toString(subContext)));
    }
    return subReturn.storageType() == superReturn.storageType() && subReturn.isReference() == superReturn.isReference();
}

std::unique_ptr<Function> SemanticAnalyser::enforcePromises(Function *sub, Function *super,
                                                            const Type &superSource,
                                                            const TypeContext &subContext,
                                                            const TypeContext &superContext) {
    analyseFunctionDeclaration(sub);
    analyseFunctionDeclaration(super);
    if (super->final()) {
        package_->compiler()->error(CompilerError(sub->position(), superSource.toString(subContext),
                                                  "’s implementation of ", utf8(sub->name()), " was marked 🔏."));
    }
    if (sub->accessLevel() == AccessLevel::Private || (sub->accessLevel() == AccessLevel::Protected &&
            super->accessLevel() == AccessLevel::Public)) {
        package_->compiler()->error(CompilerError(sub->position(), "Overriding method must be as accessible or more ",
                                                  "accessible than the overridden method."));
    }

    bool isReturnOk = checkReturnPromise(sub, subContext, super, superContext, superSource);
    bool isParamsOk = checkArgumentPromise(sub, super, subContext, superContext) ;
    if (!isParamsOk || !isReturnOk) {
        return buildBoxingThunk(superContext, super, sub);
    }
    return nullptr;
}

bool SemanticAnalyser::checkArgumentPromise(const Function *sub, const Function *super, const TypeContext &subContext,
                                            const TypeContext &superContext) const {
    if (super->parameters().size() != sub->parameters().size()) {
        package_->compiler()->error(CompilerError(sub->position(), "Parameter count does not match."));
        return true;
    }

    bool compatible = true;
    for (size_t i = 0; i < super->parameters().size(); i++) { // More general arguments are OK
        auto superArgumentType = super->parameters()[i].type->type().resolveOn(superContext);
        if (!superArgumentType.compatibleTo(sub->parameters()[i].type->type().resolveOn(subContext), subContext)) {
            auto supertype = superArgumentType.toString(subContext);
            auto thisname = sub->parameters()[i].type->type().toString(subContext);
            package_->compiler()->error(CompilerError(sub->position(), "Type ", thisname, " of argument ", i + 1,
                                                      " is not compatible with its ", thisname, " argument type ",
                                                      supertype, "."));
        }
        if (sub->parameters()[i].type->type().resolveOn(subContext).storageType() != superArgumentType.storageType()) {
            compatible = false;  // Boxing Thunk required for parameter i
        }
    }
    return compatible;
}

void SemanticAnalyser::finalizeProtocol(const Type &type, const Type &protocol, const SourcePosition &p) {
    for (auto method : protocol.protocol()->methodList()) {
        auto methodImplementation = type.typeDefinition()->lookupMethod(method->name(), method->mood());
        if (methodImplementation == nullptr) {
            package_->compiler()->error(
                    CompilerError(p, type.toString(TypeContext()), " does not conform to protocol ",
                                  protocol.toString(TypeContext()), ": Method ",
                                  utf8(method->name()), " not provided."));
            continue;
        }

        if (imported_) {
            continue;
        }

        methodImplementation->createUnspecificReification();
        auto layer = enforcePromises(methodImplementation, method, protocol, TypeContext(type), TypeContext(protocol));
        if (layer != nullptr) {
            type.typeDefinition()->addMethod(std::move(layer));
        }
    }
}

void SemanticAnalyser::checkProtocolConformance(const Type &type) {
    for (auto &protocol : type.typeDefinition()->protocols()) {
        finalizeProtocol(type, protocol->type(), protocol->position());
    }
}

void SemanticAnalyser::finalizeProtocols(const Type &type) {
    std::set<Type> protocols;

    for (auto &protocol : type.typeDefinition()->protocols()) {
        auto &protocolType = protocol->analyseType(TypeContext(type));
        if (protocolType.unboxedType() != TypeType::Protocol) {
            package_->compiler()->error(CompilerError(protocol->position(), "Type is not a protocol."));
            continue;
        }
        if (protocols.find(protocolType.unboxed()) != protocols.end()) {
            package_->compiler()->error(CompilerError(protocol->position(),
                                                      "Conformance to protocol was already declared."));
            continue;
        }
        protocols.emplace(protocolType.unboxed());
    }
}

}  // namespace EmojicodeCompiler
<|endoftext|>


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

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

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

#include "mitkStateMachine.h"
#include "mitkStateMachineFactory.h"
#include "mitkStateTransitionOperation.h"
#include "mitkOperationEvent.h"
#include "mitkUndoController.h"
#include "mitkInteractionConst.h"
#include <itkMacro.h>
#include "mitkInteractor.h"

//##ModelId=3E5B2DB301FD
//##Documentation
//## Constructor
//## daclares a new StateMachine and connects
//## it to a StateMachine of Type type;
mitk::StateMachine::StateMachine(const char * type) : m_CurrentState(NULL)
{
  if(type!=NULL)
  {
    m_Type = type;
	  m_CurrentState = mitk::StateMachineFactory::GetStartState(type);
    if (m_CurrentState == NULL)
      itkWarningMacro("Error: no start-state defined in state-machine definition "<<type<<".");
  }
  //else
  //  itkWarningMacro("Error from "<<this->GetNameOfClass()<<"; Message: Type of StateMachine is NULL!");
  //\*todo: check the process with BaseControllers, cause they are always instantiated with type ==NULL! So here we can't check and warn the user.

  m_UndoController = new UndoController(LIMITEDLINEARUNDO);//switch to LLU or add LLU
	m_UndoEnabled = true;
}
mitk::StateMachine::~StateMachine()
{
  delete m_UndoController;
}

//##ModelId=3E5B2E660087
std::string mitk::StateMachine::GetType() const
{
	return m_Type;
}

const mitk::State* mitk::StateMachine::GetCurrentState() const
{
  if (m_CurrentState)
    return m_CurrentState;
  return NULL;
}

//##ModelId=3E5B2DE30378
bool mitk::StateMachine::HandleEvent(StateEvent const* stateEvent)
{
  if (m_CurrentState == NULL)
    return false;//m_CurrentState needs to be set first!

  //get the Transition from m_CurrentState which waits for this EventId
  const Transition *tempTransition = m_CurrentState->GetTransition(stateEvent->GetId());
  if (tempTransition == NULL) //no transition in this state for that EventId
  {
    return false;
  }

  //get next State
  State *tempNextState = tempTransition->GetNextState();
  if (tempNextState == NULL) //wrong built up statemachine!
    return false;

  //and ActionId to execute later on
  if ( m_CurrentState->GetId() != tempNextState->GetId() )//statechange only if there is a real statechange
  {
    if ( m_UndoEnabled )	//write to UndoMechanism if Undo is enabled
    {
      //UNDO for this statechange; since we directly change the state, we don't need the do-Operation in case m_UndoEnables == false
  	  StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, tempNextState);
      StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentState);
	    OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp);
	    m_UndoController->SetOperationEvent(operationEvent);
    }
//#define INTERDEBUG
            #ifdef INTERDEBUG
            //Debug StateChanges through cout output! Thus very slow!
            //itkWarningMacro(<<this->GetType()<<": Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<tempNextState->GetId());
            itkWarningMacro(<<": Changing from State "<<m_CurrentState->GetName()<<" to State "<<tempNextState->GetName() << " via Transition " << tempTransition->GetName() << " due to Event " << stateEvent->GetId());
            #endif

    //first following StateChange(or calling ExecuteOperation(tempNextStateOp)), then operation(action)
    m_CurrentState = tempNextState;
  }
  else
  {
    #ifdef INTERDEBUG
    if( (tempTransition != m_CurrentState->GetTransition(0)) //dont show 0 events
        && (m_CurrentState->GetName()!="neutral"))
    {
      //Debug StateChanges through cout output! Thus very slow!
      //itkWarningMacro(<<this->GetType()<<": Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<tempNextState->GetId());
      itkWarningMacro(<<": Keeping State "<<m_CurrentState->GetName()<< " at Transition " << tempTransition->GetName() << " due to Event " << stateEvent->GetId());
    }
    #endif
  }

  std::vector<Action*>::iterator actionIdIterator = tempTransition->GetActionBeginIterator();
  const std::vector<Action*>::iterator actionIdIteratorEnd = tempTransition->GetActionEndIterator();
  bool ok = true;

  while ( actionIdIterator != actionIdIteratorEnd )
  {
    if ( !ExecuteAction(*actionIdIterator, stateEvent) )
    {
      #ifdef INTERDEBUG
      itkWarningMacro( << "Warning: no action defind for " << *actionIdIterator << " in " << m_Type );
      #endif

      ok = false;
    }
    actionIdIterator++;
  }
  return ok;
}

//##ModelId=3EDCAECB0175
void mitk::StateMachine::EnableUndo(bool enable)
{
	m_UndoEnabled = enable;
}

//##ModelId=3EF099EA03C0
void mitk::StateMachine::IncCurrGroupEventId()
{
	mitk::OperationEvent::IncCurrGroupEventId();
}

void mitk::StateMachine::ExecuteOperation(Operation* operation)
{
	switch (operation->GetOperationType())
	{
	case OpNOTHING:
		break;
	case OpSTATECHANGE:
		{
			mitk::StateTransitionOperation* stateTransOp = dynamic_cast<mitk::StateTransitionOperation *>(operation);
			if (stateTransOp == NULL)
			{
				itkWarningMacro("Error! see mitkStateMachine.cpp");
				return;
			}
#ifdef INTERDEBUG
//Debug StateChanges through cout output! Thus very slow!
std::cout<<this->GetType()<<": Undo: Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<stateTransOp->GetState()->GetId()<<std::endl;
std::cout<<this->GetType()<<": Undo: Changing from State "<<m_CurrentState->GetName()<<" to State "<<stateTransOp->GetState()->GetName()<<std::endl;
#endif
			m_CurrentState = stateTransOp->GetState();
		}
		break;
  case OpDELETE:
    {
      //delete this!
      //before all lower statemachines has to be deleted in a action
      //this->Delete();//might not work!!!check itk!
    }
  case OpUNDELETE:
    {
      //this is just new! and now the m_CurrentState has to be set on a special State
      //that way a delete of a StateMachine can be undone
      //IMPORTANT: The type has to be the same!!!Done by a higher instance, that creates this!
      mitk::StateTransitionOperation* stateTransOp = dynamic_cast<mitk::StateTransitionOperation *>(operation);
			if (stateTransOp != NULL)
      {
        m_CurrentState = stateTransOp->GetState();
      }
    }
	default:
		;
	}
}
<commit_msg>modified debug information<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

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

#include "mitkStateMachine.h"
#include "mitkStateMachineFactory.h"
#include "mitkStateTransitionOperation.h"
#include "mitkOperationEvent.h"
#include "mitkUndoController.h"
#include "mitkInteractionConst.h"
#include <itkMacro.h>
#include "mitkInteractor.h"

//##ModelId=3E5B2DB301FD
//##Documentation
//## Constructor
//## daclares a new StateMachine and connects
//## it to a StateMachine of Type type;
mitk::StateMachine::StateMachine(const char * type) : m_CurrentState(NULL)
{
  if(type!=NULL)
  {
    m_Type = type;
	  m_CurrentState = mitk::StateMachineFactory::GetStartState(type);
    if (m_CurrentState == NULL)
      itkWarningMacro("Error: no start-state defined in state-machine definition "<<type<<".");
  }
  //else
  //  itkWarningMacro("Error from "<<this->GetNameOfClass()<<"; Message: Type of StateMachine is NULL!");
  //\*todo: check the process with BaseControllers, cause they are always instantiated with type ==NULL! So here we can't check and warn the user.

  m_UndoController = new UndoController(LIMITEDLINEARUNDO);//switch to LLU or add LLU
	m_UndoEnabled = true;
}
mitk::StateMachine::~StateMachine()
{
  delete m_UndoController;
}

//##ModelId=3E5B2E660087
std::string mitk::StateMachine::GetType() const
{
	return m_Type;
}

const mitk::State* mitk::StateMachine::GetCurrentState() const
{
  if (m_CurrentState)
    return m_CurrentState;
  return NULL;
}

//##ModelId=3E5B2DE30378
bool mitk::StateMachine::HandleEvent(StateEvent const* stateEvent)
{
  if (m_CurrentState == NULL)
    return false;//m_CurrentState needs to be set first!

  //get the Transition from m_CurrentState which waits for this EventId
  const Transition *tempTransition = m_CurrentState->GetTransition(stateEvent->GetId());
  if (tempTransition == NULL) //no transition in this state for that EventId
  {
    return false;
    #ifdef INTERDEBUG
            //Debug StateChanges through cout output! Thus very slow!
            //itkWarningMacro(<<this->GetType()<<": Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<tempNextState->GetId());
            itkWarningMacro(<<": Did not find transition for Event Id " << stateEvent->GetId());
    #endif
  }

  //get next State
  State *tempNextState = tempTransition->GetNextState();
  if (tempNextState == NULL) //wrong built up statemachine!
    return false;

  //and ActionId to execute later on
  if ( m_CurrentState->GetId() != tempNextState->GetId() )//statechange only if there is a real statechange
  {
    if ( m_UndoEnabled )	//write to UndoMechanism if Undo is enabled
    {
      //UNDO for this statechange; since we directly change the state, we don't need the do-Operation in case m_UndoEnables == false
  	  StateTransitionOperation* doOp = new StateTransitionOperation(OpSTATECHANGE, tempNextState);
      StateTransitionOperation* undoOp = new StateTransitionOperation(OpSTATECHANGE, m_CurrentState);
	    OperationEvent *operationEvent = new OperationEvent(((mitk::OperationActor*)(this)), doOp, undoOp);
	    m_UndoController->SetOperationEvent(operationEvent);
    }
//#define INTERDEBUG
            #ifdef INTERDEBUG
            //Debug StateChanges through cout output! Thus very slow!
            //itkWarningMacro(<<this->GetType()<<": Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<tempNextState->GetId());
            itkWarningMacro(<<": Changing from State "<<m_CurrentState->GetName()<<" to State "<<tempNextState->GetName() << " via Transition " << tempTransition->GetName() << " due to Event " << stateEvent->GetId());
            #endif

    //first following StateChange(or calling ExecuteOperation(tempNextStateOp)), then operation(action)
    m_CurrentState = tempNextState;
  }
  else
  {
    #ifdef INTERDEBUG
    if( (tempTransition != m_CurrentState->GetTransition(0)) //dont show 0 events
        && (m_CurrentState->GetName()!="neutral"))
    {
      //Debug StateChanges through cout output! Thus very slow!
      //itkWarningMacro(<<this->GetType()<<": Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<tempNextState->GetId());
      itkWarningMacro(<<": Keeping State "<<m_CurrentState->GetName()<< " at Transition " << tempTransition->GetName() << " due to Event " << stateEvent->GetId());
    }
    #endif
  }

  std::vector<Action*>::iterator actionIdIterator = tempTransition->GetActionBeginIterator();
  const std::vector<Action*>::iterator actionIdIteratorEnd = tempTransition->GetActionEndIterator();
  bool ok = true;

  while ( actionIdIterator != actionIdIteratorEnd )
  {
    if ( !ExecuteAction(*actionIdIterator, stateEvent) )
    {
      #ifdef INTERDEBUG
      itkWarningMacro( << "Warning: no action defind for " << *actionIdIterator << " in " << m_Type );
      #endif

      ok = false;
    }
    actionIdIterator++;
  }
  return ok;
}

//##ModelId=3EDCAECB0175
void mitk::StateMachine::EnableUndo(bool enable)
{
	m_UndoEnabled = enable;
}

//##ModelId=3EF099EA03C0
void mitk::StateMachine::IncCurrGroupEventId()
{
	mitk::OperationEvent::IncCurrGroupEventId();
}

void mitk::StateMachine::ExecuteOperation(Operation* operation)
{
	switch (operation->GetOperationType())
	{
	case OpNOTHING:
		break;
	case OpSTATECHANGE:
		{
			mitk::StateTransitionOperation* stateTransOp = dynamic_cast<mitk::StateTransitionOperation *>(operation);
			if (stateTransOp == NULL)
			{
				itkWarningMacro("Error! see mitkStateMachine.cpp");
				return;
			}
#ifdef INTERDEBUG
//Debug StateChanges through cout output! Thus very slow!
std::cout<<this->GetType()<<": Undo: Changing from StateId "<<m_CurrentState->GetId()<<" to StateId "<<stateTransOp->GetState()->GetId()<<std::endl;
std::cout<<this->GetType()<<": Undo: Changing from State "<<m_CurrentState->GetName()<<" to State "<<stateTransOp->GetState()->GetName()<<std::endl;
#endif
			m_CurrentState = stateTransOp->GetState();
		}
		break;
  case OpDELETE:
    {
      //delete this!
      //before all lower statemachines has to be deleted in a action
      //this->Delete();//might not work!!!check itk!
    }
  case OpUNDELETE:
    {
      //this is just new! and now the m_CurrentState has to be set on a special State
      //that way a delete of a StateMachine can be undone
      //IMPORTANT: The type has to be the same!!!Done by a higher instance, that creates this!
      mitk::StateTransitionOperation* stateTransOp = dynamic_cast<mitk::StateTransitionOperation *>(operation);
			if (stateTransOp != NULL)
      {
        m_CurrentState = stateTransOp->GetState();
      }
    }
	default:
		;
	}
}
<|endoftext|>


<commit_before>//   Copyright 2016-2022 Jean-Francois Poilpret
//
//   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.

/*
 * TODO comments Simple LED chaser. Take #1
 * This program shows usage of FastArduino events support and GPIO port API.
 * The number of LED roundtrips is limited to one because all events are pushed
 * at startup and not regenerated.
 *
 * Wiring: TODO
 * - on Arduino UNO:
 *   - D0-D7 (port D) branch 8 LED (in series with 330 Ohm resistors to limit
 *     current) connected to ground
 */

#include <fastarduino/uart.h>
#include <fastarduino/soft_uart.h>
#include <fastarduino/devices/grove_rfid_reader.h>
#include <fastarduino/time.h>

#if !defined(ARDUINO_UNO)
#error "Current target is not yet supported!"
#endif

#define USART_NUM 0
static constexpr board::USART USART = board::USART::USART0;
using UARX = serial::hard::UARX<USART>;
using GROVE = devices::rfid::Grove125KHzRFIDReaderUART<UARX>;

static constexpr board::DigitalPin UATX_TX = board::DigitalPin::D2;
using UATX = serial::soft::UATX<UATX_TX>;

static constexpr uint8_t RFID_INPUT_BUFFER_SIZE = 64;
static char input_buffer[RFID_INPUT_BUFFER_SIZE];

static constexpr uint8_t DEBUG_OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[DEBUG_OUTPUT_BUFFER_SIZE];

static constexpr uint8_t TAG_DATA_SIZE = 16;

REGISTER_UARX_ISR(USART_NUM)
REGISTER_OSTREAMBUF_LISTENERS(UATX)

using streams::endl;

int main() __attribute__((OS_main));
int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();

	UATX uatx{output_buffer};
	uatx.begin(115200);
	auto out = uatx.out();
	out << F("Starting...") << endl;

	UARX uarx{input_buffer};
	GROVE rfid{uarx};
	rfid.begin();

	while (true)
	{
		if (rfid.has_data())
		{
			char data[TAG_DATA_SIZE];
			rfid.get_data(data, TAG_DATA_SIZE);
			//TODO display data to SW serial
			out << data << endl;
		}
		time::delay_ms(100);
	}

	rfid.end();
	return 0;
}
<commit_msg>Update doc of 1st Grove RFID reader example<commit_after>//   Copyright 2016-2022 Jean-Francois Poilpret
//
//   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.

/*
 * RFID 125KHz Grove reader example, UART mode, using Hardware UART.
 * This program shows usage of FastArduino Grove 125KHz RFID Reader support.
 * It displays the ID of tags that are nearing the device coil.
 *
 * Wiring:
 * - on Arduino UNO:
 *   - RX (D0): connect to Grove TX (yellow cable)
 *   - D2: connect to serial USB converter, connected to a console
 */

#include <fastarduino/uart.h>
#include <fastarduino/soft_uart.h>
#include <fastarduino/devices/grove_rfid_reader.h>
#include <fastarduino/time.h>

#if !defined(ARDUINO_UNO)
#error "Current target is not yet supported!"
#endif

#define USART_NUM 0
static constexpr board::USART USART = board::USART::USART0;
using UARX = serial::hard::UARX<USART>;
using GROVE = devices::rfid::Grove125KHzRFIDReaderUART<UARX>;

static constexpr board::DigitalPin UATX_TX = board::DigitalPin::D2;
using UATX = serial::soft::UATX<UATX_TX>;

static constexpr uint8_t RFID_INPUT_BUFFER_SIZE = 64;
static char input_buffer[RFID_INPUT_BUFFER_SIZE];

static constexpr uint8_t DEBUG_OUTPUT_BUFFER_SIZE = 64;
static char output_buffer[DEBUG_OUTPUT_BUFFER_SIZE];

static constexpr uint8_t TAG_DATA_SIZE = 16;

REGISTER_UARX_ISR(USART_NUM)
REGISTER_OSTREAMBUF_LISTENERS(UATX)

using streams::endl;

int main() __attribute__((OS_main));
int main()
{
	board::init();
	// Enable interrupts at startup time
	sei();

	UATX uatx{output_buffer};
	uatx.begin(115200);
	auto out = uatx.out();
	out << F("Starting...") << endl;

	UARX uarx{input_buffer};
	GROVE rfid{uarx};
	rfid.begin();

	while (true)
	{
		if (rfid.has_data())
		{
			char data[TAG_DATA_SIZE];
			rfid.get_data(data, TAG_DATA_SIZE);
			// display data to SW serial
			out << data << endl;
		}
		time::delay_ms(100);
	}

	rfid.end();
	return 0;
}
<|endoftext|>


<commit_before>/*
 * Jamoma DSP Soundfile Player
 * Copyright © 2010, Tim Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTSoundfilePlayer.h"

#define thisTTClass			TTSoundfilePlayer
#define thisTTClassName		"soundfile.player"
#define thisTTClassTags		"audio, soundfile, playback"

TT_AUDIO_CONSTRUCTOR,
mFilePath(kTTSymEmpty),
mSoundFile(NULL),
mPlay(false),
mLoop(false),
mContinue(false),
mSeek(0),
mNumChannels(0),
mNumBufferFrames(0)
{
	addAttributeWithSetter(	FilePath,		kTypeSymbol);
	addAttributeWithSetter(	Play,			kTypeBoolean);
	addAttributeWithSetter(	Seek,			kTypeFloat64);
	addAttribute(			Loop,			kTypeBoolean);
	addAttribute(			NumChannels,	kTypeUInt16);
	addAttributeProperty(	NumChannels,	readOnly, kTTBoolYes);

	addMessage(Pause);
	addMessage(Resume);
	setProcessMethod(processAudio);
	//setAttributeValue(TT("MaxNumChannels"),	arguments);			// This attribute is inherited
}

TTSoundfilePlayer::~TTSoundfilePlayer()
{
	setAttributeValue(TT("Play"), kTTBoolNo);
	if (mSoundFile)
		sf_close(mSoundFile);
}

// takes a POSIX path, e.g. /Users/tim/Music/Demos/whiteandnerdy.aif
TTErr TTSoundfilePlayer::setFilePath(const TTValue& newValue)
{
	TTSymbolPtr potentialFilePath = newValue;
	SNDFILE*	soundfile;
#ifdef TT_PLATFORM_WIN
	// There is a bug in libsndfile on Windows where upon return from this function a runtime check fails
	// because the stack is corrupted around soundfileInfo when sf_open() is called.
	// We work around this by allocating some extra memory to absorb the overrun. [tap]
	SF_INFO		soundfileInfo[2];
#else
	SF_INFO		soundfileInfo[1];
#endif

	memset(&soundfileInfo, 0, sizeof(SF_INFO));
	//soundfileInfo.format = 0;
	soundfile = sf_open(potentialFilePath->getCString(), SFM_READ, soundfileInfo);

	if (soundfile) {
		SNDFILE* oldSoundFile = mSoundFile;

		mSoundFile = soundfile;
		if (oldSoundFile)
			sf_close(oldSoundFile);
		memcpy(&mSoundFileInfo, soundfileInfo, sizeof(SF_INFO));
		mFilePath = potentialFilePath;
		mPlay = 0;
		mContinue = 1; //eliminating previous pause state
		// TODO: fill in things like the NumChannels attr here
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTSoundfilePlayer::setPlay(const TTValue& newValue)
{
	mPlay = newValue;
	mContinue = 1; //eliminating previous pause state
	if (mPlay == 0){
		if (mSoundFile){
			mSeek = 0;
			sf_seek(mSoundFile, 0, SEEK_SET);
		}
	}
return kTTErrNone;
}

TTErr TTSoundfilePlayer::setSeek(const TTValue& newValue)
{
	if (mSoundFile) {
		mSeek = newValue;
		mSeek = mSeek * sr/1000.0;
		mContinue = 1; //eliminating previous pause state
		sf_seek(mSoundFile, mSeek, SEEK_SET);
		mPlay = 1;
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTSoundfilePlayer::Pause()
{
	mContinue = 0;
	return kTTErrNone;
}

TTErr TTSoundfilePlayer::Resume()
{
	mContinue = 1;
	return kTTErrNone;
}

TTErr TTSoundfilePlayer::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TTAudioSignal&	out = outputs->getSignal(0);
	TTUInt16		outChannelCount = out.getMaxNumChannelsAsInt();
	TTUInt16		numFrames = out.getVectorSizeAsInt();
	TTBoolean		bufferNeedsResize = NO;
	sf_count_t			numSamplesRead;
	TTUInt16			n;
	TTSampleValuePtr	outSample;
	TTUInt16			channel;

	if (mSoundFile) {
	// resize of the number of output channels, if needed
	if (outChannelCount != mSoundFileInfo.channels || !mNumChannels) {
		mNumChannels = mSoundFileInfo.channels;
		out.setMaxNumChannels(mNumChannels);
		bufferNeedsResize = YES;
	}
	out.setNumChannelsWithInt(mNumChannels);

	if (mNumBufferFrames != numFrames) {
		mNumBufferFrames = numFrames;
		bufferNeedsResize = YES;
	}
	if (bufferNeedsResize)
		mBuffer.resize(mNumBufferFrames * mNumChannels);

	if (!mNumChannels)
		return TTAudioObject::muteProcess(inputs, outputs);

	// if there is an input, we want to treat it as a sample-accurate on/off switch for playback
	//if (inputs->numAudioSignals) {
	//	; // TODO: implement this
	//}
	//else {

		mBuffer.assign(mBuffer.size(), 0.0);

		if (mPlay && mContinue) {
			numSamplesRead = sf_read_double(mSoundFile, &mBuffer[0], numFrames*mNumChannels);
			if (numSamplesRead < numFrames*mNumChannels) {
				sf_seek(mSoundFile, mSeek, SEEK_SET);
				mPlay = mLoop;
			}
		}

		for (channel=0; channel<mNumChannels; channel++) {
			outSample = out.mSampleVectors[channel];

			for (n=0; n<numFrames; n++)
				outSample[n] = mBuffer[n * mNumChannels + channel];
		}
	}
	else {         //no soundfile selected, we send out a zero signal on one channel
		out.setMaxNumChannels(1);
		out.setNumChannelsWithInt(1);
		for (n=0; n<numFrames; n++)
		out.mSampleVectors[0][n] = 0.0;
	}

	return kTTErrNone;
}
<commit_msg>TTSoundfilePlayer : avoid to set the number of output channels if not necessary.  <commit_after>/*
 * Jamoma DSP Soundfile Player
 * Copyright © 2010, Tim Place
 *
 * License: This code is licensed under the terms of the GNU LGPL
 * http://www.gnu.org/licenses/lgpl.html
 */

#include "TTSoundfilePlayer.h"

#define thisTTClass			TTSoundfilePlayer
#define thisTTClassName		"soundfile.player"
#define thisTTClassTags		"audio, soundfile, playback"

TT_AUDIO_CONSTRUCTOR,
mFilePath(kTTSymEmpty),
mSoundFile(NULL),
mPlay(false),
mLoop(false),
mContinue(false),
mSeek(0),
mNumChannels(0),
mNumBufferFrames(0)
{
	addAttributeWithSetter(	FilePath,		kTypeSymbol);
	addAttributeWithSetter(	Play,			kTypeBoolean);
	addAttributeWithSetter(	Seek,			kTypeFloat64);
	addAttribute(			Loop,			kTypeBoolean);
	addAttribute(			NumChannels,	kTypeUInt16);
	addAttributeProperty(	NumChannels,	readOnly, kTTBoolYes);

	addMessage(Pause);
	addMessage(Resume);
	setProcessMethod(processAudio);
	//setAttributeValue(TT("MaxNumChannels"),	arguments);			// This attribute is inherited
}

TTSoundfilePlayer::~TTSoundfilePlayer()
{
	setAttributeValue(TT("Play"), kTTBoolNo);
	if (mSoundFile)
		sf_close(mSoundFile);
}

// takes a POSIX path, e.g. /Users/tim/Music/Demos/whiteandnerdy.aif
TTErr TTSoundfilePlayer::setFilePath(const TTValue& newValue)
{
	TTSymbolPtr potentialFilePath = newValue;
	SNDFILE*	soundfile;
#ifdef TT_PLATFORM_WIN
	// There is a bug in libsndfile on Windows where upon return from this function a runtime check fails
	// because the stack is corrupted around soundfileInfo when sf_open() is called.
	// We work around this by allocating some extra memory to absorb the overrun. [tap]
	SF_INFO		soundfileInfo[2];
#else
	SF_INFO		soundfileInfo[1];
#endif

	memset(&soundfileInfo, 0, sizeof(SF_INFO));
	//soundfileInfo.format = 0;
	soundfile = sf_open(potentialFilePath->getCString(), SFM_READ, soundfileInfo);

	if (soundfile) {
		SNDFILE* oldSoundFile = mSoundFile;

		mSoundFile = soundfile;
		if (oldSoundFile)
			sf_close(oldSoundFile);
		memcpy(&mSoundFileInfo, soundfileInfo, sizeof(SF_INFO));
		mFilePath = potentialFilePath;
		mPlay = 0;
		mContinue = 1; //eliminating previous pause state
		// TODO: fill in things like the NumChannels attr here
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTSoundfilePlayer::setPlay(const TTValue& newValue)
{
	mPlay = newValue;
	mContinue = 1; //eliminating previous pause state
	if (mPlay == 0){
		if (mSoundFile){
			mSeek = 0;
			sf_seek(mSoundFile, 0, SEEK_SET);
		}
	}
return kTTErrNone;
}

TTErr TTSoundfilePlayer::setSeek(const TTValue& newValue)
{
	if (mSoundFile) {
		mSeek = newValue;
		mSeek = mSeek * sr/1000.0;
		mContinue = 1; //eliminating previous pause state
		sf_seek(mSoundFile, mSeek, SEEK_SET);
		mPlay = 1;
		return kTTErrNone;
	}
	else
		return kTTErrGeneric;
}

TTErr TTSoundfilePlayer::Pause()
{
	mContinue = 0;
	return kTTErrNone;
}

TTErr TTSoundfilePlayer::Resume()
{
	mContinue = 1;
	return kTTErrNone;
}

TTErr TTSoundfilePlayer::processAudio(TTAudioSignalArrayPtr inputs, TTAudioSignalArrayPtr outputs)
{
	TTAudioSignal&	out = outputs->getSignal(0);
	TTUInt16		outChannelCount = out.getMaxNumChannelsAsInt();
	TTUInt16		numFrames = out.getVectorSizeAsInt();
	TTBoolean		bufferNeedsResize = NO;
	sf_count_t			numSamplesRead;
	TTUInt16			n;
	TTSampleValuePtr	outSample;
	TTUInt16			channel;

	if (mSoundFile) {
	// resize of the number of output channels, if needed
	if (outChannelCount != mSoundFileInfo.channels || !mNumChannels) {
		mNumChannels = mSoundFileInfo.channels;
		out.setMaxNumChannels(mNumChannels);
		bufferNeedsResize = YES;
		out.setNumChannelsWithInt(mNumChannels);
	}

	if (mNumBufferFrames != numFrames) {
		mNumBufferFrames = numFrames;
		bufferNeedsResize = YES;
	}
	if (bufferNeedsResize)
		mBuffer.resize(mNumBufferFrames * mNumChannels);

	if (!mNumChannels)
		return TTAudioObject::muteProcess(inputs, outputs);

	// if there is an input, we want to treat it as a sample-accurate on/off switch for playback
	//if (inputs->numAudioSignals) {
	//	; // TODO: implement this
	//}
	//else {

		mBuffer.assign(mBuffer.size(), 0.0);

		if (mPlay && mContinue) {
			numSamplesRead = sf_read_double(mSoundFile, &mBuffer[0], numFrames*mNumChannels);
			if (numSamplesRead < numFrames*mNumChannels) {
				sf_seek(mSoundFile, mSeek, SEEK_SET);
				mPlay = mLoop;
			}
		}

		for (channel=0; channel<mNumChannels; channel++) {
			outSample = out.mSampleVectors[channel];

			for (n=0; n<numFrames; n++)
				outSample[n] = mBuffer[n * mNumChannels + channel];
		}
	}
	else {         //no soundfile selected, we send out a zero signal on one channel
		out.setMaxNumChannels(1);
		out.setNumChannelsWithInt(1);
		for (n=0; n<numFrames; n++)
		out.mSampleVectors[0][n] = 0.0;
	}

	return kTTErrNone;
}
<|endoftext|>


<commit_before>/*
 * Copyright deipi.com LLC and contributors. All rights reserved.
 *
 * 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 <assert.h>
#include <sys/socket.h>

#include "server.h"
#include "utils.h"
#include "client_binary.h"
#include "xapiand.h"
#include "length.h"

//
// Xapian binary client
//

BinaryClient::BinaryClient(XapiandServer *server_, ev::loop_ref *loop, int sock_, DatabasePool *database_pool_, ThreadPool *thread_pool_, double active_timeout_, double idle_timeout_)
	: BaseClient(server_, loop, sock_, database_pool_, thread_pool_, active_timeout_, idle_timeout_),
	  RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  running(false),
      started(false)
{
	pthread_mutex_lock(&XapiandServer::static_mutex);
	int total_clients = XapiandServer::total_clients;
	int binary_clients = ++XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_CONN(this, "Got connection (sock=%d), %d binary client(s) of a total of %d connected.\n", sock, binary_clients, XapiandServer::total_clients);

	thread_pool->addTask(this);

	LOG_OBJ(this, "CREATED BINARY CLIENT! (%d clients)\n", binary_clients);
	assert(binary_clients <= total_clients);
}

BinaryClient::~BinaryClient()
{
	std::unordered_map<Xapian::Database *, Database *>::const_iterator it(databases.begin());
	for (; it != databases.end(); it++) {
		Database *database = it->second;
		database_pool->checkin(&database);
	}

	pthread_mutex_lock(&XapiandServer::static_mutex);
	int binary_clients = --XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_OBJ(this, "DELETED BINARY CLIENT! (%d clients left)\n", binary_clients);
	assert(binary_clients >= 0);
}

void BinaryClient::on_read(const char *buf, ssize_t received)
{
	buffer.append(buf, received);
	while (buffer.length() >= 2) {
		const char *o = buffer.data();
		const char *p = o;
		const char *p_end = p + buffer.size();

		message_type type = static_cast<message_type>(*p++);
		size_t len = decode_length(&p, p_end, true);
		if (len == -1) {
			return;
		}
		std::string data = std::string(p, len);
		buffer.erase(0, p - o + len);

		Buffer *msg = new Buffer(type, data.c_str(), data.size());

		messages_queue.push(msg);
	}
    pthread_mutex_lock(&qmtx);
    if (!messages_queue.empty()) {
        if (!running) {
            running = true;
            thread_pool->addTask(this);
        }
    }
    pthread_mutex_unlock(&qmtx);
}

message_type BinaryClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	const char *msg_str = msg->dpos();
	size_t msg_size = msg->nbytes();

	std::string message = std::string(msg_str, msg_size);

	std::string buf(&msg->type, 1);
	buf += encode_length(msg_size);
	buf += message;
	LOG_BINARY_PROTO(this, "get_message: '%s'\n", repr(buf).c_str());

	result = message;

	message_type type = static_cast<message_type>(msg->type);

	delete msg;

	return type;
}

void BinaryClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	LOG_BINARY_PROTO(this, "send_message: '%s'\n", repr(buf).c_str());

	write(buf);
}

void BinaryClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

void BinaryClient::shutdown()
{
	server->manager->async_shutdown.send();
}

Xapian::Database * BinaryClient::get_db(bool writable_)
{
	pthread_mutex_lock(&qmtx);
	if (endpoints.empty()) {
		pthread_mutex_unlock(&qmtx);
		return NULL;
	}
	Endpoints endpoints_ = endpoints;
	pthread_mutex_unlock(&qmtx);

	Database *database = NULL;
	if (!database_pool->checkout(&database, endpoints_, writable_)) {
		return NULL;
	}

	pthread_mutex_lock(&qmtx);
	databases[database->db] = database;
	pthread_mutex_unlock(&qmtx);

	return database->db;
}

void BinaryClient::release_db(Xapian::Database *db_)
{
	if (db_) {
		pthread_mutex_lock(&qmtx);
		Database *database = databases[db_];
		databases.erase(db_);
		pthread_mutex_unlock(&qmtx);

		database_pool->checkin(&database);
	}
}

void BinaryClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_, int flags)
{
	pthread_mutex_lock(&qmtx);
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	endpoints.clear();
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), XAPIAND_BINARY_SERVERPORT);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
	pthread_mutex_unlock(&qmtx);
}

// FIXME: The following is a legacy method for old 1.3.2, remove it!
void BinaryClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_)
{
	select_db(dbpaths_, writable_, Xapian::DB_OPEN);
}

void BinaryClient::run()
{
    while (true) {
        pthread_mutex_lock(&qmtx);
        if (started && messages_queue.empty()) {
            running = false;
            pthread_mutex_unlock(&qmtx);
            break;
        }
        pthread_mutex_unlock(&qmtx);

        try {
            if (started) {
                run_one();
            } else {
                started = true;
                msg_update(std::string());
            }
        } catch (const Xapian::NetworkError &e) {
            LOG_ERR(this, "ERROR: %s\n", e.get_msg().c_str());
        } catch (...) {
            LOG_ERR(this, "ERROR!\n");
        }
    }
}

<commit_msg>RemoteProtocol is in a public xapian header after latest patches<commit_after>/*
 * Copyright deipi.com LLC and contributors. All rights reserved.
 *
 * 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 <assert.h>
#include <sys/socket.h>

#include "server.h"
#include "utils.h"
#include "client_binary.h"
#include "xapiand.h"
#include "length.h"

//
// Xapian binary client
//

BinaryClient::BinaryClient(XapiandServer *server_, ev::loop_ref *loop, int sock_, DatabasePool *database_pool_, ThreadPool *thread_pool_, double active_timeout_, double idle_timeout_)
	: BaseClient(server_, loop, sock_, database_pool_, thread_pool_, active_timeout_, idle_timeout_),
	  RemoteProtocol(std::vector<std::string>(), active_timeout_, idle_timeout_, true),
	  running(false),
      started(false)
{
	pthread_mutex_lock(&XapiandServer::static_mutex);
	int total_clients = XapiandServer::total_clients;
	int binary_clients = ++XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_CONN(this, "Got connection (sock=%d), %d binary client(s) of a total of %d connected.\n", sock, binary_clients, XapiandServer::total_clients);

	thread_pool->addTask(this);

	LOG_OBJ(this, "CREATED BINARY CLIENT! (%d clients)\n", binary_clients);
	assert(binary_clients <= total_clients);
}

BinaryClient::~BinaryClient()
{
	std::unordered_map<Xapian::Database *, Database *>::const_iterator it(databases.begin());
	for (; it != databases.end(); it++) {
		Database *database = it->second;
		database_pool->checkin(&database);
	}

	pthread_mutex_lock(&XapiandServer::static_mutex);
	int binary_clients = --XapiandServer::binary_clients;
	pthread_mutex_unlock(&XapiandServer::static_mutex);

	LOG_OBJ(this, "DELETED BINARY CLIENT! (%d clients left)\n", binary_clients);
	assert(binary_clients >= 0);
}

void BinaryClient::on_read(const char *buf, ssize_t received)
{
	buffer.append(buf, received);
	while (buffer.length() >= 2) {
		const char *o = buffer.data();
		const char *p = o;
		const char *p_end = p + buffer.size();

		message_type type = static_cast<message_type>(*p++);
		size_t len = decode_length(&p, p_end, true);
		if (len == -1) {
			return;
		}
		std::string data = std::string(p, len);
		buffer.erase(0, p - o + len);

		Buffer *msg = new Buffer(type, data.c_str(), data.size());

		messages_queue.push(msg);
	}
    pthread_mutex_lock(&qmtx);
    if (!messages_queue.empty()) {
        if (!running) {
            running = true;
            thread_pool->addTask(this);
        }
    }
    pthread_mutex_unlock(&qmtx);
}

message_type BinaryClient::get_message(double timeout, std::string & result, message_type required_type)
{
	Buffer* msg;
	if (!messages_queue.pop(msg)) {
		throw Xapian::NetworkError("No message available");
	}

	const char *msg_str = msg->dpos();
	size_t msg_size = msg->nbytes();

	std::string message = std::string(msg_str, msg_size);

	std::string buf(&msg->type, 1);
	buf += encode_length(msg_size);
	buf += message;
	LOG_BINARY_PROTO(this, "get_message: '%s'\n", repr(buf).c_str());

	result = message;

	message_type type = static_cast<message_type>(msg->type);

	delete msg;

	return type;
}

void BinaryClient::send_message(reply_type type, const std::string &message) {
	char type_as_char = static_cast<char>(type);
	std::string buf(&type_as_char, 1);
	buf += encode_length(message.size());
	buf += message;

	LOG_BINARY_PROTO(this, "send_message: '%s'\n", repr(buf).c_str());

	write(buf);
}

void BinaryClient::send_message(reply_type type, const std::string &message, double end_time)
{
	send_message(type, message);
}

void BinaryClient::shutdown()
{
	server->manager->async_shutdown.send();
}

Xapian::Database * BinaryClient::get_db(bool writable_)
{
	pthread_mutex_lock(&qmtx);
	if (endpoints.empty()) {
		pthread_mutex_unlock(&qmtx);
		return NULL;
	}
	Endpoints endpoints_ = endpoints;
	pthread_mutex_unlock(&qmtx);

	Database *database = NULL;
	if (!database_pool->checkout(&database, endpoints_, writable_)) {
		return NULL;
	}

	pthread_mutex_lock(&qmtx);
	databases[database->db] = database;
	pthread_mutex_unlock(&qmtx);

	return database->db;
}

void BinaryClient::release_db(Xapian::Database *db_)
{
	if (db_) {
		pthread_mutex_lock(&qmtx);
		Database *database = databases[db_];
		databases.erase(db_);
		pthread_mutex_unlock(&qmtx);

		database_pool->checkin(&database);
	}
}

void BinaryClient::select_db(const std::vector<std::string> &dbpaths_, bool writable_, int flags)
{
	pthread_mutex_lock(&qmtx);
	std::vector<std::string>::const_iterator i(dbpaths_.begin());
	endpoints.clear();
	for (; i != dbpaths_.end(); i++) {
		Endpoint endpoint = Endpoint(*i, std::string(), XAPIAND_BINARY_SERVERPORT);
		endpoints.push_back(endpoint);
	}
	dbpaths = dbpaths_;
	pthread_mutex_unlock(&qmtx);
}

void BinaryClient::run()
{
    while (true) {
        pthread_mutex_lock(&qmtx);
        if (started && messages_queue.empty()) {
            running = false;
            pthread_mutex_unlock(&qmtx);
            break;
        }
        pthread_mutex_unlock(&qmtx);

        try {
            if (started) {
                run_one();
            } else {
                started = true;
                msg_update(std::string());
            }
        } catch (const Xapian::NetworkError &e) {
            LOG_ERR(this, "ERROR: %s\n", e.get_msg().c_str());
        } catch (...) {
            LOG_ERR(this, "ERROR!\n");
        }
    }
}

<|endoftext|>


<commit_before>#include "graycom.h"

#ifndef _WIN32
#include <errno.h>	// errno
#endif

bool CFile::SetFilePath( LPCTSTR pszName )
{
	ADDTOCALLSTACK("CFile::SetFilePath");
	if ( pszName == NULL )
		return false;
	if ( ! m_strFileName.CompareNoCase( pszName ))
		return( true );
	bool fIsOpen = ( m_hFile != NOFILE_HANDLE );
	if ( fIsOpen )
	{
		Close();
	}
	m_strFileName = pszName;
	if ( fIsOpen )
	{
		return( Open( NULL, OF_READ|OF_BINARY )); // GetMode()	// open it back up. (in same mode as before)
	}
	return( true );
}

LPCTSTR CFile::GetFileTitle() const
{
	ADDTOCALLSTACK("CFile::GetFileTitle");
	return( CGFile::GetFilesTitle( GetFilePath()));
}

bool CFile::Open( LPCTSTR pszName, UINT uMode, CFileException * e )
{
	UNREFERENCED_PARAMETER(e);
	ASSERT( m_hFile == NOFILE_HANDLE );
	SetFilePath( pszName );

#ifdef _WIN32
	DWORD dwShareMode, dwCreationDisposition, dwDesiredAccess;

	dwDesiredAccess = GENERIC_READ;
	if ( uMode & OF_WRITE )
		dwDesiredAccess |= GENERIC_WRITE;
	if ( uMode & OF_READWRITE )
		dwDesiredAccess |= (GENERIC_READ | GENERIC_WRITE);

	if ( uMode & OF_SHARE_COMPAT )
		dwShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
	else if ( uMode & OF_SHARE_EXCLUSIVE )
		dwShareMode = 0;
	else if ( uMode & OF_SHARE_DENY_WRITE )
		dwShareMode = FILE_SHARE_READ;
	else if ( uMode & OF_SHARE_DENY_READ )
		dwShareMode = FILE_SHARE_WRITE;
	else if ( uMode & OF_SHARE_DENY_NONE )
		dwShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
	else
		dwShareMode = 0;

	if ( uMode & OF_CREATE )
		dwCreationDisposition = (OPEN_ALWAYS|CREATE_NEW);
	else
		dwCreationDisposition = OPEN_EXISTING;

	m_hFile = CreateFile( GetFilePath(), dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL );
#else // _WIN32
	m_hFile = open( GetFilePath(), uMode );
#endif // _WIN32
	return( m_hFile != NOFILE_HANDLE );
}

void CFile::Close()
{
	if ( m_hFile != NOFILE_HANDLE )
	{
#ifdef _WIN32
		CloseHandle( m_hFile );
#else
		close( m_hFile );
#endif
		m_hFile = NOFILE_HANDLE;
	}
}

DWORD CFile::GetLength()
{
	// Get the size of the file.
	DWORD lPos = GetPosition();	// save current pos.
	DWORD lSize = SeekToEnd();
	Seek( lPos, SEEK_SET );	// restore previous pos.
	return( lSize );
}

DWORD CFile::GetPosition() const
{
#ifdef _WIN32
	return SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT );
#else
	return( lseek( m_hFile, 0, SEEK_CUR ) );
#endif
}

DWORD CFile::Seek( LONG lOffset, UINT iOrigin )
{
#ifdef _WIN32
	return SetFilePointer( m_hFile, lOffset, NULL, iOrigin );
#else
	if ( m_hFile <= 0 )
		return( -1 );
	return( lseek( m_hFile, lOffset, iOrigin ));
#endif
}

DWORD CFile::Read( void * pData, DWORD dwLength ) const
{
#ifdef _WIN32
	DWORD dwRead;
	if ( ! ReadFile( m_hFile, pData, dwLength, &dwRead, NULL ))
	{
		NotifyIOError("read");
		return 0;
	}
	return dwRead;
#else
	return( read( m_hFile, pData, (long) dwLength ));
#endif
}

bool CFile::Write( const void * pData, DWORD dwLength ) const
{
#ifdef _WIN32
	DWORD dwWritten;
	BOOL ret = ::WriteFile( m_hFile, pData, dwLength, &dwWritten, NULL );
	if ( ret == FALSE )
	{
		NotifyIOError("write");
		return false;
	}
	return true;
#else
	return( write( m_hFile, (const char *) pData, (long) dwLength ) == (long) dwLength );
#endif
}

#ifdef _WIN32
void CFile::NotifyIOError( LPCTSTR szMessage )
{
	LPVOID lpMsgBuf;
	FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL );
	DEBUG_ERR(( "File I/O \"%s\" failed on file \"%s\" (%ld): %s\n", szMessage, static_cast<LPCTSTR>(GetFilePath()), GetLastError(), static_cast<LPTSTR>(lpMsgBuf) ));

	LocalFree( lpMsgBuf );
}
#endif

//***************************************************************************
// -CGFile

int CGFile::GetLastError()	// static
{
#ifdef _WIN32
	return ::GetLastError();
#else
	return errno;
#endif
}

CGString CGFile::GetMergedFileName( LPCTSTR pszBase, LPCTSTR pszName ) // static
{
	ADDTOCALLSTACK("CGFile::GetMergedFileName");
	// Merge path and file name.

	TCHAR szFilePath[ _MAX_PATH ];
	if ( pszBase && pszBase[0] )
	{
		strcpy( szFilePath, pszBase );
		int len = static_cast<int>(strlen( szFilePath ));
		if ( len && szFilePath[ len-1 ] != '\\' &&
			szFilePath[ len-1 ] != '/' )	// Might be LINUX
		{
			strcat( szFilePath, "\\" );
		}
	}
	else
	{
		szFilePath[0] = '\0';
	}
	if ( pszName )
	{
		strcat( szFilePath, pszName );
	}
	return static_cast<CGString>(szFilePath);
}

LPCTSTR CGFile::GetFilesTitle( LPCTSTR pszPath )	// static
{
	ADDTOCALLSTACK("CGFile::GetFilesTitle");
	// Just use COMMDLG.H GetFileTitle(LPCTSTR, LPTSTR, WORD) instead ?
	// strrchr
	size_t len = strlen(pszPath);
	while ( len > 0 )
	{
		len--;
		if ( pszPath[len] == '\\' || pszPath[len] == '/' )
		{
			len++;
			break;
		}
	}
	return( pszPath + len );
}

LPCTSTR CGFile::GetFilesExt( LPCTSTR pszName )	// static
{
	ADDTOCALLSTACK("CGFile::GetFilesExt");
	// get the EXTension including the .
	size_t lenall = strlen( pszName );
	size_t len = lenall;
	while ( len > 0 )
	{
		len--;
		if ( pszName[len] == '\\' || pszName[len] == '/' )
			break;
		if ( pszName[len] == '.' )
		{
			return( pszName + len );
		}
	}
	return( NULL );	// has no ext.
}

LPCTSTR CGFile::GetFileExt() const
{
	ADDTOCALLSTACK("CGFile::GetFileExt");
	// get the EXTension including the .
	return( GetFilesExt( GetFilePath()));
}

bool CGFile::OpenBase( void * pExtra )
{
	ADDTOCALLSTACK("CGFile::OpenBase");
	UNREFERENCED_PARAMETER(pExtra);

	return static_cast<CFile *>(this)->Open(GetFilePath(), GetMode());
}

void CGFile::CloseBase()
{
	ADDTOCALLSTACK("CGFile::CloseBase");
	CFile::Close();
}

bool CGFile::Open( LPCTSTR pszFilename, UINT uModeFlags, void FAR * pExtra )
{
	ADDTOCALLSTACK("CGFile::Open");
	// RETURN: true = success.
	// OF_BINARY | OF_WRITE
	if ( pszFilename == NULL )
	{
		if ( IsFileOpen())
			return( true );
	}
	else
	{
		Close();	// Make sure it's closed first.
	}

	if ( pszFilename == NULL )
		pszFilename = GetFilePath();
	else
		m_strFileName = pszFilename;

	if ( m_strFileName.IsEmpty())
		return( false );

	m_uMode = uModeFlags;
	if ( ! OpenBase( pExtra ))
		return( false );

	return( true );
}

void CGFile::Close()
{
	ADDTOCALLSTACK("CGFile::Close");
	if ( ! IsFileOpen())
		return;

	CloseBase();
	m_hFile = NOFILE_HANDLE;
}

//***************************************************************************
// -CFileText

LPCTSTR CFileText::GetModeStr() const
{
	ADDTOCALLSTACK("CFileText::GetModeStr");
	// end of line translation is crap. ftell and fseek don't work correctly when you use it.
	// fopen() args
	if ( IsBinaryMode())
		return ( IsWriteMode()) ? "wb" : "rb";
	if ( GetMode() & OF_READWRITE )
		return "a+b";
	if ( GetMode() & OF_CREATE )
		return "w";
	if ( IsWriteMode() )
		return "w";
	else
		return "rb";	// don't parse out the \n\r
}

void CFileText::CloseBase()
{
	ADDTOCALLSTACK("CFileText::CloseBase");
	if ( IsWriteMode())
	{
		fflush(m_pStream);
	}

	fclose(m_pStream);
	m_pStream = NULL;
}

bool CFileText::OpenBase( void FAR * pszExtra )
{
	ADDTOCALLSTACK("CFileText::OpenBase");
	UNREFERENCED_PARAMETER(pszExtra);

	// Open a file.
	m_pStream = fopen( GetFilePath(), GetModeStr());
	if ( m_pStream == NULL )
	{
		return( false );
	}
	// Get the low level handle for it.
	m_hFile = (OSFILE_TYPE)STDFUNC_FILENO(m_pStream);

	return( true );
}

DWORD CFileText::Seek( LONG offset, UINT origin )
{
	// true = success
	if ( ! IsFileOpen())
		return 0;
	if ( offset < 0 )
		return 0;
	if ( fseek( m_pStream, offset, origin ) != 0 )
		return 0;
	long position = ftell(m_pStream);
	if (position < 0)
		return 0;
	return position;
}

void CFileText::Flush() const
{
	if ( ! IsFileOpen())
		return;
	ASSERT(m_pStream);
	fflush(m_pStream);
}

DWORD CFileText::GetPosition() const
{
	// RETURN: -1 = error.
	if ( ! IsFileOpen())
		return static_cast<DWORD>(-1);
	return( ftell(m_pStream));
}

DWORD CFileText::Read( void * pBuffer, size_t sizemax ) const
{
	// This can return: EOF(-1) constant.
	// returns the number of full items actually read
	ASSERT(pBuffer);
	if ( IsEOF())
		return( 0 );	// LINUX will ASSERT if we read past end.
	return( static_cast<DWORD>(fread( pBuffer, 1, sizemax, m_pStream )));
}

TCHAR * CFileText::ReadString( TCHAR * pBuffer, size_t sizemax ) const
{
	// Read a line of text. NULL = EOF
	ASSERT(pBuffer);
	if ( IsEOF())
		return( NULL );	// LINUX will ASSERT if we read past end.
	return( fgets( pBuffer, static_cast<int>(sizemax), m_pStream ));
}

#ifndef _WIN32
	bool CFileText::Write( const void * pData, DWORD iLen ) const
#else
	bool CFileText::Write( const void * pData, DWORD iLen )
#endif
{
	// RETURN: 1 = success else fail.
	ASSERT(pData);
	if ( ! IsFileOpen())
		return( false );
#ifdef _WIN32 //	Windows flushing, the only safe mode to cancel it ;)
	if ( !bNoBuffer )
	{
		setvbuf(m_pStream, NULL, _IONBF, 0);
		bNoBuffer = true;
	}
#endif
	size_t iStatus = fwrite( pData, iLen, 1, m_pStream );
#ifndef _WIN32	// However, in unix, it works
	fflush( m_pStream );
#endif
	return ( iStatus == 1 );
}

bool CFileText::WriteString( LPCTSTR pStr )
{
	// RETURN: < 0 = failed.
	ASSERT(pStr);
	return( Write( pStr, static_cast<DWORD>(strlen( pStr ))));
}

bool CFileText::IsEOF() const
{
	if ( ! IsFileOpen())
		return( true );
	return(( feof( m_pStream )) ? true : false );
}

size_t CFileText::VPrintf( LPCTSTR pFormat, va_list args )
{
	ASSERT(pFormat);
	if ( ! IsFileOpen())
		return static_cast<size_t>(-1);

	size_t lenret = vfprintf( m_pStream, pFormat, args );
	return( lenret );
}

size_t _cdecl CFileText::Printf( LPCTSTR pFormat, ... )
{
	ASSERT(pFormat);
	va_list vargs;
	va_start( vargs, pFormat );
	size_t iRet = VPrintf( pFormat, vargs );
	va_end( vargs );
	return( iRet );
}
<commit_msg>Fix for problems with last doc commint and _WIN32 (sorry windows devs :-()<commit_after>#include "graycom.h"

#ifndef _WIN32
#include <errno.h>	// errno
#endif

#include "CString.h"

bool CFile::SetFilePath( LPCTSTR pszName )
{
	ADDTOCALLSTACK("CFile::SetFilePath");
	if ( pszName == NULL )
		return false;
	if ( ! m_strFileName.CompareNoCase( pszName ))
		return( true );
	bool fIsOpen = ( m_hFile != NOFILE_HANDLE );
	if ( fIsOpen )
	{
		Close();
	}
	m_strFileName = pszName;
	if ( fIsOpen )
	{
		return( Open( NULL, OF_READ|OF_BINARY )); // GetMode()	// open it back up. (in same mode as before)
	}
	return( true );
}

LPCTSTR CFile::GetFileTitle() const
{
	ADDTOCALLSTACK("CFile::GetFileTitle");
	return( CGFile::GetFilesTitle( GetFilePath()));
}

bool CFile::Open( LPCTSTR pszName, UINT uMode, CFileException * e )
{
	UNREFERENCED_PARAMETER(e);
	ASSERT( m_hFile == NOFILE_HANDLE );
	SetFilePath( pszName );

#ifdef _WIN32
	DWORD dwShareMode, dwCreationDisposition, dwDesiredAccess;

	dwDesiredAccess = GENERIC_READ;
	if ( uMode & OF_WRITE )
		dwDesiredAccess |= GENERIC_WRITE;
	if ( uMode & OF_READWRITE )
		dwDesiredAccess |= (GENERIC_READ | GENERIC_WRITE);

	if ( uMode & OF_SHARE_COMPAT )
		dwShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
	else if ( uMode & OF_SHARE_EXCLUSIVE )
		dwShareMode = 0;
	else if ( uMode & OF_SHARE_DENY_WRITE )
		dwShareMode = FILE_SHARE_READ;
	else if ( uMode & OF_SHARE_DENY_READ )
		dwShareMode = FILE_SHARE_WRITE;
	else if ( uMode & OF_SHARE_DENY_NONE )
		dwShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE);
	else
		dwShareMode = 0;

	if ( uMode & OF_CREATE )
		dwCreationDisposition = (OPEN_ALWAYS|CREATE_NEW);
	else
		dwCreationDisposition = OPEN_EXISTING;

	m_hFile = CreateFile( GetFilePath(), dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL );
#else // _WIN32
	m_hFile = open( GetFilePath(), uMode );
#endif // _WIN32
	return( m_hFile != NOFILE_HANDLE );
}

void CFile::Close()
{
	if ( m_hFile != NOFILE_HANDLE )
	{
#ifdef _WIN32
		CloseHandle( m_hFile );
#else
		close( m_hFile );
#endif
		m_hFile = NOFILE_HANDLE;
	}
}

DWORD CFile::GetLength()
{
	// Get the size of the file.
	DWORD lPos = GetPosition();	// save current pos.
	DWORD lSize = SeekToEnd();
	Seek( lPos, SEEK_SET );	// restore previous pos.
	return( lSize );
}

DWORD CFile::GetPosition() const
{
#ifdef _WIN32
	return SetFilePointer( m_hFile, 0, NULL, FILE_CURRENT );
#else
	return( lseek( m_hFile, 0, SEEK_CUR ) );
#endif
}

DWORD CFile::Seek( LONG lOffset, UINT iOrigin )
{
#ifdef _WIN32
	return SetFilePointer( m_hFile, lOffset, NULL, iOrigin );
#else
	if ( m_hFile <= 0 )
		return( -1 );
	return( lseek( m_hFile, lOffset, iOrigin ));
#endif
}

DWORD CFile::Read( void * pData, DWORD dwLength ) const
{
#ifdef _WIN32
	DWORD dwRead;
	if ( ! ReadFile( m_hFile, pData, dwLength, &dwRead, NULL ))
	{
		NotifyIOError("read");
		return 0;
	}
	return dwRead;
#else
	return( read( m_hFile, pData, (long) dwLength ));
#endif
}

bool CFile::Write( const void * pData, DWORD dwLength ) const
{
#ifdef _WIN32
	DWORD dwWritten;
	BOOL ret = ::WriteFile( m_hFile, pData, dwLength, &dwWritten, NULL );
	if ( ret == FALSE )
	{
		NotifyIOError("write");
		return false;
	}
	return true;
#else
	return( write( m_hFile, (const char *) pData, (long) dwLength ) == (long) dwLength );
#endif
}

#ifdef _WIN32
void CFile::NotifyIOError( LPCTSTR szMessage ) const
{
	LPVOID lpMsgBuf;
	FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, reinterpret_cast<LPTSTR>(&lpMsgBuf), 0, NULL );
	DEBUG_ERR(( "File I/O \"%s\" failed on file \"%s\" (%ld): %s\n", szMessage, static_cast<LPCTSTR>(GetFilePath()), GetLastError(), static_cast<LPTSTR>(lpMsgBuf) ));

	LocalFree( lpMsgBuf );
}
#endif

//***************************************************************************
// -CGFile

int CGFile::GetLastError()	// static
{
#ifdef _WIN32
	return ::GetLastError();
#else
	return errno;
#endif
}

CGString CGFile::GetMergedFileName( LPCTSTR pszBase, LPCTSTR pszName ) // static
{
	ADDTOCALLSTACK("CGFile::GetMergedFileName");
	// Merge path and file name.

	TCHAR szFilePath[ _MAX_PATH ];
	if ( pszBase && pszBase[0] )
	{
		strcpy( szFilePath, pszBase );
		int len = static_cast<int>(strlen( szFilePath ));
		if ( len && szFilePath[ len-1 ] != '\\' &&
			szFilePath[ len-1 ] != '/' )	// Might be LINUX
		{
			strcat( szFilePath, "\\" );
		}
	}
	else
	{
		szFilePath[0] = '\0';
	}
	if ( pszName )
	{
		strcat( szFilePath, pszName );
	}
	return static_cast<CGString>(szFilePath);
}

LPCTSTR CGFile::GetFilesTitle( LPCTSTR pszPath )	// static
{
	ADDTOCALLSTACK("CGFile::GetFilesTitle");
	// Just use COMMDLG.H GetFileTitle(LPCTSTR, LPTSTR, WORD) instead ?
	// strrchr
	size_t len = strlen(pszPath);
	while ( len > 0 )
	{
		len--;
		if ( pszPath[len] == '\\' || pszPath[len] == '/' )
		{
			len++;
			break;
		}
	}
	return( pszPath + len );
}

LPCTSTR CGFile::GetFilesExt( LPCTSTR pszName )	// static
{
	ADDTOCALLSTACK("CGFile::GetFilesExt");
	// get the EXTension including the .
	size_t lenall = strlen( pszName );
	size_t len = lenall;
	while ( len > 0 )
	{
		len--;
		if ( pszName[len] == '\\' || pszName[len] == '/' )
			break;
		if ( pszName[len] == '.' )
		{
			return( pszName + len );
		}
	}
	return( NULL );	// has no ext.
}

LPCTSTR CGFile::GetFileExt() const
{
	ADDTOCALLSTACK("CGFile::GetFileExt");
	// get the EXTension including the .
	return( GetFilesExt( GetFilePath()));
}

bool CGFile::OpenBase( void * pExtra )
{
	ADDTOCALLSTACK("CGFile::OpenBase");
	UNREFERENCED_PARAMETER(pExtra);

	return static_cast<CFile *>(this)->Open(GetFilePath(), GetMode());
}

void CGFile::CloseBase()
{
	ADDTOCALLSTACK("CGFile::CloseBase");
	CFile::Close();
}

bool CGFile::Open( LPCTSTR pszFilename, UINT uModeFlags, void FAR * pExtra )
{
	ADDTOCALLSTACK("CGFile::Open");
	// RETURN: true = success.
	// OF_BINARY | OF_WRITE
	if ( pszFilename == NULL )
	{
		if ( IsFileOpen())
			return( true );
	}
	else
	{
		Close();	// Make sure it's closed first.
	}

	if ( pszFilename == NULL )
		pszFilename = GetFilePath();
	else
		m_strFileName = pszFilename;

	if ( m_strFileName.IsEmpty())
		return( false );

	m_uMode = uModeFlags;
	if ( ! OpenBase( pExtra ))
		return( false );

	return( true );
}

void CGFile::Close()
{
	ADDTOCALLSTACK("CGFile::Close");
	if ( ! IsFileOpen())
		return;

	CloseBase();
	m_hFile = NOFILE_HANDLE;
}

//***************************************************************************
// -CFileText

LPCTSTR CFileText::GetModeStr() const
{
	ADDTOCALLSTACK("CFileText::GetModeStr");
	// end of line translation is crap. ftell and fseek don't work correctly when you use it.
	// fopen() args
	if ( IsBinaryMode())
		return ( IsWriteMode()) ? "wb" : "rb";
	if ( GetMode() & OF_READWRITE )
		return "a+b";
	if ( GetMode() & OF_CREATE )
		return "w";
	if ( IsWriteMode() )
		return "w";
	else
		return "rb";	// don't parse out the \n\r
}

void CFileText::CloseBase()
{
	ADDTOCALLSTACK("CFileText::CloseBase");
	if ( IsWriteMode())
	{
		fflush(m_pStream);
	}

	fclose(m_pStream);
	m_pStream = NULL;
}

bool CFileText::OpenBase( void FAR * pszExtra )
{
	ADDTOCALLSTACK("CFileText::OpenBase");
	UNREFERENCED_PARAMETER(pszExtra);

	// Open a file.
	m_pStream = fopen( GetFilePath(), GetModeStr());
	if ( m_pStream == NULL )
	{
		return( false );
	}
	// Get the low level handle for it.
	m_hFile = (OSFILE_TYPE)STDFUNC_FILENO(m_pStream);

	return( true );
}

DWORD CFileText::Seek( LONG offset, UINT origin )
{
	// true = success
	if ( ! IsFileOpen())
		return 0;
	if ( offset < 0 )
		return 0;
	if ( fseek( m_pStream, offset, origin ) != 0 )
		return 0;
	long position = ftell(m_pStream);
	if (position < 0)
		return 0;
	return position;
}

void CFileText::Flush() const
{
	if ( ! IsFileOpen())
		return;
	ASSERT(m_pStream);
	fflush(m_pStream);
}

DWORD CFileText::GetPosition() const
{
	// RETURN: -1 = error.
	if ( ! IsFileOpen())
		return static_cast<DWORD>(-1);
	return( ftell(m_pStream));
}

DWORD CFileText::Read( void * pBuffer, size_t sizemax ) const
{
	// This can return: EOF(-1) constant.
	// returns the number of full items actually read
	ASSERT(pBuffer);
	if ( IsEOF())
		return( 0 );	// LINUX will ASSERT if we read past end.
	return( static_cast<DWORD>(fread( pBuffer, 1, sizemax, m_pStream )));
}

TCHAR * CFileText::ReadString( TCHAR * pBuffer, size_t sizemax ) const
{
	// Read a line of text. NULL = EOF
	ASSERT(pBuffer);
	if ( IsEOF())
		return( NULL );	// LINUX will ASSERT if we read past end.
	return( fgets( pBuffer, static_cast<int>(sizemax), m_pStream ));
}

#ifndef _WIN32
	bool CFileText::Write( const void * pData, DWORD iLen ) const
#else
	bool CFileText::Write( const void * pData, DWORD iLen )
#endif
{
	// RETURN: 1 = success else fail.
	ASSERT(pData);
	if ( ! IsFileOpen())
		return( false );
#ifdef _WIN32 //	Windows flushing, the only safe mode to cancel it ;)
	if ( !bNoBuffer )
	{
		setvbuf(m_pStream, NULL, _IONBF, 0);
		bNoBuffer = true;
	}
#endif
	size_t iStatus = fwrite( pData, iLen, 1, m_pStream );
#ifndef _WIN32	// However, in unix, it works
	fflush( m_pStream );
#endif
	return ( iStatus == 1 );
}

bool CFileText::WriteString( LPCTSTR pStr )
{
	// RETURN: < 0 = failed.
	ASSERT(pStr);
	return( Write( pStr, static_cast<DWORD>(strlen( pStr ))));
}

bool CFileText::IsEOF() const
{
	if ( ! IsFileOpen())
		return( true );
	return(( feof( m_pStream )) ? true : false );
}

size_t CFileText::VPrintf( LPCTSTR pFormat, va_list args )
{
	ASSERT(pFormat);
	if ( ! IsFileOpen())
		return static_cast<size_t>(-1);

	size_t lenret = vfprintf( m_pStream, pFormat, args );
	return( lenret );
}

size_t _cdecl CFileText::Printf( LPCTSTR pFormat, ... )
{
	ASSERT(pFormat);
	va_list vargs;
	va_start( vargs, pFormat );
	size_t iRet = VPrintf( pFormat, vargs );
	va_end( vargs );
	return( iRet );
}
<|endoftext|>