<commit_before>#include "saverthread.h"

#include "trace_writer.hpp"
#include "trace_model.hpp"
#include "trace_parser.hpp"

#include <QFile>
#include <QHash>
#include <QUrl>

#include <QDebug>

#if 0
static trace::FunctionSig *
createFunctionSig(ApiTraceCall *call, unsigned id)
{
    trace::FunctionSig *sig = new trace::FunctionSig();

    sig->id = id;
    sig->name = qstrdup(call->name().toLocal8Bit());

    QStringList args = call->argNames();
    sig->num_args = args.count();
    sig->arg_names = new const char*[args.count()];
    for (int i = 0; i < args.count(); ++i) {
        sig->arg_names[i] = qstrdup(args[i].toLocal8Bit());
    }

    return sig;
}

static void
deleteFunctionSig(trace::FunctionSig *sig)
{
    for (int i = 0; i < sig->num_args; ++i) {
        delete [] sig->arg_names[i];
    }
    delete [] sig->arg_names;
    delete [] sig->name;
    delete sig;
}

static trace::StructSig *
createStructSig(const ApiStruct &str, unsigned id)
{
    ApiStruct::Signature aSig = str.signature();

    trace::StructSig *sig = new trace::StructSig();
    sig->id = id;
    sig->name = qstrdup(aSig.name.toLocal8Bit());
    sig->num_members = aSig.memberNames.count();
    char **member_names = new char*[aSig.memberNames.count()];
    sig->member_names = (const char **)member_names;
    for (int i = 0; i < aSig.memberNames.count(); ++i) {
        member_names[i] = qstrdup(aSig.memberNames[i].toLocal8Bit());
    }
    return sig;
}

static void
deleteStructSig(trace::StructSig *sig)
{
    for (int i = 0; i < sig->num_members; ++i) {
        delete [] sig->member_names[i];
    }
    delete [] sig->member_names;
    delete [] sig->name;
    delete sig;
}

static trace::EnumSig *
createEnumSig(const ApiEnum &en, unsigned id)
{
    trace::EnumSig *sig = new trace::EnumSig();

    sig->id = id;
    sig->name = qstrdup(en.name().toLocal8Bit());
    sig->value = en.value().toLongLong();

    return sig;
}

static void
deleteEnumSig(trace::EnumSig *sig)
{
    delete [] sig->name;
    delete sig;
}

static trace::BitmaskSig *
createBitmaskSig(const ApiBitmask &bt, unsigned id)
{
    ApiBitmask::Signature bsig = bt.signature();
    ApiBitmask::Signature::const_iterator itr;

    trace::BitmaskSig *sig = new trace::BitmaskSig();
    trace::BitmaskFlag *flags = new trace::BitmaskFlag[bsig.count()];

    sig->id = id;
    sig->num_flags = bsig.count();
    sig->flags = flags;

    int i = 0;
    for (itr = bsig.constBegin(); itr != bsig.constEnd(); ++itr, ++i) {
        flags[i].name = qstrdup(itr->first.toLocal8Bit());
        flags[i].value = itr->second;
    }

    return sig;
}

static void
deleteBitmaskSig(trace::BitmaskSig *sig)
{
    for (int i = 0; i < sig->num_flags; ++i) {
        delete [] sig->flags[i].name;
    }
    delete [] sig->flags;
    delete sig;
}

static void
writeValue(trace::Writer &writer, const QVariant &var, unsigned &id)
{
    int arrayType   = QMetaType::type("ApiArray");
    int bitmaskType = QMetaType::type("ApiBitmask");
    int structType  = QMetaType::type("ApiStruct");
    int pointerType = QMetaType::type("ApiPointer");
    int enumType    = QMetaType::type("ApiEnum");
    int type = var.userType();

    switch(type) {
    case QVariant::Bool:
        writer.writeBool(var.toBool());
        break;
    case QVariant::ByteArray: {
        QByteArray ba = var.toByteArray();
        writer.writeBlob((const void*)ba.constData(), ba.size());
    }
        break;
    case QVariant::Double:
        writer.writeDouble(var.toDouble());
        break;
    case QMetaType::Float:
        writer.writeFloat(var.toFloat());
        break;
    case QVariant::Int:
        writer.writeSInt(var.toInt());
        break;
    case QVariant::LongLong:
        writer.writeSInt(var.toLongLong());
        break;
    case QVariant::String: {
        QString str = var.toString();
        writer.writeString(str.toLocal8Bit().constData(), str.length());
    }
        break;
    case QVariant::UInt:
        writer.writeUInt(var.toInt());
        break;
    case QVariant::ULongLong:
        writer.writeUInt(var.toLongLong());
        break;
    default:
        if (type == arrayType) {
            ApiArray array = var.value<ApiArray>();
            QVector<QVariant> vals = array.values();
            writer.beginArray(vals.count());
            foreach(QVariant el, vals) {
                writer.beginElement();
                writeValue(writer, el, ++id);
                writer.endElement();
            }
            writer.endArray();
        } else if (type == bitmaskType) {
            ApiBitmask bm = var.value<ApiBitmask>();
            trace::BitmaskSig *sig = createBitmaskSig(bm, ++id);
            writer.writeBitmask(sig, bm.value());
            deleteBitmaskSig(sig);
        } else if (type == structType) {
            ApiStruct apiStr = var.value<ApiStruct>();
            QList<QVariant> vals = apiStr.values();
            trace::StructSig *str = createStructSig(apiStr, ++id);
            writer.beginStruct(str);
            foreach(QVariant val, vals) {
                writeValue(writer, val, ++id);
            }
            writer.endStruct();
            deleteStructSig(str);
        } else if (type == pointerType) {
            ApiPointer apiPtr = var.value<ApiPointer>();
            writer.writePointer(apiPtr.value());
        } else if (type == enumType) {
            ApiEnum apiEnum = var.value<ApiEnum>();
            trace::EnumSig *sig = createEnumSig(apiEnum, ++id);
            writer.writeEnum(sig);
            deleteEnumSig(sig);
        } else {
            qWarning()<<"Unsupported write variant : "
                      << QMetaType::typeName(type);
        }
    }
}
#endif

class EditVisitor : public trace::Visitor
{
public:
    EditVisitor(const QVariant &variant)
        : m_variant(variant),
          m_editedValue(0)
    {}
    virtual void visit(trace::Null *val)
    {
        m_editedValue = val;
    }

    virtual void visit(trace::Bool *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::Bool);
        bool var = m_variant.toBool();
        m_editedValue = new trace::Bool(var);
    }

    virtual void visit(trace::SInt *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::Int);
        m_editedValue = new trace::SInt(m_variant.toInt());
    }

    virtual void visit(trace::UInt *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::UInt);
        m_editedValue = new trace::SInt(m_variant.toUInt());
    }

    virtual void visit(trace::Float *node)
    {
        m_editedValue = new trace::Float(m_variant.toFloat());
    }

    virtual void visit(trace::Double *node)
    {
        m_editedValue = new trace::Double(m_variant.toDouble());
    }

    virtual void visit(trace::String *node)
    {
        QString str = m_variant.toString();
        m_editedValue = new trace::String(str.toLocal8Bit().constData());
    }

    virtual void visit(trace::Enum *e)
    {
        m_editedValue = e;
    }

    virtual void visit(trace::Bitmask *bitmask)
    {
        m_editedValue = bitmask;
    }

    virtual void visit(trace::Struct *str)
    {
        m_editedValue = str;
    }

    virtual void visit(trace::Array *array)
    {
        ApiArray apiArray = m_variant.value<ApiArray>();
        QVector<QVariant> vals = apiArray.values();

        trace::Array *newArray = new trace::Array(vals.count());
        for (int i = 0; i < vals.count(); ++i) {
            EditVisitor visitor(vals[i]);

            array->values[i]->visit(visitor);
            if (array->values[i] == visitor.value()) {
                //non-editabled
                delete newArray;
                m_editedValue = array;
                return;
            }

            newArray->values.push_back(visitor.value());
        }
        m_editedValue = newArray;
    }

    virtual void visit(trace::Blob *blob)
    {
        m_editedValue = blob;
    }

    virtual void visit(trace::Pointer *ptr)
    {
        m_editedValue = ptr;
    }

    trace::Value *value() const
    {
        return m_editedValue;
    }
private:
    QVariant m_variant;
    trace::Value *m_editedValue;
};

static void
overwriteValue(trace::Call *call, const QVariant &val, int index)
{
    EditVisitor visitor(val);
    trace::Value *origValue = call->args[index].value;
    origValue->visit(visitor);

    if (visitor.value() && origValue != visitor.value()) {
        delete origValue;
        call->args[index].value = visitor.value();
    }
}

SaverThread::SaverThread(QObject *parent)
    : QThread(parent)
{
}

void SaverThread::saveFile(const QString &writeFileName,
                           const QString &readFileName,
                           const QSet<ApiTraceCall*> &editedCalls)
{
    m_writeFileName = writeFileName;
    m_readFileName = readFileName;
    m_editedCalls = editedCalls;
    start();
}

