<commit_before>#include "LLVM_Headers.h"
#include "LLVM_Output.h"
#include "LLVM_Runtime_Linker.h"
#include "CodeGen_LLVM.h"
#include "CodeGen_C.h"
#include "CodeGen_Internal.h"

#include <iostream>
#include <fstream>

#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

namespace Halide {

std::unique_ptr<llvm::raw_fd_ostream> make_raw_fd_ostream(const std::string &filename) {
    std::string error_string;
    std::error_code err;
    std::unique_ptr<llvm::raw_fd_ostream> raw_out(new llvm::raw_fd_ostream(filename, err, llvm::sys::fs::F_None));
    if (err) error_string = err.message();
    internal_assert(error_string.empty())
        << "Error opening output " << filename << ": " << error_string << "\n";

    return raw_out;
}

void emit_file(llvm::Module &module, Internal::LLVMOStream& out, llvm::TargetMachine::CodeGenFileType file_type) {
    Internal::debug(1) << "emit_file.Compiling to native code...\n";
    Internal::debug(2) << "Target triple: " << module.getTargetTriple() << "\n";

    // Get the target specific parser.
    auto target_machine = Internal::make_target_machine(module);
    internal_assert(target_machine.get()) << "Could not allocate target machine!\n";

    #if LLVM_VERSION == 37
    llvm::DataLayout target_data_layout(*(target_machine->getDataLayout()));
    #else
    llvm::DataLayout target_data_layout(target_machine->createDataLayout());
    #endif
    if (!(target_data_layout == module.getDataLayout())) {
        internal_error << "Warning: module's data layout does not match target machine's\n"
                       << target_data_layout.getStringRepresentation() << "\n"
                       << module.getDataLayout().getStringRepresentation() << "\n";
    }

    // Build up all of the passes that we want to do to the module.
    llvm::legacy::PassManager pass_manager;

    pass_manager.add(new llvm::TargetLibraryInfoWrapperPass(llvm::Triple(module.getTargetTriple())));

    // Make sure things marked as always-inline get inlined
    #if LLVM_VERSION < 40
    pass_manager.add(llvm::createAlwaysInlinerPass());
    #else
    pass_manager.add(llvm::createAlwaysInlinerLegacyPass());
    #endif

    // Enable symbol rewriting. This allows code outside libHalide to
    // use symbol rewriting when compiling Halide code (for example, by
    // using cl::ParseCommandLineOption and then passing the appropriate
    // rewrite options via -mllvm flags).
    pass_manager.add(llvm::createRewriteSymbolsPass());

    // Override default to generate verbose assembly.
    target_machine->Options.MCOptions.AsmVerbose = true;

    // Ask the target to add backend passes as necessary.
    target_machine->addPassesToEmitFile(pass_manager, out, file_type);

    pass_manager.run(module);
}

std::unique_ptr<llvm::Module> compile_module_to_llvm_module(const Module &module, llvm::LLVMContext &context) {
    return codegen_llvm(module, context);
}

void compile_llvm_module_to_object(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_ObjectFile);
}

void compile_llvm_module_to_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_AssemblyFile);
}

void compile_llvm_module_to_llvm_bitcode(llvm::Module &module, Internal::LLVMOStream& out) {
    WriteBitcodeToFile(&module, out);
}

void compile_llvm_module_to_llvm_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    module.print(out, nullptr);
}

// Note that the utilities for get/set working directory are deliberately *not* in Util.h;
// generally speaking, you shouldn't ever need or want to do this, and doing so is asking for
// trouble. This exists solely to work around an issue with LLVM, hence its restricted
// location. If we ever legitimately need this elsewhere, consider moving it to Util.h.
namespace {

std::string get_current_directory() {
#ifdef _WIN32
    std::string dir;
    char p[MAX_PATH];
    DWORD ret = GetCurrentDirectoryA(MAX_PATH, p);
    internal_assert(ret == 0) << "GetCurrentDirectoryA() failed";
    dir = p;
    return dir;
#else
    std::string dir;
    char *p = getcwd(nullptr, 0);
    internal_assert(p != NULL) << "getcwd() failed";
    dir = p;
    free(p);
    return dir;
#endif
}

void set_current_directory(const std::string &d) {
#ifdef _WIN32
    internal_assert(SetCurrentDirectoryA(d.c_str())) << "SetCurrentDirectoryA() failed";
#else
    internal_assert(chdir(d.c_str()) == 0) << "chdir() failed";
#endif
}

std::pair<std::string, std::string> dir_and_file(const std::string &path) {
    std::string dir, file;
    size_t slash_pos = path.rfind('/');
#ifdef _WIN32
    if (slash_pos == std::string::npos) {
        // Windows is a thing
        slash_pos = path.rfind('\\');
    }
#endif
    if (slash_pos != std::string::npos) {
        dir = path.substr(0, slash_pos);
        file = path.substr(slash_pos + 1);
    } else {
        file = path;
    }
    return { dir, file };
}

struct SetCwd {
    const std::string original_directory;
    explicit SetCwd(const std::string &d) : original_directory(get_current_directory()) {
        if (!d.empty()) {
            set_current_directory(d);
        }
    }
    ~SetCwd() {
        set_current_directory(original_directory);
    }
};

}

void create_static_library(const std::vector<std::string> &src_files_in, const Target &target,
                           const std::string &dst_file, bool deterministic) {
    internal_assert(!src_files_in.empty());

    // If we give absolute paths to LLVM, it will dutifully embed them in the resulting
    // .a file; some versions of 'ar x' are unable to deal with the resulting files,
    // which is inconvenient. So let's doctor the inputs to be simple filenames,
    // and temporarily change the working directory. (Note that this requires all the
    // input files be in the same directory; this is currently always the case for
    // our existing usage.)
    std::string src_dir = dir_and_file(src_files_in.front()).first;
    std::vector<std::string> src_files;
    for (auto &s_in : src_files_in) {
        auto df = dir_and_file(s_in);
        internal_assert(df.first == src_dir) << "All inputs to create_static_library() must be in the same directory";
        for (auto &s_existing : src_files) {
            internal_assert(s_existing != df.second) << "create_static_library() does not allow duplicate filenames.";
        }
        src_files.push_back(df.second);
    }

    SetCwd set_cwd(src_dir);

#if LLVM_VERSION >= 39
    std::vector<llvm::NewArchiveMember> new_members;
    for (auto &src : src_files) {
        llvm::Expected<llvm::NewArchiveMember> new_member =
            llvm::NewArchiveMember::getFile(src, /*Deterministic=*/true);
        if (!new_member) {
            // Don't use internal_assert: the call to new_member.takeError() will be evaluated
            // even if the assert does not fail, leaving new_member in an indeterminate
            // state.
            internal_error << src << ": " << llvm::toString(new_member.takeError()) << "\n";
        }
        new_members.push_back(std::move(*new_member));
    }
#elif LLVM_VERSION == 38
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src));
    }
#else
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src, src));
    }
#endif

    const bool write_symtab = true;
    const auto kind = Internal::get_triple_for_target(target).isOSDarwin()
        ? llvm::object::Archive::K_BSD
        : llvm::object::Archive::K_GNU;
#if LLVM_VERSION == 37
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic);
#elif LLVM_VERSION == 38
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin);
#else
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin, nullptr);
#endif
    internal_assert(!result.second) << "Failed to write archive: " << dst_file
        << ", reason: " << result.second << "\n";
}

}  // namespace Halide
<commit_msg>Ensure that the dst_file for create_static_library() is an absolute path<commit_after>#include "LLVM_Headers.h"
#include "LLVM_Output.h"
#include "LLVM_Runtime_Linker.h"
#include "CodeGen_LLVM.h"
#include "CodeGen_C.h"
#include "CodeGen_Internal.h"

#include <iostream>
#include <fstream>

#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <windows.h>
#else
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#endif

namespace Halide {

std::unique_ptr<llvm::raw_fd_ostream> make_raw_fd_ostream(const std::string &filename) {
    std::string error_string;
    std::error_code err;
    std::unique_ptr<llvm::raw_fd_ostream> raw_out(new llvm::raw_fd_ostream(filename, err, llvm::sys::fs::F_None));
    if (err) error_string = err.message();
    internal_assert(error_string.empty())
        << "Error opening output " << filename << ": " << error_string << "\n";

    return raw_out;
}

void emit_file(llvm::Module &module, Internal::LLVMOStream& out, llvm::TargetMachine::CodeGenFileType file_type) {
    Internal::debug(1) << "emit_file.Compiling to native code...\n";
    Internal::debug(2) << "Target triple: " << module.getTargetTriple() << "\n";

    // Get the target specific parser.
    auto target_machine = Internal::make_target_machine(module);
    internal_assert(target_machine.get()) << "Could not allocate target machine!\n";

    #if LLVM_VERSION == 37
    llvm::DataLayout target_data_layout(*(target_machine->getDataLayout()));
    #else
    llvm::DataLayout target_data_layout(target_machine->createDataLayout());
    #endif
    if (!(target_data_layout == module.getDataLayout())) {
        internal_error << "Warning: module's data layout does not match target machine's\n"
                       << target_data_layout.getStringRepresentation() << "\n"
                       << module.getDataLayout().getStringRepresentation() << "\n";
    }

    // Build up all of the passes that we want to do to the module.
    llvm::legacy::PassManager pass_manager;

    pass_manager.add(new llvm::TargetLibraryInfoWrapperPass(llvm::Triple(module.getTargetTriple())));

    // Make sure things marked as always-inline get inlined
    #if LLVM_VERSION < 40
    pass_manager.add(llvm::createAlwaysInlinerPass());
    #else
    pass_manager.add(llvm::createAlwaysInlinerLegacyPass());
    #endif

    // Enable symbol rewriting. This allows code outside libHalide to
    // use symbol rewriting when compiling Halide code (for example, by
    // using cl::ParseCommandLineOption and then passing the appropriate
    // rewrite options via -mllvm flags).
    pass_manager.add(llvm::createRewriteSymbolsPass());

    // Override default to generate verbose assembly.
    target_machine->Options.MCOptions.AsmVerbose = true;

    // Ask the target to add backend passes as necessary.
    target_machine->addPassesToEmitFile(pass_manager, out, file_type);

    pass_manager.run(module);
}

std::unique_ptr<llvm::Module> compile_module_to_llvm_module(const Module &module, llvm::LLVMContext &context) {
    return codegen_llvm(module, context);
}

void compile_llvm_module_to_object(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_ObjectFile);
}

void compile_llvm_module_to_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    emit_file(module, out, llvm::TargetMachine::CGFT_AssemblyFile);
}

void compile_llvm_module_to_llvm_bitcode(llvm::Module &module, Internal::LLVMOStream& out) {
    WriteBitcodeToFile(&module, out);
}

void compile_llvm_module_to_llvm_assembly(llvm::Module &module, Internal::LLVMOStream& out) {
    module.print(out, nullptr);
}

