<commit_before>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/filesystem.h>

#include <primitives/file_iterator.h>

#include <boost/algorithm/string.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/filesystem.hpp>

#include <iostream>
#include <regex>

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif

path get_home_directory()
{
#ifdef _WIN32
    auto home = _wgetenv(L"USERPROFILE");
    if (!home)
        std::cerr << "Cannot get user's home directory (%USERPROFILE%)\n";
#else
    auto home = getenv("HOME");
    if (!home)
    {
        auto p = getpwuid(getuid());
        if (p)
            return p->pw_dir;
        std::cerr << "Cannot get user's home directory ($HOME)\n";
    }
#endif
    if (home == nullptr)
        return "";
    return home;
}

path current_thread_path(const path &p)
{
    thread_local path thread_working_dir = p.empty() ? fs::current_path() : fs::canonical(fs::absolute(p));
    if (p.empty())
        return thread_working_dir;
    return thread_working_dir = fs::canonical(fs::absolute(p));
}

void remove_file(const path &p)
{
    error_code ec;
    fs::remove(p, ec);
    if (ec)
        std::cerr << "Cannot remove file: " << p.string() << "\n";
}

void remove_all_from_dir(const path &dir)
{
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        fs::remove_all(f);
}

String normalize_path(const path &p)
{
    if (p.empty())
        return "";
    auto s = p.string();
    normalize_string(s);
    return s;
}

std::wstring wnormalize_path(const path &p)
{
    if (p.empty())
        return L"";
    auto s = p.wstring();
    normalize_string(s);
    return s;
}

String read_file(const path &p, bool no_size_check)
{
    if (!fs::exists(p))
        throw std::runtime_error("File '" + p.string() + "' does not exist");

    auto fn = p.string();
    ScopedFile ifile(fn, "rb");

    size_t sz = (size_t)fs::file_size(p);
    if (!no_size_check && sz > 10'000'000)
        throw std::runtime_error("File " + fn + " is very big (> ~10 MB)");

    String f;
    f.resize(sz);
    ifile.read(&f[0], sz);
    return f;
}

String read_file_without_bom(const path &p, bool no_size_check)
{
    auto s = read_file(p, no_size_check);

    static const std::vector<std::vector<uint8_t>> boms{
        // UTF-8
        { 0xEF, 0xBB, 0xBF, },

        // UTF-16 BE, LE
        { 0xFE, 0xFF, },
        { 0xFF, 0xFE, },

        // UTF-32 BE, LE
        { 0x00, 0x00, 0xFE, 0xFF, },
        { 0xFF, 0xFE, 0x00, 0x00, },

        // UTF-7
        { 0x2B, 0x2F, 0x76, 0x38 },
        { 0x2B, 0x2F, 0x76, 0x39 },
        { 0x2B, 0x2F, 0x76, 0x2B },
        { 0x2B, 0x2F, 0x76, 0x2F },
        { 0x2B, 0x2F, 0x76, 0x38, 0x2D },

        // UTF-1
        { 0xF7, 0x64, 0x4C, },

        // UTF-EBCDIC
        { 0xDD, 0x73, 0x66, 0x73 },

        // SCSU
        { 0x0E, 0xFE, 0xFF, },

        // BOCU-1
        { 0xFB, 0xEE, 0x28, },

        // GB-18030
        { 0x84, 0x31, 0x95, 0x33 },
    };

    for (auto &b : boms)
    {
        if (s.size() >= b.size() && memcmp(s.data(), b.data(), b.size()) == 0)
        {
            s = s.substr(b.size());
            break;
        }
    }

    return s;
}

Strings read_lines(const path &p)
{
    auto s = read_file(p);
    return split_lines(s);
}

static void write_file1(const path &p, const String &s, const char *mode)
{
    auto pp = p.parent_path();
    if (!pp.empty())
        fs::create_directories(pp);

    auto f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file '" + p.string() + "' for writing");
    fwrite(s.c_str(), s.size(), 1, f);
    fclose(f);
}

void write_file(const path &p, const String &s)
{
    write_file1(p, s, "wb");
}

void write_file_if_different(const path &p, const String &s)
{
    if (fs::exists(p))
    {
        auto s2 = read_file(p, true);
        if (s == s2)
            return;
    }
    write_file(p, s);
}

void prepend_file(const path &p, const String &s)
{
    auto s2 = read_file(p);
    write_file(p, s + s2);
}

void append_file(const path &p, const String &s)
{
    write_file1(p, s, "ab");
}

void copy_dir(const path &src, const path &dst)
{
    error_code ec;
    fs::create_directories(dst, ec);
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(src), {}))
    {
        if (fs::is_directory(f))
            copy_dir(f, dst / f.path().filename());
        else
            copy_file(f, dst / f.path().filename(), fs::copy_options::overwrite_existing);
    }
}

void remove_files(const Files &files)
{
    for (auto &f : files)
        fs::remove(f);
}

void remove_files_like(const Files &files, const String &regex)
{
    remove_files(filter_files_like(files, regex));
}

void remove_files_like(const path &dir, const String &regex, bool recursive)
{
    remove_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files(const path &dir, bool recursive)
{
    Files files;
    if (!fs::exists(dir))
        return files;
    if (recursive)
    {
        for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    else
    {
        for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    return files;
}

Files enumerate_files_like(const path &dir, const String &regex, bool recursive)
{
    return filter_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files_like(const Files &files, const String &regex)
{
    return filter_files_like(files, regex);
}

Files filter_files_like(const Files &files, const String &regex)
{
    Files fls;
    std::regex r(regex);
    for (auto &f : files)
    {
        if (!std::regex_match(f.filename().string(), r))
            continue;
        fls.insert(f);
    }
    return fls;
}

bool is_under_root(path p, const path &root_dir)
{
    if (p.empty())
        return false;
    if (fs::exists(p))
        // Converts p, which must exist, to an absolute path
        // that has no symbolic link, dot, or dot-dot elements.
        p = fs::canonical(p);
    while (!p.empty() && p != p.root_path())
    {
        if (p == root_dir)
            return true;
        p = p.parent_path();
    }
    return false;
}

bool compare_files(const path &fn1, const path &fn2)
{
    FileIterator fi({ fn1, fn2 });
    return fi.iterate([](const auto &bufs, auto sz)
    {
        if (memcmp(bufs[0].get().data(), bufs[1].get().data(), (size_t)sz) != 0)
            return false;
        return true;
    });
}

bool compare_dirs(const path &dir1, const path &dir2)
{
    auto traverse_dir = [](const auto &dir)
    {
        std::vector<path> files;
        if (fs::exists(dir))
            for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
            {
                if (!fs::is_regular_file(f))
                    continue;
                files.push_back(f);
            }
        return files;
    };

    auto files1 = traverse_dir(dir1);
    auto files2 = traverse_dir(dir2);

    if (files1.empty())
        return false; // throw std::runtime_error("left side has no files");
    if (files2.empty())
        return false; // throw std::runtime_error("right side has no files");
    if (files1.size() != files2.size())
        return false; // throw std::runtime_error("different number of files");

    auto sz = files1.size();
    for (size_t i = 0; i < sz; i++)
    {
        if (!compare_files(files1[i], files2[i]))
            return false;
    }

    return true;
}

FileIterator::FileIterator(const FilesOrdered &fns)
{
    if (fns.empty())
        throw std::runtime_error("Provide at least one file");

    for (auto &f : fns)
    {
        File d;
        d.size = fs::file_size(f);
        d.ifile = std::make_unique<ScopedFile>(f, "rb");
        files.emplace_back(std::move(d));
    }
}

bool FileIterator::is_same_size() const
{
    auto sz = files.front().size;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.size == sz; });
}

bool FileIterator::is_same_read_size() const
{
    auto sz = files.front().read;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.read == sz; });
}

bool FileIterator::iterate(std::function<bool(const BuffersRef &, uint64_t)> f)
{
    if (!is_same_size())
        return false;

    BuffersRef buffers;
    for (auto &f : files)
    {
        f.buf.resize(buffer_size);
        buffers.emplace_back(f.buf);
    }

    while (std::none_of(files.begin(), files.end(),
        [](const auto &f) { return feof(f.ifile->getHandle()); }))
    {
        std::for_each(files.begin(), files.end(), [this](auto &f)
        {
            f.read = f.ifile->read((char *)&f.buf[0], buffer_size);
        });
        if (!is_same_read_size())
            return false;

        if (!f(buffers, files.front().read))
            return false;
    }
    return true;
}

void setup_utf8_filesystem()
{
    boost::nowide::nowide_filesystem();
}

path unique_path(const path &p)
{
    return boost::filesystem::unique_path(p.u8string()).wstring();
}

namespace primitives::filesystem
{

FILE *fopen(const path &p, const char *mode)
{
#ifdef _WIN32
    auto s = p.wstring();
    if (s.size() >= 255)
    {
        s = L"\\\\?\\" + s;
        boost::replace_all(s, L"/", L"\\");
    }
    return _wfopen(s.c_str(), path(mode).wstring().c_str());
#else
    return ::fopen(p.string().c_str(), mode);
#endif
}

void create(const path &p)
{
    auto s = p.parent_path().string();
#ifdef _WIN32
    if (s.size() >= 255)
    {
        s = "\\\\?\\" + s;
        boost::replace_all(s, "/", "\\");
    }
#endif
    fs::create_directories(s);
    fclose(fopen(p, "wb"));
}

}

ScopedFile::ScopedFile(const path &p, const char *mode)
{
    f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file: " + p.string() + ", errno = " + std::to_string(errno));
}

ScopedFile::~ScopedFile()
{
    if (f)
        fclose(f);
}

size_t ScopedFile::read(void *buf, size_t sz)
{
    return fread(buf, 1, sz, f);
}
<commit_msg>Fix path comparison.<commit_after>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/filesystem.h>

#include <primitives/file_iterator.h>

#include <boost/algorithm/string.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/filesystem.hpp>

#include <iostream>
#include <regex>

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif

path get_home_directory()
{
#ifdef _WIN32
    auto home = _wgetenv(L"USERPROFILE");
    if (!home)
        std::cerr << "Cannot get user's home directory (%USERPROFILE%)\n";
#else
    auto home = getenv("HOME");
    if (!home)
    {
        auto p = getpwuid(getuid());
        if (p)
            return p->pw_dir;
        std::cerr << "Cannot get user's home directory ($HOME)\n";
    }
#endif
    if (home == nullptr)
        return "";
    return home;
}

