<commit_before>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */
// Copyright 2001-2005  California Institute of Technology

#include "cantera/base/ct_defs.h"
#include "cantera/base/ctexceptions.h"
#include "cantera/base/ctml.h"
#include "cantera/base/global.h"
#include "cantera/base/stringUtils.h"
#include "../../ext/libexecstream/exec-stream.h"

#include <fstream>
#include <sstream>

using namespace Cantera;
using namespace std;

namespace ctml
{

//! return the full path to the Python interpreter.
/*!
 * Use the environment variable PYTHON_CMD if it is set. If not, return
 * the string 'python'.
 *
 * Note, there are hidden problems here that really direct us to use
 * a full pathname for the location of python. Basically the system
 * call will use the shell /bin/sh, in order to launch python.
 * This default shell may not be the shell that the user is employing.
 * Therefore, the default path to python may be different during
 * a system call than during the default user shell environment.
 * This is quite a headache. The answer is to always set the
 * PYTHON_CMD environmental variable in the user environment to
 * an absolute path to locate the python executable. Then this
 * issue goes away.
 */
static string pypath()
{
    string s = "python";
    const char* py = getenv("PYTHON_CMD");

    if (py) {
        string sp = stripws(string(py));
        if (sp.size() > 0) {
            s = sp;
        }
    }
    return s;
}

void ct2ctml(const char* file, const int debug)
{
#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
                       "python cti to ctml conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    string python_output;
    int python_exit_code;
    try {
        exec_stream_t python;
        python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
        python.start(pypath(), "-i");
        stringstream output_stream;
        python.in() <<
                    "if True:\n" << // Use this so that the rest is a single block
                    "    import sys\n" <<
                    "    sys.stderr = sys.stdout\n" <<
                    "    try:\n" <<
                    "        from cantera import ctml_writer\n" <<
                    "    except ImportError:\n" <<
                    "        import ctml_writer\n" <<
                    "    ctml_writer.convert(r'" << file << "')\n" <<
                    "    sys.exit(0)\n\n"
                    "sys.exit(7)\n";
        python.close_in();
        std::string line;
        while (python.out().good()) {
            std::getline(python.out(), line);
            output_stream << line << std::endl;;
        }
        python.close();
        python_exit_code = python.exit_code();
        python_output = stripws(output_stream.str());
    } catch (std::exception& err) {
        // Report failure to execute Python
        stringstream message;
        message << "Error executing python while converting input file:\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << err.what() << std::endl;
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_exit_code != 0) {
        // Report a failure in the conversion process
        stringstream message;
        message << "Error converting input file \"" << file << "\" to CTML.\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << "The exit code was: " << python_exit_code << "\n";
        if (python_output.size() > 0) {
            message << "-------------- start of converter log --------------\n";
            message << python_output << std::endl;
            message << "--------------- end of converter log ---------------";
        } else {
            message << "The command did not produce any output." << endl;
        }
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_output.size() > 0) {
        // Warn if there was any output from the conversion process
        stringstream message;
        message << "Warning: Unexpected output from CTI converter\n";
        message << "-------------- start of converter log --------------\n";
        message << python_output << std::endl;
        message << "--------------- end of converter log ---------------\n";
        writelog(message.str());
    }
}

void ck2cti(const std::string& in_file, const std::string& thermo_file,
            const std::string& transport_file, const std::string& id_tag)
{
#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = in_file;
    throw CanteraError("ct2ctml",
                       "python ck to cti conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    string python_output;
    int python_exit_code;
    try {
        exec_stream_t python;
        python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
        python.start(pypath(), "-i");
        stringstream output_stream;

        ostream& pyin = python.in();
        pyin << "if True:\n"; // Use this so that the rest is a single block
        pyin << "    import sys\n";
        pyin << "    sys.stderr = sys.stdout\n";
        pyin << "    import ck2cti\n";
        pyin << "    ck2cti.convertMech(r'" << in_file << "',";
        if (thermo_file != "" && thermo_file != "-") {
            pyin << " thermoFile=r'" << thermo_file << "',";
        }
        if (transport_file != "" && transport_file != "-") {
            pyin << " transportFile=r'" << transport_file << "',";
        }
        pyin << " phaseName='" << id_tag << "',";
        pyin << " quiet=True)\n";
        pyin << "    sys.exit(0)\n\n";
        pyin << "sys.exit(7)\n";
        python.close_in();

        std::string line;
        while (python.out().good()) {
            std::getline(python.out(), line);
            output_stream << line << std::endl;;
        }
        python.close();
        python_exit_code = python.exit_code();
        python_output = stripws(output_stream.str());
    } catch (std::exception& err) {
        // Report failure to execute Python
        stringstream message;
        message << "Error executing python while converting input file:\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << err.what() << std::endl;
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_exit_code != 0) {
        // Report a failure in the conversion process
        stringstream message;
        message << "Error converting input file \"" << in_file << "\" to CTI.\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << "The exit code was: " << python_exit_code << "\n";
        if (python_output.size() > 0) {
            message << "-------------- start of converter log --------------\n";
            message << python_output << std::endl;
            message << "--------------- end of converter log ---------------";
        } else {
            message << "The command did not produce any output." << endl;
        }
        throw CanteraError("ck2cti", message.str());
    }

    if (python_output.size() > 0) {
        // Warn if there was any output from the conversion process
        stringstream message;
        message << "Warning: Unexpected output from CTI converter\n";
        message << "-------------- start of converter log --------------\n";
        message << python_output << std::endl;
        message << "--------------- end of converter log ---------------\n";
        writelog(message.str());
    }
}

void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string& file, const int debug)
{
    std::string ff, ext = "";

    // find the input file on the Cantera search path
    std::string inname = findInputFile(file);
    writelog("Found file: "+inname+"\n", debug);

    if (inname == "") {
        throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    std::string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
        ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
        try {
            ctml::ct2ctml(inname.c_str(), debug);
        } catch (std::exception& err) {
            writelog("get_CTML_Tree: caught an exception:\n");
            writelog(err.what());
        }
        string ffull = inname.substr(0,idot) + ".xml";
        ff = "./" + getBaseName(ffull) + ".xml";
        if (debug > 0) {
            writelogf("ffull name = %s\n", ffull.c_str());
            writelogf("ff name = %s\n", ff.c_str());
        }
    } else {
        ff = inname;
    }
    writelog("Attempting to parse xml file " + ff + "\n", debug);
    ifstream fin(ff.c_str());
    if (!fin) {
        throw
        CanteraError("get_CTML_Tree",
                     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
}

Cantera::XML_Node getCtmlTree(const std::string& file)
{
    Cantera::XML_Node root;
    get_CTML_Tree(&root, file);
    return root;
}

}
<commit_msg>Fix calls to ck2cti that pass through the C++ interface<commit_after>/**
 * @file ct2ctml.cpp
 * Driver for the system call to the python executable that converts
 * cti files to ctml files (see \ref inputfiles).
 */
// Copyright 2001-2005  California Institute of Technology

#include "cantera/base/ct_defs.h"
#include "cantera/base/ctexceptions.h"
#include "cantera/base/ctml.h"
#include "cantera/base/global.h"
#include "cantera/base/stringUtils.h"
#include "../../ext/libexecstream/exec-stream.h"

#include <fstream>
#include <sstream>

using namespace Cantera;
using namespace std;

namespace ctml
{

//! return the full path to the Python interpreter.
/*!
 * Use the environment variable PYTHON_CMD if it is set. If not, return
 * the string 'python'.
 *
 * Note, there are hidden problems here that really direct us to use
 * a full pathname for the location of python. Basically the system
 * call will use the shell /bin/sh, in order to launch python.
 * This default shell may not be the shell that the user is employing.
 * Therefore, the default path to python may be different during
 * a system call than during the default user shell environment.
 * This is quite a headache. The answer is to always set the
 * PYTHON_CMD environmental variable in the user environment to
 * an absolute path to locate the python executable. Then this
 * issue goes away.
 */
static string pypath()
{
    string s = "python";
    const char* py = getenv("PYTHON_CMD");

    if (py) {
        string sp = stripws(string(py));
        if (sp.size() > 0) {
            s = sp;
        }
    }
    return s;
}

void ct2ctml(const char* file, const int debug)
{
#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = file;
    throw CanteraError("ct2ctml",
                       "python cti to ctml conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    string python_output;
    int python_exit_code;
    try {
        exec_stream_t python;
        python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
        python.start(pypath(), "-i");
        stringstream output_stream;
        python.in() <<
                    "if True:\n" << // Use this so that the rest is a single block
                    "    import sys\n" <<
                    "    sys.stderr = sys.stdout\n" <<
                    "    try:\n" <<
                    "        from cantera import ctml_writer\n" <<
                    "    except ImportError:\n" <<
                    "        import ctml_writer\n" <<
                    "    ctml_writer.convert(r'" << file << "')\n" <<
                    "    sys.exit(0)\n\n"
                    "sys.exit(7)\n";
        python.close_in();
        std::string line;
        while (python.out().good()) {
            std::getline(python.out(), line);
            output_stream << line << std::endl;;
        }
        python.close();
        python_exit_code = python.exit_code();
        python_output = stripws(output_stream.str());
    } catch (std::exception& err) {
        // Report failure to execute Python
        stringstream message;
        message << "Error executing python while converting input file:\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << err.what() << std::endl;
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_exit_code != 0) {
        // Report a failure in the conversion process
        stringstream message;
        message << "Error converting input file \"" << file << "\" to CTML.\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << "The exit code was: " << python_exit_code << "\n";
        if (python_output.size() > 0) {
            message << "-------------- start of converter log --------------\n";
            message << python_output << std::endl;
            message << "--------------- end of converter log ---------------";
        } else {
            message << "The command did not produce any output." << endl;
        }
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_output.size() > 0) {
        // Warn if there was any output from the conversion process
        stringstream message;
        message << "Warning: Unexpected output from CTI converter\n";
        message << "-------------- start of converter log --------------\n";
        message << python_output << std::endl;
        message << "--------------- end of converter log ---------------\n";
        writelog(message.str());
    }
}

void ck2cti(const std::string& in_file, const std::string& thermo_file,
            const std::string& transport_file, const std::string& id_tag)
{
#ifdef HAS_NO_PYTHON
    /*
     *  Section to bomb out if python is not
     *  present in the computation environment.
     */
    string ppath = in_file;
    throw CanteraError("ct2ctml",
                       "python ck to cti conversion requested for file, " + ppath +
                       ", but not available in this computational environment");
#endif

    string python_output;
    int python_exit_code;
    try {
        exec_stream_t python;
        python.set_wait_timeout(exec_stream_t::s_all, 1800000); // 30 minutes
        python.start(pypath(), "-i");
        stringstream output_stream;

        ostream& pyin = python.in();
        pyin << "if True:\n"; // Use this so that the rest is a single block
        pyin << "    import sys\n";
        pyin << "    sys.stderr = sys.stdout\n";
        pyin << "    import ck2cti\n";
        pyin << "    ck2cti.Parser().convertMech(r'" << in_file << "',";
        if (thermo_file != "" && thermo_file != "-") {
            pyin << " thermoFile=r'" << thermo_file << "',";
        }
        if (transport_file != "" && transport_file != "-") {
            pyin << " transportFile=r'" << transport_file << "',";
        }
        pyin << " phaseName='" << id_tag << "',";
        pyin << " quiet=True)\n";
        pyin << "    sys.exit(0)\n\n";
        pyin << "sys.exit(7)\n";
        python.close_in();

        std::string line;
        while (python.out().good()) {
            std::getline(python.out(), line);
            output_stream << line << std::endl;;
        }
        python.close();
        python_exit_code = python.exit_code();
        python_output = stripws(output_stream.str());
    } catch (std::exception& err) {
        // Report failure to execute Python
        stringstream message;
        message << "Error executing python while converting input file:\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << err.what() << std::endl;
        throw CanteraError("ct2ctml", message.str());
    }

    if (python_exit_code != 0) {
        // Report a failure in the conversion process
        stringstream message;
        message << "Error converting input file \"" << in_file << "\" to CTI.\n";
        message << "Python command was: '" << pypath() << "'\n";
        message << "The exit code was: " << python_exit_code << "\n";
        if (python_output.size() > 0) {
            message << "-------------- start of converter log --------------\n";
            message << python_output << std::endl;
            message << "--------------- end of converter log ---------------";
        } else {
            message << "The command did not produce any output." << endl;
        }
        throw CanteraError("ck2cti", message.str());
    }

    if (python_output.size() > 0) {
        // Warn if there was any output from the conversion process
        stringstream message;
        message << "Warning: Unexpected output from CTI converter\n";
        message << "-------------- start of converter log --------------\n";
        message << python_output << std::endl;
        message << "--------------- end of converter log ---------------\n";
        writelog(message.str());
    }
}

void get_CTML_Tree(Cantera::XML_Node* rootPtr, const std::string& file, const int debug)
{
    std::string ff, ext = "";

    // find the input file on the Cantera search path
    std::string inname = findInputFile(file);
    writelog("Found file: "+inname+"\n", debug);

    if (inname == "") {
        throw CanteraError("get_CTML_Tree", "file "+file+" not found");
    }

    /*
     * Check whether or not the file is XML. If not, it will be first
     * processed with the preprocessor.
     */
    std::string::size_type idot = inname.rfind('.');
    if (idot != string::npos) {
        ext = inname.substr(idot, inname.size());
    }
    if (ext != ".xml" && ext != ".ctml") {
        try {
            ctml::ct2ctml(inname.c_str(), debug);
        } catch (std::exception& err) {
            writelog("get_CTML_Tree: caught an exception:\n");
            writelog(err.what());
        }
        string ffull = inname.substr(0,idot) + ".xml";
        ff = "./" + getBaseName(ffull) + ".xml";
        if (debug > 0) {
            writelogf("ffull name = %s\n", ffull.c_str());
            writelogf("ff name = %s\n", ff.c_str());
        }
    } else {
        ff = inname;
    }
    writelog("Attempting to parse xml file " + ff + "\n", debug);
    ifstream fin(ff.c_str());
    if (!fin) {
        throw
        CanteraError("get_CTML_Tree",
                     "XML file " + ff + " not found");
    }
    rootPtr->build(fin);
    fin.close();
}

Cantera::XML_Node getCtmlTree(const std::string& file)
{
    Cantera::XML_Node root;
    get_CTML_Tree(&root, file);
    return root;
}

}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2022 Arm Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __BASE_MEMOIZER_HH__
#define __BASE_MEMOIZER_HH__

#include <functional>
#include <map>
#include <type_traits>
#include <utility>

namespace gem5
{

/**
 * This class takes a function as a constructor argument and memoizes
 * it: every time the function gets invoked through the Memoizer object
 * (see operator()), the result gets saved in the internal cache, ready
 * to be retrieved next time an invokation is made with the same
 * arguments.
 *
 * Example usage:
 *
 * int fibonacci(int n);
 *
 * Memoizer fibonacci_memo(fibonacci);
 * fibonacci_memo(5);
 *
 * Not every function is eligible for memoization. We explicitly
 * validate the memoizer at compile time and produce an error if the
 * function signature contains a reference.
 *
 * There are two ways to discard a memoization
 *
 * 1) Delete the Memoizer object
 * 2) Use the Memoizer::flush method
 *
 * In some cases there is little or no reason to discard a memoization
 * (like in the fibonacci example, where fibonacci(k) always returns
 * the same value for the same input k)
 * The memoizer could be used in more complex cases, where a change in
 * the global state affects the output of the function, which
 * effectively invalidates the cached results.
 * It is up to the client to understand when memoization is no longer
 * valid and to flush the result cache as a consequence.
 */
template <typename Ret, typename... Args>
class Memoizer
{
  public:
    using ret_type = Ret;
    using args_type = std::tuple<Args...>;

    constexpr Memoizer(Ret _func(Args...))
     : func(_func)
    {
        validateMemoizer();
    }

    Memoizer() = delete;
    Memoizer(const Memoizer &rhs) = delete;
    Memoizer& operator=(const Memoizer &rhs) = delete;

    auto
    operator()(Args... args) const
    {
        auto key = std::make_tuple(args...);
        if (auto it = cache.find(key); it != cache.end()) {
            // Return the cached result
            return it->second;
        }
        auto [it, emplaced] = cache.emplace(key, func(args...));
        return it->second;
    };

    /** Clear the memoization cache */
    void flush() { cache.clear(); }

  protected:
    /** True if the passed value is cached, false otherwise */
    bool
    cached(args_type args) const
    {
        return cache.find(args) != cache.end();
    }

    /** Return the size of the memoizer cache */
    size_t
    cacheSize() const
    {
        return cache.size();
    }

  private:
    /**
     * Validate the memoizer and produce an error if the
     * function signature contains a reference.
     */
    constexpr void
    validateMemoizer()
    {
        constexpr size_t num_args = std::tuple_size_v<std::decay_t<args_type>>;
        iterateTupleArgs<size_t(0), num_args, size_t(1)>([&](auto i) {
            static_assert(!std::is_reference_v<
                typename std::tuple_element<
                    i.value, args_type>::type>);
        });
    }

    template <auto Start, auto End, auto Inc, class F>
    constexpr void
    iterateTupleArgs(F&& func)
    {
        if constexpr (Start < End) {
            func(std::integral_constant<decltype(Start), Start>());
            iterateTupleArgs<Start + Inc, End, Inc>(func);
        }
    }

  private:
    /** Memoized function */
    const std::function<Ret(Args...)> func;
    /** Result cache */
    mutable std::map<args_type, ret_type> cache;

};

} // namespace gem5

#endif // __BASE_MEMOIZER_HH__
<commit_msg>base: Fix -Werror=unused-variable in Memoizer<commit_after>/*
 * Copyright (c) 2022 Arm Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef __BASE_MEMOIZER_HH__
#define __BASE_MEMOIZER_HH__

#include <functional>
#include <map>
#include <type_traits>
#include <utility>

namespace gem5
{

/**
 * This class takes a function as a constructor argument and memoizes
 * it: every time the function gets invoked through the Memoizer object
 * (see operator()), the result gets saved in the internal cache, ready
 * to be retrieved next time an invokation is made with the same
 * arguments.
 *
 * Example usage:
 *
 * int fibonacci(int n);
 *
 * Memoizer fibonacci_memo(fibonacci);
 * fibonacci_memo(5);
 *
 * Not every function is eligible for memoization. We explicitly
 * validate the memoizer at compile time and produce an error if the
 * function signature contains a reference.
 *
 * There are two ways to discard a memoization
 *
 * 1) Delete the Memoizer object
 * 2) Use the Memoizer::flush method
 *
 * In some cases there is little or no reason to discard a memoization
 * (like in the fibonacci example, where fibonacci(k) always returns
 * the same value for the same input k)
 * The memoizer could be used in more complex cases, where a change in
 * the global state affects the output of the function, which
 * effectively invalidates the cached results.
 * It is up to the client to understand when memoization is no longer
 * valid and to flush the result cache as a consequence.
 */
template <typename Ret, typename... Args>
class Memoizer
{
  public:
    using ret_type = Ret;
    using args_type = std::tuple<Args...>;

    constexpr Memoizer(Ret _func(Args...))
     : func(_func)
    {
        validateMemoizer();
    }

    Memoizer() = delete;
    Memoizer(const Memoizer &rhs) = delete;
    Memoizer& operator=(const Memoizer &rhs) = delete;

    auto
    operator()(Args... args) const
    {
        auto key = std::make_tuple(args...);
        if (auto it = cache.find(key); it != cache.end()) {
            // Return the cached result
            return it->second;
        }
        auto emplaced = cache.emplace(key, func(args...));
        return emplaced.first->second;
    };

    /** Clear the memoization cache */
    void flush() { cache.clear(); }

  protected:
    /** True if the passed value is cached, false otherwise */
    bool
    cached(args_type args) const
    {
        return cache.find(args) != cache.end();
    }

    /** Return the size of the memoizer cache */
    size_t
    cacheSize() const
    {
        return cache.size();
    }

  private:
    /**
     * Validate the memoizer and produce an error if the
     * function signature contains a reference.
     */
    constexpr void
    validateMemoizer()
    {
        constexpr size_t num_args = std::tuple_size_v<std::decay_t<args_type>>;
        iterateTupleArgs<size_t(0), num_args, size_t(1)>([&](auto i) {
            static_assert(!std::is_reference_v<
                typename std::tuple_element<
                    i.value, args_type>::type>);
        });
    }

    template <auto Start, auto End, auto Inc, class F>
    constexpr void
    iterateTupleArgs(F&& func)
    {
        if constexpr (Start < End) {
            func(std::integral_constant<decltype(Start), Start>());
            iterateTupleArgs<Start + Inc, End, Inc>(func);
        }
    }

  private:
    /** Memoized function */
    const std::function<Ret(Args...)> func;
    /** Result cache */
    mutable std::map<args_type, ret_type> cache;

};

} // namespace gem5

#endif // __BASE_MEMOIZER_HH__
<|endoftext|>


<commit_before>#include "problemes.h"
#include "utilitaires.h"
#include "mp_nombre.h"
#include "premiers.h"
#include "puissance.h"
#include "arithmetiques.h"
#include "multidimension.h"

#include <cmath>

typedef long double nombre;
typedef std::vector<nombre> vecteur;
typedef std::pair<nombre, nombre> paire;
typedef std::tuple<nombre, nombre, nombre> point;

namespace
{
    nombre distance(nombre a, nombre b) {
        return std::sqrt(a*a + b*b);
    }

    // nombre distance(nombre a, nombre b, nombre c) {
    //     return std::sqrt(a*a + b*b + c*c);
    // }

    paire coordonnees_polaire(nombre a, nombre b, nombre c) {
        nombre lambda = std::atan2(a, b);
        nombre d = distance(a, b);
        nombre phi = std::atan2(d, c);

        return std::make_pair(phi, lambda);
    }

    paire coordonnees_polaire(const point & p) {
        nombre a,b,c;
        std::tie(a,b,c) = p;
        return coordonnees_polaire(a,b,c);
    }

    nombre angle(const paire & p1, const paire & p2) {
        nombre phi1 = p1.first;
        nombre phi2 = p2.first;

        nombre lambda1 = p1.second;
        nombre lambda2 = p2.second;

        return std::acos(std::sin(phi1)*std::sin(phi2) + std::cos(phi1)*std::cos(phi2)*std::cos(lambda2 - lambda1));
    }

    nombre angle_solide(const paire & p1, const paire & p2, const paire & p3) {
        nombre a = std::abs(angle(p1, p2));
        nombre b = std::abs(angle(p2, p3));
        nombre c = std::abs(angle(p3, p1));
        nombre s = (a + b + c) / 2;

        nombre e = std::tan(s / 2) * std::tan((s - a) / 2) * std::tan((s - b) / 2) * std::tan((s - b) / 2);
        return 4 * std::atan(std::sqrt(e));
    }
}

ENREGISTRER_PROBLEME(332, "Spherical triangles")
{
    // A spherical triangle is a figure formed on the surface of a sphere by three great circular arcs
    // intersecting pairwise in three vertices.
    //
    // p332_spherical.jpg
    // Let C(r) be the sphere with the centre (0,0,0) and radius r.
    // Let Z(r) be the set of points on the surface of C(r) with integer coordinates.
    // Let T(r) be the set of spherical triangles with vertices in Z(r). Degenerate spherical triangles,
    // formed by three points on the same great arc, are not included in T(r).
    // Let A(r) be the area of the smallest spherical triangle in T(r).
    //
    // For example A(14) is 3.294040 rounded to six decimal places.
    //
    // Find p332_sum.gif A(r). Give your answer rounded to six decimal places.
    nombre resultat = 0.0;

    std::vector<point> points;

    for (int a = 0; a < 15; ++a)
    for (int b = 0; b < 15; ++b)
    for (int c = 0; c < 15; ++c) {
        if (auto r = carre_parfait(a*a + b*b + c*c)) {
            if (*r == 14) {
                points.push_back(std::make_tuple(a,b,c));
                // points.push_back(std::make_tuple(-a,b,c));
                // points.push_back(std::make_tuple(a,-b,c));
                // points.push_back(std::make_tuple(a,b,-c));
                // // points.push_back(std::make_tuple(a,b,c));
            }
        }
    }

    std::cout << points << std::endl;

    for (auto it1 = points.begin(), en = points.end(); it1 != en; ++it1) {
        auto p1 = coordonnees_polaire(*it1);
        for (auto it2 = std::next(it1); it2 != en; ++it2) {
            auto p2 = coordonnees_polaire(*it2);
            for (auto it3 = std::next(it1); it3 != en; ++it3) {
                auto p3 = coordonnees_polaire(*it3);
                nombre aire = 14*14*angle_solide(p1, p2, p3);
                if (aire > 0) {
                    std::cout << std::make_tuple(*it1, *it2, *it3) << " = " << aire << std::endl;
                }
            }
        }
    }

    return std::to_string(resultat);
}
<commit_msg>Added 332<commit_after>#include "problemes.h"
#include "utilitaires.h"
#include "arithmetiques.h"

#include <cmath>
#include <limits>

namespace {
    typedef std::tuple<long, long, long> point;

    bool plan(const point &p1, const point &p2, const point &p3) {
        long x1, x2, x3, y1, y2, y3, z1, z2, z3;
        std::tie(x1, y1, z1) = p1;
        std::tie(x2, y2, z2) = p2;
        std::tie(x3, y3, z3) = p3;
        return x1 * y2 * z3 + y1 * z2 * x3 + z1 * x2 * y3
             - z1 * y2 * x3 - y1 * x2 * z3 - x1 * z2 * y3 != 0;
    }

    long double angle(const long r, const point &p1, const point &p2) {
        long x1, x2, y1, y2, z1, z2;
        std::tie(x1, y1, z1) = p1;
        std::tie(x2, y2, z2) = p2;
        const long double m = x1 * x2 + y1 * y2 + z1 * z2;
        return std::acos(m / (1.0L * r * r));
    }

    // long double angle(const paire & p1, const paire & p2) {
    //     long double phi1 = p1.first;
    //     long double phi2 = p2.first;
    //
    //     long double lambda1 = p1.second;
    //     long double lambda2 = p2.second;
    //
    //     return std::acos(std::sin(phi1)*std::sin(phi2)
    //                     + std::cos(phi1)*std::cos(phi2)*std::cos(lambda2 - lambda1));
    // }

    long double angle_solide(const long double a, const long double b, const long double c) {
        long double s = (a + b + c) / 2;
        long double e = std::tan(s / 2) * std::tan((s - a) / 2) * std::tan((s - b) / 2) * std::tan((s - c) / 2);
        return 4 * std::atan(std::sqrt(e));
    }
}

ENREGISTRER_PROBLEME(332, "Spherical triangles")
{
    // A spherical triangle is a figure formed on the surface of a sphere by three great circular arcs
    // intersecting pairwise in three vertices.
    //
    // p332_spherical.jpg
    // Let C(r) be the sphere with the centre (0,0,0) and radius r.
    // Let Z(r) be the set of points on the surface of C(r) with integer coordinates.
    // Let T(r) be the set of spherical triangles with vertices in Z(r). Degenerate spherical triangles,
    // formed by three points on the same great arc, are not included in T(r).
    // Let A(r) be the area of the smallest spherical triangle in T(r).
    //
    // For example A(14) is 3.294040 rounded to six decimal places.
    //
    // Find p332_sum.gif A(r). Give your answer rounded to six decimal places.
    const long R = 50;
    std::map<long, std::vector<point>> sphere;

    for (long xx = -R; xx <= R; ++xx)
    for (long yy = -R; yy <= R; ++yy)
    for (long zz = -R; zz <= R; ++zz) {
        if (auto r = carre_parfait(xx * xx + yy * yy + zz * zz)) {
            sphere[*r].emplace_back(xx, yy, zz);
        }
    }

    long double resultat = 0;
    for (long r = 1; r <= R; ++r) {
        const auto &points = sphere[r];
        long double optimum = std::numeric_limits<long double>::max();
        for (auto it1 = points.begin(), en = points.end(); it1 != en; ++it1)
        for (auto it2 = it1 + 1; it2 != en; ++it2)
        for (auto it3 = it2 + 1; it3 != en; ++it3) {
            const auto &i = *it1;
            const auto &j = *it2;
            const auto &k = *it3;
            if (plan(i, j, k)) {
                long double a = angle(r, i, j);
                long double b = angle(r, j, k);
                long double c = angle(r, k, i);
                long double e = angle_solide(a,b,c);
                long double aire = r * r * e;
                optimum = std::min(optimum, aire);
            }
        }
        // std::cout << r << ' ' << points.size() << ": " << optimum << std::endl;
        resultat += optimum;
    }
    return std::to_string(resultat, 6);
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <QHostAddress>
#include <QtEndian>

#include <qmdnsengine/bitmap.h>
#include <qmdnsengine/dns.h>
#include <qmdnsengine/message.h>
#include <qmdnsengine/query.h>
#include <qmdnsengine/record.h>

namespace QMdnsEngine
{

template<class T>
bool parseInteger(const QByteArray &packet, quint16 &offset, T &value)
{
    if (offset + sizeof(T) > static_cast<unsigned int>(packet.length())) {
        return false;  // out-of-bounds
    }
    value = qFromBigEndian<T>(reinterpret_cast<const uchar*>(packet.constData() + offset));
    offset += sizeof(T);
    return true;
}

template<class T>
void writeInteger(QByteArray &packet, quint16 &offset, T value)
{
    value = qToBigEndian<T>(value);
    packet.append(reinterpret_cast<const char*>(&value), sizeof(T));
    offset += sizeof(T);
}

bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name)
{
    quint16 offsetEnd = 0;
    quint16 offsetPtr = offset;
    forever {
        quint8 nBytes;
        if (!parseInteger<quint8>(packet, offset, nBytes)) {
            return false;
        }
        if (!nBytes) {
            break;
        }
        switch (nBytes & 0xc0) {
        case 0x00:
            if (offset + nBytes > packet.length()) {
                return false;  // length exceeds message
            }
            name.append(packet.mid(offset, nBytes));
            name.append('.');
            offset += nBytes;
            break;
        case 0xc0:
        {
            quint8 nBytes2;
            quint16 newOffset;
            if (!parseInteger<quint8>(packet, offset, nBytes2)) {
                return false;
            }
            newOffset = ((nBytes & ~0xc0) << 8) | nBytes2;
            if (newOffset >= offsetPtr) {
                return false;  // prevent infinite loop
            }
            offsetPtr = newOffset;
            if (!offsetEnd) {
                offsetEnd = offset;
            }
            offset = newOffset;
            break;
        }
        default:
            return false;  // no other types supported
        }
    }
    if (offsetEnd) {
        offset = offsetEnd;
    }
    return true;
}

void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap<QByteArray, quint16> &nameMap)
{
    QByteArray fragment = name;
    if (fragment.endsWith('.')) {
        fragment.chop(1);
    }
    while (fragment.length()) {
        if (nameMap.contains(fragment)) {
            writeInteger<quint16>(packet, offset, nameMap.value(fragment) | 0xc000);
            return;
        }
        nameMap.insert(fragment, offset);
        int index = fragment.indexOf('.');
        if (index == -1) {
            index = fragment.length();
        }
        writeInteger<quint8>(packet, offset, index);
        packet.append(fragment.left(index));
        offset += index;
        fragment.remove(0, index + 1);
    }
    writeInteger<quint8>(packet, offset, 0);
}

bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record)
{
    QByteArray name;
    quint16 type, class_, dataLen;
    quint32 ttl;
    if (!parseName(packet, offset, name) ||
            !parseInteger<quint16>(packet, offset, type) ||
            !parseInteger<quint16>(packet, offset, class_) ||
            !parseInteger<quint32>(packet, offset, ttl) ||
            !parseInteger<quint16>(packet, offset, dataLen)) {
        return false;
    }
    record.setName(name);
    record.setType(type);
    record.setFlushCache(class_ & 0x8000);
    record.setTtl(ttl);
    switch (type) {
    case A:
    {
        quint32 ipv4Addr;
        if (!parseInteger<quint32>(packet, offset, ipv4Addr)) {
            return false;
        }
        record.setAddress(QHostAddress(ipv4Addr));
        break;
    }
    case AAAA:
    {
        if (offset + 16 > packet.length()) {
            return false;
        }
        record.setAddress(QHostAddress(
            reinterpret_cast<const quint8*>(packet.constData() + offset)
        ));
        offset += 16;
        break;
    }
    case NSEC:
    {
        QByteArray nextDomainName;
        quint8 number;
        quint8 length;
        if (!parseName(packet, offset, nextDomainName) ||
                !parseInteger<quint8>(packet, offset, number) ||
                !parseInteger<quint8>(packet, offset, length) ||
                number != 0 ||
                offset + length > packet.length()) {
            return false;
        }
        Bitmap bitmap;
        bitmap.setData(length, reinterpret_cast<const quint8*>(packet.constData() + offset));
        record.setNextDomainName(nextDomainName);
        record.setBitmap(bitmap);
        break;
    }
    case PTR:
    {
        QByteArray target;
        if (!parseName(packet, offset, target)) {
            return false;
        }
        record.setTarget(target);
        break;
    }
    case SRV:
    {
        quint16 priority, weight, port;
        QByteArray target;
        if (!parseInteger<quint16>(packet, offset, priority) ||
                !parseInteger<quint16>(packet, offset, weight) ||
                !parseInteger<quint16>(packet, offset, port) ||
                !parseName(packet, offset, target)) {
            return false;
        }
        record.setPriority(priority);
        record.setWeight(weight);
        record.setPort(port);
        record.setTarget(target);
        break;
    }
    case TXT:
    {
        quint16 start = offset;
        while (offset < start + dataLen) {
            quint8 nBytes;
            if (!parseInteger<quint8>(packet, offset, nBytes) ||
                    offset + nBytes > packet.length()) {
                return false;
            }
            if (nBytes == 0) {
                break;
            }
            QByteArray attr(packet.constData() + offset, nBytes);
            offset += nBytes;
            int splitIndex = attr.indexOf('=');
            if (splitIndex == -1) {
                record.addAttribute(attr, QByteArray());
            } else {
                record.addAttribute(attr.left(splitIndex), attr.mid(splitIndex + 1));
            }
        }
        break;
    }
    default:
        offset += dataLen;
        break;
    }
    return true;
}

void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap<QByteArray, quint16> &nameMap)
{
    writeName(packet, offset, record.name(), nameMap);
    writeInteger<quint16>(packet, offset, record.type());
    writeInteger<quint16>(packet, offset, record.flushCache() ? 0x8001 : 1);
    writeInteger<quint32>(packet, offset, record.ttl());
    offset += 2;
    QByteArray data;
    switch (record.type()) {
    case A:
        writeInteger<quint32>(data, offset, record.address().toIPv4Address());
        break;
    case AAAA:
    {
        Q_IPV6ADDR ipv6Addr = record.address().toIPv6Address();
        data.append(reinterpret_cast<const char*>(&ipv6Addr), sizeof(Q_IPV6ADDR));
        offset += data.length();
        break;
    }
    case NSEC:
    {
        quint8 length = record.bitmap().length();
        writeName(data, offset, record.nextDomainName(), nameMap);
        writeInteger<quint8>(data, offset, 0);
        writeInteger<quint8>(data, offset, length);
        data.append(reinterpret_cast<const char*>(record.bitmap().data()), length);
        offset += length;
        break;
    }
    case PTR:
        writeName(data, offset, record.target(), nameMap);
        break;
    case SRV:
        writeInteger<quint16>(data, offset, record.priority());
        writeInteger<quint16>(data, offset, record.weight());
        writeInteger<quint16>(data, offset, record.port());
        writeName(data, offset, record.target(), nameMap);
        break;
    case TXT:
        if (!record.attributes().count()) {
            writeInteger<quint8>(data, offset, 0);
            break;
        }
        for (auto i = record.attributes().constBegin(); i != record.attributes().constEnd(); ++i) {
            QByteArray entry = i.value().isNull() ? i.key() : i.key() + "=" + i.value();
            writeInteger<quint8>(data, offset, entry.length());
            data.append(entry);
            offset += entry.length();
        }
        break;
    default:
        break;
    }
    offset -= 2;
    writeInteger<quint16>(packet, offset, data.length());
    packet.append(data);
}

