<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "File.h"
#include "Folder.h"
#include "Device.h"
#include "Media.h"

#include "database/SqliteTools.h"
#include "filesystem/IDirectory.h"
#include "filesystem/IDevice.h"
#include "utils/Filename.h"

#include <unordered_map>

namespace medialibrary
{

namespace policy
{
    const std::string FolderTable::Name = "Folder";
    const std::string FolderTable::PrimaryKeyColumn = "id_folder";
    int64_t Folder::* const FolderTable::PrimaryKey = &Folder::m_id;
}

Folder::Folder( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
{
    bool dummy;
    row >> m_id
        >> m_path
        >> m_parent
        >> m_isBlacklisted
        >> m_deviceId
        >> dummy
        >> m_isRemovable;
}

Folder::Folder(MediaLibraryPtr ml, const std::string& path, int64_t parent, int64_t deviceId, bool isRemovable )
    : m_ml( ml )
    , m_id( 0 )
    , m_path( path )
    , m_parent( parent )
    , m_isBlacklisted( false )
    , m_deviceId( deviceId )
    , m_isRemovable( isRemovable )
{
}

bool Folder::createTable( DBConnection connection)
{
    std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FolderTable::Name +
            "("
            "id_folder INTEGER PRIMARY KEY AUTOINCREMENT,"
            "path TEXT,"
            "parent_id UNSIGNED INTEGER,"
            "is_blacklisted BOOLEAN NOT NULL DEFAULT 0,"
            "device_id UNSIGNED INTEGER,"
            "is_present BOOLEAN NOT NULL DEFAULT 1,"
            "is_removable BOOLEAN NOT NULL,"
            "FOREIGN KEY (parent_id) REFERENCES " + policy::FolderTable::Name +
            "(id_folder) ON DELETE CASCADE,"
            "FOREIGN KEY (device_id) REFERENCES " + policy::DeviceTable::Name +
            "(id_device) ON DELETE CASCADE,"
            "UNIQUE(path, device_id) ON CONFLICT FAIL"
            ")";
    std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_device_present AFTER UPDATE OF is_present ON "
            + policy::DeviceTable::Name +
            " BEGIN"
            " UPDATE " + policy::FolderTable::Name + " SET is_present = new.is_present WHERE device_id = new.id_device;"
            " END";
    std::string deviceIndexReq = "CREATE INDEX IF NOT EXISTS folder_device_id_idx ON " +
            policy::FolderTable::Name + " (device_id)";
    std::string parentFolderIndexReq = "CREATE INDEX IF NOT EXISTS parent_folder_id_idx ON " +
            policy::FolderTable::Name + " (parent_id)";
    return sqlite::Tools::executeRequest( connection, req ) &&
            sqlite::Tools::executeRequest( connection, triggerReq ) &&
            sqlite::Tools::executeRequest( connection, deviceIndexReq ) &&
            sqlite::Tools::executeRequest( connection, parentFolderIndexReq );
}

std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& mrl,
                                        int64_t parentId, Device& device, fs::IDevice& deviceFs )
{
    std::string path;
    if ( device.isRemovable() == true )
        path = utils::file::removePath( mrl, deviceFs.mountpoint() );
    else
        path = mrl;
    auto self = std::make_shared<Folder>( ml, path, parentId, device.id(), device.isRemovable() );
    static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
            "(path, parent_id, device_id, is_removable) VALUES(?, ?, ?, ?)";
    if ( insert( ml, self, req, path, sqlite::ForeignKey( parentId ), device.id(), device.isRemovable() ) == false )
        return nullptr;
    if ( device.isRemovable() == true )
    {
        self->m_deviceMountpoint = deviceFs.mountpoint();
        self->m_fullPath = self->m_deviceMountpoint.get() + path;
    }
    return self;
}

bool Folder::blacklist( MediaLibraryPtr ml, const std::string& mrl )
{
    // Ensure we delete the existing folder if any & blacklist the folder in an "atomic" way
    return sqlite::Tools::withRetries( 3, [ml, &mrl]() {
        auto t = ml->getConn()->newTransaction();

        auto f = fromMrl( ml, mrl, BannedType::Any );
        if ( f != nullptr )
        {
            // No need to blacklist a folder twice
            if ( f->m_isBlacklisted == true )
                return true;
            // Let the foreign key destroy everything beneath this folder
            destroy( ml, f->id() );
        }
        auto fsFactory = ml->fsFactoryForMrl( mrl );
        if ( fsFactory == nullptr )
            return false;
        auto folderFs = fsFactory->createDirectory( mrl );
        assert( folderFs != nullptr );
        auto deviceFs = folderFs->device();
        if ( deviceFs == nullptr )
        {
            LOG_ERROR( "Can't find device associated with mrl ", mrl );
            return false;
        }
        auto device = Device::fromUuid( ml, deviceFs->uuid() );
        if ( device == nullptr )
            device = Device::create( ml, deviceFs->uuid(), utils::file::scheme( mrl ), deviceFs->isRemovable() );
        std::string path;
        if ( deviceFs->isRemovable() == true )
            path = utils::file::removePath( mrl, deviceFs->mountpoint() );
        else
            path = mrl;
        static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
                "(path, parent_id, is_blacklisted, device_id, is_removable) VALUES(?, ?, ?, ?, ?)";
        auto res = sqlite::Tools::executeInsert( ml->getConn(), req, path, nullptr, true, device->id(), deviceFs->isRemovable() ) != 0;
        t->commit();
        return res;
    });
}

std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl )
{
    return fromMrl( ml, mrl, BannedType::No );
}

std::shared_ptr<Folder> Folder::blacklistedFolder( MediaLibraryPtr ml, const std::string& mrl )
{
    return fromMrl( ml, mrl, BannedType::Yes );
}

std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl, BannedType bannedType )
{
    auto fsFactory = ml->fsFactoryForMrl( mrl );
    if ( fsFactory == nullptr )
        return nullptr;
    auto folderFs = fsFactory->createDirectory( mrl );
    assert( folderFs != nullptr );
    auto deviceFs = folderFs->device();
    if ( deviceFs == nullptr )
    {
        LOG_ERROR( "Failed to get device containing an existing folder: ", folderFs->mrl() );
        return nullptr;
    }
    if ( deviceFs->isRemovable() == false )
    {
        std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND is_removable = 0";
        if ( bannedType == BannedType::Any )
            return fetch( ml, req, folderFs->mrl() );
        req += " AND is_blacklisted = ?";
        return fetch( ml, req, folderFs->mrl(), bannedType == BannedType::Yes ? true : false );
    }

    auto device = Device::fromUuid( ml, deviceFs->uuid() );
    // We are trying to find a folder. If we don't know the device it's on, we don't know the folder.
    if ( device == nullptr )
        return nullptr;
    auto path = utils::file::removePath( folderFs->mrl(), deviceFs->mountpoint() );
    std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND device_id = ?";
    std::shared_ptr<Folder> folder;
    if ( bannedType == BannedType::Any )
    {
        folder = fetch( ml, req, path, device->id() );
    }
    else
    {
        req += " AND is_blacklisted = ?";
        folder = fetch( ml, req, path, device->id(), bannedType == BannedType::Yes ? true : false );
    }
    if ( folder == nullptr )
        return nullptr;
    folder->m_deviceMountpoint = deviceFs->mountpoint();
    folder->m_fullPath = folder->m_deviceMountpoint.get() + path;
    return folder;
}

int64_t Folder::id() const
{
    return m_id;
}

const std::string& Folder::mrl() const
{
    if ( m_isRemovable == false )
        return m_path;

    auto lock = m_deviceMountpoint.lock();
    if ( m_deviceMountpoint.isCached() == true )
        return m_fullPath;

    // We can't compute the full path of a folder if it's removable and the device isn't present.
    // When there's no device, we don't know the mountpoint, therefor we don't know the full path
    // Calling isPresent will ensure we have the device representation cached locally
    if ( isPresent() == false )
    {
        assert( !"Device isn't present" );
        m_fullPath = "";
        return m_fullPath;
    }

    auto fsFactory = m_ml->fsFactoryForMrl( m_device.get()->scheme() );
    assert( fsFactory != nullptr );
    auto deviceFs = fsFactory->createDevice( m_device.get()->uuid() );
    m_deviceMountpoint = deviceFs->mountpoint();
    m_fullPath = m_deviceMountpoint.get() + m_path;
    return m_fullPath;
}

std::vector<std::shared_ptr<File>> Folder::files()
{
    static const std::string req = "SELECT * FROM " + policy::FileTable::Name +
        " WHERE folder_id = ?";
    return File::fetchAll<File>( m_ml, req, m_id );
}

std::vector<std::shared_ptr<Folder>> Folder::folders()
{
    static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
            + " WHERE parent_id = ? AND is_blacklisted = 0 AND is_present = 1";
    return DatabaseHelpers::fetchAll<Folder>( m_ml, req, m_id );
}

std::shared_ptr<Folder> Folder::parent()
{
    return fetch( m_ml, m_parent );
}

int64_t Folder::deviceId() const
{
    return m_deviceId;
}

bool Folder::isPresent() const
{
    auto deviceLock = m_device.lock();
    if ( m_device.isCached() == false )
        m_device = Device::fetch( m_ml, m_deviceId );
    assert( m_device.get() != nullptr );
    return m_device.get()->isPresent();
}

bool Folder::isRootFolder() const
{
    return m_parent == 0;
}

std::vector<std::shared_ptr<Folder>> Folder::fetchRootFolders( MediaLibraryPtr ml )
{
    static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
            + " WHERE parent_id IS NULL AND is_blacklisted = 0 AND is_present = 1";
    return DatabaseHelpers::fetchAll<Folder>( ml, req );
}

}
<commit_msg>Folder: Handle sporadic read error gracefully<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015 Hugo Beauzée-Luyssen, Videolabs
 *
 * Authors: Hugo Beauzée-Luyssen<hugo@beauzee.fr>
 *
 * This program 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 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "File.h"
#include "Folder.h"
#include "Device.h"
#include "Media.h"

#include "database/SqliteTools.h"
#include "filesystem/IDirectory.h"
#include "filesystem/IDevice.h"
#include "utils/Filename.h"

#include <unordered_map>

