<commit_before>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "router/database_sqlite.h"

#include "base/logging.h"
#include "base/files/base_paths.h"
#include "base/strings/unicode.h"
#include "build/build_config.h"

#include <optional>

namespace router {

namespace {

template <typename T>
std::optional<T> readInteger(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_INTEGER)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_INTEGER";
        return std::nullopt;
    }

    return static_cast<T>(sqlite3_column_int64(statement, column));
}

std::optional<base::ByteArray> readBlob(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_BLOB)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_BLOB";
        return std::nullopt;
    }

    int blob_size = sqlite3_column_bytes(statement, column);
    if (blob_size <= 0)
    {
        LOG(LS_ERROR) << "Field has an invalid size";
        return std::nullopt;
    }

    const void* blob = sqlite3_column_blob(statement, column);
    if (!blob)
    {
        LOG(LS_ERROR) << "Failed to get the pointer to the field";
        return std::nullopt;
    }

    return base::fromData(blob, static_cast<size_t>(blob_size));
}

std::optional<std::string> readText(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_TEXT)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_TEXT";
        return std::nullopt;
    }

    int string_size = sqlite3_column_bytes(statement, column);
    if (string_size <= 0)
    {
        LOG(LS_ERROR) << "Field has an invalid size";
        return std::nullopt;
    }

    const uint8_t* string = sqlite3_column_text(statement, column);
    if (!string)
    {
        LOG(LS_ERROR) << "Failed to get the pointer to the field";
        return std::nullopt;
    }

    return std::string(reinterpret_cast<const char*>(string), string_size);
}

std::optional<std::u16string> readText16(sqlite3_stmt* statement, int column)
{
    std::optional<std::string> str = readText(statement, column);
    if (!str.has_value())
        return std::nullopt;

    return base::utf16FromUtf8(str.value());
}

std::optional<net::User> readUser(sqlite3_stmt* statement)
{
    std::optional<uint64_t> entry_id = readInteger<uint64_t>(statement, 0);
    if (!entry_id.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'id'";
        return std::nullopt;
    }

    std::optional<std::u16string> name = readText16(statement, 1);
    if (!name.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'name'";
        return std::nullopt;
    }

    std::optional<std::string> group = readText(statement, 2);
    if (!group.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'group'";
        return std::nullopt;
    }

    std::optional<base::ByteArray> salt = readBlob(statement, 3);
    if (!salt.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'salt'";
        return std::nullopt;
    }

    std::optional<base::ByteArray> verifier = readBlob(statement, 4);
    if (!verifier.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'verifier'";
        return std::nullopt;
    }

    std::optional<uint32_t> sessions = readInteger<uint32_t>(statement, 5);
    if (!sessions.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'sessions'";
        return std::nullopt;
    }

    std::optional<uint32_t> flags = readInteger<uint32_t>(statement, 6);
    if (!flags.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'flags'";
        return std::nullopt;
    }

    net::User user;

    user.entry_id  = entry_id.value();
    user.name      = std::move(name.value());
    user.group     = std::move(group.value());
    user.salt      = std::move(salt.value());
    user.verifier  = std::move(verifier.value());
    user.sessions  = sessions.value();
    user.flags     = flags.value();

    return user;
}

} // namespace

DatabaseSqlite::DatabaseSqlite(sqlite3* db)
    : db_(db)
{
    DCHECK(db_);
}

DatabaseSqlite::~DatabaseSqlite()
{
    sqlite3_close(db_);
}

// static
std::unique_ptr<DatabaseSqlite> DatabaseSqlite::open()
{
    std::filesystem::path file_path = filePath();
    if (file_path.empty())
    {
        LOG(LS_WARNING) << "Invalid file path";
        return nullptr;
    }

    sqlite3* db = nullptr;

    int error_code = sqlite3_open(file_path.u8string().c_str(), &db);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_WARNING) << "sqlite3_open failed: " << sqlite3_errstr(error_code);
        return nullptr;
    }

    return std::unique_ptr<DatabaseSqlite>(new DatabaseSqlite(db));
}

// static
std::filesystem::path DatabaseSqlite::filePath()
{
    std::filesystem::path file_path;

#if defined(OS_WIN)
    if (!base::BasePaths::currentExecDir(&file_path))
        return std::filesystem::path();

    file_path.append(u"aspia_router.db3");
#else // defined(OS_*)
#error Not implemented
#endif // defined(OS_*)

    return file_path;
}

net::UserList DatabaseSqlite::userList() const
{
    const char kQuery[] = "SELECT * FROM users";

    sqlite3_stmt* statement;
    int error_code = sqlite3_prepare(db_, kQuery, std::size(kQuery), &statement, nullptr);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_prepare failed: " << sqlite3_errstr(error_code);
        return net::UserList();
    }

    net::UserList users;
    for (;;)
    {
        if (sqlite3_step(statement) != SQLITE_ROW)
            break;

        std::optional<net::User> user = readUser(statement);
        if (user.has_value())
            users.add(std::move(user.value()));
    }

    sqlite3_finalize(statement);
    return users;
}

bool DatabaseSqlite::addUser(const net::User& user)
{
    NOTIMPLEMENTED();
    return false;
}

bool DatabaseSqlite::removeUser(uint64_t entry_id)
{
    NOTIMPLEMENTED();
    return false;
}

uint64_t DatabaseSqlite::peerId(std::string_view key) const
{
    NOTIMPLEMENTED();
    return 0;
}

} // namespace router
<commit_msg>Implemented users management in database.<commit_after>//
// Aspia Project
// Copyright (C) 2020 Dmitry Chapyshev <dmitry@aspia.ru>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

#include "router/database_sqlite.h"

#include "base/logging.h"
#include "base/files/base_paths.h"
#include "base/strings/strcat.h"
#include "base/strings/unicode.h"
#include "build/build_config.h"

#include <optional>

namespace router {

namespace {

bool writeText(sqlite3_stmt* statement, const std::string& text, int column)
{
    int error_code = sqlite3_bind_text(statement, column, text.c_str(), text.length(), SQLITE_STATIC);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_bind_text failed: " << sqlite3_errstr(error_code)
                      << " (column: " << column << ")";
        return false;
    }

    return true;
}

bool writeBlob(sqlite3_stmt* statement, const base::ByteArray& blob, int column)
{
    int error_code = sqlite3_bind_blob(statement, column, blob.data(), blob.size(), SQLITE_STATIC);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_bind_blob failed: " << sqlite3_errstr(error_code)
                      << " (column: " << column << ")";
        return false;
    }

    return true;
}

bool writeInt(sqlite3_stmt* statement, int number, int column)
{
    int error_code = sqlite3_bind_int(statement, column, number);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_bind_int failed: " << sqlite3_errstr(error_code)
                      << " (column: " << column << ")";
        return false;
    }

    return true;
}

bool writeInt64(sqlite3_stmt* statement, int64_t number, int column)
{
    int error_code = sqlite3_bind_int64(statement, column, number);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_bind_int64 failed: " << sqlite3_errstr(error_code)
                      << " (column: " << column << ")";
        return false;
    }

    return true;
}

template <typename T>
std::optional<T> readInteger(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_INTEGER)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_INTEGER";
        return std::nullopt;
    }

    return static_cast<T>(sqlite3_column_int64(statement, column));
}

std::optional<base::ByteArray> readBlob(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_BLOB)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_BLOB";
        return std::nullopt;
    }

    int blob_size = sqlite3_column_bytes(statement, column);
    if (blob_size <= 0)
    {
        LOG(LS_ERROR) << "Field has an invalid size";
        return std::nullopt;
    }

    const void* blob = sqlite3_column_blob(statement, column);
    if (!blob)
    {
        LOG(LS_ERROR) << "Failed to get the pointer to the field";
        return std::nullopt;
    }

    return base::fromData(blob, static_cast<size_t>(blob_size));
}

std::optional<std::string> readText(sqlite3_stmt* statement, int column)
{
    int column_type = sqlite3_column_type(statement, column);
    if (column_type != SQLITE_TEXT)
    {
        LOG(LS_ERROR) << "Type is not SQLITE_TEXT";
        return std::nullopt;
    }

    int string_size = sqlite3_column_bytes(statement, column);
    if (string_size <= 0)
    {
        LOG(LS_ERROR) << "Field has an invalid size";
        return std::nullopt;
    }

    const uint8_t* string = sqlite3_column_text(statement, column);
    if (!string)
    {
        LOG(LS_ERROR) << "Failed to get the pointer to the field";
        return std::nullopt;
    }

    return std::string(reinterpret_cast<const char*>(string), string_size);
}

std::optional<std::u16string> readText16(sqlite3_stmt* statement, int column)
{
    std::optional<std::string> str = readText(statement, column);
    if (!str.has_value())
        return std::nullopt;

    return base::utf16FromUtf8(str.value());
}

std::optional<net::User> readUser(sqlite3_stmt* statement)
{
    std::optional<uint64_t> entry_id = readInteger<uint64_t>(statement, 0);
    if (!entry_id.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'id'";
        return std::nullopt;
    }

    std::optional<std::u16string> name = readText16(statement, 1);
    if (!name.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'name'";
        return std::nullopt;
    }

    std::optional<std::string> group = readText(statement, 2);
    if (!group.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'group'";
        return std::nullopt;
    }

    std::optional<base::ByteArray> salt = readBlob(statement, 3);
    if (!salt.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'salt'";
        return std::nullopt;
    }

    std::optional<base::ByteArray> verifier = readBlob(statement, 4);
    if (!verifier.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'verifier'";
        return std::nullopt;
    }

    std::optional<uint32_t> sessions = readInteger<uint32_t>(statement, 5);
    if (!sessions.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'sessions'";
        return std::nullopt;
    }

    std::optional<uint32_t> flags = readInteger<uint32_t>(statement, 6);
    if (!flags.has_value())
    {
        LOG(LS_ERROR) << "Failed to get field 'flags'";
        return std::nullopt;
    }

    net::User user;

    user.entry_id  = entry_id.value();
    user.name      = std::move(name.value());
    user.group     = std::move(group.value());
    user.salt      = std::move(salt.value());
    user.verifier  = std::move(verifier.value());
    user.sessions  = sessions.value();
    user.flags     = flags.value();

    return user;
}

} // namespace

DatabaseSqlite::DatabaseSqlite(sqlite3* db)
    : db_(db)
{
    DCHECK(db_);
}

DatabaseSqlite::~DatabaseSqlite()
{
    sqlite3_close(db_);
}

// static
std::unique_ptr<DatabaseSqlite> DatabaseSqlite::open()
{
    std::filesystem::path file_path = filePath();
    if (file_path.empty())
    {
        LOG(LS_WARNING) << "Invalid file path";
        return nullptr;
    }

    sqlite3* db = nullptr;

    int error_code = sqlite3_open(file_path.u8string().c_str(), &db);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_WARNING) << "sqlite3_open failed: " << sqlite3_errstr(error_code);
        return nullptr;
    }

    return std::unique_ptr<DatabaseSqlite>(new DatabaseSqlite(db));
}

// static
std::filesystem::path DatabaseSqlite::filePath()
{
    std::filesystem::path file_path;

#if defined(OS_WIN)
    if (!base::BasePaths::currentExecDir(&file_path))
        return std::filesystem::path();

    file_path.append(u"aspia_router.db3");
#else // defined(OS_*)
#error Not implemented
#endif // defined(OS_*)

    return file_path;
}

