<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "factory.h"
#include "factory_p.h"

#include "backendinterface.h"
#include "medianode_p.h"
#include "audiooutput.h"
#include "audiooutput_p.h"
#include "globalstatic_p.h"
#include "platformplugin.h"
#include "phononnamespace_p.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QLibrary>
#include <QtCore/QList>
#include <QtCore/QPluginLoader>
#include <QtGui/QIcon>
#ifndef QT_NO_DBUS
#include <QtDBus/QtDBus>
#endif

namespace Phonon
{

PHONON_GLOBAL_STATIC(Phonon::FactoryPrivate, globalFactory)

void Factory::setBackend(QObject *b)
{
    Q_ASSERT(globalFactory->m_backendObject == 0);
    globalFactory->m_backendObject = b;
}

/*void Factory::createBackend(const QString &library, const QString &version)
{
    Q_ASSERT(globalFactory->m_backendObject == 0);
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        globalFactory->m_backendObject = f->createBackend(library, version);
    }
}*/

bool FactoryPrivate::createBackend()
{
    Q_ASSERT(m_backendObject == 0);
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        m_backendObject = f->createBackend();
    }
    if (!m_backendObject) {
        // could not load a backend through the platform plugin. Falling back to the default
        // (finding the first loadable backend).
        const QLatin1String suffix("/phonon_backend/");
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            if (!dir.exists()) {
                pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist";
                continue;
            }
            foreach (QString pluginName, dir.entryList(QDir::Files)) {
                QPluginLoader pluginLoader(libPath + pluginName);
                if (!pluginLoader.load()) {
                    pDebug() << Q_FUNC_INFO << "  load failed:"
                             << pluginLoader.errorString();
                    continue;
                }
                pDebug() << pluginLoader.instance();
                m_backendObject = pluginLoader.instance();
                if (m_backendObject) {
                    break;
                }

                // no backend found, don't leave an unused plugin in memory
                pluginLoader.unload();
            }

            if (m_backendObject) {
                break;
            }
        }
        if (!m_backendObject) {
            pDebug() << Q_FUNC_INFO << "phonon backend plugin could not be loaded";
            return false;
        }
    }

    connect(m_backendObject, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)),
            SLOT(objectDescriptionChanged(ObjectDescriptionType)));

    return true;
}

FactoryPrivate::FactoryPrivate()
    : m_backendObject(0),
    m_platformPlugin(0),
    m_noPlatformPlugin(false)
{
    // Add the post routine to make sure that all other global statics (especially the ones from Qt)
    // are still available. If the FactoryPrivate dtor is called too late many bad things can happen
    // as the whole backend might still be alive.
    qAddPostRoutine(globalFactory.destroy);
#ifndef QT_NO_DBUS
    QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.Phonon.Factory",
            "phononBackendChanged", this, SLOT(phononBackendChanged()));
#endif
}

FactoryPrivate::~FactoryPrivate()
{
    foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
        bp->deleteBackendObject();
    }
    if (objects.size() > 0) {
        pError() << "The backend objects are not deleted as was requested.";
        qDeleteAll(objects);
    }
    delete m_backendObject;
    delete m_platformPlugin;
}

void FactoryPrivate::objectDescriptionChanged(ObjectDescriptionType type)
{
#ifdef PHONON_METHODTEST
    Q_UNUSED(type);
#else
    pDebug() << Q_FUNC_INFO << type;
    switch (type) {
    case AudioOutputDeviceType:
        emit availableAudioOutputDevicesChanged();
        break;
    default:
        break;
    }
    //emit capabilitiesChanged();
#endif // PHONON_METHODTEST
}

Factory::Sender *Factory::sender()
{
    return globalFactory;
}

bool Factory::isMimeTypeAvailable(const QString &mimeType)
{
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        return f->isMimeTypeAvailable(mimeType);
    }
    return true; // the MIME type might be supported, let BackendCapabilities find out
}

void Factory::registerFrontendObject(MediaNodePrivate *bp)
{
    globalFactory->mediaNodePrivateList.prepend(bp); // inserted last => deleted first
}

void Factory::deregisterFrontendObject(MediaNodePrivate *bp)
{
    // The Factory can already be cleaned up while there are other frontend objects still alive.
    // When those are deleted they'll call deregisterFrontendObject through ~BasePrivate
    if (!globalFactory.isDestroyed()) {
        globalFactory->mediaNodePrivateList.removeAll(bp);
    }
}

void FactoryPrivate::phononBackendChanged()
{
    if (m_backendObject) {
        foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
            bp->deleteBackendObject();
        }
        if (objects.size() > 0) {
            pDebug() << "WARNING: we were asked to change the backend but the application did\n"
                "not free all references to objects created by the factory. Therefore we can not\n"
                "change the backend without crashing. Now we have to wait for a restart to make\n"
                "backendswitching possible.";
            // in case there were objects deleted give 'em a chance to recreate
            // them now
            foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
                bp->createBackendObject();
            }
            return;
        }
        delete m_backendObject;
        m_backendObject = 0;
    }
    createBackend();
    foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
        bp->createBackendObject();
    }
    emit backendChanged();
}

//X void Factory::freeSoundcardDevices()
//X {
//X     if (globalFactory->backend) {
//X         globalFactory->backend->freeSoundcardDevices();
//X     }
//X }

void FactoryPrivate::objectDestroyed(QObject * obj)
{
    //pDebug() << Q_FUNC_INFO << obj;
    objects.removeAll(obj);
}

#define FACTORY_IMPL(classname) \
QObject *Factory::create ## classname(QObject *parent) \
{ \
    if (backend()) { \
        return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent)); \
    } \
    return 0; \
}
#define FACTORY_IMPL_1ARG(classname) \
QObject *Factory::create ## classname(int arg1, QObject *parent) \
{ \
    if (backend()) { \
        return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent, QList<QVariant>() << arg1)); \
    } \
    return 0; \
}

FACTORY_IMPL(MediaObject)
FACTORY_IMPL_1ARG(Effect)
FACTORY_IMPL(VolumeFaderEffect)
FACTORY_IMPL(AudioOutput)
FACTORY_IMPL(AudioDataOutput)
FACTORY_IMPL(Visualization)
FACTORY_IMPL(VideoDataOutput)
FACTORY_IMPL(VideoWidget)

#undef FACTORY_IMPL

PlatformPlugin *FactoryPrivate::platformPlugin()
{
    if (!m_platformPlugin) {
        if (m_noPlatformPlugin) {
            return 0;
        }
#ifndef QT_NO_DBUS
        if (!QCoreApplication::instance() || QCoreApplication::applicationName().isEmpty()) {
            pWarning() << "Phonon needs QCoreApplication::applicationName to be set to export audio output names through the DBUS interface";
        }
#endif
        const QString suffix(QLatin1String("/phonon_platform"));
        Q_ASSERT(QCoreApplication::instance());
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            if (!dir.exists()) {
                pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist";
                continue;
            }
            QPluginLoader pluginLoader(libPath + QLatin1String("/kde"));
            Q_ASSERT_X(pluginLoader.load(), Q_FUNC_INFO,
                       qPrintable(pluginLoader.errorString()));
            pDebug() << pluginLoader.instance();
            m_platformPlugin = qobject_cast<PlatformPlugin *>(pluginLoader.instance());
            pDebug() << m_platformPlugin;
            if (m_platformPlugin) {
                return m_platformPlugin;
            } else {
                pDebug() << Q_FUNC_INFO << dir.absolutePath() << "exists but the KDE platform plugin was not loadable:" << pluginLoader.errorString();
                pluginLoader.unload();
            }
        }
        if (!m_platformPlugin) {
            pDebug() << Q_FUNC_INFO << "phonon_platform/kde plugin could not be loaded";
            m_noPlatformPlugin = true;
        }
    }
    return m_platformPlugin;
}

PlatformPlugin *Factory::platformPlugin()
{
    return globalFactory->platformPlugin();
}

QObject *Factory::backend(bool createWhenNull)
{
    if (globalFactory.isDestroyed()) {
        return 0;
    }
    if (createWhenNull && globalFactory->m_backendObject == 0) {
        globalFactory->createBackend();
        // XXX: might create "reentrancy" problems:
        // a method calls this method and is called again because the
        // backendChanged signal is emitted
        emit globalFactory->backendChanged();
    }
    return globalFactory->m_backendObject;
}

#define GET_STRING_PROPERTY(name) \
QString Factory::name() \
{ \
    if (globalFactory->m_backendObject) { \
        return globalFactory->m_backendObject->property(#name).toString(); \
    } \
    return QString(); \
} \

GET_STRING_PROPERTY(identifier)
GET_STRING_PROPERTY(backendName)
GET_STRING_PROPERTY(backendComment)
GET_STRING_PROPERTY(backendVersion)
GET_STRING_PROPERTY(backendIcon)
GET_STRING_PROPERTY(backendWebsite)

QObject *Factory::registerQObject(QObject *o)
{
    if (o) {
        QObject::connect(o, SIGNAL(destroyed(QObject *)), globalFactory, SLOT(objectDestroyed(QObject *)), Qt::DirectConnection);
        globalFactory->objects.append(o);
    }
    return o;
}

} //namespace Phonon

#include "moc_factory.cpp"
#include "moc_factory_p.cpp"