namespace medialibrary
{

namespace policy
{
    const std::string FolderTable::Name = "Folder";
    const std::string FolderTable::PrimaryKeyColumn = "id_folder";
    int64_t Folder::* const FolderTable::PrimaryKey = &Folder::m_id;
}

Folder::Folder( MediaLibraryPtr ml, sqlite::Row& row )
    : m_ml( ml )
{
    bool dummy;
    row >> m_id
        >> m_path
        >> m_parent
        >> m_isBlacklisted
        >> m_deviceId
        >> dummy
        >> m_isRemovable;
}

Folder::Folder(MediaLibraryPtr ml, const std::string& path, int64_t parent, int64_t deviceId, bool isRemovable )
    : m_ml( ml )
    , m_id( 0 )
    , m_path( path )
    , m_parent( parent )
    , m_isBlacklisted( false )
    , m_deviceId( deviceId )
    , m_isRemovable( isRemovable )
{
}

bool Folder::createTable( DBConnection connection)
{
    std::string req = "CREATE TABLE IF NOT EXISTS " + policy::FolderTable::Name +
            "("
            "id_folder INTEGER PRIMARY KEY AUTOINCREMENT,"
            "path TEXT,"
            "parent_id UNSIGNED INTEGER,"
            "is_blacklisted BOOLEAN NOT NULL DEFAULT 0,"
            "device_id UNSIGNED INTEGER,"
            "is_present BOOLEAN NOT NULL DEFAULT 1,"
            "is_removable BOOLEAN NOT NULL,"
            "FOREIGN KEY (parent_id) REFERENCES " + policy::FolderTable::Name +
            "(id_folder) ON DELETE CASCADE,"
            "FOREIGN KEY (device_id) REFERENCES " + policy::DeviceTable::Name +
            "(id_device) ON DELETE CASCADE,"
            "UNIQUE(path, device_id) ON CONFLICT FAIL"
            ")";
    std::string triggerReq = "CREATE TRIGGER IF NOT EXISTS is_device_present AFTER UPDATE OF is_present ON "
            + policy::DeviceTable::Name +
            " BEGIN"
            " UPDATE " + policy::FolderTable::Name + " SET is_present = new.is_present WHERE device_id = new.id_device;"
            " END";
    std::string deviceIndexReq = "CREATE INDEX IF NOT EXISTS folder_device_id_idx ON " +
            policy::FolderTable::Name + " (device_id)";
    std::string parentFolderIndexReq = "CREATE INDEX IF NOT EXISTS parent_folder_id_idx ON " +
            policy::FolderTable::Name + " (parent_id)";
    return sqlite::Tools::executeRequest( connection, req ) &&
            sqlite::Tools::executeRequest( connection, triggerReq ) &&
            sqlite::Tools::executeRequest( connection, deviceIndexReq ) &&
            sqlite::Tools::executeRequest( connection, parentFolderIndexReq );
}

std::shared_ptr<Folder> Folder::create( MediaLibraryPtr ml, const std::string& mrl,
                                        int64_t parentId, Device& device, fs::IDevice& deviceFs )
{
    std::string path;
    if ( device.isRemovable() == true )
        path = utils::file::removePath( mrl, deviceFs.mountpoint() );
    else
        path = mrl;
    auto self = std::make_shared<Folder>( ml, path, parentId, device.id(), device.isRemovable() );
    static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
            "(path, parent_id, device_id, is_removable) VALUES(?, ?, ?, ?)";
    if ( insert( ml, self, req, path, sqlite::ForeignKey( parentId ), device.id(), device.isRemovable() ) == false )
        return nullptr;
    if ( device.isRemovable() == true )
    {
        self->m_deviceMountpoint = deviceFs.mountpoint();
        self->m_fullPath = self->m_deviceMountpoint.get() + path;
    }
    return self;
}

bool Folder::blacklist( MediaLibraryPtr ml, const std::string& mrl )
{
    // Ensure we delete the existing folder if any & blacklist the folder in an "atomic" way
    return sqlite::Tools::withRetries( 3, [ml, &mrl]() {
        auto t = ml->getConn()->newTransaction();

        auto f = fromMrl( ml, mrl, BannedType::Any );
        if ( f != nullptr )
        {
            // No need to blacklist a folder twice
            if ( f->m_isBlacklisted == true )
                return true;
            // Let the foreign key destroy everything beneath this folder
            destroy( ml, f->id() );
        }
        auto fsFactory = ml->fsFactoryForMrl( mrl );
        if ( fsFactory == nullptr )
            return false;
        auto folderFs = fsFactory->createDirectory( mrl );
        assert( folderFs != nullptr );
        auto deviceFs = folderFs->device();
        if ( deviceFs == nullptr )
        {
            LOG_ERROR( "Can't find device associated with mrl ", mrl );
            return false;
        }
        auto device = Device::fromUuid( ml, deviceFs->uuid() );
        if ( device == nullptr )
            device = Device::create( ml, deviceFs->uuid(), utils::file::scheme( mrl ), deviceFs->isRemovable() );
        std::string path;
        if ( deviceFs->isRemovable() == true )
            path = utils::file::removePath( mrl, deviceFs->mountpoint() );
        else
            path = mrl;
        static const std::string req = "INSERT INTO " + policy::FolderTable::Name +
                "(path, parent_id, is_blacklisted, device_id, is_removable) VALUES(?, ?, ?, ?, ?)";
        auto res = sqlite::Tools::executeInsert( ml->getConn(), req, path, nullptr, true, device->id(), deviceFs->isRemovable() ) != 0;
        t->commit();
        return res;
    });
}

std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl )
{
    return fromMrl( ml, mrl, BannedType::No );
}

std::shared_ptr<Folder> Folder::blacklistedFolder( MediaLibraryPtr ml, const std::string& mrl )
{
    return fromMrl( ml, mrl, BannedType::Yes );
}

std::shared_ptr<Folder> Folder::fromMrl( MediaLibraryPtr ml, const std::string& mrl, BannedType bannedType )
{
    auto fsFactory = ml->fsFactoryForMrl( mrl );
    if ( fsFactory == nullptr )
        return nullptr;
    auto folderFs = fsFactory->createDirectory( mrl );
    assert( folderFs != nullptr );
    auto deviceFs = folderFs->device();
    if ( deviceFs == nullptr )
    {
        LOG_ERROR( "Failed to get device containing an existing folder: ", folderFs->mrl() );
        return nullptr;
    }
    if ( deviceFs->isRemovable() == false )
    {
        std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND is_removable = 0";
        if ( bannedType == BannedType::Any )
            return fetch( ml, req, folderFs->mrl() );
        req += " AND is_blacklisted = ?";
        return fetch( ml, req, folderFs->mrl(), bannedType == BannedType::Yes ? true : false );
    }

    auto device = Device::fromUuid( ml, deviceFs->uuid() );
    // We are trying to find a folder. If we don't know the device it's on, we don't know the folder.
    if ( device == nullptr )
        return nullptr;
    auto path = utils::file::removePath( folderFs->mrl(), deviceFs->mountpoint() );
    std::string req = "SELECT * FROM " + policy::FolderTable::Name + " WHERE path = ? AND device_id = ?";
    std::shared_ptr<Folder> folder;
    if ( bannedType == BannedType::Any )
    {
        folder = fetch( ml, req, path, device->id() );
    }
    else
    {
        req += " AND is_blacklisted = ?";
        folder = fetch( ml, req, path, device->id(), bannedType == BannedType::Yes ? true : false );
    }
    if ( folder == nullptr )
        return nullptr;
    folder->m_deviceMountpoint = deviceFs->mountpoint();
    folder->m_fullPath = folder->m_deviceMountpoint.get() + path;
    return folder;
}

int64_t Folder::id() const
{
    return m_id;
}

const std::string& Folder::mrl() const
{
    if ( m_isRemovable == false )
        return m_path;

    auto lock = m_deviceMountpoint.lock();
    if ( m_deviceMountpoint.isCached() == true )
        return m_fullPath;

    // We can't compute the full path of a folder if it's removable and the device isn't present.
    // When there's no device, we don't know the mountpoint, therefor we don't know the full path
    // Calling isPresent will ensure we have the device representation cached locally
    if ( isPresent() == false )
    {
        assert( !"Device isn't present" );
        m_fullPath = "";
        return m_fullPath;
    }

    auto fsFactory = m_ml->fsFactoryForMrl( m_device.get()->scheme() );
    assert( fsFactory != nullptr );
    auto deviceFs = fsFactory->createDevice( m_device.get()->uuid() );
    m_deviceMountpoint = deviceFs->mountpoint();
    m_fullPath = m_deviceMountpoint.get() + m_path;
    return m_fullPath;
}

std::vector<std::shared_ptr<File>> Folder::files()
{
    static const std::string req = "SELECT * FROM " + policy::FileTable::Name +
        " WHERE folder_id = ?";
    return File::fetchAll<File>( m_ml, req, m_id );
}

std::vector<std::shared_ptr<Folder>> Folder::folders()
{
    static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
            + " WHERE parent_id = ? AND is_blacklisted = 0 AND is_present = 1";
    return DatabaseHelpers::fetchAll<Folder>( m_ml, req, m_id );
}

std::shared_ptr<Folder> Folder::parent()
{
    return fetch( m_ml, m_parent );
}

int64_t Folder::deviceId() const
{
    return m_deviceId;
}

bool Folder::isPresent() const
{
    auto deviceLock = m_device.lock();
    if ( m_device.isCached() == false )
        m_device = Device::fetch( m_ml, m_deviceId );
    // There must be a device containing the folder, since we never create a folder
    // without a device
    assert( m_device.get() != nullptr );
    // However, handle potential sporadic errors gracefully
    if( m_device.get() == nullptr )
        return false;
    return m_device.get()->isPresent();
}

bool Folder::isRootFolder() const
{
    return m_parent == 0;
}

std::vector<std::shared_ptr<Folder>> Folder::fetchRootFolders( MediaLibraryPtr ml )
{
    static const std::string req = "SELECT * FROM " + policy::FolderTable::Name
            + " WHERE parent_id IS NULL AND is_blacklisted = 0 AND is_present = 1";
    return DatabaseHelpers::fetchAll<Folder>( ml, req );
}

}
<|endoftext|>


<commit_before>#include "../include/crow_all.h"

#include <sstream>

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
        .name("hello")
    ([]{
        return "Hello World!";
    });

    app.port(1234)
        .multithreaded()
        .run();
}
<commit_msg>Hello world<commit_after>#include "crow_all.h"

#include <sstream>

int main()
{
    crow::SimpleApp app;

    CROW_ROUTE(app, "/")
        .name("hello")
    ([]{
        return "Hello World!";
    });

    app.port(1234)
        .multithreaded()
        .run();
}
<|endoftext|>


<commit_before>#include "Halide.h"

using namespace Halide;

int main(int argc, char **argv) {
    ImageParam in(UInt(8), 3, "input");

    Var x("x"), y("y"), c("c"), x1, y1;
    Func f("f"), g("g"), h("h"), k("k");

    f(x, y, c) = cast<uint8_t>(255 - in(x, y, c));
    g(x, y, c) = cast<uint8_t>(2*in(x, y, c));
    h(x, y, c) = f(x, y, c) + g(x, y, c);
    k(x, y, c) = f(x, y, c) - g(x, y, c);

    //g.compute_with(f, y);
    f.gpu_tile(x, y, x1, y1, 16, 16);
    g.gpu_tile(x, y, x1, y1, 16, 16);
    h.gpu_tile(x, y, x1, y1, 16, 16);
    k.gpu_tile(x, y, x1, y1, 16, 16);

    Halide::Target target = Halide::get_host_target();
    target.set_feature(Halide::Target::CUDA, true);

    Pipeline({f, g, h, k}).compile_to_object("build/generated_fct_fusion_ref.o", {in}, "fusion_ref", target);

    Pipeline({f, g, h, k}).compile_to_lowered_stmt("build/generated_fct_fusion_ref.txt", {in}, Halide::Text, target);

    return 0;
}
<commit_msg>fusiongpu name ref<commit_after>#include "Halide.h"

