<commit_before>/*  This file is part of the KDE project.

    Copyright (C) 2007 Trolltech ASA. All rights reserved.

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

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

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

#include "videowidget.h"
#include <QtCore/QEvent>
#include <QtGui/QResizeEvent>
#include <QtGui/QPalette>
#include <QtGui/QImage>
#include <QtGui/QPainter>
#include <QtGui/QBoxLayout>
#include <QApplication>
#include <gst/gst.h>
#include <gst/interfaces/propertyprobe.h>
#include "mediaobject.h"
#include "message.h"
#include "common.h"

#include "glrenderer.h"
#include "widgetrenderer.h"
#include "x11renderer.h"

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

VideoWidget::VideoWidget(Backend *backend, QWidget *parent) :
    QWidget(parent),
    MediaNode(backend, VideoSink),
    m_videoBin(0),
    m_renderer(0),
    m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
    m_brightness(0.0),
    m_hue(0.0),
    m_contrast(0.0),
    m_saturation(0.0),
    m_videoBalance(0),
    m_colorspace(0),
    m_videoplug(0)
{
    setupVideoBin();
}

VideoWidget::~VideoWidget()
{
    if (m_videoBin) {
        gst_element_set_state (m_videoBin, GST_STATE_NULL);
        gst_object_unref (m_videoBin);
    }

    if (m_renderer)
        delete m_renderer;
}

void VideoWidget::setupVideoBin()
{

    m_renderer = m_backend->deviceManager()->createVideoRenderer(this);
    GstElement *videoSink = m_renderer->videoSink();

    m_videoBin = gst_bin_new (NULL);
    Q_ASSERT(m_videoBin);
    gst_object_ref (GST_OBJECT (m_videoBin)); //Take ownership
    gst_object_sink (GST_OBJECT (m_videoBin));

    //The videoplug element is the final element before the pluggable videosink
    m_videoplug = gst_element_factory_make ("identity", NULL);

    //Colorspace ensures that the output of the stream matches the input format accepted by our video sink
    m_colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);

    //Video scale is used to prepare the correct aspect ratio and scale.
    GstElement *videoScale = gst_element_factory_make ("videoscale", NULL);

    //We need a queue to support the tee from parent node
    GstElement *queue = gst_element_factory_make ("queue", NULL);

    if (queue && m_videoBin && videoScale && m_colorspace && videoSink && m_videoplug) {
        //Ensure that the bare essentials are prepared
        gst_bin_add_many (GST_BIN (m_videoBin), queue, m_colorspace, m_videoplug, videoScale, videoSink, NULL);
        bool success = false;
        //Video balance controls color/sat/hue in the YUV colorspace
        m_videoBalance = gst_element_factory_make ("videobalance", NULL);
        if (m_videoBalance) {
            // For video balance to work we have to first ensure that the video is in YUV colorspace,
            // then hand it off to the videobalance filter before finally converting it back to RGB.
            // Hence we nede a videoFilter to convert the colorspace before and after videobalance
            GstElement *m_colorspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
            gst_bin_add_many(GST_BIN(m_videoBin), m_videoBalance, m_colorspace2, NULL);
            success = gst_element_link_many(queue, m_colorspace, m_videoBalance, m_colorspace2, videoScale, m_videoplug, videoSink, NULL);
        } else {
            //If video balance is not available, just connect to sink directly
            success = gst_element_link_many(queue, m_colorspace, videoScale, m_videoplug, videoSink, NULL);
        }

        if (success) {
            GstPad *videopad = gst_element_get_pad (queue, "sink");
            gst_element_add_pad (m_videoBin, gst_ghost_pad_new ("sink", videopad));
            gst_object_unref (videopad);
            QWidget *parentWidget = qobject_cast<QWidget*>(parent());
            if (parentWidget)
                parentWidget->winId();  // Due to some existing issues with alien in 4.4,
                                        //  we must currently force the creation of a parent widget.
            m_isValid = true; //initialization ok, accept input
        }
    }
}

void VideoWidget::paintEvent(QPaintEvent *event)
{
    Q_ASSERT(m_renderer);
    m_renderer->handlePaint(event);
}

void VideoWidget::setVisible(bool val) {
    Q_ASSERT(m_renderer);

    // Disable overlays for graphics view
    if (root() && window() && window()->testAttribute(Qt::WA_DontShowOnScreen) && !m_renderer->paintsOnWidget()) {
        m_backend->logMessage(QString("Widget rendering forced"), Backend::Info, this);
        GstElement *videoSink = m_renderer->videoSink();
        Q_ASSERT(videoSink);

        gst_element_set_state (videoSink, GST_STATE_NULL);
        gst_bin_remove(GST_BIN(m_videoBin), videoSink);
        delete m_renderer;
        m_renderer = 0;

        // Use widgetRenderer as a fallback
        m_renderer = new WidgetRenderer(this);
        videoSink = m_renderer->videoSink();
        gst_bin_add(GST_BIN(m_videoBin), videoSink);
        gst_element_link(m_videoplug, videoSink);
        gst_element_set_state (videoSink, GST_STATE_PAUSED);

        // Request return to current state
        root()->invalidateGraph();
        root()->setState(root()->state());
    }
    QWidget::setVisible(val);
}

bool VideoWidget::event(QEvent *event)
{
    if (m_renderer && m_renderer->eventFilter(event))
        return true;
    return QWidget::event(event);
}

Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
{
    return m_aspectRatio;
}

QSize VideoWidget::sizeHint() const
{
    if (!m_movieSize.isEmpty())
        return m_movieSize;
    else
        return QSize(640, 480);
}

void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
{
    m_aspectRatio = aspectRatio;
    if (m_renderer)
        m_renderer->aspectRatioChanged(aspectRatio);
}

Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
{
    return m_scaleMode;
}

QRect VideoWidget::scaleToAspect(QRect srcRect, int w, int h) const
{
    float width = srcRect.width();
    float height = srcRect.width() * (float(h) / float(w));
    if (height > srcRect.height()) {
        height = srcRect.height();
        width = srcRect.height() * (float(w) / float(h));
    }
    return QRect(0, 0, (int)width, (int)height);
}

/***
 * Calculates the actual rectangle the movie will be presented with
 *
 * ### This function does currently asume a 1:1 pixel aspect
 **/
QRect VideoWidget::calculateDrawFrameRect() const
{
    QRect widgetRect = rect();
    QRect drawFrameRect;
    // Set m_drawFrameRect to be the size of the smallest possible
    // rect conforming to the aspect and containing the whole frame:
    switch (aspectRatio()) {

    case Phonon::VideoWidget::AspectRatioWidget:
        drawFrameRect = widgetRect;
        // No more calculations needed.
        return drawFrameRect;

    case Phonon::VideoWidget::AspectRatio4_3:
        drawFrameRect = scaleToAspect(widgetRect, 4, 3);
        break;

    case Phonon::VideoWidget::AspectRatio16_9:
        drawFrameRect = scaleToAspect(widgetRect, 16, 9);
        break;

    case Phonon::VideoWidget::AspectRatioAuto:
    default:
        drawFrameRect = QRect(0, 0, movieSize().width(), movieSize().height());
        break;
    }

    // Scale m_drawFrameRect to fill the widget
    // without breaking aspect:
    float widgetWidth = widgetRect.width();
    float widgetHeight = widgetRect.height();
    float frameWidth = widgetWidth;
    float frameHeight = drawFrameRect.height() * float(widgetWidth) / float(drawFrameRect.width());

    switch (scaleMode()) {
    case Phonon::VideoWidget::ScaleAndCrop:
        if (frameHeight < widgetHeight) {
            frameWidth *= float(widgetHeight) / float(frameHeight);
            frameHeight = widgetHeight;
        }
        break;
    case Phonon::VideoWidget::FitInView:
    default:
        if (frameHeight > widgetHeight) {
            frameWidth *= float(widgetHeight) / float(frameHeight);
            frameHeight = widgetHeight;
        }
        break;
    }
    drawFrameRect.setSize(QSize(int(frameWidth), int(frameHeight)));
    drawFrameRect.moveTo(int((widgetWidth - frameWidth) / 2.0f),
                           int((widgetHeight - frameHeight) / 2.0f));
    return drawFrameRect;
}

void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode)
{
    m_scaleMode = scaleMode;
    if (m_renderer)
        m_renderer->scaleModeChanged(scaleMode);
}

qreal VideoWidget::brightness() const
{
    return m_brightness;
}

qreal clampedValue(qreal val)
{
    if (val > 1.0 )
        return 1.0;
    else if (val < -1.0)
        return -1.0;
    else return val;
}

void VideoWidget::setBrightness(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_brightness)
        return;

    m_brightness = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "brightness", newValue, NULL); //gstreamer range is [-1, 1]

}

qreal VideoWidget::contrast() const
{
    return m_contrast;
}

void VideoWidget::setContrast(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_contrast)
        return;

    m_contrast = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "contrast", (newValue + 1.0), NULL); //gstreamer range is [0-2]
}

qreal VideoWidget::hue() const
{
    return m_hue;
}

void VideoWidget::setHue(qreal newValue)
{
    if (newValue == m_hue)
        return;

    newValue = clampedValue(newValue);

    m_hue = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "hue", newValue, NULL); //gstreamer range is [-1, 1]
}

qreal VideoWidget::saturation() const
{
    return m_saturation;
}

void VideoWidget::setSaturation(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_saturation)
        return;

    m_saturation = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "saturation", newValue + 1.0, NULL); //gstreamer range is [0, 2]
}

void VideoWidget::setMovieSize(const QSize &size)
{
    m_backend->logMessage(QString("New video size %0 x %1").arg(size.width()).arg(size.height()), Backend::Info);
    if (size == m_movieSize)
        return;
    m_movieSize = size;
    widget()->updateGeometry();
    widget()->update();
}

void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event)
{
    switch (event->type()) {
    case MediaNodeEvent::VideoSizeChanged: {
            const QSize *size = static_cast<const QSize*>(event->data());
            setMovieSize(*size);
        }
        break;
    default:
        break;
    }

    // Forward events to renderer
    if (m_renderer)
        m_renderer->handleMediaNodeEvent(event);
}

}
} //namespace Phonon::Gstreamer

QT_END_NAMESPACE