net::UserList DatabaseSqlite::userList() const
{
    const char kQuery[] = "SELECT * FROM users";

    sqlite3_stmt* statement;
    int error_code = sqlite3_prepare(db_, kQuery, std::size(kQuery), &statement, nullptr);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_prepare failed: " << sqlite3_errstr(error_code);
        return net::UserList();
    }

    net::UserList users;
    for (;;)
    {
        if (sqlite3_step(statement) != SQLITE_ROW)
            break;

        std::optional<net::User> user = readUser(statement);
        if (user.has_value())
            users.add(std::move(user.value()));
    }

    sqlite3_finalize(statement);
    return users;
}

bool DatabaseSqlite::addUser(const net::User& user)
{
    if (!user.isValid())
        return false;

    static const char kQuery[] =
        "INSERT INTO users (name, group, salt, verifier, sessions, flags) VALUES (?, ?, ?, ?, ?, ?)";

    sqlite3_stmt* statement = nullptr;
    int error_code = sqlite3_prepare(db_, kQuery, std::size(kQuery), &statement, nullptr);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_prepare failed: " << sqlite3_errstr(error_code);
        return false;
    }

    do
    {
        if (!writeText(statement, base::utf8FromUtf16(user.name), 1))
            break;

        if (!writeText(statement, user.group, 2))
            break;

        if (!writeBlob(statement, user.salt, 3))
            break;

        if (!writeBlob(statement, user.verifier, 4))
            break;

        if (!writeInt(statement, static_cast<int>(user.sessions), 5))
            break;

        if (!writeInt(statement, static_cast<int>(user.flags), 6))
            break;

        error_code = sqlite3_step(statement);
        if (error_code != SQLITE_OK)
        {
            LOG(LS_ERROR) << "sqlite3_step failed: " << sqlite3_errstr(error_code);
        }
    }
    while (false);

    sqlite3_finalize(statement);
    return true;
}

bool DatabaseSqlite::removeUser(uint64_t entry_id)
{
    static const char kQuery[] = "DELETE FROM users WHERE id=?";

    sqlite3_stmt* statement = nullptr;
    int error_code = sqlite3_prepare(db_, kQuery, std::size(kQuery), &statement, nullptr);
    if (error_code != SQLITE_OK)
    {
        LOG(LS_ERROR) << "sqlite3_prepare failed: " << sqlite3_errstr(error_code);
        return false;
    }

    do
    {
        if (!writeInt64(statement, static_cast<int64_t>(entry_id), 1))
            break;

        error_code = sqlite3_step(statement);
        if (error_code != SQLITE_OK)
        {
            LOG(LS_ERROR) << "sqlite3_step failed: " << sqlite3_errstr(error_code);
        }
    }
    while (false);

    sqlite3_finalize(statement);
    return true;
}

uint64_t DatabaseSqlite::peerId(std::string_view key) const
{
    NOTIMPLEMENTED();
    return 0;
}

} // namespace router
<|endoftext|>


<commit_before>#include "OsreEdApp.h"
#include "Modules/ModuleBase.h"
#include "Modules/InspectorModule/InspectorModule.h"
#include <osre/Platform/AbstractWindow.h>
#include <osre/Platform/PlatformOperations.h>
#include <osre/RenderBackend/RenderBackendService.h>
#include <osre/RenderBackend/RenderCommon.h>
#include <osre/Scene/TrackBall.h>
#include <osre/App/AssimpWrapper.h>
#include <osre/UI/Canvas.h>
#include <osre/UI/Panel.h>
#include <osre/IO/Directory.h>
#include <osre/IO/Uri.h>
#include <osre/App/Entity.h>

namespace OSRE {
namespace Editor {

using namespace ::OSRE::App;
using namespace ::OSRE::RenderBackend;

static const ui32 HorizontalMargin = 2;
static const ui32 VerticalMargin = 2;

OsreEdApp::UiScreen::UiScreen() :
        m_canvas( nullptr ),
        m_mainPanel( nullptr ),
        m_logPanel( nullptr ),
        m_modelPanel( nullptr ) {
    // empty
}

OsreEdApp::OsreEdApp(int argc, char *argv[]) :
        AppBase(argc, (const char **)argv, "api", "The render API"),
        mUiScreen(),
        mModuleArray(),
        mModulePathArray(),
        mTrackBall( nullptr ){
    // empty
}

OsreEdApp::~OsreEdApp() {
    // empty
}

bool OsreEdApp::addModulePath( const String &path ) {
    if (path.empty()) {
        return false;
    }

    if (mModulePathArray.find(path) == nullptr) {
        mModulePathArray.add(path);
    }

    return true;
}

bool OsreEdApp::registerModule( ModuleBase *mod ) {
    if (nullptr == mod) {
        return false;
    }

    if (nullptr == mModuleArray.find(mod) ) {
        mModuleArray.add(mod);
    }

    return true;
}

bool OsreEdApp::loadModules() {
    // Load registered modules
    for (size_t i = 0; i < mModuleArray.size(); ++i) {
        ModuleBase *mod = mModuleArray[i];
        if (!mod->load(this)) {
            return false;
        }
    }

    return true;
}

UI::Panel *OsreEdApp::getRootPanel() const {
    return mUiScreen.m_mainPanel;
}

bool OsreEdApp::onCreate() {
    if (!AppBase::onCreate()) {
        return false;
    }

    //registerModule(new InspectorModule());
    //loadModules();

    /*Rect2ui r;
    AppBase::getRootWindow()->getWindowsRect(r);*/
    /*mUiScreen.m_canvas = AppBase::createScreen("OsreEd");
    mUiScreen.m_canvas->setRect(r);

    mUiScreen.m_mainPanel = new UI::Panel("main_panel", mUiScreen.m_canvas);
    mUiScreen.m_mainPanel->setRect(r);
    mUiScreen.m_logPanel = new UI::Panel("log_panel", mUiScreen.m_mainPanel);
    mUiScreen.m_logPanel->setHeadline("Log");
    mUiScreen.m_logPanel->setRect(r.getX1() + HorizontalMargin, r.getY1() + r.getHeight() / 3,
        r.getWidth() - 2 * HorizontalMargin, r.getHeight() - (r.getY1() + r.getHeight() / 3) - 2 * VerticalMargin);
    mUiScreen.m_modelPanel = new UI::Panel("model_panel", mUiScreen.m_mainPanel);
    mUiScreen.m_modelPanel->setHeadline("Model");*/

    AppBase::setWindowsTitle("OSRE ED! Press o to import an Asset");

    return true;
}

void OsreEdApp::loadAsset(const IO::Uri &modelLoc) {
    AssimpWrapper assimpWrapper(*getIdContainer());
    if (!assimpWrapper.importAsset(modelLoc, 0)) {
        return;
    }

    RenderBackendService *rbSrv = getRenderBackendService();
    if (nullptr == rbSrv) {
        return;
    }
    Platform::AbstractWindow *rootWindow = getRootWindow();
    if (nullptr == rootWindow) {
        return;
    }
    mStage = AppBase::createStage("ModelLoading");
    AppBase::setActiveStage(mStage);
    m_view = mStage->addView("default_view", nullptr);
    AppBase::setActiveView(m_view);

    Rect2ui windowsRect;
    rootWindow->getWindowsRect(windowsRect);
    mTrackBall = new Scene::TrackBall("trackball", windowsRect.getWidth(), windowsRect.getHeight());
    m_view->setProjectionParameters(60.f, (f32)windowsRect.m_width, (f32)windowsRect.m_height, 0.01f, 1000.f);
    Entity *entity = assimpWrapper.getEntity();

    World *world = getActiveWorld();
    world->addEntity(entity);
    m_view->observeBoundingBox(entity->getAABB());
    m_modelNode = entity->getNode();
}

void OsreEdApp::onUpdate() {
    if (AppBase::isKeyPressed(Platform::KEY_O)) {
        IO::Uri modelLoc;
        Platform::PlatformOperations::getFileOpenDialog("*", modelLoc);
        if (modelLoc.isValid()) {
            loadAsset(modelLoc);
        }
    }
    i32 x = 0, y = 0;
    if (AppBase::isMouseMoved(x, y)) {

    }

    /*for (ui32 i = 0; i < mModuleArray.size(); ++i) {
        mModuleArray[i]->update();
    }*/

            // Rotate the model
    glm::mat4 rot(1.0);
    m_transformMatrix.m_model = glm::rotate(rot, m_angle, glm::vec3(0, 1, 1));

    m_angle += 0.01f;
    RenderBackendService *rbSrv = getRenderBackendService();

    rbSrv->beginPass(PipelinePass::getPassNameById(RenderPassId));
    rbSrv->beginRenderBatch("b1");

    rbSrv->setMatrix(MatrixType::Model, m_transformMatrix.m_model);

    rbSrv->endRenderBatch();
    rbSrv->endPass();

    AppBase::onUpdate();
}

bool OsreEdApp::onDestroy() {
    return true;
}

} // namespace Editor
} // namespace OSRE
<commit_msg>add menu to osre_ed.<commit_after>#include "OsreEdApp.h"
#include "Modules/ModuleBase.h"
#include "Modules/InspectorModule/InspectorModule.h"
#include <osre/Platform/AbstractWindow.h>
#include <osre/Platform/PlatformOperations.h>
#include <osre/RenderBackend/RenderBackendService.h>
#include <osre/RenderBackend/RenderCommon.h>
#include <osre/Scene/TrackBall.h>
#include <osre/App/AssimpWrapper.h>
#include <osre/UI/Canvas.h>
#include <osre/UI/Panel.h>
#include <osre/IO/Directory.h>
#include <osre/IO/Uri.h>
#include <osre/App/Entity.h>

#include "Engine/Platform/win32/Win32Window.h"