// Note that the utilities for get/set working directory are deliberately *not* in Util.h;
// generally speaking, you shouldn't ever need or want to do this, and doing so is asking for
// trouble. This exists solely to work around an issue with LLVM, hence its restricted
// location. If we ever legitimately need this elsewhere, consider moving it to Util.h.
namespace {

std::string get_current_directory() {
#ifdef _WIN32
    std::string dir;
    char p[MAX_PATH];
    DWORD ret = GetCurrentDirectoryA(MAX_PATH, p);
    internal_assert(ret == 0) << "GetCurrentDirectoryA() failed";
    dir = p;
    return dir;
#else
    std::string dir;
    char *p = getcwd(nullptr, 0);
    internal_assert(p != NULL) << "getcwd() failed";
    dir = p;
    free(p);
    return dir;
#endif
}

void set_current_directory(const std::string &d) {
#ifdef _WIN32
    internal_assert(SetCurrentDirectoryA(d.c_str())) << "SetCurrentDirectoryA() failed";
#else
    internal_assert(chdir(d.c_str()) == 0) << "chdir() failed";
#endif
}

std::pair<std::string, std::string> dir_and_file(const std::string &path) {
    std::string dir, file;
    size_t slash_pos = path.rfind('/');
#ifdef _WIN32
    if (slash_pos == std::string::npos) {
        // Windows is a thing
        slash_pos = path.rfind('\\');
    }
#endif
    if (slash_pos != std::string::npos) {
        dir = path.substr(0, slash_pos);
        file = path.substr(slash_pos + 1);
    } else {
        file = path;
    }
    return { dir, file };
}

std::string make_absolute_path(const std::string &path) {
    bool is_absolute = path.size() >= 1 && path[0] == '/';
    char sep = '/';
#ifdef _WIN32
    // Allow for C:\whatever or c:/whatever on Windows
    if (path.size() >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) {
        is_absolute = true;
        sep = path[2];
    }
#endif
    if (!is_absolute) {
        return get_current_directory() + sep + path;
    }
    return path;
}

struct SetCwd {
    const std::string original_directory;
    explicit SetCwd(const std::string &d) : original_directory(get_current_directory()) {
        if (!d.empty()) {
            set_current_directory(d);
        }
    }
    ~SetCwd() {
        set_current_directory(original_directory);
    }
};

}

void create_static_library(const std::vector<std::string> &src_files_in, const Target &target,
                           const std::string &dst_file_in, bool deterministic) {
    internal_assert(!src_files_in.empty());

    // Ensure that dst_file is an absolute path, since we're going to change the
    // working directory temporarily.
    std::string dst_file = make_absolute_path(dst_file_in);

    // If we give absolute paths to LLVM, it will dutifully embed them in the resulting
    // .a file; some versions of 'ar x' are unable to deal with the resulting files,
    // which is inconvenient. So let's doctor the inputs to be simple filenames,
    // and temporarily change the working directory. (Note that this requires all the
    // input files be in the same directory; this is currently always the case for
    // our existing usage.)
    std::string src_dir = dir_and_file(src_files_in.front()).first;
    std::vector<std::string> src_files;
    for (auto &s_in : src_files_in) {
        auto df = dir_and_file(s_in);
        internal_assert(df.first == src_dir) << "All inputs to create_static_library() must be in the same directory";
        for (auto &s_existing : src_files) {
            internal_assert(s_existing != df.second) << "create_static_library() does not allow duplicate filenames.";
        }
        src_files.push_back(df.second);
    }

    SetCwd set_cwd(src_dir);

#if LLVM_VERSION >= 39
    std::vector<llvm::NewArchiveMember> new_members;
    for (auto &src : src_files) {
        llvm::Expected<llvm::NewArchiveMember> new_member =
            llvm::NewArchiveMember::getFile(src, /*Deterministic=*/true);
        if (!new_member) {
            // Don't use internal_assert: the call to new_member.takeError() will be evaluated
            // even if the assert does not fail, leaving new_member in an indeterminate
            // state.
            internal_error << src << ": " << llvm::toString(new_member.takeError()) << "\n";
        }
        new_members.push_back(std::move(*new_member));
    }
#elif LLVM_VERSION == 38
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src));
    }
#else
    std::vector<llvm::NewArchiveIterator> new_members;
    for (auto &src : src_files) {
        new_members.push_back(llvm::NewArchiveIterator(src, src));
    }
#endif

    const bool write_symtab = true;
    const auto kind = Internal::get_triple_for_target(target).isOSDarwin()
        ? llvm::object::Archive::K_BSD
        : llvm::object::Archive::K_GNU;
#if LLVM_VERSION == 37
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic);
#elif LLVM_VERSION == 38
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin);
#else
    const bool thin = false;
    auto result = llvm::writeArchive(dst_file, new_members,
                       write_symtab, kind,
                       deterministic, thin, nullptr);
#endif
    internal_assert(!result.second) << "Failed to write archive: " << dst_file
        << ", reason: " << result.second << "\n";
}

}  // namespace Halide
<|endoftext|>