// vim: sw=4 ts=4
<commit_msg>- make it a warning instead of debug message if no backend could be loaded (otherwise the first warning a developer will see is that Phonon::createPath has failed - like for the backend plugins: list the platform plugins in the phonon_platform dir instead of hardcoding kde. This will also make it work with QPluginLoader which needs a full path (including the .so)<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2004-2007 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "factory.h"
#include "factory_p.h"

#include "backendinterface.h"
#include "medianode_p.h"
#include "audiooutput.h"
#include "audiooutput_p.h"
#include "globalstatic_p.h"
#include "platformplugin.h"
#include "phononnamespace_p.h"

#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QtCore/QLibrary>
#include <QtCore/QList>
#include <QtCore/QPluginLoader>
#include <QtGui/QIcon>
#ifndef QT_NO_DBUS
#include <QtDBus/QtDBus>
#endif

namespace Phonon
{

PHONON_GLOBAL_STATIC(Phonon::FactoryPrivate, globalFactory)

void Factory::setBackend(QObject *b)
{
    Q_ASSERT(globalFactory->m_backendObject == 0);
    globalFactory->m_backendObject = b;
}

/*void Factory::createBackend(const QString &library, const QString &version)
{
    Q_ASSERT(globalFactory->m_backendObject == 0);
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        globalFactory->m_backendObject = f->createBackend(library, version);
    }
}*/

bool FactoryPrivate::createBackend()
{
    Q_ASSERT(m_backendObject == 0);
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        m_backendObject = f->createBackend();
    }
    if (!m_backendObject) {
        // could not load a backend through the platform plugin. Falling back to the default
        // (finding the first loadable backend).
        const QLatin1String suffix("/phonon_backend/");
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            if (!dir.exists()) {
                pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist";
                continue;
            }
            foreach (QString pluginName, dir.entryList(QDir::Files)) {
                QPluginLoader pluginLoader(libPath + pluginName);
                if (!pluginLoader.load()) {
                    pDebug() << Q_FUNC_INFO << "  load failed:"
                             << pluginLoader.errorString();
                    continue;
                }
                pDebug() << pluginLoader.instance();
                m_backendObject = pluginLoader.instance();
                if (m_backendObject) {
                    break;
                }

                // no backend found, don't leave an unused plugin in memory
                pluginLoader.unload();
            }

            if (m_backendObject) {
                break;
            }
        }
        if (!m_backendObject) {
            pWarning() << Q_FUNC_INFO << "phonon backend plugin could not be loaded";
            return false;
        }
    }

    connect(m_backendObject, SIGNAL(objectDescriptionChanged(ObjectDescriptionType)),
            SLOT(objectDescriptionChanged(ObjectDescriptionType)));

    return true;
}

FactoryPrivate::FactoryPrivate()
    : m_backendObject(0),
    m_platformPlugin(0),
    m_noPlatformPlugin(false)
{
    // Add the post routine to make sure that all other global statics (especially the ones from Qt)
    // are still available. If the FactoryPrivate dtor is called too late many bad things can happen
    // as the whole backend might still be alive.
    qAddPostRoutine(globalFactory.destroy);
#ifndef QT_NO_DBUS
    QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.Phonon.Factory",
            "phononBackendChanged", this, SLOT(phononBackendChanged()));
#endif
}

FactoryPrivate::~FactoryPrivate()
{
    foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
        bp->deleteBackendObject();
    }
    if (objects.size() > 0) {
        pError() << "The backend objects are not deleted as was requested.";
        qDeleteAll(objects);
    }
    delete m_backendObject;
    delete m_platformPlugin;
}

void FactoryPrivate::objectDescriptionChanged(ObjectDescriptionType type)
{
#ifdef PHONON_METHODTEST
    Q_UNUSED(type);
#else
    pDebug() << Q_FUNC_INFO << type;
    switch (type) {
    case AudioOutputDeviceType:
        emit availableAudioOutputDevicesChanged();
        break;
    default:
        break;
    }
    //emit capabilitiesChanged();
#endif // PHONON_METHODTEST
}

Factory::Sender *Factory::sender()
{
    return globalFactory;
}

bool Factory::isMimeTypeAvailable(const QString &mimeType)
{
    PlatformPlugin *f = globalFactory->platformPlugin();
    if (f) {
        return f->isMimeTypeAvailable(mimeType);
    }
    return true; // the MIME type might be supported, let BackendCapabilities find out
}

void Factory::registerFrontendObject(MediaNodePrivate *bp)
{
    globalFactory->mediaNodePrivateList.prepend(bp); // inserted last => deleted first
}

void Factory::deregisterFrontendObject(MediaNodePrivate *bp)
{
    // The Factory can already be cleaned up while there are other frontend objects still alive.
    // When those are deleted they'll call deregisterFrontendObject through ~BasePrivate
    if (!globalFactory.isDestroyed()) {
        globalFactory->mediaNodePrivateList.removeAll(bp);
    }
}

void FactoryPrivate::phononBackendChanged()
{
    if (m_backendObject) {
        foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
            bp->deleteBackendObject();
        }
        if (objects.size() > 0) {
            pDebug() << "WARNING: we were asked to change the backend but the application did\n"
                "not free all references to objects created by the factory. Therefore we can not\n"
                "change the backend without crashing. Now we have to wait for a restart to make\n"
                "backendswitching possible.";
            // in case there were objects deleted give 'em a chance to recreate
            // them now
            foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
                bp->createBackendObject();
            }
            return;
        }
        delete m_backendObject;
        m_backendObject = 0;
    }
    createBackend();
    foreach (MediaNodePrivate *bp, mediaNodePrivateList) {
        bp->createBackendObject();
    }
    emit backendChanged();
}

//X void Factory::freeSoundcardDevices()
//X {
//X     if (globalFactory->backend) {
//X         globalFactory->backend->freeSoundcardDevices();
//X     }
//X }

void FactoryPrivate::objectDestroyed(QObject * obj)
{
    //pDebug() << Q_FUNC_INFO << obj;
    objects.removeAll(obj);
}