#include "moc_videowidget.cpp"
<commit_msg>silence a valgrind warning - this initialization is not really necessary, though<commit_after>/*  This file is part of the KDE project.

    Copyright (C) 2007 Trolltech ASA. All rights reserved.

    This library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2.1 or 3 of the License.

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

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

#include "videowidget.h"
#include <QtCore/QEvent>
#include <QtGui/QResizeEvent>
#include <QtGui/QPalette>
#include <QtGui/QImage>
#include <QtGui/QPainter>
#include <QtGui/QBoxLayout>
#include <QApplication>
#include <gst/gst.h>
#include <gst/interfaces/propertyprobe.h>
#include "mediaobject.h"
#include "message.h"
#include "common.h"

#include "glrenderer.h"
#include "widgetrenderer.h"
#include "x11renderer.h"

QT_BEGIN_NAMESPACE

namespace Phonon
{
namespace Gstreamer
{

VideoWidget::VideoWidget(Backend *backend, QWidget *parent) :
    QWidget(parent),
    MediaNode(backend, VideoSink),
    m_videoBin(0),
    m_renderer(0),
    m_aspectRatio(Phonon::VideoWidget::AspectRatioAuto),
    m_brightness(0.0),
    m_hue(0.0),
    m_contrast(0.0),
    m_saturation(0.0),
    m_scaleMode(Phonon::VideoWidget::FitInView),
    m_videoBalance(0),
    m_colorspace(0),
    m_videoplug(0)
{
    setupVideoBin();
}

VideoWidget::~VideoWidget()
{
    if (m_videoBin) {
        gst_element_set_state (m_videoBin, GST_STATE_NULL);
        gst_object_unref (m_videoBin);
    }

    if (m_renderer)
        delete m_renderer;
}

void VideoWidget::setupVideoBin()
{

    m_renderer = m_backend->deviceManager()->createVideoRenderer(this);
    GstElement *videoSink = m_renderer->videoSink();

    m_videoBin = gst_bin_new (NULL);
    Q_ASSERT(m_videoBin);
    gst_object_ref (GST_OBJECT (m_videoBin)); //Take ownership
    gst_object_sink (GST_OBJECT (m_videoBin));

    //The videoplug element is the final element before the pluggable videosink
    m_videoplug = gst_element_factory_make ("identity", NULL);

    //Colorspace ensures that the output of the stream matches the input format accepted by our video sink
    m_colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);

    //Video scale is used to prepare the correct aspect ratio and scale.
    GstElement *videoScale = gst_element_factory_make ("videoscale", NULL);

    //We need a queue to support the tee from parent node
    GstElement *queue = gst_element_factory_make ("queue", NULL);

    if (queue && m_videoBin && videoScale && m_colorspace && videoSink && m_videoplug) {
        //Ensure that the bare essentials are prepared
        gst_bin_add_many (GST_BIN (m_videoBin), queue, m_colorspace, m_videoplug, videoScale, videoSink, NULL);
        bool success = false;
        //Video balance controls color/sat/hue in the YUV colorspace
        m_videoBalance = gst_element_factory_make ("videobalance", NULL);
        if (m_videoBalance) {
            // For video balance to work we have to first ensure that the video is in YUV colorspace,
            // then hand it off to the videobalance filter before finally converting it back to RGB.
            // Hence we nede a videoFilter to convert the colorspace before and after videobalance
            GstElement *m_colorspace2 = gst_element_factory_make ("ffmpegcolorspace", NULL);
            gst_bin_add_many(GST_BIN(m_videoBin), m_videoBalance, m_colorspace2, NULL);
            success = gst_element_link_many(queue, m_colorspace, m_videoBalance, m_colorspace2, videoScale, m_videoplug, videoSink, NULL);
        } else {
            //If video balance is not available, just connect to sink directly
            success = gst_element_link_many(queue, m_colorspace, videoScale, m_videoplug, videoSink, NULL);
        }

        if (success) {
            GstPad *videopad = gst_element_get_pad (queue, "sink");
            gst_element_add_pad (m_videoBin, gst_ghost_pad_new ("sink", videopad));
            gst_object_unref (videopad);
            QWidget *parentWidget = qobject_cast<QWidget*>(parent());
            if (parentWidget)
                parentWidget->winId();  // Due to some existing issues with alien in 4.4,
                                        //  we must currently force the creation of a parent widget.
            m_isValid = true; //initialization ok, accept input
        }
    }
}

void VideoWidget::paintEvent(QPaintEvent *event)
{
    Q_ASSERT(m_renderer);
    m_renderer->handlePaint(event);
}

void VideoWidget::setVisible(bool val) {
    Q_ASSERT(m_renderer);

    // Disable overlays for graphics view
    if (root() && window() && window()->testAttribute(Qt::WA_DontShowOnScreen) && !m_renderer->paintsOnWidget()) {
        m_backend->logMessage(QString("Widget rendering forced"), Backend::Info, this);
        GstElement *videoSink = m_renderer->videoSink();
        Q_ASSERT(videoSink);

        gst_element_set_state (videoSink, GST_STATE_NULL);
        gst_bin_remove(GST_BIN(m_videoBin), videoSink);
        delete m_renderer;
        m_renderer = 0;

        // Use widgetRenderer as a fallback
        m_renderer = new WidgetRenderer(this);
        videoSink = m_renderer->videoSink();
        gst_bin_add(GST_BIN(m_videoBin), videoSink);
        gst_element_link(m_videoplug, videoSink);
        gst_element_set_state (videoSink, GST_STATE_PAUSED);

        // Request return to current state
        root()->invalidateGraph();
        root()->setState(root()->state());
    }
    QWidget::setVisible(val);
}

bool VideoWidget::event(QEvent *event)
{
    if (m_renderer && m_renderer->eventFilter(event))
        return true;
    return QWidget::event(event);
}

Phonon::VideoWidget::AspectRatio VideoWidget::aspectRatio() const
{
    return m_aspectRatio;
}

QSize VideoWidget::sizeHint() const
{
    if (!m_movieSize.isEmpty())
        return m_movieSize;
    else
        return QSize(640, 480);
}

void VideoWidget::setAspectRatio(Phonon::VideoWidget::AspectRatio aspectRatio)
{
    m_aspectRatio = aspectRatio;
    if (m_renderer)
        m_renderer->aspectRatioChanged(aspectRatio);
}

Phonon::VideoWidget::ScaleMode VideoWidget::scaleMode() const
{
    return m_scaleMode;
}

QRect VideoWidget::scaleToAspect(QRect srcRect, int w, int h) const
{
    float width = srcRect.width();
    float height = srcRect.width() * (float(h) / float(w));
    if (height > srcRect.height()) {
        height = srcRect.height();
        width = srcRect.height() * (float(w) / float(h));
    }
    return QRect(0, 0, (int)width, (int)height);
}

/***
 * Calculates the actual rectangle the movie will be presented with
 *
 * ### This function does currently asume a 1:1 pixel aspect
 **/
QRect VideoWidget::calculateDrawFrameRect() const
{
    QRect widgetRect = rect();
    QRect drawFrameRect;
    // Set m_drawFrameRect to be the size of the smallest possible
    // rect conforming to the aspect and containing the whole frame:
    switch (aspectRatio()) {

    case Phonon::VideoWidget::AspectRatioWidget:
        drawFrameRect = widgetRect;
        // No more calculations needed.
        return drawFrameRect;

    case Phonon::VideoWidget::AspectRatio4_3:
        drawFrameRect = scaleToAspect(widgetRect, 4, 3);
        break;

    case Phonon::VideoWidget::AspectRatio16_9:
        drawFrameRect = scaleToAspect(widgetRect, 16, 9);
        break;

    case Phonon::VideoWidget::AspectRatioAuto:
    default:
        drawFrameRect = QRect(0, 0, movieSize().width(), movieSize().height());
        break;
    }

    // Scale m_drawFrameRect to fill the widget
    // without breaking aspect:
    float widgetWidth = widgetRect.width();
    float widgetHeight = widgetRect.height();
    float frameWidth = widgetWidth;
    float frameHeight = drawFrameRect.height() * float(widgetWidth) / float(drawFrameRect.width());

    switch (scaleMode()) {
    case Phonon::VideoWidget::ScaleAndCrop:
        if (frameHeight < widgetHeight) {
            frameWidth *= float(widgetHeight) / float(frameHeight);
            frameHeight = widgetHeight;
        }
        break;
    case Phonon::VideoWidget::FitInView:
    default:
        if (frameHeight > widgetHeight) {
            frameWidth *= float(widgetHeight) / float(frameHeight);
            frameHeight = widgetHeight;
        }
        break;
    }
    drawFrameRect.setSize(QSize(int(frameWidth), int(frameHeight)));
    drawFrameRect.moveTo(int((widgetWidth - frameWidth) / 2.0f),
                           int((widgetHeight - frameHeight) / 2.0f));
    return drawFrameRect;
}

void VideoWidget::setScaleMode(Phonon::VideoWidget::ScaleMode scaleMode)
{
    m_scaleMode = scaleMode;
    if (m_renderer)
        m_renderer->scaleModeChanged(scaleMode);
}

qreal VideoWidget::brightness() const
{
    return m_brightness;
}

qreal clampedValue(qreal val)
{
    if (val > 1.0 )
        return 1.0;
    else if (val < -1.0)
        return -1.0;
    else return val;
}

void VideoWidget::setBrightness(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_brightness)
        return;

    m_brightness = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "brightness", newValue, NULL); //gstreamer range is [-1, 1]

}

qreal VideoWidget::contrast() const
{
    return m_contrast;
}

void VideoWidget::setContrast(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_contrast)
        return;

    m_contrast = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "contrast", (newValue + 1.0), NULL); //gstreamer range is [0-2]
}

qreal VideoWidget::hue() const
{
    return m_hue;
}

void VideoWidget::setHue(qreal newValue)
{
    if (newValue == m_hue)
        return;

    newValue = clampedValue(newValue);

    m_hue = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "hue", newValue, NULL); //gstreamer range is [-1, 1]
}

qreal VideoWidget::saturation() const
{
    return m_saturation;
}

void VideoWidget::setSaturation(qreal newValue)
{
    newValue = clampedValue(newValue);

    if (newValue == m_saturation)
        return;

    m_saturation = newValue;

    if (m_videoBalance)
        g_object_set(G_OBJECT(m_videoBalance), "saturation", newValue + 1.0, NULL); //gstreamer range is [0, 2]
}