path current_thread_path(const path &p)
{
    thread_local path thread_working_dir = p.empty() ? fs::current_path() : fs::canonical(fs::absolute(p));
    if (p.empty())
        return thread_working_dir;
    return thread_working_dir = fs::canonical(fs::absolute(p));
}

void remove_file(const path &p)
{
    error_code ec;
    fs::remove(p, ec);
    if (ec)
        std::cerr << "Cannot remove file: " << p.string() << "\n";
}

void remove_all_from_dir(const path &dir)
{
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        fs::remove_all(f);
}

String normalize_path(const path &p)
{
    if (p.empty())
        return "";
    auto s = p.string();
    normalize_string(s);
    return s;
}

std::wstring wnormalize_path(const path &p)
{
    if (p.empty())
        return L"";
    auto s = p.wstring();
    normalize_string(s);
    return s;
}

String read_file(const path &p, bool no_size_check)
{
    if (!fs::exists(p))
        throw std::runtime_error("File '" + p.string() + "' does not exist");

    auto fn = p.string();
    ScopedFile ifile(fn, "rb");

    size_t sz = (size_t)fs::file_size(p);
    if (!no_size_check && sz > 10'000'000)
        throw std::runtime_error("File " + fn + " is very big (> ~10 MB)");

    String f;
    f.resize(sz);
    ifile.read(&f[0], sz);
    return f;
}

String read_file_without_bom(const path &p, bool no_size_check)
{
    auto s = read_file(p, no_size_check);

    static const std::vector<std::vector<uint8_t>> boms{
        // UTF-8
        { 0xEF, 0xBB, 0xBF, },

        // UTF-16 BE, LE
        { 0xFE, 0xFF, },
        { 0xFF, 0xFE, },

        // UTF-32 BE, LE
        { 0x00, 0x00, 0xFE, 0xFF, },
        { 0xFF, 0xFE, 0x00, 0x00, },

        // UTF-7
        { 0x2B, 0x2F, 0x76, 0x38 },
        { 0x2B, 0x2F, 0x76, 0x39 },
        { 0x2B, 0x2F, 0x76, 0x2B },
        { 0x2B, 0x2F, 0x76, 0x2F },
        { 0x2B, 0x2F, 0x76, 0x38, 0x2D },

        // UTF-1
        { 0xF7, 0x64, 0x4C, },

        // UTF-EBCDIC
        { 0xDD, 0x73, 0x66, 0x73 },

        // SCSU
        { 0x0E, 0xFE, 0xFF, },

        // BOCU-1
        { 0xFB, 0xEE, 0x28, },

        // GB-18030
        { 0x84, 0x31, 0x95, 0x33 },
    };

    for (auto &b : boms)
    {
        if (s.size() >= b.size() && memcmp(s.data(), b.data(), b.size()) == 0)
        {
            s = s.substr(b.size());
            break;
        }
    }

    return s;
}

Strings read_lines(const path &p)
{
    auto s = read_file(p);
    return split_lines(s);
}

static void write_file1(const path &p, const String &s, const char *mode)
{
    auto pp = p.parent_path();
    if (!pp.empty())
        fs::create_directories(pp);

    auto f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file '" + p.string() + "' for writing");
    fwrite(s.c_str(), s.size(), 1, f);
    fclose(f);
}

void write_file(const path &p, const String &s)
{
    write_file1(p, s, "wb");
}

void write_file_if_different(const path &p, const String &s)
{
    if (fs::exists(p))
    {
        auto s2 = read_file(p, true);
        if (s == s2)
            return;
    }
    write_file(p, s);
}

void prepend_file(const path &p, const String &s)
{
    auto s2 = read_file(p);
    write_file(p, s + s2);
}

void append_file(const path &p, const String &s)
{
    write_file1(p, s, "ab");
}

void copy_dir(const path &src, const path &dst)
{
    error_code ec;
    fs::create_directories(dst, ec);
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(src), {}))
    {
        if (fs::is_directory(f))
            copy_dir(f, dst / f.path().filename());
        else
            copy_file(f, dst / f.path().filename(), fs::copy_options::overwrite_existing);
    }
}

void remove_files(const Files &files)
{
    for (auto &f : files)
        fs::remove(f);
}

void remove_files_like(const Files &files, const String &regex)
{
    remove_files(filter_files_like(files, regex));
}

void remove_files_like(const path &dir, const String &regex, bool recursive)
{
    remove_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files(const path &dir, bool recursive)
{
    Files files;
    if (!fs::exists(dir))
        return files;
    if (recursive)
    {
        for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    else
    {
        for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    return files;
}

Files enumerate_files_like(const path &dir, const String &regex, bool recursive)
{
    return filter_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files_like(const Files &files, const String &regex)
{
    return filter_files_like(files, regex);
}

Files filter_files_like(const Files &files, const String &regex)
{
    Files fls;
    std::regex r(regex);
    for (auto &f : files)
    {
        if (!std::regex_match(f.filename().string(), r))
            continue;
        fls.insert(f);
    }
    return fls;
}

bool is_under_root(path p, const path &root_dir)
{
    if (p.empty())
        return false;
    if (fs::exists(p))
        // Converts p, which must exist, to an absolute path
        // that has no symbolic link, dot, or dot-dot elements.
        p = fs::canonical(p);
    error_code ec;
    while (!p.empty() && p != p.root_path())
    {
        if (fs::equivalent(p, root_dir, ec))
            return true;
        p = p.parent_path();
    }
    return false;
}

bool compare_files(const path &fn1, const path &fn2)
{
    FileIterator fi({ fn1, fn2 });
    return fi.iterate([](const auto &bufs, auto sz)
    {
        if (memcmp(bufs[0].get().data(), bufs[1].get().data(), (size_t)sz) != 0)
            return false;
        return true;
    });
}

bool compare_dirs(const path &dir1, const path &dir2)
{
    auto traverse_dir = [](const auto &dir)
    {
        std::vector<path> files;
        if (fs::exists(dir))
            for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
            {
                if (!fs::is_regular_file(f))
                    continue;
                files.push_back(f);
            }
        return files;
    };

    auto files1 = traverse_dir(dir1);
    auto files2 = traverse_dir(dir2);

    if (files1.empty())
        return false; // throw std::runtime_error("left side has no files");
    if (files2.empty())
        return false; // throw std::runtime_error("right side has no files");
    if (files1.size() != files2.size())
        return false; // throw std::runtime_error("different number of files");

    auto sz = files1.size();
    for (size_t i = 0; i < sz; i++)
    {
        if (!compare_files(files1[i], files2[i]))
            return false;
    }

    return true;
}

FileIterator::FileIterator(const FilesOrdered &fns)
{
    if (fns.empty())
        throw std::runtime_error("Provide at least one file");

    for (auto &f : fns)
    {
        File d;
        d.size = fs::file_size(f);
        d.ifile = std::make_unique<ScopedFile>(f, "rb");
        files.emplace_back(std::move(d));
    }
}

bool FileIterator::is_same_size() const
{
    auto sz = files.front().size;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.size == sz; });
}

bool FileIterator::is_same_read_size() const
{
    auto sz = files.front().read;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.read == sz; });
}

bool FileIterator::iterate(std::function<bool(const BuffersRef &, uint64_t)> f)
{
    if (!is_same_size())
        return false;

    BuffersRef buffers;
    for (auto &f : files)
    {
        f.buf.resize(buffer_size);
        buffers.emplace_back(f.buf);
    }

    while (std::none_of(files.begin(), files.end(),
        [](const auto &f) { return feof(f.ifile->getHandle()); }))
    {
        std::for_each(files.begin(), files.end(), [this](auto &f)
        {
            f.read = f.ifile->read((char *)&f.buf[0], buffer_size);
        });
        if (!is_same_read_size())
            return false;

        if (!f(buffers, files.front().read))
            return false;
    }
    return true;
}

void setup_utf8_filesystem()
{
    boost::nowide::nowide_filesystem();
}

path unique_path(const path &p)
{
    return boost::filesystem::unique_path(p.u8string()).wstring();
}

namespace primitives::filesystem
{

FILE *fopen(const path &p, const char *mode)
{
#ifdef _WIN32
    auto s = p.wstring();
    if (s.size() >= 255)
    {
        s = L"\\\\?\\" + s;
        boost::replace_all(s, L"/", L"\\");
    }
    return _wfopen(s.c_str(), path(mode).wstring().c_str());
#else
    return ::fopen(p.string().c_str(), mode);
#endif
}

void create(const path &p)
{
    auto s = p.parent_path().string();
#ifdef _WIN32
    if (s.size() >= 255)
    {
        s = "\\\\?\\" + s;
        boost::replace_all(s, "/", "\\");
    }
#endif
    fs::create_directories(s);
    fclose(fopen(p, "wb"));
}

}

ScopedFile::ScopedFile(const path &p, const char *mode)
{
    f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file: " + p.string() + ", errno = " + std::to_string(errno));
}

ScopedFile::~ScopedFile()
{
    if (f)
        fclose(f);
}

size_t ScopedFile::read(void *buf, size_t sz)
{
    return fread(buf, 1, sz, f);
}
<|endoftext|>


<commit_before>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/filesystem.h>

#include <primitives/file_iterator.h>

#include <boost/algorithm/string.hpp>
#include <boost/nowide/integration/filesystem.hpp>

#include <iostream>
#include <regex>

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif

path get_home_directory()
{
#ifdef _WIN32
    auto home = getenv("USERPROFILE");
    if (!home)
        std::cerr << "Cannot get user's home directory (%USERPROFILE%)\n";
#else
    auto home = getenv("HOME");
    if (!home)
    {
        auto p = getpwuid(getuid());
        if (p)
            return p->pw_dir;
        std::cerr << "Cannot get user's home directory ($HOME)\n";
    }
#endif
    if (home == nullptr)
        return "";
    return home;
}

path current_thread_path(const path &p)
{
    thread_local path thread_working_dir = p.empty() ? fs::current_path() : fs::canonical(fs::absolute(p));
    if (p.empty())
        return thread_working_dir;
    return thread_working_dir = fs::canonical(fs::absolute(p));
}