<commit_before>/*
 * This file is part of the bitcoin-classic project
 * Copyright (C) 2017 Tom Zander <tomz@freedommail.ch>
 *
 * 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 "LogChannels_p.h"
#include "util.h"

#include <iostream>

namespace {
std::string shortenMethod(const char *methodName) {
    assert(methodName);
    const char *start = strchr(methodName, ' ');
    const char *end = strchr(methodName, '(');
    if (!start || start > end)
        start = methodName;
    if (start && end) {
        ++start;
        ++end;
        std::string copy(start, end - start);
        return std::move(copy);
    }
    return std::string();
}

}

Log::Channel::Channel(TimeStampFormat f)
    : m_timeStampFormat(f),
      m_printSection(true),
      m_printLineNumber(false),
      m_printMethodName(true),
      m_printFilename(false),
      m_showSubSecondPrecision(true)
{
}

bool Log::Channel::printSection() const
{
    return m_printSection;
}

void Log::Channel::setPrintSection(bool printSection)
{
    m_printSection = printSection;
}

bool Log::Channel::printLineNumber() const
{
    return m_printLineNumber;
}

void Log::Channel::setPrintLineNumber(bool printLineNumber)
{
    m_printLineNumber = printLineNumber;
}

bool Log::Channel::printMethodName() const
{
    return m_printMethodName;
}

void Log::Channel::setPrintMethodName(bool printMethodName)
{
    m_printMethodName = printMethodName;
}

bool Log::Channel::printFilename() const
{
    return m_printFilename;
}

void Log::Channel::setPrintFilename(bool printFilename)
{
    m_printFilename = printFilename;
}

Log::Channel::TimeStampFormat Log::Channel::timeStampFormat() const
{
    return m_timeStampFormat;
}

void Log::Channel::setTimeStampFormat(const TimeStampFormat &timeStampFormat)
{
    m_timeStampFormat = timeStampFormat;
}

bool Log::Channel::showSubSecondPrecision() const
{
    return m_showSubSecondPrecision;
}

void Log::Channel::setShowSubSecondPrecision(bool showSubSecondPrecision)
{
    m_showSubSecondPrecision = showSubSecondPrecision;
}

// ------------------------------------------------------

ConsoleLogChannel::ConsoleLogChannel()
    : Channel(TimeOnly)
{
}

void ConsoleLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char *filename, int lineNumber, const char *methodName, short logSection, short logLevel)
{
    std::ostream &out = logLevel >= Log::WarningLevel ? std::clog : std::cout;
    if (timestamp)
        out << *timestamp << ' ';
    if (m_printSection && logSection) {
        out << '[';
        const std::string section = Log::Manager::sectionString(logSection);
        if (!section.empty())
            out << section;
        else
            out << logSection;
        out << "] ";
    }
    if (m_printFilename && filename)
        out << filename << (m_printLineNumber ? ':' : ' ');
    if (m_printLineNumber && lineNumber)
        out << lineNumber << ';';
    if (m_printMethodName && methodName) {
        std::string m(shortenMethod(methodName));
        if (!m.empty())
            out << m << ") ";
    }
    out << line;
    if (line.empty() || line.back() != '\n')
        out << std::endl;
    out.flush();
}

FileLogChannel::FileLogChannel()
    : Channel(DateTime),
      m_fileout(0)
{
    reopenLogFiles();
}

FileLogChannel::~FileLogChannel()
{
    if (m_fileout)
        fclose(m_fileout);
}

static void FileWriteStr(const std::string &str, FILE *fp) {
    fwrite(str.data(), 1, str.size(), fp);
}

void FileLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char*, int, const char *methodName, short logSection, short)
{
    if (m_fileout) {
        if (timestamp) {
            FileWriteStr(*timestamp, m_fileout);
            FileWriteStr(" ", m_fileout);
        }
        if (m_printSection && logSection) {
            FileWriteStr("[", m_fileout);
            const std::string section = Log::Manager::sectionString(logSection);
            if (section.empty()) {
                std::ostringstream num;
                num << logSection;
                FileWriteStr(num.str() , m_fileout);
            } else {
                FileWriteStr(section , m_fileout);
            }
            FileWriteStr("] ", m_fileout);
        }
        if (m_printMethodName && methodName) {
            std::string m(shortenMethod(methodName));
            if (!m.empty()) {
                FileWriteStr(m , m_fileout);
                FileWriteStr(") " , m_fileout);
            }
        }
        FileWriteStr(line, m_fileout);
        if (line.empty() || line.back() != '\n')
            FileWriteStr("\n", m_fileout);
    }
}

void FileLogChannel::reopenLogFiles()
{
    if (m_fileout)
        fclose(m_fileout);

    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
    m_fileout = fopen(pathDebug.string().c_str(), "a");
    if (m_fileout)
        setbuf(m_fileout, NULL); // unbuffered
}
<commit_msg>Fix logger skipping first char of constructor<commit_after>/*
 * This file is part of the bitcoin-classic project
 * Copyright (C) 2017 Tom Zander <tomz@freedommail.ch>
 *
 * 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 "LogChannels_p.h"
#include "util.h"

#include <iostream>

namespace {
std::string shortenMethod(const char *methodName) {
    assert(methodName);
    const char *start = strchr(methodName, ' ');
    const char *end = strchr(methodName, '(');
    if (end) {
        if (!start || start > end)
            start = methodName;
        else
            ++start;
        ++end;
        std::string copy(start, end - start);
        return std::move(copy);
    }
    return std::string();
}

}

Log::Channel::Channel(TimeStampFormat f)
    : m_timeStampFormat(f),
      m_printSection(true),
      m_printLineNumber(false),
      m_printMethodName(true),
      m_printFilename(false),
      m_showSubSecondPrecision(true)
{
}

bool Log::Channel::printSection() const
{
    return m_printSection;
}

void Log::Channel::setPrintSection(bool printSection)
{
    m_printSection = printSection;
}

bool Log::Channel::printLineNumber() const
{
    return m_printLineNumber;
}

void Log::Channel::setPrintLineNumber(bool printLineNumber)
{
    m_printLineNumber = printLineNumber;
}

bool Log::Channel::printMethodName() const
{
    return m_printMethodName;
}

void Log::Channel::setPrintMethodName(bool printMethodName)
{
    m_printMethodName = printMethodName;
}

bool Log::Channel::printFilename() const
{
    return m_printFilename;
}

void Log::Channel::setPrintFilename(bool printFilename)
{
    m_printFilename = printFilename;
}

Log::Channel::TimeStampFormat Log::Channel::timeStampFormat() const
{
    return m_timeStampFormat;
}

void Log::Channel::setTimeStampFormat(const TimeStampFormat &timeStampFormat)
{
    m_timeStampFormat = timeStampFormat;
}

bool Log::Channel::showSubSecondPrecision() const
{
    return m_showSubSecondPrecision;
}

void Log::Channel::setShowSubSecondPrecision(bool showSubSecondPrecision)
{
    m_showSubSecondPrecision = showSubSecondPrecision;
}

// ------------------------------------------------------

ConsoleLogChannel::ConsoleLogChannel()
    : Channel(TimeOnly)
{
}

void ConsoleLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char *filename, int lineNumber, const char *methodName, short logSection, short logLevel)
{
    std::ostream &out = logLevel >= Log::WarningLevel ? std::clog : std::cout;
    if (timestamp)
        out << *timestamp << ' ';
    if (m_printSection && logSection) {
        out << '[';
        const std::string section = Log::Manager::sectionString(logSection);
        if (!section.empty())
            out << section;
        else
            out << logSection;
        out << "] ";
    }
    if (m_printFilename && filename)
        out << filename << (m_printLineNumber ? ':' : ' ');
    if (m_printLineNumber && lineNumber)
        out << lineNumber << ';';
    if (m_printMethodName && methodName) {
        std::string m(shortenMethod(methodName));
        if (!m.empty())
            out << m << ") ";
    }
    out << line;
    if (line.empty() || line.back() != '\n')
        out << std::endl;
    out.flush();
}

FileLogChannel::FileLogChannel()
    : Channel(DateTime),
      m_fileout(0)
{
    reopenLogFiles();
}

FileLogChannel::~FileLogChannel()
{
    if (m_fileout)
        fclose(m_fileout);
}

static void FileWriteStr(const std::string &str, FILE *fp) {
    fwrite(str.data(), 1, str.size(), fp);
}

void FileLogChannel::pushLog(int64_t, std::string *timestamp, const std::string &line, const char*, int, const char *methodName, short logSection, short)
{
    if (m_fileout) {
        if (timestamp) {
            FileWriteStr(*timestamp, m_fileout);
            FileWriteStr(" ", m_fileout);
        }
        if (m_printSection && logSection) {
            FileWriteStr("[", m_fileout);
            const std::string section = Log::Manager::sectionString(logSection);
            if (section.empty()) {
                std::ostringstream num;
                num << logSection;
                FileWriteStr(num.str() , m_fileout);
            } else {
                FileWriteStr(section , m_fileout);
            }
            FileWriteStr("] ", m_fileout);
        }
        if (m_printMethodName && methodName) {
            std::string m(shortenMethod(methodName));
            if (!m.empty()) {
                FileWriteStr(m , m_fileout);
                FileWriteStr(") " , m_fileout);
            }
        }
        FileWriteStr(line, m_fileout);
        if (line.empty() || line.back() != '\n')
            FileWriteStr("\n", m_fileout);
    }
}

void FileLogChannel::reopenLogFiles()
{
    if (m_fileout)
        fclose(m_fileout);

    boost::filesystem::path pathDebug = GetDataDir() / "debug.log";
    m_fileout = fopen(pathDebug.string().c_str(), "a");
    if (m_fileout)
        setbuf(m_fileout, NULL); // unbuffered
}
<|endoftext|>


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

#include "service/control-service.h"

#include "common/constant-strings.h"
#include "exec/kudu-util.h"
#include "kudu/rpc/rpc_context.h"
#include "rpc/rpc-mgr.h"
#include "rpc/rpc-mgr.inline.h"
#include "runtime/coordinator.h"
#include "runtime/exec-env.h"
#include "runtime/mem-tracker.h"
#include "service/client-request-state.h"
#include "service/impala-server.h"
#include "testutil/fault-injection-util.h"
#include "util/debug-util.h"
#include "util/memory-metrics.h"
#include "util/parse-util.h"
#include "util/uid-util.h"

#include "gen-cpp/control_service.pb.h"
#include "gen-cpp/control_service.proxy.h"
#include "gen-cpp/RuntimeProfile_types.h"

#include "common/names.h"

using kudu::rpc::RpcContext;

static const string QUEUE_LIMIT_MSG = "(Advanced) Limit on RPC payloads consumption for "
    "ControlService. " + Substitute(MEM_UNITS_HELP_MSG, "the process memory limit");
DEFINE_string(control_service_queue_mem_limit, "50MB", QUEUE_LIMIT_MSG.c_str());
DEFINE_int32(control_service_num_svc_threads, 0, "Number of threads for processing "
    "control service's RPCs. if left at default value 0, it will be set to number of "
    "CPU cores. Set it to a positive value to change from the default.");

namespace impala {

ControlService::ControlService(MetricGroup* metric_group)
  : ControlServiceIf(ExecEnv::GetInstance()->rpc_mgr()->metric_entity(),
        ExecEnv::GetInstance()->rpc_mgr()->result_tracker()) {
  MemTracker* process_mem_tracker = ExecEnv::GetInstance()->process_mem_tracker();
  bool is_percent; // not used
  int64_t bytes_limit = ParseUtil::ParseMemSpec(FLAGS_control_service_queue_mem_limit,
      &is_percent, process_mem_tracker->limit());
  if (bytes_limit <= 0) {
    CLEAN_EXIT_WITH_ERROR(Substitute("Invalid mem limit for control service queue: "
        "'$0'.", FLAGS_control_service_queue_mem_limit));
  }
  mem_tracker_.reset(new MemTracker(
      bytes_limit, "Control Service Queue", process_mem_tracker));
  MemTrackerMetric::CreateMetrics(metric_group, mem_tracker_.get(), "ControlService");
}

Status ControlService::Init() {
  int num_svc_threads = FLAGS_control_service_num_svc_threads > 0 ?
      FLAGS_control_service_num_svc_threads : CpuInfo::num_cores();
  // The maximum queue length is set to maximum 32-bit value. Its actual capacity is
  // bound by memory consumption against 'mem_tracker_'.
  RETURN_IF_ERROR(ExecEnv::GetInstance()->rpc_mgr()->RegisterService(num_svc_threads,
      std::numeric_limits<int32_t>::max(), this, mem_tracker_.get()));
  return Status::OK();
}

Status ControlService::GetProxy(const TNetworkAddress& address, const string& hostname,
    unique_ptr<ControlServiceProxy>* proxy) {
  // Create a ControlService proxy to the destination.
  return ExecEnv::GetInstance()->rpc_mgr()->GetProxy(address, hostname, proxy);
}

bool ControlService::Authorize(const google::protobuf::Message* req,
    google::protobuf::Message* resp, RpcContext* context) {
  return ExecEnv::GetInstance()->rpc_mgr()->Authorize("ControlService", context,
      mem_tracker_.get());
}

Status ControlService::GetProfile(const ReportExecStatusRequestPB& request,
    const ClientRequestState& request_state, kudu::rpc::RpcContext* rpc_context,
    TRuntimeProfileForest* thrift_profiles) {
  // Debug action to simulate deserialization failure.
  RETURN_IF_ERROR(DebugAction(request_state.query_options(),
      "REPORT_EXEC_STATUS_PROFILE"));
  kudu::Slice thrift_profiles_slice;
  KUDU_RETURN_IF_ERROR(rpc_context->GetInboundSidecar(
      request.thrift_profiles_sidecar_idx(), &thrift_profiles_slice),
      "Failed to get thrift profile sidecar");
  uint32_t len = thrift_profiles_slice.size();
  RETURN_IF_ERROR(DeserializeThriftMsg(thrift_profiles_slice.data(),
      &len, true, thrift_profiles));
  return Status::OK();
}

void ControlService::ReportExecStatus(const ReportExecStatusRequestPB* request,
    ReportExecStatusResponsePB* response, kudu::rpc::RpcContext* rpc_context) {
  const TUniqueId query_id = ProtoToQueryId(request->query_id());
  shared_ptr<ClientRequestState> request_state =
      ExecEnv::GetInstance()->impala_server()->GetClientRequestState(query_id);

  if (request_state.get() == nullptr) {
    // This is expected occasionally (since a report RPC might be in flight while
    // cancellation is happening). Return an error to the caller to get it to stop.
    const string& err = Substitute("ReportExecStatus(): Received report for unknown "
        "query ID (probably closed or cancelled): $0", PrintId(query_id));
    VLOG(1) << err;
    RespondAndReleaseRpc(Status::Expected(err), response, rpc_context);
    return;
  }

  // This failpoint is to allow jitter to be injected.
  DebugActionNoFail(request_state->query_options(), "REPORT_EXEC_STATUS_DELAY");

  // The runtime profile is sent as a Thrift serialized buffer via sidecar. Get the
  // sidecar and deserialize the thrift profile if there is any. The sender may have
  // failed to serialize the Thrift profile so an empty thrift profile is valid.
  // TODO: Fix IMPALA-7232 to indicate incomplete profile in this case.
  TRuntimeProfileForest thrift_profiles;
  if (LIKELY(request->has_thrift_profiles_sidecar_idx())) {
    const Status& profile_status =
        GetProfile(*request, *request_state.get(), rpc_context, &thrift_profiles);
    if (UNLIKELY(!profile_status.ok())) {
      LOG(ERROR) << Substitute("ReportExecStatus(): Failed to deserialize profile "
          "for query ID $0: $1", PrintId(request_state->query_id()),
          profile_status.GetDetail());
      // Do not expose a partially deserialized profile.
      TRuntimeProfileForest empty_profiles;
      swap(thrift_profiles, empty_profiles);
    }
  }

  Status resp_status = request_state->UpdateBackendExecStatus(*request, thrift_profiles);
  RespondAndReleaseRpc(resp_status, response, rpc_context);
}

template<typename ResponsePBType>
void ControlService::RespondAndReleaseRpc(const Status& status, ResponsePBType* response,
    kudu::rpc::RpcContext* rpc_context) {
  status.ToProto(response->mutable_status());
  // Release the memory against the control service's memory tracker.
  mem_tracker_->Release(rpc_context->GetTransferSize());
  rpc_context->RespondSuccess();
}

}
<commit_msg>IMPALA-7183: Include remote host when logging unknown execution status.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "service/control-service.h"

#include "common/constant-strings.h"
#include "exec/kudu-util.h"
#include "kudu/rpc/rpc_context.h"
#include "rpc/rpc-mgr.h"
#include "rpc/rpc-mgr.inline.h"
#include "runtime/coordinator.h"
#include "runtime/exec-env.h"
#include "runtime/mem-tracker.h"
#include "service/client-request-state.h"
#include "service/impala-server.h"
#include "testutil/fault-injection-util.h"
#include "util/debug-util.h"
#include "util/memory-metrics.h"
#include "util/parse-util.h"
#include "util/uid-util.h"

#include "gen-cpp/control_service.pb.h"
#include "gen-cpp/control_service.proxy.h"
#include "gen-cpp/RuntimeProfile_types.h"

#include "common/names.h"

using kudu::rpc::RpcContext;

static const string QUEUE_LIMIT_MSG = "(Advanced) Limit on RPC payloads consumption for "
    "ControlService. " + Substitute(MEM_UNITS_HELP_MSG, "the process memory limit");
DEFINE_string(control_service_queue_mem_limit, "50MB", QUEUE_LIMIT_MSG.c_str());
DEFINE_int32(control_service_num_svc_threads, 0, "Number of threads for processing "
    "control service's RPCs. if left at default value 0, it will be set to number of "
    "CPU cores. Set it to a positive value to change from the default.");

namespace impala {

ControlService::ControlService(MetricGroup* metric_group)
  : ControlServiceIf(ExecEnv::GetInstance()->rpc_mgr()->metric_entity(),
        ExecEnv::GetInstance()->rpc_mgr()->result_tracker()) {
  MemTracker* process_mem_tracker = ExecEnv::GetInstance()->process_mem_tracker();
  bool is_percent; // not used
  int64_t bytes_limit = ParseUtil::ParseMemSpec(FLAGS_control_service_queue_mem_limit,
      &is_percent, process_mem_tracker->limit());
  if (bytes_limit <= 0) {
    CLEAN_EXIT_WITH_ERROR(Substitute("Invalid mem limit for control service queue: "
        "'$0'.", FLAGS_control_service_queue_mem_limit));
  }
  mem_tracker_.reset(new MemTracker(
      bytes_limit, "Control Service Queue", process_mem_tracker));
  MemTrackerMetric::CreateMetrics(metric_group, mem_tracker_.get(), "ControlService");
}

Status ControlService::Init() {
  int num_svc_threads = FLAGS_control_service_num_svc_threads > 0 ?
      FLAGS_control_service_num_svc_threads : CpuInfo::num_cores();
  // The maximum queue length is set to maximum 32-bit value. Its actual capacity is
  // bound by memory consumption against 'mem_tracker_'.
  RETURN_IF_ERROR(ExecEnv::GetInstance()->rpc_mgr()->RegisterService(num_svc_threads,
      std::numeric_limits<int32_t>::max(), this, mem_tracker_.get()));
  return Status::OK();
}

Status ControlService::GetProxy(const TNetworkAddress& address, const string& hostname,
    unique_ptr<ControlServiceProxy>* proxy) {
  // Create a ControlService proxy to the destination.
  return ExecEnv::GetInstance()->rpc_mgr()->GetProxy(address, hostname, proxy);
}

bool ControlService::Authorize(const google::protobuf::Message* req,
    google::protobuf::Message* resp, RpcContext* context) {
  return ExecEnv::GetInstance()->rpc_mgr()->Authorize("ControlService", context,
      mem_tracker_.get());
}

Status ControlService::GetProfile(const ReportExecStatusRequestPB& request,
    const ClientRequestState& request_state, kudu::rpc::RpcContext* rpc_context,
    TRuntimeProfileForest* thrift_profiles) {
  // Debug action to simulate deserialization failure.
  RETURN_IF_ERROR(DebugAction(request_state.query_options(),
      "REPORT_EXEC_STATUS_PROFILE"));
  kudu::Slice thrift_profiles_slice;
  KUDU_RETURN_IF_ERROR(rpc_context->GetInboundSidecar(
      request.thrift_profiles_sidecar_idx(), &thrift_profiles_slice),
      "Failed to get thrift profile sidecar");
  uint32_t len = thrift_profiles_slice.size();
  RETURN_IF_ERROR(DeserializeThriftMsg(thrift_profiles_slice.data(),
      &len, true, thrift_profiles));
  return Status::OK();
}

void ControlService::ReportExecStatus(const ReportExecStatusRequestPB* request,
    ReportExecStatusResponsePB* response, kudu::rpc::RpcContext* rpc_context) {
  const TUniqueId query_id = ProtoToQueryId(request->query_id());
  shared_ptr<ClientRequestState> request_state =
      ExecEnv::GetInstance()->impala_server()->GetClientRequestState(query_id);

  if (request_state.get() == nullptr) {
    // This is expected occasionally (since a report RPC might be in flight while
    // cancellation is happening). Return an error to the caller to get it to stop.
    const string& err = Substitute("ReportExecStatus(): Received report for unknown "
                                   "query ID (probably closed or cancelled): $0 "
                                   "remote host=$1",
        PrintId(query_id), rpc_context->remote_address().ToString());
    VLOG(1) << err;
    RespondAndReleaseRpc(Status::Expected(err), response, rpc_context);
    return;
  }

  // This failpoint is to allow jitter to be injected.
  DebugActionNoFail(request_state->query_options(), "REPORT_EXEC_STATUS_DELAY");

  // The runtime profile is sent as a Thrift serialized buffer via sidecar. Get the
  // sidecar and deserialize the thrift profile if there is any. The sender may have
  // failed to serialize the Thrift profile so an empty thrift profile is valid.
  // TODO: Fix IMPALA-7232 to indicate incomplete profile in this case.
  TRuntimeProfileForest thrift_profiles;
  if (LIKELY(request->has_thrift_profiles_sidecar_idx())) {
    const Status& profile_status =
        GetProfile(*request, *request_state.get(), rpc_context, &thrift_profiles);
    if (UNLIKELY(!profile_status.ok())) {
      LOG(ERROR) << Substitute("ReportExecStatus(): Failed to deserialize profile "
          "for query ID $0: $1", PrintId(request_state->query_id()),
          profile_status.GetDetail());
      // Do not expose a partially deserialized profile.
      TRuntimeProfileForest empty_profiles;
      swap(thrift_profiles, empty_profiles);
    }
  }

  Status resp_status = request_state->UpdateBackendExecStatus(*request, thrift_profiles);
  RespondAndReleaseRpc(resp_status, response, rpc_context);
}

template<typename ResponsePBType>
void ControlService::RespondAndReleaseRpc(const Status& status, ResponsePBType* response,
    kudu::rpc::RpcContext* rpc_context) {
  status.ToProto(response->mutable_status());
  // Release the memory against the control service's memory tracker.
  mem_tracker_->Release(rpc_context->GetTransferSize());
  rpc_context->RespondSuccess();
}

}
<|endoftext|>


<commit_before>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Rabbit.h"
#include "../Entities/Player.h"
#include "../World.h"

cRabbit::cRabbit(void) :
	super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.9, 0.9)
{
}

void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
	int LootingLevel = 0;
	if (a_Killer != nullptr)
	{
		LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
	}
	AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_RABBIT : E_ITEM_RAW_RABBIT);
	AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RABBIT_HIDE);
	cItems RareDrops;
	RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT));
	AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
}

<commit_msg>Fixed Rabbit size<commit_after>
#include "Globals.h"  // NOTE: MSVC stupidness requires this to be the same across all modules

#include "Rabbit.h"
#include "../Entities/Player.h"
#include "../World.h"

cRabbit::cRabbit(void) :
	super("Rabbit", mtRabbit, "mob.rabbit.idle", "mob.rabbit.death", 0.82, 0.68)
{
}

void cRabbit::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
	int LootingLevel = 0;
	if (a_Killer != nullptr)
	{
		LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
	}
	AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, IsOnFire() ? E_ITEM_COOKED_RABBIT : E_ITEM_RAW_RABBIT);
	AddRandomDropItem(a_Drops, 0, 1 + LootingLevel, E_ITEM_RABBIT_HIDE);
	cItems RareDrops;
	RareDrops.Add(cItem(E_ITEM_RABBITS_FOOT));
	AddRandomRareDropItem(a_Drops, RareDrops, LootingLevel);
}

<|endoftext|>


<commit_before>/* Copyright (C) 2017 BARBOTIN Nicolas
 *
 * 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 "mgpcl/ProgramArgs.h"
#include <iostream>

/*************************************** ArgValue ***************************************/