void VideoWidget::setMovieSize(const QSize &size)
{
    m_backend->logMessage(QString("New video size %0 x %1").arg(size.width()).arg(size.height()), Backend::Info);
    if (size == m_movieSize)
        return;
    m_movieSize = size;
    widget()->updateGeometry();
    widget()->update();
}

void VideoWidget::mediaNodeEvent(const MediaNodeEvent *event)
{
    switch (event->type()) {
    case MediaNodeEvent::VideoSizeChanged: {
            const QSize *size = static_cast<const QSize*>(event->data());
            setMovieSize(*size);
        }
        break;
    default:
        break;
    }

    // Forward events to renderer
    if (m_renderer)
        m_renderer->handleMediaNodeEvent(event);
}

}
} //namespace Phonon::Gstreamer

QT_END_NAMESPACE

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


<commit_before>/**************************************************************************
**
** Copyright (C) 2015 Denis Mingulov
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "classviewparsertreeitem.h"
#include "classviewsymbollocation.h"
#include "classviewsymbolinformation.h"
#include "classviewconstants.h"
#include "classviewutils.h"

#include <QHash>
#include <QPair>
#include <QIcon>
#include <QStandardItem>
#include <QMutex>

#include <QDebug>

enum { debug = false };

namespace ClassView {
namespace Internal {

///////////////////////////////// ParserTreeItemPrivate //////////////////////////////////

/*!
    \class ParserTreeItemPrivate
    \brief The ParserTreeItemPrivate class defines private class data for
    the ParserTreeItem class.
   \sa ParserTreeItem
 */
class ParserTreeItemPrivate
{
public:
    //! symbol locations
    QSet<SymbolLocation> symbolLocations;

    //! symbol information
    QHash<SymbolInformation, ParserTreeItem::Ptr> symbolInformations;

    //! An icon
    QIcon icon;
};

///////////////////////////////// ParserTreeItem //////////////////////////////////

/*!
    \class ParserTreeItem
    \brief The ParserTreeItem class is an item for the internal Class View tree.

    Not virtual - to speed up its work.
*/

ParserTreeItem::ParserTreeItem() :
    d(new ParserTreeItemPrivate())
{
}

ParserTreeItem::~ParserTreeItem()
{
    delete d;
}

ParserTreeItem &ParserTreeItem::operator=(const ParserTreeItem &other)
{
    d->symbolLocations = other.d->symbolLocations;
    d->icon = other.d->icon;
    d->symbolInformations.clear();
    return *this;
}

/*!
    Copies a parser tree item from the location specified by \a from to this
    item.
*/

void ParserTreeItem::copy(const ParserTreeItem::ConstPtr &from)
{
    if (from.isNull())
        return;

    d->symbolLocations = from->d->symbolLocations;
    d->icon = from->d->icon;
    d->symbolInformations = from->d->symbolInformations;
}

/*!
    \fn void copyTree(const ParserTreeItem::ConstPtr &from)
    Copies a parser tree item with children from the location specified by
    \a from to this item.
*/

void ParserTreeItem::copyTree(const ParserTreeItem::ConstPtr &target)
{
    if (target.isNull())
        return;

    // copy content
    d->symbolLocations = target->d->symbolLocations;
    d->icon = target->d->icon;
    d->symbolInformations.clear();

    // reserve memory
//    int amount = qMin(100 , target->d_ptr->symbolInformations.count() * 2);
//    d_ptr->symbolInformations.reserve(amount);

    // every child
    CitSymbolInformations cur = target->d->symbolInformations.constBegin();
    CitSymbolInformations end = target->d->symbolInformations.constEnd();

    for (; cur != end; ++cur) {
        ParserTreeItem::Ptr item(new ParserTreeItem());
        item->copyTree(cur.value());
        appendChild(item, cur.key());
    }
}

/*!
    Adds information about symbol location from a \location.
    \sa SymbolLocation, removeSymbolLocation, symbolLocations
*/

void ParserTreeItem::addSymbolLocation(const SymbolLocation &location)
{
    d->symbolLocations.insert(location);
}

/*!
    Adds information about symbol locations from \a locations.
    \sa SymbolLocation, removeSymbolLocation, symbolLocations
*/

void ParserTreeItem::addSymbolLocation(const QSet<SymbolLocation> &locations)
{
    d->symbolLocations.unite(locations);
}

/*!
    Removes information about \a location.
    \sa SymbolLocation, addSymbolLocation, symbolLocations
*/

void ParserTreeItem::removeSymbolLocation(const SymbolLocation &location)
{
    d->symbolLocations.remove(location);
}

/*!
    Removes information about \a locations.
    \sa SymbolLocation, addSymbolLocation, symbolLocations
*/

void ParserTreeItem::removeSymbolLocations(const QSet<SymbolLocation> &locations)
{
    d->symbolLocations.subtract(locations);
}

/*!
    Gets information about symbol positions.
    \sa SymbolLocation, addSymbolLocation, removeSymbolLocation
*/

QSet<SymbolLocation> ParserTreeItem::symbolLocations() const
{
    return d->symbolLocations;
}

/*!
    Appends the child item \a item to \a inf symbol information.
*/

void ParserTreeItem::appendChild(const ParserTreeItem::Ptr &item, const SymbolInformation &inf)
{
    // removeChild must be used to remove an item
    if (item.isNull())
        return;

    d->symbolInformations[inf] = item;
}

/*!
    Removes the \a inf symbol information.
*/

void ParserTreeItem::removeChild(const SymbolInformation &inf)
{
    d->symbolInformations.remove(inf);
}

/*!
    Returns the child item specified by \a inf symbol information.
*/

ParserTreeItem::Ptr ParserTreeItem::child(const SymbolInformation &inf) const
{
    return d->symbolInformations.value(inf);
}

/*!
    Returns the amount of children of the tree item.
*/

int ParserTreeItem::childCount() const
{
    return d->symbolInformations.count();
}

/*!
    \property QIcon::icon
    \brief the icon assigned to the tree item
*/

QIcon ParserTreeItem::icon() const
{
    return d->icon;
}

/*!
    Sets the \a icon for the tree item.
 */
void ParserTreeItem::setIcon(const QIcon &icon)
{
    d->icon = icon;
}

/*!
    Adds an internal state with \a target, which contains the correct current
    state.
*/

void ParserTreeItem::add(const ParserTreeItem::ConstPtr &target)
{
    if (target.isNull())
        return;

    // add locations
    d->symbolLocations = d->symbolLocations.unite(target->d->symbolLocations);

    // add children
    // every target child
    CitSymbolInformations cur = target->d->symbolInformations.constBegin();
    CitSymbolInformations end = target->d->symbolInformations.constEnd();
    while (cur != end) {
        const SymbolInformation &inf = cur.key();
        const ParserTreeItem::Ptr &targetChild = cur.value();

        ParserTreeItem::Ptr child = d->symbolInformations.value(inf);
        if (!child.isNull()) {
            child->add(targetChild);
        } else {
            ParserTreeItem::Ptr add(new ParserTreeItem());
            add->copyTree(targetChild);
            d->symbolInformations[inf] = add;
        }
        // next item
        ++cur;
    }
}

/*!
    Appends this item to the QStandardIten item \a item.
*/

void ParserTreeItem::convertTo(QStandardItem *item) const
{
    if (!item)
        return;

    QMap<SymbolInformation, ParserTreeItem::Ptr> map;

    // convert to map - to sort it
    CitSymbolInformations curHash = d->symbolInformations.constBegin();
    CitSymbolInformations endHash = d->symbolInformations.constEnd();
    while (curHash != endHash) {
        map.insert(curHash.key(), curHash.value());
        ++curHash;
    }

    typedef QMap<SymbolInformation, ParserTreeItem::Ptr>::const_iterator MapCitSymbolInformations;
    // add to item
    MapCitSymbolInformations cur = map.constBegin();
    MapCitSymbolInformations end = map.constEnd();
    while (cur != end) {
        const SymbolInformation &inf = cur.key();
        ParserTreeItem::Ptr ptr = cur.value();

        QStandardItem *add = new QStandardItem();
        Utils::setSymbolInformationToItem(inf, add);
        if (!ptr.isNull()) {
            // icon
            add->setIcon(ptr->icon());

            // draggable
            if (!ptr->symbolLocations().isEmpty())
                add->setFlags(add->flags() | Qt::ItemIsDragEnabled);

            // locations
            add->setData(Utils::locationsToRole(ptr->symbolLocations()),
                         Constants::SymbolLocationsRole);
        }
        item->appendRow(add);
        ++cur;
    }
}

/*!
    Checks \a item in a QStandardItemModel for lazy data population.
*/

bool ParserTreeItem::canFetchMore(QStandardItem *item) const
{
    if (!item)
        return false;

    int storedChildren = item->rowCount();
    int internalChildren = d->symbolInformations.count();

    if (storedChildren < internalChildren)
        return true;

    return false;
}

/*!
    Performs lazy data population for \a item in a QStandardItemModel if needed.
*/

void ParserTreeItem::fetchMore(QStandardItem *item) const
{
    if (!item)
        return;

    convertTo(item);
}

/*!
    Debug dump.
*/

void ParserTreeItem::debugDump(int ident) const
{
    CitSymbolInformations curHash = d->symbolInformations.constBegin();
    CitSymbolInformations endHash = d->symbolInformations.constEnd();
    while (curHash != endHash) {
        const SymbolInformation &inf = curHash.key();
        qDebug() << QString(2*ident, QLatin1Char(' ')) << inf.iconType() << inf.name() << inf.type()
                << curHash.value().isNull();
        if (!curHash.value().isNull())
            curHash.value()->debugDump(ident + 1);

        ++curHash;
    }
}

} // namespace Internal
} // namespace ClassView

<commit_msg>ClassView: removed dead code<commit_after>/**************************************************************************
**
** Copyright (C) 2015 Denis Mingulov
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company.  For licensing terms and
** conditions see http://www.qt.io/terms-conditions.  For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file.  Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights.  These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

#include "classviewparsertreeitem.h"
#include "classviewsymbollocation.h"
#include "classviewsymbolinformation.h"
#include "classviewconstants.h"
#include "classviewutils.h"

#include <QHash>
#include <QPair>
#include <QIcon>
#include <QStandardItem>
#include <QMutex>

#include <QDebug>

namespace ClassView {
namespace Internal {

///////////////////////////////// ParserTreeItemPrivate //////////////////////////////////

/*!
    \class ParserTreeItemPrivate
    \brief The ParserTreeItemPrivate class defines private class data for
    the ParserTreeItem class.
   \sa ParserTreeItem
 */