void SaverThread::run()
{
    qDebug() << "Saving  " << m_readFileName
             << ", to " << m_writeFileName;
    QMap<int, ApiTraceCall*> callIndexMap;

    foreach(ApiTraceCall *call, m_editedCalls) {
        callIndexMap.insert(call->index(), call);
    }

    trace::Writer writer;
    writer.open(m_writeFileName.toLocal8Bit());

    trace::Parser parser;
    parser.open(m_readFileName.toLocal8Bit());

    trace::Call *call;
    while ((call = parser.parse_call())) {
        if (callIndexMap.contains(call->no)) {
            QVector<QVariant> values = callIndexMap[call->no]->editedValues();
            for (int i = 0; i < values.count(); ++i) {
                const QVariant &val = values[i];
                overwriteValue(call, val, i);
            }
            writer.writeCall(call);
        } else {
            writer.writeCall(call);
        }
    }

    writer.close();

    emit traceSaved();
}

#include "saverthread.moc"
<commit_msg>gui: Fix editing of the shaders<commit_after>#include "saverthread.h"

#include "trace_writer.hpp"
#include "trace_model.hpp"
#include "trace_parser.hpp"

#include <QFile>
#include <QHash>
#include <QUrl>

#include <QDebug>

#if 0
static trace::FunctionSig *
createFunctionSig(ApiTraceCall *call, unsigned id)
{
    trace::FunctionSig *sig = new trace::FunctionSig();

    sig->id = id;
    sig->name = qstrdup(call->name().toLocal8Bit());

    QStringList args = call->argNames();
    sig->num_args = args.count();
    sig->arg_names = new const char*[args.count()];
    for (int i = 0; i < args.count(); ++i) {
        sig->arg_names[i] = qstrdup(args[i].toLocal8Bit());
    }

    return sig;
}

static void
deleteFunctionSig(trace::FunctionSig *sig)
{
    for (int i = 0; i < sig->num_args; ++i) {
        delete [] sig->arg_names[i];
    }
    delete [] sig->arg_names;
    delete [] sig->name;
    delete sig;
}

static trace::StructSig *
createStructSig(const ApiStruct &str, unsigned id)
{
    ApiStruct::Signature aSig = str.signature();

    trace::StructSig *sig = new trace::StructSig();
    sig->id = id;
    sig->name = qstrdup(aSig.name.toLocal8Bit());
    sig->num_members = aSig.memberNames.count();
    char **member_names = new char*[aSig.memberNames.count()];
    sig->member_names = (const char **)member_names;
    for (int i = 0; i < aSig.memberNames.count(); ++i) {
        member_names[i] = qstrdup(aSig.memberNames[i].toLocal8Bit());
    }
    return sig;
}

static void
deleteStructSig(trace::StructSig *sig)
{
    for (int i = 0; i < sig->num_members; ++i) {
        delete [] sig->member_names[i];
    }
    delete [] sig->member_names;
    delete [] sig->name;
    delete sig;
}

static trace::EnumSig *
createEnumSig(const ApiEnum &en, unsigned id)
{
    trace::EnumSig *sig = new trace::EnumSig();

    sig->id = id;
    sig->name = qstrdup(en.name().toLocal8Bit());
    sig->value = en.value().toLongLong();

    return sig;
}

static void
deleteEnumSig(trace::EnumSig *sig)
{
    delete [] sig->name;
    delete sig;
}

static trace::BitmaskSig *
createBitmaskSig(const ApiBitmask &bt, unsigned id)
{
    ApiBitmask::Signature bsig = bt.signature();
    ApiBitmask::Signature::const_iterator itr;

    trace::BitmaskSig *sig = new trace::BitmaskSig();
    trace::BitmaskFlag *flags = new trace::BitmaskFlag[bsig.count()];

    sig->id = id;
    sig->num_flags = bsig.count();
    sig->flags = flags;

    int i = 0;
    for (itr = bsig.constBegin(); itr != bsig.constEnd(); ++itr, ++i) {
        flags[i].name = qstrdup(itr->first.toLocal8Bit());
        flags[i].value = itr->second;
    }

    return sig;
}

static void
deleteBitmaskSig(trace::BitmaskSig *sig)
{
    for (int i = 0; i < sig->num_flags; ++i) {
        delete [] sig->flags[i].name;
    }
    delete [] sig->flags;
    delete sig;
}

static void
writeValue(trace::Writer &writer, const QVariant &var, unsigned &id)
{
    int arrayType   = QMetaType::type("ApiArray");
    int bitmaskType = QMetaType::type("ApiBitmask");
    int structType  = QMetaType::type("ApiStruct");
    int pointerType = QMetaType::type("ApiPointer");
    int enumType    = QMetaType::type("ApiEnum");
    int type = var.userType();

    switch(type) {
    case QVariant::Bool:
        writer.writeBool(var.toBool());
        break;
    case QVariant::ByteArray: {
        QByteArray ba = var.toByteArray();
        writer.writeBlob((const void*)ba.constData(), ba.size());
    }
        break;
    case QVariant::Double:
        writer.writeDouble(var.toDouble());
        break;
    case QMetaType::Float:
        writer.writeFloat(var.toFloat());
        break;
    case QVariant::Int:
        writer.writeSInt(var.toInt());
        break;
    case QVariant::LongLong:
        writer.writeSInt(var.toLongLong());
        break;
    case QVariant::String: {
        QString str = var.toString();
        writer.writeString(str.toLocal8Bit().constData(), str.length());
    }
        break;
    case QVariant::UInt:
        writer.writeUInt(var.toInt());
        break;
    case QVariant::ULongLong:
        writer.writeUInt(var.toLongLong());
        break;
    default:
        if (type == arrayType) {
            ApiArray array = var.value<ApiArray>();
            QVector<QVariant> vals = array.values();
            writer.beginArray(vals.count());
            foreach(QVariant el, vals) {
                writer.beginElement();
                writeValue(writer, el, ++id);
                writer.endElement();
            }
            writer.endArray();
        } else if (type == bitmaskType) {
            ApiBitmask bm = var.value<ApiBitmask>();
            trace::BitmaskSig *sig = createBitmaskSig(bm, ++id);
            writer.writeBitmask(sig, bm.value());
            deleteBitmaskSig(sig);
        } else if (type == structType) {
            ApiStruct apiStr = var.value<ApiStruct>();
            QList<QVariant> vals = apiStr.values();
            trace::StructSig *str = createStructSig(apiStr, ++id);
            writer.beginStruct(str);
            foreach(QVariant val, vals) {
                writeValue(writer, val, ++id);
            }
            writer.endStruct();
            deleteStructSig(str);
        } else if (type == pointerType) {
            ApiPointer apiPtr = var.value<ApiPointer>();
            writer.writePointer(apiPtr.value());
        } else if (type == enumType) {
            ApiEnum apiEnum = var.value<ApiEnum>();
            trace::EnumSig *sig = createEnumSig(apiEnum, ++id);
            writer.writeEnum(sig);
            deleteEnumSig(sig);
        } else {
            qWarning()<<"Unsupported write variant : "
                      << QMetaType::typeName(type);
        }
    }
}
#endif

class EditVisitor : public trace::Visitor
{
public:
    EditVisitor(const QVariant &variant)
        : m_variant(variant),
          m_editedValue(0)
    {}
    virtual void visit(trace::Null *val)
    {
        m_editedValue = val;
    }

    virtual void visit(trace::Bool *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::Bool);
        bool var = m_variant.toBool();
        m_editedValue = new trace::Bool(var);
    }

    virtual void visit(trace::SInt *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::Int);
        m_editedValue = new trace::SInt(m_variant.toInt());
    }

    virtual void visit(trace::UInt *node)
    {
//        Q_ASSERT(m_variant.userType() == QVariant::UInt);
        m_editedValue = new trace::SInt(m_variant.toUInt());
    }

    virtual void visit(trace::Float *node)
    {
        m_editedValue = new trace::Float(m_variant.toFloat());
    }

    virtual void visit(trace::Double *node)
    {
        m_editedValue = new trace::Double(m_variant.toDouble());
    }

    virtual void visit(trace::String *node)
    {
        QString str = m_variant.toString();
        char *newString = new char[str.length() + 1];
        QByteArray ba = str.toLocal8Bit();
        strcpy(newString, ba.constData());
        m_editedValue = new trace::String(newString);
    }

    virtual void visit(trace::Enum *e)
    {
        m_editedValue = e;
    }

    virtual void visit(trace::Bitmask *bitmask)
    {
        m_editedValue = bitmask;
    }

    virtual void visit(trace::Struct *str)
    {
        m_editedValue = str;
    }

    virtual void visit(trace::Array *array)
    {
        ApiArray apiArray = m_variant.value<ApiArray>();
        QVector<QVariant> vals = apiArray.values();

        trace::Array *newArray = new trace::Array(vals.count());
        for (int i = 0; i < vals.count(); ++i) {
            EditVisitor visitor(vals[i]);
            array->values[i]->visit(visitor);
            if (array->values[i] == visitor.value()) {
                //non-editabled
                delete newArray;
                m_editedValue = array;
                return;
            }

            newArray->values[i] = visitor.value();
        }
        m_editedValue = newArray;
    }

    virtual void visit(trace::Blob *blob)
    {
        m_editedValue = blob;
    }

    virtual void visit(trace::Pointer *ptr)
    {
        m_editedValue = ptr;
    }

    trace::Value *value() const
    {
        return m_editedValue;
    }