namespace OSRE {
namespace Editor {

using namespace ::OSRE::App;
using namespace ::OSRE::RenderBackend;

static const ui32 HorizontalMargin = 2;
static const ui32 VerticalMargin = 2;

#ifdef OSRE_WINDOWS

#define IDM_FILE_NEW 1
#define IDM_FILE_OPEN 2
#define IDM_FILE_SAVE 3
#define IDM_FILE_IMPORT 4
#define IDM_FILE_QUIT 5

#define IDM_INFO_VERSION 6

void AddFileMenus(HWND hwnd) {
    HMENU hMenubar;
    HMENU hMenu;

    hMenubar = CreateMenu();
    hMenu = CreateMenu();

    AppendMenuW(hMenu, MF_STRING, IDM_FILE_NEW, L"&New");
    AppendMenuW(hMenu, MF_STRING, IDM_FILE_OPEN, L"&Open Project");
    AppendMenuW(hMenu, MF_STRING, IDM_FILE_SAVE, L"&Save Project");
    AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
    AppendMenuW(hMenu, MF_STRING, IDM_FILE_OPEN, L"&Import");
    AppendMenuW(hMenu, MF_SEPARATOR, 0, NULL);
    AppendMenuW(hMenu, MF_STRING, IDM_FILE_QUIT, L"&Quit");

    AppendMenuW(hMenubar, MF_POPUP, (UINT_PTR)hMenu, L"&File");

    hMenu = CreateMenu();
    AppendMenuW(hMenu, MF_STRING, IDM_INFO_VERSION, L"&Version");
    AppendMenuW(hMenubar, MF_POPUP, (UINT_PTR)hMenu, L"&Info");

    SetMenu(hwnd, hMenubar);
}

#endif // OSRE_WINDOWS

OsreEdApp::UiScreen::UiScreen() :
        m_canvas( nullptr ),
        m_mainPanel( nullptr ),
        m_logPanel( nullptr ),
        m_modelPanel( nullptr ) {
    // empty
}

OsreEdApp::OsreEdApp(int argc, char *argv[]) :
        AppBase(argc, (const char **)argv, "api", "The render API"),
        mUiScreen(),
        mModuleArray(),
        mModulePathArray(),
        mTrackBall( nullptr ){
    // empty
}

OsreEdApp::~OsreEdApp() {
    // empty
}

bool OsreEdApp::addModulePath( const String &path ) {
    if (path.empty()) {
        return false;
    }

    if (mModulePathArray.find(path) == nullptr) {
        mModulePathArray.add(path);
    }

    return true;
}

bool OsreEdApp::registerModule( ModuleBase *mod ) {
    if (nullptr == mod) {
        return false;
    }

    if (nullptr == mModuleArray.find(mod) ) {
        mModuleArray.add(mod);
    }

    return true;
}

bool OsreEdApp::loadModules() {
    // Load registered modules
    for (size_t i = 0; i < mModuleArray.size(); ++i) {
        ModuleBase *mod = mModuleArray[i];
        if (!mod->load(this)) {
            return false;
        }
    }

    return true;
}

UI::Panel *OsreEdApp::getRootPanel() const {
    return mUiScreen.m_mainPanel;
}

bool OsreEdApp::onCreate() {
    if (!AppBase::onCreate()) {
        return false;
    }

    //registerModule(new InspectorModule());
    //loadModules();

    /*Rect2ui r;
    AppBase::getRootWindow()->getWindowsRect(r);*/
    /*mUiScreen.m_canvas = AppBase::createScreen("OsreEd");
    mUiScreen.m_canvas->setRect(r);

    mUiScreen.m_mainPanel = new UI::Panel("main_panel", mUiScreen.m_canvas);
    mUiScreen.m_mainPanel->setRect(r);
    mUiScreen.m_logPanel = new UI::Panel("log_panel", mUiScreen.m_mainPanel);
    mUiScreen.m_logPanel->setHeadline("Log");
    mUiScreen.m_logPanel->setRect(r.getX1() + HorizontalMargin, r.getY1() + r.getHeight() / 3,
        r.getWidth() - 2 * HorizontalMargin, r.getHeight() - (r.getY1() + r.getHeight() / 3) - 2 * VerticalMargin);
    mUiScreen.m_modelPanel = new UI::Panel("model_panel", mUiScreen.m_mainPanel);
    mUiScreen.m_modelPanel->setHeadline("Model");*/

    AppBase::setWindowsTitle("OSRE ED! Press o to import an Asset");

    Platform::Win32Window *w = (Platform::Win32Window *)getRootWindow();
    if (nullptr != w) {
        AddFileMenus(w->getHWnd());
    }

    return true;
}

void OsreEdApp::loadAsset(const IO::Uri &modelLoc) {
    AssimpWrapper assimpWrapper(*getIdContainer());
    if (!assimpWrapper.importAsset(modelLoc, 0)) {
        return;
    }

    RenderBackendService *rbSrv = getRenderBackendService();
    if (nullptr == rbSrv) {
        return;
    }
    Platform::AbstractWindow *rootWindow = getRootWindow();
    if (nullptr == rootWindow) {
        return;
    }
    mStage = AppBase::createStage("ModelLoading");
    AppBase::setActiveStage(mStage);
    m_view = mStage->addView("default_view", nullptr);
    AppBase::setActiveView(m_view);

    Rect2ui windowsRect;
    rootWindow->getWindowsRect(windowsRect);
    mTrackBall = new Scene::TrackBall("trackball", windowsRect.getWidth(), windowsRect.getHeight());
    m_view->setProjectionParameters(60.f, (f32)windowsRect.m_width, (f32)windowsRect.m_height, 0.01f, 1000.f);
    Entity *entity = assimpWrapper.getEntity();

    World *world = getActiveWorld();
    world->addEntity(entity);
    m_view->observeBoundingBox(entity->getAABB());
    m_modelNode = entity->getNode();
}

void OsreEdApp::onUpdate() {
    if (AppBase::isKeyPressed(Platform::KEY_O)) {
        IO::Uri modelLoc;
        Platform::PlatformOperations::getFileOpenDialog("*", modelLoc);
        if (modelLoc.isValid()) {
            loadAsset(modelLoc);
        }
    }
    i32 x = 0, y = 0;
    if (AppBase::isMouseMoved(x, y)) {

    }

    /*for (ui32 i = 0; i < mModuleArray.size(); ++i) {
        mModuleArray[i]->update();
    }*/

            // Rotate the model
    glm::mat4 rot(1.0);
    m_transformMatrix.m_model = glm::rotate(rot, m_angle, glm::vec3(0, 1, 1));

    m_angle += 0.01f;
    RenderBackendService *rbSrv = getRenderBackendService();

    rbSrv->beginPass(PipelinePass::getPassNameById(RenderPassId));
    rbSrv->beginRenderBatch("b1");

    rbSrv->setMatrix(MatrixType::Model, m_transformMatrix.m_model);

    rbSrv->endRenderBatch();
    rbSrv->endPass();

    AppBase::onUpdate();
}

bool OsreEdApp::onDestroy() {
    return true;
}

} // namespace Editor
} // namespace OSRE
<|endoftext|>


<commit_before>#include <chrono>
#include <iostream>
#include "peers.hpp"
#include "config.hpp"
#include "requestHandler.hpp"

Peers::Peers() {
	pMap = PeerMap();
	it = std::begin(pMap);
	auto time_point = std::chrono::system_clock::now();
	auto duration = time_point.time_since_epoch();
	lastUpdate = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
}

Peer* Peers::getPeer(const std::string& peerID, long long now) {
	lastUpdate = now;
	try {
		return &pMap.at(peerID);
	} catch (const std::exception& e) {
		return nullptr;
	}
}

Peer* Peers::addPeer(const Request& req, unsigned int fid, long long now) {
	User* u = nullptr;
	if (Config::get("type") == "private")
		u = RequestHandler::getUser(req.at("passkey"));
	Peer* p = new Peer(req.at("ip"), u, req.at("left") == "0", std::stoul(req.at("left")), fid, req.at("user-agent"), req.at("peer_id"));
	pMap.emplace(req.at("peer_id"), *p);
	lastUpdate = now;
	return p;
}

void Peers::removePeer(const Request& req) {
	pMap.erase(req.at("peer_id"));
}

Peer* Peers::nextPeer(long long now) {
	while (pMap.size() > 0) {
		if (it == std::end(pMap)) {
			it = std::begin(pMap);
		} else if (it->second.timedOut(now)) {
			pMap.erase(it++);
		} else {
			PeerMap::iterator tmp = it;
			it = std::next(it);
			return &tmp->second;
		}
	}
	return nullptr;
}

unsigned long Peers::size () {
	return pMap.size();
}

bool Peers::timedOut(long long now, Database* db)
{
	auto it = pMap.begin();
	bool changed = false;
	while (it != pMap.end()) {
		if (it->second.timedOut(now)) {
			if (Config::get("type") == "public")
				pMap.erase(it);
			else {
				it->second.inactive();
				db->recordPeer(&it->second, now);
				changed = true;
			}
		}
		++it;
	}
	return changed;
}
<commit_msg>remove inactive peers from memory<commit_after>#include <chrono>
#include <iostream>
#include "peers.hpp"
#include "config.hpp"
#include "requestHandler.hpp"

Peers::Peers() {
	pMap = PeerMap();
	it = std::begin(pMap);
	auto time_point = std::chrono::system_clock::now();
	auto duration = time_point.time_since_epoch();
	lastUpdate = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
}

Peer* Peers::getPeer(const std::string& peerID, long long now) {
	lastUpdate = now;
	try {
		return &pMap.at(peerID);
	} catch (const std::exception& e) {
		return nullptr;
	}
}

Peer* Peers::addPeer(const Request& req, unsigned int fid, long long now) {
	User* u = nullptr;
	if (Config::get("type") == "private")
		u = RequestHandler::getUser(req.at("passkey"));
	Peer* p = new Peer(req.at("ip"), u, req.at("left") == "0", std::stoul(req.at("left")), fid, req.at("user-agent"), req.at("peer_id"));
	pMap.emplace(req.at("peer_id"), *p);
	lastUpdate = now;
	return p;
}

void Peers::removePeer(const Request& req) {
	pMap.erase(req.at("peer_id"));
}

Peer* Peers::nextPeer(long long now) {
	while (pMap.size() > 0) {
		if (it == std::end(pMap)) {
			it = std::begin(pMap);
		} else if (it->second.timedOut(now)) {
			pMap.erase(it++);
		} else {
			PeerMap::iterator tmp = it;
			it = std::next(it);
			return &tmp->second;
		}
	}
	return nullptr;
}

unsigned long Peers::size () {
	return pMap.size();
}