void remove_file(const path &p)
{
    boost::system::error_code ec;
    fs::remove(p, ec);
    if (ec)
        std::cerr << "Cannot remove file: " << p.string() << "\n";
}

void remove_all_from_dir(const path &dir)
{
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        fs::remove_all(f);
}

String normalize_path(const path &p)
{
    if (p.empty())
        return "";
    auto s = p.string();
    normalize_string(s);
    return s;
}

std::wstring wnormalize_path(const path &p)
{
    if (p.empty())
        return L"";
    auto s = p.wstring();
    normalize_string(s);
    return s;
}

String read_file(const path &p, bool no_size_check)
{
    if (!fs::exists(p))
        throw std::runtime_error("File '" + p.string() + "' does not exist");

    auto fn = p.string();
    ScopedFile ifile(fn, "rb");

    size_t sz = (size_t)fs::file_size(p);
    if (!no_size_check && sz > 10'000'000)
        throw std::runtime_error("File " + fn + " is very big (> ~10 MB)");

    String f;
    f.resize(sz);
    ifile.read(&f[0], sz);
    return f;
}

String read_file_without_bom(const path &p, bool no_size_check)
{
    auto s = read_file(p, no_size_check);

    static const std::vector<std::vector<uint8_t>> boms{
        // UTF-8
        { 0xEF, 0xBB, 0xBF, },

        // UTF-16 BE, LE
        { 0xFE, 0xFF, },
        { 0xFF, 0xFE, },

        // UTF-32 BE, LE
        { 0x00, 0x00, 0xFE, 0xFF, },
        { 0xFF, 0xFE, 0x00, 0x00, },

        // UTF-7
        { 0x2B, 0x2F, 0x76, 0x38 },
        { 0x2B, 0x2F, 0x76, 0x39 },
        { 0x2B, 0x2F, 0x76, 0x2B },
        { 0x2B, 0x2F, 0x76, 0x2F },
        { 0x2B, 0x2F, 0x76, 0x38, 0x2D },

        // UTF-1
        { 0xF7, 0x64, 0x4C, },

        // UTF-EBCDIC
        { 0xDD, 0x73, 0x66, 0x73 },

        // SCSU
        { 0x0E, 0xFE, 0xFF, },

        // BOCU-1
        { 0xFB, 0xEE, 0x28, },

        // GB-18030
        { 0x84, 0x31, 0x95, 0x33 },
    };

    for (auto &b : boms)
    {
        if (s.size() >= b.size() && memcmp(s.data(), b.data(), b.size()) == 0)
        {
            s = s.substr(b.size());
            break;
        }
    }

    return s;
}

Strings read_lines(const path &p)
{
    auto s = read_file(p);
    return split_lines(s);
}

static void write_file1(const path &p, const String &s, const char *mode)
{
    auto pp = p.parent_path();
    if (!pp.empty())
        fs::create_directories(pp);

    auto f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file '" + p.string() + "' for writing");
    fwrite(s.c_str(), s.size(), 1, f);
    fclose(f);
}

void write_file(const path &p, const String &s)
{
    write_file1(p, s, "wb");
}

void write_file_if_different(const path &p, const String &s)
{
    if (fs::exists(p))
    {
        auto s2 = read_file(p, true);
        if (s == s2)
            return;
    }
    write_file(p, s);
}

void prepend_file(const path &p, const String &s)
{
    auto s2 = read_file(p);
    write_file(p, s + s2);
}

void append_file(const path &p, const String &s)
{
    write_file1(p, s, "ab");
}

void copy_dir(const path &src, const path &dst)
{
    boost::system::error_code ec;
    fs::create_directories(dst, ec);
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(src), {}))
    {
        if (fs::is_directory(f))
            copy_dir(f, dst / f.path().filename());
        else
            fs::copy_file(f, dst / f.path().filename(), fs::copy_option::overwrite_if_exists);
    }
}

void remove_files(const Files &files)
{
    for (auto &f : files)
        fs::remove(f);
}

void remove_files_like(const Files &files, const String &regex)
{
    remove_files(filter_files_like(files, regex));
}

void remove_files_like(const path &dir, const String &regex, bool recursive)
{
    remove_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files(const path &dir, bool recursive)
{
    Files files;
    if (!fs::exists(dir))
        return files;
    if (recursive)
    {
        for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    else
    {
        for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    return files;
}

Files enumerate_files_like(const path &dir, const String &regex, bool recursive)
{
    return filter_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files_like(const Files &files, const String &regex)
{
    return filter_files_like(files, regex);
}

Files filter_files_like(const Files &files, const String &regex)
{
    Files fls;
    std::regex r(regex);
    for (auto &f : files)
    {
        if (!std::regex_match(f.filename().string(), r))
            continue;
        fls.insert(f);
    }
    return fls;
}

bool is_under_root(path p, const path &root_dir)
{
    if (p.empty())
        return false;
    if (fs::exists(p))
        // Converts p, which must exist, to an absolute path
        // that has no symbolic link, dot, or dot-dot elements.
        p = fs::canonical(p);
    while (!p.empty())
    {
        if (p == root_dir)
            return true;
        p = p.parent_path();
    }
    return false;
}

bool compare_files(const path &fn1, const path &fn2)
{
    FileIterator fi({ fn1, fn2 });
    return fi.iterate([](const auto &bufs, auto sz)
    {
        if (memcmp(bufs[0].get().data(), bufs[1].get().data(), (size_t)sz) != 0)
            return false;
        return true;
    });
}

bool compare_dirs(const path &dir1, const path &dir2)
{
    auto traverse_dir = [](const auto &dir)
    {
        std::vector<path> files;
        if (fs::exists(dir))
            for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
            {
                if (!fs::is_regular_file(f))
                    continue;
                files.push_back(f);
            }
        return files;
    };

    auto files1 = traverse_dir(dir1);
    auto files2 = traverse_dir(dir2);

    if (files1.empty())
        return false; // throw std::runtime_error("left side has no files");
    if (files2.empty())
        return false; // throw std::runtime_error("right side has no files");
    if (files1.size() != files2.size())
        return false; // throw std::runtime_error("different number of files");

    auto sz = files1.size();
    for (size_t i = 0; i < sz; i++)
    {
        if (!compare_files(files1[i], files2[i]))
            return false;
    }

    return true;
}

FileIterator::FileIterator(const std::vector<path> &fns)
{
    if (fns.empty())
        throw std::runtime_error("Provide at least one file");

    for (auto &f : fns)
    {
        File d;
        d.size = fs::file_size(f);
        d.ifile = std::make_unique<ScopedFile>(f, "rb");
        files.push_back(std::move(d));
    }
}

bool FileIterator::is_same_size() const
{
    auto sz = files.front().size;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.size == sz; });
}

bool FileIterator::is_same_read_size() const
{
    auto sz = files.front().read;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.read == sz; });
}

bool FileIterator::iterate(std::function<bool(const BuffersRef &, uint64_t)> f)
{
    if (!is_same_size())
        return false;

    BuffersRef buffers;
    for (auto &f : files)
    {
        f.buf.resize(buffer_size);
        buffers.emplace_back(f.buf);
    }

    while (std::none_of(files.begin(), files.end(),
        [](const auto &f) { return feof(f.ifile->getHandle()); }))
    {
        std::for_each(files.begin(), files.end(), [this](auto &f)
        {
            f.read = f.ifile->read((char *)&f.buf[0], buffer_size);
        });
        if (!is_same_read_size())
            return false;

        if (!f(buffers, files.front().read))
            return false;
    }
    return true;
}

void setup_utf8_filesystem()
{
    boost::nowide::nowide_filesystem();
}

namespace primitives::filesystem
{

FILE *fopen(const path &p, const char *mode)
{
#ifdef _WIN32
    auto s = p.wstring();
    if (s.size() >= 255)
    {
        s = L"\\\\?\\" + s;
        boost::replace_all(s, L"/", L"\\");
    }
    return _wfopen(s.c_str(), path(mode).wstring().c_str());
#else
    return ::fopen(p.string().c_str(), mode);
#endif
}

void create(const path &p)
{
    auto s = p.parent_path().string();
#ifdef _WIN32
    if (s.size() >= 255)
    {
        s = "\\\\?\\" + s;
        boost::replace_all(s, "/", "\\");
    }
#endif
    fs::create_directories(s);
    fclose(fopen(p, "wb"));
}

}

ScopedFile::ScopedFile(const path &p, const char *mode)
{
    f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file: " + p.string() + ", errno = " + std::to_string(errno));
}

ScopedFile::~ScopedFile()
{
    if (f)
        fclose(f);
}

size_t ScopedFile::read(void *buf, size_t sz)
{
    return fread(buf, 1, sz, f);
}
<commit_msg>Correctly get user profile on windows with wide strings.<commit_after>// Copyright (C) 2018 Egor Pugin <egor.pugin@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <primitives/filesystem.h>

#include <primitives/file_iterator.h>

#include <boost/algorithm/string.hpp>
#include <boost/nowide/integration/filesystem.hpp>

#include <iostream>
#include <regex>

#ifndef _WIN32
#include <unistd.h>
#include <sys/types.h>
#include <pwd.h>
#endif

path get_home_directory()
{
#ifdef _WIN32
    auto home = _wgetenv(L"USERPROFILE");
    if (!home)
        std::cerr << "Cannot get user's home directory (%USERPROFILE%)\n";
#else
    auto home = getenv("HOME");
    if (!home)
    {
        auto p = getpwuid(getuid());
        if (p)
            return p->pw_dir;
        std::cerr << "Cannot get user's home directory ($HOME)\n";
    }
#endif
    if (home == nullptr)
        return "";
    return home;
}

path current_thread_path(const path &p)
{
    thread_local path thread_working_dir = p.empty() ? fs::current_path() : fs::canonical(fs::absolute(p));
    if (p.empty())
        return thread_working_dir;
    return thread_working_dir = fs::canonical(fs::absolute(p));
}

void remove_file(const path &p)
{
    boost::system::error_code ec;
    fs::remove(p, ec);
    if (ec)
        std::cerr << "Cannot remove file: " << p.string() << "\n";
}

void remove_all_from_dir(const path &dir)
{
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        fs::remove_all(f);
}

String normalize_path(const path &p)
{
    if (p.empty())
        return "";
    auto s = p.string();
    normalize_string(s);
    return s;
}

