<commit_before>/**
    Copyright (c) 2017 Ryan Porter
    You may use, distribute, or modify this code under the terms of the MIT license.
*/

#include <stdio.h>
#include <vector>

#include "parseArgs.h"
#include "polyDeformerWeights.h"
#include "polySymmetryNode.h"
#include "sceneCache.h"
#include "selection.h"

#include <maya/MArgList.h>
#include <maya/MArgDatabase.h>
#include <maya/MDagPath.h>
#include <maya/MFnMesh.h>
#include <maya/MFnWeightGeometryFilter.h>
#include <maya/MFloatArray.h>
#include <maya/MGlobal.h>
#include <maya/MItGeometry.h>
#include <maya/MObject.h>
#include <maya/MObjectArray.h>
#include <maya/MPxCommand.h>
#include <maya/MSelectionList.h>
#include <maya/MString.h>
#include <maya/MStatus.h>
#include <maya/MSyntax.h>

using namespace std;

// Indicates the source deformer.
#define SOURCE_DEFORMER_FLAG            "-sd"
#define SOURCE_DEFORMER_LONG_FLAG       "-sourceDeformer"

// Indicates the source deformed mesh.
#define SOURCE_MESH_FLAG               "-sm"
#define SOURCE_MESH_LONG_FLAG          "-sourceMesh"

// Indicates the destination deformer.
#define DESTINATION_DEFORMER_FLAG       "-dd"
#define DESTINATION_DEFORMER_LONG_FLAG  "-destinationDeformer"

// Indicates the destination deformed shape.
#define DESTINATION_MESH_FLAG          "-dm"
#define DESTINATION_MESH_LONG_FLAG     "-destinationMesh"

// Indicates the direction of the mirror or flip action - 1 (left to right) or -1 (right to left)
#define DIRECTION_FLAG                  "-d"
#define DIRECTION_LONG_FLAG             "-direction"

// Indicates that the deformer weights should be mirrored.
#define MIRROR_FLAG                     "-m"
#define MIRROR_LONG_FLAG                "-mirror"

// Indicates that the deformer weights should be flipped.
#define FLIP_FLAG                       "-f"
#define FLIP_LONG_FLAG                  "-flip"

#define RETURN_IF_ERROR(s) if (!s) { return s; }

PolyDeformerWeightsCommand::PolyDeformerWeightsCommand() {}
PolyDeformerWeightsCommand::~PolyDeformerWeightsCommand() {}

void* PolyDeformerWeightsCommand::creator()
{
    return new PolyDeformerWeightsCommand();
}

MSyntax PolyDeformerWeightsCommand::getSyntax()
{
    MSyntax syntax;

    syntax.addFlag(SOURCE_DEFORMER_FLAG, SOURCE_DEFORMER_LONG_FLAG, MSyntax::kSelectionItem);
    syntax.addFlag(SOURCE_MESH_FLAG, SOURCE_MESH_LONG_FLAG, MSyntax::kSelectionItem);

    syntax.addFlag(DESTINATION_DEFORMER_FLAG, DESTINATION_DEFORMER_LONG_FLAG, MSyntax::kSelectionItem);
    syntax.addFlag(DESTINATION_MESH_FLAG, DESTINATION_MESH_LONG_FLAG, MSyntax::kSelectionItem);

    syntax.addFlag(DIRECTION_FLAG, DIRECTION_LONG_FLAG, MSyntax::kLong);

    syntax.addFlag(MIRROR_FLAG, MIRROR_LONG_FLAG);
    syntax.addFlag(FLIP_FLAG, FLIP_LONG_FLAG);

    syntax.enableEdit(false);
    syntax.enableQuery(false);

    return syntax;
}

MStatus PolyDeformerWeightsCommand::parseArguments(MArgDatabase &argsData)
{
    MStatus status;

    status = parseArgs::getNodeArgument(argsData, SOURCE_DEFORMER_FLAG, this->sourceDeformer, true);
    RETURN_IF_ERROR(status);

    status = parseArgs::getNodeArgument(argsData, DESTINATION_DEFORMER_FLAG, this->destinationDeformer, false);
    RETURN_IF_ERROR(status);

    status = parseArgs::getDagPathArgument(argsData, SOURCE_MESH_FLAG, this->sourceMesh, true);
    RETURN_IF_ERROR(status);

    status = parseArgs::getDagPathArgument(argsData, DESTINATION_MESH_FLAG, this->destinationMesh, false);
    RETURN_IF_ERROR(status);

    this->mirrorWeights = argsData.isFlagSet(MIRROR_FLAG);
    this->flipWeights = argsData.isFlagSet(FLIP_FLAG);

    status = argsData.getFlagArgument(DIRECTION_FLAG, 0, this->direction);

    return MStatus::kSuccess;
}