m::ArgValue::~ArgValue()
{
    if(m_values != nullptr) {
        const int cnt = valueCount();
        for(int i = 0; i < cnt; i++)
            m_values[i].~String();

        delete[] reinterpret_cast<uint8_t*>(m_values);
    }
}

/*************************************** ArgDescriptor ***************************************/

m::ArgDescriptor::ArgDescriptor(ArgType t, const String &n)
{
    m_type = t;
    m_optional = true;
    m_numeric = false;
    m_unique = true;
    m_names.add(n);
    m_lastErr = kAE_NoError;
    m_default = nullptr;
}

m::ArgDescriptor::~ArgDescriptor()
{
    for(ArgValue *v : m_values)
        v->removeRef();

    if(m_default != nullptr)
        m_default->removeRef();
}

bool m::ArgDescriptor::matches(const String &name) const
{
    for(const String &s: m_names) {
        if(name == s)
            return true;
    }

    return false;
}

m::ArgValue *m::ArgDescriptor::parse(int *argc, const char ***argv)
{
    if((m_unique || m_type == kAT_Switch) && !m_values.isEmpty()) {
        m_lastErr = kAE_AlreadySet;
        return nullptr;
    }

    int needed = static_cast<int>(m_type) + 1; //Don't forget (*argv)[0] == argName, and then comes values
    if(*argc < needed) {
        m_lastErr = kAE_MissingValues;
        return nullptr;
    }

    ArgValue *ret = new ArgValue(this);
    ret->allocate(needed);

    if(m_type == kAT_Switch)
        new(ret->m_values) String("true");
    else {
        bool wrongFormat = false;

        for(int i = 0; i < needed - 1; i++) {
            new(ret->m_values + i) String((*argv)[i + 1]);

            if(m_numeric && !wrongFormat && !ret->m_values[i].isNumber())
                wrongFormat = true;
        }

        if(wrongFormat) {
            m_lastErr = kAE_NotNumeric;
            delete ret; //Destructor will care about allocated strings. Also, no need to use removeRef()
            return nullptr;
        }
    }

    //Everything went fine
    m_values.add(ret);
    ret->addRef();

    *argc -= needed;
    *argv += needed;
    return ret;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Single, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate();
    new(m_default->m_values) String(a);

    m_default->addRef();
    return *this;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a, const String &b)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Dual, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate(2);
    new(m_default->m_values + 0) String(a);
    new(m_default->m_values + 1) String(b);

    m_default->addRef();
    return *this;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a, const String &b, const String &c)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Triple, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate(3);
    new(m_default->m_values + 0) String(a);
    new(m_default->m_values + 1) String(b);
    new(m_default->m_values + 2) String(c);

    m_default->addRef();
    return *this;
}

void m::ArgDescriptor::clearValues()
{
    for(ArgValue *v : m_values)
        v->removeRef();

    m_values.cleanup();
}

m::String m::ArgDescriptor::errorString() const
{
    switch(m_lastErr) {
    case kAE_NoError:
        return String("No error");

    case kAE_MissingValues:
        return String(m_type == kAT_Single ? "Missing value" : "Missing values");

    case kAE_NotNumeric:
        return String("One or more value has an invalid numeric format");

    case kAE_AlreadySet:
        return String("Multiple instances of this argument found");

    case kAE_Required:
        return String("This argument is required and wasn't set");

    default:
        return String("Unknown error");
    }
}

/*************************************** ProgramArgs ***************************************/

m::ProgramArgs::~ProgramArgs()
{
    for(ArgValue *v : m_args)
        v->removeRef();

    for(ArgDescriptor *d: m_descr)
        delete d;
}

m::ArgDescriptor *m::ProgramArgs::argByName(const String &name)
{
    for(ArgDescriptor *d: m_descr) {
        if(d->matches(name))
            return d;
    }

    return nullptr;
}

m::ArgDescriptor &m::ProgramArgs::add(const String &n, ArgType t)
{
    ArgDescriptor *ret = new ArgDescriptor(t, n);
    m_descr.add(ret);

    return *ret;
}

m::ArgDescriptor &m::ProgramArgs::addHelpSwitch(const String &name)
{
    ArgDescriptor *ret = new ArgDescriptor(kAT_Switch, name);
    m_descr.add(ret);

    m_helpSwitch = ret;
    return *ret;
}