using namespace Halide;

int main(int argc, char **argv) {
    ImageParam in(UInt(8), 3, "input");

    Var x("x"), y("y"), c("c"), x1, y1;
    Func f("f"), g("g"), h("h"), k("k");

    f(x, y, c) = cast<uint8_t>(255 - in(x, y, c));
    g(x, y, c) = cast<uint8_t>(2*in(x, y, c));
    h(x, y, c) = f(x, y, c) + g(x, y, c);
    k(x, y, c) = f(x, y, c) - g(x, y, c);

    //g.compute_with(f, y);
    f.gpu_tile(x, y, x1, y1, 16, 16);
    g.gpu_tile(x, y, x1, y1, 16, 16);
    h.gpu_tile(x, y, x1, y1, 16, 16);
    k.gpu_tile(x, y, x1, y1, 16, 16);

    Halide::Target target = Halide::get_host_target();
    target.set_feature(Halide::Target::CUDA, true);

    Pipeline({f, g, h, k}).compile_to_object("build/generated_fct_fusiongpu_ref.o", {in}, "fusiongpu_ref", target);

    Pipeline({f, g, h, k}).compile_to_lowered_stmt("build/generated_fct_fusiongpu_ref.txt", {in}, Halide::Text, target);

    return 0;
}
<|endoftext|>


<commit_before>#include "imagefetcher.h"
using namespace AhoViewer::Booru;

int ImageFetcher::socket_cb(CURL*, curl_socket_t s, int action, void *userp, void *sockp)
{
    ImageFetcher *self = static_cast<ImageFetcher*>(userp);
    SockInfo *fdp = static_cast<SockInfo*>(sockp);

    if (action == CURL_POLL_REMOVE && fdp)
    {
        delete fdp;
    }
    else
    {
        if (!fdp)
        {
            fdp = new SockInfo();
            fdp->chan = Glib::IOChannel::create_from_fd(s);
            curl_multi_assign(self->m_MultiHandle, s, fdp);
        }

        Glib::IOCondition kind;
        if (action & CURL_POLL_IN)  kind |= Glib::IO_IN;
        if (action & CURL_POLL_OUT) kind |= Glib::IO_OUT;

        Glib::RefPtr<Glib::IOSource> source = fdp->chan->create_watch(kind);
        fdp->sockfd = s;

        if (fdp->conn)
            fdp->conn.disconnect();

        fdp->conn = source->connect(sigc::bind<0>(sigc::mem_fun(self, &ImageFetcher::event_cb), s));
        source->attach(self->m_MainContext);
    }

    return 0;
}

int ImageFetcher::timer_cb(CURLM*, long timeout_ms, void *userp)
{
    ImageFetcher *self = static_cast<ImageFetcher*>(userp);

    self->m_TimeoutConn = self->m_MainContext->signal_timeout().connect(
            sigc::mem_fun(self, &ImageFetcher::timeout_cb), timeout_ms);

    return 0;
}

ImageFetcher::ImageFetcher()
  : m_MainContext(Glib::MainContext::create()),
    m_MainLoop(Glib::MainLoop::create(m_MainContext)),
    m_Thread(nullptr),
    m_MultiHandle(curl_multi_init()),
    m_RunningHandles(0)
{
    m_Thread = Glib::Threads::Thread::create([ this ]() { m_MainLoop->run(); });

    curl_multi_setopt(m_MultiHandle, CURLMOPT_SOCKETFUNCTION, &ImageFetcher::socket_cb);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_SOCKETDATA, this);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_TIMERFUNCTION, &ImageFetcher::timer_cb);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_TIMERDATA, this);
}

ImageFetcher::~ImageFetcher()
{
    m_MainLoop->quit();
    m_Thread->join();
    m_Thread = nullptr;

    for (Curler *c : m_Curlers)
        remove_handle(c);

    curl_multi_cleanup(m_MultiHandle);
}

void ImageFetcher::add_handle(Curler *curler)
{
    Glib::Threads::Mutex::Lock lock(m_Mutex);

    curl_easy_setopt(curler->m_EasyHandle, CURLOPT_PRIVATE, curler);

    curler->m_Cancel->reset();
    curler->clear();

    m_Curlers.push_back(curler);
    curl_multi_add_handle(m_MultiHandle, curler->m_EasyHandle);

    curler->m_Active = true;
    curler->m_StartTime = std::chrono::steady_clock::now();
}

void ImageFetcher::remove_handle(Curler *curler)
{
    curl_multi_remove_handle(m_MultiHandle, curler->m_EasyHandle);
    m_Curlers.erase(std::remove(m_Curlers.begin(), m_Curlers.end(), curler), m_Curlers.end());

    curler->m_Active = false;
}

bool ImageFetcher::event_cb(curl_socket_t sockfd, Glib::IOCondition cond)
{
    int action = (cond & Glib::IO_IN ? CURL_CSELECT_IN : 0) |
                 (cond & Glib::IO_OUT ? CURL_CSELECT_OUT : 0);

    {
        Glib::Threads::Mutex::Lock lock(m_Mutex);
        curl_multi_socket_action(m_MultiHandle, sockfd, action, &m_RunningHandles);
    }
    read_info();

    if (m_RunningHandles == 0)
    {
        m_TimeoutConn.disconnect();
        return false;
    }

    return true;
}

bool ImageFetcher::timeout_cb()
{
    curl_multi_socket_action(m_MultiHandle, CURL_SOCKET_TIMEOUT, 0, &m_RunningHandles);
    read_info();

    return false;
}

void ImageFetcher::read_info()
{
    int msgs;
    CURLMsg *msg = nullptr;
    Glib::Threads::Mutex::Lock lock(m_Mutex);

    while ((msg = curl_multi_info_read(m_MultiHandle, &msgs)))
    {
        if (msg->msg == CURLMSG_DONE)
        {
            Curler *curler = nullptr;
            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curler);

            if (curler)
            {
                remove_handle(curler);

                if (!curler->is_cancelled())
                    curler->m_SignalFinished();
            }
        }
    }
}
<commit_msg>booru/imagefetcher: Add null checks in remove_handle<commit_after>#include "imagefetcher.h"
using namespace AhoViewer::Booru;

int ImageFetcher::socket_cb(CURL*, curl_socket_t s, int action, void *userp, void *sockp)
{
    ImageFetcher *self = static_cast<ImageFetcher*>(userp);
    SockInfo *fdp = static_cast<SockInfo*>(sockp);

    if (action == CURL_POLL_REMOVE && fdp)
    {
        delete fdp;
    }
    else
    {
        if (!fdp)
        {
            fdp = new SockInfo();
            fdp->chan = Glib::IOChannel::create_from_fd(s);
            curl_multi_assign(self->m_MultiHandle, s, fdp);
        }

        Glib::IOCondition kind;
        if (action & CURL_POLL_IN)  kind |= Glib::IO_IN;
        if (action & CURL_POLL_OUT) kind |= Glib::IO_OUT;

        Glib::RefPtr<Glib::IOSource> source = fdp->chan->create_watch(kind);
        fdp->sockfd = s;

        if (fdp->conn)
            fdp->conn.disconnect();

        fdp->conn = source->connect(sigc::bind<0>(sigc::mem_fun(self, &ImageFetcher::event_cb), s));
        source->attach(self->m_MainContext);
    }

    return 0;
}

int ImageFetcher::timer_cb(CURLM*, long timeout_ms, void *userp)
{
    ImageFetcher *self = static_cast<ImageFetcher*>(userp);

    self->m_TimeoutConn = self->m_MainContext->signal_timeout().connect(
            sigc::mem_fun(self, &ImageFetcher::timeout_cb), timeout_ms);

    return 0;
}

ImageFetcher::ImageFetcher()
  : m_MainContext(Glib::MainContext::create()),
    m_MainLoop(Glib::MainLoop::create(m_MainContext)),
    m_Thread(nullptr),
    m_MultiHandle(curl_multi_init()),
    m_RunningHandles(0)
{
    m_Thread = Glib::Threads::Thread::create([ this ]() { m_MainLoop->run(); });

    curl_multi_setopt(m_MultiHandle, CURLMOPT_SOCKETFUNCTION, &ImageFetcher::socket_cb);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_SOCKETDATA, this);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_TIMERFUNCTION, &ImageFetcher::timer_cb);
    curl_multi_setopt(m_MultiHandle, CURLMOPT_TIMERDATA, this);
}

ImageFetcher::~ImageFetcher()
{
    m_MainLoop->quit();
    m_Thread->join();
    m_Thread = nullptr;

    for (Curler *c : m_Curlers)
        remove_handle(c);

    curl_multi_cleanup(m_MultiHandle);
}

void ImageFetcher::add_handle(Curler *curler)
{
    Glib::Threads::Mutex::Lock lock(m_Mutex);

    curl_easy_setopt(curler->m_EasyHandle, CURLOPT_PRIVATE, curler);

    curler->m_Cancel->reset();
    curler->clear();

    m_Curlers.push_back(curler);
    curl_multi_add_handle(m_MultiHandle, curler->m_EasyHandle);

    curler->m_Active = true;
    curler->m_StartTime = std::chrono::steady_clock::now();
}

void ImageFetcher::remove_handle(Curler *curler)
{
    if (!curler)
        return;

    if (curler->m_EasyHandle)
        curl_multi_remove_handle(m_MultiHandle, curler->m_EasyHandle);

    m_Curlers.erase(std::remove(m_Curlers.begin(), m_Curlers.end(), curler), m_Curlers.end());

    curler->m_Active = false;
}

bool ImageFetcher::event_cb(curl_socket_t sockfd, Glib::IOCondition cond)
{
    int action = (cond & Glib::IO_IN ? CURL_CSELECT_IN : 0) |
                 (cond & Glib::IO_OUT ? CURL_CSELECT_OUT : 0);

    {
        Glib::Threads::Mutex::Lock lock(m_Mutex);
        curl_multi_socket_action(m_MultiHandle, sockfd, action, &m_RunningHandles);
    }
    read_info();

    if (m_RunningHandles == 0)
    {
        m_TimeoutConn.disconnect();
        return false;
    }

    return true;
}

bool ImageFetcher::timeout_cb()
{
    curl_multi_socket_action(m_MultiHandle, CURL_SOCKET_TIMEOUT, 0, &m_RunningHandles);
    read_info();

    return false;
}

void ImageFetcher::read_info()
{
    int msgs;
    CURLMsg *msg = nullptr;
    Glib::Threads::Mutex::Lock lock(m_Mutex);

    while ((msg = curl_multi_info_read(m_MultiHandle, &msgs)))
    {
        if (msg->msg == CURLMSG_DONE)
        {
            Curler *curler = nullptr;
            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &curler);

            if (curler)
            {
                remove_handle(curler);

                if (!curler->is_cancelled())
                    curler->m_SignalFinished();
            }
        }
    }
}
<|endoftext|>


<commit_before>#include <memory>
#include <limits>
#include <algorithm>
#include "GlError.hpp"
#include "Duration.hpp"
#include "GlMesh.hpp"