bool fromPacket(const QByteArray &packet, Message &message)
{
    quint16 offset = 0;
    quint16 transactionId, flags, nQuestion, nAnswer, nAuthority, nAdditional;
    if (!parseInteger<quint16>(packet, offset, transactionId) ||
            !parseInteger<quint16>(packet, offset, flags) ||
            !parseInteger<quint16>(packet, offset, nQuestion) ||
            !parseInteger<quint16>(packet, offset, nAnswer) ||
            !parseInteger<quint16>(packet, offset, nAuthority) ||
            !parseInteger<quint16>(packet, offset, nAdditional)) {
        return false;
    }
    message.setTransactionId(transactionId);
    message.setResponse(flags & 0x8400);
    for (int i = 0; i < nQuestion; ++i) {
        QByteArray name;
        quint16 type, class_;
        if (!parseName(packet, offset, name) ||
                !parseInteger<quint16>(packet, offset, type) ||
                !parseInteger<quint16>(packet, offset, class_)) {
            return false;
        }
        Query query;
        query.setName(name);
        query.setType(type);
        query.setUnicastResponse(class_ & 0x8000);
        message.addQuery(query);
    }
    quint16 nRecord = nAnswer + nAuthority + nAdditional;
    for (int i = 0; i < nRecord; ++i) {
        Record record;
        if (!parseRecord(packet, offset, record)) {
            return false;
        }
        message.addRecord(record);
    }
    return true;
}