m::ArgParseError m::ProgramArgs::parse()
{
    int argc          = m_argc - 1; //First argument is program binary location, so ignore it...
    const char **argv = m_argv + 1;

    //Cleanup existing args & error
    for(ArgDescriptor *ad : m_descr)
        ad->clearValues();

    for(ArgValue *v : m_args)
        v->removeRef();

    m_args.cleanup();
    m_err = nullptr;
    m_remainingIdx = -1;
    m_unrecognized.clear();

    //Parse from descriptors
    bool argFound = true;
    while(argFound && argc > 0) {
        ArgDescriptor *d = argByName(*argv);

        if(d == nullptr)
            argFound = false;
        else {
            ArgValue *v = d->parse(&argc, &argv);
            if(v == nullptr) {
                m_err = d;
                m_lastErr = kAPE_ArgError;

                return kAPE_ArgError; //Something wrong happened.
            }

            m_args.add(v);
            v->addRef();
        }
    }

    //Make sure everyone has its value
    for(ArgDescriptor *d: m_descr) {
        if(d->m_type == kAT_Switch && d->m_values.isEmpty()) {
            ArgValue *av = new ArgValue(d);
            av->allocate();
            new(av->m_values) String("false");

            av->addRef(); //Ref will be destroyed by ArgDescriptor
            d->m_values.add(av);
        } if(!d->isSatisfied() && (!m_helpIgnoreReqs || m_helpSwitch == nullptr)) {
            d->m_lastErr = kAE_Required;

            m_err = d;
            m_lastErr = kAPE_ArgError;
            return kAPE_ArgError;
        }
    }

    //Append remaining arguments
    if(argc > 0) {
        if(!m_acceptsRem) {
            m_unrecognized = *argv;
            m_lastErr = kAPE_UnknownArgFound;
            return kAPE_UnknownArgFound;
        }

        m_remainingIdx = m_args.size();
    }

    while(argc > 0) {
        ArgValue *v = new ArgValue(nullptr); //No parent
        v->allocate();
        new(v->m_values) String(*argv);

        //Append value & next arg
        m_args.add(v);
        v->addRef();

        argc--;
        argv++;
    }

    //Print help if needed
    if(m_helpSwitch != nullptr && m_helpSwitch->isSet())
        printHelp();

    m_lastErr = kAPE_NoError;
    return kAPE_NoError;
}

void m::ProgramArgs::printHelp() const
{
    int maxlen = 0;
    String *descr = new String[m_descr.size()];

    for(int i = 0; i < m_descr.size(); i++) {
        descr[i] = m_descr[i]->name();
        for(int j = 1; j < m_descr[i]->nameCount(); j++) {
            descr[i] += ", ";
            descr[i] += m_descr[i]->name(j);
        }

        descr[i] += ": ";
        if(descr[i].length() > maxlen)
            maxlen = descr[i].length();
    }

    if(!m_helpHdr.isEmpty())
        std::cout << m_helpHdr.raw() << std::endl << std::endl;

    for(int i = 0; i < m_descr.size(); i++) {
        int delta = maxlen - descr[i].length();
        std::cout << descr[i].raw();

        for(int j = 0; j < delta; j++)
            std::cout << ' ';

        std::cout << m_descr[i]->helpText().raw() << std::endl;
    }

    delete[] descr;
}

m::String m::ProgramArgs::errorString() const
{
    if(m_lastErr == kAPE_NoError)
        return String("No error");
    else if(m_lastErr == kAPE_UnknownArgFound)
        return String("Found unrecognized argument ") + m_unrecognized;
    else if(m_lastErr == kAPE_ArgError) {
        String ret("Argument \"");
        ret += m_err->name();
        ret += "\" errored: ";
        ret += m_err->errorString();

        return ret;
    }

    return String("Unknown error");
}
<commit_msg>* Fixed segmentation fault caused by help switch of ProgramArgs<commit_after>/* Copyright (C) 2017 BARBOTIN Nicolas
 *
 * 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 "mgpcl/ProgramArgs.h"
#include <iostream>

/*************************************** ArgValue ***************************************/

m::ArgValue::~ArgValue()
{
    if(m_values != nullptr) {
        const int cnt = valueCount();
        for(int i = 0; i < cnt; i++)
            m_values[i].~String();

        delete[] reinterpret_cast<uint8_t*>(m_values);
    }
}

/*************************************** ArgDescriptor ***************************************/

m::ArgDescriptor::ArgDescriptor(ArgType t, const String &n)
{
    m_type = t;
    m_optional = true;
    m_numeric = false;
    m_unique = true;
    m_names.add(n);
    m_lastErr = kAE_NoError;
    m_default = nullptr;
}

m::ArgDescriptor::~ArgDescriptor()
{
    for(ArgValue *v : m_values)
        v->removeRef();

    if(m_default != nullptr)
        m_default->removeRef();
}

bool m::ArgDescriptor::matches(const String &name) const
{
    for(const String &s: m_names) {
        if(name == s)
            return true;
    }

    return false;
}

m::ArgValue *m::ArgDescriptor::parse(int *argc, const char ***argv)
{
    if((m_unique || m_type == kAT_Switch) && !m_values.isEmpty()) {
        m_lastErr = kAE_AlreadySet;
        return nullptr;
    }

    int needed = static_cast<int>(m_type) + 1; //Don't forget (*argv)[0] == argName, and then comes values
    if(*argc < needed) {
        m_lastErr = kAE_MissingValues;
        return nullptr;
    }

    ArgValue *ret = new ArgValue(this);
    ret->allocate(needed);

    if(m_type == kAT_Switch)
        new(ret->m_values) String("true");
    else {
        bool wrongFormat = false;

        for(int i = 0; i < needed - 1; i++) {
            new(ret->m_values + i) String((*argv)[i + 1]);

            if(m_numeric && !wrongFormat && !ret->m_values[i].isNumber())
                wrongFormat = true;
        }

        if(wrongFormat) {
            m_lastErr = kAE_NotNumeric;
            delete ret; //Destructor will care about allocated strings. Also, no need to use removeRef()
            return nullptr;
        }
    }

    //Everything went fine
    m_values.add(ret);
    ret->addRef();

    *argc -= needed;
    *argv += needed;
    return ret;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Single, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate();
    new(m_default->m_values) String(a);

    m_default->addRef();
    return *this;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a, const String &b)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Dual, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate(2);
    new(m_default->m_values + 0) String(a);
    new(m_default->m_values + 1) String(b);

    m_default->addRef();
    return *this;
}

m::ArgDescriptor &m::ArgDescriptor::setDefault(const String &a, const String &b, const String &c)
{
    if(m_default != nullptr)
        m_default->removeRef();

    mAssert(m_type == kAT_Triple, "invalid default value");

    m_default = new ArgValue(this);
    m_default->allocate(3);
    new(m_default->m_values + 0) String(a);
    new(m_default->m_values + 1) String(b);
    new(m_default->m_values + 2) String(c);

    m_default->addRef();
    return *this;
}

void m::ArgDescriptor::clearValues()
{
    for(ArgValue *v : m_values)
        v->removeRef();

    m_values.cleanup();
}

m::String m::ArgDescriptor::errorString() const
{
    switch(m_lastErr) {
    case kAE_NoError:
        return String("No error");

    case kAE_MissingValues:
        return String(m_type == kAT_Single ? "Missing value" : "Missing values");

    case kAE_NotNumeric:
        return String("One or more value has an invalid numeric format");

    case kAE_AlreadySet:
        return String("Multiple instances of this argument found");

    case kAE_Required:
        return String("This argument is required and wasn't set");

    default:
        return String("Unknown error");
    }
}

/*************************************** ProgramArgs ***************************************/

m::ProgramArgs::~ProgramArgs()
{
    for(ArgValue *v : m_args)
        v->removeRef();

    for(ArgDescriptor *d: m_descr)
        delete d;
}

m::ArgDescriptor *m::ProgramArgs::argByName(const String &name)
{
    for(ArgDescriptor *d: m_descr) {
        if(d->matches(name))
            return d;
    }

    return nullptr;
}

m::ArgDescriptor &m::ProgramArgs::add(const String &n, ArgType t)
{
    ArgDescriptor *ret = new ArgDescriptor(t, n);
    m_descr.add(ret);

    return *ret;
}

m::ArgDescriptor &m::ProgramArgs::addHelpSwitch(const String &name)
{
    ArgDescriptor *ret = new ArgDescriptor(kAT_Switch, name);
    m_descr.add(ret);

    m_helpSwitch = ret;
    return *ret;
}

m::ArgParseError m::ProgramArgs::parse()
{
    int argc          = m_argc - 1; //First argument is program binary location, so ignore it...
    const char **argv = m_argv + 1;

    //Cleanup existing args & error
    for(ArgDescriptor *ad : m_descr)
        ad->clearValues();

    for(ArgValue *v : m_args)
        v->removeRef();

    m_args.cleanup();
    m_err = nullptr;
    m_remainingIdx = -1;
    m_unrecognized.clear();

    //Parse from descriptors
    bool argFound = true;
    while(argFound && argc > 0) {
        ArgDescriptor *d = argByName(*argv);

        if(d == nullptr)
            argFound = false;
        else {
            ArgValue *v = d->parse(&argc, &argv);
            if(v == nullptr) {
                m_err = d;
                m_lastErr = kAPE_ArgError;

                return kAPE_ArgError; //Something wrong happened.
            }

            m_args.add(v);
            v->addRef();
        }
    }

    const bool ignoreReqs = m_helpIgnoreReqs && m_helpSwitch != nullptr && !m_helpSwitch->m_values.isEmpty();

    //Make sure everyone has its value
    for(ArgDescriptor *d: m_descr) {
        if(d->m_type == kAT_Switch && d->m_values.isEmpty()) {
            ArgValue *av = new ArgValue(d);
            av->allocate();
            new(av->m_values) String("false");

            av->addRef(); //Ref will be destroyed by ArgDescriptor
            d->m_values.add(av);
        } if(!d->isSatisfied() && !ignoreReqs) {
            d->m_lastErr = kAE_Required;

            m_err = d;
            m_lastErr = kAPE_ArgError;
            return kAPE_ArgError;
        }
    }

    //Append remaining arguments
    if(argc > 0) {
        if(!m_acceptsRem) {
            m_unrecognized = *argv;
            m_lastErr = kAPE_UnknownArgFound;
            return kAPE_UnknownArgFound;
        }

        m_remainingIdx = m_args.size();
    }

    while(argc > 0) {
        ArgValue *v = new ArgValue(nullptr); //No parent
        v->allocate();
        new(v->m_values) String(*argv);

        //Append value & next arg
        m_args.add(v);
        v->addRef();

        argc--;
        argv++;
    }

    //Print help if needed
    if(m_helpSwitch != nullptr && m_helpSwitch->isSet())
        printHelp();

    m_lastErr = kAPE_NoError;
    return kAPE_NoError;
}

void m::ProgramArgs::printHelp() const
{
    int maxlen = 0;
    String *descr = new String[m_descr.size()];

    for(int i = 0; i < m_descr.size(); i++) {
        descr[i] = m_descr[i]->name();
        for(int j = 1; j < m_descr[i]->nameCount(); j++) {
            descr[i] += ", ";
            descr[i] += m_descr[i]->name(j);
        }

        descr[i] += ": ";
        if(descr[i].length() > maxlen)
            maxlen = descr[i].length();
    }

    if(!m_helpHdr.isEmpty())
        std::cout << m_helpHdr.raw() << std::endl << std::endl;

    for(int i = 0; i < m_descr.size(); i++) {
        int delta = maxlen - descr[i].length();
        std::cout << descr[i].raw();

        for(int j = 0; j < delta; j++)
            std::cout << ' ';

        std::cout << m_descr[i]->helpText().raw() << std::endl;
    }

    delete[] descr;
}