#define FACTORY_IMPL(classname) \
QObject *Factory::create ## classname(QObject *parent) \
{ \
    if (backend()) { \
        return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent)); \
    } \
    return 0; \
}
#define FACTORY_IMPL_1ARG(classname) \
QObject *Factory::create ## classname(int arg1, QObject *parent) \
{ \
    if (backend()) { \
        return registerQObject(qobject_cast<BackendInterface *>(backend())->createObject(BackendInterface::classname##Class, parent, QList<QVariant>() << arg1)); \
    } \
    return 0; \
}

FACTORY_IMPL(MediaObject)
FACTORY_IMPL_1ARG(Effect)
FACTORY_IMPL(VolumeFaderEffect)
FACTORY_IMPL(AudioOutput)
FACTORY_IMPL(AudioDataOutput)
FACTORY_IMPL(Visualization)
FACTORY_IMPL(VideoDataOutput)
FACTORY_IMPL(VideoWidget)

#undef FACTORY_IMPL

PlatformPlugin *FactoryPrivate::platformPlugin()
{
    if (!m_platformPlugin) {
        if (m_noPlatformPlugin) {
            return 0;
        }
#ifndef QT_NO_DBUS
        if (!QCoreApplication::instance() || QCoreApplication::applicationName().isEmpty()) {
            pWarning() << "Phonon needs QCoreApplication::applicationName to be set to export audio output names through the DBUS interface";
        }
#endif
        const QString suffix(QLatin1String("/phonon_platform"));
        Q_ASSERT(QCoreApplication::instance());
        foreach (QString libPath, QCoreApplication::libraryPaths()) {
            libPath += suffix;
            const QDir dir(libPath);
            if (!dir.exists()) {
                pDebug() << Q_FUNC_INFO << dir.absolutePath() << "does not exist";
                continue;
            }
            foreach (const QString &pluginName, dir.entryList(QDir::Files)) {
                QPluginLoader pluginLoader(libPath + pluginName);
                if (!pluginLoader.load()) {
                    pDebug() << Q_FUNC_INFO << "  platform plugin load failed:"
                        << pluginLoader.errorString();
                    continue;
                }
                pDebug() << pluginLoader.instance();
                m_platformPlugin = qobject_cast<PlatformPlugin *>(pluginLoader.instance());
                pDebug() << m_platformPlugin;
                if (m_platformPlugin) {
                    return m_platformPlugin;
                } else {
                    pDebug() << Q_FUNC_INFO << dir.absolutePath() << "exists but the KDE platform plugin was not loadable:" << pluginLoader.errorString();
                    pluginLoader.unload();
                }
            }
        }
        if (!m_platformPlugin) {
            pDebug() << Q_FUNC_INFO << "phonon_platform/kde plugin could not be loaded";
            m_noPlatformPlugin = true;
        }
    }
    return m_platformPlugin;
}

PlatformPlugin *Factory::platformPlugin()
{
    return globalFactory->platformPlugin();
}

QObject *Factory::backend(bool createWhenNull)
{
    if (globalFactory.isDestroyed()) {
        return 0;
    }
    if (createWhenNull && globalFactory->m_backendObject == 0) {
        globalFactory->createBackend();
        // XXX: might create "reentrancy" problems:
        // a method calls this method and is called again because the
        // backendChanged signal is emitted
        emit globalFactory->backendChanged();
    }
    return globalFactory->m_backendObject;
}

#define GET_STRING_PROPERTY(name) \
QString Factory::name() \
{ \
    if (globalFactory->m_backendObject) { \
        return globalFactory->m_backendObject->property(#name).toString(); \
    } \
    return QString(); \
} \

GET_STRING_PROPERTY(identifier)
GET_STRING_PROPERTY(backendName)
GET_STRING_PROPERTY(backendComment)
GET_STRING_PROPERTY(backendVersion)
GET_STRING_PROPERTY(backendIcon)
GET_STRING_PROPERTY(backendWebsite)

QObject *Factory::registerQObject(QObject *o)
{
    if (o) {
        QObject::connect(o, SIGNAL(destroyed(QObject *)), globalFactory, SLOT(objectDestroyed(QObject *)), Qt::DirectConnection);
        globalFactory->objects.append(o);
    }
    return o;
}

} //namespace Phonon

#include "moc_factory.cpp"
#include "moc_factory_p.cpp"

// vim: sw=4 ts=4
<|endoftext|>


<commit_before>/*  This file is part of the KDE project
    Copyright (C) 2004-2006 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "factory.h"
#include "base_p.h"

#include <kservicetypetrader.h>
#include <klibloader.h>
#include <kmessagebox.h>
#include <QFile>
#include <QList>
#include <klocale.h>
#include <kmimetype.h>
#include <kdebug.h>
#include <kstaticdeleter.h>

#include <QtDBus/QtDBus>

static KStaticDeleter<Phonon::Factory> sd;

#define PHONON_LOAD_BACKEND_GLOBAL 1

namespace Phonon
{
class Factory::Private
{
	public:
		Private()
			: backend( 0 )
		{
			qRegisterMetaType<qint64>( "qint64" );
			qRegisterMetaType<qint32>( "qint32" );
		}

		void createBackend()
		{
			const KService::List offers = KServiceTypeTrader::self()->query( "PhononBackend",
					"Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1" );
			KService::List::const_iterator it = offers.begin();
			const KService::List::const_iterator end = offers.end();
			QStringList errormsg;
			for( ; it != end; ++it )
			{
				KService::Ptr ptr = *it;
				KLibFactory* factory = 0;
#ifdef PHONON_LOAD_BACKEND_GLOBAL
				// This code is in here temporarily until NMM gets fixed.
				// Currently the NMM backend will fail with undefined symbols if
				// the backend is not loaded with global symbol resolution
				KLibrary* library = KLibLoader::self()->globalLibrary( QFile::encodeName( ptr->library() ) );
				if( library )
					factory = library->factory();
#else
				factory = KLibLoader::self()->factory( QFile::encodeName( ptr->library() ) );
#endif
				if( factory )
				{
					backend = factory->create();
					if( 0 == backend )
					{
						QString e = i18n( "create method returned 0" );
						errormsg.append( e );
						kDebug( 600 ) << "Error getting backend from factory for " <<
							ptr->name() << ", " << ptr->library() << ":\n" << e << endl;
					}
					else
					{
						service = ptr;
						kDebug( 600 ) << "using backend: " << ptr->name() << endl;
						break;
					}
				}
				else
				{
					QString e = KLibLoader::self()->lastErrorMessage();
					errormsg.append( e );
					kDebug( 600 ) << "Error getting factory for " << ptr->name() <<
						":\n" << e << endl;
				}
			}
			if( 0 == backend )
			{
				if( offers.size() == 0 )
					KMessageBox::error( 0, i18n( "Unable to find a Multimedia Backend" ) );
				else
				{
					QString details = "<qt>
";
					QStringList::Iterator eit = errormsg.begin();
					QStringList::Iterator eend = errormsg.end();
					KService::List::const_iterator oit = offers.begin();
					const KService::List::const_iterator oend = offers.end();
					for( ; eit != eend || oit != oend; ++eit, ++oit )
						details += QString( "
%1	%2
" )
							.arg( ( *oit )->name() ).arg( *eit );
					details += "
</qt>";

					KMessageBox::detailedError( 0,
							i18n( "Unable to use any of the available Multimedia Backends" ), details );
				}
			}
		}

		QObject* backend;
		KService::Ptr service;

		QList<QObject*> objects;
		QList<BasePrivate*> basePrivateList;
};

Factory * Factory::m_self = 0;

Factory * Factory::self()
{
	if( ! m_self )
	{
		m_self = new Factory();
		::sd.setObject( m_self, m_self );
	}
	return m_self;
}

Factory::Factory()
	: d( new Private )
{
	QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.Phonon.Factory",
			"phononBackendChanged", this, SLOT(phononBackendChanged()));
}

Factory::~Factory()
{
	//kDebug( 600 ) << k_funcinfo << endl;
	emit aboutToBeDestroyed();

	foreach( BasePrivate* bp, d->basePrivateList )
		bp->deleteIface();
	qDeleteAll(d->objects);
	delete d->backend;
	delete d;
}

void Factory::registerFrontendObject( BasePrivate* bp )
{
	d->basePrivateList.append( bp );
}

void Factory::deregisterFrontendObject( BasePrivate* bp )
{
	d->basePrivateList.removeAll( bp );
}

void Factory::phononBackendChanged()
{
	if( d->backend )
	{
		foreach( BasePrivate* bp, d->basePrivateList )
			bp->deleteIface();
		if( d->objects.size() > 0 )
		{
			kWarning( 600 ) << "we were asked to change the backend but the application did\n"
				"not free all references to objects created by the factory. Therefore we can not\n"
				"change the backend without crashing. Now we have to wait for a restart to make\n"
				"backendswitching possible." << endl;
			// in case there were objects deleted give 'em a chance to recreate
			// them now
			foreach( BasePrivate* bp, d->basePrivateList )
				bp->createIface();
			return;
		}
		delete d->backend;
		d->backend = 0;
	}
	d->createBackend();
	foreach( BasePrivate* bp, d->basePrivateList )
		bp->createIface();
	emit backendChanged();
}

//X void Factory::freeSoundcardDevices()
//X {
//X 	if( d->backend )
//X 	{
//X 		d->backend->freeSoundcardDevices();
//X 	}
//X }

void Factory::objectDestroyed( QObject * obj )
{
	//kDebug( 600 ) << k_funcinfo << obj << endl;
	d->objects.removeAll( obj );
}

#define FACTORY_IMPL( classname ) \
QObject* Factory::create ## classname( QObject* parent ) \
{ \
	if( backend() ) \
	{ \
		QObject* ret; \
		if( QMetaObject::invokeMethod( d->backend, "create"#classname, Qt::DirectConnection, Q_RETURN_ARG( QObject*, ret ), Q_ARG( QObject*, parent ) ) ) \
			return registerQObject( ret ); \
	} \
	return 0; \
}
#define FACTORY_IMPL_1ARG( type1, classname ) \
QObject* Factory::create ## classname( type1 name1, QObject* parent ) \
{ \
	if( backend() ) \
	{ \
		QObject* ret; \
		if( QMetaObject::invokeMethod( d->backend, "create"#classname, Qt::DirectConnection, Q_RETURN_ARG( QObject*, ret ), Q_ARG( type1, name1 ), Q_ARG( QObject*, parent ) ) ) \
			return registerQObject( ret ); \
	} \
	return 0; \
}

FACTORY_IMPL( MediaObject )
FACTORY_IMPL( MediaQueue )
FACTORY_IMPL( AvCapture )
FACTORY_IMPL( ByteStream )
FACTORY_IMPL( AudioPath )
FACTORY_IMPL_1ARG( int, AudioEffect )
FACTORY_IMPL( VolumeFaderEffect )
FACTORY_IMPL( AudioOutput )
FACTORY_IMPL( AudioDataOutput )
FACTORY_IMPL( Visualization )
FACTORY_IMPL( VideoPath )
FACTORY_IMPL_1ARG( int, VideoEffect )
FACTORY_IMPL( BrightnessControl )
FACTORY_IMPL( VideoDataOutput )

#undef FACTORY_IMPL

QObject* Factory::backend( bool createWhenNull )
{
	if( createWhenNull && d->backend == 0 )
	{
		d->createBackend();
		// XXX: might create "reentrancy" problems:
		// a method calls this method and is called again because the
		// backendChanged signal is emitted
		emit backendChanged();
	}
	return d->backend;
}

const char* Factory::uiLibrary()
{
	if( !backend() )
		return 0;
	const char* ret = 0;
	QMetaObject::invokeMethod( d->backend, "uiLibrary", Qt::DirectConnection, Q_RETURN_ARG( const char*, ret ) );
	return ret;
}

const char* Factory::uiSymbol()
{
	if( !backend() )
		return 0;
	const char* ret = 0;
	// the backend doesn't have to implement the symbol - the default factory
	// symbol will be used then
	if( QMetaObject::invokeMethod( d->backend, "uiSymbol", Qt::DirectConnection, Q_RETURN_ARG( const char*, ret ) ) )
		return ret;
	return 0;
}

QString Factory::backendName() const
{
	if( d->service )
		return d->service->name();
	else
		return QString();
}

QString Factory::backendComment() const
{
	if( d->service )
		return d->service->comment();
	else
		return QString();
}

QString Factory::backendVersion() const
{
	if( d->service )
		return d->service->property( "X-KDE-PhononBackendInfo-Version" ).toString();
	else
		return QString();
}

QString Factory::backendIcon() const
{
	if( d->service )
		return d->service->icon();
	else
		return QString();
}

QString Factory::backendWebsite() const
{
	if( d->service )
		return d->service->property( "X-KDE-PhononBackendInfo-Website" ).toString();
	else
		return QString();
}

QObject* Factory::registerQObject( QObject* o )
{
	connect( o, SIGNAL( destroyed( QObject* ) ), SLOT( objectDestroyed( QObject* ) ), Qt::DirectConnection );
	d->objects.append( o );
	return o;
}

} //namespace Phonon

#include "factory.moc"

// vim: sw=4 ts=4 noet
<commit_msg>use new KLibLoader API<commit_after>/*  This file is part of the KDE project
    Copyright (C) 2004-2006 Matthias Kretz <kretz@kde.org>

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License version 2 as published by the Free Software Foundation.

    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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

*/

#include "factory.h"
#include "base_p.h"

#include <kservicetypetrader.h>
#include <klibloader.h>
#include <kmessagebox.h>
#include <QFile>
#include <QList>
#include <klocale.h>
#include <kmimetype.h>
#include <kdebug.h>
#include <kstaticdeleter.h>

#include <QtDBus/QtDBus>

static KStaticDeleter<Phonon::Factory> sd;

#define PHONON_LOAD_BACKEND_GLOBAL 1

namespace Phonon
{
class Factory::Private
{
	public:
		Private()
			: backend( 0 )
		{
			qRegisterMetaType<qint64>( "qint64" );
			qRegisterMetaType<qint32>( "qint32" );
		}

		void createBackend()
		{
			const KService::List offers = KServiceTypeTrader::self()->query( "PhononBackend",
					"Type == 'Service' and [X-KDE-PhononBackendInfo-InterfaceVersion] == 1" );
			KService::List::const_iterator it = offers.begin();
			const KService::List::const_iterator end = offers.end();
			QStringList errormsg;
			for( ; it != end; ++it )
			{
				KService::Ptr ptr = *it;
				KLibFactory* factory = 0;
#ifdef PHONON_LOAD_BACKEND_GLOBAL
				// This code is in here temporarily until NMM gets fixed.
				// Currently the NMM backend will fail with undefined symbols if
				// the backend is not loaded with global symbol resolution
				KLibrary* library = KLibLoader::self()->library( QFile::encodeName( ptr->library() ), QLibrary::ExportExternalSymbolsHint );
				if( library )
					factory = library->factory();
#else
				factory = KLibLoader::self()->factory( QFile::encodeName( ptr->library() ) );
#endif
				if( factory )
				{
					backend = factory->create();
					if( 0 == backend )
					{
						QString e = i18n( "create method returned 0" );
						errormsg.append( e );
						kDebug( 600 ) << "Error getting backend from factory for " <<
							ptr->name() << ", " << ptr->library() << ":\n" << e << endl;
					}
					else
					{
						service = ptr;
						kDebug( 600 ) << "using backend: " << ptr->name() << endl;
						break;
					}
				}
				else
				{
					QString e = KLibLoader::self()->lastErrorMessage();
					errormsg.append( e );
					kDebug( 600 ) << "Error getting factory for " << ptr->name() <<
						":\n" << e << endl;
				}
			}
			if( 0 == backend )
			{
				if( offers.size() == 0 )
					KMessageBox::error( 0, i18n( "Unable to find a Multimedia Backend" ) );
				else
				{
					QString details = "<qt>
";
					QStringList::Iterator eit = errormsg.begin();
					QStringList::Iterator eend = errormsg.end();
					KService::List::const_iterator oit = offers.begin();
					const KService::List::const_iterator oend = offers.end();
					for( ; eit != eend || oit != oend; ++eit, ++oit )
						details += QString( "
%1	%2
" )
							.arg( ( *oit )->name() ).arg( *eit );
					details += "
</qt>";

					KMessageBox::detailedError( 0,
							i18n( "Unable to use any of the available Multimedia Backends" ), details );
				}
			}
		}

		QObject* backend;
		KService::Ptr service;

		QList<QObject*> objects;
		QList<BasePrivate*> basePrivateList;
};

Factory * Factory::m_self = 0;

Factory * Factory::self()
{
	if( ! m_self )
	{
		m_self = new Factory();
		::sd.setObject( m_self, m_self );
	}
	return m_self;
}

Factory::Factory()
	: d( new Private )
{
	QDBusConnection::sessionBus().connect(QString(), QString(), "org.kde.Phonon.Factory",
			"phononBackendChanged", this, SLOT(phononBackendChanged()));
}

Factory::~Factory()
{
	//kDebug( 600 ) << k_funcinfo << endl;
	emit aboutToBeDestroyed();

	foreach( BasePrivate* bp, d->basePrivateList )
		bp->deleteIface();
	qDeleteAll(d->objects);
	delete d->backend;
	delete d;
}

void Factory::registerFrontendObject( BasePrivate* bp )
{
	d->basePrivateList.append( bp );
}

void Factory::deregisterFrontendObject( BasePrivate* bp )
{
	d->basePrivateList.removeAll( bp );
}

void Factory::phononBackendChanged()
{
	if( d->backend )
	{
		foreach( BasePrivate* bp, d->basePrivateList )
			bp->deleteIface();
		if( d->objects.size() > 0 )
		{
			kWarning( 600 ) << "we were asked to change the backend but the application did\n"
				"not free all references to objects created by the factory. Therefore we can not\n"
				"change the backend without crashing. Now we have to wait for a restart to make\n"
				"backendswitching possible." << endl;
			// in case there were objects deleted give 'em a chance to recreate
			// them now
			foreach( BasePrivate* bp, d->basePrivateList )
				bp->createIface();
			return;
		}
		delete d->backend;
		d->backend = 0;
	}
	d->createBackend();
	foreach( BasePrivate* bp, d->basePrivateList )
		bp->createIface();
	emit backendChanged();
}

//X void Factory::freeSoundcardDevices()
//X {
//X 	if( d->backend )
//X 	{
//X 		d->backend->freeSoundcardDevices();
//X 	}
//X }

void Factory::objectDestroyed( QObject * obj )
{
	//kDebug( 600 ) << k_funcinfo << obj << endl;
	d->objects.removeAll( obj );
}

#define FACTORY_IMPL( classname ) \
QObject* Factory::create ## classname( QObject* parent ) \
{ \
	if( backend() ) \
	{ \
		QObject* ret; \
		if( QMetaObject::invokeMethod( d->backend, "create"#classname, Qt::DirectConnection, Q_RETURN_ARG( QObject*, ret ), Q_ARG( QObject*, parent ) ) ) \
			return registerQObject( ret ); \
	} \
	return 0; \
}
#define FACTORY_IMPL_1ARG( type1, classname ) \
QObject* Factory::create ## classname( type1 name1, QObject* parent ) \
{ \
	if( backend() ) \
	{ \
		QObject* ret; \
		if( QMetaObject::invokeMethod( d->backend, "create"#classname, Qt::DirectConnection, Q_RETURN_ARG( QObject*, ret ), Q_ARG( type1, name1 ), Q_ARG( QObject*, parent ) ) ) \
			return registerQObject( ret ); \
	} \
	return 0; \
}

FACTORY_IMPL( MediaObject )
FACTORY_IMPL( MediaQueue )
FACTORY_IMPL( AvCapture )
FACTORY_IMPL( ByteStream )
FACTORY_IMPL( AudioPath )
FACTORY_IMPL_1ARG( int, AudioEffect )
FACTORY_IMPL( VolumeFaderEffect )
FACTORY_IMPL( AudioOutput )
FACTORY_IMPL( AudioDataOutput )
FACTORY_IMPL( Visualization )
FACTORY_IMPL( VideoPath )
FACTORY_IMPL_1ARG( int, VideoEffect )
FACTORY_IMPL( BrightnessControl )
FACTORY_IMPL( VideoDataOutput )

#undef FACTORY_IMPL

QObject* Factory::backend( bool createWhenNull )
{
	if( createWhenNull && d->backend == 0 )
	{
		d->createBackend();
		// XXX: might create "reentrancy" problems:
		// a method calls this method and is called again because the
		// backendChanged signal is emitted
		emit backendChanged();
	}
	return d->backend;
}

const char* Factory::uiLibrary()
{
	if( !backend() )
		return 0;
	const char* ret = 0;
	QMetaObject::invokeMethod( d->backend, "uiLibrary", Qt::DirectConnection, Q_RETURN_ARG( const char*, ret ) );
	return ret;
}

const char* Factory::uiSymbol()
{
	if( !backend() )
		return 0;
	const char* ret = 0;
	// the backend doesn't have to implement the symbol - the default factory
	// symbol will be used then
	if( QMetaObject::invokeMethod( d->backend, "uiSymbol", Qt::DirectConnection, Q_RETURN_ARG( const char*, ret ) ) )
		return ret;
	return 0;
}

QString Factory::backendName() const
{
	if( d->service )
		return d->service->name();
	else
		return QString();
}

QString Factory::backendComment() const
{
	if( d->service )
		return d->service->comment();
	else
		return QString();
}

QString Factory::backendVersion() const
{
	if( d->service )
		return d->service->property( "X-KDE-PhononBackendInfo-Version" ).toString();
	else
		return QString();
}

QString Factory::backendIcon() const
{
	if( d->service )
		return d->service->icon();
	else
		return QString();
}

QString Factory::backendWebsite() const
{
	if( d->service )
		return d->service->property( "X-KDE-PhononBackendInfo-Website" ).toString();
	else
		return QString();
}

QObject* Factory::registerQObject( QObject* o )
{
	connect( o, SIGNAL( destroyed( QObject* ) ), SLOT( objectDestroyed( QObject* ) ), Qt::DirectConnection );
	d->objects.append( o );
	return o;
}

} //namespace Phonon

#include "factory.moc"

// vim: sw=4 ts=4 noet
<|endoftext|>


<commit_before>#include "tensor_io.h"

#include <fstream>
#include <algorithm>

using namespace smurff;

TensorConfig tensor_io::read_dense_float64(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_dense_float64(fileStream);
}

TensorConfig tensor_io::read_dense_float64(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz = std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<std::uint64_t>());
   std::vector<double> values(nnz);
   in.read(reinterpret_cast<char*>(values.data()), values.size() * sizeof(double));

   return TensorConfig(std::move(dims), std::move(values), NoiseConfig());
}

TensorConfig tensor_io::read_sparse_float64(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_sparse_float64(fileStream);
}

TensorConfig tensor_io::read_sparse_float64(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz;
   in.read(reinterpret_cast<char*>(&nnz), sizeof(std::uint64_t));

   std::vector<std::uint32_t> columns(nmodes * nnz);

   for (std::uint64_t i = 0; i < nmodes; i++)
   {
      std::uint64_t dataOffset = i * nnz;
      in.read(reinterpret_cast<char*>(columns.data() + dataOffset), nnz * sizeof(std::uint32_t));
   }

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col--; });

   std::vector<double> values(nnz);
   in.read(reinterpret_cast<char*>(values.data()), values.size() * sizeof(double));

   return TensorConfig(std::move(dims), std::move(columns), std::move(values), NoiseConfig());
}

TensorConfig tensor_io::read_sparse_binary(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_sparse_binary(fileStream);
}

TensorConfig tensor_io::read_sparse_binary(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<std::uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz;
   in.read(reinterpret_cast<char*>(&nnz), sizeof(std::uint64_t));

   std::vector<std::uint32_t> columns(nmodes * nnz);

   for (std::uint64_t i = 0; i < nmodes; i++)
   {
      std::uint64_t dataOffset = i * nnz;
      in.read(reinterpret_cast<char*>(columns.data() + dataOffset), nnz * sizeof(std::uint32_t));
   }

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col--; });

   return TensorConfig(std::move(dims), std::move(columns), NoiseConfig());
}

void tensor_io::write_dense_float64(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_dense_float64(fileStream, tensorConfig);
}

void tensor_io::write_dense_float64(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   const std::vector<double>& values = tensorConfig.getValues();

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(values.data()), values.size() * sizeof(double));
}

void tensor_io::write_sparse_float64(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_sparse_float64(fileStream, tensorConfig);
}

void tensor_io::write_sparse_float64(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   std::uint64_t nnz = tensorConfig.getNNZ();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   std::vector<std::uint32_t> columns = tensorConfig.getColumns();
   const std::vector<double>& values = tensorConfig.getValues();

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col++; });

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(&nnz), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(columns.data()), columns.size() * sizeof(std::uint32_t));
   out.write(reinterpret_cast<const char*>(values.data()), values.size() * sizeof(double));
}