private:
    QVariant m_variant;
    trace::Value *m_editedValue;
};

static void
overwriteValue(trace::Call *call, const QVariant &val, int index)
{
    EditVisitor visitor(val);
    trace::Value *origValue = call->args[index].value;
    origValue->visit(visitor);

    if (visitor.value() && origValue != visitor.value()) {
        delete origValue;
        call->args[index].value = visitor.value();
    }
}

SaverThread::SaverThread(QObject *parent)
    : QThread(parent)
{
}

void SaverThread::saveFile(const QString &writeFileName,
                           const QString &readFileName,
                           const QSet<ApiTraceCall*> &editedCalls)
{
    m_writeFileName = writeFileName;
    m_readFileName = readFileName;
    m_editedCalls = editedCalls;
    start();
}

void SaverThread::run()
{
    qDebug() << "Saving  " << m_readFileName
             << ", to " << m_writeFileName;
    QMap<int, ApiTraceCall*> callIndexMap;

    foreach(ApiTraceCall *call, m_editedCalls) {
        callIndexMap.insert(call->index(), call);
    }

    trace::Writer writer;
    writer.open(m_writeFileName.toLocal8Bit());

    trace::Parser parser;
    parser.open(m_readFileName.toLocal8Bit());

    trace::Call *call;
    while ((call = parser.parse_call())) {
        if (callIndexMap.contains(call->no)) {
            QVector<QVariant> values = callIndexMap[call->no]->editedValues();
            for (int i = 0; i < values.count(); ++i) {
                const QVariant &val = values[i];
                overwriteValue(call, val, i);
            }
            writer.writeCall(call);
        } else {
            writer.writeCall(call);
        }
    }

    writer.close();

    emit traceSaved();
}

#include "saverthread.moc"
<|endoftext|>


<commit_before>/*
 * Qumulus UML editor
 * Author: Randy Thiemann
 *
 */

#include "AssociationEdge.h"
#include <Uml/Kernel/Association.h>
#include <Uml/Kernel/Element.h>
#include <QtGui/QPainter>

QUML_BEGIN_NAMESPACE_GD

AssociationEdge::AssociationEdge(QuUK::Element* e, DiagramElement* p) :
        Edge(e, p) {
}

AssociationEdge::AssociationEdge(const AssociationEdge& e) :
        Edge(e) {}

void AssociationEdge::paint(QPainter* p,
        const QStyleOptionGraphicsItem* o,
        QWidget* w) {
    Edge::paint(p, o, w);
    Shape* t = target();
    Direction entry = targetEntry();
    QuUK::AggregationKind aggregationKind =
        static_cast<QuUK::Association*>(modelElement())->aggregation();
    QPointF origin = {t->pos().x() + t->width() / 2,
            t->pos().y() + t->height() / 2};

    switch(aggregationKind) {
    case QuUK::AggregationKind::Shared:
        switch(entry) {
        case Up: {
            origin.ry() -= t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()-10},
                {origin.x(), origin.y()-20},
                {origin.x()-5, origin.y()-10}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Down: {
            origin.ry() += t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()+10},
                {origin.x(), origin.y()+20},
                {origin.x()-5, origin.y()+10}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Left: {
            origin.rx() -= t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()-10, origin.y()+5},
                {origin.x()-20, origin.y()},
                {origin.x()-10, origin.y()-5}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Right: {
            origin.rx() += t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+10, origin.y()+5},
                {origin.x()+20, origin.y()},
                {origin.x()+10, origin.y()-5}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        }
        break;
    case QuUK::AggregationKind::Composite:
        switch(entry) {
        case Up: {
            origin.ry() -= t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()-10},
                {origin.x(), origin.y()-20},
                {origin.x()-5, origin.y()-10}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Down: {
            origin.ry() += t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()+10},
                {origin.x(), origin.y()+20},
                {origin.x()-5, origin.y()+10}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Left: {
            origin.rx() -= t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()-10, origin.y()+5},
                {origin.x()-20, origin.y()},
                {origin.x()-10, origin.y()-5}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Right: {
            origin.rx() += t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+10, origin.y()+5},
                {origin.x()+20, origin.y()},
                {origin.x()+10, origin.y()-5}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        }
        break;
    default:
        break;
    }
    }
QUML_END_NAMESPACE_GD

<commit_msg>Reverse arrow.<commit_after>/*
 * Qumulus UML editor
 * Author: Randy Thiemann
 *
 */

#include "AssociationEdge.h"
#include <Uml/Kernel/Association.h>
#include <Uml/Kernel/Element.h>
#include <QtGui/QPainter>

QUML_BEGIN_NAMESPACE_GD

AssociationEdge::AssociationEdge(QuUK::Element* e, DiagramElement* p) :
        Edge(e, p) {
}

AssociationEdge::AssociationEdge(const AssociationEdge& e) :
        Edge(e) {}

void AssociationEdge::paint(QPainter* p,
        const QStyleOptionGraphicsItem* o,
        QWidget* w) {
    Edge::paint(p, o, w);
    Shape* t = source();
    Direction entry = sourceExit();
    QuUK::AggregationKind aggregationKind =
        static_cast<QuUK::Association*>(modelElement())->aggregation();
    QPointF origin = {t->pos().x() + t->width() / 2,
            t->pos().y() + t->height() / 2};

    switch(aggregationKind) {
    case QuUK::AggregationKind::Shared:
        switch(entry) {
        case Up: {
            origin.ry() -= t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()-10},
                {origin.x(), origin.y()-20},
                {origin.x()-5, origin.y()-10}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Down: {
            origin.ry() += t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()+10},
                {origin.x(), origin.y()+20},
                {origin.x()-5, origin.y()+10}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Left: {
            origin.rx() -= t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()-10, origin.y()+5},
                {origin.x()-20, origin.y()},
                {origin.x()-10, origin.y()-5}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        case Right: {
            origin.rx() += t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+10, origin.y()+5},
                {origin.x()+20, origin.y()},
                {origin.x()+10, origin.y()-5}
            });
            p->setBrush(Qt::white);
            p->drawPolygon(poly);
            break;
        }
        }
        break;
    case QuUK::AggregationKind::Composite:
        switch(entry) {
        case Up: {
            origin.ry() -= t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()-10},
                {origin.x(), origin.y()-20},
                {origin.x()-5, origin.y()-10}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Down: {
            origin.ry() += t->height() / 2;
            origin.rx() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+5, origin.y()+10},
                {origin.x(), origin.y()+20},
                {origin.x()-5, origin.y()+10}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Left: {
            origin.rx() -= t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()-10, origin.y()+5},
                {origin.x()-20, origin.y()},
                {origin.x()-10, origin.y()-5}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        case Right: {
            origin.rx() += t->width() / 2;
            origin.ry() += 6;
            QPolygonF poly({
                {origin.x(), origin.y()},
                {origin.x()+10, origin.y()+5},
                {origin.x()+20, origin.y()},
                {origin.x()+10, origin.y()-5}
            });
            p->setBrush(Qt::black);
            p->drawPolygon(poly);
            break;
        }
        }
        break;
    default:
        break;
    }
    }
QUML_END_NAMESPACE_GD

<|endoftext|>


<commit_before>#ifndef RADIUM_CONVERT_TESTS_HPP_
#define RADIUM_CONVERT_TESTS_HPP_

#include <Core/Mesh/MeshPrimitives.hpp>
#include <Core/Mesh/TopologicalTriMesh/TopologicalMesh.hpp>
#include <Core/Mesh/TriangleMesh.hpp>
#include <Core/Mesh/Wrapper/TopologicalMeshConvert.hpp>
#include <Tests/Tests.hpp>

#include <OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh>
#include <OpenMesh/Tools/Subdivider/Uniform/LoopT.hh>

#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>

using Ra::Core::MeshConverter;
using Ra::Core::TopologicalMesh;
using Ra::Core::TriangleMesh;
using Ra::Core::Vector3;

namespace RaTests {

class TopologicalMeshTests : public Test {
    using Catmull = OpenMesh::Subdivider::Uniform::CatmullClarkT<Ra::Core::TopologicalMesh>;
    using Loop = OpenMesh::Subdivider::Uniform::LoopT<Ra::Core::TopologicalMesh>;

    using Decimater = OpenMesh::Decimater::DecimaterT<Ra::Core::TopologicalMesh>;
    using HModQuadric = OpenMesh::Decimater::ModQuadricT<Ra::Core::TopologicalMesh>::Handle;