m::String m::ProgramArgs::errorString() const
{
    if(m_lastErr == kAPE_NoError)
        return String("No error");
    else if(m_lastErr == kAPE_UnknownArgFound)
        return String("Found unrecognized argument ") + m_unrecognized;
    else if(m_lastErr == kAPE_ArgError) {
        String ret("Argument \"");
        ret += m_err->name();
        ret += "\" errored: ";
        ret += m_err->errorString();

        return ret;
    }

    return String("Unknown error");
}
<|endoftext|>


<commit_before>// PythonStuff.cpp
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.

#include "stdafx.h"
#include <wx/file.h>
#include <wx/mimetype.h>
#include <wx/stdpaths.h>
#include <wx/filename.h>
#include <wx/txtstrm.h>
#include <wx/log.h>
#include "PythonStuff.h"
#include "ProgramCanvas.h"
#include "OutputCanvas.h"
#include "Program.h"
#include "CNCConfig.h"
#include "interface/PropertyString.h"

//static
bool CPyProcess::redirect = true;

CPyProcess::CPyProcess(void)
{
  m_pid = 0;
  wxProcess(heeksCAD->GetMainFrame());
  Connect(wxEVT_TIMER, wxTimerEventHandler(CPyProcess::OnTimer));
  m_timer.SetOwner(this);

}

void CPyProcess::OnTimer(wxTimerEvent& event)
{
  HandleInput();
}

void CPyProcess::HandleInput(void) {
	wxInputStream *m_in, *m_err;

	m_in = GetInputStream();
	m_err = GetErrorStream();

	if (m_in) {
		wxString s;
		while (m_in->CanRead()) {
			char buffer[4096];
			m_in->Read(buffer, sizeof(buffer));
			s += wxString::From8BitData(buffer, m_in->LastRead());
		}
		if (s.Length() > 0) {
			wxLogMessage(_T("> %s"), s.c_str());
		}
	}
	if (m_err) {
		wxString s;
		while (m_err->CanRead()) {
			char buffer[4096];
			m_err->Read(buffer, sizeof(buffer));
			s += wxString::From8BitData(buffer, m_err->LastRead());
		}
		if (s.Length() > 0) {
			wxLogMessage(_T("! %s"), s.c_str());
		}
	}
}

void CPyProcess::Execute(const wxChar* cmd)
{
	if(redirect) {
		Redirect();
	}
	// make process group leader so Cancel kan terminate process including children
	m_pid = wxExecute(cmd, wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER, this);
	if (!m_pid) {
	  wxLogMessage(_T("could not execute '%s'"),cmd);
	} else {
	  wxLogMessage(_T("starting '%s' (%d)"),cmd,m_pid);
	}
	if (redirect) {
		m_timer.Start(100);   //msec
	}
}

void CPyProcess::Cancel(void)
{
	if (m_pid)
	{
		wxKillError kerror;
		wxSignal sig = wxSIGTERM;
		int retcode;

		if ( wxProcess::Exists(m_pid) ) {
			retcode = wxKill(m_pid, sig, &kerror, wxKILL_CHILDREN);
			switch (kerror) {
			case wxKILL_OK:
				wxLogMessage(_T("sent signal %d to process %d"),sig, m_pid);
				break;
			case wxKILL_NO_PROCESS:
				wxLogMessage(_T("process %d already exited"),m_pid);
				break;
			case wxKILL_ACCESS_DENIED:
				wxLogMessage(_T("sending signal %d to process %d - access denied"),sig, m_pid);
				break;
			case wxKILL_BAD_SIGNAL:      // no such signal
				wxLogMessage(_T("no such signal: %d"),sig);
				break;
			case wxKILL_ERROR:            // another, unspecified error
				wxLogMessage(_T("unspecified error sending signal %d to process %d"),sig, m_pid);
				break;
			}
		} else {
			wxLogMessage(_T("process %d has gone away"), m_pid);
		}
		m_pid = 0;
	}
}

void CPyProcess::OnTerminate(int pid, int status)
{
	if (pid == m_pid)
	{
	  if (redirect) {
		  m_timer.Stop();
		  HandleInput();   // anything left?
	  }
	  if (status) {
		  wxLogMessage(_T("process %d exit(%d)"),pid, status);
	  } else {
		  wxLogDebug(_T("process %d exit(0)"),pid);
	  }
	  m_pid = 0;
	  ThenDo();
	}
	// else: the process already was already treated with Cancel() so m_pid is 0
}

////////////////////////////////////////////////////////

class CPyBackPlot : public CPyProcess
{
protected:
	const CProgram* m_program;
	HeeksObj* m_into;
	wxString m_filename;
	wxBusyCursor *m_busy_cursor;

	static CPyBackPlot* m_object;

public:
	CPyBackPlot(const CProgram* program, HeeksObj* into, const wxChar* filename): m_program(program), m_into(into),m_filename(filename),m_busy_cursor(NULL) { m_object = this; }
	~CPyBackPlot(void) { m_object = NULL; }

	static void StaticCancel(void) { if (m_object) m_object->Cancel(); }

	void Do(void)
	{
		if(m_busy_cursor == NULL)m_busy_cursor = new wxBusyCursor();

		if (m_program->m_machine.file_name == _T("not found"))
		{
			wxMessageBox(_T("Machine name (defined in Program Properties) not found"));
		} // End if - then
		else
		{
#ifdef WIN32
			Execute(wxString(_T("\"")) + theApp.GetDllFolder() + _T("\\nc_read.bat\" ") + m_program->m_machine.file_name + _T(" \"") + m_filename + _T("\""));
#else
#ifdef RUNINPLACE
            wxString path(_T("/nc/"));
#else
            wxString path(_T("/../heekscnc/nc/"));
#endif

			Execute(wxString(_T("python \"")) + theApp.GetDllFolder() + path + m_program->m_machine.file_name + wxString(_T("_read.py\" \"")) + m_filename + wxString(_T("\"")) );
#endif
		} // End if - else
	}
	void ThenDo(void)
	{
		// there should now be a .nc.xml written
		wxString xml_file_str = m_filename + wxString(_T(".nc.xml"));
		wxFile ofs(xml_file_str.c_str());
		if(!ofs.IsOpened())
		{
			wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + xml_file_str);
			return;
		}

		// read the xml file, just like paste, into the program
		heeksCAD->OpenXMLFile(xml_file_str, m_into);
		heeksCAD->Repaint();

		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		delete m_busy_cursor;
		m_busy_cursor = NULL;
	}
};

CPyBackPlot* CPyBackPlot::m_object = NULL;

class CPyPostProcess : public CPyProcess
{
protected:
	const CProgram* m_program;
	wxString m_filename;
	bool m_include_backplot_processing;

	static CPyPostProcess* m_object;

public:
	CPyPostProcess(const CProgram* program,
			const wxChar* filename,
			const bool include_backplot_processing = true ) :
		m_program(program), m_filename(filename), m_include_backplot_processing(include_backplot_processing)
	{
		m_object = this;
	}

	~CPyPostProcess(void) { m_object = NULL; }

	static void StaticCancel(void) { if (m_object) m_object->Cancel(); }

	void Do(void)
	{
		wxBusyCursor wait; // show an hour glass until the end of this function
		wxStandardPaths standard_paths;
		wxFileName path( standard_paths.GetTempDir().c_str(), _T("post.py"));

#ifdef WIN32
        Execute(wxString(_T("\"")) + theApp.GetDllFolder() + wxString(_T("\\post.bat\" \"")) + path.GetFullPath() + wxString(_T("\"")));
#else

        wxString post_path = wxString(_T("python ")) + path.GetFullPath();
		Execute(post_path);
#endif
	}
	void ThenDo(void)
	{
		if (m_include_backplot_processing)
		{
			(new CPyBackPlot(m_program, (HeeksObj*)m_program, m_filename))->Do();
		}
	}
};

CPyPostProcess* CPyPostProcess::m_object = NULL;

////////////////////////////////////////////////////////

static bool write_python_file(const wxString& python_file_path)
{
	wxFile ofs(python_file_path.c_str(), wxFile::write);
	if(!ofs.IsOpened())return false;

	ofs.Write(theApp.m_program->m_python_program.c_str());

	return true;
}

bool HeeksPyPostProcess(const CProgram* program, const wxString &filepath, const bool include_backplot_processing)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		// write the python file
		wxStandardPaths standard_paths;
		wxFileName file_str( standard_paths.GetTempDir().c_str(), _T("post.py"));

		if(!write_python_file(file_str.GetFullPath()))
		{
		    wxString error;
		    error << _T("couldn't write ") << file_str.GetFullPath();
		    wxMessageBox(error.c_str());
		}
		else
		{
#ifdef WIN32
			// Set the working directory to the area that contains the DLL so that
			// the system can find the post.bat file correctly.
			::wxSetWorkingDirectory(theApp.GetDllFolder());
#else
			::wxSetWorkingDirectory(standard_paths.GetTempDir());
#endif

			// call the python file
			(new CPyPostProcess(program, filepath, include_backplot_processing))->Do();

			return true;
		}
	}
	catch(...)
	{
		wxMessageBox(_T("Error while post-processing the program!"));
	}
	return false;
}

bool HeeksPyBackplot(const CProgram* program, HeeksObj* into, const wxString &filepath)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		::wxSetWorkingDirectory(theApp.GetDllFolder());

		// call the python file
		(new CPyBackPlot(program, into, filepath))->Do();

		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		return true;
	}
	catch(...)
	{
		wxMessageBox(_T("Error while backplotting the program!"));
	}
	return false;
}

void HeeksPyCancel(void)
{
	CPyBackPlot::StaticCancel();
	CPyPostProcess::StaticCancel();
}

// create a temporary ngc file
// make your favorite machine load it
void CSendToMachine::Cancel(void) { CPyProcess::Cancel(); }
void CSendToMachine::SendGCode(const wxChar *gcode)
	{
		wxBusyCursor wait; // show an hour glass until the end of this function

		// write the ngc file
		wxStandardPaths standard_paths;
		wxFileName ngcpath( standard_paths.GetTempDir().c_str(), wxString::Format(_T("heekscnc-%d.ngc"), m_serial));
		m_serial++;
		{
			wxFile ofs(ngcpath.GetFullPath(), wxFile::write);
			if(!ofs.IsOpened())
			{
				wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + ngcpath.GetFullPath());
				return;
			}
			ofs.Write(theApp.m_output_canvas->m_textCtrl->GetValue());
		}
		wxLogDebug(_T("created '%s')"), ngcpath.GetFullPath().c_str());

#ifdef WIN32
        Execute(wxString(_T("\"")) + theApp.GetDllFolder() +wxString(_T("\\")) + m_command + wxString(_T("\" \"")) + ngcpath.GetFullPath() + wxString(_T("\"")));
#else
        wxString sendto_cmdline = m_command + wxString(_T(" ")) + ngcpath.GetFullPath();
        wxLogDebug(_T("executing '%s')"), sendto_cmdline.c_str());
		Execute(sendto_cmdline);