void toPacket(const Message &message, QByteArray &packet)
{
    quint16 offset = 0;
    writeInteger<quint16>(packet, offset, message.transactionId());
    writeInteger<quint16>(packet, offset, message.isResponse() ? 0x8400 : 0);
    writeInteger<quint16>(packet, offset, message.queries().length());
    writeInteger<quint16>(packet, offset, message.records().length());
    writeInteger<quint16>(packet, offset, 0);
    writeInteger<quint16>(packet, offset, 0);
    QMap<QByteArray, quint16> nameMap;
    foreach (Query query, message.queries()) {
        writeName(packet, offset, query.name(), nameMap);
        writeInteger<quint16>(packet, offset, query.type());
        writeInteger<quint16>(packet, offset, query.unicastResponse() ? 0x8001 : 1);
    }
    foreach (Record record, message.records()) {
        writeRecord(packet, offset, record, nameMap);
    }
}

}
<commit_msg>Fix bug with parsing NSEC records.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Nathan Osman
 *
 * 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 <QHostAddress>
#include <QtEndian>

#include <qmdnsengine/bitmap.h>
#include <qmdnsengine/dns.h>
#include <qmdnsengine/message.h>
#include <qmdnsengine/query.h>
#include <qmdnsengine/record.h>

namespace QMdnsEngine
{

template<class T>
bool parseInteger(const QByteArray &packet, quint16 &offset, T &value)
{
    if (offset + sizeof(T) > static_cast<unsigned int>(packet.length())) {
        return false;  // out-of-bounds
    }
    value = qFromBigEndian<T>(reinterpret_cast<const uchar*>(packet.constData() + offset));
    offset += sizeof(T);
    return true;
}

template<class T>
void writeInteger(QByteArray &packet, quint16 &offset, T value)
{
    value = qToBigEndian<T>(value);
    packet.append(reinterpret_cast<const char*>(&value), sizeof(T));
    offset += sizeof(T);
}

bool parseName(const QByteArray &packet, quint16 &offset, QByteArray &name)
{
    quint16 offsetEnd = 0;
    quint16 offsetPtr = offset;
    forever {
        quint8 nBytes;
        if (!parseInteger<quint8>(packet, offset, nBytes)) {
            return false;
        }
        if (!nBytes) {
            break;
        }
        switch (nBytes & 0xc0) {
        case 0x00:
            if (offset + nBytes > packet.length()) {
                return false;  // length exceeds message
            }
            name.append(packet.mid(offset, nBytes));
            name.append('.');
            offset += nBytes;
            break;
        case 0xc0:
        {
            quint8 nBytes2;
            quint16 newOffset;
            if (!parseInteger<quint8>(packet, offset, nBytes2)) {
                return false;
            }
            newOffset = ((nBytes & ~0xc0) << 8) | nBytes2;
            if (newOffset >= offsetPtr) {
                return false;  // prevent infinite loop
            }
            offsetPtr = newOffset;
            if (!offsetEnd) {
                offsetEnd = offset;
            }
            offset = newOffset;
            break;
        }
        default:
            return false;  // no other types supported
        }
    }
    if (offsetEnd) {
        offset = offsetEnd;
    }
    return true;
}

void writeName(QByteArray &packet, quint16 &offset, const QByteArray &name, QMap<QByteArray, quint16> &nameMap)
{
    QByteArray fragment = name;
    if (fragment.endsWith('.')) {
        fragment.chop(1);
    }
    while (fragment.length()) {
        if (nameMap.contains(fragment)) {
            writeInteger<quint16>(packet, offset, nameMap.value(fragment) | 0xc000);
            return;
        }
        nameMap.insert(fragment, offset);
        int index = fragment.indexOf('.');
        if (index == -1) {
            index = fragment.length();
        }
        writeInteger<quint8>(packet, offset, index);
        packet.append(fragment.left(index));
        offset += index;
        fragment.remove(0, index + 1);
    }
    writeInteger<quint8>(packet, offset, 0);
}

bool parseRecord(const QByteArray &packet, quint16 &offset, Record &record)
{
    QByteArray name;
    quint16 type, class_, dataLen;
    quint32 ttl;
    if (!parseName(packet, offset, name) ||
            !parseInteger<quint16>(packet, offset, type) ||
            !parseInteger<quint16>(packet, offset, class_) ||
            !parseInteger<quint32>(packet, offset, ttl) ||
            !parseInteger<quint16>(packet, offset, dataLen)) {
        return false;
    }
    record.setName(name);
    record.setType(type);
    record.setFlushCache(class_ & 0x8000);
    record.setTtl(ttl);
    switch (type) {
    case A:
    {
        quint32 ipv4Addr;
        if (!parseInteger<quint32>(packet, offset, ipv4Addr)) {
            return false;
        }
        record.setAddress(QHostAddress(ipv4Addr));
        break;
    }
    case AAAA:
    {
        if (offset + 16 > packet.length()) {
            return false;
        }
        record.setAddress(QHostAddress(
            reinterpret_cast<const quint8*>(packet.constData() + offset)
        ));
        offset += 16;
        break;
    }
    case NSEC:
    {
        QByteArray nextDomainName;
        quint8 number;
        quint8 length;
        if (!parseName(packet, offset, nextDomainName) ||
                !parseInteger<quint8>(packet, offset, number) ||
                !parseInteger<quint8>(packet, offset, length) ||
                number != 0 ||
                offset + length > packet.length()) {
            return false;
        }
        Bitmap bitmap;
        bitmap.setData(length, reinterpret_cast<const quint8*>(packet.constData() + offset));
        record.setNextDomainName(nextDomainName);
        record.setBitmap(bitmap);
        offset += length;
        break;
    }
    case PTR:
    {
        QByteArray target;
        if (!parseName(packet, offset, target)) {
            return false;
        }
        record.setTarget(target);
        break;
    }
    case SRV:
    {
        quint16 priority, weight, port;
        QByteArray target;
        if (!parseInteger<quint16>(packet, offset, priority) ||
                !parseInteger<quint16>(packet, offset, weight) ||
                !parseInteger<quint16>(packet, offset, port) ||
                !parseName(packet, offset, target)) {
            return false;
        }
        record.setPriority(priority);
        record.setWeight(weight);
        record.setPort(port);
        record.setTarget(target);
        break;
    }
    case TXT:
    {
        quint16 start = offset;
        while (offset < start + dataLen) {
            quint8 nBytes;
            if (!parseInteger<quint8>(packet, offset, nBytes) ||
                    offset + nBytes > packet.length()) {
                return false;
            }
            if (nBytes == 0) {
                break;
            }
            QByteArray attr(packet.constData() + offset, nBytes);
            offset += nBytes;
            int splitIndex = attr.indexOf('=');
            if (splitIndex == -1) {
                record.addAttribute(attr, QByteArray());
            } else {
                record.addAttribute(attr.left(splitIndex), attr.mid(splitIndex + 1));
            }
        }
        break;
    }
    default:
        offset += dataLen;
        break;
    }
    return true;
}

void writeRecord(QByteArray &packet, quint16 &offset, Record &record, QMap<QByteArray, quint16> &nameMap)
{
    writeName(packet, offset, record.name(), nameMap);
    writeInteger<quint16>(packet, offset, record.type());
    writeInteger<quint16>(packet, offset, record.flushCache() ? 0x8001 : 1);
    writeInteger<quint32>(packet, offset, record.ttl());
    offset += 2;
    QByteArray data;
    switch (record.type()) {
    case A:
        writeInteger<quint32>(data, offset, record.address().toIPv4Address());
        break;
    case AAAA:
    {
        Q_IPV6ADDR ipv6Addr = record.address().toIPv6Address();
        data.append(reinterpret_cast<const char*>(&ipv6Addr), sizeof(Q_IPV6ADDR));
        offset += data.length();
        break;
    }
    case NSEC:
    {
        quint8 length = record.bitmap().length();
        writeName(data, offset, record.nextDomainName(), nameMap);
        writeInteger<quint8>(data, offset, 0);
        writeInteger<quint8>(data, offset, length);
        data.append(reinterpret_cast<const char*>(record.bitmap().data()), length);
        offset += length;
        break;
    }
    case PTR:
        writeName(data, offset, record.target(), nameMap);
        break;
    case SRV:
        writeInteger<quint16>(data, offset, record.priority());
        writeInteger<quint16>(data, offset, record.weight());
        writeInteger<quint16>(data, offset, record.port());
        writeName(data, offset, record.target(), nameMap);
        break;
    case TXT:
        if (!record.attributes().count()) {
            writeInteger<quint8>(data, offset, 0);
            break;
        }
        for (auto i = record.attributes().constBegin(); i != record.attributes().constEnd(); ++i) {
            QByteArray entry = i.value().isNull() ? i.key() : i.key() + "=" + i.value();
            writeInteger<quint8>(data, offset, entry.length());
            data.append(entry);
            offset += entry.length();
        }
        break;
    default:
        break;
    }
    offset -= 2;
    writeInteger<quint16>(packet, offset, data.length());
    packet.append(data);
}

bool fromPacket(const QByteArray &packet, Message &message)
{
    quint16 offset = 0;
    quint16 transactionId, flags, nQuestion, nAnswer, nAuthority, nAdditional;
    if (!parseInteger<quint16>(packet, offset, transactionId) ||
            !parseInteger<quint16>(packet, offset, flags) ||
            !parseInteger<quint16>(packet, offset, nQuestion) ||
            !parseInteger<quint16>(packet, offset, nAnswer) ||
            !parseInteger<quint16>(packet, offset, nAuthority) ||
            !parseInteger<quint16>(packet, offset, nAdditional)) {
        return false;
    }
    message.setTransactionId(transactionId);
    message.setResponse(flags & 0x8400);
    for (int i = 0; i < nQuestion; ++i) {
        QByteArray name;
        quint16 type, class_;
        if (!parseName(packet, offset, name) ||
                !parseInteger<quint16>(packet, offset, type) ||
                !parseInteger<quint16>(packet, offset, class_)) {
            return false;
        }
        Query query;
        query.setName(name);
        query.setType(type);
        query.setUnicastResponse(class_ & 0x8000);
        message.addQuery(query);
    }
    quint16 nRecord = nAnswer + nAuthority + nAdditional;
    for (int i = 0; i < nRecord; ++i) {
        Record record;
        if (!parseRecord(packet, offset, record)) {
            return false;
        }
        message.addRecord(record);
    }
    return true;
}

void toPacket(const Message &message, QByteArray &packet)
{
    quint16 offset = 0;
    writeInteger<quint16>(packet, offset, message.transactionId());
    writeInteger<quint16>(packet, offset, message.isResponse() ? 0x8400 : 0);
    writeInteger<quint16>(packet, offset, message.queries().length());
    writeInteger<quint16>(packet, offset, message.records().length());
    writeInteger<quint16>(packet, offset, 0);
    writeInteger<quint16>(packet, offset, 0);
    QMap<QByteArray, quint16> nameMap;
    foreach (Query query, message.queries()) {
        writeName(packet, offset, query.name(), nameMap);
        writeInteger<quint16>(packet, offset, query.type());
        writeInteger<quint16>(packet, offset, query.unicastResponse() ? 0x8001 : 1);
    }
    foreach (Record record, message.records()) {
        writeRecord(packet, offset, record, nameMap);
    }
}

}
<|endoftext|>


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