void tensor_io::write_sparse_binary(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_sparse_binary(fileStream, tensorConfig);
}

void tensor_io::write_sparse_binary(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   std::uint64_t nnz = tensorConfig.getNNZ();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   std::vector<std::uint32_t> columns = tensorConfig.getColumns();

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col++; });

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(&nnz), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(columns.data()), columns.size() * sizeof(std::uint32_t));
}<commit_msg>missing include<commit_after>#include "tensor_io.h"

#include <fstream>
#include <algorithm>
#include <numeric>

using namespace smurff;

TensorConfig tensor_io::read_dense_float64(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_dense_float64(fileStream);
}

TensorConfig tensor_io::read_dense_float64(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz = std::accumulate(dims.begin(), dims.end(), 1, std::multiplies<std::uint64_t>());
   std::vector<double> values(nnz);
   in.read(reinterpret_cast<char*>(values.data()), values.size() * sizeof(double));

   return TensorConfig(std::move(dims), std::move(values), NoiseConfig());
}

TensorConfig tensor_io::read_sparse_float64(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_sparse_float64(fileStream);
}

TensorConfig tensor_io::read_sparse_float64(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz;
   in.read(reinterpret_cast<char*>(&nnz), sizeof(std::uint64_t));

   std::vector<std::uint32_t> columns(nmodes * nnz);

   for (std::uint64_t i = 0; i < nmodes; i++)
   {
      std::uint64_t dataOffset = i * nnz;
      in.read(reinterpret_cast<char*>(columns.data() + dataOffset), nnz * sizeof(std::uint32_t));
   }

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col--; });

   std::vector<double> values(nnz);
   in.read(reinterpret_cast<char*>(values.data()), values.size() * sizeof(double));

   return TensorConfig(std::move(dims), std::move(columns), std::move(values), NoiseConfig());
}