std::wstring wnormalize_path(const path &p)
{
    if (p.empty())
        return L"";
    auto s = p.wstring();
    normalize_string(s);
    return s;
}

String read_file(const path &p, bool no_size_check)
{
    if (!fs::exists(p))
        throw std::runtime_error("File '" + p.string() + "' does not exist");

    auto fn = p.string();
    ScopedFile ifile(fn, "rb");

    size_t sz = (size_t)fs::file_size(p);
    if (!no_size_check && sz > 10'000'000)
        throw std::runtime_error("File " + fn + " is very big (> ~10 MB)");

    String f;
    f.resize(sz);
    ifile.read(&f[0], sz);
    return f;
}

String read_file_without_bom(const path &p, bool no_size_check)
{
    auto s = read_file(p, no_size_check);

    static const std::vector<std::vector<uint8_t>> boms{
        // UTF-8
        { 0xEF, 0xBB, 0xBF, },

        // UTF-16 BE, LE
        { 0xFE, 0xFF, },
        { 0xFF, 0xFE, },

        // UTF-32 BE, LE
        { 0x00, 0x00, 0xFE, 0xFF, },
        { 0xFF, 0xFE, 0x00, 0x00, },

        // UTF-7
        { 0x2B, 0x2F, 0x76, 0x38 },
        { 0x2B, 0x2F, 0x76, 0x39 },
        { 0x2B, 0x2F, 0x76, 0x2B },
        { 0x2B, 0x2F, 0x76, 0x2F },
        { 0x2B, 0x2F, 0x76, 0x38, 0x2D },

        // UTF-1
        { 0xF7, 0x64, 0x4C, },

        // UTF-EBCDIC
        { 0xDD, 0x73, 0x66, 0x73 },

        // SCSU
        { 0x0E, 0xFE, 0xFF, },

        // BOCU-1
        { 0xFB, 0xEE, 0x28, },

        // GB-18030
        { 0x84, 0x31, 0x95, 0x33 },
    };

    for (auto &b : boms)
    {
        if (s.size() >= b.size() && memcmp(s.data(), b.data(), b.size()) == 0)
        {
            s = s.substr(b.size());
            break;
        }
    }

    return s;
}

Strings read_lines(const path &p)
{
    auto s = read_file(p);
    return split_lines(s);
}

static void write_file1(const path &p, const String &s, const char *mode)
{
    auto pp = p.parent_path();
    if (!pp.empty())
        fs::create_directories(pp);

    auto f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file '" + p.string() + "' for writing");
    fwrite(s.c_str(), s.size(), 1, f);
    fclose(f);
}

void write_file(const path &p, const String &s)
{
    write_file1(p, s, "wb");
}

void write_file_if_different(const path &p, const String &s)
{
    if (fs::exists(p))
    {
        auto s2 = read_file(p, true);
        if (s == s2)
            return;
    }
    write_file(p, s);
}

void prepend_file(const path &p, const String &s)
{
    auto s2 = read_file(p);
    write_file(p, s + s2);
}

void append_file(const path &p, const String &s)
{
    write_file1(p, s, "ab");
}

void copy_dir(const path &src, const path &dst)
{
    boost::system::error_code ec;
    fs::create_directories(dst, ec);
    for (auto &f : boost::make_iterator_range(fs::directory_iterator(src), {}))
    {
        if (fs::is_directory(f))
            copy_dir(f, dst / f.path().filename());
        else
            fs::copy_file(f, dst / f.path().filename(), fs::copy_option::overwrite_if_exists);
    }
}

void remove_files(const Files &files)
{
    for (auto &f : files)
        fs::remove(f);
}

void remove_files_like(const Files &files, const String &regex)
{
    remove_files(filter_files_like(files, regex));
}

void remove_files_like(const path &dir, const String &regex, bool recursive)
{
    remove_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files(const path &dir, bool recursive)
{
    Files files;
    if (!fs::exists(dir))
        return files;
    if (recursive)
    {
        for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    else
    {
        for (auto &f : boost::make_iterator_range(fs::directory_iterator(dir), {}))
        {
            if (!fs::is_regular_file(f))
                continue;
            files.insert(f);
        }
    }
    return files;
}

Files enumerate_files_like(const path &dir, const String &regex, bool recursive)
{
    return filter_files_like(enumerate_files(dir, recursive), regex);
}

Files enumerate_files_like(const Files &files, const String &regex)
{
    return filter_files_like(files, regex);
}

Files filter_files_like(const Files &files, const String &regex)
{
    Files fls;
    std::regex r(regex);
    for (auto &f : files)
    {
        if (!std::regex_match(f.filename().string(), r))
            continue;
        fls.insert(f);
    }
    return fls;
}

bool is_under_root(path p, const path &root_dir)
{
    if (p.empty())
        return false;
    if (fs::exists(p))
        // Converts p, which must exist, to an absolute path
        // that has no symbolic link, dot, or dot-dot elements.
        p = fs::canonical(p);
    while (!p.empty())
    {
        if (p == root_dir)
            return true;
        p = p.parent_path();
    }
    return false;
}

bool compare_files(const path &fn1, const path &fn2)
{
    FileIterator fi({ fn1, fn2 });
    return fi.iterate([](const auto &bufs, auto sz)
    {
        if (memcmp(bufs[0].get().data(), bufs[1].get().data(), (size_t)sz) != 0)
            return false;
        return true;
    });
}

bool compare_dirs(const path &dir1, const path &dir2)
{
    auto traverse_dir = [](const auto &dir)
    {
        std::vector<path> files;
        if (fs::exists(dir))
            for (auto &f : boost::make_iterator_range(fs::recursive_directory_iterator(dir), {}))
            {
                if (!fs::is_regular_file(f))
                    continue;
                files.push_back(f);
            }
        return files;
    };

    auto files1 = traverse_dir(dir1);
    auto files2 = traverse_dir(dir2);

    if (files1.empty())
        return false; // throw std::runtime_error("left side has no files");
    if (files2.empty())
        return false; // throw std::runtime_error("right side has no files");
    if (files1.size() != files2.size())
        return false; // throw std::runtime_error("different number of files");

    auto sz = files1.size();
    for (size_t i = 0; i < sz; i++)
    {
        if (!compare_files(files1[i], files2[i]))
            return false;
    }

    return true;
}

FileIterator::FileIterator(const std::vector<path> &fns)
{
    if (fns.empty())
        throw std::runtime_error("Provide at least one file");

    for (auto &f : fns)
    {
        File d;
        d.size = fs::file_size(f);
        d.ifile = std::make_unique<ScopedFile>(f, "rb");
        files.push_back(std::move(d));
    }
}

bool FileIterator::is_same_size() const
{
    auto sz = files.front().size;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.size == sz; });
}

bool FileIterator::is_same_read_size() const
{
    auto sz = files.front().read;
    return std::all_of(files.begin(), files.end(),
        [sz](const auto &f) { return f.read == sz; });
}

bool FileIterator::iterate(std::function<bool(const BuffersRef &, uint64_t)> f)
{
    if (!is_same_size())
        return false;

    BuffersRef buffers;
    for (auto &f : files)
    {
        f.buf.resize(buffer_size);
        buffers.emplace_back(f.buf);
    }

    while (std::none_of(files.begin(), files.end(),
        [](const auto &f) { return feof(f.ifile->getHandle()); }))
    {
        std::for_each(files.begin(), files.end(), [this](auto &f)
        {
            f.read = f.ifile->read((char *)&f.buf[0], buffer_size);
        });
        if (!is_same_read_size())
            return false;

        if (!f(buffers, files.front().read))
            return false;
    }
    return true;
}

void setup_utf8_filesystem()
{
    boost::nowide::nowide_filesystem();
}

namespace primitives::filesystem
{

FILE *fopen(const path &p, const char *mode)
{
#ifdef _WIN32
    auto s = p.wstring();
    if (s.size() >= 255)
    {
        s = L"\\\\?\\" + s;
        boost::replace_all(s, L"/", L"\\");
    }
    return _wfopen(s.c_str(), path(mode).wstring().c_str());
#else
    return ::fopen(p.string().c_str(), mode);
#endif
}

void create(const path &p)
{
    auto s = p.parent_path().string();
#ifdef _WIN32
    if (s.size() >= 255)
    {
        s = "\\\\?\\" + s;
        boost::replace_all(s, "/", "\\");
    }
#endif
    fs::create_directories(s);
    fclose(fopen(p, "wb"));
}

}

ScopedFile::ScopedFile(const path &p, const char *mode)
{
    f = primitives::filesystem::fopen(p, mode);
    if (!f)
        throw std::runtime_error("Cannot open file: " + p.string() + ", errno = " + std::to_string(errno));
}

ScopedFile::~ScopedFile()
{
    if (f)
        fclose(f);
}

size_t ScopedFile::read(void *buf, size_t sz)
{
    return fread(buf, 1, sz, f);
}
<|endoftext|>


<commit_before>/*
    OpenDeck MIDI platform firmware
    Copyright (C) 2015-2018 Igor Petrovic

    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 <http://www.gnu.org/licenses/>.
*/

#include "SysExConf.h"
#include "../interface/Interface.h"

void SysExConfig::createLayout()
{
    //last db block is ID block which isn't needed
    addBlocks(SYSEX_BLOCKS-1);

    sysExSection section;

    {
        //MIDI block

        //midi feature section
        section.numberOfParameters = MIDI_FEATURES;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_MIDI, section);

        //midi thru section
        section.numberOfParameters = MIDI_THRU_OPTIONS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_MIDI, section);
    }

    {
        //button block

        //type section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = BUTTON_TYPES-1;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi message type section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = BUTTON_MESSAGE_TYPES-1;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi id section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi velocity section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_BUTTONS, section);
    }

    {
        //encoder block

        //encoder enabled section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //encoder inverted section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //encoding mode section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = ENCODING_MODES-1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //midi id section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_ENCODERS, section);
    }

    {
        //analog block

        //analog enabled section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //analog inverted section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //analog type section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = ANALOG_TYPES-1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi id section, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi id section, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //lower cc limit, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //lower cc limit, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //upper cc limit, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //upper cc limit, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_ANALOG, section);
    }

    {
        //led block

        //hardware parameters section
        section.numberOfParameters = LED_HARDWARE_PARAMETERS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_LEDS, section);

        //activation note section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_LEDS, section);

        //rgb enabled section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //local led control enabled section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //single led velocity value section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 1;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_LEDS, section);

        //led color section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = LED_COLORS-1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //led blink section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_LEDS, section);
    }

    {
        //display block
        //features section
        section.numberOfParameters = DISPLAY_FEATURES;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_DISPLAY, section);

        //hw section
        section.numberOfParameters = DISPLAY_HW_PARAMETERS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_DISPLAY, section);
    }
}<commit_msg>fix last block not being added to sysex layout<commit_after>/*
    OpenDeck MIDI platform firmware
    Copyright (C) 2015-2018 Igor Petrovic

    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 <http://www.gnu.org/licenses/>.
*/