bool Peers::timedOut(long long now, Database* db)
{
	auto it = pMap.begin();
	bool changed = false;
	while (it != pMap.end()) {
		if (it->second.timedOut(now)) {
			if (Config::get("type") == "private") {
				it->second.inactive();
				db->recordPeer(&it->second, now);
				changed = true;
			}
		}
		pMap.erase(it++);
	}
	return changed;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: txtcmp.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-08 14:12:20 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, 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
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/
#ifndef _TXTCMP_HXX
#define _TXTCMP_HXX

#include <unotools/textsearch.hxx>

#endif

<commit_msg>INTEGRATION: CWS changefileheader (1.3.774); FILE MERGED 2008/03/31 13:00:57 rt 1.3.774.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: txtcmp.hxx,v $
 * $Revision: 1.4 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef _TXTCMP_HXX
#define _TXTCMP_HXX

#include <unotools/textsearch.hxx>

#endif

<|endoftext|>


<commit_before>/*
 *
 *  Resource loader, functions for loading all supported data types
 *      - Loads scene information from json
 *      - Loads textures form TGA's
 *      - Loads models from 3ds
 *
 */

#include "resourceloader.hpp"

#include <glm/glm.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <FreeImage.h>
#include <json.hpp>

#include <memory>
#include <vector>
#include <fstream>
#include <iostream>

#include <Utilities/exceptions.hpp>
#include <Core/glapi.hpp>
#include <Resources/model3d.hpp>
#include <Models/line.hpp>
#include <Models/staticmodel.hpp>

using namespace gl;
using json = nlohmann::json;

std::shared_ptr<IRenderObject> LoadStaticModelJSON(json object) {
    std::string type = "";
    std::string name = "";

    try {
        type = object["datatype"];
    } catch (std::domain_error& error) {
        type = "normal";
    }

    try {
        name = object["name"];
    } catch (std::domain_error& error) {
        name = "null";
    }

    std::shared_ptr<IRenderObject> rObject;

    if(type == "normal") {
        rObject = std::make_shared<StaticModel>(ResourceLoader::LoadModel3D(object["filename"]));
    } else if(type == "raw") {
        std::vector<glm::vec3> verts;
        std::vector<std::vector<float>> vertsf = object["vertices"];
        for(std::vector<float> vert : vertsf) {
            if(vert.size() != 3) {
                throw bad_resource("Vertex data size is not 3", name);
            }
            verts.push_back(glm::vec3(vert[0], vert[1], vert[2]));
        }

        std::vector<GLuint> indices;
        std::vector<uint> indicesf = object["indices"];
        for(uint index : indicesf) {
            if(index >= verts.size()) {
                throw bad_resource("Index is out of range", name);
            }
            indices.push_back(index);
        }

        std::vector<glm::vec3> norms;
        try {
            std::vector<std::vector<float>> normsf = object["normals"];
            for(std::vector<float> norm : normsf) {
                if(norm.size() != 3) {
                    throw bad_resource("Normal data size is not 3", name);
                }
                norms.push_back(glm::vec3(norm[0], norm[1], norm[2]));
            }
        } catch (std::domain_error& error) {
            norms.clear();
            for(uint i = 0; i < verts.size(); i++) {
                norms.push_back(glm::vec3(0.0f));
            }
        }

        std::vector<glm::vec2> uvs;
        try {
            std::vector<std::vector<float>> uvsf = object["uvs"];
            for(std::vector<float> uv : uvsf) {
                if(uv.size() != 2) {
                    throw bad_resource("UV data size is not 2", name);
                }
                uvs.push_back(glm::vec2(uv[0], uv[1]));
            }
        } catch (std::domain_error& error) {
            uvs.clear();
            for(uint i = 0; i < verts.size(); i++) {
                uvs.push_back(glm::vec2(0.0f));
            }
        }

        std::vector<Model3D::Vertex> vertices;
        for(uint i = 0; i < verts.size(); i++) {
            vertices.push_back(Model3D::Vertex(verts[i], norms[i], uvs[i]));
        }

        std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
        meshes.push_back(std::make_shared<Model3D::Mesh>(vertices, indices));
        meshes[0]->SetMatName("texture");

        std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
        textures["texture"] = ResourceLoader::LoadTexture(object["matname"], true);

        rObject = std::make_shared<StaticModel>(std::make_shared<Model3D>(meshes, textures));
    } else {
        throw bad_resource("Unknown data type", name);
    }

    try {
        std::vector<float> scale = object["scale"];
        if(scale.size() != 3) {
            throw std::domain_error("scale data size is not 3");
        }
        rObject->Scale(glm::vec3(scale[0], scale[1], scale[2]));
    } catch (std::domain_error& error) {}

    try {
        std::vector<float> translation = object["translation"];
        if(translation.size() != 3) {
            throw bad_resource("translation data size is not 3", name);
        }
        rObject->Translate(glm::vec3(translation[0], translation[1], translation[2]));
    } catch (std::domain_error& error) {}

    try {
        std::vector<float> rotation = object["rotation"];
        if(rotation.size() != 3) {
            throw bad_resource("Rotation data size is not 3", name);
        }
    } catch (std::domain_error& error) {}

    return rObject;
}

std::shared_ptr<IRenderObject> LoadLineJSON(json object) {
    glm::vec3 head, tail, color;
    std::string name;

    try {
        name = object["name"];
    } catch (std::domain_error& error) {
        name = "null";
    }

    std::vector<float> head3f = object["head"];
    if(head3f.size() == 3) {
        head = glm::vec3(head3f[0], head3f[1], head3f[2]);
    } else {
        throw bad_resource("head data size is not 3", name);
    }

    std::vector<float> tail3f = object["tail"];
    if(tail3f.size() == 3) {
        tail = glm::vec3(tail3f[0], tail3f[1], tail3f[2]);
    } else {
        throw bad_resource("tail data size is not 3", name);
    }

    std::vector<float> color3f = object["color"];
    if(head3f.size() == 3) {
        color = glm::vec3(color3f[0], color3f[1], color3f[2]);
    } else {
        throw bad_resource("color data size is not 3", name);
    }

    return std::make_shared<Line>(head, tail, color);
}

std::vector<std::shared_ptr<IRenderObject>> ResourceLoader::LoadScene(std::string filename) {
    std::vector<std::shared_ptr<IRenderObject>> renderObjects;

    filename = "Scenes/" + filename + ".json";
    std::string contents;
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in) {
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
    } else {
        throw generic_exception(filename + " doesn't exist");
    }

    try {
        json scene = json::parse(contents);
        std::vector<json> objects = scene["staticObjects"];

        for(json object : objects) {
            std::string type = "";
            std::string name = "";

            try {
                type = object["type"];

                std::shared_ptr<IRenderObject> rObject;
                if(type == "line") {
                    rObject = LoadLineJSON(object);
                } else if(type == "staticmodel") {
                    rObject = LoadStaticModelJSON(object);
                }
                if(rObject != nullptr) {
                    renderObjects.push_back(rObject);
                }
            } catch (bad_resource& error) {
                std::cout << "[" << error.GetResourceName() << "] " << error.GetError() << std::endl;
            }
        }
    } catch(std::exception& error) {
        std::cout << "Parsing of " << filename << " failed : " << error.what() << std::endl;
    }

    return renderObjects;
}

std::shared_ptr<Texture> ResourceLoader::LoadTexture(std::string filename, bool genMipMaps) {
    FIBITMAP *img;
    filename = "./Textures/" + filename + ".tga";
    FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename.c_str());

    if(!FreeImage_FIFSupportsReading(format)) {
        throw bad_resource("FreeImage can't read from file", filename);
    }

    if(format == FIF_UNKNOWN) {
        throw bad_resource("Unknown format", filename);
    }

    img = FreeImage_Load(format, filename.c_str());

    if(!img) {
        throw bad_resource("Couldn't load image data", filename);
    }

    if(FreeImage_GetBPP(img) != 32) {
        FIBITMAP* oldImg = img;
        img = FreeImage_ConvertTo32Bits(oldImg);
        FreeImage_Unload(oldImg);
    }

    int height, width;
    width = FreeImage_GetWidth(img);
    height = FreeImage_GetHeight(img);

    unsigned char* bytes = FreeImage_GetBits(img);

    if(bytes == nullptr) {
        FreeImage_Unload(img);
        throw bad_resource("couldn't load image bytes", filename);
    }

    GLuint glTexture;
    glBindTexture(GL_TEXTURE_2D, 0);
    glGenTextures(1, &glTexture);
    glBindTexture(GL_TEXTURE_2D, glTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, bytes);

    if(genMipMaps) {
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    glBindTexture(GL_TEXTURE_2D, 0);

    if(!glIsTexture(glTexture)) {
        FreeImage_Unload(img);
        throw bad_resource("texture is not valid", filename);
    }

    FreeImage_Unload(img);

    std::shared_ptr<Texture> texture = std::make_shared<Texture>();
    texture->SetFileName(filename);
    texture->SetTexture(glTexture);
    return texture;
}

void CopyaiMat(const aiMatrix4x4* from, glm::mat4& to) {
    to[0][0] = from->a1; to[1][0] = from->a2;
    to[2][0] = from->a3; to[3][0] = from->a4;
    to[0][1] = from->b1; to[1][1] = from->b2;
    to[2][1] = from->b3; to[3][1] = from->b4;
    to[0][2] = from->c1; to[1][2] = from->c2;
    to[2][2] = from->c3; to[3][2] = from->c4;
    to[0][3] = from->d1; to[1][3] = from->d2;
    to[2][3] = from->d3; to[3][3] = from->d4;
}

void LoadNode(const aiScene* scene, const aiNode* node, std::vector<std::shared_ptr<Model3D::Mesh>>& meshes) {
    for(uint i = 0; i < node->mNumChildren; i++) {
        LoadNode(scene, node->mChildren[i], meshes);
    }

    for(uint i = 0; i < node->mNumMeshes; i++) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        std::vector<Model3D::Vertex> vertices;

        for(uint j = 0; j < mesh->mNumVertices; j++) {
            Model3D::Vertex vertex;

            vertex.position = glm::vec3(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);

            vertex.normal = (mesh->HasNormals())
                ? glm::vec3(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z)
                : glm::vec3(0.0f, 0.0f, 0.0f);

            vertex.texCoord = (mesh->HasTextureCoords(0))
                ? glm::vec2(mesh->mTextureCoords[0][j].x, mesh->mTextureCoords[0][j].y)
                : glm::vec2(0.0f, 0.0f);

            vertices.push_back(vertex);
        }

        std::vector<uint> indices;
        if(mesh->HasFaces()) {
            for(uint j = 0; j < mesh->mNumFaces; j++) {
                aiFace face = mesh->mFaces[j];
                for(uint k = 0; k < face.mNumIndices; k++) {
                    indices.push_back(face.mIndices[k]);
                }
            }
        } else {
            throw bad_resource("Node was missing faces, load cancled");
        }

        glm::mat4x4 transformation;
        CopyaiMat(&node->mTransformation, transformation);
        std::shared_ptr<Model3D::Mesh> rmesh = std::make_shared<Model3D::Mesh>(vertices, indices);
        rmesh->SetTransformation(transformation);
        rmesh->SetMatIndex(mesh->mMaterialIndex);

        meshes.push_back(rmesh);
    }
}

std::shared_ptr<Model3D> ResourceLoader::LoadModel3D(std::string modelname) throw(bad_resource) {
    std::string filename = "Models/" + modelname + ".3ds";
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename.c_str(),
        aiProcess_CalcTangentSpace |
        aiProcess_Triangulate |
        aiProcess_JoinIdenticalVertices |
        aiProcess_SortByPType);

    if(!scene || !scene->mRootNode || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) {
        throw bad_resource(importer.GetErrorString(), filename);
    }

    auto model = std::make_shared<Model3D>();

    aiMaterial** materials = scene->mMaterials;
    std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
    for(uint i = 0; i < scene->mNumMaterials; i++) {
        aiString aName;
        materials[i]->Get(AI_MATKEY_NAME, aName);
        std::string name = std::string(aName.C_Str());

        if(textures.find(name) == textures.end()) {
            try {
                std::shared_ptr<Texture> temp = LoadTexture(modelname + "/" + name, true);
                if(temp != nullptr) {
                    textures[name] = temp;
                }
            } catch (bad_resource& error) {
                std::cout << "[" << error.GetResourceName() << "] " << error.GetError() << std::endl;
            }
        }
    }
    model->SetTextures(textures);

    std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
    LoadNode(scene, scene->mRootNode, meshes);

    for(std::shared_ptr<Model3D::Mesh>& mesh : meshes) {
        aiString aName;
        uint index = mesh->GetMatIndex();
        if(index < scene->mNumMaterials) {
            materials[mesh->GetMatIndex()]->Get(AI_MATKEY_NAME, aName);
            mesh->SetMatName(std::string(aName.C_Str()));
        }
    }

    model->SetMeshes(meshes);

    return model;
}
<commit_msg>stupid error fixed<commit_after>/*
 *
 *  Resource loader, functions for loading all supported data types
 *      - Loads scene information from json
 *      - Loads textures form TGA's
 *      - Loads models from 3ds
 *
 */

#include "resourceloader.hpp"

#include <glm/glm.hpp>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <FreeImage.h>
#include <json.hpp>

#include <memory>
#include <vector>
#include <fstream>
#include <iostream>

#include <Utilities/exceptions.hpp>
#include <Core/glapi.hpp>
#include <Resources/model3d.hpp>
#include <Models/line.hpp>
#include <Models/staticmodel.hpp>

using namespace gl;
using json = nlohmann::json;