TensorConfig tensor_io::read_sparse_binary(const std::string& filename)
{
   std::ifstream fileStream(filename, std::ios_base::binary);
   return tensor_io::read_sparse_binary(fileStream);
}

TensorConfig tensor_io::read_sparse_binary(std::istream& in)
{
   std::uint64_t nmodes;
   in.read(reinterpret_cast<char*>(&nmodes), sizeof(std::uint64_t));

   std::vector<std::uint64_t> dims(nmodes);
   in.read(reinterpret_cast<char*>(dims.data()), dims.size() * sizeof(std::uint64_t));

   std::uint64_t nnz;
   in.read(reinterpret_cast<char*>(&nnz), sizeof(std::uint64_t));

   std::vector<std::uint32_t> columns(nmodes * nnz);

   for (std::uint64_t i = 0; i < nmodes; i++)
   {
      std::uint64_t dataOffset = i * nnz;
      in.read(reinterpret_cast<char*>(columns.data() + dataOffset), nnz * sizeof(std::uint32_t));
   }

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col--; });

   return TensorConfig(std::move(dims), std::move(columns), NoiseConfig());
}

void tensor_io::write_dense_float64(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_dense_float64(fileStream, tensorConfig);
}

void tensor_io::write_dense_float64(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   const std::vector<double>& values = tensorConfig.getValues();

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(values.data()), values.size() * sizeof(double));
}

void tensor_io::write_sparse_float64(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_sparse_float64(fileStream, tensorConfig);
}

void tensor_io::write_sparse_float64(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   std::uint64_t nnz = tensorConfig.getNNZ();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   std::vector<std::uint32_t> columns = tensorConfig.getColumns();
   const std::vector<double>& values = tensorConfig.getValues();

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col++; });

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(&nnz), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(columns.data()), columns.size() * sizeof(std::uint32_t));
   out.write(reinterpret_cast<const char*>(values.data()), values.size() * sizeof(double));
}

void tensor_io::write_sparse_binary(const std::string& filename, const TensorConfig& tensorConfig)
{
   std::ofstream fileStream(filename, std::ios_base::binary);
   tensor_io::write_sparse_binary(fileStream, tensorConfig);
}

void tensor_io::write_sparse_binary(std::ostream& out, const TensorConfig& tensorConfig)
{
   std::uint64_t nmodes = tensorConfig.getNModes();
   std::uint64_t nnz = tensorConfig.getNNZ();
   const std::vector<std::uint64_t>& dims = tensorConfig.getDims();
   std::vector<std::uint32_t> columns = tensorConfig.getColumns();

   std::for_each(columns.begin(), columns.end(), [](std::uint32_t& col){ col++; });

   out.write(reinterpret_cast<const char*>(&nmodes), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(dims.data()), dims.size() * sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(&nnz), sizeof(std::uint64_t));
   out.write(reinterpret_cast<const char*>(columns.data()), columns.size() * sizeof(std::uint32_t));
}
<|endoftext|>


<commit_before>#include "zip.h"

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

#include <miniz/miniz.h>

#include "../leanify.h"
#include "../utils.h"

using std::cerr;
using std::endl;
using std::string;
using std::vector;

const uint8_t Zip::header_magic[] = { 0x50, 0x4B, 0x03, 0x04 };

namespace {

PACK(struct CDHeader {
  uint8_t magic[4] = { 0x50, 0x4B, 0x01, 0x02 };
  uint16_t version_made_by;
  uint16_t version_needed;
  uint16_t flag;
  uint16_t compression_method;
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  uint32_t crc32;
  uint32_t compressed_size;
  uint32_t uncompressed_size;
  uint16_t filename_len;
  uint16_t extra_field_len;
  uint16_t comment_len;
  uint16_t disk_file_start;
  uint16_t internal_file_attributes;
  uint32_t external_file_attributes;
  uint32_t local_header_offset;
});

PACK(struct EOCD {
  uint8_t magic[4] = { 0x50, 0x4B, 0x05, 0x06 };
  uint16_t disk_num;
  uint16_t disk_cd_start;
  uint16_t num_records;
  uint16_t num_records_total;
  uint32_t cd_size;
  uint32_t cd_offset;
  uint16_t comment_len;
});

}  // namespace

size_t Zip::Leanify(size_t size_leanified /*= 0*/) {
  depth++;

  EOCD eocd;
  uint8_t* p_end = fp_ + size_;
  // smallest possible location of EOCD if there's a 64K comment
  uint8_t* p_searchstart = std::max(fp_, p_end - 65535 - sizeof(eocd.magic));
  uint8_t* p_eocd = std::find_end(p_searchstart, p_end, eocd.magic, eocd.magic + sizeof(eocd.magic));
  if (p_eocd == p_end) {
    cerr << "EOCD not found!" << endl;
    return Format::Leanify(size_leanified);
  }

  if (p_eocd + sizeof(EOCD) > p_end) {
    cerr << "EOF with EOCD!" << endl;
    return Format::Leanify(size_leanified);
  }
  memcpy(&eocd, p_eocd, sizeof(EOCD));

  if (eocd.disk_num != 0 || eocd.disk_cd_start != 0 || eocd.num_records != eocd.num_records_total) {
    cerr << "Neither split nor spanned archives is supported!" << endl;
    return Format::Leanify(size_leanified);
  }
  uint8_t* cd_end = fp_ + eocd.cd_offset + eocd.cd_size;
  if (cd_end > p_eocd) {
    cerr << "Central directory too large!" << endl;
    return Format::Leanify(size_leanified);
  }

  uint8_t* first_local_header =
      std::search(fp_, fp_ + eocd.cd_offset, header_magic, header_magic + sizeof(header_magic));
  // The offset of the first local header, we should keep everything before this offset.
  off_t zip_offset = first_local_header - fp_;
  // The offset that all the offsets in the zip file based on (relative to).
  // Should be 0 by default except when we detected that the input file has a base offset.
  off_t base_offset = 0;

  // Copy cd headers to vector
  vector<CDHeader> cd_headers;
  uint8_t* p_read = fp_ + eocd.cd_offset;
  for (int i = 0; i < eocd.num_records; i++) {
    CDHeader cd_header;
    if (p_read + sizeof(CDHeader) > cd_end) {
      cerr << "Central directory header " << i << " passed end, all remaining headers ignored." << endl;
      break;
    }
    if (memcmp(p_read, cd_header.magic, sizeof(cd_header.magic))) {
      // The offset might be relative to the first local file header instead of the beginning of the file.
      if (i == 0 && cd_end + zip_offset <= p_end &&
          memcmp(p_read + zip_offset, cd_header.magic, sizeof(cd_header.magic)) == 0) {
        // This is indeed the case, set the |base_offset| to |zip_offset| and move pointer forward.
        base_offset = zip_offset;
        p_read += base_offset;
        cd_end += base_offset;
      } else {
        cerr << "Central directory header magic mismatch at offset 0x" << std::hex << p_read - fp_ << std::dec << endl;
        break;
      }
    }
    memcpy(&cd_header, p_read, sizeof(CDHeader));
    p_read += sizeof(CDHeader) + cd_header.filename_len + cd_header.extra_field_len + cd_header.comment_len;

    cd_headers.push_back(cd_header);
  }
  if (p_read != cd_end) {
    cerr << "Warning: Central directory size mismatch!" << endl;
  }

  std::sort(cd_headers.begin(), cd_headers.end(),
            [](const CDHeader& a, const CDHeader& b) { return a.local_header_offset < b.local_header_offset; });

  uint8_t* fp_w = fp_ - size_leanified;
  uint8_t* fp_w_base = fp_w + base_offset;
  memmove(fp_w, fp_, zip_offset);
  uint8_t* p_write = fp_w + zip_offset;
  // Local file header
  for (CDHeader& cd_header : cd_headers) {
    p_read = fp_ + base_offset + cd_header.local_header_offset;

    if (p_read + 30 > p_end || memcmp(p_read, header_magic, sizeof(header_magic))) {
      cerr << "Invalid local header offset: 0x" << std::hex << cd_header.local_header_offset << std::dec << endl;
      continue;
    }
    cd_header.local_header_offset = p_write - fp_w_base;

    uint16_t filename_length = *(uint16_t*)(p_read + 26);

    if (filename_length != cd_header.filename_len) {
      cerr << "Warning: Filename length mismatch between local file header and central directory!" << endl;
    }

    size_t header_size = 30 + filename_length;
    if (p_read + header_size > p_end) {
      cerr << "Reached EOF in local header!" << endl;
      break;
    }
    // move header
    memmove(p_write, p_read, header_size);

    // if Extra field length is not 0, then skip it and set it to 0
    if (*(uint16_t*)(p_write + 28)) {
      p_read += *(uint16_t*)(p_write + 28);
      *(uint16_t*)(p_write + 28) = 0;
    }

    uint32_t* crc = (uint32_t*)(p_write + 14);
    uint32_t* compressed_size = crc + 1;
    uint32_t* uncompressed_size = compressed_size + 1;

    uint32_t orig_comp_size = *compressed_size;

    uint16_t flag = *(uint16_t*)(p_write + 6);
    uint16_t* compression_method = (uint16_t*)(p_write + 8);

    string filename(reinterpret_cast<char*>(p_write) + 30, filename_length);
    // do not output filename if it is a directory
    if ((orig_comp_size || *compression_method || flag & 8) && depth <= max_depth)
      PrintFileName(filename);

    if (flag & 8) {
      // set this bit to 0
      *(uint16_t*)(p_write + 6) &= ~8;

      // Use the correct value from central directory
      *crc = cd_header.crc32;
      *compressed_size = orig_comp_size = cd_header.compressed_size;
      *uncompressed_size = cd_header.uncompressed_size;
    }

    if (p_read + orig_comp_size > p_end) {
      cerr << "Compressed size too large!" << endl;
      break;
    }

    p_read += header_size;
    p_write += header_size;

    // if compression method is not deflate or fast mode
    // then only Leanify embedded file if the method is store
    // otherwise just memmove the compressed part
    if (*compression_method != 8 || (flag & 1) || is_fast) {
      if (*compression_method == 0 && depth <= max_depth && !(flag & 1)) {
        // method is store
        if (orig_comp_size) {
          uint32_t new_size = LeanifyFile(p_read, orig_comp_size, p_read - p_write, filename);
          cd_header.compressed_size = *compressed_size = cd_header.uncompressed_size = *uncompressed_size = new_size;
          cd_header.crc32 = *crc = mz_crc32(0, p_write, new_size);
        }
      } else {
        // unsupported compression method or encrypted, move it
        memmove(p_write, p_read, orig_comp_size);
      }
      p_write += *compressed_size;

    } else {
      // Compression method is deflate, recompress it with zopfli

      // Switch from deflate to store for empty file.
      if (*uncompressed_size == 0) {
        cd_header.compression_method = *compression_method = 0;
        cd_header.compressed_size = *compressed_size = 0;
        continue;
      }

      // decompress
      size_t s = 0;
      uint8_t* buffer = static_cast<uint8_t*>(tinfl_decompress_mem_to_heap(p_read, orig_comp_size, &s, 0));

      if (!buffer || s != *uncompressed_size || *crc != mz_crc32(0, buffer, *uncompressed_size)) {
        cerr << "Decompression failed or CRC32 mismatch, skipping this file." << endl;
        mz_free(buffer);
        memmove(p_write, p_read, orig_comp_size);
        p_write += orig_comp_size;
        continue;
      }

      // Leanify uncompressed file
      uint32_t new_uncomp_size = LeanifyFile(buffer, s, 0, filename);

      // recompress
      uint8_t bp = 0, *out = nullptr;
      size_t new_comp_size = 0;
      ZopfliDeflate(&zopfli_options_, 2, 1, buffer, new_uncomp_size, &bp, &out, &new_comp_size);

      // switch to store if deflate makes file larger
      if (new_uncomp_size <= new_comp_size && new_uncomp_size <= orig_comp_size) {
        cd_header.compression_method = *compression_method = 0;
        cd_header.crc32 = *crc = mz_crc32(0, buffer, new_uncomp_size);
        cd_header.compressed_size = *compressed_size = new_uncomp_size;
        cd_header.uncompressed_size = *uncompressed_size = new_uncomp_size;
        memcpy(p_write, buffer, new_uncomp_size);
      } else if (new_comp_size < orig_comp_size) {
        cd_header.crc32 = *crc = mz_crc32(0, buffer, new_uncomp_size);
        cd_header.compressed_size = *compressed_size = new_comp_size;
        cd_header.uncompressed_size = *uncompressed_size = new_uncomp_size;
        memcpy(p_write, out, new_comp_size);
      } else {
        memmove(p_write, p_read, orig_comp_size);
      }
      p_write += *compressed_size;

      mz_free(buffer);
      delete[] out;
    }
  }

  // central directory offset
  eocd.cd_offset = p_write - fp_w_base;
  for (CDHeader& cd_header : cd_headers) {
    // set bit 3 of General purpose bit flag to 0
    cd_header.flag &= ~8;
    cd_header.extra_field_len = cd_header.comment_len = 0;

    memcpy(p_write, &cd_header, sizeof(CDHeader));
    p_write += sizeof(CDHeader);
    // Copy the filename from local file header to central directory,
    // the old central directory might have been overwritten already because we sort them.
    memcpy(p_write, fp_w_base + cd_header.local_header_offset + 30, cd_header.filename_len);
    p_write += cd_header.filename_len;
  }

  // Update end of central directory record
  eocd.num_records = eocd.num_records_total = cd_headers.size();
  eocd.cd_size = p_write - fp_w_base - eocd.cd_offset;
  eocd.comment_len = 0;

  memcpy(p_write, &eocd, sizeof(eocd));

  fp_ -= size_leanified;
  size_ = p_write + sizeof(eocd) - fp_;
  return size_;
}
<commit_msg>ZIP: Use struct for local file header<commit_after>#include "zip.h"