#include "logging/logger.h"
#include "parser/io/ptb_reader.h"
#include "parser/trees/visitors/annotation_remover.h"
#include "parser/trees/visitors/empty_remover.h"
#include "parser/trees/visitors/unary_chain_remover.h"
#include "parser/trees/visitors/multi_transformer.h"
#include "parser/trees/visitors/head_finder.h"
#include "parser/trees/visitors/binarizer.h"
#include "parser/trees/visitors/debinarizer.h"

using namespace meta;

struct annotation_checker : public parser::const_visitor<bool>
{
    bool operator()(const parser::leaf_node&) override
    {
        return true;
    }

    bool operator()(const parser::internal_node& inode) override
    {
        if (!inode.head_constituent())
        {
            std::cout << "Node missing head: " << inode.category() << std::endl;
            return false;
        }

        if (!inode.head_lexicon())
        {
            std::cout << "Node missing head lex: " << inode.category()
                      << std::endl;
            return false;
        }

        bool res = true;
        inode.each_child([&](const parser::node* child)
                         {
                             res = res && child->accept(*this);
                         });
        return res;
    }
};

struct binary_checker : public parser::const_visitor<bool>
{
    bool operator()(const parser::leaf_node&) override
    {
        return true;
    }

    bool operator()(const parser::internal_node& inode) override
    {
        if (inode.num_children() > 2)
            return false;

        bool res = true;
        inode.each_child([&](const parser::node* child)
                         {
                             res = res && child->accept(*this);
                         });
        return res;
    }
};