class ParserTreeItemPrivate
{
public:
    //! symbol locations
    QSet<SymbolLocation> symbolLocations;

    //! symbol information
    QHash<SymbolInformation, ParserTreeItem::Ptr> symbolInformations;

    //! An icon
    QIcon icon;
};

///////////////////////////////// ParserTreeItem //////////////////////////////////

/*!
    \class ParserTreeItem
    \brief The ParserTreeItem class is an item for the internal Class View tree.

    Not virtual - to speed up its work.
*/

ParserTreeItem::ParserTreeItem() :
    d(new ParserTreeItemPrivate())
{
}

ParserTreeItem::~ParserTreeItem()
{
    delete d;
}

ParserTreeItem &ParserTreeItem::operator=(const ParserTreeItem &other)
{
    d->symbolLocations = other.d->symbolLocations;
    d->icon = other.d->icon;
    d->symbolInformations.clear();
    return *this;
}

/*!
    Copies a parser tree item from the location specified by \a from to this
    item.
*/

void ParserTreeItem::copy(const ParserTreeItem::ConstPtr &from)
{
    if (from.isNull())
        return;

    d->symbolLocations = from->d->symbolLocations;
    d->icon = from->d->icon;
    d->symbolInformations = from->d->symbolInformations;
}

/*!
    \fn void copyTree(const ParserTreeItem::ConstPtr &from)
    Copies a parser tree item with children from the location specified by
    \a from to this item.
*/

void ParserTreeItem::copyTree(const ParserTreeItem::ConstPtr &target)
{
    if (target.isNull())
        return;

    // copy content
    d->symbolLocations = target->d->symbolLocations;
    d->icon = target->d->icon;
    d->symbolInformations.clear();

    // reserve memory
//    int amount = qMin(100 , target->d_ptr->symbolInformations.count() * 2);
//    d_ptr->symbolInformations.reserve(amount);

    // every child
    CitSymbolInformations cur = target->d->symbolInformations.constBegin();
    CitSymbolInformations end = target->d->symbolInformations.constEnd();

    for (; cur != end; ++cur) {
        ParserTreeItem::Ptr item(new ParserTreeItem());
        item->copyTree(cur.value());
        appendChild(item, cur.key());
    }
}

/*!
    Adds information about symbol location from a \location.
    \sa SymbolLocation, removeSymbolLocation, symbolLocations
*/

void ParserTreeItem::addSymbolLocation(const SymbolLocation &location)
{
    d->symbolLocations.insert(location);
}

/*!
    Adds information about symbol locations from \a locations.
    \sa SymbolLocation, removeSymbolLocation, symbolLocations
*/

void ParserTreeItem::addSymbolLocation(const QSet<SymbolLocation> &locations)
{
    d->symbolLocations.unite(locations);
}

/*!
    Removes information about \a location.
    \sa SymbolLocation, addSymbolLocation, symbolLocations
*/

void ParserTreeItem::removeSymbolLocation(const SymbolLocation &location)
{
    d->symbolLocations.remove(location);
}

/*!
    Removes information about \a locations.
    \sa SymbolLocation, addSymbolLocation, symbolLocations
*/

void ParserTreeItem::removeSymbolLocations(const QSet<SymbolLocation> &locations)
{
    d->symbolLocations.subtract(locations);
}

/*!
    Gets information about symbol positions.
    \sa SymbolLocation, addSymbolLocation, removeSymbolLocation
*/

QSet<SymbolLocation> ParserTreeItem::symbolLocations() const
{
    return d->symbolLocations;
}

/*!
    Appends the child item \a item to \a inf symbol information.
*/

void ParserTreeItem::appendChild(const ParserTreeItem::Ptr &item, const SymbolInformation &inf)
{
    // removeChild must be used to remove an item
    if (item.isNull())
        return;

    d->symbolInformations[inf] = item;
}

/*!
    Removes the \a inf symbol information.
*/

void ParserTreeItem::removeChild(const SymbolInformation &inf)
{
    d->symbolInformations.remove(inf);
}

/*!
    Returns the child item specified by \a inf symbol information.
*/

ParserTreeItem::Ptr ParserTreeItem::child(const SymbolInformation &inf) const
{
    return d->symbolInformations.value(inf);
}

/*!
    Returns the amount of children of the tree item.
*/

int ParserTreeItem::childCount() const
{
    return d->symbolInformations.count();
}

/*!
    \property QIcon::icon
    \brief the icon assigned to the tree item
*/

QIcon ParserTreeItem::icon() const
{
    return d->icon;
}

/*!
    Sets the \a icon for the tree item.
 */
void ParserTreeItem::setIcon(const QIcon &icon)
{
    d->icon = icon;
}

/*!
    Adds an internal state with \a target, which contains the correct current
    state.
*/

void ParserTreeItem::add(const ParserTreeItem::ConstPtr &target)
{
    if (target.isNull())
        return;

    // add locations
    d->symbolLocations = d->symbolLocations.unite(target->d->symbolLocations);

    // add children
    // every target child
    CitSymbolInformations cur = target->d->symbolInformations.constBegin();
    CitSymbolInformations end = target->d->symbolInformations.constEnd();
    while (cur != end) {
        const SymbolInformation &inf = cur.key();
        const ParserTreeItem::Ptr &targetChild = cur.value();

        ParserTreeItem::Ptr child = d->symbolInformations.value(inf);
        if (!child.isNull()) {
            child->add(targetChild);
        } else {
            ParserTreeItem::Ptr add(new ParserTreeItem());
            add->copyTree(targetChild);
            d->symbolInformations[inf] = add;
        }
        // next item
        ++cur;
    }
}

/*!
    Appends this item to the QStandardIten item \a item.
*/

void ParserTreeItem::convertTo(QStandardItem *item) const
{
    if (!item)
        return;

    QMap<SymbolInformation, ParserTreeItem::Ptr> map;

    // convert to map - to sort it
    CitSymbolInformations curHash = d->symbolInformations.constBegin();
    CitSymbolInformations endHash = d->symbolInformations.constEnd();
    while (curHash != endHash) {
        map.insert(curHash.key(), curHash.value());
        ++curHash;
    }

    typedef QMap<SymbolInformation, ParserTreeItem::Ptr>::const_iterator MapCitSymbolInformations;
    // add to item
    MapCitSymbolInformations cur = map.constBegin();
    MapCitSymbolInformations end = map.constEnd();
    while (cur != end) {
        const SymbolInformation &inf = cur.key();
        ParserTreeItem::Ptr ptr = cur.value();

        QStandardItem *add = new QStandardItem();
        Utils::setSymbolInformationToItem(inf, add);
        if (!ptr.isNull()) {
            // icon
            add->setIcon(ptr->icon());

            // draggable
            if (!ptr->symbolLocations().isEmpty())
                add->setFlags(add->flags() | Qt::ItemIsDragEnabled);

            // locations
            add->setData(Utils::locationsToRole(ptr->symbolLocations()),
                         Constants::SymbolLocationsRole);
        }
        item->appendRow(add);
        ++cur;
    }
}

/*!
    Checks \a item in a QStandardItemModel for lazy data population.
*/

bool ParserTreeItem::canFetchMore(QStandardItem *item) const
{
    if (!item)
        return false;

    int storedChildren = item->rowCount();
    int internalChildren = d->symbolInformations.count();

    if (storedChildren < internalChildren)
        return true;

    return false;
}

/*!
    Performs lazy data population for \a item in a QStandardItemModel if needed.
*/

void ParserTreeItem::fetchMore(QStandardItem *item) const
{
    if (!item)
        return;

    convertTo(item);
}

/*!
    Debug dump.
*/

void ParserTreeItem::debugDump(int ident) const
{
    CitSymbolInformations curHash = d->symbolInformations.constBegin();
    CitSymbolInformations endHash = d->symbolInformations.constEnd();
    while (curHash != endHash) {
        const SymbolInformation &inf = curHash.key();
        qDebug() << QString(2*ident, QLatin1Char(' ')) << inf.iconType() << inf.name() << inf.type()
                << curHash.value().isNull();
        if (!curHash.value().isNull())
            curHash.value()->debugDump(ident + 1);

        ++curHash;
    }
}

} // namespace Internal
} // namespace ClassView

<|endoftext|>


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

#include <stddef.h>
#include <stdint.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/temp_file.h"
#include "src/profiling/memory/shared_ring_buffer.h"