#include <algorithm>
#include <cstdint>
#include <iostream>
#include <string>
#include <vector>

#include <miniz/miniz.h>

#include "../leanify.h"
#include "../utils.h"

using std::cerr;
using std::endl;
using std::string;
using std::vector;

const uint8_t Zip::header_magic[] = { 0x50, 0x4B, 0x03, 0x04 };

namespace {

PACK(struct LocalHeader {
  uint8_t magic[4];
  uint16_t version_needed;
  uint16_t flag;
  uint16_t compression_method;
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  uint32_t crc32;
  uint32_t compressed_size;
  uint32_t uncompressed_size;
  uint16_t filename_len;
  uint16_t extra_field_len;
});

PACK(struct CDHeader {
  uint8_t magic[4] = { 0x50, 0x4B, 0x01, 0x02 };
  uint16_t version_made_by;
  uint16_t version_needed;
  uint16_t flag;
  uint16_t compression_method;
  uint16_t last_mod_time;
  uint16_t last_mod_date;
  uint32_t crc32;
  uint32_t compressed_size;
  uint32_t uncompressed_size;
  uint16_t filename_len;
  uint16_t extra_field_len;
  uint16_t comment_len;
  uint16_t disk_file_start;
  uint16_t internal_file_attributes;
  uint32_t external_file_attributes;
  uint32_t local_header_offset;
});

PACK(struct EOCD {
  uint8_t magic[4] = { 0x50, 0x4B, 0x05, 0x06 };
  uint16_t disk_num;
  uint16_t disk_cd_start;
  uint16_t num_records;
  uint16_t num_records_total;
  uint32_t cd_size;
  uint32_t cd_offset;
  uint16_t comment_len;
});

}  // namespace