namespace
{
    const auto MAX_UINT = std::numeric_limits<unsigned int>::max();
    const auto MAX_GL_UNSIGNED_BYTE = std::numeric_limits<GLubyte>::max();
    const auto MAX_GL_UNSIGNED_SHORT = std::numeric_limits<GLushort>::max();
    const auto MAX_FLOAT = std::numeric_limits<float>::max();
    const auto MIN_FLOAT = -std::numeric_limits<float>::max();

    enum VertexAttributeBuffer{VERTEX_POSITION, VERTEX_TEXTURE_COORD, VERTEX_NORMAL, VERTEX_TANGENT, NB_VERTEX_ATTRIBUTES};

    void copy(GLfloat *dest, const glm::vec4 &source, std::size_t destWitdh, bool homogeneous = false)
    {
        switch(destWitdh)
        {
        case 4:
            dest[3] = source.w;
        case 3:
            dest[2] = source.z;
        case 2:
            dest[1] = source.y;
        case 1:
            dest[0] = source.x;
        }

        if (homogeneous && destWitdh < 4 && source.w != .0f)
        {
            switch(destWitdh)
            {
            case 3:
                dest[2] /= source.w;
            case 2:
                dest[1] /= source.w;
            case 1:
                dest[0] /= source.w;
            }
        }
    }

    void copy(GLfloat *dest, const glm::vec3 &source, std::size_t destWitdh)
    {
        switch(destWitdh)
        {
        case 4:
            dest[3] = 1.0f;
        case 3:
            dest[2] = source.z;
        case 2:
            dest[1] = source.y;
        case 1:
            dest[0] = source.x;
        }
    }

    VertexAttributeBuffer toVertexAttributeBuffer(const gl::VertexAttributeDeclaration &vad)
    {
        if (vad.name() == "vertexPosition")
        {
            return VERTEX_POSITION;
        }
        else if (vad.name() == "vertexNormal")
        {
            return VERTEX_NORMAL;
        }
        else if (vad.name() == "vertexTextureCoord")
        {
            return VERTEX_TEXTURE_COORD;
        }
        else if (vad.name() == "vertexTangent")
        {
            return VERTEX_TANGENT;
        }
        return NB_VERTEX_ATTRIBUTES;
    }

    struct VertexAttributeBufferDesc
    {
        GLuint index;
        VertexAttributeBuffer type;
        std::size_t offset;
        std::size_t size;
    };

    using VertexAttributeBufferDescVector = std::vector<VertexAttributeBufferDesc>;

    inline std::size_t computeVertexAttributesStructureSize(const VertexAttributeBufferDescVector &v)
    {
        return v.empty() ? 0 : v.back().offset + v.back().size;
    }

    VertexAttributeBufferDescVector createVertexAttributeBufferDescVector(const gl::VertexAttributeDeclarationVector &vads)
    {
        std::vector<VertexAttributeBufferDesc> vertexAttributeBufferDescVector;
        std::size_t vertexAttributePointerOffset = 0;
        VertexAttributeBufferDesc vabd;
        for(const gl::VertexAttributeDeclaration &vad : vads)
        {
            vabd.index = vad.index();
            vabd.size = vad.sizeOf() / sizeof(GLfloat);
            vabd.type = toVertexAttributeBuffer(vad);
            vabd.offset = vertexAttributePointerOffset;
            vertexAttributeBufferDescVector.push_back(vabd);

            vertexAttributePointerOffset += vabd.size;
        }
        return vertexAttributeBufferDescVector;
    }

    gl::GlMeshGeneration checkAndComputeVertexAttributes(const gl::VertexAttributeDeclarationVector &vads, vfm::ObjModel &objModel)
    {
        bool noBufferAvailable = false;
        int nbVertexAttributes = 0;
        for(const gl::VertexAttributeDeclaration &vad : vads)
        {
            switch(toVertexAttributeBuffer(vad))
            {
            case VERTEX_POSITION:
                noBufferAvailable = objModel.positions.empty();
                break;
            case VERTEX_NORMAL:
                if (objModel.normals.empty())
                {
                    objModel.computeNormals();
                    noBufferAvailable = objModel.normals.empty();
                }
                break;
            case VERTEX_TANGENT:
                if (objModel.tangents.empty())
                {
                    objModel.computeTangents();
                    noBufferAvailable = objModel.tangents.empty();
                }
                break;
            case VERTEX_TEXTURE_COORD:
                noBufferAvailable = objModel.textures.empty();
                break;

            default:
                noBufferAvailable = true;
            }

            if (noBufferAvailable)
            {
                return gl::GlMeshGeneration::failed(std::string("No buffer available for vertex attribute '") + vad.name() + "'!");
            }

            if (vad.type() != GL_FLOAT_VEC2 && vad.type() != GL_FLOAT_VEC3 && vad.type() != GL_FLOAT_VEC4)
            {
                return gl::GlMeshGeneration::failed(std::string("Invalid type for vertex attribute '") + vad.name() + "': expected float, vec2, vec3 or vec4.");
            }
            ++nbVertexAttributes;
        }

        if (nbVertexAttributes == 0)
        {
            return gl::GlMeshGeneration::failed("No vertex attributes found!");
        }
        return gl::GlMeshGeneration::succeeded();
    }

    void fillBuffer(GLfloat *tmpBuffer, gl::BoundingBox &boundingBox, const vfm::ObjModel &objModel, const VertexAttributeBufferDescVector &vertexAttributeBufferDescVector)
    {
        int tmpBufferOffset = 0;
        std::size_t vertexAttributesStructureSize = computeVertexAttributesStructureSize(vertexAttributeBufferDescVector);

        for(const vfm::Object &o : objModel.objects)
        {
            for(const vfm::VertexIndex &vertexIndex : o.vertexIndices)
            {
                for (const VertexAttributeBufferDesc &vabd : vertexAttributeBufferDescVector)
                {
                    switch (vabd.type) {
                    case VERTEX_POSITION:
                        if (vertexIndex.position != 0)
                        {
                            const glm::vec4 & position = objModel.positions[vertexIndex.position-1];
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], position, vabd.size, true);
                            boundingBox.accept(position.x / position.w, position.y / position.w, position.z / position.w);
                        }
                        break;
                    case VERTEX_NORMAL:
                        if (vertexIndex.normal != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.normals[vertexIndex.normal-1], vabd.size);
                        }
                        break;
                    case VERTEX_TANGENT:
                        if (vertexIndex.normal != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.tangents[vertexIndex.normal-1], vabd.size);
                        }
                        break;
                    case VERTEX_TEXTURE_COORD:
                        if (vertexIndex.texture != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.textures[vertexIndex.texture-1], vabd.size);
                        }
                        break;
                    default:
                        break;
                    }
                }
                tmpBufferOffset += vertexAttributesStructureSize;
            }
        }
    }

    template<typename T>
    void createPackedIndexBufferData(const vfm::ObjModel &objModel, std::size_t nbIndices)
    {
        auto indices = std::unique_ptr<T[]>(new T[nbIndices]);
        std::size_t i = 0;
        std::size_t startIndex = 0;
        for (const vfm::Object &o : objModel.objects)
        {
            for(std::size_t index : o.triangles)
            {
                indices[i++] = static_cast<T>(startIndex + index);
            }
            startIndex += o.vertexIndices.size();
        }

        glBufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(T), &indices[0], GL_STATIC_DRAW);
    }

}

const gl::MaterialIndex gl::MaterialHandler::NO_MATERIAL_INDEX = MAX_UINT;

gl::GlMesh::MaterialGroup::MaterialGroup(MaterialIndex index, std::size_t size) : index{index}, size{size} {}

gl::BoundingBox::BoundingBox() : min{MAX_FLOAT, MAX_FLOAT, MAX_FLOAT}, max{MIN_FLOAT, MIN_FLOAT, MIN_FLOAT}
{
}

void gl::BoundingBox::accept(float x, float y, float z)
{
    min.x = std::min(min.x, x);
    min.y = std::min(min.y, y);
    min.z = std::min(min.z, z);
    max.x = std::max(max.x, x);
    max.y = std::max(max.y, y);
    max.z = std::max(max.z, z);
}

gl::GlMesh::GlMesh() : _vertexArray{0}, _indexFormat{GL_UNSIGNED_SHORT}
{
}

void gl::GlMesh::render(gl::MaterialHandler *handler)
{
    glBindVertexArray(_vertexArray);
    std::for_each(_definedVertexAttributes.begin(), _definedVertexAttributes.end(), glEnableVertexAttribArray);

    std::size_t firstPrimitive = 0;
    std::size_t sizeofIndex = gl::glSizeof(_indexFormat);
    for(gl::GlMesh::MaterialGroup &materialGroup : _materialGroups)
    {
        if (handler)
        {
            handler->use(materialGroup.index);
        }
        glDrawElements(GL_TRIANGLES, materialGroup.size, _indexFormat, (void*)(firstPrimitive * sizeofIndex));
        firstPrimitive += materialGroup.size;
    }

    std::for_each(_definedVertexAttributes.begin(), _definedVertexAttributes.end(), glDisableVertexAttribArray);
    glBindVertexArray(0);
}

gl::GlMesh::~GlMesh()
{
    clear();
}

void gl::GlMesh::clear()
{
    glBindVertexArray(0);
    if (_vertexArray > 0)
    {
        glDeleteVertexArrays(1, &_vertexArray);
        _vertexArray = 0;
    }
    if (! _buffers.empty())
    {
        glDeleteBuffers(_buffers.size(), &_buffers[0]);
        _buffers.clear();
    }
    _boundingBox = {};
    _definedVertexAttributes.clear();
    _materialGroups.clear();
}

void gl::GlMesh::createMaterialGroups(const vfm::ObjModel &objModel)
{
    for(const vfm::Object &o : objModel.objects)
    {
        if(o.materialActivations.empty())
        {
            _materialGroups.push_back(MaterialGroup{MaterialHandler::NO_MATERIAL_INDEX, o.triangles.size()});
        }
        else
        {
            if(o.materialActivations[0].start > 0)
            {
                _materialGroups.push_back(MaterialGroup{MaterialHandler::NO_MATERIAL_INDEX, o.materialActivations[0].start});
            }
            for(const vfm::MaterialActivation &ma : o.materialActivations)
            {
                _materialGroups.push_back(MaterialGroup{ma.materialIndex, ma.end - ma.start});
            }
        }
    }
}

void gl::GlMesh::createIndexBufferData(const vfm::ObjModel &objModel)
{
    std::size_t nbIndices = objModel.nbTriangleVertices();

    if(nbIndices < MAX_GL_UNSIGNED_BYTE)
    {
        this->_indexFormat = GL_UNSIGNED_BYTE;
        createPackedIndexBufferData<GLubyte>(objModel, nbIndices);
    }
    else if(nbIndices < MAX_GL_UNSIGNED_SHORT)
    {
        this->_indexFormat = GL_UNSIGNED_SHORT;
        createPackedIndexBufferData<GLushort>(objModel, nbIndices);
    }
    else
    {
        this->_indexFormat = GL_UNSIGNED_INT;
        createPackedIndexBufferData<GLuint>(objModel, nbIndices);
    }
}