namespace perfetto {
namespace profiling {
namespace {

struct MetadataHeader {
  alignas(uint64_t) std::atomic<bool> spinlock;
  uint64_t read_pos;
  uint64_t write_pos;
};

size_t RoundToPow2(size_t v) {
  uint64_t x = static_cast<size_t>(v);
  if (x < 2)
    return 2;

  x--;
  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
  x |= x >> 32;
  x++;
  return static_cast<size_t>(x);
}

int FuzzRingBuffer(const uint8_t* data, size_t size) {
  if (size <= sizeof(MetadataHeader))
    return 0;

  auto fd = base::TempFile::CreateUnlinked().ReleaseFD();
  PERFETTO_CHECK(fd);

  // Put the remaining fuzzer input into the data portion of the ring buffer.
  size_t payload_size = size - sizeof(MetadataHeader);
  const uint8_t* payload = data + sizeof(MetadataHeader);
  size_t payload_size_pages =
      (payload_size + base::kPageSize - 1) / base::kPageSize;
  // Upsize test buffer to be 2^n data pages (precondition of the impl) + 1 page
  // for the metadata.
  size_t total_size_pages = 1 + RoundToPow2(payload_size_pages);

  PERFETTO_CHECK(ftruncate(*fd, total_size_pages * base::kPageSize) == 0);

  PERFETTO_CHECK(base::WriteAll(*fd, data, sizeof(MetadataHeader)) != -1);
  PERFETTO_CHECK(lseek(*fd, base::kPageSize, SEEK_SET) != -1);
  PERFETTO_CHECK(base::WriteAll(*fd, payload, payload_size) != -1);

  auto buf = SharedRingBuffer::Attach(std::move(fd));
  PERFETTO_CHECK(!!buf);

  bool did_read;
  do {
    auto read_buf = buf->BeginRead();
    did_read = bool(read_buf);
    if (did_read) {
      volatile uint8_t* v_data = read_buf.data;
      // Assert we get a reference to valid memory.
      for (size_t i = 0; i < read_buf.size; ++i)
        v_data[i] = v_data[i];
    }
    buf->EndRead(std::move(read_buf));
  } while (did_read);
  return 0;
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  return perfetto::profiling::FuzzRingBuffer(data, size);
}
<commit_msg>Fix typo in fuzzer (functionally equivalent). am: 0e7d41f09a am: 6322f83417<commit_after>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stddef.h>
#include <stdint.h>

#include "perfetto/base/file_utils.h"
#include "perfetto/base/temp_file.h"
#include "src/profiling/memory/shared_ring_buffer.h"

namespace perfetto {
namespace profiling {
namespace {

struct MetadataHeader {
  alignas(uint64_t) std::atomic<bool> spinlock;
  uint64_t read_pos;
  uint64_t write_pos;
};

size_t RoundToPow2(size_t v) {
  uint64_t x = static_cast<uint64_t>(v);
  if (x < 2)
    return 2;

  x--;
  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
  x |= x >> 32;
  x++;
  return static_cast<size_t>(x);
}

int FuzzRingBuffer(const uint8_t* data, size_t size) {
  if (size <= sizeof(MetadataHeader))
    return 0;

  auto fd = base::TempFile::CreateUnlinked().ReleaseFD();
  PERFETTO_CHECK(fd);

  // Put the remaining fuzzer input into the data portion of the ring buffer.
  size_t payload_size = size - sizeof(MetadataHeader);
  const uint8_t* payload = data + sizeof(MetadataHeader);
  size_t payload_size_pages =
      (payload_size + base::kPageSize - 1) / base::kPageSize;
  // Upsize test buffer to be 2^n data pages (precondition of the impl) + 1 page
  // for the metadata.
  size_t total_size_pages = 1 + RoundToPow2(payload_size_pages);

  PERFETTO_CHECK(ftruncate(*fd, total_size_pages * base::kPageSize) == 0);

  PERFETTO_CHECK(base::WriteAll(*fd, data, sizeof(MetadataHeader)) != -1);
  PERFETTO_CHECK(lseek(*fd, base::kPageSize, SEEK_SET) != -1);
  PERFETTO_CHECK(base::WriteAll(*fd, payload, payload_size) != -1);

  auto buf = SharedRingBuffer::Attach(std::move(fd));
  PERFETTO_CHECK(!!buf);

  bool did_read;
  do {
    auto read_buf = buf->BeginRead();
    did_read = bool(read_buf);
    if (did_read) {
      volatile uint8_t* v_data = read_buf.data;
      // Assert we get a reference to valid memory.
      for (size_t i = 0; i < read_buf.size; ++i)
        v_data[i] = v_data[i];
    }
    buf->EndRead(std::move(read_buf));
  } while (did_read);
  return 0;
}

}  // namespace
}  // namespace profiling
}  // namespace perfetto

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  return perfetto::profiling::FuzzRingBuffer(data, size);
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <cassert>

#include "api-loader.h"
#include "pointers.h"

#include <string>
#include <vector>
#include <stdexcept>

#ifdef USE_DETOURS
#include "detours/detours.h"
#endif

#ifdef USE_MHOOK
#include "mhook/mhook-lib/mhook.h"
#endif

#include "gl-wrappers.h"

#include <DGLCommon/os.h>

#ifndef _WIN32
#include <dlfcn.h>
#endif

//here direct pointers are kept (pointers to entrypoints exposed by underlying OpenGL32 implementation
//use DIRECT_CALL(name) to call one of these pointers
LoadedPointer g_DirectPointers[Entrypoints_NUM] = {
#define FUNC_LIST_ELEM_SUPPORTED(name, type, library) { NULL, library},
#define FUNC_LIST_ELEM_NOT_SUPPORTED(name, type, library) FUNC_LIST_ELEM_SUPPORTED(name, type, library)
    #include "codegen/functionList.inl"
#undef FUNC_LIST_ELEM_SUPPORTED
#undef FUNC_LIST_ELEM_NOT_SUPPORTED
};

APILoader::APILoader():m_GlueLibrary(LIBRARY_NONE), m_LoadedApiLibraries(LIBRARY_NONE) {}

void* APILoader::loadGLPointer(LoadedLib library, Entrypoint entryp) {
#ifdef _WIN32
    return GetProcAddress((HINSTANCE)library, GetEntryPointName(entryp));
#else
    return dlsym(library, GetEntryPointName(entryp));
#endif
}

void* APILoader::loadExtPointer(Entrypoint entryp) {
    if (!g_DirectPointers[entryp].ptr) {

        if (!m_GlueLibrary) {
            throw std::runtime_error("Trying to call *GetProcAdress, but no glue library loaded");
        }

        void * ptr = NULL;

        switch (m_GlueLibrary) {
#ifdef _WIN32
        case LIBRARY_WGL:
            ptr  = DIRECT_CALL(wglGetProcAddress)(GetEntryPointName(entryp));
            break;
#endif
        case LIBRARY_EGL:
            ptr = (void*)DIRECT_CALL(eglGetProcAddress)(GetEntryPointName(entryp));
            break;
        default:
            assert(!"unknown glue library");
        }
        g_DirectPointers[entryp].ptr = ptr;
    }
    return g_DirectPointers[entryp].ptr;
}

std::string APILoader::getLibraryName(ApiLibrary apiLibrary) {
    switch (apiLibrary) {
        case LIBRARY_EGL:
#ifdef _WIN32
            return "libEGL.dll";
#else
            return "libEGL.so.1";
#endif
        case LIBRARY_GL:
        case LIBRARY_WGL:
#ifdef _WIN32
            return "opengl32.dll";
#else
            return "libGL.so.1";
#endif
        case LIBRARY_ES2:
        case LIBRARY_ES3:
#ifdef _WIN32
            return "libGLESv2.dll";
#else
            return "libGLESv2.so.1";
#endif
        default:
            assert(!"unknown library");
            throw std::runtime_error("Unknown GL library name");
    }
}

void APILoader::loadLibrary(ApiLibrary apiLibrary) {

    std::string libraryName = getLibraryName(apiLibrary);

    if (m_LoadedLibraries.find(libraryName) == m_LoadedLibraries.end()) {
        std::vector<std::string> libSearchPath;

        LoadedLib openGLLibraryHandle = NULL;

#ifdef _WIN32
        char buffer[1000];
#ifndef _WIN64
        if (GetSystemWow64Directory(buffer, sizeof(buffer)) > 0) {
            //we are running 32bit app on 64 bit windows
            libSearchPath.push_back(buffer);
        }
#endif
        if (!openGLLibraryHandle) {
            if (GetSystemDirectory(buffer, sizeof(buffer)) > 0) {
                //we are running on native system (32 on 32 or 64 on 64)
                libSearchPath.push_back(buffer);
            }
        }
#ifndef _WIN64
        libSearchPath.push_back("C:\\Windows\\SysWOW64\\");
#endif
        libSearchPath.push_back("C:\\Windows\\System32\\");
        libSearchPath.push_back(".");
#endif
        libSearchPath.push_back("");

        for (size_t i = 0; i < libSearchPath.size() && !openGLLibraryHandle; i++) {
#ifdef _WIN32
            openGLLibraryHandle = (LoadedLib)LoadLibrary((libSearchPath[i] + libraryName).c_str());
#else
            openGLLibraryHandle = dlopen((libSearchPath[i] + libraryName).c_str(), RTLD_NOW);
#endif
        }

        if (!openGLLibraryHandle) {
            std::string msg = std::string("Cannot load ") + libraryName + "  system library";
            Os::fatal(msg);
        } else {
            m_LoadedLibraries[libraryName] = openGLLibraryHandle;
        }
    }

    LoadedLib library = m_LoadedLibraries[libraryName];

    //we use MS Detours only on win32, on x64 mHook is used
#ifdef USE_DETOURS
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
#endif
    //g_DirectPointers is now filled with opengl32.dll pointers
    // we will now detour (hook) them all, so g_DirectPointers will still lead to original opengl32.dll, but
    //application will always call us.
    for (int i = 0; i < Entrypoints_NUM; i++) {
        if (!(g_DirectPointers[i].libraryMask & apiLibrary)) {
            //Do not load - entrypoint does not belong to currently loaded API
            continue;
        }

        if (m_LoadedApiLibraries & g_DirectPointers[i].libraryMask) {
            //Do not load - entrypoint belongs to already loaded API
            continue;
        }

        g_DirectPointers[i].ptr = loadGLPointer(library, i);

        if (g_DirectPointers[i].ptr) {
            //this entrypoint was loaded from OpenGL32.dll, detour it!
            void * hookPtr = getWrapperPointer(i);
#ifdef USE_DETOURS
            DetourAttach(&(PVOID&)g_DirectPointers[i].ptr, hookPtr);
#endif
#ifdef USE_MHOOK
            if (!Mhook_SetHook(&(PVOID&)g_DirectPointers[i].ptr, hookPtr)) {
                std::string error = "Cannot load OpenGL32.dll funcion ";
                error += GetEntryPointName(i);
                error += "().";
                Os::fatal(error);
            }
#endif
        }
    }
#ifdef USE_DETOURS
    DetourTransactionCommit();
#endif
    if (apiLibrary == LIBRARY_EGL || apiLibrary == LIBRARY_WGL)
        m_GlueLibrary = apiLibrary;

    m_LoadedApiLibraries |= apiLibrary;
}

void* APILoader::ensurePointer(Entrypoint entryp) {
    if (g_DirectPointers[entryp].ptr || loadExtPointer(entryp)) {
        return g_DirectPointers[entryp].ptr;
    } else {
        std::string error = "Operation aborted, because the ";
        error += GetEntryPointName(entryp);
        error += " function is not available on current context. Try updating GPU drivers.";
        throw std::runtime_error(error);
    }
}

APILoader g_ApiLoader;
<commit_msg>Change order of paths in dll search: first search without path. This will definitely kill loading DGLWrapper.dll by changing name to opengl32.dll, but fixes problem when library is both in . and in %SYSTEM%.<commit_after>#include <cstdlib>
#include <cassert>

#include "api-loader.h"
#include "pointers.h"

#include <string>
#include <vector>
#include <stdexcept>

#ifdef USE_DETOURS
#include "detours/detours.h"
#endif

#ifdef USE_MHOOK
#include "mhook/mhook-lib/mhook.h"
#endif

#include "gl-wrappers.h"

#include <DGLCommon/os.h>

#ifndef _WIN32
#include <dlfcn.h>
#endif

//here direct pointers are kept (pointers to entrypoints exposed by underlying OpenGL32 implementation
//use DIRECT_CALL(name) to call one of these pointers
LoadedPointer g_DirectPointers[Entrypoints_NUM] = {
#define FUNC_LIST_ELEM_SUPPORTED(name, type, library) { NULL, library},
#define FUNC_LIST_ELEM_NOT_SUPPORTED(name, type, library) FUNC_LIST_ELEM_SUPPORTED(name, type, library)
    #include "codegen/functionList.inl"
#undef FUNC_LIST_ELEM_SUPPORTED
#undef FUNC_LIST_ELEM_NOT_SUPPORTED
};

APILoader::APILoader():m_GlueLibrary(LIBRARY_NONE), m_LoadedApiLibraries(LIBRARY_NONE) {}

void* APILoader::loadGLPointer(LoadedLib library, Entrypoint entryp) {
#ifdef _WIN32
    return GetProcAddress((HINSTANCE)library, GetEntryPointName(entryp));
#else
    return dlsym(library, GetEntryPointName(entryp));
#endif
}

void* APILoader::loadExtPointer(Entrypoint entryp) {
    if (!g_DirectPointers[entryp].ptr) {

        if (!m_GlueLibrary) {
            throw std::runtime_error("Trying to call *GetProcAdress, but no glue library loaded");
        }

        void * ptr = NULL;

        switch (m_GlueLibrary) {
#ifdef _WIN32
        case LIBRARY_WGL:
            ptr  = DIRECT_CALL(wglGetProcAddress)(GetEntryPointName(entryp));
            break;
#endif
        case LIBRARY_EGL:
            ptr = (void*)DIRECT_CALL(eglGetProcAddress)(GetEntryPointName(entryp));
            break;
        default:
            assert(!"unknown glue library");
        }
        g_DirectPointers[entryp].ptr = ptr;
    }
    return g_DirectPointers[entryp].ptr;
}

std::string APILoader::getLibraryName(ApiLibrary apiLibrary) {
    switch (apiLibrary) {
        case LIBRARY_EGL:
#ifdef _WIN32
            return "libEGL.dll";
#else
            return "libEGL.so.1";
#endif
        case LIBRARY_GL:
        case LIBRARY_WGL:
#ifdef _WIN32
            return "opengl32.dll";
#else
            return "libGL.so.1";
#endif
        case LIBRARY_ES2:
        case LIBRARY_ES3:
#ifdef _WIN32
            return "libGLESv2.dll";
#else
            return "libGLESv2.so.1";
#endif
        default:
            assert(!"unknown library");
            throw std::runtime_error("Unknown GL library name");
    }
}

void APILoader::loadLibrary(ApiLibrary apiLibrary) {

    std::string libraryName = getLibraryName(apiLibrary);

    if (m_LoadedLibraries.find(libraryName) == m_LoadedLibraries.end()) {
        std::vector<std::string> libSearchPath;

        LoadedLib openGLLibraryHandle = NULL;

        libSearchPath.push_back("");
#ifdef _WIN32
        char buffer[1000];
#ifndef _WIN64
        if (GetSystemWow64Directory(buffer, sizeof(buffer)) > 0) {
            //we are running 32bit app on 64 bit windows
            libSearchPath.push_back(buffer + std::string("\\"));
        }
#endif
        if (!openGLLibraryHandle) {
            if (GetSystemDirectory(buffer, sizeof(buffer)) > 0) {
                //we are running on native system (32 on 32 or 64 on 64)
                libSearchPath.push_back(buffer + std::string("\\"));
            }
        }
#ifndef _WIN64
        libSearchPath.push_back("C:\\Windows\\SysWOW64\\");
#endif
        libSearchPath.push_back("C:\\Windows\\System32\\");
        libSearchPath.push_back(".");
#endif

        for (size_t i = 0; i < libSearchPath.size() && !openGLLibraryHandle; i++) {
#ifdef _WIN32
            openGLLibraryHandle = (LoadedLib)LoadLibrary((libSearchPath[i] + libraryName).c_str());
#else
            openGLLibraryHandle = dlopen((libSearchPath[i] + libraryName).c_str(), RTLD_NOW);
#endif
        }

        if (!openGLLibraryHandle) {
            std::string msg = std::string("Cannot load ") + libraryName + "  system library";
            Os::fatal(msg);
        } else {
            m_LoadedLibraries[libraryName] = openGLLibraryHandle;
        }
    }

    LoadedLib library = m_LoadedLibraries[libraryName];

    //we use MS Detours only on win32, on x64 mHook is used
#ifdef USE_DETOURS
    DetourRestoreAfterWith();
    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
#endif
    //g_DirectPointers is now filled with opengl32.dll pointers
    // we will now detour (hook) them all, so g_DirectPointers will still lead to original opengl32.dll, but
    //application will always call us.
    for (int i = 0; i < Entrypoints_NUM; i++) {
        if (!(g_DirectPointers[i].libraryMask & apiLibrary)) {
            //Do not load - entrypoint does not belong to currently loaded API
            continue;
        }

        if (m_LoadedApiLibraries & g_DirectPointers[i].libraryMask) {
            //Do not load - entrypoint belongs to already loaded API
            continue;
        }

        g_DirectPointers[i].ptr = loadGLPointer(library, i);

        if (g_DirectPointers[i].ptr) {
            //this entrypoint was loaded from OpenGL32.dll, detour it!
            void * hookPtr = getWrapperPointer(i);
#ifdef USE_DETOURS
            DetourAttach(&(PVOID&)g_DirectPointers[i].ptr, hookPtr);
#endif
#ifdef USE_MHOOK
            if (!Mhook_SetHook(&(PVOID&)g_DirectPointers[i].ptr, hookPtr)) {
                std::string error = "Cannot load OpenGL32.dll funcion ";
                error += GetEntryPointName(i);
                error += "().";
                Os::fatal(error);
            }
#endif
        }
    }
#ifdef USE_DETOURS
    DetourTransactionCommit();
#endif
    if (apiLibrary == LIBRARY_EGL || apiLibrary == LIBRARY_WGL)
        m_GlueLibrary = apiLibrary;

    m_LoadedApiLibraries |= apiLibrary;
}

void* APILoader::ensurePointer(Entrypoint entryp) {
    if (g_DirectPointers[entryp].ptr || loadExtPointer(entryp)) {
        return g_DirectPointers[entryp].ptr;
    } else {
        std::string error = "Operation aborted, because the ";
        error += GetEntryPointName(entryp);
        error += " function is not available on current context. Try updating GPU drivers.";
        throw std::runtime_error(error);
    }
}

APILoader g_ApiLoader;
<|endoftext|>


<commit_before>/*
Copyright (c) 2009-2011 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <ctype.h>

#include <boost/unordered_map.hpp>
#include <boost/algorithm/string.hpp>

#include <OpenImageIO/dassert.h>
#include <OpenImageIO/pugixml.hpp>

#include "oslexec_pvt.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif

namespace OSL {
namespace pvt {   // OSL::pvt

namespace pugi = OIIO_NAMESPACE::pugi;

// Helper class to manage the dictionaries.
//
// Shaders are written as if they parse arbitrary things from whole
// cloth on every call: from potentially loading XML from disk, parsing
// it, doing queries, and converting the string data to other types.
//
// But that is expensive, so we really cache all this stuff at several
// levels.
//
// We have parsed xml (as pugi::xml_document *'s) cached in a hash table,
// looked up by the xml and/or dictionary name.  Either will do, if it
// looks like a filename, it will read the XML from the file, otherwise it
// will interpret it as xml directly.
//
// Also, individual queries are cached in a hash table.  The key is a
// tuple of (nodeID, query_string, type_requested), so that asking for a
// particular query to return a string is a totally different cache
// entry than asking for it to be converted to a matrix, say.
//
class Dictionary {
public:
    Dictionary (ShadingSystemImpl &ss) : m_shadingsys(ss) {
        // Create placeholder element 0 == 'not found'
        m_nodes.push_back (Node(0, pugi::xml_node()));
    }
    ~Dictionary () {
        // Free all the documents.
        for (size_t i = 0, e = m_documents.size(); i < e; ++i)
            delete m_documents[i];
    }

    int dict_find (ustring dictionaryname, ustring query);
    int dict_find (int nodeID, ustring query);
    int dict_next (int nodeID);
    int dict_value (int nodeID, ustring attribname, TypeDesc type, void *data);

private:
    // We cache individual queries with a key that is a tuple of the
    // (nodeID, query_string, type_requested).
    struct Query {
        int document;   // which dictionary document
        int node;       // root node for the search
        ustring name;   // name for the the search
        TypeDesc type;  // UNKNOWN signifies a node, versus an attribute value
        Query (int doc_, int node_, ustring name_,
               TypeDesc type_=TypeDesc::UNKNOWN) :
            document(doc_), node(node_), name(name_), type(type_) { }
        bool operator== (const Query &q) const {
            return document == q.document && node == q.node &&
                   name == q.name && type == q.type;
        }
    };

    // Must define a hash operation to build the unordered_map.
    struct QueryHash {
        size_t operator() (const Query &key) const {
            return key.name.hash() + 17*key.node + 79*key.document;
        }
    };

    // The cached query result is mostly just a 'valueoffset', which is
    // the index into floatdata/intdata/stringdata (depending on the type
    // being asked for) at which the decoded data live, or a node ID
    // if the query was for a node rather than for an attribute.
    struct QueryResult {
        int valueoffset;  // Offset into one of the 'data' vectors, or nodeID
        bool is_valid;    // true: query found
        QueryResult (bool valid=true) : valueoffset(0), is_valid(valid) { }
        QueryResult (bool isnode, int value)
            : valueoffset(value), is_valid(true) { }
    };

    // Nodes we've looked up.  Includes a 'next' index of the matching node
    // for the query that generated this one.
    struct Node {
        int document;         // which document the node belongs to
        pugi::xml_node node;  // which node within the dictionary
        int next;             // next node for the same query
        Node (int d, const pugi::xml_node &n)
            : document(d), node(n), next(0) { }
    };

    typedef boost::unordered_map <Query, QueryResult, QueryHash> QueryMap;
    typedef boost::unordered_map<ustring, int, ustringHash> DocMap;

    ShadingSystemImpl &m_shadingsys;  // back-pointer to shading sys

    // List of XML documents we've read in.
    std::vector<pugi::xml_document *> m_documents;

    // Map xml strings and/or filename to indices in m_documents.
    DocMap m_document_map;

    // Cache of fully resolved queries.
    Dictionary::QueryMap m_cache;  // query cache

    // List of all the nodes we've found by queries.
    std::vector<Dictionary::Node> m_nodes;

    // m_floatdata, m_intdata, and m_stringdata hold the decoded data
    // results (including type conversion) of cached queries.
    std::vector<float>   m_floatdata;
    std::vector<int>     m_intdata;
    std::vector<ustring> m_stringdata;

    // Helper function: return the document index given dictionary name.
    int get_document_index (ustring dictionaryname);
};

int
Dictionary::get_document_index (ustring dictionaryname)
{
    DocMap::iterator dm = m_document_map.find(dictionaryname);
    int dindex;
    if (dm == m_document_map.end()) {
        dindex = m_documents.size();
        m_document_map[dictionaryname] = dindex;
        pugi::xml_document *doc = new pugi::xml_document;
        m_documents.push_back (doc);
        pugi::xml_parse_result parse_result;
        if (boost::ends_with (dictionaryname.string(), ".xml")) {
            // xml file -- read it
            parse_result = doc->load_file (dictionaryname.c_str());
        } else {
            // load xml directly from the string
            parse_result = doc->load_buffer (dictionaryname.c_str(),
                                             dictionaryname.length());
        }
        if (! parse_result) {
            m_shadingsys.error ("XML parsed with errors: %s, at offset %d",
                                parse_result.description(),
                                parse_result.offset);
            return 0;
        }
    } else {
        dindex = dm->second;
    }

    DASSERT (dindex >= 0 && dindex < (int)m_documents.size());
    return dindex;
}

int
Dictionary::dict_find (ustring dictionaryname, ustring query)
{
    int dindex = get_document_index (dictionaryname);
    ASSERT (dindex >= 0 && dindex < (int)m_documents.size());

    Query q (dindex, 0, query);
    QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        return qfound->second.valueoffset;
    }

    pugi::xml_document *doc = m_documents[dindex];

    // Query was not found.  Do the expensive lookup and cache it
    pugi::xpath_node_set matches;

    try {
        matches = doc->select_nodes (query.c_str());
    }
    catch (const pugi::xpath_exception& e) {
        m_shadingsys.error ("Invalid dict_find query '%s': %s",
                            query.c_str(), e.what());
        return 0;
    }

    if (matches.empty()) {
        m_cache[q] = QueryResult (false);  // mark invalid
        return 0;   // Not found
    }
    int firstmatch = (int) m_nodes.size();
    int last = -1;
    for (int i = 0, e = (int)matches.size(); i < e;  ++i) {
        m_nodes.push_back (Node (dindex, matches[i].node()));
        int nodeid = (int) m_nodes.size()-1;
        if (last < 0) {
            // If this is the first match, add a cache entry for it
            m_cache[q] = QueryResult (true /* it's a node */, nodeid);
        } else {
            // If this is a subsequent match, set the last match's 'next'
            m_nodes[last].next = nodeid;
        }
        last = nodeid;
    }
    return firstmatch;
}