size_t Zip::Leanify(size_t size_leanified /*= 0*/) {
  depth++;

  EOCD eocd;
  uint8_t* p_end = fp_ + size_;
  // smallest possible location of EOCD if there's a 64K comment
  uint8_t* p_searchstart = std::max(fp_, p_end - 65535 - sizeof(eocd.magic));
  uint8_t* p_eocd = std::find_end(p_searchstart, p_end, eocd.magic, eocd.magic + sizeof(eocd.magic));
  if (p_eocd == p_end) {
    cerr << "EOCD not found!" << endl;
    return Format::Leanify(size_leanified);
  }

  if (p_eocd + sizeof(EOCD) > p_end) {
    cerr << "EOF with EOCD!" << endl;
    return Format::Leanify(size_leanified);
  }
  memcpy(&eocd, p_eocd, sizeof(EOCD));

  if (eocd.disk_num != 0 || eocd.disk_cd_start != 0 || eocd.num_records != eocd.num_records_total) {
    cerr << "Neither split nor spanned archives is supported!" << endl;
    return Format::Leanify(size_leanified);
  }
  uint8_t* cd_end = fp_ + eocd.cd_offset + eocd.cd_size;
  if (cd_end > p_eocd) {
    cerr << "Central directory too large!" << endl;
    return Format::Leanify(size_leanified);
  }

  uint8_t* first_local_header =
      std::search(fp_, fp_ + eocd.cd_offset, header_magic, header_magic + sizeof(header_magic));
  // The offset of the first local header, we should keep everything before this offset.
  off_t zip_offset = first_local_header - fp_;
  // The offset that all the offsets in the zip file based on (relative to).
  // Should be 0 by default except when we detected that the input file has a base offset.
  off_t base_offset = 0;

  // Copy cd headers to vector
  vector<CDHeader> cd_headers;
  uint8_t* p_read = fp_ + eocd.cd_offset;
  for (int i = 0; i < eocd.num_records; i++) {
    CDHeader cd_header;
    if (p_read + sizeof(CDHeader) > cd_end) {
      cerr << "Central directory header " << i << " passed end, all remaining headers ignored." << endl;
      break;
    }
    if (memcmp(p_read, cd_header.magic, sizeof(cd_header.magic))) {
      // The offset might be relative to the first local file header instead of the beginning of the file.
      if (i == 0 && cd_end + zip_offset <= p_end &&
          memcmp(p_read + zip_offset, cd_header.magic, sizeof(cd_header.magic)) == 0) {
        // This is indeed the case, set the |base_offset| to |zip_offset| and move pointer forward.
        base_offset = zip_offset;
        p_read += base_offset;
        cd_end += base_offset;
      } else {
        cerr << "Central directory header magic mismatch at offset 0x" << std::hex << p_read - fp_ << std::dec << endl;
        break;
      }
    }
    memcpy(&cd_header, p_read, sizeof(CDHeader));
    p_read += sizeof(CDHeader) + cd_header.filename_len + cd_header.extra_field_len + cd_header.comment_len;

    cd_headers.push_back(cd_header);
  }
  if (p_read != cd_end) {
    cerr << "Warning: Central directory size mismatch!" << endl;
  }

  std::sort(cd_headers.begin(), cd_headers.end(),
            [](const CDHeader& a, const CDHeader& b) { return a.local_header_offset < b.local_header_offset; });

  uint8_t* fp_w = fp_ - size_leanified;
  uint8_t* fp_w_base = fp_w + base_offset;
  memmove(fp_w, fp_, zip_offset);
  uint8_t* p_write = fp_w + zip_offset;
  // Local file header
  for (CDHeader& cd_header : cd_headers) {
    p_read = fp_ + base_offset + cd_header.local_header_offset;

    if (p_read + sizeof(LocalHeader) > p_end || memcmp(p_read, header_magic, sizeof(header_magic))) {
      cerr << "Invalid local header offset: 0x" << std::hex << cd_header.local_header_offset << std::dec << endl;
      continue;
    }
    cd_header.local_header_offset = p_write - fp_w_base;

    LocalHeader* local_header = reinterpret_cast<LocalHeader*>(p_read);

    if (local_header->filename_len != cd_header.filename_len) {
      cerr << "Warning: Filename length mismatch between local file header and central directory!" << endl;
    }

    size_t header_size = sizeof(LocalHeader) + local_header->filename_len;
    if (p_read + header_size > p_end) {
      cerr << "Reached EOF in local header!" << endl;
      break;
    }
    // move header
    memmove(p_write, p_read, header_size);
    local_header = reinterpret_cast<LocalHeader*>(p_write);

    // if Extra field length is not 0, then skip it and set it to 0
    if (local_header->extra_field_len) {
      p_read += local_header->extra_field_len;
      local_header->extra_field_len = 0;
    }

    if (local_header->flag & 8) {
      // set this bit to 0, we don't use data descriptor to save 16 byte
      local_header->flag &= ~8;
      cd_header.flag &= ~8;

      // Use the correct value from central directory
      local_header->crc32 = cd_header.crc32;
      local_header->compressed_size = cd_header.compressed_size;
      local_header->uncompressed_size = cd_header.uncompressed_size;
    }

    string filename(reinterpret_cast<char*>(local_header) + sizeof(LocalHeader), local_header->filename_len);
    // do not output filename if it is a directory
    if ((local_header->compressed_size || local_header->compression_method) && depth <= max_depth)
      PrintFileName(filename);

    if (p_read + local_header->compressed_size > p_end) {
      cerr << "Compressed size too large: " << local_header->compressed_size << endl;
      break;
    }

    p_read += header_size;
    p_write += header_size;

    // If the method is store, just Leanify the embedded file
    // don't try to change it to deflate, it might break some file.
    if (local_header->compression_method == 0) {
      // method is store
      if (local_header->compressed_size) {
        uint32_t new_size = LeanifyFile(p_read, local_header->compressed_size, p_read - p_write, filename);
        cd_header.crc32 = local_header->crc32 = mz_crc32(0, p_write, new_size);
        cd_header.compressed_size = local_header->compressed_size = new_size;
        cd_header.uncompressed_size = local_header->uncompressed_size = new_size;
        p_write += local_header->compressed_size;
      }
      continue;
    }

    // If unsupported compression method or fast mode or encrypted, just move it.
    if (local_header->compression_method != 8 || is_fast || local_header->flag & 1) {
      memmove(p_write, p_read, local_header->compressed_size);
      p_write += local_header->compressed_size;
      continue;
    }

    // Switch from deflate to store for empty file.
    if (local_header->uncompressed_size == 0) {
      cd_header.compression_method = local_header->compression_method = 0;
      cd_header.compressed_size = local_header->compressed_size = 0;
      continue;
    }

    // decompress
    size_t decompressed_size = 0;
    uint8_t* decompress_buf = static_cast<uint8_t*>(
        tinfl_decompress_mem_to_heap(p_read, local_header->compressed_size, &decompressed_size, 0));

    if (!decompress_buf || decompressed_size != local_header->uncompressed_size ||
        local_header->crc32 != mz_crc32(0, decompress_buf, local_header->uncompressed_size)) {
      cerr << "Decompression failed or CRC32 mismatch, skipping this file." << endl;
      mz_free(decompress_buf);
      memmove(p_write, p_read, local_header->compressed_size);
      p_write += local_header->compressed_size;
      continue;
    }

    // Leanify uncompressed file
    uint32_t new_uncomp_size = LeanifyFile(decompress_buf, decompressed_size, 0, filename);

    // recompress
    uint8_t bp = 0, *compress_buf = nullptr;
    size_t new_comp_size = 0;
    ZopfliDeflate(&zopfli_options_, 2, 1, decompress_buf, new_uncomp_size, &bp, &compress_buf, &new_comp_size);

    // switch to store if deflate makes file larger
    if (new_uncomp_size <= new_comp_size && new_uncomp_size <= local_header->compressed_size) {
      cd_header.compression_method = local_header->compression_method = 0;
      cd_header.crc32 = local_header->crc32 = mz_crc32(0, decompress_buf, new_uncomp_size);
      cd_header.compressed_size = local_header->compressed_size = new_uncomp_size;
      cd_header.uncompressed_size = local_header->uncompressed_size = new_uncomp_size;
      memcpy(p_write, decompress_buf, new_uncomp_size);
    } else if (new_comp_size < local_header->compressed_size) {
      cd_header.crc32 = local_header->crc32 = mz_crc32(0, decompress_buf, new_uncomp_size);
      cd_header.compressed_size = local_header->compressed_size = new_comp_size;
      cd_header.uncompressed_size = local_header->uncompressed_size = new_uncomp_size;
      memcpy(p_write, compress_buf, new_comp_size);
    } else {
      memmove(p_write, p_read, local_header->compressed_size);
    }
    p_write += local_header->compressed_size;

    mz_free(decompress_buf);
    delete[] compress_buf;
  }

  // central directory offset
  eocd.cd_offset = p_write - fp_w_base;
  for (CDHeader& cd_header : cd_headers) {
    cd_header.extra_field_len = cd_header.comment_len = 0;

    memcpy(p_write, &cd_header, sizeof(CDHeader));
    p_write += sizeof(CDHeader);
    // Copy the filename from local file header to central directory,
    // the old central directory might have been overwritten already because we sort them.
    memcpy(p_write, fp_w_base + cd_header.local_header_offset + 30, cd_header.filename_len);
    p_write += cd_header.filename_len;
  }

  // Update end of central directory record
  eocd.num_records = eocd.num_records_total = cd_headers.size();
  eocd.cd_size = p_write - fp_w_base - eocd.cd_offset;
  eocd.comment_len = 0;

  memcpy(p_write, &eocd, sizeof(EOCD));

  fp_ -= size_leanified;
  size_ = p_write + sizeof(EOCD) - fp_;
  return size_;
}
<|endoftext|>


<commit_before>//===- LanaiDisassembler.cpp - Disassembler for Lanai -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is part of the Lanai Disassembler.
//
//===----------------------------------------------------------------------===//

#include "LanaiDisassembler.h"

#include "Lanai.h"
#include "LanaiSubtarget.h"
#include "TargetInfo/LanaiTargetInfo.h"
#include "llvm/MC/MCFixedLenDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

typedef MCDisassembler::DecodeStatus DecodeStatus;

namespace llvm {
Target &getTheLanaiTarget();
}

static MCDisassembler *createLanaiDisassembler(const Target & /*T*/,
                                               const MCSubtargetInfo &STI,
                                               MCContext &Ctx) {
  return new LanaiDisassembler(STI, Ctx);
}

extern "C" void LLVMInitializeLanaiDisassembler() {
  // Register the disassembler
  TargetRegistry::RegisterMCDisassembler(getTheLanaiTarget(),
                                         createLanaiDisassembler);
}

LanaiDisassembler::LanaiDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
    : MCDisassembler(STI, Ctx) {}

// Forward declare because the autogenerated code will reference this.
// Definition is further down.
static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo,
                                           uint64_t Address,
                                           const void *Decoder);

static DecodeStatus decodeRiMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder);

static DecodeStatus decodeRrMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder);

static DecodeStatus decodeSplsValue(MCInst &Inst, unsigned Insn,
                                    uint64_t Address, const void *Decoder);

static DecodeStatus decodeBranch(MCInst &Inst, unsigned Insn, uint64_t Address,
                                 const void *Decoder);

static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
                                           uint64_t Address,
                                           const void *Decoder);

static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
                                   uint64_t Address, const void *Decoder);

#include "LanaiGenDisassemblerTables.inc"

static DecodeStatus readInstruction32(ArrayRef<uint8_t> Bytes, uint64_t &Size,
                                      uint32_t &Insn) {
  // We want to read exactly 4 bytes of data.
  if (Bytes.size() < 4) {
    Size = 0;
    return MCDisassembler::Fail;
  }

  // Encoded as big-endian 32-bit word in the stream.
  Insn =
      (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | (Bytes[3] << 0);

  return MCDisassembler::Success;
}

static void PostOperandDecodeAdjust(MCInst &Instr, uint32_t Insn) {
  unsigned AluOp = LPAC::ADD;
  // Fix up for pre and post operations.
  int PqShift = -1;
  if (isRMOpcode(Instr.getOpcode()))
    PqShift = 16;
  else if (isSPLSOpcode(Instr.getOpcode()))
    PqShift = 10;
  else if (isRRMOpcode(Instr.getOpcode())) {
    PqShift = 16;
    // Determine RRM ALU op.
    AluOp = (Insn >> 8) & 0x7;
    if (AluOp == 7)
      // Handle JJJJJ
      // 0b10000 or 0b11000
      AluOp |= 0x20 | (((Insn >> 3) & 0xf) << 1);
  }

  if (PqShift != -1) {
    unsigned PQ = (Insn >> PqShift) & 0x3;
    switch (PQ) {
    case 0x0:
      if (Instr.getOperand(2).isReg()) {
        Instr.getOperand(2).setReg(Lanai::R0);
      }
      if (Instr.getOperand(2).isImm())
        Instr.getOperand(2).setImm(0);
      break;
    case 0x1:
      AluOp = LPAC::makePostOp(AluOp);
      break;
    case 0x2:
      break;
    case 0x3:
      AluOp = LPAC::makePreOp(AluOp);
      break;
    }
    Instr.addOperand(MCOperand::createImm(AluOp));
  }
}

DecodeStatus LanaiDisassembler::getInstruction(
    MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address,
    raw_ostream & /*VStream*/, raw_ostream & /*CStream*/) const {
  uint32_t Insn;

  DecodeStatus Result = readInstruction32(Bytes, Size, Insn);

  if (Result == MCDisassembler::Fail)
    return MCDisassembler::Fail;

  // Call auto-generated decoder function
  Result =
      decodeInstruction(DecoderTableLanai32, Instr, Insn, Address, this, STI);

  if (Result != MCDisassembler::Fail) {
    PostOperandDecodeAdjust(Instr, Insn);
    Size = 4;
    return Result;
  }

  return MCDisassembler::Fail;
}

static const unsigned GPRDecoderTable[] = {
    Lanai::R0,  Lanai::R1,  Lanai::PC,  Lanai::R3,  Lanai::SP,  Lanai::FP,
    Lanai::R6,  Lanai::R7,  Lanai::RV,  Lanai::R9,  Lanai::RR1, Lanai::RR2,
    Lanai::R12, Lanai::R13, Lanai::R14, Lanai::RCA, Lanai::R16, Lanai::R17,
    Lanai::R18, Lanai::R19, Lanai::R20, Lanai::R21, Lanai::R22, Lanai::R23,
    Lanai::R24, Lanai::R25, Lanai::R26, Lanai::R27, Lanai::R28, Lanai::R29,
    Lanai::R30, Lanai::R31};

DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo,
                                    uint64_t /*Address*/,
                                    const void * /*Decoder*/) {
  if (RegNo > 31)
    return MCDisassembler::Fail;

  unsigned Reg = GPRDecoderTable[RegNo];
  Inst.addOperand(MCOperand::createReg(Reg));
  return MCDisassembler::Success;
}