std::shared_ptr<IRenderObject> LoadStaticModelJSON(json object) {
    std::string type = "";
    std::string name = "";

    try {
        type = object["datatype"];
    } catch (std::domain_error& error) {
        type = "normal";
    }

    try {
        name = object["name"];
    } catch (std::domain_error& error) {
        name = "null";
    }

    std::shared_ptr<IRenderObject> rObject;

    if(type == "normal") {
        rObject = std::make_shared<StaticModel>(ResourceLoader::LoadModel3D(object["filename"]));
    } else if(type == "raw") {
        std::vector<glm::vec3> verts;
        std::vector<std::vector<float>> vertsf = object["vertices"];
        for(std::vector<float> vert : vertsf) {
            if(vert.size() != 3) {
                throw bad_resource("Vertex data size is not 3", name);
            }
            verts.push_back(glm::vec3(vert[0], vert[1], vert[2]));
        }

        std::vector<GLuint> indices;
        std::vector<uint> indicesf = object["indices"];
        for(uint index : indicesf) {
            if(index >= verts.size()) {
                throw bad_resource("Index is out of range", name);
            }
            indices.push_back(index);
        }

        std::vector<glm::vec3> norms;
        try {
            std::vector<std::vector<float>> normsf = object["normals"];
            for(std::vector<float> norm : normsf) {
                if(norm.size() != 3) {
                    throw bad_resource("Normal data size is not 3", name);
                }
                norms.push_back(glm::vec3(norm[0], norm[1], norm[2]));
            }
        } catch (std::domain_error& error) {
            norms.clear();
            for(uint i = 0; i < verts.size(); i++) {
                norms.push_back(glm::vec3(0.0f));
            }
        }

        std::vector<glm::vec2> uvs;
        try {
            std::vector<std::vector<float>> uvsf = object["uvs"];
            for(std::vector<float> uv : uvsf) {
                if(uv.size() != 2) {
                    throw bad_resource("UV data size is not 2", name);
                }
                uvs.push_back(glm::vec2(uv[0], uv[1]));
            }
        } catch (std::domain_error& error) {
            uvs.clear();
            for(uint i = 0; i < verts.size(); i++) {
                uvs.push_back(glm::vec2(0.0f));
            }
        }

        std::vector<Model3D::Vertex> vertices;
        for(uint i = 0; i < verts.size(); i++) {
            vertices.push_back(Model3D::Vertex(verts[i], norms[i], uvs[i]));
        }

        std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
        meshes.push_back(std::make_shared<Model3D::Mesh>(vertices, indices));
        meshes[0]->SetMatName("texture");

        std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
        textures["texture"] = ResourceLoader::LoadTexture(object["matname"], true);

        rObject = std::make_shared<StaticModel>(std::make_shared<Model3D>(meshes, textures));
    } else {
        throw bad_resource("Unknown data type", name);
    }

    try {
        std::vector<float> scale = object["scale"];
        if(scale.size() != 3) {
            throw std::domain_error("scale data size is not 3");
        }
        rObject->Scale(glm::vec3(scale[0], scale[1], scale[2]));
    } catch (std::domain_error& error) {}

    try {
        std::vector<float> translation = object["translation"];
        if(translation.size() != 3) {
            throw bad_resource("translation data size is not 3", name);
        }
        rObject->Translate(glm::vec3(translation[0], translation[1], translation[2]));
    } catch (std::domain_error& error) {}

    try {
        std::vector<float> rotation = object["rotation"];
        if(rotation.size() != 3) {
            throw bad_resource("Rotation data size is not 3", name);
        }
    } catch (std::domain_error& error) {}

    return rObject;
}

std::shared_ptr<IRenderObject> LoadLineJSON(json object) {
    glm::vec3 head, tail, color;
    std::string name;

    try {
        name = object["name"];
    } catch (std::domain_error& error) {
        name = "null";
    }

    std::vector<float> head3f = object["head"];
    if(head3f.size() == 3) {
        head = glm::vec3(head3f[0], head3f[1], head3f[2]);
    } else {
        throw bad_resource("head data size is not 3", name);
    }

    std::vector<float> tail3f = object["tail"];
    if(tail3f.size() == 3) {
        tail = glm::vec3(tail3f[0], tail3f[1], tail3f[2]);
    } else {
        throw bad_resource("tail data size is not 3", name);
    }

    std::vector<float> color3f = object["color"];
    if(head3f.size() == 3) {
        color = glm::vec3(color3f[0], color3f[1], color3f[2]);
    } else {
        throw bad_resource("color data size is not 3", name);
    }

    return std::make_shared<Line>(head, tail, color);
}

std::vector<std::shared_ptr<IRenderObject>> ResourceLoader::LoadScene(std::string filename) {
    std::vector<std::shared_ptr<IRenderObject>> renderObjects;

    filename = "Scenes/" + filename + ".json";
    std::string contents;
    std::ifstream in(filename, std::ios::in | std::ios::binary);
    if (in) {
        in.seekg(0, std::ios::end);
        contents.resize(in.tellg());
        in.seekg(0, std::ios::beg);
        in.read(&contents[0], contents.size());
        in.close();
    } else {
        throw generic_exception(filename + " doesn't exist");
    }

    try {
        json scene = json::parse(contents);
        std::vector<json> objects = scene["staticObjects"];

        for(json object : objects) {
            std::string type = "";
            std::string name = "";

            try {
                type = object["type"];

                std::shared_ptr<IRenderObject> rObject;
                if(type == "line") {
                    rObject = LoadLineJSON(object);
                } else if(type == "staticmodel") {
                    rObject = LoadStaticModelJSON(object);
                }
                if(rObject != nullptr) {
                    renderObjects.push_back(rObject);
                }
            } catch (bad_resource& error) {
                std::cout << "[" << error.GetResourceName() << "] " << error.GetError() << std::endl;
            }
        }
    } catch(std::exception& error) {
        std::cout << "Parsing of " << filename << " failed : " << error.what() << std::endl;
    }

    return renderObjects;
}

std::shared_ptr<Texture> ResourceLoader::LoadTexture(std::string filename, bool genMipMaps) {
    FIBITMAP *img;
    filename = "./Textures/" + filename + ".tga";
    FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename.c_str());

    if(!FreeImage_FIFSupportsReading(format)) {
        throw bad_resource("FreeImage can't read from file", filename);
    }

    if(format == FIF_UNKNOWN) {
        throw bad_resource("Unknown format", filename);
    }

    img = FreeImage_Load(format, filename.c_str());

    if(!img) {
        throw bad_resource("Couldn't load image data", filename);
    }

    if(FreeImage_GetBPP(img) != 32) {
        FIBITMAP* oldImg = img;
        img = FreeImage_ConvertTo32Bits(oldImg);
        FreeImage_Unload(oldImg);
    }

    int height, width;
    width = FreeImage_GetWidth(img);
    height = FreeImage_GetHeight(img);

    unsigned char* bytes = FreeImage_GetBits(img);

    if(bytes == nullptr) {
        FreeImage_Unload(img);
        throw bad_resource("couldn't load image bytes", filename);
    }

    GLuint glTexture;
    glBindTexture(GL_TEXTURE_2D, 0);
    glGenTextures(1, &glTexture);
    glBindTexture(GL_TEXTURE_2D, glTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, bytes);

    if(genMipMaps) {
        glGenerateMipmap(GL_TEXTURE_2D);
    }

    glBindTexture(GL_TEXTURE_2D, 0);

    if(!glIsTexture(glTexture)) {
        FreeImage_Unload(img);
        throw bad_resource("texture is not valid", filename);
    }

    FreeImage_Unload(img);

    std::shared_ptr<Texture> texture = std::make_shared<Texture>();
    texture->SetFileName(filename);
    texture->SetTexture(glTexture);
    return texture;
}

void CopyaiMat(const aiMatrix4x4* from, glm::mat4& to) {
    to[0][0] = from->a1; to[1][0] = from->a2;
    to[2][0] = from->a3; to[3][0] = from->a4;
    to[0][1] = from->b1; to[1][1] = from->b2;
    to[2][1] = from->b3; to[3][1] = from->b4;
    to[0][2] = from->c1; to[1][2] = from->c2;
    to[2][2] = from->c3; to[3][2] = from->c4;
    to[0][3] = from->d1; to[1][3] = from->d2;
    to[2][3] = from->d3; to[3][3] = from->d4;
}

void LoadNode(const aiScene* scene, const aiNode* node, std::vector<std::shared_ptr<Model3D::Mesh>>& meshes) {
    for(uint i = 0; i < node->mNumChildren; i++) {
        LoadNode(scene, node->mChildren[i], meshes);
    }

    for(uint i = 0; i < node->mNumMeshes; i++) {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        std::vector<Model3D::Vertex> vertices;

        for(uint j = 0; j < mesh->mNumVertices; j++) {
            Model3D::Vertex vertex;

            vertex.position = glm::vec3(mesh->mVertices[j].x, mesh->mVertices[j].y, mesh->mVertices[j].z);

            vertex.normal = (mesh->HasNormals())
                ? glm::vec3(mesh->mNormals[j].x, mesh->mNormals[j].y, mesh->mNormals[j].z)
                : glm::vec3(0.0f, 0.0f, 0.0f);

            vertex.texCoord = (mesh->HasTextureCoords(0))
                ? glm::vec2(mesh->mTextureCoords[0][j].x, mesh->mTextureCoords[0][j].y)
                : glm::vec2(0.0f, 0.0f);

            vertices.push_back(vertex);
        }

        std::vector<uint> indices;
        if(mesh->HasFaces()) {
            for(uint j = 0; j < mesh->mNumFaces; j++) {
                aiFace face = mesh->mFaces[j];
                for(uint k = 0; k < face.mNumIndices; k++) {
                    indices.push_back(face.mIndices[k]);
                }
            }
        } else {
            throw bad_resource("Node was missing faces, load cancled");
        }

        glm::mat4x4 transformation;
        CopyaiMat(&node->mTransformation, transformation);
        std::shared_ptr<Model3D::Mesh> rmesh = std::make_shared<Model3D::Mesh>(vertices, indices);
        rmesh->SetTransformation(transformation);
        rmesh->SetMatIndex(mesh->mMaterialIndex);

        meshes.push_back(rmesh);
    }
}

std::shared_ptr<Model3D> ResourceLoader::LoadModel3D(std::string modelname) {
    std::string filename = "Models/" + modelname + ".3ds";
    Assimp::Importer importer;
    const aiScene* scene = importer.ReadFile(filename.c_str(),
        aiProcess_CalcTangentSpace |
        aiProcess_Triangulate |
        aiProcess_JoinIdenticalVertices |
        aiProcess_SortByPType);

    if(!scene || !scene->mRootNode || scene->mFlags == AI_SCENE_FLAGS_INCOMPLETE) {
        throw bad_resource(importer.GetErrorString(), filename);
    }

    auto model = std::make_shared<Model3D>();

    aiMaterial** materials = scene->mMaterials;
    std::unordered_map<std::string, std::shared_ptr<Texture>> textures;
    for(uint i = 0; i < scene->mNumMaterials; i++) {
        aiString aName;
        materials[i]->Get(AI_MATKEY_NAME, aName);
        std::string name = std::string(aName.C_Str());

        if(textures.find(name) == textures.end()) {
            try {
                std::shared_ptr<Texture> temp = LoadTexture(modelname + "/" + name, true);
                if(temp != nullptr) {
                    textures[name] = temp;
                }
            } catch (bad_resource& error) {
                std::cout << "[" << error.GetResourceName() << "] " << error.GetError() << std::endl;
            }
        }
    }
    model->SetTextures(textures);

    std::vector<std::shared_ptr<Model3D::Mesh>> meshes;
    LoadNode(scene, scene->mRootNode, meshes);

    for(std::shared_ptr<Model3D::Mesh>& mesh : meshes) {
        aiString aName;
        uint index = mesh->GetMatIndex();
        if(index < scene->mNumMaterials) {
            materials[mesh->GetMatIndex()]->Get(AI_MATKEY_NAME, aName);
            mesh->SetMatName(std::string(aName.C_Str()));
        }
    }

    model->SetMeshes(meshes);

    return model;
}
<|endoftext|>


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

#include "memory.hpp"
#include "queue.hpp"

cvk_executor_thread_pool gThreadPool;