    void testCopyConsistency() {
        TriangleMesh newMesh;
        TriangleMesh mesh;
        TopologicalMesh topologicalMesh;

        // Test for close mesh
        mesh = Ra::Core::MeshUtils::makeBox();
        Ra::Core::MeshConverter::convert( mesh, topologicalMesh );
        Ra::Core::MeshConverter::convert( topologicalMesh, newMesh );
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological box mesh failed" );

        // Test for mesh with boundaries
        mesh = Ra::Core::MeshUtils::makePlaneGrid( 2, 2 );
        Ra::Core::MeshConverter::convert( mesh, topologicalMesh );
        Ra::Core::MeshConverter::convert( topologicalMesh, newMesh );
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological grid mesh failed" );

        mesh = Ra::Core::MeshUtils::makeCylinder( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ), 1 );
        Ra::Core::MeshConverter::convert( mesh, topologicalMesh );
        Ra::Core::MeshConverter::convert( topologicalMesh, newMesh );
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ),
                      "Conversion to topological cylinder mesh failed" );
    }

    template <typename SubdividerType>
    void testSubdivision() {
        TriangleMesh mesh;
        TriangleMesh newMesh;
        TopologicalMesh topologicalMesh;

        // Generate input geometry
        const int nbIter = 2;
        const int gridSize = 1;

        mesh = Ra::Core::MeshUtils::makePlaneGrid( gridSize, gridSize );
        Ra::Core::MeshConverter::convert( mesh, topologicalMesh );

        // Subdivide
        SubdividerType subdivider;
        subdivider.attach( topologicalMesh );
        subdivider( nbIter );
        subdivider.detach();
        topologicalMesh.triangulate();
        Ra::Core::MeshConverter::convert( topologicalMesh, newMesh );

        RA_UNIT_TEST( newMesh.m_triangles.size() > 2 * nbIter * mesh.m_triangles.size(),
                      "Conversion to topological cylinder mesh failed" );
    }

    template <typename DecimationModule>
    void testDecimation() {

        TriangleMesh mesh;
        TriangleMesh newMesh;
        TopologicalMesh topologicalMesh;

        // Generate input geometry
        const int nbIter = 2;
        const int gridSize = 5;

        mesh = Ra::Core::MeshUtils::makePlaneGrid( gridSize, gridSize );
        Ra::Core::MeshConverter::convert( mesh, topologicalMesh );

        // Decimate
        Decimater decimater( topologicalMesh );
        DecimationModule mod;
        decimater.add( mod );
        decimater.initialize();
        decimater.decimate();
        Ra::Core::MeshConverter::convert( topologicalMesh, newMesh );

        RA_UNIT_TEST( newMesh.m_triangles.size() < 2 * nbIter * mesh.m_triangles.size(),
                      "Conversion to topological cylinder mesh failed" );
    }

    bool isSameMesh( TriangleMesh& meshOne, TriangleMesh& meshTwo ) {
        bool result = true;
        int i = 0;
        // Check length
        if ( meshOne.vertices().size() != meshTwo.vertices().size() )
            return false;
        if ( meshOne.normals().size() != meshTwo.normals().size() )
            return false;
        if ( meshOne.m_triangles.size() != meshTwo.m_triangles.size() )
            return false;

        // Check triangles
        std::vector<Ra::Core::Vector3> stackVertices;

        i = 0;
        while ( result && i < meshOne.m_triangles.size() )
        {
            std::vector<Ra::Core::Vector3>::iterator it;
            stackVertices.clear();
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][0]] );
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][1]] );
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][2]] );
            for ( int j = 0; j < 3; ++j )
            {
                it = find( stackVertices.begin(), stackVertices.end(),
                           meshTwo.vertices()[meshTwo.m_triangles[i][j]] );
                if ( it != stackVertices.end() )
                {
                    stackVertices.erase( it );
                }
                else
                { result = false; }
            }
            ++i;
        }
        return result;
    }

    void run() override {
        testCopyConsistency();
        testSubdivision<Catmull>();
        testSubdivision<Loop>();
        testDecimation<HModQuadric>();
    }
};
RA_TEST_CLASS( TopologicalMeshTests );
} // namespace RaTests

#endif // RADIUM_CONVERT_TESTS_HPP_
<commit_msg>Wrong header guard. remove subdivide tests (since they do not check attr).<commit_after>#ifndef RADIUM_TOPOLOGICALMESH_TESTS_HPP_
#define RADIUM_TOPOLOGICALMESH_TESTS_HPP_

#include <Core/Mesh/MeshPrimitives.hpp>
#include <Core/Mesh/TopologicalTriMesh/TopologicalMesh.hpp>
#include <Core/Mesh/TriangleMesh.hpp>
#include <Tests/Tests.hpp>

#include <OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh>
#include <OpenMesh/Tools/Subdivider/Uniform/LoopT.hh>

#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>

using Ra::Core::TopologicalMesh;
using Ra::Core::TriangleMesh;
using Ra::Core::Vector3;

namespace RaTests {

class TopologicalMeshTests : public Test {
    using Catmull = OpenMesh::Subdivider::Uniform::CatmullClarkT<Ra::Core::TopologicalMesh>;
    using Loop = OpenMesh::Subdivider::Uniform::LoopT<Ra::Core::TopologicalMesh>;

    using Decimater = OpenMesh::Decimater::DecimaterT<Ra::Core::TopologicalMesh>;
    using HModQuadric = OpenMesh::Decimater::ModQuadricT<Ra::Core::TopologicalMesh>::Handle;

    void testCopyConsistency() {
        TriangleMesh newMesh;
        TriangleMesh mesh;
        TopologicalMesh topologicalMesh;

        // Test for close mesh
        mesh = Ra::Core::MeshUtils::makeBox();
        topologicalMesh = TopologicalMesh( mesh );
        newMesh = topologicalMesh.toTriangleMesh();
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological box mesh" );

        mesh = Ra::Core::MeshUtils::makeSharpBox();
        topologicalMesh = TopologicalMesh( mesh );
        newMesh = topologicalMesh.toTriangleMesh();
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological sharp box mesh" );

        // Test for mesh with boundaries
        mesh = Ra::Core::MeshUtils::makePlaneGrid( 2, 2 );
        topologicalMesh = TopologicalMesh( mesh );
        newMesh = topologicalMesh.toTriangleMesh();
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological grid mesh" );

        mesh = Ra::Core::MeshUtils::makeCylinder( Vector3( 0, 0, 0 ), Vector3( 0, 0, 1 ), 1 );
        topologicalMesh = TopologicalMesh( mesh );
        newMesh = topologicalMesh.toTriangleMesh();
        RA_UNIT_TEST( isSameMesh( mesh, newMesh ), "Conversion to topological cylinder mesh" );
    }

    bool isSameMesh( TriangleMesh& meshOne, TriangleMesh& meshTwo ) {
        bool result = true;
        int i = 0;
        // Check length
        if ( meshOne.vertices().size() != meshTwo.vertices().size() )
            return false;
        if ( meshOne.normals().size() != meshTwo.normals().size() )
            return false;
        if ( meshOne.m_triangles.size() != meshTwo.m_triangles.size() )
            return false;

        // Check triangles
        std::vector<Ra::Core::Vector3> stackVertices;
        std::vector<Ra::Core::Vector3> stackNormals;

        i = 0;
        while ( result && i < meshOne.m_triangles.size() )
        {
            std::vector<Ra::Core::Vector3>::iterator it;
            stackVertices.clear();
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][0]] );
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][1]] );
            stackVertices.push_back( meshOne.vertices()[meshOne.m_triangles[i][2]] );

            stackNormals.clear();
            stackNormals.push_back( meshOne.normals()[meshOne.m_triangles[i][0]] );
            stackNormals.push_back( meshOne.normals()[meshOne.m_triangles[i][1]] );
            stackNormals.push_back( meshOne.normals()[meshOne.m_triangles[i][2]] );

            for ( int j = 0; j < 3; ++j )
            {
                it = find( stackVertices.begin(), stackVertices.end(),
                           meshTwo.vertices()[meshTwo.m_triangles[i][j]] );
                if ( it != stackVertices.end() )
                {
                    stackVertices.erase( it );
                } else
                { result = false; }
            }

            for ( int j = 0; j < 3; ++j )
            {
                it = find( stackNormals.begin(), stackNormals.end(),
                           meshTwo.normals()[meshTwo.m_triangles[i][j]] );
                if ( it != stackNormals.end() )
                {
                    stackNormals.erase( it );
                } else
                { result = false; }
            }

            ++i;
        }
        return result;
    }

    void run() override { testCopyConsistency(); }
};
RA_TEST_CLASS( TopologicalMeshTests );

} // namespace RaTests

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string.h>
#include <time.h>
#include <vector>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/time.h"
#include "skia/ext/convolver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkTypes.h"

namespace skia {

namespace {

// Fills the given filter with impulse functions for the range 0->num_entries.
void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) {
  float one = 1.0f;
  for (int i = 0; i < num_entries; i++)
    filter->AddFilter(i, &one, 1);
}

// Filters the given input with the impulse function, and verifies that it
// does not change.
void TestImpulseConvolution(const unsigned char* data, int width, int height) {
  int byte_count = width * height * 4;

  ConvolutionFilter1D filter_x;
  FillImpulseFilter(width, &filter_x);

  ConvolutionFilter1D filter_y;
  FillImpulseFilter(height, &filter_y);

  std::vector<unsigned char> output;
  output.resize(byte_count);
  BGRAConvolve2D(data, width * 4, true, filter_x, filter_y,
                 filter_x.num_values() * 4, &output[0], false);

  // Output should exactly match input.
  EXPECT_EQ(0, memcmp(data, &output[0], byte_count));
}

// Fills the destination filter with a box filter averaging every two pixels
// to produce the output.
void FillBoxFilter(int size, ConvolutionFilter1D* filter) {
  const float box[2] = { 0.5, 0.5 };
  for (int i = 0; i < size; i++)
    filter->AddFilter(i * 2, box, 2);
}

}  // namespace