static DecodeStatus decodeRiMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder) {
  // RI memory values encoded using 23 bits:
  //   5 bit register, 16 bit constant
  unsigned Register = (Insn >> 18) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  unsigned Offset = (Insn & 0xffff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Offset)));

  return MCDisassembler::Success;
}

static DecodeStatus decodeRrMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder) {
  // RR memory values encoded using 20 bits:
  //   5 bit register, 5 bit register, 2 bit PQ, 3 bit ALU operator, 5 bit JJJJJ
  unsigned Register = (Insn >> 15) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  Register = (Insn >> 10) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));

  return MCDisassembler::Success;
}

static DecodeStatus decodeSplsValue(MCInst &Inst, unsigned Insn,
                                    uint64_t Address, const void *Decoder) {
  // RI memory values encoded using 17 bits:
  //   5 bit register, 10 bit constant
  unsigned Register = (Insn >> 12) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  unsigned Offset = (Insn & 0x3ff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<10>(Offset)));

  return MCDisassembler::Success;
}

static bool tryAddingSymbolicOperand(int64_t Value, bool IsBranch,
                                     uint64_t Address, uint64_t Offset,
                                     uint64_t Width, MCInst &MI,
                                     const void *Decoder) {
  const MCDisassembler *Dis = static_cast<const MCDisassembler *>(Decoder);
  return Dis->tryAddingSymbolicOperand(MI, Value, Address, IsBranch, Offset,
                                       Width);
}

static DecodeStatus decodeBranch(MCInst &MI, unsigned Insn, uint64_t Address,
                                 const void *Decoder) {
  if (!tryAddingSymbolicOperand(Insn + Address, false, Address, 2, 23, MI,
                                Decoder))
    MI.addOperand(MCOperand::createImm(Insn));
  return MCDisassembler::Success;
}

static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
                                   uint64_t Address, const void *Decoder) {
  unsigned Offset = (Insn & 0xffff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Offset)));

  return MCDisassembler::Success;
}

static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
                                           uint64_t Address,
                                           const void *Decoder) {
  if (Val >= LPCC::UNKNOWN)
    return MCDisassembler::Fail;
  Inst.addOperand(MCOperand::createImm(Val));
  return MCDisassembler::Success;
}
<commit_msg>Include what you use in LanaiDisassembler.cpp<commit_after>//===- LanaiDisassembler.cpp - Disassembler for Lanai -----------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is part of the Lanai Disassembler.
//
//===----------------------------------------------------------------------===//

#include "LanaiDisassembler.h"

#include "LanaiAluCode.h"
#include "LanaiCondCode.h"
#include "LanaiInstrInfo.h"
#include "TargetInfo/LanaiTargetInfo.h"
#include "llvm/MC/MCFixedLenDisassembler.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/TargetRegistry.h"

using namespace llvm;

typedef MCDisassembler::DecodeStatus DecodeStatus;

namespace llvm {
Target &getTheLanaiTarget();
}

static MCDisassembler *createLanaiDisassembler(const Target & /*T*/,
                                               const MCSubtargetInfo &STI,
                                               MCContext &Ctx) {
  return new LanaiDisassembler(STI, Ctx);
}

extern "C" void LLVMInitializeLanaiDisassembler() {
  // Register the disassembler
  TargetRegistry::RegisterMCDisassembler(getTheLanaiTarget(),
                                         createLanaiDisassembler);
}

LanaiDisassembler::LanaiDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
    : MCDisassembler(STI, Ctx) {}

// Forward declare because the autogenerated code will reference this.
// Definition is further down.
static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo,
                                           uint64_t Address,
                                           const void *Decoder);

static DecodeStatus decodeRiMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder);

static DecodeStatus decodeRrMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder);

static DecodeStatus decodeSplsValue(MCInst &Inst, unsigned Insn,
                                    uint64_t Address, const void *Decoder);

static DecodeStatus decodeBranch(MCInst &Inst, unsigned Insn, uint64_t Address,
                                 const void *Decoder);

static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
                                           uint64_t Address,
                                           const void *Decoder);

static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
                                   uint64_t Address, const void *Decoder);

#include "LanaiGenDisassemblerTables.inc"

static DecodeStatus readInstruction32(ArrayRef<uint8_t> Bytes, uint64_t &Size,
                                      uint32_t &Insn) {
  // We want to read exactly 4 bytes of data.
  if (Bytes.size() < 4) {
    Size = 0;
    return MCDisassembler::Fail;
  }

  // Encoded as big-endian 32-bit word in the stream.
  Insn =
      (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | (Bytes[3] << 0);

  return MCDisassembler::Success;
}

static void PostOperandDecodeAdjust(MCInst &Instr, uint32_t Insn) {
  unsigned AluOp = LPAC::ADD;
  // Fix up for pre and post operations.
  int PqShift = -1;
  if (isRMOpcode(Instr.getOpcode()))
    PqShift = 16;
  else if (isSPLSOpcode(Instr.getOpcode()))
    PqShift = 10;
  else if (isRRMOpcode(Instr.getOpcode())) {
    PqShift = 16;
    // Determine RRM ALU op.
    AluOp = (Insn >> 8) & 0x7;
    if (AluOp == 7)
      // Handle JJJJJ
      // 0b10000 or 0b11000
      AluOp |= 0x20 | (((Insn >> 3) & 0xf) << 1);
  }

  if (PqShift != -1) {
    unsigned PQ = (Insn >> PqShift) & 0x3;
    switch (PQ) {
    case 0x0:
      if (Instr.getOperand(2).isReg()) {
        Instr.getOperand(2).setReg(Lanai::R0);
      }
      if (Instr.getOperand(2).isImm())
        Instr.getOperand(2).setImm(0);
      break;
    case 0x1:
      AluOp = LPAC::makePostOp(AluOp);
      break;
    case 0x2:
      break;
    case 0x3:
      AluOp = LPAC::makePreOp(AluOp);
      break;
    }
    Instr.addOperand(MCOperand::createImm(AluOp));
  }
}

DecodeStatus LanaiDisassembler::getInstruction(
    MCInst &Instr, uint64_t &Size, ArrayRef<uint8_t> Bytes, uint64_t Address,
    raw_ostream & /*VStream*/, raw_ostream & /*CStream*/) const {
  uint32_t Insn;

  DecodeStatus Result = readInstruction32(Bytes, Size, Insn);

  if (Result == MCDisassembler::Fail)
    return MCDisassembler::Fail;

  // Call auto-generated decoder function
  Result =
      decodeInstruction(DecoderTableLanai32, Instr, Insn, Address, this, STI);

  if (Result != MCDisassembler::Fail) {
    PostOperandDecodeAdjust(Instr, Insn);
    Size = 4;
    return Result;
  }

  return MCDisassembler::Fail;
}

static const unsigned GPRDecoderTable[] = {
    Lanai::R0,  Lanai::R1,  Lanai::PC,  Lanai::R3,  Lanai::SP,  Lanai::FP,
    Lanai::R6,  Lanai::R7,  Lanai::RV,  Lanai::R9,  Lanai::RR1, Lanai::RR2,
    Lanai::R12, Lanai::R13, Lanai::R14, Lanai::RCA, Lanai::R16, Lanai::R17,
    Lanai::R18, Lanai::R19, Lanai::R20, Lanai::R21, Lanai::R22, Lanai::R23,
    Lanai::R24, Lanai::R25, Lanai::R26, Lanai::R27, Lanai::R28, Lanai::R29,
    Lanai::R30, Lanai::R31};

DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, unsigned RegNo,
                                    uint64_t /*Address*/,
                                    const void * /*Decoder*/) {
  if (RegNo > 31)
    return MCDisassembler::Fail;

  unsigned Reg = GPRDecoderTable[RegNo];
  Inst.addOperand(MCOperand::createReg(Reg));
  return MCDisassembler::Success;
}

static DecodeStatus decodeRiMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder) {
  // RI memory values encoded using 23 bits:
  //   5 bit register, 16 bit constant
  unsigned Register = (Insn >> 18) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  unsigned Offset = (Insn & 0xffff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Offset)));

  return MCDisassembler::Success;
}

static DecodeStatus decodeRrMemoryValue(MCInst &Inst, unsigned Insn,
                                        uint64_t Address, const void *Decoder) {
  // RR memory values encoded using 20 bits:
  //   5 bit register, 5 bit register, 2 bit PQ, 3 bit ALU operator, 5 bit JJJJJ
  unsigned Register = (Insn >> 15) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  Register = (Insn >> 10) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));

  return MCDisassembler::Success;
}

static DecodeStatus decodeSplsValue(MCInst &Inst, unsigned Insn,
                                    uint64_t Address, const void *Decoder) {
  // RI memory values encoded using 17 bits:
  //   5 bit register, 10 bit constant
  unsigned Register = (Insn >> 12) & 0x1f;
  Inst.addOperand(MCOperand::createReg(GPRDecoderTable[Register]));
  unsigned Offset = (Insn & 0x3ff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<10>(Offset)));

  return MCDisassembler::Success;
}

static bool tryAddingSymbolicOperand(int64_t Value, bool IsBranch,
                                     uint64_t Address, uint64_t Offset,
                                     uint64_t Width, MCInst &MI,
                                     const void *Decoder) {
  const MCDisassembler *Dis = static_cast<const MCDisassembler *>(Decoder);
  return Dis->tryAddingSymbolicOperand(MI, Value, Address, IsBranch, Offset,
                                       Width);
}

static DecodeStatus decodeBranch(MCInst &MI, unsigned Insn, uint64_t Address,
                                 const void *Decoder) {
  if (!tryAddingSymbolicOperand(Insn + Address, false, Address, 2, 23, MI,
                                Decoder))
    MI.addOperand(MCOperand::createImm(Insn));
  return MCDisassembler::Success;
}

static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
                                   uint64_t Address, const void *Decoder) {
  unsigned Offset = (Insn & 0xffff);
  Inst.addOperand(MCOperand::createImm(SignExtend32<16>(Offset)));

  return MCDisassembler::Success;
}

static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
                                           uint64_t Address,
                                           const void *Decoder) {
  if (Val >= LPCC::UNKNOWN)
    return MCDisassembler::Fail;
  Inst.addOperand(MCOperand::createImm(Val));
  return MCDisassembler::Success;
}
<|endoftext|>