int
Dictionary::dict_find (int nodeID, ustring query)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID

    const Dictionary::Node &node (m_nodes[nodeID]);
    Query q (node.document, nodeID, query);
    QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        return qfound->second.valueoffset;
    }

    // Query was not found.  Do the expensive lookup and cache it
    pugi::xpath_node_set matches;
    try {
        matches = node.node.select_nodes (query.c_str());
    }
    catch (const pugi::xpath_exception& e) {
        m_shadingsys.error ("Invalid dict_find query '%s': %s",
                            query.c_str(), e.what());
        return 0;
    }

    if (matches.empty()) {
        m_cache[q] = QueryResult (false);  // mark invalid
        return 0;   // Not found
    }
    int firstmatch = (int) m_nodes.size();
    int last = -1;
    for (int i = 0, e = (int)matches.size(); i < e;  ++i) {
        m_nodes.push_back (Node (node.document, matches[i].node()));
        int nodeid = (int) m_nodes.size()-1;
        if (last < 0) {
            // If this is the first match, add a cache entry for it
            m_cache[q] = QueryResult (true /* it's a node */, nodeid);
        } else {
            // If this is a subsequent match, set the last match's 'next'
            m_nodes[last].next = nodeid;
        }
        last = nodeid;
    }
    return firstmatch;
}