#include "SysExConf.h"
#include "../interface/Interface.h"

void SysExConfig::createLayout()
{
    //last db block is ID block which isn't needed
    addBlocks(SYSEX_BLOCKS);

    sysExSection section;

    {
        //MIDI block

        //midi feature section
        section.numberOfParameters = MIDI_FEATURES;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_MIDI, section);

        //midi thru section
        section.numberOfParameters = MIDI_THRU_OPTIONS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_MIDI, section);
    }

    {
        //button block

        //type section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = BUTTON_TYPES-1;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi message type section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = BUTTON_MESSAGE_TYPES-1;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi id section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi velocity section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_BUTTONS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_BUTTONS+MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_BUTTONS, section);
    }

    {
        //encoder block

        //encoder enabled section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //encoder inverted section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //encoding mode section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = ENCODING_MODES-1;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //midi id section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ENCODERS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_ENCODERS;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_ENCODERS, section);
    }

    {
        //analog block

        //analog enabled section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //analog inverted section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //analog type section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = ANALOG_TYPES-1;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi id section, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi id section, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //lower cc limit, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //lower cc limit, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //upper cc limit, lsb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //upper cc limit, msb
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_ANALOG, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_ANALOG;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_ANALOG, section);
    }

    {
        //led block

        //hardware parameters section
        section.numberOfParameters = LED_HARDWARE_PARAMETERS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_LEDS, section);

        //activation note section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_LEDS, section);

        //rgb enabled section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //local led control enabled section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //single led velocity value section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 1;
        section.newValueMax = 127;

        addSection(SYSEX_BLOCK_LEDS, section);

        //led color section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = LED_COLORS-1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //led blink section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 0;
        section.newValueMax = 1;

        addSection(SYSEX_BLOCK_LEDS, section);

        //midi channel section
        section.numberOfParameters = MAX_NUMBER_OF_LEDS;
        section.newValueMin = 1;
        section.newValueMax = 16;

        addSection(SYSEX_BLOCK_LEDS, section);
    }

    {
        //display block
        //features section
        section.numberOfParameters = DISPLAY_FEATURES;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_DISPLAY, section);

        //hw section
        section.numberOfParameters = DISPLAY_HW_PARAMETERS;
        section.newValueMin = 0;
        section.newValueMax = 0;

        addSection(SYSEX_BLOCK_DISPLAY, section);
    }
}<|endoftext|>


<commit_before>//===- Printer.cpp - Code for printing data structure graphs nicely -------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the 'dot' graph printer.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/DataStructure.h"
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Analysis/DSGraphTraits.h"
#include "llvm/Module.h"
#include "llvm/Constants.h"
#include "llvm/Assembly/Writer.h"
#include "Support/CommandLine.h"
#include "Support/GraphWriter.h"
#include "Support/Statistic.h"
#include <fstream>
#include <sstream>

namespace llvm {

// OnlyPrintMain - The DataStructure printer exposes this option to allow
// printing of only the graph for "main".
//
namespace {
  cl::opt<bool> OnlyPrintMain("only-print-main-ds", cl::ReallyHidden);
  Statistic<> MaxGraphSize   ("dsnode", "Maximum graph size");
  Statistic<> NumFoldedNodes ("dsnode", "Number of folded nodes (in final graph)");
}

void DSNode::dump() const { print(std::cerr, 0); }

static std::string getCaption(const DSNode *N, const DSGraph *G) {
  std::stringstream OS;
  Module *M = 0;
  // Get the module from ONE of the functions in the graph it is available.
  if (G && !G->getReturnNodes().empty())
    M = G->getReturnNodes().begin()->first->getParent();

  if (N->isNodeCompletelyFolded())
    OS << "COLLAPSED";
  else {
    WriteTypeSymbolic(OS, N->getType(), M);
    if (N->isArray())
      OS << " array";
  }
  if (unsigned NodeType = N->getNodeFlags()) {
    OS << ": ";
    if (NodeType & DSNode::AllocaNode ) OS << "S";
    if (NodeType & DSNode::HeapNode   ) OS << "H";
    if (NodeType & DSNode::GlobalNode ) OS << "G";
    if (NodeType & DSNode::UnknownNode) OS << "U";
    if (NodeType & DSNode::Incomplete ) OS << "I";
    if (NodeType & DSNode::Modified   ) OS << "M";
    if (NodeType & DSNode::Read       ) OS << "R";
#ifndef NDEBUG
    if (NodeType & DSNode::DEAD       ) OS << "<dead>";
#endif
    OS << "\n";
  }

  for (unsigned i = 0, e = N->getGlobals().size(); i != e; ++i) {
    WriteAsOperand(OS, N->getGlobals()[i], false, true, M);
    OS << "\n";
  }

  return OS.str();
}

template<>
struct DOTGraphTraits<const DSGraph*> : public DefaultDOTGraphTraits {
  static std::string getGraphName(const DSGraph *G) {
    switch (G->getReturnNodes().size()) {
    case 0: return G->getFunctionNames();
    case 1: return "Function " + G->getFunctionNames();
    default: return "Functions: " + G->getFunctionNames();
    }
  }

  static const char *getGraphProperties(const DSGraph *G) {
    return "\tsize=\"10,7.5\";\n"
           "\trotate=\"90\";\n";
  }

  static std::string getNodeLabel(const DSNode *Node, const DSGraph *Graph) {
    return getCaption(Node, Graph);
  }

  static std::string getNodeAttributes(const DSNode *N) {
    return "shape=Mrecord";//fontname=Courier";
  }

  /// addCustomGraphFeatures - Use this graph writing hook to emit call nodes
  /// and the return node.
  ///
  static void addCustomGraphFeatures(const DSGraph *G,
                                     GraphWriter<const DSGraph*> &GW) {
    Module *CurMod = 0;
    if (!G->getReturnNodes().empty())
      CurMod = G->getReturnNodes().begin()->first->getParent();

    // Add scalar nodes to the graph...
    const DSGraph::ScalarMapTy &VM = G->getScalarMap();
    for (DSGraph::ScalarMapTy::const_iterator I = VM.begin(); I != VM.end();++I)
      if (!isa<GlobalValue>(I->first) && !isa<ConstantPointerRef>(I->first)) {
        std::stringstream OS;
        WriteAsOperand(OS, I->first, false, true, CurMod);
        GW.emitSimpleNode(I->first, "", OS.str());

        // Add edge from return node to real destination
        int EdgeDest = I->second.getOffset() >> DS::PointerShift;
        if (EdgeDest == 0) EdgeDest = -1;
        GW.emitEdge(I->first, -1, I->second.getNode(),
                    EdgeDest, "arrowtail=tee,color=gray63");
      }

    // Output the returned value pointer...
    const DSGraph::ReturnNodesTy &RetNodes = G->getReturnNodes();
    for (DSGraph::ReturnNodesTy::const_iterator I = RetNodes.begin(),
           E = RetNodes.end(); I != E; ++I)
      if (I->second.getNode()) {
        std::string Label;
        if (RetNodes.size() == 1)
          Label = "returning";
        else
          Label = I->first->getName() + " ret node";
        // Output the return node...
        GW.emitSimpleNode((void*)1, "plaintext=circle", Label);

        // Add edge from return node to real destination
        int RetEdgeDest = I->second.getOffset() >> DS::PointerShift;;
        if (RetEdgeDest == 0) RetEdgeDest = -1;
        GW.emitEdge((void*)1, -1, I->second.getNode(),
                    RetEdgeDest, "arrowtail=tee,color=gray63");
      }

    // Output all of the call nodes...
    const std::vector<DSCallSite> &FCs =
      G->shouldPrintAuxCalls() ? G->getAuxFunctionCalls()
      : G->getFunctionCalls();
    for (unsigned i = 0, e = FCs.size(); i != e; ++i) {
      const DSCallSite &Call = FCs[i];
      std::vector<std::string> EdgeSourceCaptions(Call.getNumPtrArgs()+2);
      EdgeSourceCaptions[0] = "r";
      if (Call.isDirectCall())
        EdgeSourceCaptions[1] = Call.getCalleeFunc()->getName();
      else
        EdgeSourceCaptions[1] = "f";

      GW.emitSimpleNode(&Call, "shape=record", "call", Call.getNumPtrArgs()+2,
                        &EdgeSourceCaptions);

      if (DSNode *N = Call.getRetVal().getNode()) {
        int EdgeDest = Call.getRetVal().getOffset() >> DS::PointerShift;
        if (EdgeDest == 0) EdgeDest = -1;
        GW.emitEdge(&Call, 0, N, EdgeDest, "color=gray63,tailclip=false");
      }

      // Print out the callee...
      if (Call.isIndirectCall()) {
        DSNode *N = Call.getCalleeNode();
        assert(N && "Null call site callee node!");
        GW.emitEdge(&Call, 1, N, -1, "color=gray63,tailclip=false");
      }

      for (unsigned j = 0, e = Call.getNumPtrArgs(); j != e; ++j)
        if (DSNode *N = Call.getPtrArg(j).getNode()) {
          int EdgeDest = Call.getPtrArg(j).getOffset() >> DS::PointerShift;
          if (EdgeDest == 0) EdgeDest = -1;
          GW.emitEdge(&Call, j+2, N, EdgeDest, "color=gray63,tailclip=false");
        }
    }
  }
};

void DSNode::print(std::ostream &O, const DSGraph *G) const {
  GraphWriter<const DSGraph *> W(O, G);
  W.writeNode(this);
}

void DSGraph::print(std::ostream &O) const {
  WriteGraph(O, this, "DataStructures");
}

void DSGraph::writeGraphToFile(std::ostream &O,
                               const std::string &GraphName) const {
  std::string Filename = GraphName + ".dot";
  O << "Writing '" << Filename << "'...";
  std::ofstream F(Filename.c_str());

  if (F.good()) {
    print(F);
    unsigned NumCalls = shouldPrintAuxCalls() ?
      getAuxFunctionCalls().size() : getFunctionCalls().size();
    O << " [" << getGraphSize() << "+" << NumCalls << "]\n";
  } else {
    O << "  error opening file for writing!\n";
  }
}

/// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
/// then cleanup.  For use from the debugger.
///
void DSGraph::viewGraph() const {
  std::ofstream F("/tmp/tempgraph.dot");
  if (!F.good()) {
    std::cerr << "Error opening '/tmp/tempgraph.dot' for temporary graph!\n";
    return;
  }
  print(F);
  F.close();
  if (system("dot -Tps /tmp/tempgraph.dot > /tmp/tempgraph.ps"))
    std::cerr << "Error running dot: 'dot' not in path?\n";
  system("gv /tmp/tempgraph.ps");
  system("rm /tmp/tempgraph.dot /tmp/tempgraph.ps");
}

template <typename Collection>
static void printCollection(const Collection &C, std::ostream &O,
                            const Module *M, const std::string &Prefix) {
  if (M == 0) {
    O << "Null Module pointer, cannot continue!\n";
    return;
  }

  unsigned TotalNumNodes = 0, TotalCallNodes = 0;
  for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I)
    if (C.hasGraph(*I)) {
      DSGraph &Gr = C.getDSGraph((Function&)*I);
      TotalNumNodes += Gr.getGraphSize();
      unsigned NumCalls = Gr.shouldPrintAuxCalls() ?
        Gr.getAuxFunctionCalls().size() : Gr.getFunctionCalls().size();

      TotalCallNodes += NumCalls;
      if (I->getName() == "main" || !OnlyPrintMain)
        Gr.writeGraphToFile(O, Prefix+I->getName());
      else {
        O << "Skipped Writing '" << Prefix+I->getName() << ".dot'... ["
          << Gr.getGraphSize() << "+" << NumCalls << "]\n";
      }

      if (MaxGraphSize < Gr.getNodes().size())
        MaxGraphSize = Gr.getNodes().size();
      for (unsigned i = 0, e = Gr.getNodes().size(); i != e; ++i)
        if (Gr.getNodes()[i]->isNodeCompletelyFolded())
          ++NumFoldedNodes;
    }