// Tests that each pixel, when set and run through the impulse filter, does
// not change.
TEST(Convolver, Impulse) {
  // We pick an "odd" size that is not likely to fit on any boundaries so that
  // we can see if all the widths and paddings are handled properly.
  int width = 15;
  int height = 31;
  int byte_count = width * height * 4;
  std::vector<unsigned char> input;
  input.resize(byte_count);

  unsigned char* input_ptr = &input[0];
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      for (int channel = 0; channel < 3; channel++) {
        memset(input_ptr, 0, byte_count);
        input_ptr[(y * width + x) * 4 + channel] = 0xff;
        // Always set the alpha channel or it will attempt to "fix" it for us.
        input_ptr[(y * width + x) * 4 + 3] = 0xff;
        TestImpulseConvolution(input_ptr, width, height);
      }
    }
  }
}

// Tests that using a box filter to halve an image results in every square of 4
// pixels in the original get averaged to a pixel in the output.
TEST(Convolver, Halve) {
  static const int kSize = 16;

  int src_width = kSize;
  int src_height = kSize;
  int src_row_stride = src_width * 4;
  int src_byte_count = src_row_stride * src_height;
  std::vector<unsigned char> input;
  input.resize(src_byte_count);

  int dest_width = src_width / 2;
  int dest_height = src_height / 2;
  int dest_byte_count = dest_width * dest_height * 4;
  std::vector<unsigned char> output;
  output.resize(dest_byte_count);

  // First fill the array with a bunch of random data.
  srand(static_cast<unsigned>(time(NULL)));
  for (int i = 0; i < src_byte_count; i++)
    input[i] = rand() * 255 / RAND_MAX;

  // Compute the filters.
  ConvolutionFilter1D filter_x, filter_y;
  FillBoxFilter(dest_width, &filter_x);
  FillBoxFilter(dest_height, &filter_y);

  // Do the convolution.
  BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y,
                 filter_x.num_values() * 4, &output[0], false);

  // Compute the expected results and check, allowing for a small difference
  // to account for rounding errors.
  for (int y = 0; y < dest_height; y++) {
    for (int x = 0; x < dest_width; x++) {
      for (int channel = 0; channel < 4; channel++) {
        int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel;
        int value = input[src_offset] +  // Top left source pixel.
                    input[src_offset + 4] +  // Top right source pixel.
                    input[src_offset + src_row_stride] +  // Lower left.
                    input[src_offset + src_row_stride + 4];  // Lower right.
        value /= 4;  // Average.
        int difference = value - output[(y * dest_width + x) * 4 + channel];
        EXPECT_TRUE(difference >= -1 || difference <= 1);
      }
    }
  }
}

// Tests the optimization in Convolver1D::AddFilter that avoids storing
// leading/trailing zeroes.
TEST(Convolver, AddFilter) {
  skia::ConvolutionFilter1D filter;

  const skia::ConvolutionFilter1D::Fixed* values = NULL;
  int filter_offset = 0;
  int filter_length = 0;

  // An all-zero filter is handled correctly, all factors ignored
  static const float factors1[] = { 0.0f, 0.0f, 0.0f };
  filter.AddFilter(11, factors1, arraysize(factors1));
  ASSERT_EQ(0, filter.max_filter());
  ASSERT_EQ(1, filter.num_values());

  values = filter.FilterForValue(0, &filter_offset, &filter_length);
  ASSERT_TRUE(values == NULL);   // No values => NULL.
  ASSERT_EQ(11, filter_offset);  // Same as input offset.
  ASSERT_EQ(0, filter_length);   // But no factors since all are zeroes.

  // Zeroes on the left are ignored
  static const float factors2[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f };
  filter.AddFilter(22, factors2, arraysize(factors2));
  ASSERT_EQ(4, filter.max_filter());
  ASSERT_EQ(2, filter.num_values());

  values = filter.FilterForValue(1, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(23, filter_offset);  // 22 plus 1 leading zero
  ASSERT_EQ(4, filter_length);   // 5 - 1 leading zero

  // Zeroes on the right are ignored
  static const float factors3[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
  filter.AddFilter(33, factors3, arraysize(factors3));
  ASSERT_EQ(5, filter.max_filter());
  ASSERT_EQ(3, filter.num_values());

  values = filter.FilterForValue(2, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(33, filter_offset);  // 33, same as input due to no leading zero
  ASSERT_EQ(5, filter_length);   // 7 - 2 trailing zeroes

  // Zeroes in leading & trailing positions
  static const float factors4[] = { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
  filter.AddFilter(44, factors4, arraysize(factors4));
  ASSERT_EQ(5, filter.max_filter());  // No change from existing value.
  ASSERT_EQ(4, filter.num_values());

  values = filter.FilterForValue(3, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(46, filter_offset);  // 44 plus 2 leading zeroes
  ASSERT_EQ(3, filter_length);   // 7 - (2 leading + 2 trailing) zeroes

  // Zeroes surrounded by non-zero values are ignored
  static const float factors5[] = { 0.0f, 0.0f,
                                    1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
                                    0.0f };
  filter.AddFilter(55, factors5, arraysize(factors5));
  ASSERT_EQ(6, filter.max_filter());
  ASSERT_EQ(5, filter.num_values());

  values = filter.FilterForValue(4, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(57, filter_offset);  // 55 plus 2 leading zeroes
  ASSERT_EQ(6, filter_length);   // 9 - (2 leading + 1 trailing) zeroes

  // All-zero filters after the first one also work
  static const float factors6[] = { 0.0f };
  filter.AddFilter(66, factors6, arraysize(factors6));
  ASSERT_EQ(6, filter.max_filter());
  ASSERT_EQ(6, filter.num_values());

  values = filter.FilterForValue(5, &filter_offset, &filter_length);
  ASSERT_TRUE(values == NULL);   // filter_length == 0 => values is NULL
  ASSERT_EQ(66, filter_offset);  // value passed in
  ASSERT_EQ(0, filter_length);
}

TEST(Convolver, SIMDVerification) {
#if defined(SIMD_SSE2)
  base::CPU cpu;
  if (!cpu.has_sse2()) return;

  int source_sizes[][2] = { {1920, 1080}, {720, 480}, {1377, 523}, {325, 241} };
  int dest_sizes[][2] = { {1280, 1024}, {480, 270}, {177, 123} };
  float filter[] = { 0.05f, -0.15f, 0.6f, 0.6f, -0.15f, 0.05f };

  srand(static_cast<unsigned int>(time(0)));

  // Loop over some specific source and destination dimensions.
  for (unsigned int i = 0; i < arraysize(source_sizes); ++i) {
    unsigned int source_width = source_sizes[i][0];
    unsigned int source_height = source_sizes[i][1];
    for (unsigned int j = 0; j < arraysize(dest_sizes); ++j) {
      unsigned int dest_width = source_sizes[j][0];
      unsigned int dest_height = source_sizes[j][1];

      // Preparing convolve coefficients.
      ConvolutionFilter1D x_filter, y_filter;
      for (unsigned int p = 0; p < dest_width; ++p) {
        unsigned int offset = source_width * p / dest_width;
        if (offset > source_width - arraysize(filter))
          offset = source_width - arraysize(filter);
        x_filter.AddFilter(offset, filter, arraysize(filter));
      }
      for (unsigned int p = 0; p < dest_height; ++p) {
        unsigned int offset = source_height * p / dest_height;
        if (offset > source_height - arraysize(filter))
          offset = source_height - arraysize(filter);
        y_filter.AddFilter(offset, filter, arraysize(filter));
      }

      // Allocate input and output skia bitmap.
      SkBitmap source, result_c, result_sse;
      source.setConfig(SkBitmap::kARGB_8888_Config,
                       source_width, source_height);
      source.allocPixels();
      result_c.setConfig(SkBitmap::kARGB_8888_Config,
                         dest_width, dest_height);
      result_c.allocPixels();
      result_sse.setConfig(SkBitmap::kARGB_8888_Config,
                           dest_width, dest_height);
      result_sse.allocPixels();

      // Randomize source bitmap for testing.
      unsigned char* src_ptr = static_cast<unsigned char*>(source.getPixels());
      for (int y = 0; y < source.height(); y++) {
        for (int x = 0; x < source.rowBytes(); x++)
          src_ptr[x] = rand() % 255;
        src_ptr += source.rowBytes();
      }

      // Test both cases with different has_alpha.
      for (int alpha = 0; alpha < 2; alpha++) {
        // Convolve using C code.
        base::TimeTicks resize_start;
        base::TimeDelta delta_c, delta_sse;
        unsigned char* r1 = static_cast<unsigned char*>(result_c.getPixels());
        unsigned char* r2 = static_cast<unsigned char*>(result_sse.getPixels());

        resize_start = base::TimeTicks::Now();
        BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
                       static_cast<int>(source.rowBytes()),
                       alpha ? true : false, x_filter, y_filter,
                       static_cast<int>(result_c.rowBytes()), r1, false);
        delta_c = base::TimeTicks::Now() - resize_start;

        resize_start = base::TimeTicks::Now();
        // Convolve using SSE2 code
        BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
                       static_cast<int>(source.rowBytes()),
                       alpha ? true : false, x_filter, y_filter,
                       static_cast<int>(result_sse.rowBytes()), r2, true);
        delta_sse = base::TimeTicks::Now() - resize_start;

        // Unfortunately I could not enable the performance check now.
        // Most bots use debug version, and there are great difference between
        // the code generation for intrinsic, etc. In release version speed
        // difference was 150%-200% depend on alpha channel presence;
        // while in debug version speed difference was 96%-120%.
        // TODO(jiesun): optimize further until we could enable this for
        // debug version too.
        // EXPECT_LE(delta_sse, delta_c);

        int64 c_us = delta_c.InMicroseconds();
        int64 sse_us = delta_sse.InMicroseconds();
        LOG(INFO) << "from:" << source_width << "x" << source_height
                  << " to:" << dest_width << "x" << dest_height
                  << (alpha ? " with alpha" : " w/o alpha");
        LOG(INFO) << "c:" << c_us << " sse:" << sse_us;
        LOG(INFO) << "ratio:" << static_cast<float>(c_us) / sse_us;

        // Comparing result.
        for (unsigned int i = 0; i < dest_height; i++) {
          for (unsigned int x = 0; x < dest_width * 4; x++) {  // RGBA always.
            EXPECT_EQ(r1[x], r2[x]);
          }
          r1 += result_c.rowBytes();
          r2 += result_sse.rowBytes();
        }
      }
    }
  }
#endif
}

}  // namespace skia
<commit_msg>Remove log-spam from Convolver.SIMDVerification.<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string.h>
#include <time.h>
#include <vector>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/time.h"
#include "skia/ext/convolver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkColorPriv.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkTypes.h"

namespace skia {

namespace {

// Fills the given filter with impulse functions for the range 0->num_entries.
void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) {
  float one = 1.0f;
  for (int i = 0; i < num_entries; i++)
    filter->AddFilter(i, &one, 1);
}

// Filters the given input with the impulse function, and verifies that it
// does not change.
void TestImpulseConvolution(const unsigned char* data, int width, int height) {
  int byte_count = width * height * 4;

  ConvolutionFilter1D filter_x;
  FillImpulseFilter(width, &filter_x);

  ConvolutionFilter1D filter_y;
  FillImpulseFilter(height, &filter_y);

  std::vector<unsigned char> output;
  output.resize(byte_count);
  BGRAConvolve2D(data, width * 4, true, filter_x, filter_y,
                 filter_x.num_values() * 4, &output[0], false);

  // Output should exactly match input.
  EXPECT_EQ(0, memcmp(data, &output[0], byte_count));
}

// Fills the destination filter with a box filter averaging every two pixels
// to produce the output.
void FillBoxFilter(int size, ConvolutionFilter1D* filter) {
  const float box[2] = { 0.5, 0.5 };
  for (int i = 0; i < size; i++)
    filter->AddFilter(i * 2, box, 2);
}

}  // namespace

// Tests that each pixel, when set and run through the impulse filter, does
// not change.
TEST(Convolver, Impulse) {
  // We pick an "odd" size that is not likely to fit on any boundaries so that
  // we can see if all the widths and paddings are handled properly.
  int width = 15;
  int height = 31;
  int byte_count = width * height * 4;
  std::vector<unsigned char> input;
  input.resize(byte_count);

  unsigned char* input_ptr = &input[0];
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      for (int channel = 0; channel < 3; channel++) {
        memset(input_ptr, 0, byte_count);
        input_ptr[(y * width + x) * 4 + channel] = 0xff;
        // Always set the alpha channel or it will attempt to "fix" it for us.
        input_ptr[(y * width + x) * 4 + 3] = 0xff;
        TestImpulseConvolution(input_ptr, width, height);
      }
    }
  }
}

// Tests that using a box filter to halve an image results in every square of 4
// pixels in the original get averaged to a pixel in the output.
TEST(Convolver, Halve) {
  static const int kSize = 16;

  int src_width = kSize;
  int src_height = kSize;
  int src_row_stride = src_width * 4;
  int src_byte_count = src_row_stride * src_height;
  std::vector<unsigned char> input;
  input.resize(src_byte_count);

  int dest_width = src_width / 2;
  int dest_height = src_height / 2;
  int dest_byte_count = dest_width * dest_height * 4;
  std::vector<unsigned char> output;
  output.resize(dest_byte_count);

  // First fill the array with a bunch of random data.
  srand(static_cast<unsigned>(time(NULL)));
  for (int i = 0; i < src_byte_count; i++)
    input[i] = rand() * 255 / RAND_MAX;

  // Compute the filters.
  ConvolutionFilter1D filter_x, filter_y;
  FillBoxFilter(dest_width, &filter_x);
  FillBoxFilter(dest_height, &filter_y);

  // Do the convolution.
  BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y,
                 filter_x.num_values() * 4, &output[0], false);

  // Compute the expected results and check, allowing for a small difference
  // to account for rounding errors.
  for (int y = 0; y < dest_height; y++) {
    for (int x = 0; x < dest_width; x++) {
      for (int channel = 0; channel < 4; channel++) {
        int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel;
        int value = input[src_offset] +  // Top left source pixel.
                    input[src_offset + 4] +  // Top right source pixel.
                    input[src_offset + src_row_stride] +  // Lower left.
                    input[src_offset + src_row_stride + 4];  // Lower right.
        value /= 4;  // Average.
        int difference = value - output[(y * dest_width + x) * 4 + channel];
        EXPECT_TRUE(difference >= -1 || difference <= 1);
      }
    }
  }
}