int main(int argc, char** argv)
{

    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0]
                  << " [options...] trees.mrg [trees2.mrg...]" << std::endl;

        std::cerr << "\noptions:\n\t--print: print the trees while parsing"
                  << std::endl;
        return 1;
    }

    std::vector<std::string> args{argv, argv + argc};
    bool print = std::find(args.begin(), args.end(), "--print") != args.end();

    logging::set_cerr_logging();

    parser::multi_transformer<parser::annotation_remover, parser::empty_remover,
                              parser::unary_chain_remover> transformer;

    parser::head_finder hf;
    annotation_checker ann_check;
    binary_checker bin_check;
    parser::binarizer bin;
    parser::debinarizer debin;

    for (size_t arg = 1; arg < args.size(); ++arg)
    {
        if (args[arg] == "--print")
            continue;
        std::cout << "Parsing: " << args[arg] << "..." << std::endl;
        auto orig_trees = parser::io::extract_trees(args[arg]);
        auto mod_trees = parser::io::extract_trees(args[arg]);
        for (size_t i = 0; i < orig_trees.size(); ++i)
        {
            parser::parse_tree t{std::move(mod_trees[i])};
            if (print)
            {
                std::cout << "Original: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            t.transform(transformer);
            if (print)
            {
                std::cout << "Transformed: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            t.visit(hf);
            if (!t.visit(ann_check))
                throw std::runtime_error{"Failed to fully annotate heads"};
            t.transform(bin);
            if (!t.visit(bin_check))
                throw std::runtime_error{
                    "Binarizer failed to fully binarize the tree"};
            if (!t.visit(ann_check))
                throw std::runtime_error{
                    "Binarized tree missing head annotations"};

            if (print)
            {
                std::cout << "Binarized: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            t.transform(debin);
            if (print)
            {
                std::cout << "Debinarized: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            orig_trees[i].transform(transformer);
            if (!(t == orig_trees[i]))
                throw std::runtime_error{"Debinarization failed"};
        }
    }
    return 0;
}
<commit_msg>Print the binarized tree before checking for exceptions in read-trees.<commit_after>#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

#include "logging/logger.h"
#include "parser/io/ptb_reader.h"
#include "parser/trees/visitors/annotation_remover.h"
#include "parser/trees/visitors/empty_remover.h"
#include "parser/trees/visitors/unary_chain_remover.h"
#include "parser/trees/visitors/multi_transformer.h"
#include "parser/trees/visitors/head_finder.h"
#include "parser/trees/visitors/binarizer.h"
#include "parser/trees/visitors/debinarizer.h"

using namespace meta;

struct annotation_checker : public parser::const_visitor<bool>
{
    bool operator()(const parser::leaf_node&) override
    {
        return true;
    }

    bool operator()(const parser::internal_node& inode) override
    {
        if (!inode.head_constituent())
        {
            std::cout << "Node missing head: " << inode.category() << std::endl;
            return false;
        }

        if (!inode.head_lexicon())
        {
            std::cout << "Node missing head lex: " << inode.category()
                      << std::endl;
            return false;
        }

        bool res = true;
        inode.each_child([&](const parser::node* child)
                         {
                             res = res && child->accept(*this);
                         });
        return res;
    }
};

struct binary_checker : public parser::const_visitor<bool>
{
    bool operator()(const parser::leaf_node&) override
    {
        return true;
    }

    bool operator()(const parser::internal_node& inode) override
    {
        if (inode.num_children() > 2)
            return false;

        bool res = true;
        inode.each_child([&](const parser::node* child)
                         {
                             res = res && child->accept(*this);
                         });
        return res;
    }
};

int main(int argc, char** argv)
{

    if (argc < 3)
    {
        std::cerr << "Usage: " << argv[0]
                  << " [options...] trees.mrg [trees2.mrg...]" << std::endl;

        std::cerr << "\noptions:\n\t--print: print the trees while parsing"
                  << std::endl;
        return 1;
    }

    std::vector<std::string> args{argv, argv + argc};
    bool print = std::find(args.begin(), args.end(), "--print") != args.end();

    logging::set_cerr_logging();

    parser::multi_transformer<parser::annotation_remover, parser::empty_remover,
                              parser::unary_chain_remover> transformer;

    parser::head_finder hf;
    annotation_checker ann_check;
    binary_checker bin_check;
    parser::binarizer bin;
    parser::debinarizer debin;

    for (size_t arg = 1; arg < args.size(); ++arg)
    {
        if (args[arg] == "--print")
            continue;
        std::cout << "Parsing: " << args[arg] << "..." << std::endl;
        auto orig_trees = parser::io::extract_trees(args[arg]);
        auto mod_trees = parser::io::extract_trees(args[arg]);
        for (size_t i = 0; i < orig_trees.size(); ++i)
        {
            parser::parse_tree t{std::move(mod_trees[i])};
            if (print)
            {
                std::cout << "Original: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            t.transform(transformer);
            if (print)
            {
                std::cout << "Transformed: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            t.visit(hf);
            if (!t.visit(ann_check))
                throw std::runtime_error{"Failed to fully annotate heads"};
            t.transform(bin);

            if (print)
            {
                std::cout << "Binarized: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            if (!t.visit(bin_check))
                throw std::runtime_error{
                    "Binarizer failed to fully binarize the tree"};
            if (!t.visit(ann_check))
                throw std::runtime_error{
                    "Binarized tree missing head annotations"};

            t.transform(debin);
            if (print)
            {
                std::cout << "Debinarized: " << std::endl;
                t.pretty_print(std::cout);
                std::cout << std::endl;
            }

            orig_trees[i].transform(transformer);
            if (!(t == orig_trees[i]))
                throw std::runtime_error{"Debinarization failed"};
        }
    }
    return 0;
}
<|endoftext|>


<commit_before>///
/// @file   popcount.cpp
/// @brief  Fast algorithm to count the number of 1 bits in an
///         array using only integer operations.
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <stdint.h>

namespace {

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// http://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
uint64_t popcount64(uint64_t x)
{
  const uint64_t m1 = 0x5555555555555555ull;
  const uint64_t m2 = 0x3333333333333333ull;
  const uint64_t m4 = 0x0F0F0F0F0F0F0F0Full;
  const uint64_t h01 = 0x0101010101010101ull;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;
  return (x * h01) >> 56;
}

/// Carry-save adder (CSA).
/// @see Chapter 5 in "Hacker's Delight".
///
void CSA(uint64_t& h, uint64_t& l, uint64_t a, uint64_t b, uint64_t c)
{
  uint64_t u = a ^ b;
  h = (a & b) | (u & c);
  l = u ^ c;
}

} // namespace

namespace primesieve {

/// Harley-Seal popcount (4th iteration).
/// The Harley-Seal popcount algorithm is one of the fastest algorithms
/// for counting 1 bits in an array using only integer operations.
/// This implementation uses only 5.69 instructions per 64-bit word.
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
uint64_t popcount(const uint64_t* array, uint64_t size)
{
  uint64_t total = 0;
  uint64_t ones = 0, twos = 0, fours = 0, eights = 0, sixteens = 0;
  uint64_t twosA, twosB, foursA, foursB, eightsA, eightsB;
  uint64_t limit = size - size % 16;
  uint64_t i = 0;

  for(; i < limit; i += 16)
  {
    CSA(twosA, ones, ones, array[i+0], array[i+1]);
    CSA(twosB, ones, ones, array[i+2], array[i+3]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, array[i+4], array[i+5]);
    CSA(twosB, ones, ones, array[i+6], array[i+7]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eightsA,fours, fours, foursA, foursB);
    CSA(twosA, ones, ones, array[i+8], array[i+9]);
    CSA(twosB, ones, ones, array[i+10], array[i+11]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, array[i+12], array[i+13]);
    CSA(twosB, ones, ones, array[i+14], array[i+15]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eightsB, fours, fours, foursA, foursB);
    CSA(sixteens, eights, eights, eightsA, eightsB);

    total += popcount64(sixteens);
  }

  total *= 16;
  total += 8 * popcount64(eights);
  total += 4 * popcount64(fours);
  total += 2 * popcount64(twos);
  total += 1 * popcount64(ones);

  for(; i < size; i++)
    total += popcount64(array[i]);

  return total;
}

} // namespace
<commit_msg>Update libprimesieve<commit_after>///
/// @file   popcount.cpp
/// @brief  Fast algorithm to count the number of 1 bits in an
///         array using only integer operations.
///
/// Copyright (C) 2018 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <stdint.h>

namespace {

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
uint64_t popcount64(uint64_t x)
{
  const uint64_t m1 = 0x5555555555555555ull;
  const uint64_t m2 = 0x3333333333333333ull;
  const uint64_t m4 = 0x0F0F0F0F0F0F0F0Full;
  const uint64_t h01 = 0x0101010101010101ull;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;
  return (x * h01) >> 56;
}

/// Carry-save adder (CSA).
/// @see Chapter 5 in "Hacker's Delight".
///
void CSA(uint64_t& h, uint64_t& l, uint64_t a, uint64_t b, uint64_t c)
{
  uint64_t u = a ^ b;
  h = (a & b) | (u & c);
  l = u ^ c;
}

} // namespace

namespace primesieve {

/// Harley-Seal popcount (4th iteration).
/// The Harley-Seal popcount algorithm is one of the fastest algorithms
/// for counting 1 bits in an array using only integer operations.
/// This implementation uses only 5.69 instructions per 64-bit word.
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
uint64_t popcount(const uint64_t* array, uint64_t size)
{
  uint64_t total = 0;
  uint64_t ones = 0, twos = 0, fours = 0, eights = 0, sixteens = 0;
  uint64_t twosA, twosB, foursA, foursB, eightsA, eightsB;
  uint64_t limit = size - size % 16;
  uint64_t i = 0;

  for(; i < limit; i += 16)
  {
    CSA(twosA, ones, ones, array[i+0], array[i+1]);
    CSA(twosB, ones, ones, array[i+2], array[i+3]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, array[i+4], array[i+5]);
    CSA(twosB, ones, ones, array[i+6], array[i+7]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eightsA,fours, fours, foursA, foursB);
    CSA(twosA, ones, ones, array[i+8], array[i+9]);
    CSA(twosB, ones, ones, array[i+10], array[i+11]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, array[i+12], array[i+13]);
    CSA(twosB, ones, ones, array[i+14], array[i+15]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eightsB, fours, fours, foursA, foursB);
    CSA(sixteens, eights, eights, eightsA, eightsB);

    total += popcount64(sixteens);
  }

  total *= 16;
  total += 8 * popcount64(eights);
  total += 4 * popcount64(fours);
  total += 2 * popcount64(twos);
  total += 1 * popcount64(ones);

  for(; i < size; i++)
    total += popcount64(array[i]);

  return total;
}

} // namespace
<|endoftext|>


<commit_before>/*
   fskit: a library for creating multi-threaded in-RAM filesystems
   Copyright (C) 2014  Jude Nelson

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation, either version 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 "read.h"
#include "route.h"

// run the user-given read route callback
// return the number of bytes read on success
// return negative on failure
ssize_t fskit_run_user_read( struct fskit_core* core, char const* path, struct fskit_entry* fent, char* buf, size_t buflen, off_t offset ) {

   int rc = 0;
   int cbrc = 0;
   struct fskit_route_dispatch_args dargs;

   fskit_route_io_args( &dargs, buf, buflen, offset );

   rc = fskit_route_call_read( core, path, fent, &dargs, &cbrc );

   if( rc == -EPERM || rc == -ENOSYS ) {
      // no routes, so EOF
      return 0;
   }

   return (ssize_t)cbrc;
}

// read up to buflen bytes into buf, starting at the given offset in the file.
// return the number of bytes read on success.
// return negative on failure.
ssize_t fskit_read( struct fskit_core* core, struct fskit_file_handle* fh, char* buf, size_t buflen, off_t offset ) {

   fskit_file_handle_rlock( fh );

   // sanity check
   if( (fh->flags & (O_RDWR | O_RDONLY)) == 0 ) {

      fskit_file_handle_unlock( fh );
      return -EBADF;
   }

   ssize_t num_read = fskit_run_user_read( core, fh->path, fh->fent, buf, buflen, offset );

   if( num_read >= 0 ) {

      // update metadata
      fskit_entry_wlock( fh->fent );

      fskit_entry_set_atime( fh->fent, NULL );

      fskit_entry_unlock( fh->fent );
   }

   fskit_file_handle_unlock( fh );

   return num_read;
}
<commit_msg>check for invalid handle by checking for the absence of O_WRONLY<commit_after>/*
   fskit: a library for creating multi-threaded in-RAM filesystems
   Copyright (C) 2014  Jude Nelson

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation, either version 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 "read.h"
#include "route.h"

// run the user-given read route callback
// return the number of bytes read on success
// return negative on failure
ssize_t fskit_run_user_read( struct fskit_core* core, char const* path, struct fskit_entry* fent, char* buf, size_t buflen, off_t offset ) {

   int rc = 0;
   int cbrc = 0;
   struct fskit_route_dispatch_args dargs;

   fskit_route_io_args( &dargs, buf, buflen, offset );

   rc = fskit_route_call_read( core, path, fent, &dargs, &cbrc );

   if( rc == -EPERM || rc == -ENOSYS ) {
      // no routes, so EOF
      return 0;
   }

   return (ssize_t)cbrc;
}

// read up to buflen bytes into buf, starting at the given offset in the file.
// return the number of bytes read on success.
// return negative on failure.
ssize_t fskit_read( struct fskit_core* core, struct fskit_file_handle* fh, char* buf, size_t buflen, off_t offset ) {

   fskit_file_handle_rlock( fh );

   // sanity check
   if( (fh->flags & O_WRONLY) != 0 ) {

      fskit_file_handle_unlock( fh );
      return -EBADF;
   }

   ssize_t num_read = fskit_run_user_read( core, fh->path, fh->fent, buf, buflen, offset );

   if( num_read >= 0 ) {

      // update metadata
      fskit_entry_wlock( fh->fent );

      fskit_entry_set_atime( fh->fent, NULL );

      fskit_entry_unlock( fh->fent );
   }

   fskit_file_handle_unlock( fh );

   return num_read;
}
<|endoftext|>