  DSGraph &GG = C.getGlobalsGraph();
  TotalNumNodes  += GG.getGraphSize();
  TotalCallNodes += GG.getFunctionCalls().size();
  if (!OnlyPrintMain) {
    GG.writeGraphToFile(O, Prefix+"GlobalsGraph");
  } else {
    O << "Skipped Writing '" << Prefix << "GlobalsGraph.dot'... ["
      << GG.getGraphSize() << "+" << GG.getFunctionCalls().size() << "]\n";
  }

  O << "\nGraphs contain [" << TotalNumNodes << "+" << TotalCallNodes
    << "] nodes total" << std::endl;
}

// print - Print out the analysis results...
void LocalDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "ds.");
}

void BUDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "bu.");
}

void TDDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "td.");
}

} // End llvm namespace
<commit_msg>Print return nodes for graphs with multiple functions in them correctly<commit_after>//===- Printer.cpp - Code for printing data structure graphs nicely -------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the 'dot' graph printer.
//
//===----------------------------------------------------------------------===//

#include "llvm/Analysis/DataStructure.h"
#include "llvm/Analysis/DSGraph.h"
#include "llvm/Analysis/DSGraphTraits.h"
#include "llvm/Module.h"
#include "llvm/Constants.h"
#include "llvm/Assembly/Writer.h"
#include "Support/CommandLine.h"
#include "Support/GraphWriter.h"
#include "Support/Statistic.h"
#include <fstream>
#include <sstream>

namespace llvm {

// OnlyPrintMain - The DataStructure printer exposes this option to allow
// printing of only the graph for "main".
//
namespace {
  cl::opt<bool> OnlyPrintMain("only-print-main-ds", cl::ReallyHidden);
  Statistic<> MaxGraphSize   ("dsnode", "Maximum graph size");
  Statistic<> NumFoldedNodes ("dsnode", "Number of folded nodes (in final graph)");
}

void DSNode::dump() const { print(std::cerr, 0); }

static std::string getCaption(const DSNode *N, const DSGraph *G) {
  std::stringstream OS;
  Module *M = 0;
  // Get the module from ONE of the functions in the graph it is available.
  if (G && !G->getReturnNodes().empty())
    M = G->getReturnNodes().begin()->first->getParent();

  if (N->isNodeCompletelyFolded())
    OS << "COLLAPSED";
  else {
    WriteTypeSymbolic(OS, N->getType(), M);
    if (N->isArray())
      OS << " array";
  }
  if (unsigned NodeType = N->getNodeFlags()) {
    OS << ": ";
    if (NodeType & DSNode::AllocaNode ) OS << "S";
    if (NodeType & DSNode::HeapNode   ) OS << "H";
    if (NodeType & DSNode::GlobalNode ) OS << "G";
    if (NodeType & DSNode::UnknownNode) OS << "U";
    if (NodeType & DSNode::Incomplete ) OS << "I";
    if (NodeType & DSNode::Modified   ) OS << "M";
    if (NodeType & DSNode::Read       ) OS << "R";
#ifndef NDEBUG
    if (NodeType & DSNode::DEAD       ) OS << "<dead>";
#endif
    OS << "\n";
  }

  for (unsigned i = 0, e = N->getGlobals().size(); i != e; ++i) {
    WriteAsOperand(OS, N->getGlobals()[i], false, true, M);
    OS << "\n";
  }

  return OS.str();
}

template<>
struct DOTGraphTraits<const DSGraph*> : public DefaultDOTGraphTraits {
  static std::string getGraphName(const DSGraph *G) {
    switch (G->getReturnNodes().size()) {
    case 0: return G->getFunctionNames();
    case 1: return "Function " + G->getFunctionNames();
    default: return "Functions: " + G->getFunctionNames();
    }
  }

  static const char *getGraphProperties(const DSGraph *G) {
    return "\tsize=\"10,7.5\";\n"
           "\trotate=\"90\";\n";
  }

  static std::string getNodeLabel(const DSNode *Node, const DSGraph *Graph) {
    return getCaption(Node, Graph);
  }

  static std::string getNodeAttributes(const DSNode *N) {
    return "shape=Mrecord";//fontname=Courier";
  }

  /// addCustomGraphFeatures - Use this graph writing hook to emit call nodes
  /// and the return node.
  ///
  static void addCustomGraphFeatures(const DSGraph *G,
                                     GraphWriter<const DSGraph*> &GW) {
    Module *CurMod = 0;
    if (!G->getReturnNodes().empty())
      CurMod = G->getReturnNodes().begin()->first->getParent();

    // Add scalar nodes to the graph...
    const DSGraph::ScalarMapTy &VM = G->getScalarMap();
    for (DSGraph::ScalarMapTy::const_iterator I = VM.begin(); I != VM.end();++I)
      if (!isa<GlobalValue>(I->first) && !isa<ConstantPointerRef>(I->first)) {
        std::stringstream OS;
        WriteAsOperand(OS, I->first, false, true, CurMod);
        GW.emitSimpleNode(I->first, "", OS.str());

        // Add edge from return node to real destination
        int EdgeDest = I->second.getOffset() >> DS::PointerShift;
        if (EdgeDest == 0) EdgeDest = -1;
        GW.emitEdge(I->first, -1, I->second.getNode(),
                    EdgeDest, "arrowtail=tee,color=gray63");
      }

    // Output the returned value pointer...
    const DSGraph::ReturnNodesTy &RetNodes = G->getReturnNodes();
    for (DSGraph::ReturnNodesTy::const_iterator I = RetNodes.begin(),
           E = RetNodes.end(); I != E; ++I)
      if (I->second.getNode()) {
        std::string Label;
        if (RetNodes.size() == 1)
          Label = "returning";
        else
          Label = I->first->getName() + " ret node";
        // Output the return node...
        GW.emitSimpleNode((void*)I->first, "plaintext=circle", Label);

        // Add edge from return node to real destination
        int RetEdgeDest = I->second.getOffset() >> DS::PointerShift;;
        if (RetEdgeDest == 0) RetEdgeDest = -1;
        GW.emitEdge((void*)I->first, -1, I->second.getNode(),
                    RetEdgeDest, "arrowtail=tee,color=gray63");
      }

    // Output all of the call nodes...
    const std::vector<DSCallSite> &FCs =
      G->shouldPrintAuxCalls() ? G->getAuxFunctionCalls()
      : G->getFunctionCalls();
    for (unsigned i = 0, e = FCs.size(); i != e; ++i) {
      const DSCallSite &Call = FCs[i];
      std::vector<std::string> EdgeSourceCaptions(Call.getNumPtrArgs()+2);
      EdgeSourceCaptions[0] = "r";
      if (Call.isDirectCall())
        EdgeSourceCaptions[1] = Call.getCalleeFunc()->getName();
      else
        EdgeSourceCaptions[1] = "f";

      GW.emitSimpleNode(&Call, "shape=record", "call", Call.getNumPtrArgs()+2,
                        &EdgeSourceCaptions);

      if (DSNode *N = Call.getRetVal().getNode()) {
        int EdgeDest = Call.getRetVal().getOffset() >> DS::PointerShift;
        if (EdgeDest == 0) EdgeDest = -1;
        GW.emitEdge(&Call, 0, N, EdgeDest, "color=gray63,tailclip=false");
      }

      // Print out the callee...
      if (Call.isIndirectCall()) {
        DSNode *N = Call.getCalleeNode();
        assert(N && "Null call site callee node!");
        GW.emitEdge(&Call, 1, N, -1, "color=gray63,tailclip=false");
      }

      for (unsigned j = 0, e = Call.getNumPtrArgs(); j != e; ++j)
        if (DSNode *N = Call.getPtrArg(j).getNode()) {
          int EdgeDest = Call.getPtrArg(j).getOffset() >> DS::PointerShift;
          if (EdgeDest == 0) EdgeDest = -1;
          GW.emitEdge(&Call, j+2, N, EdgeDest, "color=gray63,tailclip=false");
        }
    }
  }
};

void DSNode::print(std::ostream &O, const DSGraph *G) const {
  GraphWriter<const DSGraph *> W(O, G);
  W.writeNode(this);
}

void DSGraph::print(std::ostream &O) const {
  WriteGraph(O, this, "DataStructures");
}

void DSGraph::writeGraphToFile(std::ostream &O,
                               const std::string &GraphName) const {
  std::string Filename = GraphName + ".dot";
  O << "Writing '" << Filename << "'...";
  std::ofstream F(Filename.c_str());

  if (F.good()) {
    print(F);
    unsigned NumCalls = shouldPrintAuxCalls() ?
      getAuxFunctionCalls().size() : getFunctionCalls().size();
    O << " [" << getGraphSize() << "+" << NumCalls << "]\n";
  } else {
    O << "  error opening file for writing!\n";
  }
}

/// viewGraph - Emit a dot graph, run 'dot', run gv on the postscript file,
/// then cleanup.  For use from the debugger.
///
void DSGraph::viewGraph() const {
  std::ofstream F("/tmp/tempgraph.dot");
  if (!F.good()) {
    std::cerr << "Error opening '/tmp/tempgraph.dot' for temporary graph!\n";
    return;
  }
  print(F);
  F.close();
  if (system("dot -Tps /tmp/tempgraph.dot > /tmp/tempgraph.ps"))
    std::cerr << "Error running dot: 'dot' not in path?\n";
  system("gv /tmp/tempgraph.ps");
  system("rm /tmp/tempgraph.dot /tmp/tempgraph.ps");
}

template <typename Collection>
static void printCollection(const Collection &C, std::ostream &O,
                            const Module *M, const std::string &Prefix) {
  if (M == 0) {
    O << "Null Module pointer, cannot continue!\n";
    return;
  }

  unsigned TotalNumNodes = 0, TotalCallNodes = 0;
  for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I)
    if (C.hasGraph(*I)) {
      DSGraph &Gr = C.getDSGraph((Function&)*I);
      TotalNumNodes += Gr.getGraphSize();
      unsigned NumCalls = Gr.shouldPrintAuxCalls() ?
        Gr.getAuxFunctionCalls().size() : Gr.getFunctionCalls().size();

      TotalCallNodes += NumCalls;
      if (I->getName() == "main" || !OnlyPrintMain)
        Gr.writeGraphToFile(O, Prefix+I->getName());
      else {
        O << "Skipped Writing '" << Prefix+I->getName() << ".dot'... ["
          << Gr.getGraphSize() << "+" << NumCalls << "]\n";
      }

      if (MaxGraphSize < Gr.getNodes().size())
        MaxGraphSize = Gr.getNodes().size();
      for (unsigned i = 0, e = Gr.getNodes().size(); i != e; ++i)
        if (Gr.getNodes()[i]->isNodeCompletelyFolded())
          ++NumFoldedNodes;
    }