gl::GlMeshGeneration gl::GlMesh::generate(vfm::ObjModel &objModel, const gl::VertexAttributeDeclarationVector &vads)
{
    clear();
    sys::Duration duration;

    GlMeshGeneration result = checkAndComputeVertexAttributes(vads, objModel);
    if (!result) {
        return result;
    }

    createMaterialGroups(objModel);

    VertexAttributeBufferDescVector vertexAttributeBufferDescVector = createVertexAttributeBufferDescVector(vads);
    std::size_t vertexAttributesStructureSize = computeVertexAttributesStructureSize(vertexAttributeBufferDescVector);

    std::vector<GLfloat> vertexAttributeBuffer(objModel.nbVertexIndices() * vertexAttributesStructureSize);
    fillBuffer(vertexAttributeBuffer.data(), _boundingBox, objModel, vertexAttributeBufferDescVector);

    GlError glError;

    glGenVertexArrays(1, &_vertexArray);
    if (glError)
    {
        return GlMeshGeneration::failed(glError.toString("Error during vertex array generation"), duration.elapsed());
    }

    glBindVertexArray(_vertexArray);
    _buffers.resize(2);
    glGenBuffers(_buffers.size(), &_buffers[0]);
    if (glError)
    {
        glBindVertexArray(0);
        return GlMeshGeneration::failed(glError.toString("Error during buffers generation"), duration.elapsed());
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffers[0]);
    createIndexBufferData(objModel);

    glBindBuffer(GL_ARRAY_BUFFER, _buffers[1]);

    for (VertexAttributeBufferDesc vabd : vertexAttributeBufferDescVector)
    {
        glVertexAttribPointer(vabd.index, vabd.size, GL_FLOAT, (vabd.type == VERTEX_NORMAL ? GL_TRUE : GL_FALSE), (vertexAttributesStructureSize * sizeof(GL_FLOAT)), (void*)(vabd.offset  * sizeof(GL_FLOAT)));
        _definedVertexAttributes.push_back(vabd.index);
    }
    glBufferData(GL_ARRAY_BUFFER, vertexAttributeBuffer.size() * sizeof(GLfloat), vertexAttributeBuffer.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    if (glError)
    {
        return GlMeshGeneration::failed(glError.toString("defining vertex attribute"));
    }

    return GlMeshGeneration::succeeded(duration.elapsed());
}
<commit_msg>bind GL_ELEMENT_ARRAY_BUFFER at rendering time<commit_after>#include <memory>
#include <limits>
#include <algorithm>
#include "GlError.hpp"
#include "Duration.hpp"
#include "GlMesh.hpp"

namespace
{
    const auto MAX_UINT = std::numeric_limits<unsigned int>::max();
    const auto MAX_GL_UNSIGNED_BYTE = std::numeric_limits<GLubyte>::max();
    const auto MAX_GL_UNSIGNED_SHORT = std::numeric_limits<GLushort>::max();
    const auto MAX_FLOAT = std::numeric_limits<float>::max();
    const auto MIN_FLOAT = -std::numeric_limits<float>::max();

    enum VertexAttributeBuffer{VERTEX_POSITION, VERTEX_TEXTURE_COORD, VERTEX_NORMAL, VERTEX_TANGENT, NB_VERTEX_ATTRIBUTES};

    void copy(GLfloat *dest, const glm::vec4 &source, std::size_t destWitdh, bool homogeneous = false)
    {
        switch(destWitdh)
        {
        case 4:
            dest[3] = source.w;
        case 3:
            dest[2] = source.z;
        case 2:
            dest[1] = source.y;
        case 1:
            dest[0] = source.x;
        }

        if (homogeneous && destWitdh < 4 && source.w != .0f)
        {
            switch(destWitdh)
            {
            case 3:
                dest[2] /= source.w;
            case 2:
                dest[1] /= source.w;
            case 1:
                dest[0] /= source.w;
            }
        }
    }

    void copy(GLfloat *dest, const glm::vec3 &source, std::size_t destWitdh)
    {
        switch(destWitdh)
        {
        case 4:
            dest[3] = 1.0f;
        case 3:
            dest[2] = source.z;
        case 2:
            dest[1] = source.y;
        case 1:
            dest[0] = source.x;
        }
    }

    VertexAttributeBuffer toVertexAttributeBuffer(const gl::VertexAttributeDeclaration &vad)
    {
        if (vad.name() == "vertexPosition")
        {
            return VERTEX_POSITION;
        }
        else if (vad.name() == "vertexNormal")
        {
            return VERTEX_NORMAL;
        }
        else if (vad.name() == "vertexTextureCoord")
        {
            return VERTEX_TEXTURE_COORD;
        }
        else if (vad.name() == "vertexTangent")
        {
            return VERTEX_TANGENT;
        }
        return NB_VERTEX_ATTRIBUTES;
    }

    struct VertexAttributeBufferDesc
    {
        GLuint index;
        VertexAttributeBuffer type;
        std::size_t offset;
        std::size_t size;
    };

    using VertexAttributeBufferDescVector = std::vector<VertexAttributeBufferDesc>;

    inline std::size_t computeVertexAttributesStructureSize(const VertexAttributeBufferDescVector &v)
    {
        return v.empty() ? 0 : v.back().offset + v.back().size;
    }

    VertexAttributeBufferDescVector createVertexAttributeBufferDescVector(const gl::VertexAttributeDeclarationVector &vads)
    {
        std::vector<VertexAttributeBufferDesc> vertexAttributeBufferDescVector;
        std::size_t vertexAttributePointerOffset = 0;
        VertexAttributeBufferDesc vabd;
        for(const gl::VertexAttributeDeclaration &vad : vads)
        {
            vabd.index = vad.index();
            vabd.size = vad.sizeOf() / sizeof(GLfloat);
            vabd.type = toVertexAttributeBuffer(vad);
            vabd.offset = vertexAttributePointerOffset;
            vertexAttributeBufferDescVector.push_back(vabd);

            vertexAttributePointerOffset += vabd.size;
        }
        return vertexAttributeBufferDescVector;
    }

    gl::GlMeshGeneration checkAndComputeVertexAttributes(const gl::VertexAttributeDeclarationVector &vads, vfm::ObjModel &objModel)
    {
        bool noBufferAvailable = false;
        int nbVertexAttributes = 0;
        for(const gl::VertexAttributeDeclaration &vad : vads)
        {
            switch(toVertexAttributeBuffer(vad))
            {
            case VERTEX_POSITION:
                noBufferAvailable = objModel.positions.empty();
                break;
            case VERTEX_NORMAL:
                if (objModel.normals.empty())
                {
                    objModel.computeNormals();
                    noBufferAvailable = objModel.normals.empty();
                }
                break;
            case VERTEX_TANGENT:
                if (objModel.tangents.empty())
                {
                    objModel.computeTangents();
                    noBufferAvailable = objModel.tangents.empty();
                }
                break;
            case VERTEX_TEXTURE_COORD:
                noBufferAvailable = objModel.textures.empty();
                break;

            default:
                noBufferAvailable = true;
            }

            if (noBufferAvailable)
            {
                return gl::GlMeshGeneration::failed(std::string("No buffer available for vertex attribute '") + vad.name() + "'!");
            }

            if (vad.type() != GL_FLOAT_VEC2 && vad.type() != GL_FLOAT_VEC3 && vad.type() != GL_FLOAT_VEC4)
            {
                return gl::GlMeshGeneration::failed(std::string("Invalid type for vertex attribute '") + vad.name() + "': expected float, vec2, vec3 or vec4.");
            }
            ++nbVertexAttributes;
        }

        if (nbVertexAttributes == 0)
        {
            return gl::GlMeshGeneration::failed("No vertex attributes found!");
        }
        return gl::GlMeshGeneration::succeeded();
    }

    void fillBuffer(GLfloat *tmpBuffer, gl::BoundingBox &boundingBox, const vfm::ObjModel &objModel, const VertexAttributeBufferDescVector &vertexAttributeBufferDescVector)
    {
        int tmpBufferOffset = 0;
        std::size_t vertexAttributesStructureSize = computeVertexAttributesStructureSize(vertexAttributeBufferDescVector);

        for(const vfm::Object &o : objModel.objects)
        {
            for(const vfm::VertexIndex &vertexIndex : o.vertexIndices)
            {
                for (const VertexAttributeBufferDesc &vabd : vertexAttributeBufferDescVector)
                {
                    switch (vabd.type) {
                    case VERTEX_POSITION:
                        if (vertexIndex.position != 0)
                        {
                            const glm::vec4 & position = objModel.positions[vertexIndex.position-1];
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], position, vabd.size, true);
                            boundingBox.accept(position.x / position.w, position.y / position.w, position.z / position.w);
                        }
                        break;
                    case VERTEX_NORMAL:
                        if (vertexIndex.normal != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.normals[vertexIndex.normal-1], vabd.size);
                        }
                        break;
                    case VERTEX_TANGENT:
                        if (vertexIndex.normal != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.tangents[vertexIndex.normal-1], vabd.size);
                        }
                        break;
                    case VERTEX_TEXTURE_COORD:
                        if (vertexIndex.texture != 0)
                        {
                            copy(&tmpBuffer[tmpBufferOffset + vabd.offset], objModel.textures[vertexIndex.texture-1], vabd.size);
                        }
                        break;
                    default:
                        break;
                    }
                }
                tmpBufferOffset += vertexAttributesStructureSize;
            }
        }
    }

    template<typename T>
    void createPackedIndexBufferData(const vfm::ObjModel &objModel, std::size_t nbIndices)
    {
        auto indices = std::unique_ptr<T[]>(new T[nbIndices]);
        std::size_t i = 0;
        std::size_t startIndex = 0;
        for (const vfm::Object &o : objModel.objects)
        {
            for(std::size_t index : o.triangles)
            {
                indices[i++] = static_cast<T>(startIndex + index);
            }
            startIndex += o.vertexIndices.size();
        }

        glBufferData(GL_ELEMENT_ARRAY_BUFFER, nbIndices * sizeof(T), &indices[0], GL_STATIC_DRAW);
    }

}

const gl::MaterialIndex gl::MaterialHandler::NO_MATERIAL_INDEX = MAX_UINT;

gl::GlMesh::MaterialGroup::MaterialGroup(MaterialIndex index, std::size_t size) : index{index}, size{size} {}

gl::BoundingBox::BoundingBox() : min{MAX_FLOAT, MAX_FLOAT, MAX_FLOAT}, max{MIN_FLOAT, MIN_FLOAT, MIN_FLOAT}
{
}

void gl::BoundingBox::accept(float x, float y, float z)
{
    min.x = std::min(min.x, x);
    min.y = std::min(min.y, y);
    min.z = std::min(min.z, z);
    max.x = std::max(max.x, x);
    max.y = std::max(max.y, y);
    max.z = std::max(max.z, z);
}

gl::GlMesh::GlMesh() : _vertexArray{0}, _indexFormat{GL_UNSIGNED_SHORT}
{
}