cvk_command_queue* cvk_event::queue() const
{
    CVK_ASSERT(!is_user_event());
    return m_command->queue();
}

cl_command_type cvk_event::command_type() const
{
    CVK_ASSERT(!is_user_event());
    return m_command->type();
}

_cl_command_queue::_cl_command_queue(cvk_context *ctx, cvk_device *device,
                                     cl_command_queue_properties properties) :
    api_object(ctx),
    m_device(device),
    m_properties(properties),
    m_executor(nullptr),
    m_command_pool(VK_NULL_HANDLE)
{
    m_groups.push_back(new cvk_command_group());

    if (properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) {
        cvk_warn_fn("out-of-order execution enabled, will be ignored");
    }

    m_vulkan_queue = device->vulkan_queue_allocate();
    m_vulkan_queue_family = device->vulkan_queue_family();
}

cl_int cvk_command_queue::init() {

    // Create command pool
    VkCommandPoolCreateInfo createInfo = {
        VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
        nullptr,
        0, // flags
        m_vulkan_queue_family
    };

    VkResult res = vkCreateCommandPool(m_device->vulkan_device(), &createInfo, nullptr, &m_command_pool);
    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    return CL_SUCCESS;
}

_cl_command_queue::~_cl_command_queue() {
    if (m_executor != nullptr) {
        gThreadPool.return_executor(m_executor);
    }
    if (m_command_pool != VK_NULL_HANDLE) {
        vkDestroyCommandPool(m_device->vulkan_device(), m_command_pool, nullptr);
    }
}

void cvk_command_queue::enqueue_command(cvk_command *cmd, cvk_event **event) {

    std::lock_guard<std::mutex> lock(m_lock);

    m_groups.back()->commands.push_back(cmd);
    cmd->event()->retain();

    cvk_debug_fn("enqueued command %p, event %p", cmd, cmd->event());

    cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_QUEUED);

    if (event != nullptr) {
        *event = cmd->event();
        cvk_debug_fn("returning event %p", *event);
    }
}

void cvk_command_queue::enqueue_command_with_deps(cvk_command *cmd, cl_uint num_dep_events,
                                                  cvk_event *const* dep_events, cvk_event **event) {
    cmd->set_dependencies(num_dep_events, dep_events);
    enqueue_command(cmd, event);
}

void cvk_executor_thread::executor() {

    std::unique_lock<std::mutex> lock(m_lock);

    while (!m_shutdown) {

        if (m_groups.size() == 0) {
            m_cv.wait(lock);
        }

        if (m_shutdown) {
            continue;
        }

        auto group = m_groups.front();
        m_groups.pop_front();

        cvk_debug_fn("received group %p", group);

        lock.unlock();

        while (group->commands.size() > 0) {

            cvk_command *cmd = group->commands.front();
            cvk_debug_fn("executing command %p, event %p", cmd, cmd->event());

            if (m_profiling) {
                cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_START);
            }

            cl_int status = cmd->execute();
            cvk_debug_fn("command returned %d", status);

            if (m_profiling) {
                cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_END);
            }

            cmd->event()->set_status(status);

            group->commands.pop_front();

            delete cmd;
        }
        lock.lock();

        delete group;
    }
}

cl_int cvk_command_queue::flush(cvk_event** event) {

    cvk_info_fn("queue = %p, event = %p", this, event);

    // Get command group to the executor's queue
    cvk_command_group *group;
    {
        std::lock_guard<std::mutex> lock(m_lock);
        group = m_groups.front();
        if (group->commands.size() == 0) {
            return CL_SUCCESS;
        }
        m_groups.pop_front();
        m_groups.push_back(new cvk_command_group());
    }

    cvk_debug_fn("groups.size() = %zu", m_groups.size());

    CVK_ASSERT(group->commands.size() > 0);

    // Set event state and profiling info
    for (auto cmd : group->commands) {
        cmd->event()->set_status(CL_SUBMITTED);
        if (has_property(CL_QUEUE_PROFILING_ENABLE)) {
            cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_SUBMIT);
        }
    }

    // Create execution thread if it doesn't exist
    {
        std::lock_guard<std::mutex> lock(m_lock);
        if (m_executor == nullptr) {
            m_executor = gThreadPool.get_executor(this);
        }
    }

    if (event != nullptr) {
        *event = group->commands.back()->event();
        cvk_debug_fn("returned event %p", *event);
    }

    // Submit command group to executor
    m_executor->send_group(group);

    return CL_SUCCESS;
}

bool cvk_command_queue::allocate_command_buffer(VkCommandBuffer *buf) {

    std::lock_guard<std::mutex> lock(m_lock);

    VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
      VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      0,
      m_command_pool,
      VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      1 // commandBufferCount
    };

    VkResult res = vkAllocateCommandBuffers(m_device->vulkan_device(), &commandBufferAllocateInfo, buf);

    if (res != VK_SUCCESS) {
        return false;
    }

    return true;
}

void cvk_command_queue::free_command_buffer(VkCommandBuffer buf) {
    vkFreeCommandBuffers(m_device->vulkan_device(), m_command_pool, 1, &buf);
}

cl_int cvk_command_kernel::build() {

    auto vklimits = m_queue->device()->vulkan_limits();

    // Check we have a valid dispatch size
    for (cl_uint i = 0; i < 3; ++i) {
        if (m_num_wg[i] > vklimits.maxComputeWorkGroupCount[i]) {
            cvk_error_fn("global work size exceeds device limits");
            // There is no suitable error code to report this
            // use something
            return CL_INVALID_WORK_ITEM_SIZE;
        }
    }

    if (m_wg_size[0] * m_wg_size[1] * m_wg_size[2] > vklimits.maxComputeWorkGroupInvocations) {
        return CL_INVALID_WORK_GROUP_SIZE;
    }

    for (int i = 0; i < 3; i++) {
        if (m_wg_size[i] > vklimits.maxComputeWorkGroupSize[i]) {
            return CL_INVALID_WORK_ITEM_SIZE;
        }
    }

    // TODO check against the size specified at compile time, if any

    // Setup descriptors
    if (!m_kernel->setup_descriptor_set(&m_descriptor_set, m_pod_buffer)) {
        return CL_OUT_OF_RESOURCES;
    }

    // Create and populate the command buffer
    if (!m_queue->allocate_command_buffer(&m_command_buffer)) {
        return CL_OUT_OF_RESOURCES;
    }

    VkCommandBufferBeginInfo beginInfo = {
        VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
        nullptr,
        VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
        nullptr // pInheritanceInfo
    };

    VkResult res = vkBeginCommandBuffer(m_command_buffer, &beginInfo);

    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    VkPipeline pipeline = m_kernel->get_pipeline(
        m_wg_size[0],
        m_wg_size[1],
        m_wg_size[2]
    );

    if (pipeline == VK_NULL_HANDLE) {
        return CL_OUT_OF_RESOURCES;
    }

    vkCmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);

    vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
                            m_kernel->pipeline_layout(), 0, 1, &m_descriptor_set, 0, 0);

    vkCmdDispatch(m_command_buffer, m_num_wg[0], m_num_wg[1], m_num_wg[2]);

    res = vkEndCommandBuffer(m_command_buffer);

    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    return CL_SUCCESS;
}

cl_int cvk_command_kernel::do_action()
{
    VkSubmitInfo submitInfo = {
      VK_STRUCTURE_TYPE_SUBMIT_INFO,
      nullptr,
      0, // waitSemaphoreCOunt
      nullptr, // pWaitSemaphores
      nullptr, // pWaitDstStageMask
      1, // commandBufferCount
      &m_command_buffer,
      0, // signalSemaphoreCount
      nullptr, // pSignalSemaphores
    };

    auto queue = m_queue->vulkan_queue();

    VkResult res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not submit work to queue");
        return CL_OUT_OF_RESOURCES;
    }

    res = vkQueueWaitIdle(queue);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not wait for queue to become idle: %s", vulkan_error_string(res));
        return CL_OUT_OF_RESOURCES;
    }

    m_queue->free_command_buffer(m_command_buffer);

    return CL_COMPLETE;
}