  DSGraph &GG = C.getGlobalsGraph();
  TotalNumNodes  += GG.getGraphSize();
  TotalCallNodes += GG.getFunctionCalls().size();
  if (!OnlyPrintMain) {
    GG.writeGraphToFile(O, Prefix+"GlobalsGraph");
  } else {
    O << "Skipped Writing '" << Prefix << "GlobalsGraph.dot'... ["
      << GG.getGraphSize() << "+" << GG.getFunctionCalls().size() << "]\n";
  }

  O << "\nGraphs contain [" << TotalNumNodes << "+" << TotalCallNodes
    << "] nodes total" << std::endl;
}

// print - Print out the analysis results...
void LocalDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "ds.");
}

void BUDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "bu.");
}

void TDDataStructures::print(std::ostream &O, const Module *M) const {
  printCollection(*this, O, M, "td.");
}

} // End llvm namespace
<|endoftext|>


<commit_before>/* AlmaifCompile.cc - compiler support for custom devices

   Copyright (c) 2022 Topi Leppänen / Tampere University

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to
   deal in the Software without restriction, including without limitation the
   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   sell copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE.
*/

#include "pocl_cache.h"
#include "pocl_file_util.h"

#include "AlmaifShared.hh"
#include "almaif.h"
#include "AlmaifCompile.hh"

#include "common_driver.h"

// TODO TCE SPECIFIC
#if defined(TCEMC_AVAILABLE) || defined(TCE_AVAILABLE)
#define ENABLE_COMPILER
#endif

#ifdef ENABLE_COMPILER
#include "AlmaifCompileTCE.hh"
#endif

extern int pocl_offline_compile;

int pocl_almaif_compile_init(unsigned j, cl_device_id dev, const char *parameters) {
  AlmaifData *d = (AlmaifData *)dev->data;

  d->compilationData = (compilation_data_t *)pocl_aligned_malloc(
      HOST_CPU_CACHELINE_SIZE, sizeof(compilation_data_t));

  if (d->compilationData == NULL)
    return CL_OUT_OF_HOST_MEMORY;

  if (!pocl_offline_compile) {
    d->compilationData->pocl_context =
        pocl_alloc_buffer(d->Dev->AllocRegions, sizeof(pocl_context32));
    assert(d->compilationData->pocl_context &&
           "Failed to allocate pocl context on device\n");
  }

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

  /* setup device info */
  dev->image_support = CL_FALSE;
  dev->half_fp_config = CL_FP_ROUND_TO_ZERO;
  dev->single_fp_config = CL_FP_ROUND_TO_NEAREST | CL_FP_INF_NAN;
  dev->double_fp_config = 0;
  dev->llvm_target_triplet = NULL;
  dev->llvm_cpu = NULL;
  dev->has_64bit_long = 0;
  dev->spmd = CL_FALSE;
  dev->workgroup_pass = CL_TRUE;

  dev->arg_buffer_launcher = CL_TRUE;
  dev->autolocals_to_args = POCL_AUTOLOCALS_TO_ARGS_ALWAYS;

  dev->global_as_id = TTA_ASID_GLOBAL;
  dev->local_as_id = TTA_ASID_LOCAL;
  dev->constant_as_id = TTA_ASID_CONSTANT;
  dev->args_as_id = TTA_ASID_GLOBAL;
  dev->context_as_id = TTA_ASID_GLOBAL;

  dev->final_linkage_flags = NULL;

  d->compilationData->current_kernel = NULL;
  SETUP_DEVICE_CL_VERSION(1, 2);

  // dev->available = CL_TRUE;
  dev->available = pocl_offline_compile ? CL_FALSE : CL_TRUE;

  dev->compiler_available = true;
  dev->linker_available = true;

  compilation_data_t *adi = (compilation_data_t *)d->compilationData;

#ifdef ENABLE_COMPILER
  // TODO tce specific
  adi->initialize_device = pocl_almaif_tce_initialize;
  adi->cleanup_device = pocl_almaif_tce_cleanup;
  adi->compile_kernel = pocl_almaif_tce_compile;

  // backend specific init
  POCL_MSG_PRINT_ALMAIF("Starting device specific initializion\n");
  adi->initialize_device(dev, parameters);

  POCL_MSG_PRINT_ALMAIF("Device specific initializion done\n");

  SHA1_digest_t digest;
  pocl_almaif_tce_device_hash(parameters, dev->llvm_target_triplet,
                              (char *)digest);
  POCL_MSG_PRINT_ALMAIF("ALMAIF TCE DEVICE HASH=%s", (char *)digest);
  adi->build_hash = strdup((char *)digest);

#else
  char option_str[256];
  snprintf(option_str, 256, "POCL_ALMAIF%u_HASH", j);
  if (pocl_is_option_set(option_str)) {
    adi->build_hash = (char *)pocl_get_string_option(option_str, NULL);
    assert(adi->build_hash);
  } else
    adi->build_hash = strdup(DEFAULT_BUILD_HASH);
#endif

  dev->ops->build_hash = pocl_almaif_compile_build_hash;
  dev->ops->build_source = pocl_driver_build_source;
  dev->ops->setup_metadata = pocl_driver_setup_metadata;
  dev->ops->create_kernel = pocl_almaif_create_kernel;
  dev->ops->free_kernel = pocl_almaif_free_kernel;
  dev->ops->build_poclbinary = pocl_driver_build_poclbinary;
  dev->ops->build_binary = pocl_almaif_build_binary;
#ifdef ENABLE_COMPILER
  dev->ops->compile_kernel = pocl_almaif_tce_compile;
  dev->ops->init_build = pocl_tce_init_build;
#endif
  return CL_SUCCESS;
}

cl_int pocl_almaif_compile_uninit(unsigned j, cl_device_id dev) {
  AlmaifData *d = (AlmaifData *)dev->data;

#ifdef ENABLE_COMPILER
  d->compilationData->cleanup_device(dev);
#endif

  pocl_free_chunk(d->compilationData->pocl_context);
  pocl_aligned_free(d->compilationData);

  return CL_SUCCESS;
}

void pocl_almaif_compile_kernel(_cl_command_node *cmd, cl_kernel kernel,
                                cl_device_id device, int specialize) {
  if (cmd->type != CL_COMMAND_NDRANGE_KERNEL) {
    return;
  }

  if (kernel == NULL)
    kernel = cmd->command.run.kernel;
  cl_program program = kernel->program;
  cl_device_id dev = (device ? device : cmd->device);
  AlmaifData *d = (AlmaifData *)dev->data;
  unsigned dev_i = cmd->program_device_i;

  POCL_MSG_PRINT_ALMAIF("Current kernel %p, new kernel %p\n",
                       (void*)d->compilationData->current_kernel, (void*)kernel);

  /* if (d->compilationData->current_kernel == kernel) {
     POCL_MSG_PRINT_ALMAIF(
         "kernel %s is the currently loaded kernel, nothing to do\n",
         kernel->name);
     return;
   }*/

#ifdef ENABLE_COMPILER
  if (!program->pocl_binaries[dev_i]) {
    POCL_MSG_PRINT_ALMAIF("Compiling kernel %s to poclbinary\n", kernel->name);

    d->compilationData->compile_kernel(cmd, kernel, device, specialize);
  }
#endif

  if (pocl_offline_compile) {
    return;
  }
  almaif_kernel_data_t *kd =
      (almaif_kernel_data_t *)kernel->data[cmd->program_device_i];

  POCL_MSG_PRINT_ALMAIF("Loading program to device\n");
  d->Dev->loadProgramToDevice(kd, kernel, cmd);

  POCL_MSG_PRINT_ALMAIF("Loaded program to device\n");
  d->compilationData->current_kernel = kernel;
}