// Tests the optimization in Convolver1D::AddFilter that avoids storing
// leading/trailing zeroes.
TEST(Convolver, AddFilter) {
  skia::ConvolutionFilter1D filter;

  const skia::ConvolutionFilter1D::Fixed* values = NULL;
  int filter_offset = 0;
  int filter_length = 0;

  // An all-zero filter is handled correctly, all factors ignored
  static const float factors1[] = { 0.0f, 0.0f, 0.0f };
  filter.AddFilter(11, factors1, arraysize(factors1));
  ASSERT_EQ(0, filter.max_filter());
  ASSERT_EQ(1, filter.num_values());

  values = filter.FilterForValue(0, &filter_offset, &filter_length);
  ASSERT_TRUE(values == NULL);   // No values => NULL.
  ASSERT_EQ(11, filter_offset);  // Same as input offset.
  ASSERT_EQ(0, filter_length);   // But no factors since all are zeroes.

  // Zeroes on the left are ignored
  static const float factors2[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f };
  filter.AddFilter(22, factors2, arraysize(factors2));
  ASSERT_EQ(4, filter.max_filter());
  ASSERT_EQ(2, filter.num_values());

  values = filter.FilterForValue(1, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(23, filter_offset);  // 22 plus 1 leading zero
  ASSERT_EQ(4, filter_length);   // 5 - 1 leading zero

  // Zeroes on the right are ignored
  static const float factors3[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
  filter.AddFilter(33, factors3, arraysize(factors3));
  ASSERT_EQ(5, filter.max_filter());
  ASSERT_EQ(3, filter.num_values());

  values = filter.FilterForValue(2, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(33, filter_offset);  // 33, same as input due to no leading zero
  ASSERT_EQ(5, filter_length);   // 7 - 2 trailing zeroes

  // Zeroes in leading & trailing positions
  static const float factors4[] = { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
  filter.AddFilter(44, factors4, arraysize(factors4));
  ASSERT_EQ(5, filter.max_filter());  // No change from existing value.
  ASSERT_EQ(4, filter.num_values());

  values = filter.FilterForValue(3, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(46, filter_offset);  // 44 plus 2 leading zeroes
  ASSERT_EQ(3, filter_length);   // 7 - (2 leading + 2 trailing) zeroes

  // Zeroes surrounded by non-zero values are ignored
  static const float factors5[] = { 0.0f, 0.0f,
                                    1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
                                    0.0f };
  filter.AddFilter(55, factors5, arraysize(factors5));
  ASSERT_EQ(6, filter.max_filter());
  ASSERT_EQ(5, filter.num_values());

  values = filter.FilterForValue(4, &filter_offset, &filter_length);
  ASSERT_TRUE(values != NULL);
  ASSERT_EQ(57, filter_offset);  // 55 plus 2 leading zeroes
  ASSERT_EQ(6, filter_length);   // 9 - (2 leading + 1 trailing) zeroes

  // All-zero filters after the first one also work
  static const float factors6[] = { 0.0f };
  filter.AddFilter(66, factors6, arraysize(factors6));
  ASSERT_EQ(6, filter.max_filter());
  ASSERT_EQ(6, filter.num_values());

  values = filter.FilterForValue(5, &filter_offset, &filter_length);
  ASSERT_TRUE(values == NULL);   // filter_length == 0 => values is NULL
  ASSERT_EQ(66, filter_offset);  // value passed in
  ASSERT_EQ(0, filter_length);
}

TEST(Convolver, SIMDVerification) {
#if defined(SIMD_SSE2)
  base::CPU cpu;
  if (!cpu.has_sse2()) return;

  int source_sizes[][2] = { {1920, 1080}, {720, 480}, {1377, 523}, {325, 241} };
  int dest_sizes[][2] = { {1280, 1024}, {480, 270}, {177, 123} };
  float filter[] = { 0.05f, -0.15f, 0.6f, 0.6f, -0.15f, 0.05f };

  srand(static_cast<unsigned int>(time(0)));

  // Loop over some specific source and destination dimensions.
  for (unsigned int i = 0; i < arraysize(source_sizes); ++i) {
    unsigned int source_width = source_sizes[i][0];
    unsigned int source_height = source_sizes[i][1];
    for (unsigned int j = 0; j < arraysize(dest_sizes); ++j) {
      unsigned int dest_width = source_sizes[j][0];
      unsigned int dest_height = source_sizes[j][1];

      // Preparing convolve coefficients.
      ConvolutionFilter1D x_filter, y_filter;
      for (unsigned int p = 0; p < dest_width; ++p) {
        unsigned int offset = source_width * p / dest_width;
        if (offset > source_width - arraysize(filter))
          offset = source_width - arraysize(filter);
        x_filter.AddFilter(offset, filter, arraysize(filter));
      }
      for (unsigned int p = 0; p < dest_height; ++p) {
        unsigned int offset = source_height * p / dest_height;
        if (offset > source_height - arraysize(filter))
          offset = source_height - arraysize(filter);
        y_filter.AddFilter(offset, filter, arraysize(filter));
      }

      // Allocate input and output skia bitmap.
      SkBitmap source, result_c, result_sse;
      source.setConfig(SkBitmap::kARGB_8888_Config,
                       source_width, source_height);
      source.allocPixels();
      result_c.setConfig(SkBitmap::kARGB_8888_Config,
                         dest_width, dest_height);
      result_c.allocPixels();
      result_sse.setConfig(SkBitmap::kARGB_8888_Config,
                           dest_width, dest_height);
      result_sse.allocPixels();

      // Randomize source bitmap for testing.
      unsigned char* src_ptr = static_cast<unsigned char*>(source.getPixels());
      for (int y = 0; y < source.height(); y++) {
        for (int x = 0; x < source.rowBytes(); x++)
          src_ptr[x] = rand() % 255;
        src_ptr += source.rowBytes();
      }

      // Test both cases with different has_alpha.
      for (int alpha = 0; alpha < 2; alpha++) {
        // Convolve using C code.
        base::TimeTicks resize_start;
        base::TimeDelta delta_c, delta_sse;
        unsigned char* r1 = static_cast<unsigned char*>(result_c.getPixels());
        unsigned char* r2 = static_cast<unsigned char*>(result_sse.getPixels());

        resize_start = base::TimeTicks::Now();
        BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
                       static_cast<int>(source.rowBytes()),
                       alpha ? true : false, x_filter, y_filter,
                       static_cast<int>(result_c.rowBytes()), r1, false);
        delta_c = base::TimeTicks::Now() - resize_start;

        resize_start = base::TimeTicks::Now();
        // Convolve using SSE2 code
        BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
                       static_cast<int>(source.rowBytes()),
                       alpha ? true : false, x_filter, y_filter,
                       static_cast<int>(result_sse.rowBytes()), r2, true);
        delta_sse = base::TimeTicks::Now() - resize_start;

        // Unfortunately I could not enable the performance check now.
        // Most bots use debug version, and there are great difference between
        // the code generation for intrinsic, etc. In release version speed
        // difference was 150%-200% depend on alpha channel presence;
        // while in debug version speed difference was 96%-120%.
        // TODO(jiesun): optimize further until we could enable this for
        // debug version too.
        // EXPECT_LE(delta_sse, delta_c);

        int64 c_us = delta_c.InMicroseconds();
        int64 sse_us = delta_sse.InMicroseconds();
        VLOG(1) << "from:" << source_width << "x" << source_height
                << " to:" << dest_width << "x" << dest_height
                << (alpha ? " with alpha" : " w/o alpha");
        VLOG(1) << "c:" << c_us << " sse:" << sse_us;
        VLOG(1) << "ratio:" << static_cast<float>(c_us) / sse_us;

        // Comparing result.
        for (unsigned int i = 0; i < dest_height; i++) {
          for (unsigned int x = 0; x < dest_width * 4; x++) {  // RGBA always.
            EXPECT_EQ(r1[x], r2[x]);
          }
          r1 += result_c.rowBytes();
          r2 += result_sse.rowBytes();
        }
      }
    }
  }
#endif
}

}  // namespace skia
<|endoftext|>


<commit_before>
/** ==========================================================================
* 2011 by KjellKod.cc, modified by Vrecan in https://bitbucket.org/vrecan/g2log-dev
* 2015, adopted by KjellKod for g3log at:https://github.com/KjellKod/g3sinks
*
* This code is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */

#include "g3sinks/LogRotateUtility.h"
#include <iostream>
#include <sstream>
#include <algorithm>
#include <g3log/time.hpp>
#include <regex>
#include <boost/filesystem.hpp>
#include <ios>
#include <fstream>
#include <iomanip>
#include <map>

namespace  LogRotateUtility {

#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
   //http://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
   char* strptime(const char* s, const char* f, struct tm* tm) {
      // Isn't the C++ standard lib nice? std::get_time is defined such that its
      // format parameters are the exact same as strptime. Of course, we have to
      // create a string stream first, and imbue it with the current C locale, and
      // we also have to make sure we return the right things if it fails, or
      // if it succeeds, but this is still far simpler an implementation than any
      // of the versions in any of the C standard libraries.
      std::istringstream input(s);
      input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
      input >> std::get_time(tm, f);
      if (input.fail()) {
         return nullptr;
      }
      return (char*)(s + input.tellg());
   }
#endif

   // check for filename validity -  filename should not be part of PATH
   bool isValidFilename(const std::string& prefix_filename) {
      std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
      size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
      if (pos != std::string::npos) {
         std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
         return false;
      } else if (prefix_filename.empty()) {
         std::cerr << "Empty filename prefix is not allowed" << std::endl;
         return false;
      }

      return true;
   }
   /// @return a corrected prefix, if needed,
   /// illegal characters are removed from @param prefix input
   std::string prefixSanityFix(std::string prefix) {
      prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); // '/'
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); // '\\'
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); // '.'
      if (!isValidFilename(prefix)) {
         return "";
      }
      return prefix;
   }

   /// @return the file header
   std::string header() {
      std::ostringstream ss_entry;
      //  Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
      ss_entry << "\ng3log: created log file at: " << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
      return ss_entry.str();
   }

   /// @return result as time from the file name
   bool getDateFromFileName(const std::string& app_name, const std::string& file_name, long& result) {
      if (file_name.find(app_name) != std::string::npos) {
         std::string suffix = file_name.substr(app_name.size());
         if (suffix.empty()) {
            //this is the main log file
            return false;
         }
         using namespace std;

         regex date_regex("\\.(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2})\\.gz");
         smatch date_match;
         if (regex_match(suffix, date_match, date_regex)) {
            if (date_match.size() == 2) {
               std::string date = date_match[1].str();
               struct tm tm;
               time_t t;
               if (strptime(date.c_str(), "%Y-%m-%d-%H-%M-%S", &tm) == nullptr) {
                  return false;
               }
               t = mktime(&tm);
               if (t == -1) {
                  return false;
               }
               result = (long) t;
               return true;
            }
         }
      }
      return false;
   }

   std::string pathSanityFix(std::string path, std::string file_name) {
      // Unify the delimeters,. maybe sketchy solution but it seems to work
      // on at least win7 + ubuntu. All bets are off for older windows
      std::replace(path.begin(), path.end(), '\\', '/');

      // clean up in case of multiples
      auto contains_end = [&](std::string & in) -> bool {
         size_t size = in.size();
         if (!size) return false;
         char end = in[size - 1];
         return (end == '/' || end == ' ');
      };

      while (contains_end(path)) {
         path.erase(path.size() - 1);
      }

      if (!path.empty()) {
         path.insert(path.end(), '/');
      }

      path.insert(path.size(), file_name);
      return path;
   }

   /**
    * Loop through the files in the folder
    * @param dir
    * @param file_name
    */
   void expireArchives(const std::string& dir, const std::string& app_name, unsigned long max_log_count) {
      std::map<long, std::string> files;
      boost::filesystem::path dir_path(dir);

      boost::filesystem::directory_iterator end_itr;
      if (!boost::filesystem::exists(dir_path)) return;

      for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) {
         std::string current_file(itr->path().filename().string());
         long time = 0;
         if (getDateFromFileName(app_name, current_file, time)) {
            files.insert(std::pair<long, std::string > (time, current_file));
         }
      }

      //delete old logs.
      ptrdiff_t logs_to_delete = files.size() - max_log_count;
      if (logs_to_delete > 0) {

         for (std::map<long, std::string>::iterator it = files.begin(); it != files.end(); ++it) {
            if (logs_to_delete <= 0) {
               break;
            }

            std::string filename_with_path(pathSanityFix(dir, it->second));
            remove(filename_with_path.c_str());
            --logs_to_delete;
         }
      }
   }

    std::map<long, std::string> getLogFilesInDirectory(const std::string& dir, const std::string& app_name) {
        std::map<long, std::string> files;
        boost::filesystem::path dir_path(dir);

       boost::filesystem::directory_iterator end_itr;
       if (!boost::filesystem::exists(dir_path)) return {};

       for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) {
          std::string current_file(itr->path().filename().string());
          long time = 0;
          if (getDateFromFileName(app_name, current_file, time)) {
             files.insert(std::pair<long, std::string > (time, current_file));
          }
       }

       return files;
    }

   /// create the file name
   std::string createLogFileName(const std::string& verified_prefix) {
      std::stringstream oss_name;
      oss_name << verified_prefix << ".log";
      return oss_name.str();
   }

   /// @return true if @param complete_file_with_path could be opened
   /// @param outstream is the file stream
   bool openLogFile(const std::string& complete_file_with_path, std::ofstream& outstream) {
      std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
      mode |= std::ios_base::app;
      outstream.open(complete_file_with_path, mode);
      if (!outstream.is_open()) {
         std::ostringstream ss_error;
         ss_error << "FILE ERROR:  could not open log file:[" << complete_file_with_path << "]";
         ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
         std::cerr << ss_error.str().c_str() << std::endl;
         outstream.close();
         return false;
      }
      return true;
   }

   /// create the file
   std::unique_ptr<std::ofstream> createLogFile(const std::string& file_with_full_path) {
      std::unique_ptr<std::ofstream> out(new std::ofstream);
      std::ofstream& stream(*(out.get()));
      bool success_with_open_file = openLogFile(file_with_full_path, stream);
      if (false == success_with_open_file) {
         out.release();
      }
      return out;
   }
}<commit_msg>remove include for map<commit_after>
/** ==========================================================================
* 2011 by KjellKod.cc, modified by Vrecan in https://bitbucket.org/vrecan/g2log-dev
* 2015, adopted by KjellKod for g3log at:https://github.com/KjellKod/g3sinks
*
* This code is PUBLIC DOMAIN to use at your own risk and comes
* with no warranties. This code is yours to share, use and modify with no
* strings attached and no restrictions or obligations.
* ============================================================================*
* PUBLIC DOMAIN and Not copywrited. First published at KjellKod.cc
* ********************************************* */