cl_int cvk_command_copy::do_action()
{
    bool success = false;

    switch (m_type) {
    case CL_COMMAND_WRITE_BUFFER:
        success = m_mem->copy_from(m_ptr, m_offset, m_size);
        break;
    case CL_COMMAND_READ_BUFFER:
        success = m_mem->copy_to(m_ptr, m_offset, m_size);
        break;
    default:
        CVK_ASSERT(false);
        break;
    }

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

struct rectangle {
public:
    void set_params(size_t *origin, size_t slicep, size_t rowp) {
        m_origin[0] = origin[0];
        m_origin[1] = origin[1];
        m_origin[2] = origin[2];
        m_slice_pitch = slicep;
        m_row_pitch = rowp;
    }

    size_t get_row_offset(size_t slice, size_t row) {
        return m_slice_pitch * (m_origin[2] + slice) + m_row_pitch * (m_origin[1] + row) + m_origin[0];
    }
private:
    size_t m_origin[3];
    size_t m_slice_pitch;
    size_t m_row_pitch;
};

struct memobj_map_holder {
    memobj_map_holder(cvk_mem *memobj) : m_mem(memobj), m_mapped(false) {}
    ~memobj_map_holder() {
        if (m_mapped) {
            m_mem->unmap();
        }
    }

    bool CHECK_RETURN map() {
        m_mapped = m_mem->map();
        return m_mapped;
    }

private:
    cvk_mem *m_mem;
    bool m_mapped;
};

cl_int cvk_command_copy_rect::do_action()
{
    uintptr_t dst_base, src_base;
    rectangle rsrc, rdst;

    memobj_map_holder map_holder{m_mem};

    if (!map_holder.map()) {
        return CL_OUT_OF_RESOURCES;
    }

    switch (m_type) {
    case CL_COMMAND_READ_BUFFER_RECT:
        dst_base = reinterpret_cast<uintptr_t>(m_ptr);
        src_base = reinterpret_cast<uintptr_t>(m_mem->host_va());
        rsrc.set_params(m_buffer_origin, m_buffer_slice_pitch, m_buffer_row_pitch);
        rdst.set_params(m_host_origin, m_host_slice_pitch, m_host_row_pitch);
        break;
    case CL_COMMAND_WRITE_BUFFER_RECT:
        dst_base = reinterpret_cast<uintptr_t>(m_mem->host_va());
        src_base = reinterpret_cast<uintptr_t>(m_ptr);
        rsrc.set_params(m_host_origin, m_host_slice_pitch, m_host_row_pitch);
        rdst.set_params(m_buffer_origin, m_buffer_slice_pitch, m_buffer_row_pitch);
        break;
    default:
        return CL_INVALID_OPERATION;
    }

    for (size_t slice = 0; slice < m_region[2]; slice++) {
        cvk_debug_fn("slice = %zu", slice);
        for (size_t row = 0; row < m_region[1]; row++) {
            cvk_debug_fn("row = %zu", row);
            uintptr_t dst = dst_base + rdst.get_row_offset(slice, row);
            uintptr_t src = src_base + rsrc.get_row_offset(slice, row);
            memcpy(reinterpret_cast<void*>(dst), reinterpret_cast<void*>(src), m_region[0]);
        }
    }

    return CL_COMPLETE;
}

cl_int cvk_command_copy_buffer::do_action()
{
    bool success = m_src_buffer->copy_to(m_dst_buffer, m_src_offset, m_dst_offset, m_size);

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

cl_int cvk_command_fill::do_action()
{
    memobj_map_holder map_holder{m_mem};

    if (!map_holder.map()) {
        return CL_OUT_OF_RESOURCES;
    }

    uintptr_t begin = reinterpret_cast<uintptr_t>(m_mem->host_va()) + m_offset;
    uintptr_t end = begin + m_size;

    uintptr_t address = begin;
    while (address < end) {
        memcpy(reinterpret_cast<void*>(address), m_pattern.get(), m_pattern_size);
        address += m_pattern_size;
    }

    return CL_COMPLETE;
}

cl_int cvk_command_map::do_action()
{
    bool success = true;
    if (m_mem->has_flags(CL_MEM_USE_HOST_PTR)) {
        success = m_mem->copy_to(m_mem->host_ptr(), m_offset, m_size);
    }

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

cl_int cvk_command_unmap::do_action()
{
    m_mem->unmap();

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

#include "memory.hpp"
#include "queue.hpp"

cvk_executor_thread_pool gThreadPool;

cvk_command_queue* cvk_event::queue() const
{
    CVK_ASSERT(!is_user_event());
    return m_command->queue();
}

cl_command_type cvk_event::command_type() const
{
    CVK_ASSERT(!is_user_event());
    return m_command->type();
}

_cl_command_queue::_cl_command_queue(cvk_context *ctx, cvk_device *device,
                                     cl_command_queue_properties properties) :
    api_object(ctx),
    m_device(device),
    m_properties(properties),
    m_executor(nullptr),
    m_command_pool(VK_NULL_HANDLE)
{
    m_groups.push_back(new cvk_command_group());

    if (properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) {
        cvk_warn_fn("out-of-order execution enabled, will be ignored");
    }

    m_vulkan_queue = device->vulkan_queue_allocate();
    m_vulkan_queue_family = device->vulkan_queue_family();
}

cl_int cvk_command_queue::init() {

    // Create command pool
    VkCommandPoolCreateInfo createInfo = {
        VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
        nullptr,
        0, // flags
        m_vulkan_queue_family
    };

    VkResult res = vkCreateCommandPool(m_device->vulkan_device(), &createInfo, nullptr, &m_command_pool);
    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    return CL_SUCCESS;
}

_cl_command_queue::~_cl_command_queue() {
    if (m_executor != nullptr) {
        gThreadPool.return_executor(m_executor);
    }
    if (m_command_pool != VK_NULL_HANDLE) {
        vkDestroyCommandPool(m_device->vulkan_device(), m_command_pool, nullptr);
    }
}

void cvk_command_queue::enqueue_command(cvk_command *cmd, cvk_event **event) {

    std::lock_guard<std::mutex> lock(m_lock);

    m_groups.back()->commands.push_back(cmd);

    cvk_debug_fn("enqueued command %p, event %p", cmd, cmd->event());

    cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_QUEUED);

    if (event != nullptr) {
        // The event will be returned to the app, retain it for the user
        cmd->event()->retain();
        *event = cmd->event();
        cvk_debug_fn("returning event %p", *event);
    }
}

void cvk_command_queue::enqueue_command_with_deps(cvk_command *cmd, cl_uint num_dep_events,
                                                  cvk_event *const* dep_events, cvk_event **event) {
    cmd->set_dependencies(num_dep_events, dep_events);
    enqueue_command(cmd, event);
}

void cvk_executor_thread::executor() {

    std::unique_lock<std::mutex> lock(m_lock);

    while (!m_shutdown) {

        if (m_groups.size() == 0) {
            m_cv.wait(lock);
        }

        if (m_shutdown) {
            continue;
        }

        auto group = m_groups.front();
        m_groups.pop_front();

        cvk_debug_fn("received group %p", group);

        lock.unlock();

        while (group->commands.size() > 0) {

            cvk_command *cmd = group->commands.front();
            cvk_debug_fn("executing command %p, event %p", cmd, cmd->event());

            if (m_profiling) {
                cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_START);
            }

            cl_int status = cmd->execute();
            cvk_debug_fn("command returned %d", status);

            if (m_profiling) {
                cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_END);
            }

            cmd->event()->set_status(status);

            group->commands.pop_front();

            delete cmd;
        }
        lock.lock();

        delete group;
    }
}

cl_int cvk_command_queue::flush(cvk_event** event) {

    cvk_info_fn("queue = %p, event = %p", this, event);

    // Get command group to the executor's queue
    cvk_command_group *group;
    {
        std::lock_guard<std::mutex> lock(m_lock);
        group = m_groups.front();
        if (group->commands.size() == 0) {
            return CL_SUCCESS;
        }
        m_groups.pop_front();
        m_groups.push_back(new cvk_command_group());
    }

    cvk_debug_fn("groups.size() = %zu", m_groups.size());

    CVK_ASSERT(group->commands.size() > 0);

    // Set event state and profiling info
    for (auto cmd : group->commands) {
        cmd->event()->set_status(CL_SUBMITTED);
        if (has_property(CL_QUEUE_PROFILING_ENABLE)) {
            cmd->event()->set_profiling_info_from_monotonic_clock(CL_PROFILING_COMMAND_SUBMIT);
        }
    }

    // Create execution thread if it doesn't exist
    {
        std::lock_guard<std::mutex> lock(m_lock);
        if (m_executor == nullptr) {
            m_executor = gThreadPool.get_executor(this);
        }
    }

    if (event != nullptr) {
        *event = group->commands.back()->event();
        cvk_debug_fn("returned event %p", *event);
    }

    // Submit command group to executor
    m_executor->send_group(group);

    return CL_SUCCESS;
}

bool cvk_command_queue::allocate_command_buffer(VkCommandBuffer *buf) {

    std::lock_guard<std::mutex> lock(m_lock);

    VkCommandBufferAllocateInfo commandBufferAllocateInfo = {
      VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      0,
      m_command_pool,
      VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      1 // commandBufferCount
    };

    VkResult res = vkAllocateCommandBuffers(m_device->vulkan_device(), &commandBufferAllocateInfo, buf);

    if (res != VK_SUCCESS) {
        return false;
    }

    return true;
}

void cvk_command_queue::free_command_buffer(VkCommandBuffer buf) {
    vkFreeCommandBuffers(m_device->vulkan_device(), m_command_pool, 1, &buf);
}

cl_int cvk_command_kernel::build() {

    auto vklimits = m_queue->device()->vulkan_limits();

    // Check we have a valid dispatch size
    for (cl_uint i = 0; i < 3; ++i) {
        if (m_num_wg[i] > vklimits.maxComputeWorkGroupCount[i]) {
            cvk_error_fn("global work size exceeds device limits");
            // There is no suitable error code to report this
            // use something
            return CL_INVALID_WORK_ITEM_SIZE;
        }
    }

    if (m_wg_size[0] * m_wg_size[1] * m_wg_size[2] > vklimits.maxComputeWorkGroupInvocations) {
        return CL_INVALID_WORK_GROUP_SIZE;
    }

    for (int i = 0; i < 3; i++) {
        if (m_wg_size[i] > vklimits.maxComputeWorkGroupSize[i]) {
            return CL_INVALID_WORK_ITEM_SIZE;
        }
    }

    // TODO check against the size specified at compile time, if any

    // Setup descriptors
    if (!m_kernel->setup_descriptor_set(&m_descriptor_set, m_pod_buffer)) {
        return CL_OUT_OF_RESOURCES;
    }

    // Create and populate the command buffer
    if (!m_queue->allocate_command_buffer(&m_command_buffer)) {
        return CL_OUT_OF_RESOURCES;
    }

    VkCommandBufferBeginInfo beginInfo = {
        VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
        nullptr,
        VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
        nullptr // pInheritanceInfo
    };

    VkResult res = vkBeginCommandBuffer(m_command_buffer, &beginInfo);

    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    VkPipeline pipeline = m_kernel->get_pipeline(
        m_wg_size[0],
        m_wg_size[1],
        m_wg_size[2]
    );

    if (pipeline == VK_NULL_HANDLE) {
        return CL_OUT_OF_RESOURCES;
    }

    vkCmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);

    vkCmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE,
                            m_kernel->pipeline_layout(), 0, 1, &m_descriptor_set, 0, 0);

    vkCmdDispatch(m_command_buffer, m_num_wg[0], m_num_wg[1], m_num_wg[2]);

    res = vkEndCommandBuffer(m_command_buffer);

    if (res != VK_SUCCESS) {
        return CL_OUT_OF_RESOURCES;
    }

    return CL_SUCCESS;
}

cl_int cvk_command_kernel::do_action()
{
    VkSubmitInfo submitInfo = {
      VK_STRUCTURE_TYPE_SUBMIT_INFO,
      nullptr,
      0, // waitSemaphoreCOunt
      nullptr, // pWaitSemaphores
      nullptr, // pWaitDstStageMask
      1, // commandBufferCount
      &m_command_buffer,
      0, // signalSemaphoreCount
      nullptr, // pSignalSemaphores
    };

    auto queue = m_queue->vulkan_queue();

    VkResult res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not submit work to queue");
        return CL_OUT_OF_RESOURCES;
    }

    res = vkQueueWaitIdle(queue);

    if (res != VK_SUCCESS) {
        cvk_error_fn("could not wait for queue to become idle: %s", vulkan_error_string(res));
        return CL_OUT_OF_RESOURCES;
    }

    m_queue->free_command_buffer(m_command_buffer);

    return CL_COMPLETE;
}