int
Dictionary::dict_next (int nodeID)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID
    return m_nodes[nodeID].next;
}

int
Dictionary::dict_value (int nodeID, ustring attribname,
                        TypeDesc type, void *data)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID

    const Dictionary::Node &node (m_nodes[nodeID]);
    Dictionary::Query q (node.document, nodeID, attribname, type);
    Dictionary::QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        // previously found
        int offset = qfound->second.valueoffset;
        int n = type.numelements() * type.aggregate;
        if (type.basetype == TypeDesc::STRING) {
            ASSERT (n == 1 && "no string arrays in XML");
            ((ustring *)data)[0] = m_stringdata[offset];
        }
        if (type.basetype == TypeDesc::INT) {
            for (int i = 0;  i < n;  ++i)
                ((int *)data)[i] = m_intdata[offset++];
            return 1;
        }
        if (type.basetype == TypeDesc::FLOAT) {
            for (int i = 0;  i < n;  ++i)
                ((float *)data)[i] = m_floatdata[offset++];
            return 1;
        }
        return 0;  // Unknown type
    }

    // OK, the entry wasn't in the cache, we need to decode it and cache it.

    const char *val = NULL;
    if (attribname.empty()) {
        val = node.node.value();
    } else {
        for (pugi::xml_attribute_iterator ait = node.node.attributes_begin();
             ait != node.node.attributes_end(); ++ait) {
            if (ait->name() == attribname) {
                val = ait->value();
                break;
            }
        }
    }
    if (val == NULL)
        return 0;   // not found

    Dictionary::QueryResult r (false, 0);
    int n = type.numelements() * type.aggregate;
    if (type.basetype == TypeDesc::STRING && n == 1) {
        r.valueoffset = (int) m_stringdata.size();
        ustring s (val);
        m_stringdata.push_back (s);
        ((ustring *)data)[0] = s;
        m_cache[q] = r;
        return 1;
    }
    if (type.basetype == TypeDesc::INT) {
        r.valueoffset = (int) m_intdata.size();
        for (int i = 0;  i < n;  ++i) {
            int v = (int) strtol (val, (char **)&val, 10);
            while (isspace(*val) || *val == ',')
                ++val;
            m_intdata.push_back (v);
            ((int *)data)[i] = v;
        }
        m_cache[q] = r;
        return 1;
    }
    if (type.basetype == TypeDesc::FLOAT) {
        r.valueoffset = (int) m_floatdata.size();
        for (int i = 0;  i < n;  ++i) {
            float v = (int) strtof (val, (char **)&val);
            while (isspace(*val) || *val == ',')
                ++val;
            m_floatdata.push_back (v);
            ((float *)data)[i] = v;
        }
        m_cache[q] = r;
        return 1;
    }

    // Anything that's left is an unsupported type
    return 0;
}

int
ShadingContext::dict_find (ustring dictionaryname, ustring query)
{
    if (! m_dictionary) {
        m_dictionary = new Dictionary (shadingsys());
    }
    return m_dictionary->dict_find (dictionaryname, query);
}

int
ShadingContext::dict_find (int nodeID, ustring query)
{
    if (! m_dictionary) {
        m_dictionary = new Dictionary (shadingsys());
    }
    return m_dictionary->dict_find (nodeID, query);
}

int
ShadingContext::dict_next (int nodeID)
{
    if (! m_dictionary)
        return 0;
    return m_dictionary->dict_next (nodeID);
}

int
ShadingContext::dict_value (int nodeID, ustring attribname,
                            TypeDesc type, void *data)
{
    if (! m_dictionary)
        return 0;
    return m_dictionary->dict_value (nodeID, attribname, type, data);
}