#include "g3sinks/LogRotateUtility.h"
#include <iostream>
#include <sstream>
#include <algorithm>
#include <g3log/time.hpp>
#include <regex>
#include <boost/filesystem.hpp>
#include <ios>
#include <fstream>
#include <iomanip>

namespace  LogRotateUtility {

#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
   //http://stackoverflow.com/questions/321849/strptime-equivalent-on-windows
   char* strptime(const char* s, const char* f, struct tm* tm) {
      // Isn't the C++ standard lib nice? std::get_time is defined such that its
      // format parameters are the exact same as strptime. Of course, we have to
      // create a string stream first, and imbue it with the current C locale, and
      // we also have to make sure we return the right things if it fails, or
      // if it succeeds, but this is still far simpler an implementation than any
      // of the versions in any of the C standard libraries.
      std::istringstream input(s);
      input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
      input >> std::get_time(tm, f);
      if (input.fail()) {
         return nullptr;
      }
      return (char*)(s + input.tellg());
   }
#endif

   // check for filename validity -  filename should not be part of PATH
   bool isValidFilename(const std::string& prefix_filename) {
      std::string illegal_characters("/,|<>:#$%{}()[]\'\"^!?+* ");
      size_t pos = prefix_filename.find_first_of(illegal_characters, 0);
      if (pos != std::string::npos) {
         std::cerr << "Illegal character [" << prefix_filename.at(pos) << "] in logname prefix: " << "[" << prefix_filename << "]" << std::endl;
         return false;
      } else if (prefix_filename.empty()) {
         std::cerr << "Empty filename prefix is not allowed" << std::endl;
         return false;
      }

      return true;
   }
   /// @return a corrected prefix, if needed,
   /// illegal characters are removed from @param prefix input
   std::string prefixSanityFix(std::string prefix) {
      prefix.erase(std::remove_if(prefix.begin(), prefix.end(), ::isspace), prefix.end());
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '/'), prefix.end()); // '/'
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '\\'), prefix.end()); // '\\'
      prefix.erase(std::remove(prefix.begin(), prefix.end(), '.'), prefix.end()); // '.'
      if (!isValidFilename(prefix)) {
         return "";
      }
      return prefix;
   }

   /// @return the file header
   std::string header() {
      std::ostringstream ss_entry;
      //  Day Month Date Time Year: is written as "%a %b %d %H:%M:%S %Y" and formatted output as : Wed Sep 19 08:28:16 2012
      ss_entry << "\ng3log: created log file at: " << g3::localtime_formatted(g3::systemtime_now(), "%a %b %d %H:%M:%S %Y") << "\n";
      return ss_entry.str();
   }

   /// @return result as time from the file name
   bool getDateFromFileName(const std::string& app_name, const std::string& file_name, long& result) {
      if (file_name.find(app_name) != std::string::npos) {
         std::string suffix = file_name.substr(app_name.size());
         if (suffix.empty()) {
            //this is the main log file
            return false;
         }
         using namespace std;

         regex date_regex("\\.(\\d{4}-\\d{2}-\\d{2}-\\d{2}-\\d{2}-\\d{2})\\.gz");
         smatch date_match;
         if (regex_match(suffix, date_match, date_regex)) {
            if (date_match.size() == 2) {
               std::string date = date_match[1].str();
               struct tm tm;
               time_t t;
               if (strptime(date.c_str(), "%Y-%m-%d-%H-%M-%S", &tm) == nullptr) {
                  return false;
               }
               t = mktime(&tm);
               if (t == -1) {
                  return false;
               }
               result = (long) t;
               return true;
            }
         }
      }
      return false;
   }

   std::string pathSanityFix(std::string path, std::string file_name) {
      // Unify the delimeters,. maybe sketchy solution but it seems to work
      // on at least win7 + ubuntu. All bets are off for older windows
      std::replace(path.begin(), path.end(), '\\', '/');

      // clean up in case of multiples
      auto contains_end = [&](std::string & in) -> bool {
         size_t size = in.size();
         if (!size) return false;
         char end = in[size - 1];
         return (end == '/' || end == ' ');
      };

      while (contains_end(path)) {
         path.erase(path.size() - 1);
      }

      if (!path.empty()) {
         path.insert(path.end(), '/');
      }

      path.insert(path.size(), file_name);
      return path;
   }

   /**
    * Loop through the files in the folder
    * @param dir
    * @param file_name
    */
   void expireArchives(const std::string& dir, const std::string& app_name, unsigned long max_log_count) {
      std::map<long, std::string> files;
      boost::filesystem::path dir_path(dir);

      boost::filesystem::directory_iterator end_itr;
      if (!boost::filesystem::exists(dir_path)) return;

      for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) {
         std::string current_file(itr->path().filename().string());
         long time = 0;
         if (getDateFromFileName(app_name, current_file, time)) {
            files.insert(std::pair<long, std::string > (time, current_file));
         }
      }

      //delete old logs.
      ptrdiff_t logs_to_delete = files.size() - max_log_count;
      if (logs_to_delete > 0) {

         for (std::map<long, std::string>::iterator it = files.begin(); it != files.end(); ++it) {
            if (logs_to_delete <= 0) {
               break;
            }

            std::string filename_with_path(pathSanityFix(dir, it->second));
            remove(filename_with_path.c_str());
            --logs_to_delete;
         }
      }
   }

    std::map<long, std::string> getLogFilesInDirectory(const std::string& dir, const std::string& app_name) {
        std::map<long, std::string> files;
        boost::filesystem::path dir_path(dir);

       boost::filesystem::directory_iterator end_itr;
       if (!boost::filesystem::exists(dir_path)) return {};

       for (boost::filesystem::directory_iterator itr(dir_path); itr != end_itr; ++itr) {
          std::string current_file(itr->path().filename().string());
          long time = 0;
          if (getDateFromFileName(app_name, current_file, time)) {
             files.insert(std::pair<long, std::string > (time, current_file));
          }
       }

       return files;
    }

   /// create the file name
   std::string createLogFileName(const std::string& verified_prefix) {
      std::stringstream oss_name;
      oss_name << verified_prefix << ".log";
      return oss_name.str();
   }

   /// @return true if @param complete_file_with_path could be opened
   /// @param outstream is the file stream
   bool openLogFile(const std::string& complete_file_with_path, std::ofstream& outstream) {
      std::ios_base::openmode mode = std::ios_base::out; // for clarity: it's really overkill since it's an ofstream
      mode |= std::ios_base::app;
      outstream.open(complete_file_with_path, mode);
      if (!outstream.is_open()) {
         std::ostringstream ss_error;
         ss_error << "FILE ERROR:  could not open log file:[" << complete_file_with_path << "]";
         ss_error << "\n\t\t std::ios_base state = " << outstream.rdstate();
         std::cerr << ss_error.str().c_str() << std::endl;
         outstream.close();
         return false;
      }
      return true;
   }

   /// create the file
   std::unique_ptr<std::ofstream> createLogFile(const std::string& file_with_full_path) {
      std::unique_ptr<std::ofstream> out(new std::ofstream);
      std::ofstream& stream(*(out.get()));
      bool success_with_open_file = openLogFile(file_with_full_path, stream);
      if (false == success_with_open_file) {
         out.release();
      }
      return out;
   }
}<|endoftext|>