int pocl_almaif_create_kernel(cl_device_id device, cl_program program,
                              cl_kernel kernel, unsigned device_i) {
  assert(kernel->data != NULL);
  assert(kernel->data[device_i] == NULL);

  kernel->data[device_i] = (void *)calloc(1, sizeof(almaif_kernel_data_t));

  return CL_SUCCESS;
}

int pocl_almaif_free_kernel(cl_device_id device, cl_program program,
                            cl_kernel kernel, unsigned device_i) {
  assert(kernel->data != NULL);
  // may happen if creating kernel fails
  if (kernel->data[device_i] == NULL)
    return CL_SUCCESS;

  almaif_kernel_data_t *p = (almaif_kernel_data_t *)kernel->data[device_i];
  POCL_MEM_FREE(p->dmem_img);
  POCL_MEM_FREE(p->imem_img);
  POCL_MEM_FREE(p->pmem_img);
  POCL_MEM_FREE(p);
  kernel->data[device_i] = NULL;

  return CL_SUCCESS;
}

int pocl_almaif_build_binary(cl_program program, cl_uint device_i,
                             int link_program, int spir_build) {
  assert(program->pocl_binaries[device_i] != NULL);
  assert(program->pocl_binary_sizes[device_i] > 0);
  assert(link_program != 0);
  assert(spir_build == 0);
  return CL_SUCCESS;
}

char *pocl_almaif_compile_build_hash(cl_device_id device) {
  AlmaifData *d = (AlmaifData *)device->data;
  assert(d->compilationData->build_hash);
  return d->compilationData->build_hash;
}
<commit_msg>Almaif: copy the build hash in the build_hash callback<commit_after>/* AlmaifCompile.cc - compiler support for custom devices

   Copyright (c) 2022 Topi Leppänen / Tampere University

   Permission is hereby granted, free of charge, to any person obtaining a copy
   of this software and associated documentation files (the "Software"), to
   deal in the Software without restriction, including without limitation the
   rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   sell copies of the Software, and to permit persons to whom the Software is
   furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
   IN THE SOFTWARE.
*/

#include "pocl_cache.h"
#include "pocl_file_util.h"

#include "AlmaifShared.hh"
#include "almaif.h"
#include "AlmaifCompile.hh"

#include "common_driver.h"

// TODO TCE SPECIFIC
#if defined(TCEMC_AVAILABLE) || defined(TCE_AVAILABLE)
#define ENABLE_COMPILER
#endif

#ifdef ENABLE_COMPILER
#include "AlmaifCompileTCE.hh"
#endif

extern int pocl_offline_compile;

int pocl_almaif_compile_init(unsigned j, cl_device_id dev, const char *parameters) {
  AlmaifData *d = (AlmaifData *)dev->data;

  d->compilationData = (compilation_data_t *)pocl_aligned_malloc(
      HOST_CPU_CACHELINE_SIZE, sizeof(compilation_data_t));

  if (d->compilationData == NULL)
    return CL_OUT_OF_HOST_MEMORY;

  if (!pocl_offline_compile) {
    d->compilationData->pocl_context =
        pocl_alloc_buffer(d->Dev->AllocRegions, sizeof(pocl_context32));
    assert(d->compilationData->pocl_context &&
           "Failed to allocate pocl context on device\n");
  }

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

  /* setup device info */
  dev->image_support = CL_FALSE;
  dev->half_fp_config = CL_FP_ROUND_TO_ZERO;
  dev->single_fp_config = CL_FP_ROUND_TO_NEAREST | CL_FP_INF_NAN;
  dev->double_fp_config = 0;
  dev->llvm_target_triplet = NULL;
  dev->llvm_cpu = NULL;
  dev->has_64bit_long = 0;
  dev->spmd = CL_FALSE;
  dev->workgroup_pass = CL_TRUE;

  dev->arg_buffer_launcher = CL_TRUE;
  dev->autolocals_to_args = POCL_AUTOLOCALS_TO_ARGS_ALWAYS;

  dev->global_as_id = TTA_ASID_GLOBAL;
  dev->local_as_id = TTA_ASID_LOCAL;
  dev->constant_as_id = TTA_ASID_CONSTANT;
  dev->args_as_id = TTA_ASID_GLOBAL;
  dev->context_as_id = TTA_ASID_GLOBAL;

  dev->final_linkage_flags = NULL;

  d->compilationData->current_kernel = NULL;
  SETUP_DEVICE_CL_VERSION(1, 2);

  // dev->available = CL_TRUE;
  dev->available = pocl_offline_compile ? CL_FALSE : CL_TRUE;

  dev->compiler_available = true;
  dev->linker_available = true;

  compilation_data_t *adi = (compilation_data_t *)d->compilationData;

#ifdef ENABLE_COMPILER
  // TODO tce specific
  adi->initialize_device = pocl_almaif_tce_initialize;
  adi->cleanup_device = pocl_almaif_tce_cleanup;
  adi->compile_kernel = pocl_almaif_tce_compile;

  // backend specific init
  POCL_MSG_PRINT_ALMAIF("Starting device specific initializion\n");
  adi->initialize_device(dev, parameters);

  POCL_MSG_PRINT_ALMAIF("Device specific initializion done\n");

  SHA1_digest_t digest;
  pocl_almaif_tce_device_hash(parameters, dev->llvm_target_triplet,
                              (char *)digest);
  POCL_MSG_PRINT_ALMAIF("ALMAIF TCE DEVICE HASH=%s", (char *)digest);
  adi->build_hash = strdup((char *)digest);

#else
  char option_str[256];
  snprintf(option_str, 256, "POCL_ALMAIF%u_HASH", j);
  if (pocl_is_option_set(option_str)) {
    adi->build_hash = (char *)pocl_get_string_option(option_str, NULL);
    assert(adi->build_hash);
  } else
    adi->build_hash = strdup(DEFAULT_BUILD_HASH);
#endif

  dev->ops->build_hash = pocl_almaif_compile_build_hash;
  dev->ops->build_source = pocl_driver_build_source;
  dev->ops->setup_metadata = pocl_driver_setup_metadata;
  dev->ops->create_kernel = pocl_almaif_create_kernel;
  dev->ops->free_kernel = pocl_almaif_free_kernel;
  dev->ops->build_poclbinary = pocl_driver_build_poclbinary;
  dev->ops->build_binary = pocl_almaif_build_binary;
#ifdef ENABLE_COMPILER
  dev->ops->compile_kernel = pocl_almaif_tce_compile;
  dev->ops->init_build = pocl_tce_init_build;
#endif
  return CL_SUCCESS;
}

cl_int pocl_almaif_compile_uninit(unsigned j, cl_device_id dev) {
  AlmaifData *d = (AlmaifData *)dev->data;

#ifdef ENABLE_COMPILER
  d->compilationData->cleanup_device(dev);
#endif

  pocl_free_chunk(d->compilationData->pocl_context);
  pocl_aligned_free(d->compilationData);

  return CL_SUCCESS;
}

void pocl_almaif_compile_kernel(_cl_command_node *cmd, cl_kernel kernel,
                                cl_device_id device, int specialize) {
  if (cmd->type != CL_COMMAND_NDRANGE_KERNEL) {
    return;
  }

  if (kernel == NULL)
    kernel = cmd->command.run.kernel;
  cl_program program = kernel->program;
  cl_device_id dev = (device ? device : cmd->device);
  AlmaifData *d = (AlmaifData *)dev->data;
  unsigned dev_i = cmd->program_device_i;

  POCL_MSG_PRINT_ALMAIF("Current kernel %p, new kernel %p\n",
                       (void*)d->compilationData->current_kernel, (void*)kernel);

  /* if (d->compilationData->current_kernel == kernel) {
     POCL_MSG_PRINT_ALMAIF(
         "kernel %s is the currently loaded kernel, nothing to do\n",
         kernel->name);
     return;
   }*/

#ifdef ENABLE_COMPILER
  if (!program->pocl_binaries[dev_i]) {
    POCL_MSG_PRINT_ALMAIF("Compiling kernel %s to poclbinary\n", kernel->name);

    d->compilationData->compile_kernel(cmd, kernel, device, specialize);
  }
#endif

  if (pocl_offline_compile) {
    return;
  }
  almaif_kernel_data_t *kd =
      (almaif_kernel_data_t *)kernel->data[cmd->program_device_i];

  POCL_MSG_PRINT_ALMAIF("Loading program to device\n");
  d->Dev->loadProgramToDevice(kd, kernel, cmd);

  POCL_MSG_PRINT_ALMAIF("Loaded program to device\n");
  d->compilationData->current_kernel = kernel;
}

int pocl_almaif_create_kernel(cl_device_id device, cl_program program,
                              cl_kernel kernel, unsigned device_i) {
  assert(kernel->data != NULL);
  assert(kernel->data[device_i] == NULL);

  kernel->data[device_i] = (void *)calloc(1, sizeof(almaif_kernel_data_t));

  return CL_SUCCESS;
}

int pocl_almaif_free_kernel(cl_device_id device, cl_program program,
                            cl_kernel kernel, unsigned device_i) {
  assert(kernel->data != NULL);
  // may happen if creating kernel fails
  if (kernel->data[device_i] == NULL)
    return CL_SUCCESS;

  almaif_kernel_data_t *p = (almaif_kernel_data_t *)kernel->data[device_i];
  POCL_MEM_FREE(p->dmem_img);
  POCL_MEM_FREE(p->imem_img);
  POCL_MEM_FREE(p->pmem_img);
  POCL_MEM_FREE(p);
  kernel->data[device_i] = NULL;

  return CL_SUCCESS;
}

int pocl_almaif_build_binary(cl_program program, cl_uint device_i,
                             int link_program, int spir_build) {
  assert(program->pocl_binaries[device_i] != NULL);
  assert(program->pocl_binary_sizes[device_i] > 0);
  assert(link_program != 0);
  assert(spir_build == 0);
  return CL_SUCCESS;
}

char *pocl_almaif_compile_build_hash(cl_device_id device) {
  AlmaifData *d = (AlmaifData *)device->data;
  assert(d->compilationData->build_hash);
  return strdup(d->compilationData->build_hash);
}
<|endoftext|>