void gl::GlMesh::render(gl::MaterialHandler *handler)
{
    glBindVertexArray(_vertexArray);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffers[0]);
    std::for_each(_definedVertexAttributes.begin(), _definedVertexAttributes.end(), glEnableVertexAttribArray);

    std::size_t firstPrimitive = 0;
    std::size_t sizeofIndex = gl::glSizeof(_indexFormat);
    for(gl::GlMesh::MaterialGroup &materialGroup : _materialGroups)
    {
        if (handler)
        {
            handler->use(materialGroup.index);
        }
        glDrawElements(GL_TRIANGLES, materialGroup.size, _indexFormat, (void*)(firstPrimitive * sizeofIndex));
        firstPrimitive += materialGroup.size;
    }

    std::for_each(_definedVertexAttributes.begin(), _definedVertexAttributes.end(), glDisableVertexAttribArray);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

gl::GlMesh::~GlMesh()
{
    clear();
}

void gl::GlMesh::clear()
{
    glBindVertexArray(0);
    if (_vertexArray > 0)
    {
        glDeleteVertexArrays(1, &_vertexArray);
        _vertexArray = 0;
    }
    if (! _buffers.empty())
    {
        glDeleteBuffers(_buffers.size(), &_buffers[0]);
        _buffers.clear();
    }
    _boundingBox = {};
    _definedVertexAttributes.clear();
    _materialGroups.clear();
}

void gl::GlMesh::createMaterialGroups(const vfm::ObjModel &objModel)
{
    for(const vfm::Object &o : objModel.objects)
    {
        if(o.materialActivations.empty())
        {
            _materialGroups.push_back(MaterialGroup{MaterialHandler::NO_MATERIAL_INDEX, o.triangles.size()});
        }
        else
        {
            if(o.materialActivations[0].start > 0)
            {
                _materialGroups.push_back(MaterialGroup{MaterialHandler::NO_MATERIAL_INDEX, o.materialActivations[0].start});
            }
            for(const vfm::MaterialActivation &ma : o.materialActivations)
            {
                _materialGroups.push_back(MaterialGroup{ma.materialIndex, ma.end - ma.start});
            }
        }
    }
}

void gl::GlMesh::createIndexBufferData(const vfm::ObjModel &objModel)
{
    std::size_t nbIndices = objModel.nbTriangleVertices();

    if(nbIndices < MAX_GL_UNSIGNED_BYTE)
    {
        this->_indexFormat = GL_UNSIGNED_BYTE;
        createPackedIndexBufferData<GLubyte>(objModel, nbIndices);
    }
    else if(nbIndices < MAX_GL_UNSIGNED_SHORT)
    {
        this->_indexFormat = GL_UNSIGNED_SHORT;
        createPackedIndexBufferData<GLushort>(objModel, nbIndices);
    }
    else
    {
        this->_indexFormat = GL_UNSIGNED_INT;
        createPackedIndexBufferData<GLuint>(objModel, nbIndices);
    }
}

gl::GlMeshGeneration gl::GlMesh::generate(vfm::ObjModel &objModel, const gl::VertexAttributeDeclarationVector &vads)
{
    clear();
    sys::Duration duration;

    GlMeshGeneration result = checkAndComputeVertexAttributes(vads, objModel);
    if (!result) {
        return result;
    }

    createMaterialGroups(objModel);

    VertexAttributeBufferDescVector vertexAttributeBufferDescVector = createVertexAttributeBufferDescVector(vads);
    std::size_t vertexAttributesStructureSize = computeVertexAttributesStructureSize(vertexAttributeBufferDescVector);

    std::vector<GLfloat> vertexAttributeBuffer(objModel.nbVertexIndices() * vertexAttributesStructureSize);
    fillBuffer(vertexAttributeBuffer.data(), _boundingBox, objModel, vertexAttributeBufferDescVector);

    GlError glError;

    glGenVertexArrays(1, &_vertexArray);
    if (glError)
    {
        return GlMeshGeneration::failed(glError.toString("Error during vertex array generation"), duration.elapsed());
    }

    glBindVertexArray(_vertexArray);
    _buffers.resize(2);
    glGenBuffers(_buffers.size(), &_buffers[0]);
    if (glError)
    {
        glBindVertexArray(0);
        return GlMeshGeneration::failed(glError.toString("Error during buffers generation"), duration.elapsed());
    }

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _buffers[0]);
    createIndexBufferData(objModel);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glBindBuffer(GL_ARRAY_BUFFER, _buffers[1]);

    for (VertexAttributeBufferDesc vabd : vertexAttributeBufferDescVector)
    {
        glVertexAttribPointer(vabd.index, vabd.size, GL_FLOAT, (vabd.type == VERTEX_NORMAL ? GL_TRUE : GL_FALSE), (vertexAttributesStructureSize * sizeof(GL_FLOAT)), (void*)(vabd.offset  * sizeof(GL_FLOAT)));
        _definedVertexAttributes.push_back(vabd.index);
    }
    glBufferData(GL_ARRAY_BUFFER, vertexAttributeBuffer.size() * sizeof(GLfloat), vertexAttributeBuffer.data(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    if (glError)
    {
        return GlMeshGeneration::failed(glError.toString("defining vertex attribute"));
    }

    return GlMeshGeneration::succeeded(duration.elapsed());
}
<|endoftext|>


<commit_before>#include <io/network/tcpSocket.h>
#include <logging.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
static constexpr int flags = 0;

static inline int send(SOCKET s, const void* msg, size_t len, int flags)
{
    return send(s, static_cast<const char*>(msg), static_cast<int>(len), flags);
}

static inline int recv(SOCKET s, void* buf, size_t len, int flags)
{
    return recv(s, static_cast<char*>(buf), static_cast<int>(len), flags);
}

#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <poll.h>
static constexpr int flags = MSG_NOSIGNAL;
static constexpr intptr_t INVALID_SOCKET = -1;
#endif

extern "C" {
    struct X509;
    struct X509_STORE;
    struct SSL_CTX;
    struct SSL_METHOD;
    struct SSL;

    static X509_STORE* (*X509_STORE_new)();
    static X509* (*d2i_X509)(X509**, const unsigned char **, long);
    static int (*X509_STORE_add_cert)(X509_STORE*, X509*);
    static void (*X509_free)(X509*);
    static SSL_CTX* (*SSL_CTX_new)(const SSL_METHOD*);
    static const SSL_METHOD* (*TLSv1_2_client_method)();
    static long (*SSL_CTX_set_options)(SSL_CTX*, long);
    static int (*SSL_CTX_set_default_verify_paths)(SSL_CTX*);
    static void (*SSL_CTX_set_cert_store)(SSL_CTX*, X509_STORE*);
    static SSL* (*SSL_new)(SSL_CTX*);
    static int (*SSL_set_fd)(SSL *ssl, int fd);
    static int (*SSL_connect)(SSL *ssl);
    static long (*SSL_get_verify_result)(const SSL *ssl);
    static int (*SSL_read)(SSL *ssl, void *buf, int num);
    static int (*SSL_write)(SSL *ssl, const void *buf, int num);
    static void (*SSL_free)(SSL *ssl);

    static SSL_CTX* ssl_context;
}

# define SSL_OP_NO_SSLv2                                 0x01000000L
# define SSL_OP_NO_SSLv3                                 0x02000000L
# define SSL_OP_NO_TLSv1                                 0x04000000L
# define SSL_OP_NO_TLSv1_2                               0x08000000L
# define SSL_OP_NO_TLSv1_1                               0x10000000L

#ifndef __ANDROID__
#include "dynamicLibrary.h"

static std::unique_ptr<DynamicLibrary> libcrypto;
static std::unique_ptr<DynamicLibrary> libssl;
#endif

static void initializeLibSSL()
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

#ifndef __ANDROID__
#ifdef _WIN32
    libcrypto = DynamicLibrary::open("libcrypto-1_1.dll");
    libssl = DynamicLibrary::open("libssl-1_1.dll");
#else
    libcrypto = DynamicLibrary::open("libcrypto.so.1.1");
    libssl = DynamicLibrary::open("libssl.so.1.1");
#endif
    if (!libcrypto || !libssl)
        return;

    X509_STORE_new = libcrypto->getFunction<X509_STORE*(*)()>("X509_STORE_new");
    d2i_X509 = libcrypto->getFunction<X509* (*)(X509**, const unsigned char **, long)>("d2i_X509");
    X509_STORE_add_cert = libcrypto->getFunction<int (*)(X509_STORE*, X509*)>("X509_STORE_add_cert");
    X509_free = libcrypto->getFunction<void (*)(X509*)>("X509_free");

    SSL_CTX_new = libssl->getFunction<SSL_CTX*(*)(const SSL_METHOD*)>("SSL_CTX_new");
    TLSv1_2_client_method = libssl->getFunction<const SSL_METHOD* (*)()>("TLSv1_2_client_method");
    SSL_CTX_set_options = libssl->getFunction<long (*)(SSL_CTX*, long)>("SSL_CTX_set_options");
    SSL_CTX_set_default_verify_paths = libssl->getFunction<int (*)(SSL_CTX*)>("SSL_CTX_set_default_verify_paths");
    SSL_CTX_set_cert_store = libssl->getFunction<void (*)(SSL_CTX*, X509_STORE*)>("SSL_CTX_set_cert_store");
    SSL_new = libssl->getFunction<SSL* (*)(SSL_CTX*)>("SSL_new");
    SSL_set_fd = libssl->getFunction<int (*)(SSL *ssl, int fd)>("SSL_set_fd");
    SSL_connect = libssl->getFunction<int (*)(SSL *ssl)>("SSL_connect");
    SSL_get_verify_result = libssl->getFunction<long (*)(const SSL *ssl)>("SSL_get_verify_result");
    SSL_read = libssl->getFunction<int (*)(SSL *ssl, void *buf, int num)>("SSL_read");
    SSL_write = libssl->getFunction<int (*)(SSL *ssl, const void *buf, int num)>("SSL_write");
    SSL_free = libssl->getFunction<void (*)(SSL *ssl)>("SSL_free");

#ifdef _WIN32
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = NULL;
    X509 *x509;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(0, "ROOT");
    while((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
    {
        const unsigned char* c = pContext->pbCertEncoded;
        x509 = d2i_X509(nullptr, &c, pContext->cbCertEncoded);
        if (x509)
        {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }
    CertFreeCertificateContext(pContext);
    CertCloseStore(hStore, 0);
#endif

    ssl_context = SSL_CTX_new(TLSv1_2_client_method());
    SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#ifdef _WIN32
    SSL_CTX_set_cert_store(ssl_context, store);
#else
    SSL_CTX_set_default_verify_paths(ssl_context);
#endif
#endif
}

namespace sp {
namespace io {
namespace network {

TcpSocket::TcpSocket()
: ssl_handle(nullptr)
{
}

TcpSocket::~TcpSocket()
{
    close();
}

bool TcpSocket::connect(const Address& host, int port)
{
    if (handle != INVALID_SOCKET)
        close();

    for(const auto& addr_info : host.addr_info)
    {
        handle = ::socket(addr_info.family, SOCK_STREAM, 0);
        if (handle == INVALID_SOCKET)
            return false;
        setBlocking(blocking);
        if (addr_info.family == AF_INET && sizeof(struct sockaddr_in) == addr_info.addr.size())
        {
            struct sockaddr_in server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.size());
            server_addr.sin_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
                return true;
            if (isLastErrorNonBlocking())
            {
                connecting = true;
                return true;
            }
        }
        if (addr_info.family == AF_INET6 && sizeof(struct sockaddr_in6) == addr_info.addr.size())
        {
            struct sockaddr_in6 server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.size());
            server_addr.sin6_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
                return true;
            if (isLastErrorNonBlocking())
            {
                connecting = true;
                return true;
            }
        }
        close();
    }
    return false;
}

bool TcpSocket::connectSSL(const Address& host, int port)
{
    if (!connect(host, port))
        return false;
    initializeLibSSL();
    if (!SSL_new)
    {
        LOG(Warning, "Failed to connect SSL socket due to missing libssl/libcrypto v1.1");
        close();
        return false;
    }

    ssl_handle = SSL_new(ssl_context);
    SSL_set_fd(static_cast<SSL*>(ssl_handle), static_cast<int>(handle));
    if (!SSL_connect(static_cast<SSL*>(ssl_handle)))
    {
        LOG(Warning, "Failed to connect SSL socket due to SSL negotiation failure.");
        close();
        return false;
    }
    if (SSL_get_verify_result(static_cast<SSL*>(ssl_handle)) != 0)
    {
        LOG(Warning, "Failed to connect SSL socket due to certificate verfication failure.");
        close();
        return false;
    }
    return true;
}

void TcpSocket::setDelay(bool delay)
{
    if (handle == INVALID_SOCKET)
    {
        LOG(Warning, "Failed to setDelay due to being called on an incomplete socket");
        return;
    }
    int mode = delay ? 0 : 1;
    if (setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, (char*)&mode, sizeof(mode)) == -1)
    {
        LOG(Warning, "Failed to setDelay on a socket");
    }
}

void TcpSocket::close()
{
    if (handle != INVALID_SOCKET)
    {
#ifdef _WIN32
        closesocket(handle);
#else
        ::close(handle);
#endif
        handle = INVALID_SOCKET;
        connecting = false;
        clearQueue();
        if (ssl_handle)
            SSL_free(static_cast<SSL*>(ssl_handle));
        ssl_handle = nullptr;
    }
}

StreamSocket::State TcpSocket::getState()
{
    if (handle == INVALID_SOCKET)
        return StreamSocket::State::Closed;
    if (connecting) {
        struct pollfd fds;
        fds.fd = handle;
        fds.events = POLLOUT;
        fds.revents = 0;
#ifdef WIN32
        if (WSAPoll(&fds, 1, 0))
#else
        if (poll(&fds, 1, 0))
#endif
        {
            struct sockaddr_in6 server_addr;
#ifdef WIN32
            int server_addr_len = sizeof(server_addr);
#else
            size_t server_addr_len = sizeof(server_addr);
#endif
            if (getpeername(handle, reinterpret_cast<sockaddr*>(&server_addr), &server_addr_len))
            {
                close();
                return StreamSocket::State::Closed;
            }
            connecting = false;
            return StreamSocket::State::Connected;
        }
        return StreamSocket::State::Connecting;
    }
    return StreamSocket::State::Connected;
}

size_t TcpSocket::_send(const void* data, size_t size)
{
    int result;
    if (ssl_handle)
        result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(data), static_cast<int>(size));
    else
        result = ::send(handle, reinterpret_cast<const void *>(static_cast<const char*>(data)), size, flags);
    if (result < 0)
    {
        if (!isLastErrorNonBlocking())
            close();
        return 0;
    }
    return result;
}

size_t TcpSocket::_receive(void* data, size_t size)
{
    int result;
    if (ssl_handle)
        result = SSL_read(static_cast<SSL*>(ssl_handle), static_cast<char*>(data), static_cast<int>(size));
    else
        result = ::recv(handle, data, size, flags);
    if (result < 0)
    {
        result = 0;
        if (!isLastErrorNonBlocking())
            close();
    }
    return result;
}

}//namespace network
}//namespace io
}//namespace sp
<commit_msg>linux build fix 3<commit_after>#include <io/network/tcpSocket.h>
#include <logging.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
static constexpr int flags = 0;