void
ShadingContext::free_dict_resources ()
{
    delete m_dictionary;
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<commit_msg>Fix XML buglet: not returning the 'success' error code when retrieving cached XML data of type string.<commit_after>/*
Copyright (c) 2009-2011 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
  notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
  notice, this list of conditions and the following disclaimer in the
  documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <ctype.h>

#include <boost/unordered_map.hpp>
#include <boost/algorithm/string.hpp>

#include <OpenImageIO/dassert.h>
#include <OpenImageIO/pugixml.hpp>

#include "oslexec_pvt.h"

#ifdef OSL_NAMESPACE
namespace OSL_NAMESPACE {
#endif

namespace OSL {
namespace pvt {   // OSL::pvt

namespace pugi = OIIO_NAMESPACE::pugi;

// Helper class to manage the dictionaries.
//
// Shaders are written as if they parse arbitrary things from whole
// cloth on every call: from potentially loading XML from disk, parsing
// it, doing queries, and converting the string data to other types.
//
// But that is expensive, so we really cache all this stuff at several
// levels.
//
// We have parsed xml (as pugi::xml_document *'s) cached in a hash table,
// looked up by the xml and/or dictionary name.  Either will do, if it
// looks like a filename, it will read the XML from the file, otherwise it
// will interpret it as xml directly.
//
// Also, individual queries are cached in a hash table.  The key is a
// tuple of (nodeID, query_string, type_requested), so that asking for a
// particular query to return a string is a totally different cache
// entry than asking for it to be converted to a matrix, say.
//
class Dictionary {
public:
    Dictionary (ShadingSystemImpl &ss) : m_shadingsys(ss) {
        // Create placeholder element 0 == 'not found'
        m_nodes.push_back (Node(0, pugi::xml_node()));
    }
    ~Dictionary () {
        // Free all the documents.
        for (size_t i = 0, e = m_documents.size(); i < e; ++i)
            delete m_documents[i];
    }

    int dict_find (ustring dictionaryname, ustring query);
    int dict_find (int nodeID, ustring query);
    int dict_next (int nodeID);
    int dict_value (int nodeID, ustring attribname, TypeDesc type, void *data);

private:
    // We cache individual queries with a key that is a tuple of the
    // (nodeID, query_string, type_requested).
    struct Query {
        int document;   // which dictionary document
        int node;       // root node for the search
        ustring name;   // name for the the search
        TypeDesc type;  // UNKNOWN signifies a node, versus an attribute value
        Query (int doc_, int node_, ustring name_,
               TypeDesc type_=TypeDesc::UNKNOWN) :
            document(doc_), node(node_), name(name_), type(type_) { }
        bool operator== (const Query &q) const {
            return document == q.document && node == q.node &&
                   name == q.name && type == q.type;
        }
    };

    // Must define a hash operation to build the unordered_map.
    struct QueryHash {
        size_t operator() (const Query &key) const {
            return key.name.hash() + 17*key.node + 79*key.document;
        }
    };

    // The cached query result is mostly just a 'valueoffset', which is
    // the index into floatdata/intdata/stringdata (depending on the type
    // being asked for) at which the decoded data live, or a node ID
    // if the query was for a node rather than for an attribute.
    struct QueryResult {
        int valueoffset;  // Offset into one of the 'data' vectors, or nodeID
        bool is_valid;    // true: query found
        QueryResult (bool valid=true) : valueoffset(0), is_valid(valid) { }
        QueryResult (bool isnode, int value)
            : valueoffset(value), is_valid(true) { }
    };

    // Nodes we've looked up.  Includes a 'next' index of the matching node
    // for the query that generated this one.
    struct Node {
        int document;         // which document the node belongs to
        pugi::xml_node node;  // which node within the dictionary
        int next;             // next node for the same query
        Node (int d, const pugi::xml_node &n)
            : document(d), node(n), next(0) { }
    };

    typedef boost::unordered_map <Query, QueryResult, QueryHash> QueryMap;
    typedef boost::unordered_map<ustring, int, ustringHash> DocMap;

    ShadingSystemImpl &m_shadingsys;  // back-pointer to shading sys

    // List of XML documents we've read in.
    std::vector<pugi::xml_document *> m_documents;

    // Map xml strings and/or filename to indices in m_documents.
    DocMap m_document_map;

    // Cache of fully resolved queries.
    Dictionary::QueryMap m_cache;  // query cache

    // List of all the nodes we've found by queries.
    std::vector<Dictionary::Node> m_nodes;

    // m_floatdata, m_intdata, and m_stringdata hold the decoded data
    // results (including type conversion) of cached queries.
    std::vector<float>   m_floatdata;
    std::vector<int>     m_intdata;
    std::vector<ustring> m_stringdata;

    // Helper function: return the document index given dictionary name.
    int get_document_index (ustring dictionaryname);
};

int
Dictionary::get_document_index (ustring dictionaryname)
{
    DocMap::iterator dm = m_document_map.find(dictionaryname);
    int dindex;
    if (dm == m_document_map.end()) {
        dindex = m_documents.size();
        m_document_map[dictionaryname] = dindex;
        pugi::xml_document *doc = new pugi::xml_document;
        m_documents.push_back (doc);
        pugi::xml_parse_result parse_result;
        if (boost::ends_with (dictionaryname.string(), ".xml")) {
            // xml file -- read it
            parse_result = doc->load_file (dictionaryname.c_str());
        } else {
            // load xml directly from the string
            parse_result = doc->load_buffer (dictionaryname.c_str(),
                                             dictionaryname.length());
        }
        if (! parse_result) {
            m_shadingsys.error ("XML parsed with errors: %s, at offset %d",
                                parse_result.description(),
                                parse_result.offset);
            return 0;
        }
    } else {
        dindex = dm->second;
    }

    DASSERT (dindex >= 0 && dindex < (int)m_documents.size());
    return dindex;
}

int
Dictionary::dict_find (ustring dictionaryname, ustring query)
{
    int dindex = get_document_index (dictionaryname);
    ASSERT (dindex >= 0 && dindex < (int)m_documents.size());

    Query q (dindex, 0, query);
    QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        return qfound->second.valueoffset;
    }

    pugi::xml_document *doc = m_documents[dindex];

    // Query was not found.  Do the expensive lookup and cache it
    pugi::xpath_node_set matches;

    try {
        matches = doc->select_nodes (query.c_str());
    }
    catch (const pugi::xpath_exception& e) {
        m_shadingsys.error ("Invalid dict_find query '%s': %s",
                            query.c_str(), e.what());
        return 0;
    }

    if (matches.empty()) {
        m_cache[q] = QueryResult (false);  // mark invalid
        return 0;   // Not found
    }
    int firstmatch = (int) m_nodes.size();
    int last = -1;
    for (int i = 0, e = (int)matches.size(); i < e;  ++i) {
        m_nodes.push_back (Node (dindex, matches[i].node()));
        int nodeid = (int) m_nodes.size()-1;
        if (last < 0) {
            // If this is the first match, add a cache entry for it
            m_cache[q] = QueryResult (true /* it's a node */, nodeid);
        } else {
            // If this is a subsequent match, set the last match's 'next'
            m_nodes[last].next = nodeid;
        }
        last = nodeid;
    }
    return firstmatch;
}

int
Dictionary::dict_find (int nodeID, ustring query)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID

    const Dictionary::Node &node (m_nodes[nodeID]);
    Query q (node.document, nodeID, query);
    QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        return qfound->second.valueoffset;
    }

    // Query was not found.  Do the expensive lookup and cache it
    pugi::xpath_node_set matches;
    try {
        matches = node.node.select_nodes (query.c_str());
    }
    catch (const pugi::xpath_exception& e) {
        m_shadingsys.error ("Invalid dict_find query '%s': %s",
                            query.c_str(), e.what());
        return 0;
    }

    if (matches.empty()) {
        m_cache[q] = QueryResult (false);  // mark invalid
        return 0;   // Not found
    }
    int firstmatch = (int) m_nodes.size();
    int last = -1;
    for (int i = 0, e = (int)matches.size(); i < e;  ++i) {
        m_nodes.push_back (Node (node.document, matches[i].node()));
        int nodeid = (int) m_nodes.size()-1;
        if (last < 0) {
            // If this is the first match, add a cache entry for it
            m_cache[q] = QueryResult (true /* it's a node */, nodeid);
        } else {
            // If this is a subsequent match, set the last match's 'next'
            m_nodes[last].next = nodeid;
        }
        last = nodeid;
    }
    return firstmatch;
}

int
Dictionary::dict_next (int nodeID)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID
    return m_nodes[nodeID].next;
}

int
Dictionary::dict_value (int nodeID, ustring attribname,
                        TypeDesc type, void *data)
{
    if (nodeID <= 0 || nodeID >= (int)m_nodes.size())
        return 0;     // invalid node ID

    const Dictionary::Node &node (m_nodes[nodeID]);
    Dictionary::Query q (node.document, nodeID, attribname, type);
    Dictionary::QueryMap::iterator qfound = m_cache.find (q);
    if (qfound != m_cache.end()) {
        // previously found
        int offset = qfound->second.valueoffset;
        int n = type.numelements() * type.aggregate;
        if (type.basetype == TypeDesc::STRING) {
            ASSERT (n == 1 && "no string arrays in XML");
            ((ustring *)data)[0] = m_stringdata[offset];
            return 1;
        }
        if (type.basetype == TypeDesc::INT) {
            for (int i = 0;  i < n;  ++i)
                ((int *)data)[i] = m_intdata[offset++];
            return 1;
        }
        if (type.basetype == TypeDesc::FLOAT) {
            for (int i = 0;  i < n;  ++i)
                ((float *)data)[i] = m_floatdata[offset++];
            return 1;
        }
        return 0;  // Unknown type
    }

    // OK, the entry wasn't in the cache, we need to decode it and cache it.

    const char *val = NULL;
    if (attribname.empty()) {
        val = node.node.value();
    } else {
        for (pugi::xml_attribute_iterator ait = node.node.attributes_begin();
             ait != node.node.attributes_end(); ++ait) {
            if (ait->name() == attribname) {
                val = ait->value();
                break;
            }
        }
    }
    if (val == NULL)
        return 0;   // not found

    Dictionary::QueryResult r (false, 0);
    int n = type.numelements() * type.aggregate;
    if (type.basetype == TypeDesc::STRING && n == 1) {
        r.valueoffset = (int) m_stringdata.size();
        ustring s (val);
        m_stringdata.push_back (s);
        ((ustring *)data)[0] = s;
        m_cache[q] = r;
        return 1;
    }
    if (type.basetype == TypeDesc::INT) {
        r.valueoffset = (int) m_intdata.size();
        for (int i = 0;  i < n;  ++i) {
            int v = (int) strtol (val, (char **)&val, 10);
            while (isspace(*val) || *val == ',')
                ++val;
            m_intdata.push_back (v);
            ((int *)data)[i] = v;
        }
        m_cache[q] = r;
        return 1;
    }
    if (type.basetype == TypeDesc::FLOAT) {
        r.valueoffset = (int) m_floatdata.size();
        for (int i = 0;  i < n;  ++i) {
            float v = (int) strtof (val, (char **)&val);
            while (isspace(*val) || *val == ',')
                ++val;
            m_floatdata.push_back (v);
            ((float *)data)[i] = v;
        }
        m_cache[q] = r;
        return 1;
    }

    // Anything that's left is an unsupported type
    return 0;
}

int
ShadingContext::dict_find (ustring dictionaryname, ustring query)
{
    if (! m_dictionary) {
        m_dictionary = new Dictionary (shadingsys());
    }
    return m_dictionary->dict_find (dictionaryname, query);
}

int
ShadingContext::dict_find (int nodeID, ustring query)
{
    if (! m_dictionary) {
        m_dictionary = new Dictionary (shadingsys());
    }
    return m_dictionary->dict_find (nodeID, query);
}

int
ShadingContext::dict_next (int nodeID)
{
    if (! m_dictionary)
        return 0;
    return m_dictionary->dict_next (nodeID);
}

int
ShadingContext::dict_value (int nodeID, ustring attribname,
                            TypeDesc type, void *data)
{
    if (! m_dictionary)
        return 0;
    return m_dictionary->dict_value (nodeID, attribname, type, data);
}

void
ShadingContext::free_dict_resources ()
{
    delete m_dictionary;
}

}; // namespace pvt
}; // namespace OSL
#ifdef OSL_NAMESPACE
}; // end namespace OSL_NAMESPACE
#endif
<|endoftext|>