#endif
	}

int CSendToMachine::m_serial;
wxString CSendToMachine::m_command;

static void on_set_to_machine_command(const wxChar *value, HeeksObj* object)
{
	CSendToMachine::m_command = value;
	CSendToMachine::WriteToConfig();
}

// static
void CSendToMachine::GetOptions(std::list<Property *> *list)
{
	list->push_back(new PropertyString(_("send-to-machine command"), m_command, NULL, on_set_to_machine_command));
}

// static
void CSendToMachine::ReadFromConfig()
{
        CNCConfig config(CSendToMachine::ConfigScope());
        config.Read(_T("SendToMachineCommand"), &m_command , _T("axis-remote"));
}
// static
void CSendToMachine::WriteToConfig()
{
        CNCConfig config(CSendToMachine::ConfigScope());
        config.Write(_T("SendToMachineCommand"), m_command);
}

static CSendToMachine *sendto;

bool HeeksSendToMachine(const wxString &gcode)
{
	if (sendto != NULL) {
		sendto->Cancel();
		delete sendto;
	}
	sendto = new CSendToMachine;
	sendto->SendGCode(gcode);

	return false;
}

<commit_msg>I fixed a compile error on Windows; seems like "sento" was already defined in "winsock.h", so I changed it to "send_to"<commit_after>// PythonStuff.cpp
// Copyright (c) 2009, Dan Heeks
// This program is released under the BSD license. See the file COPYING for details.

#include "stdafx.h"
#include <wx/file.h>
#include <wx/mimetype.h>
#include <wx/stdpaths.h>
#include <wx/filename.h>
#include <wx/txtstrm.h>
#include <wx/log.h>
#include "PythonStuff.h"
#include "ProgramCanvas.h"
#include "OutputCanvas.h"
#include "Program.h"
#include "CNCConfig.h"
#include "interface/PropertyString.h"

//static
bool CPyProcess::redirect = true;

CPyProcess::CPyProcess(void)
{
  m_pid = 0;
  wxProcess(heeksCAD->GetMainFrame());
  Connect(wxEVT_TIMER, wxTimerEventHandler(CPyProcess::OnTimer));
  m_timer.SetOwner(this);

}

void CPyProcess::OnTimer(wxTimerEvent& event)
{
  HandleInput();
}

void CPyProcess::HandleInput(void) {
	wxInputStream *m_in, *m_err;

	m_in = GetInputStream();
	m_err = GetErrorStream();

	if (m_in) {
		wxString s;
		while (m_in->CanRead()) {
			char buffer[4096];
			m_in->Read(buffer, sizeof(buffer));
			s += wxString::From8BitData(buffer, m_in->LastRead());
		}
		if (s.Length() > 0) {
			wxLogMessage(_T("> %s"), s.c_str());
		}
	}
	if (m_err) {
		wxString s;
		while (m_err->CanRead()) {
			char buffer[4096];
			m_err->Read(buffer, sizeof(buffer));
			s += wxString::From8BitData(buffer, m_err->LastRead());
		}
		if (s.Length() > 0) {
			wxLogMessage(_T("! %s"), s.c_str());
		}
	}
}

void CPyProcess::Execute(const wxChar* cmd)
{
	if(redirect) {
		Redirect();
	}
	// make process group leader so Cancel kan terminate process including children
	m_pid = wxExecute(cmd, wxEXEC_ASYNC|wxEXEC_MAKE_GROUP_LEADER, this);
	if (!m_pid) {
	  wxLogMessage(_T("could not execute '%s'"),cmd);
	} else {
	  wxLogMessage(_T("starting '%s' (%d)"),cmd,m_pid);
	}
	if (redirect) {
		m_timer.Start(100);   //msec
	}
}

void CPyProcess::Cancel(void)
{
	if (m_pid)
	{
		wxKillError kerror;
		wxSignal sig = wxSIGTERM;
		int retcode;

		if ( wxProcess::Exists(m_pid) ) {
			retcode = wxKill(m_pid, sig, &kerror, wxKILL_CHILDREN);
			switch (kerror) {
			case wxKILL_OK:
				wxLogMessage(_T("sent signal %d to process %d"),sig, m_pid);
				break;
			case wxKILL_NO_PROCESS:
				wxLogMessage(_T("process %d already exited"),m_pid);
				break;
			case wxKILL_ACCESS_DENIED:
				wxLogMessage(_T("sending signal %d to process %d - access denied"),sig, m_pid);
				break;
			case wxKILL_BAD_SIGNAL:      // no such signal
				wxLogMessage(_T("no such signal: %d"),sig);
				break;
			case wxKILL_ERROR:            // another, unspecified error
				wxLogMessage(_T("unspecified error sending signal %d to process %d"),sig, m_pid);
				break;
			}
		} else {
			wxLogMessage(_T("process %d has gone away"), m_pid);
		}
		m_pid = 0;
	}
}

void CPyProcess::OnTerminate(int pid, int status)
{
	if (pid == m_pid)
	{
	  if (redirect) {
		  m_timer.Stop();
		  HandleInput();   // anything left?
	  }
	  if (status) {
		  wxLogMessage(_T("process %d exit(%d)"),pid, status);
	  } else {
		  wxLogDebug(_T("process %d exit(0)"),pid);
	  }
	  m_pid = 0;
	  ThenDo();
	}
	// else: the process already was already treated with Cancel() so m_pid is 0
}

////////////////////////////////////////////////////////

class CPyBackPlot : public CPyProcess
{
protected:
	const CProgram* m_program;
	HeeksObj* m_into;
	wxString m_filename;
	wxBusyCursor *m_busy_cursor;

	static CPyBackPlot* m_object;

public:
	CPyBackPlot(const CProgram* program, HeeksObj* into, const wxChar* filename): m_program(program), m_into(into),m_filename(filename),m_busy_cursor(NULL) { m_object = this; }
	~CPyBackPlot(void) { m_object = NULL; }

	static void StaticCancel(void) { if (m_object) m_object->Cancel(); }

	void Do(void)
	{
		if(m_busy_cursor == NULL)m_busy_cursor = new wxBusyCursor();

		if (m_program->m_machine.file_name == _T("not found"))
		{
			wxMessageBox(_T("Machine name (defined in Program Properties) not found"));
		} // End if - then
		else
		{
#ifdef WIN32
			Execute(wxString(_T("\"")) + theApp.GetDllFolder() + _T("\\nc_read.bat\" ") + m_program->m_machine.file_name + _T(" \"") + m_filename + _T("\""));
#else
#ifdef RUNINPLACE
            wxString path(_T("/nc/"));
#else
            wxString path(_T("/../heekscnc/nc/"));
#endif

			Execute(wxString(_T("python \"")) + theApp.GetDllFolder() + path + m_program->m_machine.file_name + wxString(_T("_read.py\" \"")) + m_filename + wxString(_T("\"")) );
#endif
		} // End if - else
	}
	void ThenDo(void)
	{
		// there should now be a .nc.xml written
		wxString xml_file_str = m_filename + wxString(_T(".nc.xml"));
		wxFile ofs(xml_file_str.c_str());
		if(!ofs.IsOpened())
		{
			wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + xml_file_str);
			return;
		}

		// read the xml file, just like paste, into the program
		heeksCAD->OpenXMLFile(xml_file_str, m_into);
		heeksCAD->Repaint();

		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		delete m_busy_cursor;
		m_busy_cursor = NULL;
	}
};

CPyBackPlot* CPyBackPlot::m_object = NULL;

class CPyPostProcess : public CPyProcess
{
protected:
	const CProgram* m_program;
	wxString m_filename;
	bool m_include_backplot_processing;

	static CPyPostProcess* m_object;

public:
	CPyPostProcess(const CProgram* program,
			const wxChar* filename,
			const bool include_backplot_processing = true ) :
		m_program(program), m_filename(filename), m_include_backplot_processing(include_backplot_processing)
	{
		m_object = this;
	}

	~CPyPostProcess(void) { m_object = NULL; }

	static void StaticCancel(void) { if (m_object) m_object->Cancel(); }

	void Do(void)
	{
		wxBusyCursor wait; // show an hour glass until the end of this function
		wxStandardPaths standard_paths;
		wxFileName path( standard_paths.GetTempDir().c_str(), _T("post.py"));

#ifdef WIN32
        Execute(wxString(_T("\"")) + theApp.GetDllFolder() + wxString(_T("\\post.bat\" \"")) + path.GetFullPath() + wxString(_T("\"")));
#else

        wxString post_path = wxString(_T("python ")) + path.GetFullPath();
		Execute(post_path);
#endif
	}
	void ThenDo(void)
	{
		if (m_include_backplot_processing)
		{
			(new CPyBackPlot(m_program, (HeeksObj*)m_program, m_filename))->Do();
		}
	}
};

CPyPostProcess* CPyPostProcess::m_object = NULL;

////////////////////////////////////////////////////////

static bool write_python_file(const wxString& python_file_path)
{
	wxFile ofs(python_file_path.c_str(), wxFile::write);
	if(!ofs.IsOpened())return false;

	ofs.Write(theApp.m_program->m_python_program.c_str());

	return true;
}

bool HeeksPyPostProcess(const CProgram* program, const wxString &filepath, const bool include_backplot_processing)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		// write the python file
		wxStandardPaths standard_paths;
		wxFileName file_str( standard_paths.GetTempDir().c_str(), _T("post.py"));

		if(!write_python_file(file_str.GetFullPath()))
		{
		    wxString error;
		    error << _T("couldn't write ") << file_str.GetFullPath();
		    wxMessageBox(error.c_str());
		}
		else
		{
#ifdef WIN32
			// Set the working directory to the area that contains the DLL so that
			// the system can find the post.bat file correctly.
			::wxSetWorkingDirectory(theApp.GetDllFolder());
#else
			::wxSetWorkingDirectory(standard_paths.GetTempDir());
#endif

			// call the python file
			(new CPyPostProcess(program, filepath, include_backplot_processing))->Do();

			return true;
		}
	}
	catch(...)
	{
		wxMessageBox(_T("Error while post-processing the program!"));
	}
	return false;
}

bool HeeksPyBackplot(const CProgram* program, HeeksObj* into, const wxString &filepath)
{
	try{
		theApp.m_output_canvas->m_textCtrl->Clear(); // clear the output window

		::wxSetWorkingDirectory(theApp.GetDllFolder());

		// call the python file
		(new CPyBackPlot(program, into, filepath))->Do();

		// in Windows, at least, executing the bat file was making HeeksCAD change it's Z order
		heeksCAD->GetMainFrame()->Raise();

		return true;
	}
	catch(...)
	{
		wxMessageBox(_T("Error while backplotting the program!"));
	}
	return false;
}

void HeeksPyCancel(void)
{
	CPyBackPlot::StaticCancel();
	CPyPostProcess::StaticCancel();
}