static inline int send(SOCKET s, const void* msg, size_t len, int flags)
{
    return send(s, static_cast<const char*>(msg), static_cast<int>(len), flags);
}

static inline int recv(SOCKET s, void* buf, size_t len, int flags)
{
    return recv(s, static_cast<char*>(buf), static_cast<int>(len), flags);
}

#else
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <string.h>
#include <poll.h>
static constexpr int flags = MSG_NOSIGNAL;
static constexpr intptr_t INVALID_SOCKET = -1;
#endif

extern "C" {
    struct X509;
    struct X509_STORE;
    struct SSL_CTX;
    struct SSL_METHOD;
    struct SSL;

    static X509_STORE* (*X509_STORE_new)();
    static X509* (*d2i_X509)(X509**, const unsigned char **, long);
    static int (*X509_STORE_add_cert)(X509_STORE*, X509*);
    static void (*X509_free)(X509*);
    static SSL_CTX* (*SSL_CTX_new)(const SSL_METHOD*);
    static const SSL_METHOD* (*TLSv1_2_client_method)();
    static long (*SSL_CTX_set_options)(SSL_CTX*, long);
    static int (*SSL_CTX_set_default_verify_paths)(SSL_CTX*);
    static void (*SSL_CTX_set_cert_store)(SSL_CTX*, X509_STORE*);
    static SSL* (*SSL_new)(SSL_CTX*);
    static int (*SSL_set_fd)(SSL *ssl, int fd);
    static int (*SSL_connect)(SSL *ssl);
    static long (*SSL_get_verify_result)(const SSL *ssl);
    static int (*SSL_read)(SSL *ssl, void *buf, int num);
    static int (*SSL_write)(SSL *ssl, const void *buf, int num);
    static void (*SSL_free)(SSL *ssl);

    static SSL_CTX* ssl_context;
}

# define SSL_OP_NO_SSLv2                                 0x01000000L
# define SSL_OP_NO_SSLv3                                 0x02000000L
# define SSL_OP_NO_TLSv1                                 0x04000000L
# define SSL_OP_NO_TLSv1_2                               0x08000000L
# define SSL_OP_NO_TLSv1_1                               0x10000000L

#ifndef __ANDROID__
#include "dynamicLibrary.h"

static std::unique_ptr<DynamicLibrary> libcrypto;
static std::unique_ptr<DynamicLibrary> libssl;
#endif

static void initializeLibSSL()
{
    static bool initialized = false;
    if (initialized) return;
    initialized = true;

#ifndef __ANDROID__
#ifdef _WIN32
    libcrypto = DynamicLibrary::open("libcrypto-1_1.dll");
    libssl = DynamicLibrary::open("libssl-1_1.dll");
#else
    libcrypto = DynamicLibrary::open("libcrypto.so.1.1");
    libssl = DynamicLibrary::open("libssl.so.1.1");
#endif
    if (!libcrypto || !libssl)
        return;

    X509_STORE_new = libcrypto->getFunction<X509_STORE*(*)()>("X509_STORE_new");
    d2i_X509 = libcrypto->getFunction<X509* (*)(X509**, const unsigned char **, long)>("d2i_X509");
    X509_STORE_add_cert = libcrypto->getFunction<int (*)(X509_STORE*, X509*)>("X509_STORE_add_cert");
    X509_free = libcrypto->getFunction<void (*)(X509*)>("X509_free");

    SSL_CTX_new = libssl->getFunction<SSL_CTX*(*)(const SSL_METHOD*)>("SSL_CTX_new");
    TLSv1_2_client_method = libssl->getFunction<const SSL_METHOD* (*)()>("TLSv1_2_client_method");
    SSL_CTX_set_options = libssl->getFunction<long (*)(SSL_CTX*, long)>("SSL_CTX_set_options");
    SSL_CTX_set_default_verify_paths = libssl->getFunction<int (*)(SSL_CTX*)>("SSL_CTX_set_default_verify_paths");
    SSL_CTX_set_cert_store = libssl->getFunction<void (*)(SSL_CTX*, X509_STORE*)>("SSL_CTX_set_cert_store");
    SSL_new = libssl->getFunction<SSL* (*)(SSL_CTX*)>("SSL_new");
    SSL_set_fd = libssl->getFunction<int (*)(SSL *ssl, int fd)>("SSL_set_fd");
    SSL_connect = libssl->getFunction<int (*)(SSL *ssl)>("SSL_connect");
    SSL_get_verify_result = libssl->getFunction<long (*)(const SSL *ssl)>("SSL_get_verify_result");
    SSL_read = libssl->getFunction<int (*)(SSL *ssl, void *buf, int num)>("SSL_read");
    SSL_write = libssl->getFunction<int (*)(SSL *ssl, const void *buf, int num)>("SSL_write");
    SSL_free = libssl->getFunction<void (*)(SSL *ssl)>("SSL_free");

#ifdef _WIN32
    HCERTSTORE hStore;
    PCCERT_CONTEXT pContext = NULL;
    X509 *x509;
    X509_STORE *store = X509_STORE_new();

    hStore = CertOpenSystemStore(0, "ROOT");
    while((pContext = CertEnumCertificatesInStore(hStore, pContext)) != nullptr)
    {
        const unsigned char* c = pContext->pbCertEncoded;
        x509 = d2i_X509(nullptr, &c, pContext->cbCertEncoded);
        if (x509)
        {
            X509_STORE_add_cert(store, x509);
            X509_free(x509);
        }
    }
    CertFreeCertificateContext(pContext);
    CertCloseStore(hStore, 0);
#endif

    ssl_context = SSL_CTX_new(TLSv1_2_client_method());
    SSL_CTX_set_options(ssl_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#ifdef _WIN32
    SSL_CTX_set_cert_store(ssl_context, store);
#else
    SSL_CTX_set_default_verify_paths(ssl_context);
#endif
#endif
}

namespace sp {
namespace io {
namespace network {

TcpSocket::TcpSocket()
: ssl_handle(nullptr)
{
}

TcpSocket::~TcpSocket()
{
    close();
}

bool TcpSocket::connect(const Address& host, int port)
{
    if (handle != INVALID_SOCKET)
        close();

    for(const auto& addr_info : host.addr_info)
    {
        handle = ::socket(addr_info.family, SOCK_STREAM, 0);
        if (handle == INVALID_SOCKET)
            return false;
        setBlocking(blocking);
        if (addr_info.family == AF_INET && sizeof(struct sockaddr_in) == addr_info.addr.size())
        {
            struct sockaddr_in server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.size());
            server_addr.sin_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
                return true;
            if (isLastErrorNonBlocking())
            {
                connecting = true;
                return true;
            }
        }
        if (addr_info.family == AF_INET6 && sizeof(struct sockaddr_in6) == addr_info.addr.size())
        {
            struct sockaddr_in6 server_addr;
            memset(&server_addr, 0, sizeof(server_addr));
            memcpy(&server_addr, addr_info.addr.data(), addr_info.addr.size());
            server_addr.sin6_port = htons(port);
            if (::connect(handle, reinterpret_cast<const sockaddr*>(&server_addr), sizeof(server_addr)) == 0)
                return true;
            if (isLastErrorNonBlocking())
            {
                connecting = true;
                return true;
            }
        }
        close();
    }
    return false;
}

bool TcpSocket::connectSSL(const Address& host, int port)
{
    if (!connect(host, port))
        return false;
    initializeLibSSL();
    if (!SSL_new)
    {
        LOG(Warning, "Failed to connect SSL socket due to missing libssl/libcrypto v1.1");
        close();
        return false;
    }

    ssl_handle = SSL_new(ssl_context);
    SSL_set_fd(static_cast<SSL*>(ssl_handle), static_cast<int>(handle));
    if (!SSL_connect(static_cast<SSL*>(ssl_handle)))
    {
        LOG(Warning, "Failed to connect SSL socket due to SSL negotiation failure.");
        close();
        return false;
    }
    if (SSL_get_verify_result(static_cast<SSL*>(ssl_handle)) != 0)
    {
        LOG(Warning, "Failed to connect SSL socket due to certificate verfication failure.");
        close();
        return false;
    }
    return true;
}

void TcpSocket::setDelay(bool delay)
{
    if (handle == INVALID_SOCKET)
    {
        LOG(Warning, "Failed to setDelay due to being called on an incomplete socket");
        return;
    }
    int mode = delay ? 0 : 1;
    if (setsockopt(handle, IPPROTO_TCP, TCP_NODELAY, (char*)&mode, sizeof(mode)) == -1)
    {
        LOG(Warning, "Failed to setDelay on a socket");
    }
}

void TcpSocket::close()
{
    if (handle != INVALID_SOCKET)
    {
#ifdef _WIN32
        closesocket(handle);
#else
        ::close(handle);
#endif
        handle = INVALID_SOCKET;
        connecting = false;
        clearQueue();
        if (ssl_handle)
            SSL_free(static_cast<SSL*>(ssl_handle));
        ssl_handle = nullptr;
    }
}

StreamSocket::State TcpSocket::getState()
{
    if (handle == INVALID_SOCKET)
        return StreamSocket::State::Closed;
    if (connecting) {
        struct pollfd fds;
        fds.fd = handle;
        fds.events = POLLOUT;
        fds.revents = 0;
#ifdef WIN32
        if (WSAPoll(&fds, 1, 0))
#else
        if (poll(&fds, 1, 0))
#endif
        {
            struct sockaddr_in6 server_addr;
            socklen_t server_addr_len = sizeof(server_addr);
            if (getpeername(handle, reinterpret_cast<sockaddr*>(&server_addr), &server_addr_len))
            {
                close();
                return StreamSocket::State::Closed;
            }
            connecting = false;
            return StreamSocket::State::Connected;
        }
        return StreamSocket::State::Connecting;
    }
    return StreamSocket::State::Connected;
}

size_t TcpSocket::_send(const void* data, size_t size)
{
    int result;
    if (ssl_handle)
        result = SSL_write(static_cast<SSL*>(ssl_handle), static_cast<const char*>(data), static_cast<int>(size));
    else
        result = ::send(handle, reinterpret_cast<const void *>(static_cast<const char*>(data)), size, flags);
    if (result < 0)
    {
        if (!isLastErrorNonBlocking())
            close();
        return 0;
    }
    return result;
}

size_t TcpSocket::_receive(void* data, size_t size)
{
    int result;
    if (ssl_handle)
        result = SSL_read(static_cast<SSL*>(ssl_handle), static_cast<char*>(data), static_cast<int>(size));
    else
        result = ::recv(handle, data, size, flags);
    if (result < 0)
    {
        result = 0;
        if (!isLastErrorNonBlocking())
            close();
    }
    return result;
}

}//namespace network
}//namespace io
}//namespace sp
<|endoftext|>