cl_int cvk_command_copy::do_action()
{
    bool success = false;

    switch (m_type) {
    case CL_COMMAND_WRITE_BUFFER:
        success = m_mem->copy_from(m_ptr, m_offset, m_size);
        break;
    case CL_COMMAND_READ_BUFFER:
        success = m_mem->copy_to(m_ptr, m_offset, m_size);
        break;
    default:
        CVK_ASSERT(false);
        break;
    }

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

struct rectangle {
public:
    void set_params(size_t *origin, size_t slicep, size_t rowp) {
        m_origin[0] = origin[0];
        m_origin[1] = origin[1];
        m_origin[2] = origin[2];
        m_slice_pitch = slicep;
        m_row_pitch = rowp;
    }

    size_t get_row_offset(size_t slice, size_t row) {
        return m_slice_pitch * (m_origin[2] + slice) + m_row_pitch * (m_origin[1] + row) + m_origin[0];
    }
private:
    size_t m_origin[3];
    size_t m_slice_pitch;
    size_t m_row_pitch;
};

struct memobj_map_holder {
    memobj_map_holder(cvk_mem *memobj) : m_mem(memobj), m_mapped(false) {}
    ~memobj_map_holder() {
        if (m_mapped) {
            m_mem->unmap();
        }
    }

    bool CHECK_RETURN map() {
        m_mapped = m_mem->map();
        return m_mapped;
    }

private:
    cvk_mem *m_mem;
    bool m_mapped;
};

cl_int cvk_command_copy_rect::do_action()
{
    uintptr_t dst_base, src_base;
    rectangle rsrc, rdst;

    memobj_map_holder map_holder{m_mem};

    if (!map_holder.map()) {
        return CL_OUT_OF_RESOURCES;
    }

    switch (m_type) {
    case CL_COMMAND_READ_BUFFER_RECT:
        dst_base = reinterpret_cast<uintptr_t>(m_ptr);
        src_base = reinterpret_cast<uintptr_t>(m_mem->host_va());
        rsrc.set_params(m_buffer_origin, m_buffer_slice_pitch, m_buffer_row_pitch);
        rdst.set_params(m_host_origin, m_host_slice_pitch, m_host_row_pitch);
        break;
    case CL_COMMAND_WRITE_BUFFER_RECT:
        dst_base = reinterpret_cast<uintptr_t>(m_mem->host_va());
        src_base = reinterpret_cast<uintptr_t>(m_ptr);
        rsrc.set_params(m_host_origin, m_host_slice_pitch, m_host_row_pitch);
        rdst.set_params(m_buffer_origin, m_buffer_slice_pitch, m_buffer_row_pitch);
        break;
    default:
        return CL_INVALID_OPERATION;
    }

    for (size_t slice = 0; slice < m_region[2]; slice++) {
        cvk_debug_fn("slice = %zu", slice);
        for (size_t row = 0; row < m_region[1]; row++) {
            cvk_debug_fn("row = %zu", row);
            uintptr_t dst = dst_base + rdst.get_row_offset(slice, row);
            uintptr_t src = src_base + rsrc.get_row_offset(slice, row);
            memcpy(reinterpret_cast<void*>(dst), reinterpret_cast<void*>(src), m_region[0]);
        }
    }

    return CL_COMPLETE;
}

cl_int cvk_command_copy_buffer::do_action()
{
    bool success = m_src_buffer->copy_to(m_dst_buffer, m_src_offset, m_dst_offset, m_size);

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

cl_int cvk_command_fill::do_action()
{
    memobj_map_holder map_holder{m_mem};

    if (!map_holder.map()) {
        return CL_OUT_OF_RESOURCES;
    }

    uintptr_t begin = reinterpret_cast<uintptr_t>(m_mem->host_va()) + m_offset;
    uintptr_t end = begin + m_size;

    uintptr_t address = begin;
    while (address < end) {
        memcpy(reinterpret_cast<void*>(address), m_pattern.get(), m_pattern_size);
        address += m_pattern_size;
    }

    return CL_COMPLETE;
}

cl_int cvk_command_map::do_action()
{
    bool success = true;
    if (m_mem->has_flags(CL_MEM_USE_HOST_PTR)) {
        success = m_mem->copy_to(m_mem->host_ptr(), m_offset, m_size);
    }

    return success ? CL_COMPLETE : CL_OUT_OF_RESOURCES;
}

cl_int cvk_command_unmap::do_action()
{
    m_mem->unmap();

    return CL_COMPLETE;
}
<|endoftext|>


<commit_before>#include <cstdio>
#include <utility>
#include "reader.hh"
#include "lisp_ptr.hh"
#include "token.hh"
#include "number.hh"
#include "cons.hh"
#include "symbol.hh"

using namespace std;

namespace {

Lisp_ptr read_la(FILE*, Token&&);

Lisp_ptr read_list(FILE* f){
  Token t = tokenize(f);

  // first check
  if(!t){
    return Lisp_ptr{};
  }else if(t.type() == Token::Type::notation){
    auto n = t.get<Token::Notation>();
    if(n == Token::Notation::r_paren){ // empty list
      return Cons::NIL;
    }else if(n == Token::Notation::dot){ // error: dotted list has no car.
      return {};
    }
  }

  // main loop
  Cons* c = new Cons();
  const Lisp_ptr head{c};

  while(1){
    Lisp_ptr datum{read_la(f, move(t))};
    if(!datum){
      return Lisp_ptr{};
    }

    c->rplaca(datum);

    // check next token
    t = tokenize(f);
    if(!t){
      // TODO: free cons's contents (here or in free_cons_list())
      free_cons_list(head);
      return Lisp_ptr{};
    }else if(t.type() == Token::Type::notation){
      auto n = t.get<Token::Notation>();
      if(n == Token::Notation::r_paren){ // proper list
        c->rplacd(Cons::NIL);
        goto end;
      }else if(n == Token::Notation::dot){ // dotted list
        c->rplacd(read(f));
        t = tokenize(f);
        if(t.type() != Token::Type::notation
           || t.get<Token::Notation>() != Token::Notation::r_paren){
          return Lisp_ptr{}; // error: dotted list has two or more cdrs.
        }
        goto end;
      }
    }

    // readying next cons
    Cons* new_c = new Cons();
    c->rplacd(Lisp_ptr{new_c});
    c = new_c;
  }

 end:
  // if(c->cdr() == undef) ...

  return head;
}

Lisp_ptr read_vector(FILE* f){
  Vector* v = new Vector();

  while(1){
    auto t = tokenize(f);
    if(!t){
      // TODO: free vector's contents
      delete v;
      return Lisp_ptr{};
    }else if((t.type() == Token::Type::notation)
             && (t.get<Token::Notation>()
                 == Token::Notation::r_paren)){
      goto end;
    }else{
      Lisp_ptr datum{read_la(f, move(t))};
      if(!datum){
        return Lisp_ptr{};
      }

      v->emplace_back(datum);
    }
  }

 end:
  return Lisp_ptr{v};
}

Lisp_ptr read_abbrev(const char* name, FILE* f){
  Lisp_ptr first{intern(VM.symtable, name)};
  Lisp_ptr second{read(f)};

  return Lisp_ptr{new Cons{first, Lisp_ptr{new Cons{second, Cons::NIL}}}};
}

Lisp_ptr read_la(FILE* f, Token&& tok){
  switch(tok.type()){
    // simple datum
  case Token::Type::boolean:
    return Lisp_ptr(tok.move<bool>());

  case Token::Type::number:
    return Lisp_ptr(new Number(tok.move<Number>()));

  case Token::Type::character:
    return Lisp_ptr(tok.move<char>());

  case Token::Type::string:
    return Lisp_ptr(new String(tok.move<string>()));

  case Token::Type::identifier:
    return Lisp_ptr{intern(VM.symtable, tok.move<string>())};

    // compound datum
  case Token::Type::notation:
    switch(auto n = tok.move<Token::Notation>()){

    case Token::Notation::l_paren: // list
      return read_list(f);

    case Token::Notation::vector_paren: // vector
      return read_vector(f);

      // abbrev prefix
    case Token::Notation::quote:
      return read_abbrev("quote", f);

    case Token::Notation::quasiquote:
      return read_abbrev("quasiquote", f);

    case Token::Notation::comma:
      return read_abbrev("unquote", f);

    case Token::Notation::comma_at:
      return read_abbrev("unquote-splicing", f);

    case Token::Notation::l_bracket:
    case Token::Notation::l_brace:
      fprintf(stderr, "reader error: not supported notation! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};

    case Token::Notation::r_paren:
    case Token::Notation::r_bracket:
    case Token::Notation::r_brace:
      fprintf(stderr, "reader error: closing notation appeared alone! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};

    case Token::Notation::dot:
    case Token::Notation::bar:
    default:
      fprintf(stderr, "reader error: unexpected notation was passed! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};
    }

  case Token::Type::uninitialized:
  default:
    fprintf(stderr, "reader error: unknown token was passed! (type=%s)\n",
            stringify(tok.type()));
    return Lisp_ptr{};
  }
}

} // namespace

Lisp_ptr read(FILE* f){
  return read_la(f, tokenize(f));
}
<commit_msg>bitly cleanup in reader<commit_after>#include <cstdio>
#include <utility>
#include "reader.hh"
#include "lisp_ptr.hh"
#include "token.hh"
#include "number.hh"
#include "cons.hh"
#include "symbol.hh"

using namespace std;

namespace {

Lisp_ptr read_la(FILE*, Token&&);

Lisp_ptr read_list(FILE* f){
  Token t = tokenize(f);

  // first check
  if(!t){
    return Lisp_ptr{};
  }else if(t.type() == Token::Type::notation){
    auto n = t.get<Token::Notation>();
    if(n == Token::Notation::r_paren){ // empty list
      return Cons::NIL;
    }else if(n == Token::Notation::dot){ // error: dotted list has no car.
      return {};
    }
  }

  // main loop
  Cons* c = new Cons();
  const Lisp_ptr head{c};

  while(1){
    Lisp_ptr datum{read_la(f, move(t))};
    if(!datum){
      return Lisp_ptr{};
    }

    c->rplaca(datum);

    // check next token
    t = tokenize(f);
    if(!t){
      // TODO: free cons's contents (here or in free_cons_list())
      free_cons_list(head);
      return Lisp_ptr{};
    }else if(t.type() == Token::Type::notation){
      auto n = t.get<Token::Notation>();
      if(n == Token::Notation::r_paren){ // proper list
        c->rplacd(Cons::NIL);
        goto end;
      }else if(n == Token::Notation::dot){ // dotted list
        c->rplacd(read(f));
        t = tokenize(f);
        if(t.type() != Token::Type::notation
           || t.get<Token::Notation>() != Token::Notation::r_paren){
          return Lisp_ptr{}; // error: dotted list has two or more cdrs.
        }
        goto end;
      }
    }

    // readying next cons
    Cons* new_c = new Cons();
    c->rplacd(Lisp_ptr{new_c});
    c = new_c;
  }

 end:
  // if(c->cdr() == undef) ...

  return head;
}

Lisp_ptr read_vector(FILE* f){
  Vector* v = new Vector();

  while(1){
    auto t = tokenize(f);
    if(!t){
      // TODO: free vector's contents
      delete v;
      return Lisp_ptr{};
    }else if((t.type() == Token::Type::notation)
             && (t.get<Token::Notation>()
                 == Token::Notation::r_paren)){
      return Lisp_ptr{v};
    }else{
      Lisp_ptr datum{read_la(f, move(t))};
      if(!datum){
        return Lisp_ptr{};
      }

      v->emplace_back(datum);
    }
  }
}

Lisp_ptr read_abbrev(const char* name, FILE* f){
  Lisp_ptr first{intern(VM.symtable, name)};
  Lisp_ptr second{read(f)};

  return Lisp_ptr{new Cons{first, Lisp_ptr{new Cons{second, Cons::NIL}}}};
}

Lisp_ptr read_la(FILE* f, Token&& tok){
  switch(tok.type()){
    // simple datum
  case Token::Type::boolean:
    return Lisp_ptr(tok.move<bool>());

  case Token::Type::number:
    return Lisp_ptr(new Number(tok.move<Number>()));

  case Token::Type::character:
    return Lisp_ptr(tok.move<char>());

  case Token::Type::string:
    return Lisp_ptr(new String(tok.move<string>()));

  case Token::Type::identifier:
    return Lisp_ptr{intern(VM.symtable, tok.move<string>())};

    // compound datum
  case Token::Type::notation:
    switch(auto n = tok.move<Token::Notation>()){

    case Token::Notation::l_paren: // list
      return read_list(f);

    case Token::Notation::vector_paren: // vector
      return read_vector(f);

      // abbrev prefix
    case Token::Notation::quote:
      return read_abbrev("quote", f);

    case Token::Notation::quasiquote:
      return read_abbrev("quasiquote", f);

    case Token::Notation::comma:
      return read_abbrev("unquote", f);

    case Token::Notation::comma_at:
      return read_abbrev("unquote-splicing", f);

    case Token::Notation::l_bracket:
    case Token::Notation::l_brace:
      fprintf(stderr, "reader error: not supported notation! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};

    case Token::Notation::r_paren:
    case Token::Notation::r_bracket:
    case Token::Notation::r_brace:
      fprintf(stderr, "reader error: closing notation appeared alone! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};

    case Token::Notation::dot:
    case Token::Notation::bar:
    default:
      fprintf(stderr, "reader error: unexpected notation was passed! (type=%s)\n",
              stringify(n));
      return Lisp_ptr{};
    }

  case Token::Type::uninitialized:
  default:
    fprintf(stderr, "reader error: unknown token was passed! (type=%s)\n",
            stringify(tok.type()));
    return Lisp_ptr{};
  }
}

} // namespace

Lisp_ptr read(FILE* f){
  return read_la(f, tokenize(f));
}
<|endoftext|>