// create a temporary ngc file
// make your favorite machine load it
void CSendToMachine::Cancel(void) { CPyProcess::Cancel(); }
void CSendToMachine::SendGCode(const wxChar *gcode)
	{
		wxBusyCursor wait; // show an hour glass until the end of this function

		// write the ngc file
		wxStandardPaths standard_paths;
		wxFileName ngcpath( standard_paths.GetTempDir().c_str(), wxString::Format(_T("heekscnc-%d.ngc"), m_serial));
		m_serial++;
		{
			wxFile ofs(ngcpath.GetFullPath(), wxFile::write);
			if(!ofs.IsOpened())
			{
				wxMessageBox(wxString(_("Couldn't open file")) + _T(" - ") + ngcpath.GetFullPath());
				return;
			}
			ofs.Write(theApp.m_output_canvas->m_textCtrl->GetValue());
		}
		wxLogDebug(_T("created '%s')"), ngcpath.GetFullPath().c_str());

#ifdef WIN32
        Execute(wxString(_T("\"")) + theApp.GetDllFolder() +wxString(_T("\\")) + m_command + wxString(_T("\" \"")) + ngcpath.GetFullPath() + wxString(_T("\"")));
#else
        wxString sendto_cmdline = m_command + wxString(_T(" ")) + ngcpath.GetFullPath();
        wxLogDebug(_T("executing '%s')"), sendto_cmdline.c_str());
		Execute(sendto_cmdline);
#endif
	}

int CSendToMachine::m_serial;
wxString CSendToMachine::m_command;

static void on_set_to_machine_command(const wxChar *value, HeeksObj* object)
{
	CSendToMachine::m_command = value;
	CSendToMachine::WriteToConfig();
}

// static
void CSendToMachine::GetOptions(std::list<Property *> *list)
{
	list->push_back(new PropertyString(_("send-to-machine command"), m_command, NULL, on_set_to_machine_command));
}

// static
void CSendToMachine::ReadFromConfig()
{
        CNCConfig config(CSendToMachine::ConfigScope());
        config.Read(_T("SendToMachineCommand"), &m_command , _T("axis-remote"));
}
// static
void CSendToMachine::WriteToConfig()
{
        CNCConfig config(CSendToMachine::ConfigScope());
        config.Write(_T("SendToMachineCommand"), m_command);
}

static CSendToMachine *send_to;

bool HeeksSendToMachine(const wxString &gcode)
{
	if (send_to != NULL) {
		send_to->Cancel();
		delete send_to;
	}
	send_to = new CSendToMachine;
	send_to->SendGCode(gcode);

	return false;
}

<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Morwenn
 *
 * 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 <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <numeric>
#include <cpp-sort/adapters/counting_adapter.h>
#include <cpp-sort/adapters/small_array_adapter.h>
#include <cpp-sort/sorters.h>

using namespace cppsort;

////////////////////////////////////////////////////////////
// Readable sorter names

template<typename Sorter>
struct sorter_name;

template<>
struct sorter_name<insertion_sorter>
{
    static constexpr const char* value = "insertion_sorter";
};

template<>
struct sorter_name<selection_sorter>
{
    static constexpr const char* value = "selection_sorter";
};

template<typename... Args>
struct sorter_name<small_array_adapter<Args...>>
{
    static constexpr const char* value = "small_array_sorter";
};

////////////////////////////////////////////////////////////
// Counting functions

template<std::size_t N, typename Sorter>
auto count_comparison()
    -> void
{
    // Fill an array of size N
    std::array<int, N> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Count comparisons made by a sorter
    cppsort::counting_adapter<Sorter> sorter;

    // Total number of comparisons
    std::size_t count = 0;

    // For each possible permutation of collection
    do
    {
        auto to_sort = collection;
        // Sort collection, get the number of comparisons made
        count += sorter(to_sort);
        // Double counts as sorter tests
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort)));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));

    // Display the result
    std::cout << std::left
              << std::setw(20)  << sorter_name<Sorter>::value
              << std::setw(6)   << count
              << '\n';
}

template<std::size_t N, typename... Sorters>
auto compare_fixed_size()
    -> void
{
    std::cout << "array of size " << N << '\n';

    // Variadic dispatch only works with expressions
    int dummy[] = {
        (count_comparison<N, Sorters>(), 0)...
    };
    (void) dummy;
}

////////////////////////////////////////////////////////////
// Main

int main()
{
    // Size of the arrays to sort
    static constexpr std::size_t size = 7;

    compare_fixed_size<size,
        insertion_sorter,
        selection_sorter,
        small_array_adapter<void>
    >();
}
<commit_msg>Test comparisons, moves and cycles at once.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Morwenn
 *
 * 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 <algorithm>
#include <array>
#include <cassert>
#include <cstddef>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <numeric>
#include <cpp-sort/adapters.h>
#include <cpp-sort/sorters.h>

using namespace cppsort;

////////////////////////////////////////////////////////////
// Readable sorter names

template<typename Sorter>
struct sorter_name;

template<>
struct sorter_name<insertion_sorter>
{
    static constexpr const char* value = "insertion_sorter";
};

template<typename... Args>
struct sorter_name<low_comparisons_adapter<Args...>>
{
    static constexpr const char* value = "low_comparisons_sorter";
};

template<typename... Args>
struct sorter_name<low_moves_adapter<Args...>>
{
    static constexpr const char* value = "low_moves_sorter";
};

template<typename... Args>
struct sorter_name<small_array_adapter<Args...>>
{
    static constexpr const char* value = "small_array_sorter";
};

////////////////////////////////////////////////////////////
// Count the number of comparisons

template<std::size_t N, typename Sorter, std::size_t... Ind>
auto count_comparisons(std::index_sequence<Ind...>)
    -> void
{
    cppsort::counting_adapter<Sorter> sorter;

    // Fill an array of size N
    std::array<int, N> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Total number of comparisons
    std::size_t count = 0;

    // For each possible permutation of collection
    do
    {
        std::array<int, N> to_sort = { std::get<Ind>(collection)... };
        // Sort collection, get the number of comparisons made
        count += sorter(to_sort);

        // Double counts as sorter tests
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort)));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));

    // Display the result
    std::cout << std::left
              << std::setw(25)  << sorter_name<Sorter>::value
              << std::setw(6)   << count
              << '\n';
}

template<std::size_t N, typename... Sorters>
auto compare_comparisons()
    -> void
{
    std::cout << "\ncomparisons needed to sort an array of size " << N << '\n';

    using indices = std::make_index_sequence<N>;

    // Variadic dispatch only works with expressions
    int dummy[] = {
        (count_comparisons<N, Sorters>(indices{}), 0)...
    };
    (void) dummy;
}

////////////////////////////////////////////////////////////
// Count the number of moves

template<typename T>
struct move_counter
{
    //
    // This structure is used to count the number of moves
    // performed by a sorting algorithm and additionally
    // checks that no read from moved-from values happen
    // during the sort
    //

    // Constructors

    move_counter() = delete;
    move_counter(const move_counter&) = delete;

    move_counter(const T& value):
        moves(0),
        can_read(true),
        value(value)
    {}

    move_counter(move_counter&& other):
        moves(other.moves + 1),
        can_read(true),
        value(std::move(other.value))
    {
        if (not std::exchange(other.can_read, false))
        {
            std::cout << "illegal read from a moved-from value\n";
            assert(false);
        }
    }

    // Assignment operators

    move_counter& operator=(const move_counter&) = delete;

    auto operator=(move_counter&& other)
        -> move_counter&
    {
        if (&other != this)
        {
            moves += other.moves + 1;
            if (not std::exchange(other.can_read, false))
            {
                std::cout << "illegal read from a moved-from value\n";
                assert(false);
            }
            can_read = true;
            value = std::move(other.value);
        }
        return *this;
    }

    // Number of moves since its creation
    std::size_t moves;
    // Whether the value can be read
    bool can_read;
    // Actual value
    T value;
};

template<typename T>
auto operator<(const move_counter<T>& lhs, const move_counter<T>& rhs)
    -> bool
{
    return lhs.value < rhs.value;
}

template<std::size_t N, typename Sorter, std::size_t... Ind>
auto count_moves(std::index_sequence<Ind...>)
    -> void
{
    Sorter sorter;

    // Fill an array of size N
    std::array<int, N> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Total number of moves
    std::size_t count = 0;

    // For each possible permutation of collection
    do
    {
        std::array<move_counter<int>, N> to_sort = { std::get<Ind>(collection)... };

        // Sort collection, get the number of loves performed
        sorter(to_sort);
        for (auto& val: to_sort)
        {
            count += val.moves;
        }

        // Double counts as sorter tests
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort)));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));

    // Display the result
    std::cout << std::left
              << std::setw(25)  << sorter_name<Sorter>::value
              << std::setw(6)   << count
              << '\n';
}

template<std::size_t N, typename... Sorters>
auto compare_moves()
    -> void
{
    std::cout << "\nmoves needed to sort and array of size " << N << '\n';

    using indices = std::make_index_sequence<N>;

    // Variadic dispatch only works with expressions
    int dummy[] = {
        (count_moves<N, Sorters>(indices{}), 0)...
    };
    (void) dummy;
}

////////////////////////////////////////////////////////////
// Measure the number of cycles

#ifdef _WIN32
    #include <intrin.h>
    #define rdtsc __rdtsc
#else
    #ifdef __i386__
        static __inline__ unsigned long long rdtsc() {
            unsigned long long int x;
            __asm__ volatile(".byte 0x0f, 0x31" : "=A" (x));
            return x;
        }
    #elif defined(__x86_64__)
        static __inline__ unsigned long long rdtsc(){
            unsigned hi, lo;
            __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
            return ((unsigned long long) lo) | (((unsigned long long) hi) << 32);
        }
    #else
        #error no rdtsc implementation
    #endif
#endif

template<std::size_t N, typename Sorter, std::size_t... Ind>
auto count_cycles(std::index_sequence<Ind...>)
    -> void
{
    Sorter sorter;

    // Fill an array of size N
    std::array<int, N> collection;
    std::iota(std::begin(collection), std::end(collection), 0);

    // Total number of cycles
    std::size_t count = 0;

    // For each possible permutation of collection
    do
    {
        std::array<int, N> to_sort = { std::get<Ind>(collection)... };
        // Sort collection, get the number of cycles
        auto start = rdtsc();
        sorter(to_sort);
        auto end = rdtsc();

        count += (end - start);

        // Double counts as sorter tests
        assert(std::is_sorted(std::begin(to_sort), std::end(to_sort)));
    } while (std::next_permutation(std::begin(collection), std::end(collection)));

    // Display the result
    std::cout << std::left
              << std::setw(25)  << sorter_name<Sorter>::value
              << std::setw(6)   << count
              << '\n';
}

template<std::size_t N, typename... Sorters>
auto compare_time()
    -> void
{
    std::cout << "\ncycles needed to sort an array of size " << N << '\n';

    using indices = std::make_index_sequence<N>;

    // Variadic dispatch only works with expressions
    int dummy[] = {
        (count_cycles<N, Sorters>(indices{}), 0)...
    };
    (void) dummy;
}

////////////////////////////////////////////////////////////
// Main

int main()
{
    // Size of the arrays to sort
    static constexpr std::size_t size = 5;

    compare_comparisons<size,
        insertion_sorter,
        low_moves_adapter<void>,
        low_comparisons_adapter<void>,
        small_array_adapter<void>
    >();

    compare_moves<size,
        insertion_sorter,
        low_moves_adapter<void>,
        low_comparisons_adapter<void>,
        small_array_adapter<void>
    >();

    compare_time<size,
        insertion_sorter,
        low_moves_adapter<void>,
        low_comparisons_adapter<void>,
        small_array_adapter<void>
    >();
}
<|endoftext|>