<commit_before>// Copyright (c) 2015-2017 William W. Fisher (at gmail dot com)
// This file is distributed under the MIT License.

#include "ofp/sys/asio_utils.h"
#include "ofp/unittest.h"

TEST(asio, async_connect_v4) {
  auto localhost_v4 = asio::ip::address_v4::loopback();

  asio::error_code result;
  asio::io_context io;
  asio::ip::tcp::socket socket{io};

  EXPECT_FALSE(socket.is_open());

  socket.async_connect(
      {localhost_v4, 9},
      [&result](const asio::error_code &error) { result = error; });

  EXPECT_TRUE(socket.is_open());

  io.run();

  EXPECT_TRUE(socket.is_open());
  EXPECT_EQ(asio::error::connection_refused, result);

  socket.close();
  EXPECT_FALSE(socket.is_open());
}

static bool isConnectionRefusedOrNetworkUnreachable(asio::error_code &err) {
  return (err == asio::error::connection_refused) ||
         (err == asio::error::network_unreachable);
}

TEST(asio, async_connect_v6) {
  auto localhost_v6 = asio::ip::address_v6::loopback();

  asio::error_code result;
  asio::io_context io;
  asio::ip::tcp::socket socket{io};

  socket.async_connect(
      {localhost_v6, 9},
      [&result](const asio::error_code &error) { result = error; });

  io.run();

  EXPECT_TRUE(socket.is_open());

  // A network connection to ::1 may be unreachable because IPv6 is disabled on
  // the loopback interface.
  EXPECT_TRUE(isConnectionRefusedOrNetworkUnreachable(result));

  socket.close();
  EXPECT_FALSE(socket.is_open());
}

TEST(asio, chrono_time_traits) {
  // Make sure that there is no overflow in the implementation of duration_cast
  // in asio::detail::chrono_time_traits.
  //
  // This test was written on Mac OS X; it may need to be revised for systems
  // where the steady_clock has less than nanosecond precision.

  using Clock = std::chrono::steady_clock;
  using TimeTraits =
      asio::detail::chrono_time_traits<Clock, asio::wait_traits<Clock>>;

  const int64_t value = 9223231944883008317LL;
  std::chrono::nanoseconds nano{value};

  TimeTraits::posix_time_duration test1{nano};
  EXPECT_EQ(value, test1.ticks());

  EXPECT_EQ(value / 1000000000, test1.total_seconds());
  EXPECT_EQ(value / 1000000, test1.total_milliseconds());
  EXPECT_EQ(value / 1000, test1.total_microseconds());
}
<commit_msg>Handle address_not_available error on Alpine Linux 3.6.2.<commit_after>// Copyright (c) 2015-2017 William W. Fisher (at gmail dot com)
// This file is distributed under the MIT License.

#include "ofp/sys/asio_utils.h"
#include "ofp/unittest.h"

TEST(asio, async_connect_v4) {
  auto localhost_v4 = asio::ip::address_v4::loopback();

  asio::error_code result;
  asio::io_context io;
  asio::ip::tcp::socket socket{io};

  EXPECT_FALSE(socket.is_open());

  socket.async_connect(
      {localhost_v4, 9},
      [&result](const asio::error_code &error) { result = error; });

  EXPECT_TRUE(socket.is_open());

  io.run();

  EXPECT_TRUE(socket.is_open());
  EXPECT_EQ(asio::error::connection_refused, result);

  socket.close();
  EXPECT_FALSE(socket.is_open());
}

static bool isConnectionRefusedOrNetworkUnreachable(asio::error_code &err) {
  // (2017-08-21) On Alpine Linux 3.6.2 the result can also be EADDRNOTAVAIL
  // when IPv6 loopback not enabled.
  return (err == asio::error::connection_refused) ||
         (err == asio::error::network_unreachable) ||
         (err == std::errc::address_not_available);
}

TEST(asio, async_connect_v6) {
  auto localhost_v6 = asio::ip::address_v6::loopback();

  asio::error_code result;
  asio::io_context io;
  asio::ip::tcp::socket socket{io};

  socket.async_connect(
      {localhost_v6, 9},
      [&result](const asio::error_code &error) { result = error; });

  io.run();

  EXPECT_TRUE(socket.is_open());

  // A network connection to ::1 may be unreachable because IPv6 is disabled on
  // the loopback interface.
  EXPECT_TRUE(isConnectionRefusedOrNetworkUnreachable(result));

  socket.close();
  EXPECT_FALSE(socket.is_open());
}

TEST(asio, chrono_time_traits) {
  // Make sure that there is no overflow in the implementation of duration_cast
  // in asio::detail::chrono_time_traits.
  //
  // This test was written on Mac OS X; it may need to be revised for systems
  // where the steady_clock has less than nanosecond precision.

  using Clock = std::chrono::steady_clock;
  using TimeTraits =
      asio::detail::chrono_time_traits<Clock, asio::wait_traits<Clock>>;

  const int64_t value = 9223231944883008317LL;
  std::chrono::nanoseconds nano{value};

  TimeTraits::posix_time_duration test1{nano};
  EXPECT_EQ(value, test1.ticks());

  EXPECT_EQ(value / 1000000000, test1.total_seconds());
  EXPECT_EQ(value / 1000000, test1.total_milliseconds());
  EXPECT_EQ(value / 1000, test1.total_microseconds());
}
<|endoftext|>


<commit_before>#pragma once

#include <exception>
#include <map>
#include <sstream>
#include <string>

namespace ipmiblob
{

class IpmiException : public std::exception
{
  public:
    const std::map<int, std::string> commonFailures = {
        {0xc0, "busy"},
        {0xc1, "invalid"},
        {0xc3, "timeout"},
    };

    explicit IpmiException(int cc)
    {
        std::ostringstream smessage;

        auto search = commonFailures.find(cc);
        if (search != commonFailures.end())
        {
            smessage << "Received IPMI_CC: " << search->second;
        }
        else
        {
            smessage << "Received IPMI_CC: " << cc;
        }

        message = smessage.str();
    }
    explicit IpmiException(const std::string& message) : message(message){};

    virtual const char* what() const noexcept override
    {
        return message.c_str();
    }

  private:
    std::string message;
};

} // namespace ipmiblob
<commit_msg>ipmi_errors: object cleanup<commit_after>#pragma once

#include <exception>
#include <map>
#include <string>

namespace ipmiblob
{

class IpmiException : public std::exception
{
  public:
    explicit IpmiException(const std::string& message) : _message(message){};

    static std::string messageFromIpmi(int cc)
    {
        const std::map<int, std::string> commonFailures = {
            {0xc0, "busy"},
            {0xc1, "invalid"},
            {0xc3, "timeout"},
        };

        auto search = commonFailures.find(cc);
        if (search != commonFailures.end())
        {
            return "Received IPMI_CC: " + search->second;
        }
        else
        {
            return "Received IPMI_CC: " + std::to_string(cc);
        }
    }

    explicit IpmiException(int cc) : IpmiException(messageFromIpmi(cc))
    {
    }

    virtual const char* what() const noexcept override
    {
        return _message.c_str();
    }

  private:
    std::string _message;
};

} // namespace ipmiblob
<|endoftext|>