MStatus PolyDeformerWeightsCommand::validateArguments()
{
    MStatus status;

    if (this->direction != 1 && this->direction != -1)
    {
        MString errorMsg("^1s/^2s flag should be 1 (left to right) or -1 (right to left)");
        errorMsg.format(errorMsg, MString(DIRECTION_LONG_FLAG), MString(DIRECTION_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (!parseArgs::isNodeType(sourceDeformer, MFn::kWeightGeometryFilt))
    {
        MString errorMsg("A deformer node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(SOURCE_DEFORMER_LONG_FLAG), MString(SOURCE_DEFORMER_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (destinationDeformer.isNull())
    {
        destinationDeformer = sourceDeformer;
    } else if (!destinationDeformer.hasFn(MFn::kWeightGeometryFilt)) {
        MString errorMsg("A deformer node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(DESTINATION_DEFORMER_LONG_FLAG), MString(DESTINATION_DEFORMER_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (!parseArgs::isNodeType(this->sourceMesh, MFn::kMesh))
    {
        MString errorMsg("A mesh node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(SOURCE_MESH_LONG_FLAG), MString(SOURCE_MESH_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (destinationMesh.node().isNull())
    {
        destinationMesh.set(sourceMesh);
    } else if (!destinationMesh.hasFn(MFn::kMesh)) {
        MString errorMsg("A mesh node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(DESTINATION_MESH_LONG_FLAG), MString(DESTINATION_MESH_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    } else {
        MFnMesh fnSourceMesh(this->sourceMesh);
        MFnMesh fnDestinationMesh(this->destinationMesh);

        if (fnSourceMesh.numVertices() != fnDestinationMesh.numVertices())
        {
            MString errorMsg("Source mesh and destination mesh are not point compatible. Cannot continue.");
            MGlobal::displayError(errorMsg);
            return MStatus::kFailure;
        }
    }

    MSelectionList activeSelection;
    MSelectionList vertexSelection;

    MGlobal::getActiveSelectionList(activeSelection);

    if (destinationMesh.node().hasFn(MFn::kMesh)) { destinationMesh.pop(); }
    getSelectedComponents(destinationMesh, activeSelection, vertexSelection, MFn::Type::kMeshVertComponent);

    if (!vertexSelection.isEmpty())
    {
        vertexSelection.getDagPath(0, destinationMesh, components);
    }

    if (!sourceMesh.node().hasFn(MFn::kMesh))
    {
        sourceMesh.extendToShapeDirectlyBelow(0);
    }

    if (!destinationMesh.node().hasFn(MFn::kMesh))
    {
        destinationMesh.extendToShapeDirectlyBelow(0);
    }

    if (mirrorWeights || flipWeights)
    {
        bool cacheHit = PolySymmetryCache::getNodeFromCache(this->sourceMesh, this->polySymmetryData);

        if (!cacheHit)
        {
            MString errorMsg("Mesh specified with the ^1s/^2s flag must have an associated ^3s node.");
            errorMsg.format(errorMsg, MString(SOURCE_MESH_LONG_FLAG), MString(SOURCE_MESH_FLAG), PolySymmetryNode::NODE_NAME);

            MGlobal::displayError(errorMsg);
            return MStatus::kFailure;
        }
    }

    return MStatus::kSuccess;
}

MStatus PolyDeformerWeightsCommand::doIt(const MArgList& argList)
{
    MStatus status;

    MArgDatabase argsData(syntax(), argList, &status);
    RETURN_IF_ERROR(status);

    status = this->parseArguments(argsData);
    RETURN_IF_ERROR(status);

    status = this->validateArguments();
    RETURN_IF_ERROR(status);

    return this->redoIt();
}

MStatus PolyDeformerWeightsCommand::redoIt()
{
    MStatus status;

    MFnWeightGeometryFilter fnSourceDeformer(this->sourceDeformer, &status);
    MFnWeightGeometryFilter fnDestinationDeformer(this->destinationDeformer, &status);

    MObjectArray sourceOutputGeometry;
    MObjectArray destinationOutputGeometry;

    int numberOfVertices = MFnMesh(this->sourceMesh).numVertices();

    MObject sourceComponents;
    MObject destinationComponents;

    getAllComponents(this->sourceMesh, sourceComponents);
    getAllComponents(this->destinationMesh, destinationComponents);

    MFloatArray sourceWeights(numberOfVertices);
    MFloatArray destinationWeights(numberOfVertices);

    oldWeightValues.setLength(numberOfVertices);

    uint sourceGeometryIndex = fnSourceDeformer.indexForOutputShape(this->sourceMesh.node(), &status);

    if (!status)
    {
        MString errorMsg("Source mesh ^1s is not deformed by deformer ^2s.");
        errorMsg.format(errorMsg, this->sourceMesh.partialPathName(), MFnDependencyNode(this->sourceDeformer).name());

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    uint destinationGeometryIndex = fnDestinationDeformer.indexForOutputShape(this->destinationMesh.node(), &status);

    if (!status)
    {
        MString errorMsg("Destination mesh ^1s is not deformed by deformer ^2s.");
        errorMsg.format(errorMsg, this->destinationMesh.partialPathName(), MFnDependencyNode(this->destinationDeformer).name());

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    status = fnSourceDeformer.getWeights(sourceGeometryIndex, sourceComponents, sourceWeights);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    status = fnDestinationDeformer.getWeights(destinationGeometryIndex, destinationComponents, oldWeightValues);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    destinationWeights.copy(sourceWeights);

    if (mirrorWeights || flipWeights)
    {
        vector<int> vertexSymmetry;
        vector<int> vertexSides;

        MFnDependencyNode fnNode(polySymmetryData);

        PolySymmetryNode::getValues(fnNode, VERTEX_SYMMETRY, vertexSymmetry);
        PolySymmetryNode::getValues(fnNode, VERTEX_SIDES, vertexSides);

        MItGeometry itGeo(destinationMesh, components);

        bool useVertexSelection = !components.isNull();

        while (!itGeo.isDone())
        {
            int i = itGeo.index();
            int o = vertexSymmetry[i];

            float wti = this->getWeight(i, sourceWeights, vertexSymmetry, vertexSides);
            destinationWeights.set(wti, i);

            if (useVertexSelection)
            {
                float wto = this->getWeight(o, sourceWeights, vertexSymmetry, vertexSides);
                destinationWeights.set(wto, o);
            }

            itGeo.next();
        }
    }

    status = fnDestinationDeformer.setWeight(this->destinationMesh, destinationGeometryIndex, destinationComponents, destinationWeights);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    this->geometryIndex = destinationGeometryIndex;
    this->components = destinationComponents;

    return MStatus::kSuccess;
}

float PolyDeformerWeightsCommand::getWeight(int vertexIndex, MFloatArray &sourceWeights, vector<int> &vertexSymmetry, vector<int> vertexSides)
{
    int i = vertexIndex;
    int o = vertexSymmetry[i];
    float wt = sourceWeights[i];

    if (flipWeights || (mirrorWeights && vertexSides[i] != direction))
    {
        wt = sourceWeights[o];
    }

    return wt;
}

MStatus PolyDeformerWeightsCommand::undoIt()
{
    MStatus status;

    MFnWeightGeometryFilter fnDeformer(this->destinationDeformer, &status);

    status = fnDeformer.setWeight(
        this->destinationMesh,
        this->geometryIndex,
        this->components,
        this->oldWeightValues
    );

    CHECK_MSTATUS_AND_RETURN_IT(status);

    return MStatus::kSuccess;
}<commit_msg>polyDeformerWeights uses new getAllVertices function.<commit_after>/**
    Copyright (c) 2017 Ryan Porter
    You may use, distribute, or modify this code under the terms of the MIT license.
*/

#include <stdio.h>
#include <vector>

#include "parseArgs.h"
#include "polyDeformerWeights.h"
#include "polySymmetryNode.h"
#include "sceneCache.h"
#include "selection.h"

#include <maya/MArgList.h>
#include <maya/MArgDatabase.h>
#include <maya/MDagPath.h>
#include <maya/MFnMesh.h>
#include <maya/MFnWeightGeometryFilter.h>
#include <maya/MFloatArray.h>
#include <maya/MGlobal.h>
#include <maya/MItGeometry.h>
#include <maya/MObject.h>
#include <maya/MObjectArray.h>
#include <maya/MPxCommand.h>
#include <maya/MSelectionList.h>
#include <maya/MString.h>
#include <maya/MStatus.h>
#include <maya/MSyntax.h>

using namespace std;

// Indicates the source deformer.
#define SOURCE_DEFORMER_FLAG            "-sd"
#define SOURCE_DEFORMER_LONG_FLAG       "-sourceDeformer"

// Indicates the source deformed mesh.
#define SOURCE_MESH_FLAG               "-sm"
#define SOURCE_MESH_LONG_FLAG          "-sourceMesh"

// Indicates the destination deformer.
#define DESTINATION_DEFORMER_FLAG       "-dd"
#define DESTINATION_DEFORMER_LONG_FLAG  "-destinationDeformer"

// Indicates the destination deformed shape.
#define DESTINATION_MESH_FLAG          "-dm"
#define DESTINATION_MESH_LONG_FLAG     "-destinationMesh"

// Indicates the direction of the mirror or flip action - 1 (left to right) or -1 (right to left)
#define DIRECTION_FLAG                  "-d"
#define DIRECTION_LONG_FLAG             "-direction"

// Indicates that the deformer weights should be mirrored.
#define MIRROR_FLAG                     "-m"
#define MIRROR_LONG_FLAG                "-mirror"

// Indicates that the deformer weights should be flipped.
#define FLIP_FLAG                       "-f"
#define FLIP_LONG_FLAG                  "-flip"

#define RETURN_IF_ERROR(s) if (!s) { return s; }

PolyDeformerWeightsCommand::PolyDeformerWeightsCommand() {}
PolyDeformerWeightsCommand::~PolyDeformerWeightsCommand() {}

void* PolyDeformerWeightsCommand::creator()
{
    return new PolyDeformerWeightsCommand();
}

MSyntax PolyDeformerWeightsCommand::getSyntax()
{
    MSyntax syntax;

    syntax.addFlag(SOURCE_DEFORMER_FLAG, SOURCE_DEFORMER_LONG_FLAG, MSyntax::kSelectionItem);
    syntax.addFlag(SOURCE_MESH_FLAG, SOURCE_MESH_LONG_FLAG, MSyntax::kSelectionItem);

    syntax.addFlag(DESTINATION_DEFORMER_FLAG, DESTINATION_DEFORMER_LONG_FLAG, MSyntax::kSelectionItem);
    syntax.addFlag(DESTINATION_MESH_FLAG, DESTINATION_MESH_LONG_FLAG, MSyntax::kSelectionItem);

    syntax.addFlag(DIRECTION_FLAG, DIRECTION_LONG_FLAG, MSyntax::kLong);

    syntax.addFlag(MIRROR_FLAG, MIRROR_LONG_FLAG);
    syntax.addFlag(FLIP_FLAG, FLIP_LONG_FLAG);

    syntax.enableEdit(false);
    syntax.enableQuery(false);

    return syntax;
}

MStatus PolyDeformerWeightsCommand::parseArguments(MArgDatabase &argsData)
{
    MStatus status;

    status = parseArgs::getNodeArgument(argsData, SOURCE_DEFORMER_FLAG, this->sourceDeformer, true);
    RETURN_IF_ERROR(status);

    status = parseArgs::getNodeArgument(argsData, DESTINATION_DEFORMER_FLAG, this->destinationDeformer, false);
    RETURN_IF_ERROR(status);

    status = parseArgs::getDagPathArgument(argsData, SOURCE_MESH_FLAG, this->sourceMesh, true);
    RETURN_IF_ERROR(status);

    status = parseArgs::getDagPathArgument(argsData, DESTINATION_MESH_FLAG, this->destinationMesh, false);
    RETURN_IF_ERROR(status);

    this->mirrorWeights = argsData.isFlagSet(MIRROR_FLAG);
    this->flipWeights = argsData.isFlagSet(FLIP_FLAG);

    status = argsData.getFlagArgument(DIRECTION_FLAG, 0, this->direction);

    return MStatus::kSuccess;
}

MStatus PolyDeformerWeightsCommand::validateArguments()
{
    MStatus status;

    if (this->direction != 1 && this->direction != -1)
    {
        MString errorMsg("^1s/^2s flag should be 1 (left to right) or -1 (right to left)");
        errorMsg.format(errorMsg, MString(DIRECTION_LONG_FLAG), MString(DIRECTION_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (!parseArgs::isNodeType(sourceDeformer, MFn::kWeightGeometryFilt))
    {
        MString errorMsg("A deformer node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(SOURCE_DEFORMER_LONG_FLAG), MString(SOURCE_DEFORMER_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (destinationDeformer.isNull())
    {
        destinationDeformer = sourceDeformer;
    } else if (!destinationDeformer.hasFn(MFn::kWeightGeometryFilt)) {
        MString errorMsg("A deformer node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(DESTINATION_DEFORMER_LONG_FLAG), MString(DESTINATION_DEFORMER_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (!parseArgs::isNodeType(this->sourceMesh, MFn::kMesh))
    {
        MString errorMsg("A mesh node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(SOURCE_MESH_LONG_FLAG), MString(SOURCE_MESH_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    if (destinationMesh.node().isNull())
    {
        destinationMesh.set(sourceMesh);
    } else if (!destinationMesh.hasFn(MFn::kMesh)) {
        MString errorMsg("A mesh node should be specified with the ^1s/^2s flag.");
        errorMsg.format(errorMsg, MString(DESTINATION_MESH_LONG_FLAG), MString(DESTINATION_MESH_FLAG));

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    } else {
        MFnMesh fnSourceMesh(this->sourceMesh);
        MFnMesh fnDestinationMesh(this->destinationMesh);

        if (fnSourceMesh.numVertices() != fnDestinationMesh.numVertices())
        {
            MString errorMsg("Source mesh and destination mesh are not point compatible. Cannot continue.");
            MGlobal::displayError(errorMsg);
            return MStatus::kFailure;
        }
    }

    MSelectionList activeSelection;
    MSelectionList vertexSelection;

    MGlobal::getActiveSelectionList(activeSelection);

    if (destinationMesh.node().hasFn(MFn::kMesh)) { destinationMesh.pop(); }
    getSelectedComponents(destinationMesh, activeSelection, vertexSelection, MFn::Type::kMeshVertComponent);

    if (!vertexSelection.isEmpty())
    {
        vertexSelection.getDagPath(0, destinationMesh, components);
    }

    if (!sourceMesh.node().hasFn(MFn::kMesh))
    {
        sourceMesh.extendToShapeDirectlyBelow(0);
    }

    if (!destinationMesh.node().hasFn(MFn::kMesh))
    {
        destinationMesh.extendToShapeDirectlyBelow(0);
    }

    if (mirrorWeights || flipWeights)
    {
        bool cacheHit = PolySymmetryCache::getNodeFromCache(this->sourceMesh, this->polySymmetryData);

        if (!cacheHit)
        {
            MString errorMsg("Mesh specified with the ^1s/^2s flag must have an associated ^3s node.");
            errorMsg.format(errorMsg, MString(SOURCE_MESH_LONG_FLAG), MString(SOURCE_MESH_FLAG), PolySymmetryNode::NODE_NAME);

            MGlobal::displayError(errorMsg);
            return MStatus::kFailure;
        }
    }

    return MStatus::kSuccess;
}

MStatus PolyDeformerWeightsCommand::doIt(const MArgList& argList)
{
    MStatus status;

    MArgDatabase argsData(syntax(), argList, &status);
    RETURN_IF_ERROR(status);

    status = this->parseArguments(argsData);
    RETURN_IF_ERROR(status);

    status = this->validateArguments();
    RETURN_IF_ERROR(status);

    return this->redoIt();
}

MStatus PolyDeformerWeightsCommand::redoIt()
{
    MStatus status;

    MFnWeightGeometryFilter fnSourceDeformer(this->sourceDeformer, &status);
    MFnWeightGeometryFilter fnDestinationDeformer(this->destinationDeformer, &status);

    MObjectArray sourceOutputGeometry;
    MObjectArray destinationOutputGeometry;

    int numberOfVertices = MFnMesh(this->sourceMesh).numVertices();

    MObject sourceComponents;
    MObject destinationComponents;

    getAllVertices(numberOfVertices, sourceComponents);
    getAllVertices(numberOfVertices, destinationComponents);

    MFloatArray sourceWeights(numberOfVertices);
    MFloatArray destinationWeights(numberOfVertices);

    oldWeightValues.setLength(numberOfVertices);

    uint sourceGeometryIndex = fnSourceDeformer.indexForOutputShape(this->sourceMesh.node(), &status);

    if (!status)
    {
        MString errorMsg("Source mesh ^1s is not deformed by deformer ^2s.");
        errorMsg.format(errorMsg, this->sourceMesh.partialPathName(), MFnDependencyNode(this->sourceDeformer).name());

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    uint destinationGeometryIndex = fnDestinationDeformer.indexForOutputShape(this->destinationMesh.node(), &status);

    if (!status)
    {
        MString errorMsg("Destination mesh ^1s is not deformed by deformer ^2s.");
        errorMsg.format(errorMsg, this->destinationMesh.partialPathName(), MFnDependencyNode(this->destinationDeformer).name());

        MGlobal::displayError(errorMsg);
        return MStatus::kFailure;
    }

    status = fnSourceDeformer.getWeights(sourceGeometryIndex, sourceComponents, sourceWeights);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    status = fnDestinationDeformer.getWeights(destinationGeometryIndex, destinationComponents, oldWeightValues);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    destinationWeights.copy(sourceWeights);

    if (mirrorWeights || flipWeights)
    {
        vector<int> vertexSymmetry;
        vector<int> vertexSides;

        MFnDependencyNode fnNode(polySymmetryData);

        PolySymmetryNode::getValues(fnNode, VERTEX_SYMMETRY, vertexSymmetry);
        PolySymmetryNode::getValues(fnNode, VERTEX_SIDES, vertexSides);

        MItGeometry itGeo(destinationMesh, components);

        bool useVertexSelection = !components.isNull();

        while (!itGeo.isDone())
        {
            int i = itGeo.index();
            int o = vertexSymmetry[i];

            float wti = this->getWeight(i, sourceWeights, vertexSymmetry, vertexSides);
            destinationWeights.set(wti, i);

            if (useVertexSelection)
            {
                float wto = this->getWeight(o, sourceWeights, vertexSymmetry, vertexSides);
                destinationWeights.set(wto, o);
            }

            itGeo.next();
        }
    }

    status = fnDestinationDeformer.setWeight(this->destinationMesh, destinationGeometryIndex, destinationComponents, destinationWeights);
    CHECK_MSTATUS_AND_RETURN_IT(status);

    this->geometryIndex = destinationGeometryIndex;
    this->components = destinationComponents;

    return MStatus::kSuccess;
}

float PolyDeformerWeightsCommand::getWeight(int vertexIndex, MFloatArray &sourceWeights, vector<int> &vertexSymmetry, vector<int> vertexSides)
{
    int i = vertexIndex;
    int o = vertexSymmetry[i];
    float wt = sourceWeights[i];

    if (flipWeights || (mirrorWeights && vertexSides[i] != direction))
    {
        wt = sourceWeights[o];
    }

    return wt;
}

MStatus PolyDeformerWeightsCommand::undoIt()
{
    MStatus status;

    MFnWeightGeometryFilter fnDeformer(this->destinationDeformer, &status);

    status = fnDeformer.setWeight(
        this->destinationMesh,
        this->geometryIndex,
        this->components,
        this->oldWeightValues
    );

    CHECK_MSTATUS_AND_RETURN_IT(status);

    return MStatus::kSuccess;
}<|endoftext|>


<commit_before>#include "quicksand.h"
#include "marian.h"

#if MKL_FOUND
#include "mkl.h"
#endif

#include "data/shortlist.h"
#include "translator/beam_search.h"
#include "translator/scorers.h"
#include "data/alignment.h"
#include "data/vocab_base.h"
#include "tensors/cpu/expression_graph_packable.h"
#include "layers/lsh.h"

#if USE_FBGEMM
#include "fbgemm/Utils.h"
#endif

namespace marian {

namespace quicksand {

template <class T>
void set(Ptr<Options> options, const std::string& key, const T& value) {
  options->set(key, value);
}

template void set(Ptr<Options> options, const std::string& key, const size_t&);
template void set(Ptr<Options> options, const std::string& key, const int&);
template void set(Ptr<Options> options, const std::string& key, const std::string&);
template void set(Ptr<Options> options, const std::string& key, const bool&);
template void set(Ptr<Options> options, const std::string& key, const std::vector<std::string>&);
template void set(Ptr<Options> options, const std::string& key, const std::vector<int>&);
template void set(Ptr<Options> options, const std::string& key, const float&);
template void set(Ptr<Options> options, const std::string& key, const double&);

Ptr<Options> newOptions() {
  return New<Options>();
}

class VocabWrapper : public IVocabWrapper {
  Ptr<Vocab> pImpl_;
public:
  VocabWrapper(Ptr<Vocab> vocab) : pImpl_(vocab) {}
  virtual ~VocabWrapper() {}
  WordIndex encode(const std::string& word) const override { return (*pImpl_)[word].toWordIndex(); }
  std::string decode(WordIndex id) const override { return (*pImpl_)[Word::fromWordIndex(id)]; }
  size_t size() const override { return pImpl_->size(); }
  void transcodeToShortlistInPlace(WordIndex* ptr, size_t num) const override { pImpl_->transcodeToShortlistInPlace(ptr, num); }
  Ptr<Vocab> getVocab() const { return pImpl_; }
};

class BeamSearchDecoder : public IBeamSearchDecoder {
private:
  Ptr<ExpressionGraph> graph_;
  Ptr<cpu::WrappedDevice> device_;

  std::vector<Ptr<Scorer>> scorers_;

  std::vector<Ptr<Vocab>> vocabs_;

public:
  BeamSearchDecoder(Ptr<Options> options,
                    const std::vector<const void*>& ptrs,
                    const std::vector<Ptr<IVocabWrapper>>& vocabs)
      : IBeamSearchDecoder(options, ptrs) {

    // copy the vocabs
    for (auto vi : vocabs)
      vocabs_.push_back(std::dynamic_pointer_cast<VocabWrapper>(vi)->getVocab());

    // setting 16-bit optimization to false for now. Re-enable with better caching or pre-computation
    graph_ = New<ExpressionGraph>(/*inference=*/true);

    DeviceId deviceId{0, DeviceType::cpu};
    device_ = New<cpu::WrappedDevice>(deviceId);
    graph_->setDevice(deviceId, device_);

#if MKL_FOUND
    mkl_set_num_threads(options_->get<int>("mkl-threads", 1));
#endif

    std::vector<std::string> models
        = options_->get<std::vector<std::string>>("model");

    for(int i = 0; i < models.size(); ++i) {
      Ptr<Options> modelOpts = New<Options>();

      YAML::Node config;
      if(io::isBin(models[i]) && ptrs_[i] != nullptr)
        io::getYamlFromModel(config, "special:model.yml", ptrs_[i]);
      else
        io::getYamlFromModel(config, "special:model.yml", models[i]);

      modelOpts->merge(options_);
      modelOpts->merge(config);

      std::cerr << modelOpts->asYamlString() << std::flush; // @TODO: take a look at why this is even here.

      auto encdec = models::createModelFromOptions(modelOpts, models::usage::translation);

      if(io::isBin(models[i]) && ptrs_[i] != nullptr) {
        // if file ends in *.bin and has been mapped by QuickSAND
        scorers_.push_back(New<ScorerWrapper>(
          encdec, "F" + std::to_string(scorers_.size()), /*weight=*/1.0f, ptrs[i]));
      } else {
        // it's a *.npz file or has not been mapped by QuickSAND
        scorers_.push_back(New<ScorerWrapper>(
          encdec, "F" + std::to_string(scorers_.size()), /*weight=*/1.0f, models[i]));
      }
    }

    for(auto scorer : scorers_) {
      scorer->init(graph_);
    }

    // run parameter init once, this is required for graph_->get("parameter name") to work correctly
    graph_->forward();
  }

  void setWorkspace(uint8_t* data, size_t size) override { device_->set(data, size); }

  QSNBestBatch decode(const QSBatch& qsBatch,
                      size_t maxLength,
                      const std::unordered_set<WordIndex>& shortlist) override {

    std::vector<int> lshOpts = options_->get<std::vector<int>>("output-approx-knn", {});
    ABORT_IF(lshOpts.size() != 0 && lshOpts.size() != 2, "--output-approx-knn takes 2 parameters");
    ABORT_IF(lshOpts.size() == 2 && shortlist.size() > 0, "LSH and shortlist cannot be used at the same time");

    if(lshOpts.size() == 2 || shortlist.size() > 0) {
      Ptr<data::ShortlistGenerator> shortListGen;
      // both ShortListGenerators are thin wrappers, hence no problem with calling this per query
      if(lshOpts.size() == 2) {
        // Setting abortIfDynamic to true disallows memory allocation for LSH parameters, this is specifically for use in Quicksand.
        // If we want to use the LSH in Quicksand we need to create a binary model that contains the LSH parameters via conversion.
        shortListGen = New<data::LSHShortlistGenerator>(lshOpts[0], lshOpts[1], vocabs_[1]->lemmaSize(), /*abortIfDynamic=*/true);
      } else {
        shortListGen = New<data::FakeShortlistGenerator>(shortlist);
      }
      for(auto scorer : scorers_)
        scorer->setShortlistGenerator(shortListGen);
    }

    // form source batch, by interleaving the words over sentences in the batch, and setting the mask
    size_t batchSize = qsBatch.size();
    auto subBatch = New<data::SubBatch>(batchSize, maxLength, vocabs_[0]);
    for(size_t i = 0; i < maxLength; ++i) {
      for(size_t j = 0; j < batchSize; ++j) {
        const auto& sent = qsBatch[j];
        if(i < sent.size()) {
          size_t idx = i * batchSize + j;
          subBatch->data()[idx] = marian::Word::fromWordIndex(sent[i]);
          subBatch->mask()[idx] = 1;
        }
      }
    }
    auto tgtSubBatch = New<data::SubBatch>(batchSize, 0, vocabs_[1]); // only holds a vocab, but data is dummy
    std::vector<Ptr<data::SubBatch>> subBatches{ subBatch, tgtSubBatch };
    std::vector<size_t> sentIds(batchSize, 0);

    auto batch = New<data::CorpusBatch>(subBatches);
    batch->setSentenceIds(sentIds);

    // decode
    auto search = New<BeamSearch>(options_, scorers_, vocabs_[1]);
    Histories histories = search->search(graph_, batch);

    // convert to QuickSAND format
    QSNBestBatch qsNbestBatch;
    for(const auto& history : histories) { // loop over batch entries
      QSNBest qsNbest;
      NBestList nbestHyps = history->nBest(SIZE_MAX); // request as many N as we have
      for (const Result& result : nbestHyps) { // loop over N-best entries
        // get hypothesis word sequence and normalized sentence score
        auto words = std::get<0>(result);
        auto score = std::get<2>(result);
        // determine alignment if present
        AlignmentSets alignmentSets;
        if (options_->hasAndNotEmpty("alignment"))
        {
          float alignmentThreshold;
          auto alignment = options_->get<std::string>("alignment"); // @TODO: this logic now exists three times in Marian
          if (alignment == "soft")
            alignmentThreshold = 0.0f;
          else if (alignment == "hard")
            alignmentThreshold = 1.0f;
          else
            alignmentThreshold = std::max(std::stof(alignment), 0.f);
          auto hyp = std::get<1>(result);
          data::WordAlignment align = data::ConvertSoftAlignToHardAlign(hyp->tracebackAlignment(), alignmentThreshold);
          // convert to QuickSAND format
          alignmentSets.resize(words.size());
          for (const auto& p : align)
            alignmentSets[p.tgtPos].insert({p.srcPos, p.prob}); // [trgPos] -> {(srcPos, P(srcPos|trgPos))}
        }
        // form hypothesis to return
        qsNbest.emplace_back(toWordIndexVector(words), std::move(alignmentSets), score);
      }
      qsNbestBatch.push_back(qsNbest);
    }

    return qsNbestBatch;
  }
};

Ptr<IBeamSearchDecoder> newDecoder(Ptr<Options> options,
                                   const std::vector<const void*>& ptrs,
                                   const std::vector<Ptr<IVocabWrapper>>& vocabs,
                                   WordIndex eosDummy) { // @TODO: remove this parameter
  marian::setThrowExceptionOnAbort(true); // globally defined to throw now
  ABORT_IF(marian::Word::fromWordIndex(eosDummy) != std::dynamic_pointer_cast<VocabWrapper>(vocabs[1])->getVocab()->getEosId(), "Inconsistent eos vs. vocabs_[1]");

  return New<BeamSearchDecoder>(options, ptrs, vocabs/*, eos*/);
}

std::vector<Ptr<IVocabWrapper>> loadVocabs(const std::vector<std::string>& vocabPaths) {
  std::vector<Ptr<IVocabWrapper>> res(vocabPaths.size());
  for (size_t i = 0; i < vocabPaths.size(); i++) {
    if (i > 0 && vocabPaths[i] == vocabPaths[i-1]) {
      res[i] = res[i-1];
      LOG(info, "[data] Input {} sharing vocabulary with input {}", i, i-1);
    }
    else {
      auto vocab = New<Vocab>(New<Options>(), i); // (empty options, since they are only used for creating vocabs)
      auto size = vocab->load(vocabPaths[i]);
      LOG(info, "[data] Loaded vocabulary size for input {} of size {}", i, size);
      res[i] = New<VocabWrapper>(vocab);
    }
  }
  return res;
}

// query CPU AVX version
DecoderCpuAvxVersion getCpuAvxVersion() {
#if USE_FBGEMM
  // Default value is AVX
  DecoderCpuAvxVersion cpuAvxVer = DecoderCpuAvxVersion::AVX;
  if (fbgemm::fbgemmHasAvx512Support())
    cpuAvxVer = DecoderCpuAvxVersion::AVX512;
  else if (fbgemm::fbgemmHasAvx2Support())
    cpuAvxVer = DecoderCpuAvxVersion::AVX2;

  return cpuAvxVer;
#else
  // Default value is AVX
  return DecoderCpuAvxVersion::AVX;
#endif
}

DecoderCpuAvxVersion parseCpuAvxVersion(std::string name) {
  if (name == "avx") {
    return DecoderCpuAvxVersion::AVX;
  } else if (name == "avx2") {
    return DecoderCpuAvxVersion::AVX2;
  } else if (name == "avx512") {
    return DecoderCpuAvxVersion::AVX512;
  } else {
    ABORT("Unknown CPU Instruction Set: {}", name);
    return DecoderCpuAvxVersion::AVX;
  }
}

// This function converts an fp32 model into an FBGEMM based packed model.
// marian defined types are used for external project as well.
// The targetPrec is passed as int32_t for the exported function definition.
bool convertModel(std::string inputFile, std::string outputFile, int32_t targetPrec, int32_t lshNBits) {
  std::cerr << "Converting from: " << inputFile << ", to: " << outputFile << ", precision: " << targetPrec << std::endl;

  YAML::Node config;
  std::stringstream configStr;
  marian::io::getYamlFromModel(config, "special:model.yml", inputFile);
  configStr << config;

  auto graph = New<ExpressionGraphPackable>();
  graph->setDevice(CPU0);

  graph->load(inputFile);

  // MJD: Note, this is a default settings which we might want to change or expose. Use this only with Polonium students.
  // The LSH will not be used by default even if it exists in the model. That has to be enabled in the decoder config.
  std::string lshOutputWeights = "Wemb";
  bool addLsh = lshNBits > 0;
  if(addLsh) {
    std::cerr << "Adding LSH to model with hash size " << lshNBits << std::endl;
    // Add dummy parameters for the LSH before the model gets actually initialized.
    // This create the parameters with useless values in the tensors, but it gives us the memory we need.
    graph->setReloaded(false);
    lsh::addDummyParameters(graph, /*weights=*/lshOutputWeights, /*nBits=*/lshNBits);
    graph->setReloaded(true);
  }

  graph->forward();  // run the initializers

  if(addLsh) {
    // After initialization, hijack the paramters for the LSH and force-overwrite with correct values.
    // Once this is done we can just pack and save as normal.
    lsh::overwriteDummyParameters(graph, /*weights=*/lshOutputWeights);
  }

  Type targetPrecType = (Type) targetPrec;
  if (targetPrecType == Type::packed16
      || targetPrecType == Type::packed8avx2
      || targetPrecType == Type::packed8avx512) {
    graph->packAndSave(outputFile, configStr.str(), targetPrecType);
    std::cerr << "Conversion Finished." << std::endl;
  } else {
    ABORT("Target type is not supported in this funcion: {}", targetPrec);
    return false;
  }

  return true;
}

}  // namespace quicksand
}  // namespace marian
<commit_msg>allow float32 conversion in QS interface<commit_after>#include "quicksand.h"
#include "marian.h"

#if MKL_FOUND
#include "mkl.h"
#endif

#include "data/shortlist.h"
#include "translator/beam_search.h"
#include "translator/scorers.h"
#include "data/alignment.h"
#include "data/vocab_base.h"
#include "tensors/cpu/expression_graph_packable.h"
#include "layers/lsh.h"

#if USE_FBGEMM
#include "fbgemm/Utils.h"
#endif

namespace marian {

namespace quicksand {

template <class T>
void set(Ptr<Options> options, const std::string& key, const T& value) {
  options->set(key, value);
}

template void set(Ptr<Options> options, const std::string& key, const size_t&);
template void set(Ptr<Options> options, const std::string& key, const int&);
template void set(Ptr<Options> options, const std::string& key, const std::string&);
template void set(Ptr<Options> options, const std::string& key, const bool&);
template void set(Ptr<Options> options, const std::string& key, const std::vector<std::string>&);
template void set(Ptr<Options> options, const std::string& key, const std::vector<int>&);
template void set(Ptr<Options> options, const std::string& key, const float&);
template void set(Ptr<Options> options, const std::string& key, const double&);

Ptr<Options> newOptions() {
  return New<Options>();
}

class VocabWrapper : public IVocabWrapper {
  Ptr<Vocab> pImpl_;
public:
  VocabWrapper(Ptr<Vocab> vocab) : pImpl_(vocab) {}
  virtual ~VocabWrapper() {}
  WordIndex encode(const std::string& word) const override { return (*pImpl_)[word].toWordIndex(); }
  std::string decode(WordIndex id) const override { return (*pImpl_)[Word::fromWordIndex(id)]; }
  size_t size() const override { return pImpl_->size(); }
  void transcodeToShortlistInPlace(WordIndex* ptr, size_t num) const override { pImpl_->transcodeToShortlistInPlace(ptr, num); }
  Ptr<Vocab> getVocab() const { return pImpl_; }
};

class BeamSearchDecoder : public IBeamSearchDecoder {
private:
  Ptr<ExpressionGraph> graph_;
  Ptr<cpu::WrappedDevice> device_;

  std::vector<Ptr<Scorer>> scorers_;

  std::vector<Ptr<Vocab>> vocabs_;

public:
  BeamSearchDecoder(Ptr<Options> options,
                    const std::vector<const void*>& ptrs,
                    const std::vector<Ptr<IVocabWrapper>>& vocabs)
      : IBeamSearchDecoder(options, ptrs) {

    // copy the vocabs
    for (auto vi : vocabs)
      vocabs_.push_back(std::dynamic_pointer_cast<VocabWrapper>(vi)->getVocab());

    // setting 16-bit optimization to false for now. Re-enable with better caching or pre-computation
    graph_ = New<ExpressionGraph>(/*inference=*/true);

    DeviceId deviceId{0, DeviceType::cpu};
    device_ = New<cpu::WrappedDevice>(deviceId);
    graph_->setDevice(deviceId, device_);

#if MKL_FOUND
    mkl_set_num_threads(options_->get<int>("mkl-threads", 1));
#endif

    std::vector<std::string> models
        = options_->get<std::vector<std::string>>("model");

    for(int i = 0; i < models.size(); ++i) {
      Ptr<Options> modelOpts = New<Options>();

      YAML::Node config;
      if(io::isBin(models[i]) && ptrs_[i] != nullptr)
        io::getYamlFromModel(config, "special:model.yml", ptrs_[i]);
      else
        io::getYamlFromModel(config, "special:model.yml", models[i]);

      modelOpts->merge(options_);
      modelOpts->merge(config);

      std::cerr << modelOpts->asYamlString() << std::flush; // @TODO: take a look at why this is even here.

      auto encdec = models::createModelFromOptions(modelOpts, models::usage::translation);

      if(io::isBin(models[i]) && ptrs_[i] != nullptr) {
        // if file ends in *.bin and has been mapped by QuickSAND
        scorers_.push_back(New<ScorerWrapper>(
          encdec, "F" + std::to_string(scorers_.size()), /*weight=*/1.0f, ptrs[i]));
      } else {
        // it's a *.npz file or has not been mapped by QuickSAND
        scorers_.push_back(New<ScorerWrapper>(
          encdec, "F" + std::to_string(scorers_.size()), /*weight=*/1.0f, models[i]));
      }
    }

    for(auto scorer : scorers_) {
      scorer->init(graph_);
    }

    // run parameter init once, this is required for graph_->get("parameter name") to work correctly
    graph_->forward();
  }

  void setWorkspace(uint8_t* data, size_t size) override { device_->set(data, size); }

  QSNBestBatch decode(const QSBatch& qsBatch,
                      size_t maxLength,
                      const std::unordered_set<WordIndex>& shortlist) override {

    std::vector<int> lshOpts = options_->get<std::vector<int>>("output-approx-knn", {});
    ABORT_IF(lshOpts.size() != 0 && lshOpts.size() != 2, "--output-approx-knn takes 2 parameters");
    ABORT_IF(lshOpts.size() == 2 && shortlist.size() > 0, "LSH and shortlist cannot be used at the same time");

    if(lshOpts.size() == 2 || shortlist.size() > 0) {
      Ptr<data::ShortlistGenerator> shortListGen;
      // both ShortListGenerators are thin wrappers, hence no problem with calling this per query
      if(lshOpts.size() == 2) {
        // Setting abortIfDynamic to true disallows memory allocation for LSH parameters, this is specifically for use in Quicksand.
        // If we want to use the LSH in Quicksand we need to create a binary model that contains the LSH parameters via conversion.
        shortListGen = New<data::LSHShortlistGenerator>(lshOpts[0], lshOpts[1], vocabs_[1]->lemmaSize(), /*abortIfDynamic=*/true);
      } else {
        shortListGen = New<data::FakeShortlistGenerator>(shortlist);
      }
      for(auto scorer : scorers_)
        scorer->setShortlistGenerator(shortListGen);
    }

    // form source batch, by interleaving the words over sentences in the batch, and setting the mask
    size_t batchSize = qsBatch.size();
    auto subBatch = New<data::SubBatch>(batchSize, maxLength, vocabs_[0]);
    for(size_t i = 0; i < maxLength; ++i) {
      for(size_t j = 0; j < batchSize; ++j) {
        const auto& sent = qsBatch[j];
        if(i < sent.size()) {
          size_t idx = i * batchSize + j;
          subBatch->data()[idx] = marian::Word::fromWordIndex(sent[i]);
          subBatch->mask()[idx] = 1;
        }
      }
    }
    auto tgtSubBatch = New<data::SubBatch>(batchSize, 0, vocabs_[1]); // only holds a vocab, but data is dummy
    std::vector<Ptr<data::SubBatch>> subBatches{ subBatch, tgtSubBatch };
    std::vector<size_t> sentIds(batchSize, 0);

    auto batch = New<data::CorpusBatch>(subBatches);
    batch->setSentenceIds(sentIds);

    // decode
    auto search = New<BeamSearch>(options_, scorers_, vocabs_[1]);
    Histories histories = search->search(graph_, batch);

    // convert to QuickSAND format
    QSNBestBatch qsNbestBatch;
    for(const auto& history : histories) { // loop over batch entries
      QSNBest qsNbest;
      NBestList nbestHyps = history->nBest(SIZE_MAX); // request as many N as we have
      for (const Result& result : nbestHyps) { // loop over N-best entries
        // get hypothesis word sequence and normalized sentence score
        auto words = std::get<0>(result);
        auto score = std::get<2>(result);
        // determine alignment if present
        AlignmentSets alignmentSets;
        if (options_->hasAndNotEmpty("alignment"))
        {
          float alignmentThreshold;
          auto alignment = options_->get<std::string>("alignment"); // @TODO: this logic now exists three times in Marian
          if (alignment == "soft")
            alignmentThreshold = 0.0f;
          else if (alignment == "hard")
            alignmentThreshold = 1.0f;
          else
            alignmentThreshold = std::max(std::stof(alignment), 0.f);
          auto hyp = std::get<1>(result);
          data::WordAlignment align = data::ConvertSoftAlignToHardAlign(hyp->tracebackAlignment(), alignmentThreshold);
          // convert to QuickSAND format
          alignmentSets.resize(words.size());
          for (const auto& p : align)
            alignmentSets[p.tgtPos].insert({p.srcPos, p.prob}); // [trgPos] -> {(srcPos, P(srcPos|trgPos))}
        }
        // form hypothesis to return
        qsNbest.emplace_back(toWordIndexVector(words), std::move(alignmentSets), score);
      }
      qsNbestBatch.push_back(qsNbest);
    }

    return qsNbestBatch;
  }
};

Ptr<IBeamSearchDecoder> newDecoder(Ptr<Options> options,
                                   const std::vector<const void*>& ptrs,
                                   const std::vector<Ptr<IVocabWrapper>>& vocabs,
                                   WordIndex eosDummy) { // @TODO: remove this parameter
  marian::setThrowExceptionOnAbort(true); // globally defined to throw now
  ABORT_IF(marian::Word::fromWordIndex(eosDummy) != std::dynamic_pointer_cast<VocabWrapper>(vocabs[1])->getVocab()->getEosId(), "Inconsistent eos vs. vocabs_[1]");

  return New<BeamSearchDecoder>(options, ptrs, vocabs/*, eos*/);
}

std::vector<Ptr<IVocabWrapper>> loadVocabs(const std::vector<std::string>& vocabPaths) {
  std::vector<Ptr<IVocabWrapper>> res(vocabPaths.size());
  for (size_t i = 0; i < vocabPaths.size(); i++) {
    if (i > 0 && vocabPaths[i] == vocabPaths[i-1]) {
      res[i] = res[i-1];
      LOG(info, "[data] Input {} sharing vocabulary with input {}", i, i-1);
    }
    else {
      auto vocab = New<Vocab>(New<Options>(), i); // (empty options, since they are only used for creating vocabs)
      auto size = vocab->load(vocabPaths[i]);
      LOG(info, "[data] Loaded vocabulary size for input {} of size {}", i, size);
      res[i] = New<VocabWrapper>(vocab);
    }
  }
  return res;
}

// query CPU AVX version
DecoderCpuAvxVersion getCpuAvxVersion() {
#if USE_FBGEMM
  // Default value is AVX
  DecoderCpuAvxVersion cpuAvxVer = DecoderCpuAvxVersion::AVX;
  if (fbgemm::fbgemmHasAvx512Support())
    cpuAvxVer = DecoderCpuAvxVersion::AVX512;
  else if (fbgemm::fbgemmHasAvx2Support())
    cpuAvxVer = DecoderCpuAvxVersion::AVX2;

  return cpuAvxVer;
#else
  // Default value is AVX
  return DecoderCpuAvxVersion::AVX;
#endif
}

DecoderCpuAvxVersion parseCpuAvxVersion(std::string name) {
  if (name == "avx") {
    return DecoderCpuAvxVersion::AVX;
  } else if (name == "avx2") {
    return DecoderCpuAvxVersion::AVX2;
  } else if (name == "avx512") {
    return DecoderCpuAvxVersion::AVX512;
  } else {
    ABORT("Unknown CPU Instruction Set: {}", name);
    return DecoderCpuAvxVersion::AVX;
  }
}

// This function converts an fp32 model into an FBGEMM based packed model.
// marian defined types are used for external project as well.
// The targetPrec is passed as int32_t for the exported function definition.
bool convertModel(std::string inputFile, std::string outputFile, int32_t targetPrec, int32_t lshNBits) {
  std::cerr << "Converting from: " << inputFile << ", to: " << outputFile << ", precision: " << targetPrec << std::endl;

  YAML::Node config;
  std::stringstream configStr;
  marian::io::getYamlFromModel(config, "special:model.yml", inputFile);
  configStr << config;

  auto graph = New<ExpressionGraphPackable>();
  graph->setDevice(CPU0);

  graph->load(inputFile);

  // MJD: Note, this is a default settings which we might want to change or expose. Use this only with Polonium students.
  // The LSH will not be used by default even if it exists in the model. That has to be enabled in the decoder config.
  std::string lshOutputWeights = "Wemb";
  bool addLsh = lshNBits > 0;
  if(addLsh) {
    std::cerr << "Adding LSH to model with hash size " << lshNBits << std::endl;
    // Add dummy parameters for the LSH before the model gets actually initialized.
    // This create the parameters with useless values in the tensors, but it gives us the memory we need.
    graph->setReloaded(false);
    lsh::addDummyParameters(graph, /*weights=*/lshOutputWeights, /*nBits=*/lshNBits);
    graph->setReloaded(true);
  }

  graph->forward();  // run the initializers

  if(addLsh) {
    // After initialization, hijack the paramters for the LSH and force-overwrite with correct values.
    // Once this is done we can just pack and save as normal.
    lsh::overwriteDummyParameters(graph, /*weights=*/lshOutputWeights);
  }

  Type targetPrecType = (Type) targetPrec;
  if (targetPrecType == Type::packed16
      || targetPrecType == Type::packed8avx2
      || targetPrecType == Type::packed8avx512
      || (targetPrecType == Type::float32 && addLsh)) { // only allow non-conversion to float32 if we also use the LSH
    graph->packAndSave(outputFile, configStr.str(), targetPrecType);
    std::cerr << "Conversion Finished." << std::endl;
  } else {
    ABORT("Target type is not supported in this funcion: {}", targetPrec);
    return false;
  }

  return true;
}

}  // namespace quicksand
}  // namespace marian
<|endoftext|>


<commit_before>#include "messagedialog.h"

#include "qommunicate.h"
#include "messenger.h"
#include "constants.h"
#include "filehandler.h"

MessageDialog::MessageDialog(Member* receiver, QWidget *parent) : QDialog(parent)
{
    receivers << receiver;

    ui.setupUi(this);
    messageTimer = NULL;
    setWindowTitle(tr("Conversation: %1").arg(receiver->name()));
    connect(messenger(), SIGNAL(msg_recvMsg(Message)), this, SLOT(incomingMessage(Message)));
    connect(messenger(), SIGNAL(msg_recvConfirmMsg(Message)), this, SLOT(messageRecvConfirm()));

    ((Qommunicate*)parent)->dialogOpened(receiver);

}

MessageDialog::MessageDialog(QList<Member*> receivers, QWidget *parent) : QDialog(parent)
{
    this->receivers = receivers;

    ui.setupUi(this);

    QStringList titleRecvs;
    Member* t;
    foreach(t, receivers)
        titleRecvs << t->name();
    setWindowTitle(tr("Conversation: %1").arg(titleRecvs.join(",")));
}

MessageDialog::MessageDialog(QWidget *parent) : QDialog(parent)
{
    ui.setupUi(this);
    setWindowTitle(tr("Multicast message"));
}

void MessageDialog::closeEvent(QCloseEvent *evt)
{
    if(receivers.size() == 1)
        ((Qommunicate*) parent())->dialogClosed(receivers[0]);
    evt->accept();
}

void MessageDialog::incomingMessage(Message msg)
{
    ui.messageEdit->append(QString("<%1> \n").arg(receivers[0]->name()));
    ui.messageEdit->append(msg.payload().replace('\a', ""));

    if(msg.command() & QOM_SENDCHECKOPT)
        messenger()->sendMessage(QOM_RECVMSG, QByteArray::number(msg.packetNo()), receivers[0]);
}

void MessageDialog::on_sendButton_clicked()
{
    if(ui.messageInput->text().trimmed().isEmpty())
        return;

    if(receivers.isEmpty())
    {
        messenger()->multicast(QOM_SENDMSG, ui.messageInput->text().toAscii());
        QTimer::singleShot(500, this, SLOT(accept()));
        return;
    }

    foreach(Member* m, receivers)
    {
        messenger()->sendMessage(
            QOM_SENDMSG|QOM_SENDCHECKOPT |
            ( ui.notifyReadCB->isChecked() ? QOM_SECRETOPT | QOM_READCHECKOPT : 0 ),
            //( ui.sealCB->isChecked() ? QOM_SECRETOPT : 0 ) ,
            ui.messageInput->text().toAscii(), m);
    }
    if(receivers.size() == 1)
    {
        if(messageTimer != NULL)
            delete messageTimer;
        messageTimer = new QTimer(this);
        messageTimer->setSingleShot(true);
        connect(messageTimer, SIGNAL(timeout()), this, SLOT(messageTimeout()));

        ui.messageInput->setEnabled(false);
        messageTimer->start(5000);
    }
    else
    {
        messageRecvConfirm();
        QTimer::singleShot(500, this, SLOT(accept()));
    }

}

void MessageDialog::messageTimeout()
{
    if(QMessageBox::warning(this,
            tr("Sending Failed"),
            tr("Failed to send message. To retry click Ok"),
            QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
    {
        on_sendButton_clicked();
    }
    else
        ui.messageInput->setEnabled(true);
}

void MessageDialog::messageRecvConfirm()
{
    if(! ui.messageInput->text().trimmed().isEmpty())
        ui.messageEdit->append(QString("<%1> %2").arg(me().name()).arg(ui.messageInput->text()));

    ui.messageInput->clear();
    ui.messageInput->setFocus();
    ui.messageInput->setEnabled(true);

    if(receivers.size() == 1 && messageTimer != NULL)
        messageTimer->stop();
}

void MessageDialog::dropEvent(QDropEvent *evt)
{
    QStringList files;
    foreach(QUrl url, evt->mimeData()->urls())
    {
        files << url.toLocalFile();
    }

    foreach(Member* to, receivers)
    {
        fileHandler()->sendFilesRequest(files, to, "");
    }
}<commit_msg>Strip incoming before appending<commit_after>#include "messagedialog.h"

#include "qommunicate.h"
#include "messenger.h"
#include "constants.h"
#include "filehandler.h"

MessageDialog::MessageDialog(Member* receiver, QWidget *parent) : QDialog(parent)
{
    receivers << receiver;

    ui.setupUi(this);
    messageTimer = NULL;
    setWindowTitle(tr("Conversation: %1").arg(receiver->name()));
    connect(messenger(), SIGNAL(msg_recvMsg(Message)), this, SLOT(incomingMessage(Message)));
    connect(messenger(), SIGNAL(msg_recvConfirmMsg(Message)), this, SLOT(messageRecvConfirm()));

    ((Qommunicate*)parent)->dialogOpened(receiver);

}

MessageDialog::MessageDialog(QList<Member*> receivers, QWidget *parent) : QDialog(parent)
{
    this->receivers = receivers;

    ui.setupUi(this);

    QStringList titleRecvs;
    Member* t;
    foreach(t, receivers)
        titleRecvs << t->name();
    setWindowTitle(tr("Conversation: %1").arg(titleRecvs.join(",")));
}

MessageDialog::MessageDialog(QWidget *parent) : QDialog(parent)
{
    ui.setupUi(this);
    setWindowTitle(tr("Multicast message"));
}

void MessageDialog::closeEvent(QCloseEvent *evt)
{
    if(receivers.size() == 1)
        ((Qommunicate*) parent())->dialogClosed(receivers[0]);
    evt->accept();
}

void MessageDialog::incomingMessage(Message msg)
{
    ui.messageEdit->append(QString("<%1> \n").arg(receivers[0]->name()));
    ui.messageEdit->append(msg.payload().replace('\a', "").trimmed());

    if(msg.command() & QOM_SENDCHECKOPT)
        messenger()->sendMessage(QOM_RECVMSG, QByteArray::number(msg.packetNo()), receivers[0]);
}

void MessageDialog::on_sendButton_clicked()
{
    if(ui.messageInput->text().trimmed().isEmpty())
        return;

    if(receivers.isEmpty())
    {
        messenger()->multicast(QOM_SENDMSG, ui.messageInput->text().toAscii());
        QTimer::singleShot(500, this, SLOT(accept()));
        return;
    }

    foreach(Member* m, receivers)
    {
        messenger()->sendMessage(
            QOM_SENDMSG|QOM_SENDCHECKOPT |
            ( ui.notifyReadCB->isChecked() ? QOM_SECRETOPT | QOM_READCHECKOPT : 0 ),
            //( ui.sealCB->isChecked() ? QOM_SECRETOPT : 0 ) ,
            ui.messageInput->text().toAscii(), m);
    }
    if(receivers.size() == 1)
    {
        if(messageTimer != NULL)
            delete messageTimer;
        messageTimer = new QTimer(this);
        messageTimer->setSingleShot(true);
        connect(messageTimer, SIGNAL(timeout()), this, SLOT(messageTimeout()));

        ui.messageInput->setEnabled(false);
        messageTimer->start(5000);
    }
    else
    {
        messageRecvConfirm();
        QTimer::singleShot(500, this, SLOT(accept()));
    }

}

void MessageDialog::messageTimeout()
{
    if(QMessageBox::warning(this,
            tr("Sending Failed"),
            tr("Failed to send message. To retry click Ok"),
            QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok)
    {
        on_sendButton_clicked();
    }
    else
        ui.messageInput->setEnabled(true);
}

void MessageDialog::messageRecvConfirm()
{
    if(! ui.messageInput->text().trimmed().isEmpty())
        ui.messageEdit->append(QString("<%1> %2").arg(me().name()).arg(ui.messageInput->text()));

    ui.messageInput->clear();
    ui.messageInput->setFocus();
    ui.messageInput->setEnabled(true);

    if(receivers.size() == 1 && messageTimer != NULL)
        messageTimer->stop();
}

void MessageDialog::dropEvent(QDropEvent *evt)
{
    QStringList files;
    foreach(QUrl url, evt->mimeData()->urls())
    {
        files << url.toLocalFile();
    }

    foreach(Member* to, receivers)
    {
        fileHandler()->sendFilesRequest(files, to, "");
    }
}<|endoftext|>


<commit_before>/**
 * @file metal_scanner.cpp
 * @author Alex Thorne
 * @version 1.0
 */

#include "metal_scanner.h"

MetalScanner::MetalScanner(void) : ArmController()
{
	//initialize
    reference_inductance = 0;
    origin_x = (X_MAX_BOUNDARY + X_MIN_BOUNDARY) / 2;
    origin_y = (Y_MAX_BOUNDARY + Y_MIN_BOUNDARY) / 2;
    origin_z = (Z_MAX_BOUNDARY + Z_MIN_BOUNDARY) / 2;
    desired_delta_uH = DEFAULT_DELTA_uH;
    uH_tolerance = DEFAULT_uH_TOLERANCE;
}

void MetalScanner::HardwareSetup(void)
{
    metal_detector.ConnectToSensor();
    metal_detector.ConfigureSensor();
    AttachMotors();
}

void MetalScanner::SetReferenceInductance(void)
{
    metal_detector.Update();
    reference_inductance = metal_detector.GetCh0InductanceuH();
}

uint8_t MetalScanner::SetScanOrigin(float x, float y, float z)
{
    if (x > X_MAX_BOUNDARY || x < X_MIN_BOUNDARY)
        return 1;
    if (y > Y_MAX_BOUNDARY || y < Y_MIN_BOUNDARY)
        return 1;
    if (z > Z_MAX_BOUNDARY || z < Z_MIN_BOUNDARY)
        return 1;
    origin_x = x;
    origin_y = y;
    origin_z = z;
    return 0;
}

uint8_t MetalScanner::SetDesiredInductanceDelta(float inductance)
{
    if (inductance < 0 || inductance > 3.0)
        return 1;
    desired_delta_uH = inductance;
    return 0;
}

uint8_t MetalScanner::SetInductanceDeltaTolerance(float tolerance)
{
    if (tolerance < 0)
        return 1;
    uH_tolerance = tolerance;
    return 0;
}
<commit_msg>Adds additional sampling to SetReferenceInductance<commit_after>/**
 * @file metal_scanner.cpp
 * @author Alex Thorne
 * @version 1.0
 */

#include "metal_scanner.h"

int float_compare(const void *val1, const void *val2);

MetalScanner::MetalScanner(void) : ArmController()
{
	//initialize
    reference_inductance = 0;
    origin_x = (X_MAX_BOUNDARY + X_MIN_BOUNDARY) / 2;
    origin_y = (Y_MAX_BOUNDARY + Y_MIN_BOUNDARY) / 2;
    origin_z = (Z_MAX_BOUNDARY + Z_MIN_BOUNDARY) / 2;
    desired_delta_uH = DEFAULT_DELTA_uH;
    uH_tolerance = DEFAULT_uH_TOLERANCE;
}

void MetalScanner::HardwareSetup(void)
{
    metal_detector.ConnectToSensor();
    metal_detector.ConfigureSensor();
    AttachMotors();
}

void MetalScanner::SetReferenceInductance(void)
{
    float inductance_vals[20];
    for (uint8_t i = 0; i < 20; i ++)
    {
        delay(50);
        metal_detector.Update();
        inductance_vals[i] = metal_detector.GetCh0InductanceuH();
    }
    qsort(inductance_vals, 20, sizeof(inductance_vals[0]), float_compare);
    reference_inductance = (inductance_vals[9] + inductance_vals[10]) / 2.0;
}

uint8_t MetalScanner::SetScanOrigin(float x, float y, float z)
{
    if (x > X_MAX_BOUNDARY || x < X_MIN_BOUNDARY)
        return 1;
    if (y > Y_MAX_BOUNDARY || y < Y_MIN_BOUNDARY)
        return 1;
    if (z > Z_MAX_BOUNDARY || z < Z_MIN_BOUNDARY)
        return 1;
    origin_x = x;
    origin_y = y;
    origin_z = z;
    return 0;
}

uint8_t MetalScanner::SetDesiredInductanceDelta(float inductance)
{
    if (inductance < 0 || inductance > 3.0)
        return 1;
    desired_delta_uH = inductance;
    return 0;
}

uint8_t MetalScanner::SetInductanceDeltaTolerance(float tolerance)
{
    if (tolerance < 0)
        return 1;
    uH_tolerance = tolerance;
    return 0;
}

int float_compare(const void *val1, const void *val2)
{
    float fval1 = *((float *)val1);
    float fval2 = *((float *)val2);

    if (fval1 < fval2)
    {
        return -1;
    }
    else if (fval1 > fval2)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <set>

using namespace std;

string func_pattern;
string void_func_pattern;
set<string> filter;

void init_filter()
{
  filter.clear();
#define add(x) filter.insert(#x)
  add(const);
  add(void);
  add(int);
  add(unsigned);
  add(pthread_mutex_t);
  add(restrict);
  add(pthread_mutexattr_t);
  add(struct);
  add(timespec);
  add(pthread_cond_t);
  add(sem_t);
  add(pthread_barrier_t);
  add(pthread_barrierattr_t);
  add(useconds_t);
  add(__useconds_t);
  add(sockaddr);
  add(size_t);
  add(ssize_t);
  add(msghdr);
  add(socklen_t);
  add(epoll_event);
#undef add
}

inline bool goodchar(char ch)
{
  return ch <= 'Z' && ch >= 'A'
    || ch <= 'z' && ch >= 'a'
    || ch <= '9' && ch >= '0'
    || ch == '_';
}

void replace(string &st, string pat, string tobe)
{
	while (true)
	{
		int p = st.find(pat);
		if (p == string::npos) return;
		st = st.replace(p, pat.size(), tobe);
	}
}

void print_func(
	FILE *file,
	const char *func_ret_type,
	const char *func_name,
	const char *args_with_name,
	const char *args_without_name,
	const char *args_only_name,
	const char *lib_path
	)
{
	string pattern = !strcmp(func_ret_type, "void") ? void_func_pattern : func_pattern;
	replace(pattern, "FUNC_RET_TYPE", func_ret_type);
	replace(pattern, "FUNC_NAME", func_name);
	replace(pattern, "ARGS_WITH_NAME", args_with_name);
	replace(pattern, "ARGS_WITHOUT_NAME", args_without_name);
	replace(pattern, "ARGS_ONLY_NAME", args_only_name);
	replace(pattern, "LIB_PATH", lib_path);
	fprintf(file, "%s", pattern.c_str());
}

char buffer[1024];

bool read_line(FILE *fin, string &ret)
{
	while (true)
	{
		if (!fgets(buffer, 1024, fin)) return false;
		ret = buffer;
		ret.erase(ret.find_last_not_of(" \t\n\r\f\v") + 1);
		if (ret.size()) return true;
	}
}

void convert(FILE *fin)
{
  FILE *hook_cpp = fopen("template.cpp", "w");
  FILE *types = fopen("hook_type_def.h", "w");
/*
  fprintf(hook_cpp, "#include <pthread.h>\n");
	fprintf(hook_cpp, "#include <stdio.h>\n");
	fprintf(hook_cpp, "#include <dlfcn.h>\n");
	fprintf(hook_cpp, "#include <stdlib.h>\n");
	fprintf(hook_cpp, "#include <sys/types.h>\n");
	fprintf(hook_cpp, "#include <sys/socket.h>\n");
	fprintf(hook_cpp, "#include <sys/select.h>\n");
	fprintf(hook_cpp, "#include <sys/time.h>\n");
  fprintf(hook_cpp, "extern \"C\" void __gxx_personality_v0(void) {} \n\n");

	fprintf(hook_cpp, "#define PRINT_DEBUG\n");
*/
	while (true)
	{
		string flag = "";
		while (flag != "STARTDEF" && flag != "START_SHORT_DEFINE")
			if (!read_line(fin, flag)) break;

    string func_ret_type;
		string func_name;
		string args_with_name = "";
		string args_without_name = "";
		string args_only_name = "";
		string lib_path = "";
		if (flag == "STARTDEF")
    {
  		if (!read_line(fin, func_ret_type)) break;
  		if (!read_line(fin, func_name)) break;
  		if (!read_line(fin, lib_path)) break;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_without_name += flag;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_with_name += flag;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_only_name += flag;
    } else
    if (flag == "START_SHORT_DEFINE")
    {
  		if (!read_line(fin, lib_path)) break;
  		if (!read_line(fin, func_ret_type)) break;
  		if (!read_line(fin, func_name)) break;
      string st;
  		if (!read_line(fin, st)) break;
      while (st[0] != '(') st = st.substr(1);
      while (st[st.size() - 1] != ')') st = st.substr(0, st.size() - 1);
      int len = st.size();
      st = st.substr(1, len - 2);
      args_with_name = st;
      args_only_name = "";
      args_without_name = "";
      do {
        string token = "";
        while (st.size() && goodchar(st[0]))
        {
          token = token + st[0];
          st = st.substr(1);
        }

        if (token.size())
          if (filter.find(token) == filter.end())
            args_only_name += token;
          else
            args_without_name += token;
        else {
          if (st[0] == ',')
          {
            args_only_name += ',';
            args_without_name += ',';
          } else
            args_without_name += st[0];
          st = st.substr(1);
        }
      } while (st.size());
    } else
      break;

    print_func(
  	  hook_cpp,
    	func_ret_type.c_str(),
  		func_name.c_str(),
  		args_with_name.c_str(),
  		args_without_name.c_str(),
  		args_only_name.c_str(),
  		lib_path.c_str()
  	);

    fprintf(types, "typedef %s (*__%s_funcdef)(%s);\n",
      func_ret_type.c_str(),
      func_name.c_str(),
      args_with_name.c_str());
	}
  fclose(hook_cpp);
  fclose(types);
}

string read_file(const char *name)
{
	FILE *file = fopen(name, "r");
	string ret = "";
	while (fgets(buffer, 1024, file))
	{
		ret = ret + buffer;
	}
	fclose(file);
	return ret;
}

int main()
{
  init_filter();
	func_pattern = read_file("func_template.cpp");
	void_func_pattern = read_file("void_func_template.cpp");
	convert(stdin);
	return 0;
}
<commit_msg>fixed a bug in code_gen.cpp when argument is empty.<commit_after>#include <stdio.h>
#include <stdlib.h>
#include <cstring>
#include <string>
#include <set>

using namespace std;

string func_pattern;
string void_func_pattern;
set<string> filter;

void init_filter()
{
  filter.clear();
#define add(x) filter.insert(#x)
  add(const);
  add(void);
  add(int);
  add(unsigned);
  add(pthread_mutex_t);
  add(restrict);
  add(pthread_mutexattr_t);
  add(struct);
  add(timespec);
  add(pthread_cond_t);
  add(sem_t);
  add(pthread_barrier_t);
  add(pthread_barrierattr_t);
  add(useconds_t);
  add(__useconds_t);
  add(sockaddr);
  add(size_t);
  add(ssize_t);
  add(pid_t);
  add(msghdr);
  add(socklen_t);
  add(epoll_event);
#undef add
}

inline bool goodchar(char ch)
{
  return ch <= 'Z' && ch >= 'A'
    || ch <= 'z' && ch >= 'a'
    || ch <= '9' && ch >= '0'
    || ch == '_';
}

void replace(string &st, string pat, string tobe)
{
	while (true)
	{
		int p = st.find(pat);
		if (p == string::npos) return;
		st = st.replace(p, pat.size(), tobe);
	}
}

void print_func(
	FILE *file,
	const char *func_ret_type,
	const char *func_name,
	const char *args_with_name,
	const char *args_without_name,
	const char *args_only_name,
	const char *lib_path
	)
{
	string pattern = !strcmp(func_ret_type, "void") ? void_func_pattern : func_pattern;
	replace(pattern, "FUNC_RET_TYPE", func_ret_type);
	replace(pattern, "FUNC_NAME", func_name);
	replace(pattern, "ARGS_WITH_NAME", args_with_name);
	replace(pattern, "ARGS_WITHOUT_NAME", args_without_name);
	if (!strlen(args_only_name))
  {
    replace(pattern, ", ARGS_ONLY_NAME", args_only_name);
    replace(pattern, "ARGS_ONLY_NAME", args_only_name);
  } else
    replace(pattern, "ARGS_ONLY_NAME", args_only_name);

	replace(pattern, "LIB_PATH", lib_path);
	fprintf(file, "%s", pattern.c_str());
}

char buffer[1024];

bool read_line(FILE *fin, string &ret)
{
	while (true)
	{
		if (!fgets(buffer, 1024, fin)) return false;
		ret = buffer;
		ret.erase(ret.find_last_not_of(" \t\n\r\f\v") + 1);
		if (ret.size()) return true;
	}
}

void convert(FILE *fin)
{
  FILE *hook_cpp = fopen("template.cpp", "w");
  FILE *types = fopen("hook_type_def.h", "w");
/*
  fprintf(hook_cpp, "#include <pthread.h>\n");
	fprintf(hook_cpp, "#include <stdio.h>\n");
	fprintf(hook_cpp, "#include <dlfcn.h>\n");
	fprintf(hook_cpp, "#include <stdlib.h>\n");
	fprintf(hook_cpp, "#include <sys/types.h>\n");
	fprintf(hook_cpp, "#include <sys/socket.h>\n");
	fprintf(hook_cpp, "#include <sys/select.h>\n");
	fprintf(hook_cpp, "#include <sys/time.h>\n");
  fprintf(hook_cpp, "extern \"C\" void __gxx_personality_v0(void) {} \n\n");

	fprintf(hook_cpp, "#define PRINT_DEBUG\n");
*/
	while (true)
	{
		string flag = "";
		while (flag != "STARTDEF" && flag != "START_SHORT_DEFINE")
			if (!read_line(fin, flag)) break;

    string func_ret_type;
		string func_name;
		string args_with_name = "";
		string args_without_name = "";
		string args_only_name = "";
		string lib_path = "";
		if (flag == "STARTDEF")
    {
  		if (!read_line(fin, func_ret_type)) break;
  		if (!read_line(fin, func_name)) break;
  		if (!read_line(fin, lib_path)) break;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_without_name += flag;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_with_name += flag;
  		while (read_line(fin, flag) && flag != "SECTION")
  			args_only_name += flag;
    } else
    if (flag == "START_SHORT_DEFINE")
    {
  		if (!read_line(fin, lib_path)) break;
  		if (!read_line(fin, func_ret_type)) break;
  		if (!read_line(fin, func_name)) break;
      string st;
  		if (!read_line(fin, st)) break;
      while (st[0] != '(') st = st.substr(1);
      while (st[st.size() - 1] != ')') st = st.substr(0, st.size() - 1);
      int len = st.size();
      st = st.substr(1, len - 2);
      args_with_name = st;
      args_only_name = "";
      args_without_name = "";
      do {
        string token = "";
        while (st.size() && goodchar(st[0]))
        {
          token = token + st[0];
          st = st.substr(1);
        }

        if (token.size())
          if (filter.find(token) == filter.end())
            args_only_name += token;
          else
            args_without_name += token;
        else {
          if (!st.size()) break;
          if (st[0] == ',')
          {
            args_only_name += ',';
            args_without_name += ',';
          } else
            args_without_name += st[0];
          st = st.substr(1);
        }
      } while (st.size());
    } else
      break;

    print_func(
  	  hook_cpp,
    	func_ret_type.c_str(),
  		func_name.c_str(),
  		args_with_name.c_str(),
  		args_without_name.c_str(),
  		args_only_name.c_str(),
  		lib_path.c_str()
  	);

    fprintf(types, "typedef %s (*__%s_funcdef)(%s);\n",
      func_ret_type.c_str(),
      func_name.c_str(),
      args_with_name.c_str());
	}
  fclose(hook_cpp);
  fclose(types);
}

string read_file(const char *name)
{
	FILE *file = fopen(name, "r");
	string ret = "";
	while (fgets(buffer, 1024, file))
	{
		ret = ret + buffer;
	}
	fclose(file);
	return ret;
}

int main()
{
  init_filter();
	func_pattern = read_file("func_template.cpp");
	void_func_pattern = read_file("void_func_template.cpp");
	convert(stdin);
	return 0;
}
<|endoftext|>


<commit_before>#include "histogram.h"
#include <QPainter>
#include <QDebug>

Histogram::Histogram(QWidget* p) :QWidget(p) {
}
void
Histogram::setData(const QList<QPair<QString,quint32> >& d) {
    data = d;
    update();
}
typedef QPair<QString, quint32> StringUIntPair;
void
Histogram::paintEvent(QPaintEvent *) {
    if (data.size() == 0) return;
    int w = width();
    int h = height();
    int m = 5;
    int bw = (w-m*(data.size()-1))/data.size();
    uint max = 0;
    foreach (const StringUIntPair& p, data) {
        if (p.second > max) max = p.second;
    }
    QPainter painter(this);
    painter.setPen(palette().highlightedText().color());
    int offset = 0;
    painter.rotate(-90);
    painter.translate(-h, 0);
    foreach (const StringUIntPair& p, data) {
        int bh = p.second*h/max;
        painter.fillRect(0, offset, bh, bw, palette().highlight());
        painter.drawText(QRect(m, offset, bh-m, bw), Qt::AlignVCenter,
            p.first);
        offset += bw + m;
    }
}
<commit_msg>Use qreal instead of int for painting.<commit_after>#include "histogram.h"
#include <QPainter>
#include <QDebug>

Histogram::Histogram(QWidget* p) :QWidget(p) {
}
void
Histogram::setData(const QList<QPair<QString,quint32> >& d) {
    data = d;
    update();
}
typedef QPair<QString, quint32> StringUIntPair;
void
Histogram::paintEvent(QPaintEvent *) {
    if (data.size() == 0) return;
    int w = width();
    int h = height();
    int m = 5;
    int bw = (w-m*(data.size()-1))/data.size();
    uint max = 0;
    foreach (const StringUIntPair& p, data) {
        if (p.second > max) max = p.second;
    }
    QPainter painter(this);
    painter.setPen(palette().highlightedText().color());
    qreal offset = 0;
    painter.rotate(-90);
    painter.translate(-h, 0);
    foreach (const StringUIntPair& p, data) {
        qreal bh = p.second*h/(qreal)max;
        painter.fillRect(QRectF(0, offset, bh, bw), palette().highlight());
        painter.drawText(QRectF(m, offset, bh-m, bw), Qt::AlignVCenter,
            p.first);
        offset += bw + m;
    }
}
<|endoftext|>


<commit_before>#include "parser.hh"
#include "file.hh" // SPECIAL_EOF
#include "bookmark.hh"
#include "bookmark_container.hh"

#include <iostream>
#include <map>
#include <string>
#include <cstring> // strncmp

using std::map;
using std::string;

struct Parser::Key
{
    Key(Language l, State s, char e): language(l), oldState(s), event(e) {}
    bool operator<(const Key& k) const
    {
        return (language < k.language ? true :
                language > k.language ? false :
                oldState < k.oldState ? true :
                oldState > k.oldState ? false :
                event < k.event);
    }
    Language language;
    State oldState;
    char  event;
};

struct Parser::Value
{
    State  newState;
    Action action;
};

static const char ANY = '\0';

const char* Parser::stateToString(Parser::State s)
{
    switch (s)
    {
    case NORMAL: return "NORMAL";
    case COMMENT_START: return "COMMENT_START";
    case C_COMMENT: return "C_COMMENT";
    case C_COMMENT_END: return "C_COMMENT_END";
    case DOUBLE_QUOTE: return "DOUBLE_QUOTE";
    case DOUBLE_QUOTE_1: return "DOUBLE_QUOTE_1";
    case DOUBLE_QUOTE_2: return "DOUBLE_QUOTE_2";
    case DOUBLE_QUOTE_3: return "DOUBLE_QUOTE_3";
    case DOUBLE_QUOTE_4: return "DOUBLE_QUOTE_4";
    case DOUBLE_QUOTE_5: return "DOUBLE_QUOTE_5";
    case SINGLE_QUOTE_1: return "SINGLE_QUOTE_1";
    case SINGLE_QUOTE_2: return "SINGLE_QUOTE_2";
    case SINGLE_QUOTE_3: return "SINGLE_QUOTE_3";
    case SINGLE_QUOTE_4: return "SINGLE_QUOTE_4";
    case SINGLE_QUOTE_5: return "SINGLE_QUOTE_5";
    case SINGLE_QUOTE: return "SINGLE_QUOTE";
    case ESCAPE_DOUBLE: return "ESCAPE_DOUBLE";
    case ESCAPE_SINGLE: return "ESCAPE_SINGLE";
    case SKIP_TO_EOL: return "SKIP_TO_EOL";
    case SPACE: return "SPACE";
    case NO_STATE: return "NO_STATE";
    default: return "unknown";
    }
}

/**
 * Reads the original text into a processed text, which is returned. Also sets
 * the bookmarks to point into the two strings.
 */
const char* Parser::process(bool wordMode)
{
    const Matrix& matrix = wordMode ? textBehavior() : codeBehavior();

    itsProcessedText = new char[Bookmark::totalLength()];

    State state = NORMAL;
    for (size_t i = 0; i < Bookmark::totalLength(); ++i)
    {
        state = processChar(state, matrix, i);
        // std::cout << stateToString(state) << ' '
        //           << Bookmark::getChar(i) << "\n";
    }

    addChar('\0', Bookmark::totalLength());

    return itsProcessedText;
}

static bool lookaheadIs(const string& s, const char& c)
{
    return strncmp(s.c_str(), &c, s.length()) == 0;
}

Parser::State Parser::processChar(State         state,
                                  const Matrix& matrix,
                                  size_t        i)
{
    static const string imports = "import";
    static const string usings = "using";
    const char& c = Bookmark::getChar(i);
    // Apparently there can be zeroes in the total string, but only when
    // running on some machines. Don't know why.
    if (c == '\0')
        return state;

    if (c == SPECIAL_EOF)
    {
        addChar(c, i);
        return NORMAL;
    }

    Language language = getLanguage(Bookmark::getFileName(i));
    Matrix::const_iterator it;
    if ((it = matrix.find({ language, state, c }))   != matrix.end() ||
        (it = matrix.find({ language, state, ANY })) != matrix.end() ||
        (it = matrix.find({ ALL, state, c }))   != matrix.end() ||
        (it = matrix.find({ ALL, state, ANY })) != matrix.end())
    {
        performAction(it->second.action, c, i);
        return it->second.newState;
    }
    if (state == NORMAL)
    { // Handle state/event pair that can't be handled by The Matrix.
        if (timeForNewBookmark && c != '}')
            if (lookaheadIs(imports, c) || lookaheadIs(usings, c))
                state = SKIP_TO_EOL;
            else
                itsContainer.addBookmark(addChar(c, i));
        else
            addChar(c, i);

        timeForNewBookmark = false;
    }
    return state;
}

static bool endsWith(const std::string& str, const std::string& suffix)
{
    return str.size() >= suffix.size() &&
      str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

Parser::Language Parser::getLanguage(const string& fileName)
{
    if (endsWith(fileName, ".c") or
        endsWith(fileName, ".cc") or
        endsWith(fileName, ".h") or
        endsWith(fileName, ".hh") or
        endsWith(fileName, ".hpp") or
        endsWith(fileName, ".cpp") or
        endsWith(fileName, ".java"))
    {
        return C_FAMILY;
    }
    if (endsWith(fileName, ".erl") or
        endsWith(fileName, ".hrl"))
    {
        return ERLANG;
    }
    if (endsWith(fileName, ".rb") or
        endsWith(fileName, ".sh") or
        endsWith(fileName, ".pl"))
    {
        return SCRIPT;
    }
    if (endsWith(fileName, ".py"))
    {
        return PYTHON;
    }
    return ALL;
}

void Parser::performAction(Action action, char c, size_t i)
{
    switch (action)
    {
    case ADD_SLASH_AND_CHAR:
        addChar('/', i-1);
        if (not isspace(c))
            addChar(c, i);
        break;
    case ADD_CHAR:
    {
        const Bookmark bm = addChar(c, i);
        if (timeForNewBookmark)
            itsContainer.addBookmark(bm);
        timeForNewBookmark = false;
        break;
    }
    case ADD_BOOKMARK:
        timeForNewBookmark = true;
        break;
    case ADD_SPACE:
        addChar(' ', i);
        itsContainer.addBookmark(addChar(c, i));
        break;
    case NA:
        break;
    }
}

/**
 * Adds a character to the processed string and sets a bookmark, which is then
 * returned.
 */
Bookmark Parser::addChar(char c, int originalIndex)
{
    static int procIx;
    Bookmark bookmark(originalIndex, &itsProcessedText[procIx]);
    itsProcessedText[procIx++] = c;
    return bookmark;
}

const Parser::Matrix& Parser::codeBehavior() const
{
    static Matrix m = {
        // language,  oldState    event     newState        action
        { { ALL,      NORMAL,     '/'  }, { COMMENT_START,  NA           } },
        { { ALL,      NORMAL,     '"'  }, { DOUBLE_QUOTE,   ADD_CHAR     } },
        { { ALL,      NORMAL,     '\'' }, { SINGLE_QUOTE,   ADD_CHAR     } },
        { { ALL,      NORMAL,     '\n' }, { NORMAL,         ADD_BOOKMARK } },
        { { ALL,      NORMAL,     ' '  }, { NORMAL,         NA           } },
        { { ALL,      NORMAL,     '\t' }, { NORMAL,         NA           } },
        { { ALL,      NORMAL,     '#'  }, { SKIP_TO_EOL,    NA           } },
        { { ERLANG,   NORMAL,     '#'  }, { NORMAL,         NA           } },
        { { ERLANG,   NORMAL,     '%'  }, { SKIP_TO_EOL,    NA           } },
        { { PYTHON,   NORMAL,     '"'  }, { DOUBLE_QUOTE_1, NA           } },
        { { PYTHON,   NORMAL,     '\'' }, { SINGLE_QUOTE_1, NA           } },
        // See special handling of NORMAL in code.

        { { PYTHON, DOUBLE_QUOTE_1, '"' }, { DOUBLE_QUOTE_2, NA       } },
        { { PYTHON, DOUBLE_QUOTE_2, '"' }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_2, ANY }, { NORMAL,         ADD_CHAR } },
        { { PYTHON, DOUBLE_QUOTE_1, ANY }, { DOUBLE_QUOTE,   ADD_CHAR } },
        { { PYTHON, DOUBLE_QUOTE_3, '"' }, { DOUBLE_QUOTE_4, NA       } },
        { { PYTHON, DOUBLE_QUOTE_3, ANY }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_4, '"' }, { DOUBLE_QUOTE_5, NA       } },
        { { PYTHON, DOUBLE_QUOTE_4, ANY }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_5, '"' }, { NORMAL,         NA       } },
        { { PYTHON, DOUBLE_QUOTE_5, ANY }, { DOUBLE_QUOTE_3, NA       } },

        { { PYTHON, SINGLE_QUOTE_1, '\'' }, { SINGLE_QUOTE_2, NA       } },
        { { PYTHON, SINGLE_QUOTE_2, '\'' }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_3, '\'' }, { SINGLE_QUOTE_4, NA       } },
        { { PYTHON, SINGLE_QUOTE_4, '\'' }, { SINGLE_QUOTE_5, NA       } },
        { { PYTHON, SINGLE_QUOTE_5, '\'' }, { NORMAL,         NA       } },
        { { PYTHON, SINGLE_QUOTE_1, ANY  }, { SINGLE_QUOTE,   ADD_CHAR } },
        { { PYTHON, SINGLE_QUOTE_2, ANY  }, { NORMAL,         ADD_CHAR } },
        { { PYTHON, SINGLE_QUOTE_3, ANY  }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_4, ANY  }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_5, ANY  }, { SINGLE_QUOTE_3, NA       } },

        { { ALL, DOUBLE_QUOTE,  '\\' }, { ESCAPE_DOUBLE, ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  '"'  }, { NORMAL,        ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  ANY  }, { DOUBLE_QUOTE,  ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  '\n' }, { NORMAL,        ADD_BOOKMARK } }, // 1
        { { ALL, SINGLE_QUOTE,  '\\' }, { ESCAPE_SINGLE, ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  '\'' }, { NORMAL,        ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  ANY  }, { SINGLE_QUOTE,  ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  '\n' }, { NORMAL,        ADD_BOOKMARK } }, // 1
        { { ALL, ESCAPE_SINGLE, ANY  }, { SINGLE_QUOTE,  ADD_CHAR     } },
        { { ALL, ESCAPE_DOUBLE, ANY  }, { DOUBLE_QUOTE,  ADD_CHAR     } },
        // 1: probably a mistake if quote reaches end-of-line.

        { { ALL, COMMENT_START, '*'  }, { C_COMMENT,     NA                 } },
        { { ALL, COMMENT_START, '/'  }, { SKIP_TO_EOL,   NA                 } },
        { { ALL, COMMENT_START, ANY  }, { NORMAL,        ADD_SLASH_AND_CHAR } },
        { { ALL, SKIP_TO_EOL,   '\n' }, { NORMAL,        ADD_BOOKMARK       } },
        { { ALL, C_COMMENT,     '*'  }, { C_COMMENT_END, NA                 } },
        { { ALL, C_COMMENT_END, '/'  }, { NORMAL,        NA                 } },
        { { ALL, C_COMMENT_END, '*'  }, { C_COMMENT_END, NA                 } },
        { { ALL, C_COMMENT_END, ANY  }, { C_COMMENT,     NA                 } },

        { { ALL, NO_STATE,      ANY  }, { NO_STATE,      NA                 } }
    };
    return m;
}

const Parser::Matrix& Parser::textBehavior() const
{
    static Matrix m = {
        //  oldState  event     newState  action
        { { ALL, NORMAL,   ' '  }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\t' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\r' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\n' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   ANY  }, { NORMAL,   ADD_CHAR  } },

        { { ALL, SPACE,    ' '  }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\t' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\r' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\n' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '' }, { SPACE,    NA        } },
        { { ALL, SPACE,    ANY  }, { NORMAL,   ADD_SPACE } },

        { { ALL, NO_STATE, ANY  }, { NO_STATE, NA        } }
    };
    return m;
}
<commit_msg>Easier to read parser matrix for python, part 2<commit_after>#include "parser.hh"
#include "file.hh" // SPECIAL_EOF
#include "bookmark.hh"
#include "bookmark_container.hh"

#include <iostream>
#include <map>
#include <string>
#include <cstring> // strncmp

using std::map;
using std::string;

struct Parser::Key
{
    Key(Language l, State s, char e): language(l), oldState(s), event(e) {}
    bool operator<(const Key& k) const
    {
        return (language < k.language ? true :
                language > k.language ? false :
                oldState < k.oldState ? true :
                oldState > k.oldState ? false :
                event < k.event);
    }
    Language language;
    State oldState;
    char  event;
};

struct Parser::Value
{
    State  newState;
    Action action;
};

static const char ANY = '\0';

const char* Parser::stateToString(Parser::State s)
{
    switch (s)
    {
    case NORMAL: return "NORMAL";
    case COMMENT_START: return "COMMENT_START";
    case C_COMMENT: return "C_COMMENT";
    case C_COMMENT_END: return "C_COMMENT_END";
    case DOUBLE_QUOTE: return "DOUBLE_QUOTE";
    case DOUBLE_QUOTE_1: return "DOUBLE_QUOTE_1";
    case DOUBLE_QUOTE_2: return "DOUBLE_QUOTE_2";
    case DOUBLE_QUOTE_3: return "DOUBLE_QUOTE_3";
    case DOUBLE_QUOTE_4: return "DOUBLE_QUOTE_4";
    case DOUBLE_QUOTE_5: return "DOUBLE_QUOTE_5";
    case SINGLE_QUOTE_1: return "SINGLE_QUOTE_1";
    case SINGLE_QUOTE_2: return "SINGLE_QUOTE_2";
    case SINGLE_QUOTE_3: return "SINGLE_QUOTE_3";
    case SINGLE_QUOTE_4: return "SINGLE_QUOTE_4";
    case SINGLE_QUOTE_5: return "SINGLE_QUOTE_5";
    case SINGLE_QUOTE: return "SINGLE_QUOTE";
    case ESCAPE_DOUBLE: return "ESCAPE_DOUBLE";
    case ESCAPE_SINGLE: return "ESCAPE_SINGLE";
    case SKIP_TO_EOL: return "SKIP_TO_EOL";
    case SPACE: return "SPACE";
    case NO_STATE: return "NO_STATE";
    default: return "unknown";
    }
}

/**
 * Reads the original text into a processed text, which is returned. Also sets
 * the bookmarks to point into the two strings.
 */
const char* Parser::process(bool wordMode)
{
    const Matrix& matrix = wordMode ? textBehavior() : codeBehavior();

    itsProcessedText = new char[Bookmark::totalLength()];

    State state = NORMAL;
    for (size_t i = 0; i < Bookmark::totalLength(); ++i)
    {
        state = processChar(state, matrix, i);
        // std::cout << stateToString(state) << ' '
        //           << Bookmark::getChar(i) << "\n";
    }

    addChar('\0', Bookmark::totalLength());

    return itsProcessedText;
}

static bool lookaheadIs(const string& s, const char& c)
{
    return strncmp(s.c_str(), &c, s.length()) == 0;
}

Parser::State Parser::processChar(State         state,
                                  const Matrix& matrix,
                                  size_t        i)
{
    static const string imports = "import";
    static const string usings = "using";
    const char& c = Bookmark::getChar(i);
    // Apparently there can be zeroes in the total string, but only when
    // running on some machines. Don't know why.
    if (c == '\0')
        return state;

    if (c == SPECIAL_EOF)
    {
        addChar(c, i);
        return NORMAL;
    }

    Language language = getLanguage(Bookmark::getFileName(i));
    Matrix::const_iterator it;
    if ((it = matrix.find({ language, state, c }))   != matrix.end() ||
        (it = matrix.find({ language, state, ANY })) != matrix.end() ||
        (it = matrix.find({ ALL, state, c }))   != matrix.end() ||
        (it = matrix.find({ ALL, state, ANY })) != matrix.end())
    {
        performAction(it->second.action, c, i);
        return it->second.newState;
    }
    if (state == NORMAL)
    { // Handle state/event pair that can't be handled by The Matrix.
        if (timeForNewBookmark && c != '}')
            if (lookaheadIs(imports, c) || lookaheadIs(usings, c))
                state = SKIP_TO_EOL;
            else
                itsContainer.addBookmark(addChar(c, i));
        else
            addChar(c, i);

        timeForNewBookmark = false;
    }
    return state;
}

static bool endsWith(const std::string& str, const std::string& suffix)
{
    return str.size() >= suffix.size() &&
      str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}

Parser::Language Parser::getLanguage(const string& fileName)
{
    if (endsWith(fileName, ".c") or
        endsWith(fileName, ".cc") or
        endsWith(fileName, ".h") or
        endsWith(fileName, ".hh") or
        endsWith(fileName, ".hpp") or
        endsWith(fileName, ".cpp") or
        endsWith(fileName, ".java"))
    {
        return C_FAMILY;
    }
    if (endsWith(fileName, ".erl") or
        endsWith(fileName, ".hrl"))
    {
        return ERLANG;
    }
    if (endsWith(fileName, ".rb") or
        endsWith(fileName, ".sh") or
        endsWith(fileName, ".pl"))
    {
        return SCRIPT;
    }
    if (endsWith(fileName, ".py"))
    {
        return PYTHON;
    }
    return ALL;
}

void Parser::performAction(Action action, char c, size_t i)
{
    switch (action)
    {
    case ADD_SLASH_AND_CHAR:
        addChar('/', i-1);
        if (not isspace(c))
            addChar(c, i);
        break;
    case ADD_CHAR:
    {
        const Bookmark bm = addChar(c, i);
        if (timeForNewBookmark)
            itsContainer.addBookmark(bm);
        timeForNewBookmark = false;
        break;
    }
    case ADD_BOOKMARK:
        timeForNewBookmark = true;
        break;
    case ADD_SPACE:
        addChar(' ', i);
        itsContainer.addBookmark(addChar(c, i));
        break;
    case NA:
        break;
    }
}

/**
 * Adds a character to the processed string and sets a bookmark, which is then
 * returned.
 */
Bookmark Parser::addChar(char c, int originalIndex)
{
    static int procIx;
    Bookmark bookmark(originalIndex, &itsProcessedText[procIx]);
    itsProcessedText[procIx++] = c;
    return bookmark;
}

const Parser::Matrix& Parser::codeBehavior() const
{
    static Matrix m = {
        // language,  oldState    event     newState        action
        { { ALL,      NORMAL,     '/'  }, { COMMENT_START,  NA           } },
        { { ALL,      NORMAL,     '"'  }, { DOUBLE_QUOTE,   ADD_CHAR     } },
        { { ALL,      NORMAL,     '\'' }, { SINGLE_QUOTE,   ADD_CHAR     } },
        { { ALL,      NORMAL,     '\n' }, { NORMAL,         ADD_BOOKMARK } },
        { { ALL,      NORMAL,     ' '  }, { NORMAL,         NA           } },
        { { ALL,      NORMAL,     '\t' }, { NORMAL,         NA           } },
        { { ALL,      NORMAL,     '#'  }, { SKIP_TO_EOL,    NA           } },
        { { ERLANG,   NORMAL,     '#'  }, { NORMAL,         NA           } },
        { { ERLANG,   NORMAL,     '%'  }, { SKIP_TO_EOL,    NA           } },
        { { PYTHON,   NORMAL,     '"'  }, { DOUBLE_QUOTE_1, NA           } },
        { { PYTHON,   NORMAL,     '\'' }, { SINGLE_QUOTE_1, NA           } },
        // See special handling of NORMAL in code.

        { { PYTHON, DOUBLE_QUOTE_1, '"' }, { DOUBLE_QUOTE_2, NA       } },
        { { PYTHON, DOUBLE_QUOTE_2, '"' }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_3, '"' }, { DOUBLE_QUOTE_4, NA       } },
        { { PYTHON, DOUBLE_QUOTE_4, '"' }, { DOUBLE_QUOTE_5, NA       } },
        { { PYTHON, DOUBLE_QUOTE_5, '"' }, { NORMAL,         NA       } },
        { { PYTHON, DOUBLE_QUOTE_1, ANY }, { DOUBLE_QUOTE,   ADD_CHAR } },
        { { PYTHON, DOUBLE_QUOTE_2, ANY }, { NORMAL,         ADD_CHAR } },
        { { PYTHON, DOUBLE_QUOTE_3, ANY }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_4, ANY }, { DOUBLE_QUOTE_3, NA       } },
        { { PYTHON, DOUBLE_QUOTE_5, ANY }, { DOUBLE_QUOTE_3, NA       } },

        { { PYTHON, SINGLE_QUOTE_1, '\'' }, { SINGLE_QUOTE_2, NA       } },
        { { PYTHON, SINGLE_QUOTE_2, '\'' }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_3, '\'' }, { SINGLE_QUOTE_4, NA       } },
        { { PYTHON, SINGLE_QUOTE_4, '\'' }, { SINGLE_QUOTE_5, NA       } },
        { { PYTHON, SINGLE_QUOTE_5, '\'' }, { NORMAL,         NA       } },
        { { PYTHON, SINGLE_QUOTE_1, ANY  }, { SINGLE_QUOTE,   ADD_CHAR } },
        { { PYTHON, SINGLE_QUOTE_2, ANY  }, { NORMAL,         ADD_CHAR } },
        { { PYTHON, SINGLE_QUOTE_3, ANY  }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_4, ANY  }, { SINGLE_QUOTE_3, NA       } },
        { { PYTHON, SINGLE_QUOTE_5, ANY  }, { SINGLE_QUOTE_3, NA       } },

        { { ALL, DOUBLE_QUOTE,  '\\' }, { ESCAPE_DOUBLE, ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  '"'  }, { NORMAL,        ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  ANY  }, { DOUBLE_QUOTE,  ADD_CHAR     } },
        { { ALL, DOUBLE_QUOTE,  '\n' }, { NORMAL,        ADD_BOOKMARK } }, // 1
        { { ALL, SINGLE_QUOTE,  '\\' }, { ESCAPE_SINGLE, ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  '\'' }, { NORMAL,        ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  ANY  }, { SINGLE_QUOTE,  ADD_CHAR     } },
        { { ALL, SINGLE_QUOTE,  '\n' }, { NORMAL,        ADD_BOOKMARK } }, // 1
        { { ALL, ESCAPE_SINGLE, ANY  }, { SINGLE_QUOTE,  ADD_CHAR     } },
        { { ALL, ESCAPE_DOUBLE, ANY  }, { DOUBLE_QUOTE,  ADD_CHAR     } },
        // 1: probably a mistake if quote reaches end-of-line.

        { { ALL, COMMENT_START, '*'  }, { C_COMMENT,     NA                 } },
        { { ALL, COMMENT_START, '/'  }, { SKIP_TO_EOL,   NA                 } },
        { { ALL, COMMENT_START, ANY  }, { NORMAL,        ADD_SLASH_AND_CHAR } },
        { { ALL, SKIP_TO_EOL,   '\n' }, { NORMAL,        ADD_BOOKMARK       } },
        { { ALL, C_COMMENT,     '*'  }, { C_COMMENT_END, NA                 } },
        { { ALL, C_COMMENT_END, '/'  }, { NORMAL,        NA                 } },
        { { ALL, C_COMMENT_END, '*'  }, { C_COMMENT_END, NA                 } },
        { { ALL, C_COMMENT_END, ANY  }, { C_COMMENT,     NA                 } },

        { { ALL, NO_STATE,      ANY  }, { NO_STATE,      NA                 } }
    };
    return m;
}

const Parser::Matrix& Parser::textBehavior() const
{
    static Matrix m = {
        //  oldState  event     newState  action
        { { ALL, NORMAL,   ' '  }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\t' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\r' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '\n' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   '' }, { SPACE,    NA        } },
        { { ALL, NORMAL,   ANY  }, { NORMAL,   ADD_CHAR  } },

        { { ALL, SPACE,    ' '  }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\t' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\r' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '\n' }, { SPACE,    NA        } },
        { { ALL, SPACE,    '' }, { SPACE,    NA        } },
        { { ALL, SPACE,    ANY  }, { NORMAL,   ADD_SPACE } },

        { { ALL, NO_STATE, ANY  }, { NO_STATE, NA        } }
    };
    return m;
}
<|endoftext|>


<commit_before>#include <gmock/gmock.h>
#include "oddlib/lvlarchive.hpp"

int main(int argc, char** argv)
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(LvlArchive, FileNotFound)
{
    ASSERT_THROW(Oddlib::LvlArchive("not_found.lvl"), Oddlib::Exception);
}

TEST(LvlArchive, /*DISABLED_*/Integration)
{
    // Load AE lvl
    Oddlib::LvlArchive lvl("MI.LVL");

    const auto file = lvl.FileByName("FLYSLIG.BND");
    ASSERT_NE(nullptr, file);

    const auto chunk = file->ChunkById(450);
    ASSERT_NE(nullptr, chunk);

    ASSERT_EQ(450, chunk->Id());

    const auto data = chunk->ReadData();
    ASSERT_EQ(false, data.empty());
}
<commit_msg>leave integration test disabled on travis<commit_after>#include <gmock/gmock.h>
#include "oddlib/lvlarchive.hpp"

int main(int argc, char** argv)
{
    ::testing::InitGoogleMock(&argc, argv);
    return RUN_ALL_TESTS();
}

TEST(LvlArchive, FileNotFound)
{
    ASSERT_THROW(Oddlib::LvlArchive("not_found.lvl"), Oddlib::Exception);
}

TEST(LvlArchive, DISABLED_Integration)
{
    // Load AE lvl
    Oddlib::LvlArchive lvl("MI.LVL");

    const auto file = lvl.FileByName("FLYSLIG.BND");
    ASSERT_NE(nullptr, file);

    const auto chunk = file->ChunkById(450);
    ASSERT_NE(nullptr, chunk);

    ASSERT_EQ(450, chunk->Id());

    const auto data = chunk->ReadData();
    ASSERT_EQ(false, data.empty());
}
<|endoftext|>