<commit_before>#include "apitrace.h"

#include "traceloader.h"
#include "saverthread.h"

#include <QDebug>
#include <QDir>
#include <QThread>

ApiTrace::ApiTrace()
    : m_needsSaving(false)
{
    m_loader = new TraceLoader();

    connect(this, SIGNAL(loadTrace(QString)),
            m_loader, SLOT(loadTrace(QString)));
    connect(this, SIGNAL(requestFrame(ApiTraceFrame*)),
            m_loader, SLOT(loadFrame(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(framesLoaded(const QList<ApiTraceFrame*>)),
            this, SLOT(addFrames(const QList<ApiTraceFrame*>)));
    connect(m_loader,
            SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
            this,
            SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
    connect(m_loader, SIGNAL(guessedApi(int)),
            this, SLOT(guessedApi(int)));
    connect(m_loader, SIGNAL(finishedParsing()),
            this, SLOT(finishedParsing()));
    connect(this, SIGNAL(loaderSearch(ApiTrace::SearchRequest)),
            m_loader, SLOT(search(ApiTrace::SearchRequest)));
    connect(m_loader,
            SIGNAL(searchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
            this,
            SLOT(loaderSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
    connect(this, SIGNAL(loaderFindFrameStart(ApiTraceFrame*)),
            m_loader, SLOT(findFrameStart(ApiTraceFrame*)));
    connect(this, SIGNAL(loaderFindFrameEnd(ApiTraceFrame*)),
            m_loader, SLOT(findFrameEnd(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(foundFrameStart(ApiTraceFrame*)),
            this, SIGNAL(foundFrameStart(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
            this, SIGNAL(foundFrameEnd(ApiTraceFrame*)));
    connect(this, SIGNAL(loaderFindCallIndex(int)),
            m_loader, SLOT(findCallIndex(int)));
    connect(m_loader, SIGNAL(foundCallIndex(ApiTraceCall*)),
            this, SIGNAL(foundCallIndex(ApiTraceCall*)));

    connect(m_loader, SIGNAL(startedParsing()),
            this, SIGNAL(startedLoadingTrace()));
    connect(m_loader, SIGNAL(parsed(int)),
            this, SIGNAL(loaded(int)));
    connect(m_loader, SIGNAL(finishedParsing()),
            this, SIGNAL(finishedLoadingTrace()));

    m_saver = new SaverThread(this);
    connect(m_saver, SIGNAL(traceSaved()),
            this, SLOT(slotSaved()));
    connect(m_saver, SIGNAL(traceSaved()),
            this, SIGNAL(saved()));

    m_loaderThread = new QThread();
    m_loader->moveToThread(m_loaderThread);
    m_loaderThread->start();
}

ApiTrace::~ApiTrace()
{
    m_loaderThread->quit();
    m_loaderThread->deleteLater();
    qDeleteAll(m_frames);
    delete m_loader;
    delete m_saver;
}

bool ApiTrace::isEmpty() const
{
    return m_frames.isEmpty();
}

QString ApiTrace::fileName() const
{
    if (edited()) {
        return m_tempFileName;
    }

    return m_fileName;
}

const QList<ApiTraceFrame*> & ApiTrace::frames() const
{
    return m_frames;
}

ApiTraceFrame * ApiTrace::frameAt(int idx) const
{
    return m_frames.value(idx);
}

int ApiTrace::numFrames() const
{
    return m_frames.count();
}

int ApiTrace::numCallsInFrame(int idx) const
{
    const ApiTraceFrame *frame = frameAt(idx);
    if (frame) {
        return frame->numChildren();
    } else {
        return 0;
    }
}

void ApiTrace::setFileName(const QString &name)
{
    if (m_fileName != name) {
        m_fileName = name;
        m_tempFileName = QString();

        m_frames.clear();
        m_errors.clear();
        m_editedCalls.clear();
        m_queuedErrors.clear();
        m_needsSaving = false;
        emit invalidated();

        emit loadTrace(m_fileName);
    }
}

void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
{
    int currentFrames = m_frames.count();
    int numNewFrames = frames.count();

    emit beginAddingFrames(currentFrames, numNewFrames);

    m_frames += frames;

    foreach(ApiTraceFrame *frame, frames) {
        frame->setParentTrace(this);
    }

    emit endAddingFrames();
}

ApiTraceCall * ApiTrace::callWithIndex(int idx) const
{
    for (int i = 0; i < m_frames.count(); ++i) {
        ApiTraceCall *call = m_frames[i]->callWithIndex(idx);
        if (call) {
            return call;
        }
    }
    return NULL;
}

ApiTraceState ApiTrace::defaultState() const
{
    ApiTraceFrame *frame = frameAt(0);
    if (!frame || !frame->isLoaded() || frame->isEmpty()) {
        return ApiTraceState();
    }

    ApiTraceCall *firstCall = frame->calls().first();
    if (!firstCall->hasState()) {
        return ApiTraceState();
    }

    return *firstCall->state();
}

void ApiTrace::callEdited(ApiTraceCall *call)
{
    if (!m_editedCalls.contains(call)) {
        //lets generate a temp filename
        QString tempPath = QDir::tempPath();
        m_tempFileName = QString::fromLatin1("%1/%2.edited")
                         .arg(tempPath)
                         .arg(m_fileName);
    }
    m_editedCalls.insert(call);
    m_needsSaving = true;

    emit changed(call);
}

void ApiTrace::callReverted(ApiTraceCall *call)
{
    m_editedCalls.remove(call);

    if (m_editedCalls.isEmpty()) {
        m_needsSaving = false;
    }
    emit changed(call);
}

bool ApiTrace::edited() const
{
    return !m_editedCalls.isEmpty();
}

bool ApiTrace::needsSaving() const
{
    return m_needsSaving;
}

void ApiTrace::save()
{
    QFileInfo fi(m_tempFileName);
    QDir dir;
    emit startedSaving();
    dir.mkpath(fi.absolutePath());
    m_saver->saveFile(m_tempFileName,
                      m_fileName,
                      m_editedCalls);
}

void ApiTrace::slotSaved()
{
    m_needsSaving = false;
}

bool ApiTrace::isSaving() const
{
    return m_saver->isRunning();
}

bool ApiTrace::hasErrors() const
{
    return !m_errors.isEmpty() || !m_queuedErrors.isEmpty();
}

void ApiTrace::loadFrame(ApiTraceFrame *frame)
{
    if (!isFrameLoading(frame)) {
        Q_ASSERT(!frame->isLoaded());
        m_loadingFrames.insert(frame);
        emit requestFrame(frame);
    }
}

void ApiTrace::guessedApi(int api)
{
    m_api = static_cast<trace::API>(api);
}

trace::API ApiTrace::api() const
{
    return m_api;
}

void ApiTrace::finishedParsing()
{
    if (!m_frames.isEmpty()) {
        ApiTraceFrame *firstFrame = m_frames[0];
        if (firstFrame && !firstFrame->isLoaded()) {
            loadFrame(firstFrame);
        }
    }
}

void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
                                 const QVector<ApiTraceCall*> &calls,
                                 quint64 binaryDataSize)
{
    Q_ASSERT(frame->numChildrenToLoad() == calls.size());

    if (!frame->isLoaded()) {
        emit beginLoadingFrame(frame, calls.size());
        frame->setCalls(calls, binaryDataSize);
        emit endLoadingFrame(frame);
        m_loadingFrames.remove(frame);
    }

    if (!m_queuedErrors.isEmpty()) {
        QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
        for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
             ++itr) {
            const ApiTraceError &error = (*itr).second;
            if ((*itr).first == frame) {
                ApiTraceCall *call = frame->callWithIndex(error.callIndex);

                if (!call) {
                    continue;
                }

                call->setError(error.message);
                m_queuedErrors.erase(itr);

                if (call->hasError()) {
                    m_errors.insert(call);
                } else {
                    m_errors.remove(call);
                }
                emit changed(call);
            }
        }
    }
}

void ApiTrace::findNext(ApiTraceFrame *frame,
                        ApiTraceCall *from,
                        const QString &str,
                        Qt::CaseSensitivity sensitivity)
{
    ApiTraceCall *foundCall = 0;
    int frameIdx = m_frames.indexOf(frame);
    SearchRequest request(SearchRequest::Next,
                          frame, from, str, sensitivity);

    if (frame->isLoaded()) {
        foundCall = frame->findNextCall(from, str, sensitivity);
        if (foundCall) {
            emit findResult(request, SearchResult_Found, foundCall);
            return;
        }

        //if the frame is loaded we already searched it above
        // so skip it
        frameIdx += 1;
    }

    //for the rest of the frames we search from beginning
    request.from = 0;
    for (int i = frameIdx; i < m_frames.count(); ++i) {
        ApiTraceFrame *frame = m_frames[i];
        request.frame = frame;
        if (!frame->isLoaded()) {
            emit loaderSearch(request);
            return;
        } else {
            ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
            if (call) {
                emit findResult(request, SearchResult_Found, call);
                return;
            }
        }
    }
    emit findResult(request, SearchResult_Wrapped, 0);
}

void ApiTrace::findPrev(ApiTraceFrame *frame,
                        ApiTraceCall *from,
                        const QString &str,
                        Qt::CaseSensitivity sensitivity)
{
    ApiTraceCall *foundCall = 0;
    int frameIdx = m_frames.indexOf(frame);
    SearchRequest request(SearchRequest::Prev,
                          frame, from, str, sensitivity);

    if (frame->isLoaded()) {
        foundCall = frame->findPrevCall(from, str, sensitivity);
        if (foundCall) {
            emit findResult(request, SearchResult_Found, foundCall);
            return;
        }

        //if the frame is loaded we already searched it above
        // so skip it
        frameIdx -= 1;
    }

    request.from = 0;
    for (int i = frameIdx; i >= 0; --i) {
        ApiTraceFrame *frame = m_frames[i];
        request.frame = frame;
        if (!frame->isLoaded()) {
            emit loaderSearch(request);
            return;
        } else {
            ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
            if (call) {
                emit findResult(request, SearchResult_Found, call);
                return;
            }
        }
    }
    emit findResult(request, SearchResult_Wrapped, 0);
}

void ApiTrace::loaderSearchResult(const ApiTrace::SearchRequest &request,
                                  ApiTrace::SearchResult result,
                                  ApiTraceCall *call)
{
    //qDebug()<<"Search result = "<<result
    //       <<", call is = "<<call;
    emit findResult(request, result, call);
}

void ApiTrace::findFrameStart(ApiTraceFrame *frame)
{
    if (frame->isLoaded()) {
        emit foundFrameStart(frame);
    } else {
        emit loaderFindFrameStart(frame);
    }
}

void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
{
    if (frame->isLoaded()) {
        emit foundFrameEnd(frame);
    } else {
        emit loaderFindFrameEnd(frame);
    }
}

void ApiTrace::findCallIndex(int index)
{
    int frameIdx = callInFrame(index);
    ApiTraceFrame *frame = 0;

    if (frameIdx < 0) {
        emit foundCallIndex(0);
        return;
    }

    frame = m_frames[frameIdx];

    if (frame) {
        if (frame->isLoaded()) {
            ApiTraceCall *call = frame->callWithIndex(index);
            emit foundCallIndex(call);
        } else {
            emit loaderFindCallIndex(index);
        }
    }
}

int ApiTrace::callInFrame(int callIdx) const
{
    unsigned numCalls = 0;

    for (int frameIdx = 0; frameIdx < m_frames.size(); ++frameIdx) {
        const ApiTraceFrame *frame = m_frames[frameIdx];
        unsigned numCallsInFrame =  frame->isLoaded()
                ? frame->numChildren()
                : frame->numChildrenToLoad();
        unsigned firstCall = numCalls;
        unsigned endCall = numCalls + numCallsInFrame;
        if (firstCall <= callIdx && endCall > callIdx) {
            return frameIdx;
        }
        numCalls = endCall;
    }

    return -1;
}

void ApiTrace::setCallError(const ApiTraceError &error)
{
    int frameIdx = callInFrame(error.callIndex);
    ApiTraceFrame *frame = 0;

    if (frameIdx < 0) {
        return;
    }
    frame = m_frames[frameIdx];

    if (frame->isLoaded()) {
        ApiTraceCall *call = frame->callWithIndex(error.callIndex);
        call->setError(error.message);
        if (call->hasError()) {
            m_errors.insert(call);
        } else {
            m_errors.remove(call);
        }
        emit changed(call);
    } else {
        loadFrame(frame);
        m_queuedErrors.append(qMakePair(frame, error));
    }
}

bool ApiTrace::isFrameLoading(ApiTraceFrame *frame) const
{
    return m_loadingFrames.contains(frame);
}

void ApiTrace::bindThumbnailsToFrames(const QList<QImage> &thumbnails)
{
    QList<ApiTraceFrame *> frames = m_frames;

    QList<QImage>::const_iterator thumbnail = thumbnails.begin();

    foreach (ApiTraceFrame *frame, frames) {
        if (thumbnail != thumbnails.end()) {
            frame->setThumbnail(*thumbnail);

            ++thumbnail;

            emit changed(frame);
        }
    }
}

#include "apitrace.moc"
<commit_msg>gui: Fix crash on trigger actions without loaded trace-file. gui/apitrace: findFrameStart(), findFrameEnd(): Do nothing, when passed frame is null-pointer.<commit_after>#include "apitrace.h"

#include "traceloader.h"
#include "saverthread.h"

#include <QDebug>
#include <QDir>
#include <QThread>

ApiTrace::ApiTrace()
    : m_needsSaving(false)
{
    m_loader = new TraceLoader();

    connect(this, SIGNAL(loadTrace(QString)),
            m_loader, SLOT(loadTrace(QString)));
    connect(this, SIGNAL(requestFrame(ApiTraceFrame*)),
            m_loader, SLOT(loadFrame(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(framesLoaded(const QList<ApiTraceFrame*>)),
            this, SLOT(addFrames(const QList<ApiTraceFrame*>)));
    connect(m_loader,
            SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
            this,
            SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
    connect(m_loader, SIGNAL(guessedApi(int)),
            this, SLOT(guessedApi(int)));
    connect(m_loader, SIGNAL(finishedParsing()),
            this, SLOT(finishedParsing()));
    connect(this, SIGNAL(loaderSearch(ApiTrace::SearchRequest)),
            m_loader, SLOT(search(ApiTrace::SearchRequest)));
    connect(m_loader,
            SIGNAL(searchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
            this,
            SLOT(loaderSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
    connect(this, SIGNAL(loaderFindFrameStart(ApiTraceFrame*)),
            m_loader, SLOT(findFrameStart(ApiTraceFrame*)));
    connect(this, SIGNAL(loaderFindFrameEnd(ApiTraceFrame*)),
            m_loader, SLOT(findFrameEnd(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(foundFrameStart(ApiTraceFrame*)),
            this, SIGNAL(foundFrameStart(ApiTraceFrame*)));
    connect(m_loader, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
            this, SIGNAL(foundFrameEnd(ApiTraceFrame*)));
    connect(this, SIGNAL(loaderFindCallIndex(int)),
            m_loader, SLOT(findCallIndex(int)));
    connect(m_loader, SIGNAL(foundCallIndex(ApiTraceCall*)),
            this, SIGNAL(foundCallIndex(ApiTraceCall*)));

    connect(m_loader, SIGNAL(startedParsing()),
            this, SIGNAL(startedLoadingTrace()));
    connect(m_loader, SIGNAL(parsed(int)),
            this, SIGNAL(loaded(int)));
    connect(m_loader, SIGNAL(finishedParsing()),
            this, SIGNAL(finishedLoadingTrace()));

    m_saver = new SaverThread(this);
    connect(m_saver, SIGNAL(traceSaved()),
            this, SLOT(slotSaved()));
    connect(m_saver, SIGNAL(traceSaved()),
            this, SIGNAL(saved()));

    m_loaderThread = new QThread();
    m_loader->moveToThread(m_loaderThread);
    m_loaderThread->start();
}

ApiTrace::~ApiTrace()
{
    m_loaderThread->quit();
    m_loaderThread->deleteLater();
    qDeleteAll(m_frames);
    delete m_loader;
    delete m_saver;
}

bool ApiTrace::isEmpty() const
{
    return m_frames.isEmpty();
}

QString ApiTrace::fileName() const
{
    if (edited()) {
        return m_tempFileName;
    }

    return m_fileName;
}

const QList<ApiTraceFrame*> & ApiTrace::frames() const
{
    return m_frames;
}

ApiTraceFrame * ApiTrace::frameAt(int idx) const
{
    return m_frames.value(idx);
}

int ApiTrace::numFrames() const
{
    return m_frames.count();
}

int ApiTrace::numCallsInFrame(int idx) const
{
    const ApiTraceFrame *frame = frameAt(idx);
    if (frame) {
        return frame->numChildren();
    } else {
        return 0;
    }
}

void ApiTrace::setFileName(const QString &name)
{
    if (m_fileName != name) {
        m_fileName = name;
        m_tempFileName = QString();

        m_frames.clear();
        m_errors.clear();
        m_editedCalls.clear();
        m_queuedErrors.clear();
        m_needsSaving = false;
        emit invalidated();

        emit loadTrace(m_fileName);
    }
}

void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
{
    int currentFrames = m_frames.count();
    int numNewFrames = frames.count();

    emit beginAddingFrames(currentFrames, numNewFrames);

    m_frames += frames;

    foreach(ApiTraceFrame *frame, frames) {
        frame->setParentTrace(this);
    }

    emit endAddingFrames();
}

ApiTraceCall * ApiTrace::callWithIndex(int idx) const
{
    for (int i = 0; i < m_frames.count(); ++i) {
        ApiTraceCall *call = m_frames[i]->callWithIndex(idx);
        if (call) {
            return call;
        }
    }
    return NULL;
}

ApiTraceState ApiTrace::defaultState() const
{
    ApiTraceFrame *frame = frameAt(0);
    if (!frame || !frame->isLoaded() || frame->isEmpty()) {
        return ApiTraceState();
    }

    ApiTraceCall *firstCall = frame->calls().first();
    if (!firstCall->hasState()) {
        return ApiTraceState();
    }

    return *firstCall->state();
}

void ApiTrace::callEdited(ApiTraceCall *call)
{
    if (!m_editedCalls.contains(call)) {
        //lets generate a temp filename
        QString tempPath = QDir::tempPath();
        m_tempFileName = QString::fromLatin1("%1/%2.edited")
                         .arg(tempPath)
                         .arg(m_fileName);
    }
    m_editedCalls.insert(call);
    m_needsSaving = true;

    emit changed(call);
}

void ApiTrace::callReverted(ApiTraceCall *call)
{
    m_editedCalls.remove(call);

    if (m_editedCalls.isEmpty()) {
        m_needsSaving = false;
    }
    emit changed(call);
}

bool ApiTrace::edited() const
{
    return !m_editedCalls.isEmpty();
}

bool ApiTrace::needsSaving() const
{
    return m_needsSaving;
}

void ApiTrace::save()
{
    QFileInfo fi(m_tempFileName);
    QDir dir;
    emit startedSaving();
    dir.mkpath(fi.absolutePath());
    m_saver->saveFile(m_tempFileName,
                      m_fileName,
                      m_editedCalls);
}

void ApiTrace::slotSaved()
{
    m_needsSaving = false;
}

bool ApiTrace::isSaving() const
{
    return m_saver->isRunning();
}

bool ApiTrace::hasErrors() const
{
    return !m_errors.isEmpty() || !m_queuedErrors.isEmpty();
}

void ApiTrace::loadFrame(ApiTraceFrame *frame)
{
    if (!isFrameLoading(frame)) {
        Q_ASSERT(!frame->isLoaded());
        m_loadingFrames.insert(frame);
        emit requestFrame(frame);
    }
}

void ApiTrace::guessedApi(int api)
{
    m_api = static_cast<trace::API>(api);
}

trace::API ApiTrace::api() const
{
    return m_api;
}

void ApiTrace::finishedParsing()
{
    if (!m_frames.isEmpty()) {
        ApiTraceFrame *firstFrame = m_frames[0];
        if (firstFrame && !firstFrame->isLoaded()) {
            loadFrame(firstFrame);
        }
    }
}

void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
                                 const QVector<ApiTraceCall*> &calls,
                                 quint64 binaryDataSize)
{
    Q_ASSERT(frame->numChildrenToLoad() == calls.size());

    if (!frame->isLoaded()) {
        emit beginLoadingFrame(frame, calls.size());
        frame->setCalls(calls, binaryDataSize);
        emit endLoadingFrame(frame);
        m_loadingFrames.remove(frame);
    }

    if (!m_queuedErrors.isEmpty()) {
        QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
        for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
             ++itr) {
            const ApiTraceError &error = (*itr).second;
            if ((*itr).first == frame) {
                ApiTraceCall *call = frame->callWithIndex(error.callIndex);

                if (!call) {
                    continue;
                }

                call->setError(error.message);
                m_queuedErrors.erase(itr);

                if (call->hasError()) {
                    m_errors.insert(call);
                } else {
                    m_errors.remove(call);
                }
                emit changed(call);
            }
        }
    }
}

void ApiTrace::findNext(ApiTraceFrame *frame,
                        ApiTraceCall *from,
                        const QString &str,
                        Qt::CaseSensitivity sensitivity)
{
    ApiTraceCall *foundCall = 0;
    int frameIdx = m_frames.indexOf(frame);
    SearchRequest request(SearchRequest::Next,
                          frame, from, str, sensitivity);

    if (frame->isLoaded()) {
        foundCall = frame->findNextCall(from, str, sensitivity);
        if (foundCall) {
            emit findResult(request, SearchResult_Found, foundCall);
            return;
        }

        //if the frame is loaded we already searched it above
        // so skip it
        frameIdx += 1;
    }

    //for the rest of the frames we search from beginning
    request.from = 0;
    for (int i = frameIdx; i < m_frames.count(); ++i) {
        ApiTraceFrame *frame = m_frames[i];
        request.frame = frame;
        if (!frame->isLoaded()) {
            emit loaderSearch(request);
            return;
        } else {
            ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
            if (call) {
                emit findResult(request, SearchResult_Found, call);
                return;
            }
        }
    }
    emit findResult(request, SearchResult_Wrapped, 0);
}

void ApiTrace::findPrev(ApiTraceFrame *frame,
                        ApiTraceCall *from,
                        const QString &str,
                        Qt::CaseSensitivity sensitivity)
{
    ApiTraceCall *foundCall = 0;
    int frameIdx = m_frames.indexOf(frame);
    SearchRequest request(SearchRequest::Prev,
                          frame, from, str, sensitivity);

    if (frame->isLoaded()) {
        foundCall = frame->findPrevCall(from, str, sensitivity);
        if (foundCall) {
            emit findResult(request, SearchResult_Found, foundCall);
            return;
        }

        //if the frame is loaded we already searched it above
        // so skip it
        frameIdx -= 1;
    }

    request.from = 0;
    for (int i = frameIdx; i >= 0; --i) {
        ApiTraceFrame *frame = m_frames[i];
        request.frame = frame;
        if (!frame->isLoaded()) {
            emit loaderSearch(request);
            return;
        } else {
            ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
            if (call) {
                emit findResult(request, SearchResult_Found, call);
                return;
            }
        }
    }
    emit findResult(request, SearchResult_Wrapped, 0);
}

void ApiTrace::loaderSearchResult(const ApiTrace::SearchRequest &request,
                                  ApiTrace::SearchResult result,
                                  ApiTraceCall *call)
{
    //qDebug()<<"Search result = "<<result
    //       <<", call is = "<<call;
    emit findResult(request, result, call);
}

void ApiTrace::findFrameStart(ApiTraceFrame *frame)
{
    if (!frame)
        return;

    if (frame->isLoaded()) {
        emit foundFrameStart(frame);
    } else {
        emit loaderFindFrameStart(frame);
    }
}

void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
{
    if (!frame)
        return;

    if (frame->isLoaded()) {
        emit foundFrameEnd(frame);
    } else {
        emit loaderFindFrameEnd(frame);
    }
}

void ApiTrace::findCallIndex(int index)
{
    int frameIdx = callInFrame(index);
    ApiTraceFrame *frame = 0;

    if (frameIdx < 0) {
        emit foundCallIndex(0);
        return;
    }

    frame = m_frames[frameIdx];

    if (frame) {
        if (frame->isLoaded()) {
            ApiTraceCall *call = frame->callWithIndex(index);
            emit foundCallIndex(call);
        } else {
            emit loaderFindCallIndex(index);
        }
    }
}

int ApiTrace::callInFrame(int callIdx) const
{
    unsigned numCalls = 0;

    for (int frameIdx = 0; frameIdx < m_frames.size(); ++frameIdx) {
        const ApiTraceFrame *frame = m_frames[frameIdx];
        unsigned numCallsInFrame =  frame->isLoaded()
                ? frame->numChildren()
                : frame->numChildrenToLoad();
        unsigned firstCall = numCalls;
        unsigned endCall = numCalls + numCallsInFrame;
        if (firstCall <= callIdx && endCall > callIdx) {
            return frameIdx;
        }
        numCalls = endCall;
    }

    return -1;
}

void ApiTrace::setCallError(const ApiTraceError &error)
{
    int frameIdx = callInFrame(error.callIndex);
    ApiTraceFrame *frame = 0;

    if (frameIdx < 0) {
        return;
    }
    frame = m_frames[frameIdx];

    if (frame->isLoaded()) {
        ApiTraceCall *call = frame->callWithIndex(error.callIndex);
        call->setError(error.message);
        if (call->hasError()) {
            m_errors.insert(call);
        } else {
            m_errors.remove(call);
        }
        emit changed(call);
    } else {
        loadFrame(frame);
        m_queuedErrors.append(qMakePair(frame, error));
    }
}

bool ApiTrace::isFrameLoading(ApiTraceFrame *frame) const
{
    return m_loadingFrames.contains(frame);
}

void ApiTrace::bindThumbnailsToFrames(const QList<QImage> &thumbnails)
{
    QList<ApiTraceFrame *> frames = m_frames;

    QList<QImage>::const_iterator thumbnail = thumbnails.begin();

    foreach (ApiTraceFrame *frame, frames) {
        if (thumbnail != thumbnails.end()) {
            frame->setThumbnail(*thumbnail);

            ++thumbnail;

            emit changed(frame);
        }
    }
}

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


<commit_before>/*  -*- Mode: C++ -*-
 *
 * contextkit-meego
 * Copyright © 2010, Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * Apache License, version 2.0.  The full text of the Apache License is at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 */

#include "connmanprovider.h"
#include <QDebug>
#include <QStringList>
#include <QVariant>

IProviderPlugin* pluginFactory(const QString& constructionString)
{
  Q_UNUSED(constructionString)
    return new ConnmanProvider();
}

const QString ConnmanProvider::networkType("Internet.NetworkType");
const QString ConnmanProvider::networkState("Internet.NetworkState");
const QString ConnmanProvider::trafficIn("Internet.TrafficIn");
const QString ConnmanProvider::trafficOut("Internet.TrafficOut");

ConnmanProvider::ConnmanProvider()
{
  qDebug() << "ConnmanProvider::ConnmanProvider()";

  m_nameMapper["wifi"] = "WLAN";
  m_nameMapper["gprs"] = "GPRS";
  m_nameMapper["edge"] = "GPRS";
  m_nameMapper["umts"] = "GPRS";

  m_nameMapper["offline"] = "disconnected";
  m_nameMapper["online"] = "connected";
  m_nameMapper["connected"] = "connected";

  //hack
  m_properties[trafficIn] = 20;
  m_properties[trafficOut] = 20;
  m_timerId = startTimer(5*1000);

  QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);

  m_networkListModel = new NetworkListModel();
  m_properties[networkType] = map(m_networkListModel->defaultTechnology());
  m_properties[networkState] = map(m_networkListModel->state());
  connect(m_networkListModel, SIGNAL(defaultTechnologyChanged(QString)),
	  this, SLOT(defaultTechnologyChanged(QString)));
  connect(m_networkListModel, SIGNAL(stateChanged(QString)),
	  this, SLOT(stateChanged(QString)));

  //sadly, QVariant is not a registered metatype
  qRegisterMetaType<QVariant>("QVariant");

  QMetaObject::invokeMethod(this, "valueChanged", Qt::QueuedConnection,
			    Q_ARG(QString, networkType),
			    Q_ARG(QVariant, m_properties[networkType]));
  QMetaObject::invokeMethod(this, "valueChanged", Qt::QueuedConnection,
			    Q_ARG(QString, networkState),
			    Q_ARG(QVariant, m_properties[networkState]));
}

ConnmanProvider::~ConnmanProvider()
{
  qDebug() << "ConnmanProvider::~ConnmanProvider()";
}

void ConnmanProvider::subscribe(QSet<QString> keys)
{
  qDebug() << "ConnmanProvider::subscribe(" << QStringList(keys.toList()).join(", ") << ")";

  m_subscribedProperties.unite(keys);

  QMetaObject::invokeMethod(this, "emitSubscribeFinished", Qt::QueuedConnection);
  QMetaObject::invokeMethod(this, "emitChanged", Qt::QueuedConnection);
}

void ConnmanProvider::unsubscribe(QSet<QString> keys)
{
  qDebug() << "ConnmanProvider::unsubscribe(" << QStringList(keys.toList()).join(", ") << ")";

  m_subscribedProperties.subtract(keys);
}

void ConnmanProvider::timerEvent(QTimerEvent *event)
{
  Q_UNUSED(event);
  m_properties[trafficIn] = qrand()*10.0 / RAND_MAX + 20;
  m_properties[trafficOut] = qrand()*10.0 / RAND_MAX + 20;

  printf("trafficIn: %d\n", m_properties[trafficIn].toInt());
  printf("traffOut: %d\n", m_properties[trafficOut].toInt());

  emit valueChanged(trafficIn, m_properties[trafficIn]);
  emit valueChanged(trafficOut, m_properties[trafficOut]);
}

QString ConnmanProvider::map(const QString &input) const
{
  return m_nameMapper[input];
}

void ConnmanProvider::emitSubscribeFinished()
{
  foreach (QString key, m_subscribedProperties) {
    emit subscribeFinished(key);
  }
}

void ConnmanProvider::emitChanged()
{
  foreach (QString key, m_subscribedProperties) {
    emit valueChanged(key, QVariant(m_properties[key]));
  }
}

void ConnmanProvider::defaultTechnologyChanged(QString Technology)
{
  qDebug() << "defaultTechnologyChanged: " << Technology;
  m_properties[networkType] = map(Technology);
  if (m_subscribedProperties.contains(networkType)) {
    emit valueChanged(networkType, QVariant(m_properties[networkType]));
  }
}

void ConnmanProvider::stateChanged(QString State)
{
  m_properties[networkState] = map(State);
  if (m_subscribedProperties.contains(networkState)) {
    emit valueChanged(networkState, QVariant(m_properties[networkState]));
  }
}
<commit_msg>remove printf that spammed stdout<commit_after>/*  -*- Mode: C++ -*-
 *
 * contextkit-meego
 * Copyright © 2010, Intel Corporation.
 *
 * This program is licensed under the terms and conditions of the
 * Apache License, version 2.0.  The full text of the Apache License is at
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 */

#include "connmanprovider.h"
#include <QDebug>
#include <QStringList>
#include <QVariant>

IProviderPlugin* pluginFactory(const QString& constructionString)
{
  Q_UNUSED(constructionString)
    return new ConnmanProvider();
}

const QString ConnmanProvider::networkType("Internet.NetworkType");
const QString ConnmanProvider::networkState("Internet.NetworkState");
const QString ConnmanProvider::trafficIn("Internet.TrafficIn");
const QString ConnmanProvider::trafficOut("Internet.TrafficOut");

ConnmanProvider::ConnmanProvider()
{
  qDebug() << "ConnmanProvider::ConnmanProvider()";

  m_nameMapper["wifi"] = "WLAN";
  m_nameMapper["gprs"] = "GPRS";
  m_nameMapper["edge"] = "GPRS";
  m_nameMapper["umts"] = "GPRS";

  m_nameMapper["offline"] = "disconnected";
  m_nameMapper["online"] = "connected";
  m_nameMapper["connected"] = "connected";

  //hack
  m_properties[trafficIn] = 20;
  m_properties[trafficOut] = 20;
  m_timerId = startTimer(5*1000);

  QMetaObject::invokeMethod(this, "ready", Qt::QueuedConnection);

  m_networkListModel = new NetworkListModel();
  m_properties[networkType] = map(m_networkListModel->defaultTechnology());
  m_properties[networkState] = map(m_networkListModel->state());
  connect(m_networkListModel, SIGNAL(defaultTechnologyChanged(QString)),
	  this, SLOT(defaultTechnologyChanged(QString)));
  connect(m_networkListModel, SIGNAL(stateChanged(QString)),
	  this, SLOT(stateChanged(QString)));

  //sadly, QVariant is not a registered metatype
  qRegisterMetaType<QVariant>("QVariant");

  QMetaObject::invokeMethod(this, "valueChanged", Qt::QueuedConnection,
			    Q_ARG(QString, networkType),
			    Q_ARG(QVariant, m_properties[networkType]));
  QMetaObject::invokeMethod(this, "valueChanged", Qt::QueuedConnection,
			    Q_ARG(QString, networkState),
			    Q_ARG(QVariant, m_properties[networkState]));
}

ConnmanProvider::~ConnmanProvider()
{
  qDebug() << "ConnmanProvider::~ConnmanProvider()";
}

void ConnmanProvider::subscribe(QSet<QString> keys)
{
  qDebug() << "ConnmanProvider::subscribe(" << QStringList(keys.toList()).join(", ") << ")";

  m_subscribedProperties.unite(keys);

  QMetaObject::invokeMethod(this, "emitSubscribeFinished", Qt::QueuedConnection);
  QMetaObject::invokeMethod(this, "emitChanged", Qt::QueuedConnection);
}

void ConnmanProvider::unsubscribe(QSet<QString> keys)
{
  qDebug() << "ConnmanProvider::unsubscribe(" << QStringList(keys.toList()).join(", ") << ")";

  m_subscribedProperties.subtract(keys);
}

void ConnmanProvider::timerEvent(QTimerEvent *event)
{
  Q_UNUSED(event);
  m_properties[trafficIn] = qrand()*10.0 / RAND_MAX + 20;
  m_properties[trafficOut] = qrand()*10.0 / RAND_MAX + 20;

  emit valueChanged(trafficIn, m_properties[trafficIn]);
  emit valueChanged(trafficOut, m_properties[trafficOut]);
}

QString ConnmanProvider::map(const QString &input) const
{
  return m_nameMapper[input];
}

void ConnmanProvider::emitSubscribeFinished()
{
  foreach (QString key, m_subscribedProperties) {
    emit subscribeFinished(key);
  }
}

void ConnmanProvider::emitChanged()
{
  foreach (QString key, m_subscribedProperties) {
    emit valueChanged(key, QVariant(m_properties[key]));
  }
}

void ConnmanProvider::defaultTechnologyChanged(QString Technology)
{
  qDebug() << "defaultTechnologyChanged: " << Technology;
  m_properties[networkType] = map(Technology);
  if (m_subscribedProperties.contains(networkType)) {
    emit valueChanged(networkType, QVariant(m_properties[networkType]));
  }
}

void ConnmanProvider::stateChanged(QString State)
{
  m_properties[networkState] = map(State);
  if (m_subscribedProperties.contains(networkState)) {
    emit valueChanged(networkState, QVariant(m_properties[networkState]));
  }
}
<|endoftext|>


<commit_before>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// common goes first
#include "common.h"

// implementation header
#include "LinkManager.h"

// system headers
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <vector>
#include <iostream>

// common headers
#include "Pack.h"
#include "Teleporter.h"
#include "ObstacleMgr.h"

// simple globbing routines
static int match_object_name (const char *string, const char *objname);
static int match_multi (const char **string, const char **objname);
static const char MATCH_MULTI  = '*'; // matches any number of characters
static const char MATCH_SINGLE = '?'; // matches a single character

LinkManager::LinkManager()
{
  // do nothing
  return;
}

LinkManager::~LinkManager()
{
  clear();
  return;
}

void LinkManager::clear()
{
  linkNames.clear();
  linkNumbers.clear();
  return;
}

void LinkManager::makeLinkName(int number, std::string& name)
{
  name = "/t";
  char buffer[8];
  sprintf(buffer, "%i", (number / 2));
  name += buffer;
  name += ":";
  if ((number % 2) == 0) {
    name += "f";
  } else {
    name += "b";
  }
  return;
}

void LinkManager::addLink(int src, int dst)
{
  LinkNameSet link;
  makeLinkName(src, link.src);
  makeLinkName(dst, link.dst);
  linkNames.push_back(link);
  return;
}

void LinkManager::addLink(const std::string& src, const std::string& dst)
{
  LinkNameSet link;
  if ((src[0] >= '0') && (src[0] <= '9')) {
    int number = atoi(src.c_str());
    makeLinkName(number, link.src);
  } else {
    link.src = src;
  }
  if ((dst[0] >= '0') && (dst[0] <= '9')) {
    int number = atoi(dst.c_str());
    makeLinkName(number, link.dst);
  } else {
    link.dst = dst;
  }
  linkNames.push_back(link);
  return;
}

static bool inIntList(int value, const std::vector<int>& list)
{
  for (unsigned int i = 0; i < list.size(); i++) {
    if (value == list[i]) {
      return true;
    }
  }
  return false;
}

void LinkManager::doLinking()
{
  unsigned int i;

  // initial the destinations
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  // findTelesByName() can not return higher then this
  linkNumbers.resize(teles.size() * 2);
  for (i = 0; i < linkNumbers.size(); i++) {
    linkNumbers[i].dsts.clear();
  }

  for (i = 0; i < linkNames.size(); i++) {
    LinkNameSet& link = linkNames[i];
    std::vector<int> srcNumbers;
    std::vector<int> dstNumbers;
    findTelesByName(link.src, srcNumbers);
    findTelesByName(link.dst, dstNumbers);

    bool broken = false;
    if (srcNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link src: %s\n", link.src.c_str());
    }
    if (dstNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link dst: %s\n", link.dst.c_str());
    }
    if (broken) {
      continue;
    }

    for (unsigned s = 0; s < srcNumbers.size(); s++) {
      for (unsigned d = 0; d < dstNumbers.size(); d++) {
        std::vector<int>& dstsList = linkNumbers[srcNumbers[s]].dsts;
        if (!inIntList(dstNumbers[d], dstsList)) { // no duplicates
          dstsList.push_back(dstNumbers[d]);
        }
      }
    }
  }

  // fill in the blanks (passthru linkage)
  for (i = 0; i < linkNumbers.size(); i++) {
    std::vector<int>& dstsList = linkNumbers[i].dsts;
    if (dstsList.size() <= 0) {
      const int t = (i / 2) * 2; // tele number
      const int f = 1 - (i % 2); // opposite link
      dstsList.push_back(t + f);
    }
  }

  if (debugLevel >= 4) {
    for (i = 0; i < teles.size(); i++) {
      Teleporter* tele = (Teleporter*) teles[i];
      printf ("TELE(%i): %s\n", i, tele->getName().c_str());
    }
    for (i = 0; i < linkNames.size(); i++) {
      LinkNameSet& link = linkNames[i];
      printf ("LINKSRC: %-32sLINKDST: %s\n", link.src.c_str(), link.dst.c_str());
    }
    for (i = 0; i < linkNumbers.size(); i++) {
      printf ("SRC %3i%c:  DSTS", (i / 2), ((i % 2) == 0) ? 'f' : 'b');
      for (unsigned int j = 0; j < linkNumbers[i].dsts.size(); j++) {
        int dst = linkNumbers[i].dsts[j];
        printf (" %i%c", (dst / 2), ((dst % 2) == 0) ? 'f' : 'b');
      }
      printf ("\n");
    }
  }

  return;
}

void LinkManager::findTelesByName(const std::string& name,
                                  std::vector<int>& list) const
{
  list.clear();

  std::string glob = name;

  // no chars, no service
  if (glob.size() <= 0) {
    return;
  }

  // a leading ':' might be used to indicate absolute linking if
  // links are ever included in group definintions. strip it here
  // for forwards compatibiliy.
  if (glob[0] == ':') {
    glob.erase(0, 1); // erase 1 char from position 0
  }

  // setup for the faces types
  bool front = false;
  bool back = false;
  int lastchar = tolower(glob[glob.size() - 1]);
  if (lastchar == '*') {
    front = true;
    back = true;
  } else if (glob.size() > 1) {
    int secondlast = tolower(glob[glob.size() - 2]);
    if ((secondlast == ':') || (secondlast == '?') || (secondlast == '*')) {
      if (lastchar == '?') {
        front = true;
        back = true;
      }else if (lastchar == 'f') {
        front = true;
      } else if (lastchar == 'b') {
        back = true;
      }
      // clip the end
      if (secondlast == '*') {
        glob.resize(glob.size() - 1);
      } else {
        glob.resize(glob.size() - 2);
      }
    }
  }

  if (!front && !back) {
    return; // no possible matches
  }

  // add all teleporters that have matching names
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  for (unsigned int i = 0; i < teles.size(); i++) {
    Teleporter* tele = (Teleporter*) teles[i];
    if (match_object_name(glob.c_str(), tele->getName().c_str())) {
      if (front) {
        list.push_back((int)(i * 2) + 0);
      }
      if (back) {
        list.push_back((int)(i * 2) + 1);
      }
    }
  }

  return;
}

int LinkManager::getTeleportTarget(int source) const
{
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  assert(source < (int)(2 * teles.size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    int target = rand() % int(dstsList.size());
    return dstsList[target];
  }
  else {
    assert(false);
    return 0;
  }
}

int LinkManager::getTeleportTarget(int source, unsigned int seed) const
{
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  assert(source < (int)(2 * teles.size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    seed = (seed * 1103515245 + 12345) >> 8; // from POSIX rand() example
    seed = seed % (dstsList.size());
    return dstsList[seed];
  }
  else {
    assert(false);
    return 0;
  }
}

void* LinkManager::pack(void* buf) const
{
  buf = nboPackUInt(buf, (uint32_t) linkNames.size());
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    buf = nboPackStdString(buf, linkNames[i].src);
    buf = nboPackStdString(buf, linkNames[i].dst);
  }
  return buf;
}

void* LinkManager::unpack(void* buf)
{
  clear(); // just in case
  unsigned int i, count;
  buf = nboUnpackUInt(buf, count);
  for (i = 0; i < count; i++) {
    LinkNameSet link;
    buf = nboUnpackStdString(buf, link.src);
    buf = nboUnpackStdString(buf, link.dst);
    linkNames.push_back(link);
  }
  return buf;
}

void LinkManager::print(std::ostream& out, const std::string& indent) const
{
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    const LinkNameSet& link = linkNames[i];
    out << indent << "link" << std::endl;
    out << indent << "  from " << link.src << std::endl;
    out << indent << "  to   " << link.dst << std::endl;
    out << indent << "end" << std::endl << std::endl;
  }
  return;
}

int LinkManager::packSize() const
{
  int fullSize = sizeof(uint32_t);
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    fullSize += nboStdStringPackSize(linkNames[i].src);
    fullSize += nboStdStringPackSize(linkNames[i].dst);
  }
  return fullSize;
}

/******************************************************************************/

static int match_object_name (const char *string, const char *objname)
{
  if (string == NULL) {
    return 0;
  }
  if (objname == NULL) {
    return 0;
  }

  if ((string[0] == MATCH_MULTI) && (string[1] == '\0')) {
    return 1;
  }

  while (*string != '\0') {
    if (*string == MATCH_MULTI) {
      string++;
      switch (match_multi (&string, &objname)) {
        case +1: {
          return 1;
        }
        case -1: {
          return 0;
        }
      }
    }
    else if (*objname == '\0') {
      return 0;
    }
    else if ((*string == MATCH_SINGLE) || (*string == *objname)) {
      string++;
      objname++;
    }
    else {
      return 0;
    }
  }

  if (*objname == '\0') {
    return 1;
  } else {
    return 0;
  }
}

/******************************************************************************/

static int match_multi (const char **string, const char **objname)
{
  const char *str = *string;
  const char *obj = *objname;

  while ((*str != '\0') && (*str == MATCH_MULTI)) {
    str++; // get rid of multiple '*'s
  }

  if (*str == '\0') { // '*' was last, auto-match
    return +1;
  }

  const char *strtop = str;
  const char *objtop = obj;

  while (*str != '\0') {
    if (*str == MATCH_MULTI) {
      *string = str;
      *objname = obj;
      return 0; // matched this segment
    }
    else if (*obj == '\0') {
      return -1; // can't match
    }
    else {
      if ((*str == MATCH_SINGLE) || (*str == *obj)) {
        str++;
        obj++;
        if ((*str == '\0') && (*obj != '\0')) { // advanced check
          obj++;
          objtop++;
          obj = objtop;
          str = strtop;
        }
      }
      else {
        obj++;
        objtop++;
        obj = objtop;
        str = strtop;
      }
    }
  }

  *string = str;
  *objname = obj;

  return +1; // full match
}

/******************************************************************************/

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<commit_msg>unused var warning<commit_after>/* bzflag
 * Copyright (c) 1993 - 2004 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

// common goes first
#include "common.h"

// implementation header
#include "LinkManager.h"

// system headers
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string>
#include <vector>
#include <iostream>

// common headers
#include "Pack.h"
#include "Teleporter.h"
#include "ObstacleMgr.h"

// simple globbing routines
static int match_object_name (const char *string, const char *objname);
static int match_multi (const char **string, const char **objname);
static const char MATCH_MULTI  = '*'; // matches any number of characters
static const char MATCH_SINGLE = '?'; // matches a single character

LinkManager::LinkManager()
{
  // do nothing
  return;
}

LinkManager::~LinkManager()
{
  clear();
  return;
}

void LinkManager::clear()
{
  linkNames.clear();
  linkNumbers.clear();
  return;
}

void LinkManager::makeLinkName(int number, std::string& name)
{
  name = "/t";
  char buffer[8];
  sprintf(buffer, "%i", (number / 2));
  name += buffer;
  name += ":";
  if ((number % 2) == 0) {
    name += "f";
  } else {
    name += "b";
  }
  return;
}

void LinkManager::addLink(int src, int dst)
{
  LinkNameSet link;
  makeLinkName(src, link.src);
  makeLinkName(dst, link.dst);
  linkNames.push_back(link);
  return;
}

void LinkManager::addLink(const std::string& src, const std::string& dst)
{
  LinkNameSet link;
  if ((src[0] >= '0') && (src[0] <= '9')) {
    int number = atoi(src.c_str());
    makeLinkName(number, link.src);
  } else {
    link.src = src;
  }
  if ((dst[0] >= '0') && (dst[0] <= '9')) {
    int number = atoi(dst.c_str());
    makeLinkName(number, link.dst);
  } else {
    link.dst = dst;
  }
  linkNames.push_back(link);
  return;
}

static bool inIntList(int value, const std::vector<int>& list)
{
  for (unsigned int i = 0; i < list.size(); i++) {
    if (value == list[i]) {
      return true;
    }
  }
  return false;
}

void LinkManager::doLinking()
{
  unsigned int i;

  // initial the destinations
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  // findTelesByName() can not return higher then this
  linkNumbers.resize(teles.size() * 2);
  for (i = 0; i < linkNumbers.size(); i++) {
    linkNumbers[i].dsts.clear();
  }

  for (i = 0; i < linkNames.size(); i++) {
    LinkNameSet& link = linkNames[i];
    std::vector<int> srcNumbers;
    std::vector<int> dstNumbers;
    findTelesByName(link.src, srcNumbers);
    findTelesByName(link.dst, dstNumbers);

    bool broken = false;
    if (srcNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link src: %s\n", link.src.c_str());
    }
    if (dstNumbers.size() <= 0) {
      broken = true;
      DEBUG1("broken link dst: %s\n", link.dst.c_str());
    }
    if (broken) {
      continue;
    }

    for (unsigned s = 0; s < srcNumbers.size(); s++) {
      for (unsigned d = 0; d < dstNumbers.size(); d++) {
        std::vector<int>& dstsList = linkNumbers[srcNumbers[s]].dsts;
        if (!inIntList(dstNumbers[d], dstsList)) { // no duplicates
          dstsList.push_back(dstNumbers[d]);
        }
      }
    }
  }

  // fill in the blanks (passthru linkage)
  for (i = 0; i < linkNumbers.size(); i++) {
    std::vector<int>& dstsList = linkNumbers[i].dsts;
    if (dstsList.size() <= 0) {
      const int t = (i / 2) * 2; // tele number
      const int f = 1 - (i % 2); // opposite link
      dstsList.push_back(t + f);
    }
  }

  if (debugLevel >= 4) {
    for (i = 0; i < teles.size(); i++) {
      Teleporter* tele = (Teleporter*) teles[i];
      printf ("TELE(%i): %s\n", i, tele->getName().c_str());
    }
    for (i = 0; i < linkNames.size(); i++) {
      LinkNameSet& link = linkNames[i];
      printf ("LINKSRC: %-32sLINKDST: %s\n", link.src.c_str(), link.dst.c_str());
    }
    for (i = 0; i < linkNumbers.size(); i++) {
      printf ("SRC %3i%c:  DSTS", (i / 2), ((i % 2) == 0) ? 'f' : 'b');
      for (unsigned int j = 0; j < linkNumbers[i].dsts.size(); j++) {
        int dst = linkNumbers[i].dsts[j];
        printf (" %i%c", (dst / 2), ((dst % 2) == 0) ? 'f' : 'b');
      }
      printf ("\n");
    }
  }

  return;
}

void LinkManager::findTelesByName(const std::string& name,
                                  std::vector<int>& list) const
{
  list.clear();

  std::string glob = name;

  // no chars, no service
  if (glob.size() <= 0) {
    return;
  }

  // a leading ':' might be used to indicate absolute linking if
  // links are ever included in group definintions. strip it here
  // for forwards compatibiliy.
  if (glob[0] == ':') {
    glob.erase(0, 1); // erase 1 char from position 0
  }

  // setup for the faces types
  bool front = false;
  bool back = false;
  int lastchar = tolower(glob[glob.size() - 1]);
  if (lastchar == '*') {
    front = true;
    back = true;
  } else if (glob.size() > 1) {
    int secondlast = tolower(glob[glob.size() - 2]);
    if ((secondlast == ':') || (secondlast == '?') || (secondlast == '*')) {
      if (lastchar == '?') {
        front = true;
        back = true;
      }else if (lastchar == 'f') {
        front = true;
      } else if (lastchar == 'b') {
        back = true;
      }
      // clip the end
      if (secondlast == '*') {
        glob.resize(glob.size() - 1);
      } else {
        glob.resize(glob.size() - 2);
      }
    }
  }

  if (!front && !back) {
    return; // no possible matches
  }

  // add all teleporters that have matching names
  const ObstacleList& teles = OBSTACLEMGR.getTeles();
  for (unsigned int i = 0; i < teles.size(); i++) {
    Teleporter* tele = (Teleporter*) teles[i];
    if (match_object_name(glob.c_str(), tele->getName().c_str())) {
      if (front) {
        list.push_back((int)(i * 2) + 0);
      }
      if (back) {
        list.push_back((int)(i * 2) + 1);
      }
    }
  }

  return;
}

int LinkManager::getTeleportTarget(int source) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    int target = rand() % int(dstsList.size());
    return dstsList[target];
  }
  else {
    assert(false);
    return 0;
  }
}

int LinkManager::getTeleportTarget(int source, unsigned int seed) const
{
  assert(source < (int)(2 * OBSTACLEMGR.getTeles().size()));

  const std::vector<int>& dstsList = linkNumbers[source].dsts;

  if (dstsList.size() == 1) {
    return dstsList[0];
  }
  else if (dstsList.size() > 1) {
    seed = (seed * 1103515245 + 12345) >> 8; // from POSIX rand() example
    seed = seed % (dstsList.size());
    return dstsList[seed];
  }
  else {
    assert(false);
    return 0;
  }
}

void* LinkManager::pack(void* buf) const
{
  buf = nboPackUInt(buf, (uint32_t) linkNames.size());
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    buf = nboPackStdString(buf, linkNames[i].src);
    buf = nboPackStdString(buf, linkNames[i].dst);
  }
  return buf;
}

void* LinkManager::unpack(void* buf)
{
  clear(); // just in case
  unsigned int i, count;
  buf = nboUnpackUInt(buf, count);
  for (i = 0; i < count; i++) {
    LinkNameSet link;
    buf = nboUnpackStdString(buf, link.src);
    buf = nboUnpackStdString(buf, link.dst);
    linkNames.push_back(link);
  }
  return buf;
}

void LinkManager::print(std::ostream& out, const std::string& indent) const
{
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    const LinkNameSet& link = linkNames[i];
    out << indent << "link" << std::endl;
    out << indent << "  from " << link.src << std::endl;
    out << indent << "  to   " << link.dst << std::endl;
    out << indent << "end" << std::endl << std::endl;
  }
  return;
}

int LinkManager::packSize() const
{
  int fullSize = sizeof(uint32_t);
  for (unsigned int i = 0; i < linkNames.size(); i++) {
    fullSize += nboStdStringPackSize(linkNames[i].src);
    fullSize += nboStdStringPackSize(linkNames[i].dst);
  }
  return fullSize;
}

/******************************************************************************/

static int match_object_name (const char *string, const char *objname)
{
  if (string == NULL) {
    return 0;
  }
  if (objname == NULL) {
    return 0;
  }

  if ((string[0] == MATCH_MULTI) && (string[1] == '\0')) {
    return 1;
  }

  while (*string != '\0') {
    if (*string == MATCH_MULTI) {
      string++;
      switch (match_multi (&string, &objname)) {
        case +1: {
          return 1;
        }
        case -1: {
          return 0;
        }
      }
    }
    else if (*objname == '\0') {
      return 0;
    }
    else if ((*string == MATCH_SINGLE) || (*string == *objname)) {
      string++;
      objname++;
    }
    else {
      return 0;
    }
  }

  if (*objname == '\0') {
    return 1;
  } else {
    return 0;
  }
}

/******************************************************************************/

static int match_multi (const char **string, const char **objname)
{
  const char *str = *string;
  const char *obj = *objname;

  while ((*str != '\0') && (*str == MATCH_MULTI)) {
    str++; // get rid of multiple '*'s
  }

  if (*str == '\0') { // '*' was last, auto-match
    return +1;
  }

  const char *strtop = str;
  const char *objtop = obj;

  while (*str != '\0') {
    if (*str == MATCH_MULTI) {
      *string = str;
      *objname = obj;
      return 0; // matched this segment
    }
    else if (*obj == '\0') {
      return -1; // can't match
    }
    else {
      if ((*str == MATCH_SINGLE) || (*str == *obj)) {
        str++;
        obj++;
        if ((*str == '\0') && (*obj != '\0')) { // advanced check
          obj++;
          objtop++;
          obj = objtop;
          str = strtop;
        }
      }
      else {
        obj++;
        objtop++;
        obj = objtop;
        str = strtop;
      }
    }
  }

  *string = str;
  *objname = obj;

  return +1; // full match
}

/******************************************************************************/

// Local Variables: ***
// mode:C++ ***
// tab-width: 8 ***
// c-basic-offset: 2 ***
// indent-tabs-mode: t ***
// End: ***
// ex: shiftwidth=2 tabstop=8
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// @author Mark Rabkin (mrabkin@fb.com)
// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)

#include "folly/Range.h"

#if FOLLY_HAVE_EMMINTRIN_H
#include <emmintrin.h>  // __v16qi
#endif
#include <iostream>

namespace folly {

/**
 * Predicates that can be used with qfind and startsWith
 */
const AsciiCaseSensitive asciiCaseSensitive = AsciiCaseSensitive();
const AsciiCaseInsensitive asciiCaseInsensitive = AsciiCaseInsensitive();

std::ostream& operator<<(std::ostream& os, const StringPiece& piece) {
  os.write(piece.start(), piece.size());
  return os;
}

namespace detail {

size_t qfind_first_byte_of_memchr(const StringPiece& haystack,
                                  const StringPiece& needles) {
  size_t best = haystack.size();
  for (char needle: needles) {
    const void* ptr = memchr(haystack.data(), needle, best);
    if (ptr) {
      auto found = static_cast<const char*>(ptr) - haystack.data();
      best = std::min<size_t>(best, found);
    }
  }
  if (best == haystack.size()) {
    return StringPiece::npos;
  }
  return best;
}

}  // namespace detail

namespace {

// It's okay if pages are bigger than this (as powers of two), but they should
// not be smaller.
constexpr size_t kMinPageSize = 4096;
static_assert(kMinPageSize >= 16,
              "kMinPageSize must be at least SSE register size");
#define PAGE_FOR(addr) \
  (reinterpret_cast<uintptr_t>(addr) / kMinPageSize)

// Earlier versions of GCC (for example, Clang on Mac OS X, which is based on
// GCC 4.2) do not have a full compliment of SSE builtins.
#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
inline size_t nextAlignedIndex(const char* arr) {
   auto firstPossible = reinterpret_cast<uintptr_t>(arr) + 1;
   return 1 +                       // add 1 because the index starts at 'arr'
     ((firstPossible + 15) & ~0xF)  // round up to next multiple of 16
     - firstPossible;
}

// build sse4.2-optimized version even if -msse4.2 is not passed to GCC
size_t qfind_first_byte_of_needles16(const StringPiece& haystack,
                                     const StringPiece& needles)
  __attribute__ ((__target__("sse4.2"), noinline))
  FOLLY_DISABLE_ADDRESS_SANITIZER;

// helper method for case where needles.size() <= 16
size_t qfind_first_byte_of_needles16(const StringPiece& haystack,
                                     const StringPiece& needles) {
  DCHECK(!haystack.empty());
  DCHECK(!needles.empty());
  DCHECK_LE(needles.size(), 16);
  // benchmarking shows that memchr beats out SSE for small needle-sets
  // with large haystacks.
  if ((needles.size() <= 2 && haystack.size() >= 256) ||
      // must bail if we can't even SSE-load a single segment of haystack
      (haystack.size() < 16 &&
       PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 15)) ||
      // can't load needles into SSE register if it could cross page boundary
      PAGE_FOR(needles.end() - 1) != PAGE_FOR(needles.data() + 15)) {
    return detail::qfind_first_byte_of_memchr(haystack, needles);
  }

  auto arr2 = __builtin_ia32_loaddqu(needles.data());
  // do an unaligned load for first block of haystack
  auto arr1 = __builtin_ia32_loaddqu(haystack.data());
  auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
                                           arr1, haystack.size(), 0);
  if (index < 16) {
    return index;
  }

  // Now, we can do aligned loads hereafter...
  size_t i = nextAlignedIndex(haystack.data());
  for (; i < haystack.size(); i+= 16) {
    void* ptr1 = __builtin_assume_aligned(haystack.data() + i, 16);
    auto arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
    auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
                                             arr1, haystack.size() - i, 0);
    if (index < 16) {
      return i + index;
    }
  }
  return StringPiece::npos;
}
#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+

// Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis
// of Computer Algorithms" (1974), but the best description is here:
// http://research.swtch.com/sparse
class FastByteSet {
 public:
  FastByteSet() : size_(0) { }  // no init of arrays required!

  inline void add(uint8_t i) {
    if (!contains(i)) {
      dense_[size_] = i;
      sparse_[i] = size_;
      size_++;
    }
  }
  inline bool contains(uint8_t i) const {
    DCHECK_LE(size_, 256);
    return sparse_[i] < size_ && dense_[sparse_[i]] == i;
  }

 private:
  uint16_t size_;  // can't use uint8_t because it would overflow if all
                   // possible values were inserted.
  uint8_t sparse_[256];
  uint8_t dense_[256];
};

}  // namespace

namespace detail {

size_t qfind_first_byte_of_byteset(const StringPiece& haystack,
                                   const StringPiece& needles) {
  FastByteSet s;
  for (auto needle: needles) {
    s.add(needle);
  }
  for (size_t index = 0; index < haystack.size(); ++index) {
    if (s.contains(haystack[index])) {
      return index;
    }
  }
  return StringPiece::npos;
}

#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)

template <bool HAYSTACK_ALIGNED>
inline size_t scanHaystackBlock(const StringPiece& haystack,
                                const StringPiece& needles,
                                int64_t idx)
// inline is okay because it's only called from other sse4.2 functions
  __attribute__ ((__target__("sse4.2")));

// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first
// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned.
// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the
// block.
template <bool HAYSTACK_ALIGNED>
inline size_t scanHaystackBlock(const StringPiece& haystack,
                                const StringPiece& needles,
                                int64_t blockStartIdx) {
  DCHECK_GT(needles.size(), 16);  // should handled by *needles16() method
  DCHECK(blockStartIdx + 16 <= haystack.size() ||
         (PAGE_FOR(haystack.data() + blockStartIdx) ==
          PAGE_FOR(haystack.data() + blockStartIdx + 15)));

  __v16qi arr1;
  if (HAYSTACK_ALIGNED) {
    void* ptr1 = __builtin_assume_aligned(haystack.data() + blockStartIdx, 16);
    arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
  } else {
    arr1 = __builtin_ia32_loaddqu(haystack.data() + blockStartIdx);
  }

  // This load is safe because needles.size() >= 16
  auto arr2 = __builtin_ia32_loaddqu(needles.data());
  size_t b = __builtin_ia32_pcmpestri128(
    arr2, 16, arr1, haystack.size() - blockStartIdx, 0);

  size_t j = nextAlignedIndex(needles.data());
  for (; j < needles.size(); j += 16) {
    void* ptr2 = __builtin_assume_aligned(needles.data() + j, 16);
    arr2 = *reinterpret_cast<const __v16qi*>(ptr2);

    auto index = __builtin_ia32_pcmpestri128(
      arr2, needles.size() - j, arr1, haystack.size() - blockStartIdx, 0);
    b = std::min<size_t>(index, b);
  }

  if (b < 16) {
    return blockStartIdx + b;
  }
  return StringPiece::npos;
}

size_t qfind_first_byte_of_sse42(const StringPiece& haystack,
                                 const StringPiece& needles)
  __attribute__ ((__target__("sse4.2"), noinline));

size_t qfind_first_byte_of_sse42(const StringPiece& haystack,
                                 const StringPiece& needles) {
  if (UNLIKELY(needles.empty() || haystack.empty())) {
    return StringPiece::npos;
  } else if (needles.size() <= 16) {
    // we can save some unnecessary load instructions by optimizing for
    // the common case of needles.size() <= 16
    return qfind_first_byte_of_needles16(haystack, needles);
  }

  if (haystack.size() < 16 &&
      PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 16)) {
    // We can't safely SSE-load haystack. Use a different approach.
    if (haystack.size() <= 2) {
      return qfind_first_of(haystack, needles, asciiCaseSensitive);
    }
    return qfind_first_byte_of_byteset(haystack, needles);
  }

  auto ret = scanHaystackBlock<false>(haystack, needles, 0);
  if (ret != StringPiece::npos) {
    return ret;
  }

  size_t i = nextAlignedIndex(haystack.data());
  for (; i < haystack.size(); i += 16) {
    auto ret = scanHaystackBlock<true>(haystack, needles, i);
    if (ret != StringPiece::npos) {
      return ret;
    }
  }

  return StringPiece::npos;
}
#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+

size_t qfind_first_byte_of_nosse(const StringPiece& haystack,
                                 const StringPiece& needles) {
  if (UNLIKELY(needles.empty() || haystack.empty())) {
    return StringPiece::npos;
  }
  // The thresholds below were empirically determined by benchmarking.
  // This is not an exact science since it depends on the CPU, the size of
  // needles, and the size of haystack.
  if (haystack.size() == 1 ||
      (haystack.size() < 4 && needles.size() <= 16)) {
    return qfind_first_of(haystack, needles, asciiCaseSensitive);
  } else if ((needles.size() >= 4 && haystack.size() <= 10) ||
             (needles.size() >= 16 && haystack.size() <= 64) ||
             needles.size() >= 32) {
    return qfind_first_byte_of_byteset(haystack, needles);
  }

  return qfind_first_byte_of_memchr(haystack, needles);
}

}  // namespace detail
}  // namespace folly
<commit_msg>folly: ASAN-exempt scanHaystackBlock, to avoid an FP buffer overrun<commit_after>/*
 * Copyright 2014 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// @author Mark Rabkin (mrabkin@fb.com)
// @author Andrei Alexandrescu (andrei.alexandrescu@fb.com)

#include "folly/Range.h"

#if FOLLY_HAVE_EMMINTRIN_H
#include <emmintrin.h>  // __v16qi
#endif
#include <iostream>

namespace folly {

/**
 * Predicates that can be used with qfind and startsWith
 */
const AsciiCaseSensitive asciiCaseSensitive = AsciiCaseSensitive();
const AsciiCaseInsensitive asciiCaseInsensitive = AsciiCaseInsensitive();

std::ostream& operator<<(std::ostream& os, const StringPiece& piece) {
  os.write(piece.start(), piece.size());
  return os;
}

namespace detail {

size_t qfind_first_byte_of_memchr(const StringPiece& haystack,
                                  const StringPiece& needles) {
  size_t best = haystack.size();
  for (char needle: needles) {
    const void* ptr = memchr(haystack.data(), needle, best);
    if (ptr) {
      auto found = static_cast<const char*>(ptr) - haystack.data();
      best = std::min<size_t>(best, found);
    }
  }
  if (best == haystack.size()) {
    return StringPiece::npos;
  }
  return best;
}

}  // namespace detail

namespace {

// It's okay if pages are bigger than this (as powers of two), but they should
// not be smaller.
constexpr size_t kMinPageSize = 4096;
static_assert(kMinPageSize >= 16,
              "kMinPageSize must be at least SSE register size");
#define PAGE_FOR(addr) \
  (reinterpret_cast<uintptr_t>(addr) / kMinPageSize)

// Earlier versions of GCC (for example, Clang on Mac OS X, which is based on
// GCC 4.2) do not have a full compliment of SSE builtins.
#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)
inline size_t nextAlignedIndex(const char* arr) {
   auto firstPossible = reinterpret_cast<uintptr_t>(arr) + 1;
   return 1 +                       // add 1 because the index starts at 'arr'
     ((firstPossible + 15) & ~0xF)  // round up to next multiple of 16
     - firstPossible;
}

// build sse4.2-optimized version even if -msse4.2 is not passed to GCC
size_t qfind_first_byte_of_needles16(const StringPiece& haystack,
                                     const StringPiece& needles)
  __attribute__ ((__target__("sse4.2"), noinline))
  FOLLY_DISABLE_ADDRESS_SANITIZER;

// helper method for case where needles.size() <= 16
size_t qfind_first_byte_of_needles16(const StringPiece& haystack,
                                     const StringPiece& needles) {
  DCHECK(!haystack.empty());
  DCHECK(!needles.empty());
  DCHECK_LE(needles.size(), 16);
  // benchmarking shows that memchr beats out SSE for small needle-sets
  // with large haystacks.
  if ((needles.size() <= 2 && haystack.size() >= 256) ||
      // must bail if we can't even SSE-load a single segment of haystack
      (haystack.size() < 16 &&
       PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 15)) ||
      // can't load needles into SSE register if it could cross page boundary
      PAGE_FOR(needles.end() - 1) != PAGE_FOR(needles.data() + 15)) {
    return detail::qfind_first_byte_of_memchr(haystack, needles);
  }

  auto arr2 = __builtin_ia32_loaddqu(needles.data());
  // do an unaligned load for first block of haystack
  auto arr1 = __builtin_ia32_loaddqu(haystack.data());
  auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
                                           arr1, haystack.size(), 0);
  if (index < 16) {
    return index;
  }

  // Now, we can do aligned loads hereafter...
  size_t i = nextAlignedIndex(haystack.data());
  for (; i < haystack.size(); i+= 16) {
    void* ptr1 = __builtin_assume_aligned(haystack.data() + i, 16);
    auto arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
    auto index = __builtin_ia32_pcmpestri128(arr2, needles.size(),
                                             arr1, haystack.size() - i, 0);
    if (index < 16) {
      return i + index;
    }
  }
  return StringPiece::npos;
}
#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+

// Aho, Hopcroft, and Ullman refer to this trick in "The Design and Analysis
// of Computer Algorithms" (1974), but the best description is here:
// http://research.swtch.com/sparse
class FastByteSet {
 public:
  FastByteSet() : size_(0) { }  // no init of arrays required!

  inline void add(uint8_t i) {
    if (!contains(i)) {
      dense_[size_] = i;
      sparse_[i] = size_;
      size_++;
    }
  }
  inline bool contains(uint8_t i) const {
    DCHECK_LE(size_, 256);
    return sparse_[i] < size_ && dense_[sparse_[i]] == i;
  }

 private:
  uint16_t size_;  // can't use uint8_t because it would overflow if all
                   // possible values were inserted.
  uint8_t sparse_[256];
  uint8_t dense_[256];
};

}  // namespace

namespace detail {

size_t qfind_first_byte_of_byteset(const StringPiece& haystack,
                                   const StringPiece& needles) {
  FastByteSet s;
  for (auto needle: needles) {
    s.add(needle);
  }
  for (size_t index = 0; index < haystack.size(); ++index) {
    if (s.contains(haystack[index])) {
      return index;
    }
  }
  return StringPiece::npos;
}

#if FOLLY_HAVE_EMMINTRIN_H && __GNUC_PREREQ(4, 6)

template <bool HAYSTACK_ALIGNED>
size_t scanHaystackBlock(const StringPiece& haystack,
                         const StringPiece& needles,
                         int64_t idx)
// inline is okay because it's only called from other sse4.2 functions
  __attribute__ ((__target__("sse4.2")))
// Turn off ASAN because the "arr2 = ..." assignment in the loop below reads
// up to 15 bytes beyond end of the buffer in #needles#.  That is ok because
// ptr2 is always 16-byte aligned, so the read can never span a page boundary.
// Also, the extra data that may be read is never actually used.
  FOLLY_DISABLE_ADDRESS_SANITIZER;

// Scans a 16-byte block of haystack (starting at blockStartIdx) to find first
// needle. If HAYSTACK_ALIGNED, then haystack must be 16byte aligned.
// If !HAYSTACK_ALIGNED, then caller must ensure that it is safe to load the
// block.
template <bool HAYSTACK_ALIGNED>
size_t scanHaystackBlock(const StringPiece& haystack,
                         const StringPiece& needles,
                         int64_t blockStartIdx) {
  DCHECK_GT(needles.size(), 16);  // should handled by *needles16() method
  DCHECK(blockStartIdx + 16 <= haystack.size() ||
         (PAGE_FOR(haystack.data() + blockStartIdx) ==
          PAGE_FOR(haystack.data() + blockStartIdx + 15)));

  __v16qi arr1;
  if (HAYSTACK_ALIGNED) {
    void* ptr1 = __builtin_assume_aligned(haystack.data() + blockStartIdx, 16);
    arr1 = *reinterpret_cast<const __v16qi*>(ptr1);
  } else {
    arr1 = __builtin_ia32_loaddqu(haystack.data() + blockStartIdx);
  }

  // This load is safe because needles.size() >= 16
  auto arr2 = __builtin_ia32_loaddqu(needles.data());
  size_t b = __builtin_ia32_pcmpestri128(
    arr2, 16, arr1, haystack.size() - blockStartIdx, 0);

  size_t j = nextAlignedIndex(needles.data());
  for (; j < needles.size(); j += 16) {
    void* ptr2 = __builtin_assume_aligned(needles.data() + j, 16);
    arr2 = *reinterpret_cast<const __v16qi*>(ptr2);

    auto index = __builtin_ia32_pcmpestri128(
      arr2, needles.size() - j, arr1, haystack.size() - blockStartIdx, 0);
    b = std::min<size_t>(index, b);
  }

  if (b < 16) {
    return blockStartIdx + b;
  }
  return StringPiece::npos;
}

size_t qfind_first_byte_of_sse42(const StringPiece& haystack,
                                 const StringPiece& needles)
  __attribute__ ((__target__("sse4.2"), noinline));

size_t qfind_first_byte_of_sse42(const StringPiece& haystack,
                                 const StringPiece& needles) {
  if (UNLIKELY(needles.empty() || haystack.empty())) {
    return StringPiece::npos;
  } else if (needles.size() <= 16) {
    // we can save some unnecessary load instructions by optimizing for
    // the common case of needles.size() <= 16
    return qfind_first_byte_of_needles16(haystack, needles);
  }

  if (haystack.size() < 16 &&
      PAGE_FOR(haystack.end() - 1) != PAGE_FOR(haystack.data() + 16)) {
    // We can't safely SSE-load haystack. Use a different approach.
    if (haystack.size() <= 2) {
      return qfind_first_of(haystack, needles, asciiCaseSensitive);
    }
    return qfind_first_byte_of_byteset(haystack, needles);
  }

  auto ret = scanHaystackBlock<false>(haystack, needles, 0);
  if (ret != StringPiece::npos) {
    return ret;
  }

  size_t i = nextAlignedIndex(haystack.data());
  for (; i < haystack.size(); i += 16) {
    auto ret = scanHaystackBlock<true>(haystack, needles, i);
    if (ret != StringPiece::npos) {
      return ret;
    }
  }

  return StringPiece::npos;
}
#endif // FOLLY_HAVE_EMMINTRIN_H && GCC 4.6+

size_t qfind_first_byte_of_nosse(const StringPiece& haystack,
                                 const StringPiece& needles) {
  if (UNLIKELY(needles.empty() || haystack.empty())) {
    return StringPiece::npos;
  }
  // The thresholds below were empirically determined by benchmarking.
  // This is not an exact science since it depends on the CPU, the size of
  // needles, and the size of haystack.
  if (haystack.size() == 1 ||
      (haystack.size() < 4 && needles.size() <= 16)) {
    return qfind_first_of(haystack, needles, asciiCaseSensitive);
  } else if ((needles.size() >= 4 && haystack.size() <= 10) ||
             (needles.size() >= 16 && haystack.size() <= 64) ||
             needles.size() >= 32) {
    return qfind_first_byte_of_byteset(haystack, needles);
  }

  return qfind_first_byte_of_memchr(haystack, needles);
}

}  // namespace detail
}  // namespace folly
<|endoftext|>


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

/*
 * Generic connection balancer.
 */

#ifndef GENERIC_BALANCER_HXX
#define GENERIC_BALANCER_HXX

#include "balancer.hxx"
#include "address_list.hxx"
#include "paddress.hxx"
#include "pool/pool.hxx"
#include "net/SocketAddress.hxx"
#include "net/FailureManager.hxx"
#include "util/Cancellable.hxx"

#include <utility>

class CancellablePointer;

template<class R>
class BalancerRequest final : R, Cancellable {
    struct pool &pool;

    Balancer &balancer;

    const AddressList &address_list;

    CancellablePointer cancel_ptr;

    /**
     * The "sticky id" of the incoming HTTP request.
     */
    const sticky_hash_t session_sticky;

    /**
     * The number of remaining connection attempts.  We give up when
     * we get an error and this attribute is already zero.
     */
    unsigned retries;

    FailurePtr failure;

public:
    template<typename... Args>
    BalancerRequest(struct pool &_pool,
                    Balancer &_balancer,
                    const AddressList &_address_list,
                    CancellablePointer &_cancel_ptr,
                    sticky_hash_t _session_sticky,
                    Args&&... args)
        :R(std::forward<Args>(args)...),
         pool(_pool), balancer(_balancer),
         address_list(_address_list),
         session_sticky(_session_sticky),
         retries(CalculateRetries(address_list))
    {
        _cancel_ptr = *this;
    }

    BalancerRequest(const BalancerRequest &) = delete;

    void Destroy() noexcept {
        this->~BalancerRequest();
    }

private:
    void Cancel() noexcept override {
        cancel_ptr.Cancel();
        Destroy();
    }

    static unsigned CalculateRetries(const AddressList &address_list) {
        const unsigned size = address_list.GetSize();
        if (size <= 1)
            return 0;
        else if (size == 2)
            return 1;
        else if (size == 3)
            return 2;
        else
            return 3;
    }

public:
    static constexpr BalancerRequest &Cast(R &r) {
        return (BalancerRequest &)r;
    }

    void Next(Expiry now) {
        const SocketAddress address =
            balancer.Get(now, address_list, session_sticky);

        /* we need to copy this address because it may come from
           the balancer's cache, and the according cache item may
           be flushed at any time */
        const auto current_address = DupAddress(pool, address);
        failure = balancer.GetFailureManager().Make(current_address);

        R::Send(pool, current_address, cancel_ptr);
    }

    void ConnectSuccess() {
        failure->UnsetConnect();
    }

    bool ConnectFailure(Expiry now) {
        failure->SetConnect(now, std::chrono::seconds(20));

        if (retries-- > 0){
            /* try again, next address */
            Next(now);
            return true;
        } else
            /* give up */
            return false;
    }

    template<typename... Args>
    static void Start(struct pool &pool, Expiry now,
                      Args&&... args) {
        auto r = NewFromPool<BalancerRequest>(pool, pool,
                                              std::forward<Args>(args)...);
        r->Next(now);
    }
};

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

/*
 * Generic connection balancer.
 */

#ifndef GENERIC_BALANCER_HXX
#define GENERIC_BALANCER_HXX

#include "balancer.hxx"
#include "address_list.hxx"
#include "paddress.hxx"
#include "pool/pool.hxx"
#include "net/SocketAddress.hxx"
#include "net/FailureManager.hxx"
#include "util/Cancellable.hxx"

#include <utility>

class CancellablePointer;

template<class R>
class BalancerRequest final : R, Cancellable {
    struct pool &pool;

    Balancer &balancer;

    const AddressList &address_list;

    CancellablePointer cancel_ptr;

    /**
     * The "sticky id" of the incoming HTTP request.
     */
    const sticky_hash_t session_sticky;

    /**
     * The number of remaining connection attempts.  We give up when
     * we get an error and this attribute is already zero.
     */
    unsigned retries;

    FailurePtr failure;

public:
    template<typename... Args>
    BalancerRequest(struct pool &_pool,
                    Balancer &_balancer,
                    const AddressList &_address_list,
                    CancellablePointer &_cancel_ptr,
                    sticky_hash_t _session_sticky,
                    Args&&... args) noexcept
        :R(std::forward<Args>(args)...),
         pool(_pool), balancer(_balancer),
         address_list(_address_list),
         session_sticky(_session_sticky),
         retries(CalculateRetries(address_list))
    {
        _cancel_ptr = *this;
    }

    BalancerRequest(const BalancerRequest &) = delete;

    void Destroy() noexcept {
        this->~BalancerRequest();
    }

private:
    void Cancel() noexcept override {
        cancel_ptr.Cancel();
        Destroy();
    }

    static unsigned CalculateRetries(const AddressList &address_list) noexcept {
        const unsigned size = address_list.GetSize();
        if (size <= 1)
            return 0;
        else if (size == 2)
            return 1;
        else if (size == 3)
            return 2;
        else
            return 3;
    }

public:
    static constexpr BalancerRequest &Cast(R &r) noexcept {
        return (BalancerRequest &)r;
    }

    void Next(Expiry now) noexcept {
        const SocketAddress address =
            balancer.Get(now, address_list, session_sticky);

        /* we need to copy this address because it may come from
           the balancer's cache, and the according cache item may
           be flushed at any time */
        const auto current_address = DupAddress(pool, address);
        failure = balancer.GetFailureManager().Make(current_address);

        R::Send(pool, current_address, cancel_ptr);
    }

    void ConnectSuccess() noexcept {
        failure->UnsetConnect();
    }

    bool ConnectFailure(Expiry now) noexcept {
        failure->SetConnect(now, std::chrono::seconds(20));

        if (retries-- > 0){
            /* try again, next address */
            Next(now);
            return true;
        } else
            /* give up */
            return false;
    }

    template<typename... Args>
    static void Start(struct pool &pool, Expiry now,
                      Args&&... args) noexcept {
        auto r = NewFromPool<BalancerRequest>(pool, pool,
                                              std::forward<Args>(args)...);
        r->Next(now);
    }
};

#endif
<|endoftext|>


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

/*
 * Generic connection balancer.
 */

#ifndef GENERIC_BALANCER_HXX
#define GENERIC_BALANCER_HXX

#include "balancer.hxx"
#include "address_list.hxx"
#include "pool/pool.hxx"
#include "net/SocketAddress.hxx"
#include "net/FailureManager.hxx"

#include <utility>

class CancellablePointer;

template<class R>
struct BalancerRequest : R {
    struct pool &pool;

    Balancer &balancer;

    const AddressList &address_list;

    CancellablePointer &cancel_ptr;

    /**
     * The "sticky id" of the incoming HTTP request.
     */
    const sticky_hash_t session_sticky;

    /**
     * The number of remaining connection attempts.  We give up when
     * we get an error and this attribute is already zero.
     */
    unsigned retries;

    SocketAddress current_address;

    template<typename... Args>
    BalancerRequest(struct pool &_pool,
                    Balancer &_balancer,
                    const AddressList &_address_list,
                    CancellablePointer &_cancel_ptr,
                    sticky_hash_t _session_sticky,
                    Args&&... args)
        :R(std::forward<Args>(args)...),
         pool(_pool), balancer(_balancer),
         address_list(_address_list),
         cancel_ptr(_cancel_ptr),
         session_sticky(_session_sticky),
         retries(CalculateRetries(address_list)) {}

    BalancerRequest(const BalancerRequest &) = delete;

    static unsigned CalculateRetries(const AddressList &address_list) {
        const unsigned size = address_list.GetSize();
        if (size <= 1)
            return 0;
        else if (size == 2)
            return 1;
        else if (size == 3)
            return 2;
        else
            return 3;
    }

    static constexpr BalancerRequest &Cast(R &r) {
        return (BalancerRequest &)r;
    }

    const SocketAddress &GetAddress() const {
        return current_address;
    }

    void Next(Expiry now) {
        const SocketAddress address =
            balancer.Get(now, address_list, session_sticky);

        /* we need to copy this address because it may come from
           the balancer's cache, and the according cache item may
           be flushed at any time */
        const struct sockaddr *new_address = (const struct sockaddr *)
            p_memdup(&pool, address.GetAddress(), address.GetSize());
        current_address = { new_address, address.GetSize() };

        R::Send(pool, current_address, cancel_ptr);
    }

    void ConnectSuccess(Expiry now) {
        balancer.GetFailureManager().Unset(now, current_address,
                                           FAILURE_CONNECT);
    }

    bool ConnectFailure(Expiry now) {
        balancer.GetFailureManager().Set(now, current_address, FAILURE_CONNECT,
                                         std::chrono::seconds(20));

        if (retries-- > 0){
            /* try again, next address */
            Next(now);
            return true;
        } else
            /* give up */
            return false;
    }

    template<typename... Args>
    static void Start(struct pool &pool, Expiry now,
                      Args&&... args) {
        auto r = NewFromPool<BalancerRequest>(pool, pool,
                                              std::forward<Args>(args)...);
        r->Next(now);
    }
};

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

/*
 * Generic connection balancer.
 */

#ifndef GENERIC_BALANCER_HXX
#define GENERIC_BALANCER_HXX

#include "balancer.hxx"
#include "address_list.hxx"
#include "pool/pool.hxx"
#include "net/SocketAddress.hxx"
#include "net/FailureManager.hxx"

#include <utility>

class CancellablePointer;

template<class R>
struct BalancerRequest : R {
    struct pool &pool;

    Balancer &balancer;

    const AddressList &address_list;

    CancellablePointer &cancel_ptr;

    /**
     * The "sticky id" of the incoming HTTP request.
     */
    const sticky_hash_t session_sticky;

    /**
     * The number of remaining connection attempts.  We give up when
     * we get an error and this attribute is already zero.
     */
    unsigned retries;

    SocketAddress current_address;

    FailurePtr failure;

    template<typename... Args>
    BalancerRequest(struct pool &_pool,
                    Balancer &_balancer,
                    const AddressList &_address_list,
                    CancellablePointer &_cancel_ptr,
                    sticky_hash_t _session_sticky,
                    Args&&... args)
        :R(std::forward<Args>(args)...),
         pool(_pool), balancer(_balancer),
         address_list(_address_list),
         cancel_ptr(_cancel_ptr),
         session_sticky(_session_sticky),
         retries(CalculateRetries(address_list)) {}

    BalancerRequest(const BalancerRequest &) = delete;

    static unsigned CalculateRetries(const AddressList &address_list) {
        const unsigned size = address_list.GetSize();
        if (size <= 1)
            return 0;
        else if (size == 2)
            return 1;
        else if (size == 3)
            return 2;
        else
            return 3;
    }

    static constexpr BalancerRequest &Cast(R &r) {
        return (BalancerRequest &)r;
    }

    const SocketAddress &GetAddress() const {
        return current_address;
    }

    void Next(Expiry now) {
        const SocketAddress address =
            balancer.Get(now, address_list, session_sticky);

        /* we need to copy this address because it may come from
           the balancer's cache, and the according cache item may
           be flushed at any time */
        const struct sockaddr *new_address = (const struct sockaddr *)
            p_memdup(&pool, address.GetAddress(), address.GetSize());
        current_address = { new_address, address.GetSize() };
        failure = balancer.GetFailureManager().Make(current_address);

        R::Send(pool, current_address, cancel_ptr);
    }

    void ConnectSuccess(Expiry now) {
        failure->Unset(now, FAILURE_CONNECT);
    }

    bool ConnectFailure(Expiry now) {
        failure->Set(now, FAILURE_CONNECT, std::chrono::seconds(20));

        if (retries-- > 0){
            /* try again, next address */
            Next(now);
            return true;
        } else
            /* give up */
            return false;
    }

    template<typename... Args>
    static void Start(struct pool &pool, Expiry now,
                      Args&&... args) {
        auto r = NewFromPool<BalancerRequest>(pool, pool,
                                              std::forward<Args>(args)...);
        r->Next(now);
    }
};

#endif
<|endoftext|>