<commit_before>#ifndef SIGMA_ENGINE_JSON_CONVERSION_HPP
#define SIGMA_ENGINE_JSON_CONVERSION_HPP

#include <sigma/config.hpp>
#include <sigma/graphics/texture.hpp>
#include <sigma/handle.hpp>

#include <json/json.h>

#include <glm/gtc/quaternion.hpp>
#include <glm/mat2x2.hpp>
#include <glm/mat3x3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/filesystem/path.hpp>

namespace sigma {
namespace json {
    namespace detial {
        template <class>
        struct type_traits;
    }

    template <class T>
    bool from_json(const Json::Value& source, T& output)
    {
        return detial::type_traits<T>::from(source, output);
    }

    template <class T>
    void to_json(const T& source, Json::Value& output)
    {
        detial::type_traits<T>::to(source, output);
    }

    namespace detial {
        template <>
        struct type_traits<bool> {
            static bool from(const Json::Value& source, bool& output)
            {
                if (source.isConvertibleTo(Json::booleanValue)) {
                    output = source.asBool();
                    return true;
                }
                return false;
            }

            static void to(bool source, Json::Value& output)
            {
                output = source;
            }
        };

        template <>
        struct type_traits<int> {
            static bool from(const Json::Value& source, int& output)
            {
                if (source.isConvertibleTo(Json::intValue)) {
                    output = source.asInt();
                    return true;
                }
                return false;
            }

            static void to(int source, Json::Value& output)
            {
                output = source;
            }
        };
        template <>
        struct type_traits<unsigned long int> {
            static bool from(const Json::Value& source, unsigned long int& output)
            {
                if (source.isConvertibleTo(Json::uintValue)) {
                    output = source.asUInt64();
                    return true;
                }
                return false;
            }

            static void to(unsigned long int source, Json::Value& output)
            {
                output = (Json::UInt64)source;
            }
        };

        template <>
        struct type_traits<float> {
            static bool from(const Json::Value& source, float& output)
            {
                if (source.isConvertibleTo(Json::realValue)) {
                    output = float(source.asDouble());
                    return true;
                }
                return false;
            }

            static void to(float source, Json::Value& output)
            {
                output = source;
            }
        };

        template <>
        struct type_traits<glm::vec2> {
            static bool from(const Json::Value& source, glm::vec2& output)
            {
                // TODO support x,y
                return source.isArray() && source.size() == 2 && from_json(source[0], output.x) && from_json(source[1], output.y);
            }

            static void to(const glm::vec2& source, Json::Value& output)
            {
                // TODO support x,y
                output[0] = source.x;
                output[1] = source.y;
            }
        };

        template <>
        struct type_traits<glm::vec3> {
            static bool from(const Json::Value& source, glm::vec3& output)
            {
                // TODO support x,y,z
                return source.isArray() && source.size() == 3 && from_json(source[0], output.x) && from_json(source[1], output.y) && from_json(source[2], output.z);
            }

            static void to(const glm::vec3& source, Json::Value& output)
            {
                // TODO support x,y,z
                output[0] = source.x;
                output[1] = source.y;
                output[2] = source.z;
            }
        };

        template <>
        struct type_traits<glm::vec4> {
            static bool from(const Json::Value& source, glm::vec4& output)
            {
                // TODO support w,x,y,z
                return source.isArray() && source.size() == 4 && from_json(source[0], output.w) && from_json(source[1], output.x) && from_json(source[2], output.y) && from_json(source[3], output.z);
            }

            static void to(const glm::vec4& source, Json::Value& output)
            {
                // TODO support w,x,y,z
                output[0] = source.w;
                output[1] = source.x;
                output[2] = source.y;
                output[3] = source.z;
            }
        };

        template <>
        struct type_traits<glm::quat> {
            static bool from(const Json::Value& source, glm::quat& output)
            {
                if (source.isArray()) {
                    if (source.size() == 4) {
                        return from_json(source[0], output.w) && from_json(source[1], output.x) && from_json(source[2], output.y) && from_json(source[3], output.z);
                    } else if (source.size() == 3) {
                        glm::vec3 e;
                        if (from_json(source, e)) {
                            output = glm::quat{ glm::radians(e) };
                            return true;
                        }
                    }
                    return false;
                } else {
                    if (!source.isMember("x") || !source.isMember("y") || !source.isMember("z"))
                        return false;
                    if (source.isMember("w")) {
                        return from_json(source["w"], output.w) && from_json(source["x"], output.x) && from_json(source["y"], output.y) && from_json(source["z"], output.z);
                    } else {
                        glm::vec3 e;
                        if (from_json(source, e)) {
                            output = glm::quat{ glm::radians(e) };
                            return true;
                        }
                    }
                }
                return false;
            }

            static void to(const glm::quat& source, Json::Value& output)
            {
                // TODO support w,x,y,z
                output[0] = source.w;
                output[1] = source.x;
                output[2] = source.y;
                output[3] = source.z;
            }
        };

        template <>
        struct type_traits<glm::mat2> {
            static bool from(const Json::Value& source, glm::mat2& output)
            {
                return source.isArray() && source.size() == 2 && from_json(source[0], output[0]) && from_json(source[1], output[1]);
            }

            static void to(const glm::mat2& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
            }
        };

        template <>
        struct type_traits<glm::mat3> {
            static bool from(const Json::Value& source, glm::mat3& output)
            {
                return source.isArray() && source.size() == 3 && from_json(source[0], output[0]) && from_json(source[1], output[1]) && from_json(source[2], output[2]);
            }

            static void to(const glm::mat3& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
                to_json(source[2], output[2]);
            }
        };

        template <>
        struct type_traits<glm::mat4> {
            static bool from(const Json::Value& source, glm::mat4& output)
            {
                return source.isArray() && source.size() == 4 && from_json(source[0], output[0]) && from_json(source[1], output[1]) && from_json(source[2], output[2]) && from_json(source[3], output[3]);
            }

            static void to(const glm::mat4& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
                to_json(source[2], output[2]);
                to_json(source[3], output[3]);
            }
        };

        template <>
        struct type_traits<boost::filesystem::path> {
            static bool from(const Json::Value& source, boost::filesystem::path& output)
            {
                output = source.asString();
                return true;
            }

            static void to(const boost::filesystem::path& source, Json::Value& output)
            {
                output = source.string();
            }
        };

        template <>
        struct type_traits<handle> {
            static bool from(const Json::Value& source, handle& output)
            {
                if (source.isConvertibleTo(Json::uintValue)) {
                    output.index = source.asUInt();
                    output.version = 0; // TODO version
                    return true;
                }
                return false;
            }

            static void to(handle source, Json::Value& output)
            {
                output = source.index; // TODO version
            }
        };

        template <class T, long int N>
        struct type_traits<T[N]> {
            static bool from(const Json::Value& source, T* output)
            {
                for (long int i = 0; i < N; ++i) {
                    if (!from_json(source[(int)i], output[i]))
                        return false;
                }
                return true;
            }

            static void to(const T* source, Json::Value& output)
            {
                for (long int i = 0; i < N; ++i)
                    to_json(source[i], output[(int)i]);
            }
        };

        template <>
        struct type_traits<sigma::graphics::texture_filter> {
            static bool from(const Json::Value& source, sigma::graphics::texture_filter& output)
            {
                auto lower = boost::to_upper_copy(source.asString());
                if (lower == "LINEAR")
                    output = graphics::texture_filter::LINEAR;
                else if (lower == "NEAREST")
                    output = graphics::texture_filter::NEAREST;
                else if (lower == "NONE")
                    output = graphics::texture_filter::NONE;
                else
                    return false;

                return true;
            }

            static void to(const sigma::graphics::texture_filter& source, Json::Value& output)
            {
                switch (source) {
                case graphics::texture_filter::LINEAR:
                    output = "LINEAR";
                    break;
                case graphics::texture_filter::NEAREST:
                    output = "NEAREST";
                    break;
                case graphics::texture_filter::NONE:
                    output = "NONE";
                    break;
                }
            }
        };

        template <>
        struct type_traits<sigma::graphics::texture_format> {
            static bool from(const Json::Value& source, sigma::graphics::texture_format& output)
            {
                auto lower = boost::to_upper_copy(source.asString());
                if (lower == "RGB8")
                    output = sigma::graphics::texture_format::RGB8;
                else if (lower == "RGBA8")
                    output = sigma::graphics::texture_format::RGBA8;
                else if (lower == "RGB32F")
                    output = sigma::graphics::texture_format::RGB32F;
                else
                    return false;
                return true;
            }

            static void to(const sigma::graphics::texture_format& source, Json::Value& output)
            {
                switch (source) {
                case sigma::graphics::texture_format::RGB8:
                    output = "RGB8";
                    break;
                case sigma::graphics::texture_format::RGBA8:
                    output = "RGBA8";
                    break;
                case sigma::graphics::texture_format::RGB32F:
                    output = "RGB32F";
                    break;
                }
            }
        };
    }
}
}

#endif // SIGMA_ENGINE_JSON_CONVERSION_HPP
<commit_msg>Convert components and worlds from json.<commit_after>#ifndef SIGMA_ENGINE_JSON_CONVERSION_HPP
#define SIGMA_ENGINE_JSON_CONVERSION_HPP

#include <sigma/component.hpp>
#include <sigma/config.hpp>
#include <sigma/graphics/texture.hpp>
#include <sigma/handle.hpp>
#include <sigma/world.hpp>

#include <json/json.h>

#include <glm/gtc/quaternion.hpp>
#include <glm/mat2x2.hpp>
#include <glm/mat3x3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>

#include <boost/algorithm/string.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/hana/at_key.hpp>
#include <boost/hana/for_each.hpp>
#include <boost/hana/keys.hpp>

namespace sigma {
namespace json {
    namespace detial {
        template <class>
        struct type_traits;
    }

    template <class T>
    bool from_json(const Json::Value& source, T& output)
    {
        return detial::type_traits<T>::from(source, output);
    }

    template <class T>
    void to_json(const T& source, Json::Value& output)
    {
        detial::type_traits<T>::to(source, output);
    }

    namespace detial {
        template <>
        struct type_traits<bool> {
            static bool from(const Json::Value& source, bool& output)
            {
                if (source.isConvertibleTo(Json::booleanValue)) {
                    output = source.asBool();
                    return true;
                }
                return false;
            }

            static void to(bool source, Json::Value& output)
            {
                output = source;
            }
        };

        template <>
        struct type_traits<int> {
            static bool from(const Json::Value& source, int& output)
            {
                if (source.isConvertibleTo(Json::intValue)) {
                    output = source.asInt();
                    return true;
                }
                return false;
            }

            static void to(int source, Json::Value& output)
            {
                output = source;
            }
        };
        template <>
        struct type_traits<unsigned long int> {
            static bool from(const Json::Value& source, unsigned long int& output)
            {
                if (source.isConvertibleTo(Json::uintValue)) {
                    output = source.asUInt64();
                    return true;
                }
                return false;
            }

            static void to(unsigned long int source, Json::Value& output)
            {
                output = (Json::UInt64)source;
            }
        };

        template <>
        struct type_traits<float> {
            static bool from(const Json::Value& source, float& output)
            {
                if (source.isConvertibleTo(Json::realValue)) {
                    output = float(source.asDouble());
                    return true;
                }
                return false;
            }

            static void to(float source, Json::Value& output)
            {
                output = source;
            }
        };

        template <>
        struct type_traits<glm::vec2> {
            static bool from(const Json::Value& source, glm::vec2& output)
            {
                // TODO support x,y
                return source.isArray() && source.size() == 2 && from_json(source[0], output.x) && from_json(source[1], output.y);
            }

            static void to(const glm::vec2& source, Json::Value& output)
            {
                // TODO support x,y
                output[0] = source.x;
                output[1] = source.y;
            }
        };

        template <>
        struct type_traits<glm::vec3> {
            static bool from(const Json::Value& source, glm::vec3& output)
            {
                // TODO support x,y,z
                return source.isArray() && source.size() == 3 && from_json(source[0], output.x) && from_json(source[1], output.y) && from_json(source[2], output.z);
            }

            static void to(const glm::vec3& source, Json::Value& output)
            {
                // TODO support x,y,z
                output[0] = source.x;
                output[1] = source.y;
                output[2] = source.z;
            }
        };

        template <>
        struct type_traits<glm::vec4> {
            static bool from(const Json::Value& source, glm::vec4& output)
            {
                // TODO support w,x,y,z
                return source.isArray() && source.size() == 4 && from_json(source[0], output.w) && from_json(source[1], output.x) && from_json(source[2], output.y) && from_json(source[3], output.z);
            }

            static void to(const glm::vec4& source, Json::Value& output)
            {
                // TODO support w,x,y,z
                output[0] = source.w;
                output[1] = source.x;
                output[2] = source.y;
                output[3] = source.z;
            }
        };

        template <>
        struct type_traits<glm::quat> {
            static bool from(const Json::Value& source, glm::quat& output)
            {
                if (source.isArray()) {
                    if (source.size() == 4) {
                        return from_json(source[0], output.w) && from_json(source[1], output.x) && from_json(source[2], output.y) && from_json(source[3], output.z);
                    } else if (source.size() == 3) {
                        glm::vec3 e;
                        if (from_json(source, e)) {
                            output = glm::quat{ glm::radians(e) };
                            return true;
                        }
                    }
                    return false;
                } else {
                    if (!source.isMember("x") || !source.isMember("y") || !source.isMember("z"))
                        return false;
                    if (source.isMember("w")) {
                        return from_json(source["w"], output.w) && from_json(source["x"], output.x) && from_json(source["y"], output.y) && from_json(source["z"], output.z);
                    } else {
                        glm::vec3 e;
                        if (from_json(source, e)) {
                            output = glm::quat{ glm::radians(e) };
                            return true;
                        }
                    }
                }
                return false;
            }

            static void to(const glm::quat& source, Json::Value& output)
            {
                // TODO support w,x,y,z
                output[0] = source.w;
                output[1] = source.x;
                output[2] = source.y;
                output[3] = source.z;
            }
        };

        template <>
        struct type_traits<glm::mat2> {
            static bool from(const Json::Value& source, glm::mat2& output)
            {
                return source.isArray() && source.size() == 2 && from_json(source[0], output[0]) && from_json(source[1], output[1]);
            }

            static void to(const glm::mat2& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
            }
        };

        template <>
        struct type_traits<glm::mat3> {
            static bool from(const Json::Value& source, glm::mat3& output)
            {
                return source.isArray() && source.size() == 3 && from_json(source[0], output[0]) && from_json(source[1], output[1]) && from_json(source[2], output[2]);
            }

            static void to(const glm::mat3& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
                to_json(source[2], output[2]);
            }
        };

        template <>
        struct type_traits<glm::mat4> {
            static bool from(const Json::Value& source, glm::mat4& output)
            {
                return source.isArray() && source.size() == 4 && from_json(source[0], output[0]) && from_json(source[1], output[1]) && from_json(source[2], output[2]) && from_json(source[3], output[3]);
            }

            static void to(const glm::mat4& source, Json::Value& output)
            {
                to_json(source[0], output[0]);
                to_json(source[1], output[1]);
                to_json(source[2], output[2]);
                to_json(source[3], output[3]);
            }
        };

        template <>
        struct type_traits<boost::filesystem::path> {
            static bool from(const Json::Value& source, boost::filesystem::path& output)
            {
                output = source.asString();
                return true;
            }

            static void to(const boost::filesystem::path& source, Json::Value& output)
            {
                output = source.string();
            }
        };

        template <>
        struct type_traits<handle> {
            static bool from(const Json::Value& source, handle& output)
            {
                if (source.isConvertibleTo(Json::uintValue)) {
                    output.index = source.asUInt();
                    output.version = 0; // TODO version
                    return true;
                }
                return false;
            }

            static void to(handle source, Json::Value& output)
            {
                output = source.index; // TODO version
            }
        };

        template <class T, long int N>
        struct type_traits<T[N]> {
            static bool from(const Json::Value& source, T* output)
            {
                for (long int i = 0; i < N; ++i) {
                    if (!from_json(source[(int)i], output[i]))
                        return false;
                }
                return true;
            }

            static void to(const T* source, Json::Value& output)
            {
                for (long int i = 0; i < N; ++i)
                    to_json(source[i], output[(int)i]);
            }
        };

        template <>
        struct type_traits<sigma::graphics::texture_filter> {
            static bool from(const Json::Value& source, sigma::graphics::texture_filter& output)
            {
                auto lower = boost::to_upper_copy(source.asString());
                if (lower == "LINEAR")
                    output = graphics::texture_filter::LINEAR;
                else if (lower == "NEAREST")
                    output = graphics::texture_filter::NEAREST;
                else if (lower == "NONE")
                    output = graphics::texture_filter::NONE;
                else
                    return false;

                return true;
            }

            static void to(const sigma::graphics::texture_filter& source, Json::Value& output)
            {
                switch (source) {
                case graphics::texture_filter::LINEAR:
                    output = "LINEAR";
                    break;
                case graphics::texture_filter::NEAREST:
                    output = "NEAREST";
                    break;
                case graphics::texture_filter::NONE:
                    output = "NONE";
                    break;
                }
            }
        };

        template <>
        struct type_traits<sigma::graphics::texture_format> {
            static bool from(const Json::Value& source, sigma::graphics::texture_format& output)
            {
                auto lower = boost::to_upper_copy(source.asString());
                if (lower == "RGB8")
                    output = sigma::graphics::texture_format::RGB8;
                else if (lower == "RGBA8")
                    output = sigma::graphics::texture_format::RGBA8;
                else if (lower == "RGB32F")
                    output = sigma::graphics::texture_format::RGB32F;
                else
                    return false;
                return true;
            }

            static void to(const sigma::graphics::texture_format& source, Json::Value& output)
            {
                switch (source) {
                case sigma::graphics::texture_format::RGB8:
                    output = "RGB8";
                    break;
                case sigma::graphics::texture_format::RGBA8:
                    output = "RGBA8";
                    break;
                case sigma::graphics::texture_format::RGB32F:
                    output = "RGB32F";
                    break;
                }
            }
        };

        template <class T>
        struct type_traits {
            static bool from(const Json::Value& source, T& output)
            {
                bool good = true;
                boost::hana::for_each(boost::hana::keys(output), [&](auto key) {
                    auto& member = boost::hana::at_key(output, key);
                    using member_type = std::remove_reference_t<decltype(member)>;
                    if (!type_traits<member_type>::from(source[key.c_str()], member))
                        good = false;
                });
                return true;
            }

            static void to(const T& source, Json::Value& output)
            {
                boost::hana::for_each(boost::hana::keys(source), [&](auto key) {
                    const auto& member = boost::hana::at_key(source, key);
                    using member_type = std::remove_const_t<std::remove_reference_t<decltype(member)>>;
                    type_traits<member_type>::to(member, output[key.c_str()]);
                });
            }
        };

        template <class... Components>
        struct type_traits<sigma::world<Components...>> {
            template <class T>
            struct component_tag {
                using component_type = T;
            };

            template <class Func>
            static void foreach_component(Func f)
            {
                auto l = { (f(component_tag<Components>{}), 0)... };
                (void)l;
            }

            static bool from(const Json::Value& source, sigma::world<Components...>& output)
            {
                bool good = true;
                for (auto entity : source.getMemberNames()) {
                    auto e = output.create();
                    auto& src = source[entity];
                    foreach_component([&](auto comp_tag) {
                        using component_type = typename decltype(comp_tag)::component_type;
                        if (src.isMember(component_name(component_type))) {
                            component_type* cmp = output.template add<component_type>(e);
                            if (!type_traits<component_type>::from(src[component_name(component_type)], *cmp))
                                good = false;
                        }
                    });
                }
                return good;
            }
        };
    }
}
}

#endif // SIGMA_ENGINE_JSON_CONVERSION_HPP
<|endoftext|>


<commit_before>#pragma once
#include <staticjson/basic.hpp>

#include <limits>
#include <string>
#include <type_traits>

namespace staticjson
{

template <class IntType>
class IntegerHandler : public BaseHandler
{
    static_assert(std::is_arithmetic<IntType>::value, "Only arithmetic types are allowed");

protected:
    IntType* m_value;

    template<class NumericType>
    static constexpr bool is_integral() {
        return std::is_integral<NumericType>::value;
    }

    template<class AnotherIntType>
    static constexpr bool type_fits()
    {
        typedef typename std::numeric_limits<IntType> this_limits;
        typedef typename std::numeric_limits<AnotherIntType> that_limits;

        return (
            this_limits::is_signed == that_limits::is_signed ? (
                this_limits::min() <= that_limits::min() &&
                this_limits::max() >= that_limits::max()
            ) : this_limits::is_signed ? (
                this_limits::max() >= that_limits::max()
            ) : (
                false
            )
        );
    }

    template <class AnotherIntType>
    static constexpr typename std::enable_if<
        is_integral<AnotherIntType>() && type_fits<AnotherIntType>(),
        bool
    >::type is_out_of_range(AnotherIntType)
    {
        return false;
    }

    template <class AnotherIntType>
    static constexpr typename std::enable_if<
        is_integral<AnotherIntType> && !type_fits<AnotherIntType>(),
        bool
    >::type is_out_of_range(AnotherIntType a)
    {
        typedef typename std::common_type<IntType, AnotherIntType>::type CommonType;
        typedef typename std::numeric_limits<IntType> this_limits;
        typedef typename std::numeric_limits<AnotherIntType> that_limits;

        return (
            (this_limits::is_signed == that_limits::is_signed) ? (
                CommonType(a) < CommonType(this_limits::min()) ||
                CommonType(a) > CommonType(this_limits::max())
            ) : (this_limits::is_signed) ? (
                CommonType(a) > CommonType(this_limits::max())
            ) : (
                a < 0 || CommonType(a) > CommonType(this_limits::max())
            )
        );
    }

    template <class FloatType>
    static constexpr typename std::enable_if<
        std::is_floating_point<FloatType>::value,
        bool
    >::type is_out_of_range(FloatType f)
    {
        return static_cast<FloatType>(static_cast<IntType>(f)) != f;
    }

    template <class ReceiveNumType>
    bool receive(ReceiveIntType r, const char* actual_type)
    {
        if (is_out_of_range(r))
            return set_out_of_range(actual_type);
        *m_value = static_cast<IntType>(r);
        this->parsed = true;
        return true;
    }

public:
    explicit IntegerHandler(IntType* value) : m_value(value) {}

    bool Int(int i) override
    {
        return receive(i, "int");
    }

    bool Uint(unsigned i) override
    {
        return receive(i, "unsigned int");
    }

    bool Int64(std::int64_t i) override
    {
        return receive(i, "std::int64_t");
    }

    bool Uint64(std::uint64_t i) override
    {
        return receive(i, "std::uint64_t");
    }

    bool Double(double d) override
    {
        IntType val = static_cast<IntType>(d);
        if (static_cast<double>(val) != d)
            return set_out_of_range("double");
        *m_value = val;
        this->parsed = true;
        return true;
    }

    bool write(IHandler* output) const override
    {
        if (std::numeric_limits<IntType>::is_signed)
        {
            return output->Int64(*m_value);
        }
        else
        {
            return output->Uint64(*m_value);
        }
    }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("integer"), alloc);
        Value minimum, maximum;
        if (std::numeric_limits<IntType>::is_signed)
        {
            minimum.SetInt64(std::numeric_limits<IntType>::min());
            maximum.SetInt64(std::numeric_limits<IntType>::max());
        }
        else
        {
            minimum.SetUint64(std::numeric_limits<IntType>::min());
            maximum.SetUint64(std::numeric_limits<IntType>::max());
        }
        output.AddMember(rapidjson::StringRef("minimum"), minimum, alloc);
        output.AddMember(rapidjson::StringRef("maximum"), maximum, alloc);
    }
};

template <>
class Handler<std::nullptr_t> : public BaseHandler
{
public:
    explicit Handler(std::nullptr_t*) {}

    bool Null() override { return true; }

    std::string type_name() const override { return "null"; }

    bool write(IHandler* output) const override { return output->Null(); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc);
    }
};

template <>
class Handler<bool> : public BaseHandler
{
private:
    bool* m_value;

public:
    explicit Handler(bool* value) : m_value(value) {}

    bool Bool(bool v) override
    {
        *m_value = v;
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "bool"; }

    bool write(IHandler* output) const override { return output->Bool(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc);
    }
};

template <>
class Handler<int> : public IntegerHandler<int>
{
public:
    explicit Handler(int* i) : IntegerHandler<int>(i) {}

    std::string type_name() const override { return "int"; }

    bool write(IHandler* output) const override { return output->Int(*m_value); }
};

template <>
class Handler<unsigned int> : public IntegerHandler<unsigned int>
{
public:
    explicit Handler(unsigned* i) : IntegerHandler<unsigned int>(i) {}

    std::string type_name() const override { return "unsigned int"; }

    bool write(IHandler* output) const override { return output->Uint(*m_value); }
};

template <>
class Handler<long> : public IntegerHandler<long>
{
public:
    explicit Handler(long* i) : IntegerHandler<long>(i) {}

    std::string type_name() const override { return "long"; }
};

template <>
class Handler<unsigned long> : public IntegerHandler<unsigned long>
{
public:
    explicit Handler(unsigned long* i) : IntegerHandler<unsigned long>(i) {}

    std::string type_name() const override { return "unsigned long"; }
};

template <>
class Handler<long long> : public IntegerHandler<long long>
{
public:
    explicit Handler(long long* i) : IntegerHandler<long long>(i) {}

    std::string type_name() const override { return "long long"; }
};

template <>
class Handler<unsigned long long> : public IntegerHandler<unsigned long long>
{
public:
    explicit Handler(unsigned long long* i) : IntegerHandler<unsigned long long>(i) {}

    std::string type_name() const override { return "unsigned long long"; }
};

// char is an alias for bool to work around the stupid `std::vector<bool>`
template <>
class Handler<char> : public BaseHandler
{
private:
    char* m_value;

public:
    explicit Handler(char* i) : m_value(i) {}

    std::string type_name() const override { return "bool"; }

    bool Bool(bool v) override
    {
        *this->m_value = v;
        this->parsed = true;
        return true;
    }

    bool write(IHandler* out) const override { return out->Bool(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc);
    }
};

template <>
class Handler<double> : public BaseHandler
{
private:
    double* m_value;

public:
    explicit Handler(double* v) : m_value(v) {}

    bool Int(int i) override
    {
        *m_value = i;
        this->parsed = true;
        return true;
    }

    bool Uint(unsigned i) override
    {
        *m_value = i;
        this->parsed = true;
        return true;
    }

    bool Int64(std::int64_t i) override
    {
        *m_value = static_cast<double>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::int64_t");
        this->parsed = true;
        return true;
    }

    bool Uint64(std::uint64_t i) override
    {
        *m_value = static_cast<double>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::uint64_t");
        this->parsed = true;
        return true;
    }

    bool Double(double d) override
    {
        *m_value = d;
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "double"; }

    bool write(IHandler* out) const override { return out->Double(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc);
    }
};

template <>
class Handler<float> : public BaseHandler
{
private:
    float* m_value;

public:
    explicit Handler(float* v) : m_value(v) {}

    bool Int(int i) override
    {
        *m_value = i;
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("int");
        this->parsed = true;
        return true;
    }

    bool Uint(unsigned i) override
    {
        *m_value = i;
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("unsigned int");
        this->parsed = true;
        return true;
    }

    bool Int64(std::int64_t i) override
    {
        *m_value = static_cast<float>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::int64_t");
        this->parsed = true;
        return true;
    }

    bool Uint64(std::uint64_t i) override
    {
        *m_value = static_cast<float>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::uint64_t");
        this->parsed = true;
        return true;
    }

    bool Double(double d) override
    {
        *m_value = d;
        if (static_cast<decltype(d)>(*m_value) != d)
            return set_out_of_range("double");
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "float"; }

    bool write(IHandler* out) const override { return out->Double(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc);
    }
};

template <>
class Handler<std::string> : public BaseHandler
{
private:
    std::string* m_value;

public:
    explicit Handler(std::string* v) : m_value(v) {}

    bool String(const char* str, SizeType length, bool) override
    {
        m_value->assign(str, length);
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "string"; }

    bool write(IHandler* out) const override
    {
        return out->String(m_value->data(), SizeType(m_value->size()), true);
    }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc);
    }
};
}
<commit_msg>Simplified is_out_of_range optimizations<commit_after>#pragma once
#include <staticjson/basic.hpp>

#include <limits>
#include <string>
#include <type_traits>

namespace staticjson
{

template <class IntType>
class IntegerHandler : public BaseHandler
{
    static_assert(std::is_arithmetic<IntType>::value, "Only arithmetic types are allowed");

protected:
    IntType* m_value;

    template <class AnotherIntType>
    static constexpr typename std::enable_if<
        std::is_integral<AnotherIntType>::value,
        bool
    >::type is_out_of_range(AnotherIntType a)
    {
        typedef typename std::common_type<IntType, AnotherIntType>::type CommonType;
        typedef typename std::numeric_limits<IntType> this_limits;
        typedef typename std::numeric_limits<AnotherIntType> that_limits;

        return (
            (this_limits::is_signed == that_limits::is_signed) ? (
                (this_limits::min() > that_limits::min()
                 this_limits::max() < that_limits::max()) &&
                (CommonType(this_limits::min()) > CommonType(a) ||
                 CommonType(this_limits::max()) < CommonType(a))
            ) : (this_limits::is_signed) ? (
                this_limits::max() < that_limits::max() &&
                CommonType(this_limits::max()) < CommonType(a))
            ) : (
                a < 0 || CommonType(a) > CommonType(this_limits::max())
            )
        );
    }

    template <class FloatType>
    static constexpr typename std::enable_if<
        std::is_floating_point<FloatType>::value,
        bool
    >::type is_out_of_range(FloatType f)
    {
        return static_cast<FloatType>(static_cast<IntType>(f)) != f;
    }

    template <class ReceiveNumType>
    bool receive(ReceiveIntType r, const char* actual_type)
    {
        if (is_out_of_range(r))
            return set_out_of_range(actual_type);
        *m_value = static_cast<IntType>(r);
        this->parsed = true;
        return true;
    }

public:
    explicit IntegerHandler(IntType* value) : m_value(value) {}

    bool Int(int i) override
    {
        return receive(i, "int");
    }

    bool Uint(unsigned i) override
    {
        return receive(i, "unsigned int");
    }

    bool Int64(std::int64_t i) override
    {
        return receive(i, "std::int64_t");
    }

    bool Uint64(std::uint64_t i) override
    {
        return receive(i, "std::uint64_t");
    }

    bool Double(double d) override
    {
        IntType val = static_cast<IntType>(d);
        if (static_cast<double>(val) != d)
            return set_out_of_range("double");
        *m_value = val;
        this->parsed = true;
        return true;
    }

    bool write(IHandler* output) const override
    {
        if (std::numeric_limits<IntType>::is_signed)
        {
            return output->Int64(*m_value);
        }
        else
        {
            return output->Uint64(*m_value);
        }
    }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("integer"), alloc);
        Value minimum, maximum;
        if (std::numeric_limits<IntType>::is_signed)
        {
            minimum.SetInt64(std::numeric_limits<IntType>::min());
            maximum.SetInt64(std::numeric_limits<IntType>::max());
        }
        else
        {
            minimum.SetUint64(std::numeric_limits<IntType>::min());
            maximum.SetUint64(std::numeric_limits<IntType>::max());
        }
        output.AddMember(rapidjson::StringRef("minimum"), minimum, alloc);
        output.AddMember(rapidjson::StringRef("maximum"), maximum, alloc);
    }
};

template <>
class Handler<std::nullptr_t> : public BaseHandler
{
public:
    explicit Handler(std::nullptr_t*) {}

    bool Null() override { return true; }

    std::string type_name() const override { return "null"; }

    bool write(IHandler* output) const override { return output->Null(); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("null"), alloc);
    }
};

template <>
class Handler<bool> : public BaseHandler
{
private:
    bool* m_value;

public:
    explicit Handler(bool* value) : m_value(value) {}

    bool Bool(bool v) override
    {
        *m_value = v;
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "bool"; }

    bool write(IHandler* output) const override { return output->Bool(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc);
    }
};

template <>
class Handler<int> : public IntegerHandler<int>
{
public:
    explicit Handler(int* i) : IntegerHandler<int>(i) {}

    std::string type_name() const override { return "int"; }

    bool write(IHandler* output) const override { return output->Int(*m_value); }
};

template <>
class Handler<unsigned int> : public IntegerHandler<unsigned int>
{
public:
    explicit Handler(unsigned* i) : IntegerHandler<unsigned int>(i) {}

    std::string type_name() const override { return "unsigned int"; }

    bool write(IHandler* output) const override { return output->Uint(*m_value); }
};

template <>
class Handler<long> : public IntegerHandler<long>
{
public:
    explicit Handler(long* i) : IntegerHandler<long>(i) {}

    std::string type_name() const override { return "long"; }
};

template <>
class Handler<unsigned long> : public IntegerHandler<unsigned long>
{
public:
    explicit Handler(unsigned long* i) : IntegerHandler<unsigned long>(i) {}

    std::string type_name() const override { return "unsigned long"; }
};

template <>
class Handler<long long> : public IntegerHandler<long long>
{
public:
    explicit Handler(long long* i) : IntegerHandler<long long>(i) {}

    std::string type_name() const override { return "long long"; }
};

template <>
class Handler<unsigned long long> : public IntegerHandler<unsigned long long>
{
public:
    explicit Handler(unsigned long long* i) : IntegerHandler<unsigned long long>(i) {}

    std::string type_name() const override { return "unsigned long long"; }
};

// char is an alias for bool to work around the stupid `std::vector<bool>`
template <>
class Handler<char> : public BaseHandler
{
private:
    char* m_value;

public:
    explicit Handler(char* i) : m_value(i) {}

    std::string type_name() const override { return "bool"; }

    bool Bool(bool v) override
    {
        *this->m_value = v;
        this->parsed = true;
        return true;
    }

    bool write(IHandler* out) const override { return out->Bool(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("boolean"), alloc);
    }
};

template <>
class Handler<double> : public BaseHandler
{
private:
    double* m_value;

public:
    explicit Handler(double* v) : m_value(v) {}

    bool Int(int i) override
    {
        *m_value = i;
        this->parsed = true;
        return true;
    }

    bool Uint(unsigned i) override
    {
        *m_value = i;
        this->parsed = true;
        return true;
    }

    bool Int64(std::int64_t i) override
    {
        *m_value = static_cast<double>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::int64_t");
        this->parsed = true;
        return true;
    }

    bool Uint64(std::uint64_t i) override
    {
        *m_value = static_cast<double>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::uint64_t");
        this->parsed = true;
        return true;
    }

    bool Double(double d) override
    {
        *m_value = d;
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "double"; }

    bool write(IHandler* out) const override { return out->Double(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc);
    }
};

template <>
class Handler<float> : public BaseHandler
{
private:
    float* m_value;

public:
    explicit Handler(float* v) : m_value(v) {}

    bool Int(int i) override
    {
        *m_value = i;
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("int");
        this->parsed = true;
        return true;
    }

    bool Uint(unsigned i) override
    {
        *m_value = i;
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("unsigned int");
        this->parsed = true;
        return true;
    }

    bool Int64(std::int64_t i) override
    {
        *m_value = static_cast<float>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::int64_t");
        this->parsed = true;
        return true;
    }

    bool Uint64(std::uint64_t i) override
    {
        *m_value = static_cast<float>(i);
        if (static_cast<decltype(i)>(*m_value) != i)
            return set_out_of_range("std::uint64_t");
        this->parsed = true;
        return true;
    }

    bool Double(double d) override
    {
        *m_value = d;
        if (static_cast<decltype(d)>(*m_value) != d)
            return set_out_of_range("double");
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "float"; }

    bool write(IHandler* out) const override { return out->Double(*m_value); }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("number"), alloc);
    }
};

template <>
class Handler<std::string> : public BaseHandler
{
private:
    std::string* m_value;

public:
    explicit Handler(std::string* v) : m_value(v) {}

    bool String(const char* str, SizeType length, bool) override
    {
        m_value->assign(str, length);
        this->parsed = true;
        return true;
    }

    std::string type_name() const override { return "string"; }

    bool write(IHandler* out) const override
    {
        return out->String(m_value->data(), SizeType(m_value->size()), true);
    }

    void generate_schema(Value& output, MemoryPoolAllocator& alloc) const override
    {
        output.SetObject();
        output.AddMember(rapidjson::StringRef("type"), rapidjson::StringRef("string"), alloc);
    }
};
}
<|endoftext|>


<commit_before>#include <errno.h>
#include <cassert>
#include <cctype>
#include <cstring>
#include <vector>
#include <string>
#include <stack>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <curl/curl.h>
#include "opkele/util.h"
#include "opkele/exception.h"

namespace opkele {
    using namespace std;

    namespace util {

	/*
	 * base64
	 */
	string encode_base64(const void *data,size_t length) {
	    BIO *b64 = 0, *bmem = 0;
	    try {
		b64 = BIO_new(BIO_f_base64());
		if(!b64)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder");
		BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
		bmem = BIO_new(BIO_s_mem());
		BIO_set_flags(b64,BIO_CLOSE);
		if(!bmem)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer");
		BIO_push(b64,bmem);
		if(((size_t)BIO_write(b64,data,length))!=length)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_write()");
		if(BIO_flush(b64)!=1)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()");
		char *rvd;
		long rvl = BIO_get_mem_data(bmem,&rvd);
		string rv(rvd,rvl);
		BIO_free_all(b64);
		return rv;
	    }catch(...) {
		if(b64) BIO_free_all(b64);
		throw;
	    }
	}

	void decode_base64(const string& data,vector<unsigned char>& rv) {
	    BIO *b64 = 0, *bmem = 0;
	    rv.clear();
	    try {
		bmem = BIO_new_mem_buf((void*)data.data(),data.size());
		if(!bmem)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()");
		b64 = BIO_new(BIO_f_base64());
		if(!b64)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder");
		BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
		BIO_push(b64,bmem);
		unsigned char tmp[512];
		size_t rb = 0;
		while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0)
		    rv.insert(rv.end(),tmp,&tmp[rb]);
		BIO_free_all(b64);
	    }catch(...) {
		if(b64) BIO_free_all(b64);
		throw;
	    }
	}

	/*
	 * big numerics
	 */

	BIGNUM *base64_to_bignum(const string& b64) {
	    vector<unsigned char> bin;
	    decode_base64(b64,bin);
	    BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0);
	    if(!rv)
		throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()");
	    return rv;
	}

	BIGNUM *dec_to_bignum(const string& dec) {
	    BIGNUM *rv = 0;
	    if(!BN_dec2bn(&rv,dec.c_str()))
		throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
	    return rv;
	}

	string bignum_to_base64(const BIGNUM *bn) {
	    vector<unsigned char> bin(BN_num_bytes(bn)+1);
	    unsigned char *binptr = &(bin.front())+1;
	    int l = BN_bn2bin(bn,binptr);
	    if(l && (*binptr)&0x80){
		(*(--binptr)) = 0; ++l;
	    }
	    return encode_base64(binptr,l);
	}

	/*
	 * w3c times
	 */

	string time_to_w3c(time_t t) {
	    struct tm tm_t;
	    if(!gmtime_r(&t,&tm_t))
		throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
	    char rv[25];
	    if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t))
		throw failed_conversion(OPKELE_CP_ "failed to strftime()");
	    return rv;
	}

	time_t w3c_to_time(const string& w) {
	    struct tm tm_t;
	    memset(&tm_t,0,sizeof(tm_t));
	    if(
		    sscanf(
			w.c_str(),
			"%04d-%02d-%02dT%02d:%02d:%02dZ",
			&tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday,
			&tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec
		    ) != 6 )
		throw failed_conversion(OPKELE_CP_ "failed to sscanf()");
	    tm_t.tm_mon--;
	    tm_t.tm_year-=1900;
	    time_t rv = mktime(&tm_t);
	    if(rv==(time_t)-1)
		throw failed_conversion(OPKELE_CP_ "failed to mktime()");
	    return rv;
	}

	/*
	 *
	 */

	string url_encode(const string& str) {
	    char * t = curl_escape(str.c_str(),str.length());
	    if(!t)
		throw failed_conversion(OPKELE_CP_ "failed to curl_escape()");
	    string rv(t);
	    curl_free(t);
	    return rv;
	}

	string long_to_string(long l) {
	    char rv[32];
	    int r=snprintf(rv,sizeof(rv),"%ld",l);
	    if(r<0 || r>=(int)sizeof(rv))
		throw failed_conversion(OPKELE_CP_ "failed to snprintf()");
	    return rv;
	}

	long string_to_long(const string& s) {
	    char *endptr = 0;
	    long rv = strtol(s.c_str(),&endptr,10);
	    if((!endptr) || endptr==s.c_str())
		throw failed_conversion(OPKELE_CP_ "failed to strtol()");
	    return rv;
	}

	/*
	 * Normalize URL according to the rules, described in rfc 3986, section 6
	 *
	 * - uppercase hext triplets (e.g. %ab -> %AB)
	 * - lowercase scheme and host
	 * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3,
	 *   that is - [:alpha:][:digit:]._~-
	 * - remove dot segments
	 * - remove empty and default ports
	 * - if there's no path component, add '/'
	 */
	 string rfc_3986_normalize_uri(const string& uri) {
	     static const char *whitespace = " \t\r\n";
	     string rv;
	     string::size_type ns = uri.find_first_not_of(whitespace);
	     if(ns==string::npos)
		 throw bad_input(OPKELE_CP_ "Can't normalize empty URI");
	     string::size_type colon = uri.find(':',ns);
	     if(colon==string::npos)
		 throw bad_input(OPKELE_CP_ "No scheme specified in URI");
	     transform(
		     uri.begin()+ns, uri.begin()+colon+1,
		     back_inserter(rv), ::tolower );
	     bool s;
	     string::size_type ul = uri.find_last_not_of(whitespace)+1;
	     if(ul <= (colon+3))
		 throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered");
	     if(uri[colon+1]!='/' || uri[colon+2]!='/')
		 throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component");
	     if(rv=="http:")
		 s = false;
	     else if(rv=="https:")
		 s = true;
	     else{
		 /* TODO: support more schemes.
		  * e.g. xri. How do we normalize
		  * xri?
		  */
		 rv.append(uri,colon+1,ul-colon-1);
		 return rv;
	     }
	     rv += "//";
	     string::size_type interesting = uri.find_first_of(":/#?",colon+3);
	     if(interesting==string::npos) {
		 transform(
			 uri.begin()+colon+3,uri.begin()+ul,
			 back_inserter(rv), ::tolower );
		 rv += '/'; return rv;
	     }
	     transform(
		     uri.begin()+colon+3,uri.begin()+interesting,
		     back_inserter(rv), ::tolower );
	     bool qf = false;
	     char ic = uri[interesting];
	     if(ic==':') {
		 string::size_type ni = uri.find_first_of("/#?%",interesting+1);
		 const char *nptr = uri.data()+interesting+1;
		 char *eptr = 0;
		 long port = strtol(nptr,&eptr,10);
		 if( (port>0) && (port<65535) && port!=(s?443:80) ) {
		     char tmp[8];
		     snprintf(tmp,sizeof(tmp),":%ld",port);
		     rv += tmp;
		 }
		 if(ni==string::npos) {
		     rv += '/'; return rv;
		 }
		 interesting = ni;
	     }else if(ic!='/') {
		 rv += '/'; rv += ic;
		 qf = true;
		 ++interesting;
	     }
	     string::size_type n = interesting;
	     char tmp[3] = { 0,0,0 };
	     stack<string::size_type> psegs; psegs.push(rv.length());
	     string pseg;
	     for(;n<ul;) {
		 string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n);
		 if(unsafe==string::npos) {
		     pseg.append(uri,n,ul-n-1); n = ul-1;
		 }else{
		     pseg.append(uri,n,unsafe-n);
		     n = unsafe;
		 }
		 char c = uri[n++];
		 if(c=='%') {
		     if((n+1)>=ul)
			 throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character");
		     tmp[0] = uri[n++];
		     tmp[1] = uri[n++];
		     if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) ))
			 throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized");
		     int cc = strtol(tmp,0,16);
		     if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) )
			 pseg += cc;
		     else{
			 pseg += '%';
			 pseg += toupper(tmp[0]); pseg += toupper(tmp[1]);
		     }
		 }else if(qf) {
		     rv += pseg; rv += c;
		     pseg.clear();
		 }else if(n>=ul || strchr("?/#",c)) {
		     if(pseg.empty() || pseg==".") {
		     }else if(pseg=="..") {
			 if(psegs.size()>1) {
			     rv.resize(psegs.top()); psegs.pop();
			 }
		     }else{
			 psegs.push(rv.length());
			 if(c!='/') {
			     pseg += c;
			     qf = true;
			 }
			 rv += '/'; rv += pseg;
		     }
		     if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) {
			 rv += '/';
			 if(n<ul)
			     qf = true;
		     }else if(strchr("?#",c)) {
			 if(psegs.size()==1 && psegs.top()==rv.length())
			     rv += '/';
			 if(pseg.empty())
			     rv += c;
			 qf = true;
		     }
		     pseg.clear();
		 }else{
		     pseg += c;
		 }
	     }
	     if(!pseg.empty()) {
		 rv += '/'; rv += pseg;
	     }
	     return rv;
	 }

    }

}
<commit_msg>corrected typo Signed-off-by: Michael Krelin <hacker@klever.net><commit_after>#include <errno.h>
#include <cassert>
#include <cctype>
#include <cstring>
#include <vector>
#include <string>
#include <stack>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <curl/curl.h>
#include "opkele/util.h"
#include "opkele/exception.h"

namespace opkele {
    using namespace std;

    namespace util {

	/*
	 * base64
	 */
	string encode_base64(const void *data,size_t length) {
	    BIO *b64 = 0, *bmem = 0;
	    try {
		b64 = BIO_new(BIO_f_base64());
		if(!b64)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 encoder");
		BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
		bmem = BIO_new(BIO_s_mem());
		BIO_set_flags(b64,BIO_CLOSE);
		if(!bmem)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() memory buffer");
		BIO_push(b64,bmem);
		if(((size_t)BIO_write(b64,data,length))!=length)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_write()");
		if(BIO_flush(b64)!=1)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_flush()");
		char *rvd;
		long rvl = BIO_get_mem_data(bmem,&rvd);
		string rv(rvd,rvl);
		BIO_free_all(b64);
		return rv;
	    }catch(...) {
		if(b64) BIO_free_all(b64);
		throw;
	    }
	}

	void decode_base64(const string& data,vector<unsigned char>& rv) {
	    BIO *b64 = 0, *bmem = 0;
	    rv.clear();
	    try {
		bmem = BIO_new_mem_buf((void*)data.data(),data.size());
		if(!bmem)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new_mem_buf()");
		b64 = BIO_new(BIO_f_base64());
		if(!b64)
		    throw exception_openssl(OPKELE_CP_ "failed to BIO_new() base64 decoder");
		BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
		BIO_push(b64,bmem);
		unsigned char tmp[512];
		size_t rb = 0;
		while((rb=BIO_read(b64,tmp,sizeof(tmp)))>0)
		    rv.insert(rv.end(),tmp,&tmp[rb]);
		BIO_free_all(b64);
	    }catch(...) {
		if(b64) BIO_free_all(b64);
		throw;
	    }
	}

	/*
	 * big numerics
	 */

	BIGNUM *base64_to_bignum(const string& b64) {
	    vector<unsigned char> bin;
	    decode_base64(b64,bin);
	    BIGNUM *rv = BN_bin2bn(&(bin.front()),bin.size(),0);
	    if(!rv)
		throw failed_conversion(OPKELE_CP_ "failed to BN_bin2bn()");
	    return rv;
	}

	BIGNUM *dec_to_bignum(const string& dec) {
	    BIGNUM *rv = 0;
	    if(!BN_dec2bn(&rv,dec.c_str()))
		throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
	    return rv;
	}

	string bignum_to_base64(const BIGNUM *bn) {
	    vector<unsigned char> bin(BN_num_bytes(bn)+1);
	    unsigned char *binptr = &(bin.front())+1;
	    int l = BN_bn2bin(bn,binptr);
	    if(l && (*binptr)&0x80){
		(*(--binptr)) = 0; ++l;
	    }
	    return encode_base64(binptr,l);
	}

	/*
	 * w3c times
	 */

	string time_to_w3c(time_t t) {
	    struct tm tm_t;
	    if(!gmtime_r(&t,&tm_t))
		throw failed_conversion(OPKELE_CP_ "failed to BN_dec2bn()");
	    char rv[25];
	    if(!strftime(rv,sizeof(rv)-1,"%Y-%m-%dT%H:%M:%SZ",&tm_t))
		throw failed_conversion(OPKELE_CP_ "failed to strftime()");
	    return rv;
	}

	time_t w3c_to_time(const string& w) {
	    struct tm tm_t;
	    memset(&tm_t,0,sizeof(tm_t));
	    if(
		    sscanf(
			w.c_str(),
			"%04d-%02d-%02dT%02d:%02d:%02dZ",
			&tm_t.tm_year,&tm_t.tm_mon,&tm_t.tm_mday,
			&tm_t.tm_hour,&tm_t.tm_min,&tm_t.tm_sec
		    ) != 6 )
		throw failed_conversion(OPKELE_CP_ "failed to sscanf()");
	    tm_t.tm_mon--;
	    tm_t.tm_year-=1900;
	    time_t rv = mktime(&tm_t);
	    if(rv==(time_t)-1)
		throw failed_conversion(OPKELE_CP_ "failed to mktime()");
	    return rv;
	}

	/*
	 *
	 */

	string url_encode(const string& str) {
	    char * t = curl_escape(str.c_str(),str.length());
	    if(!t)
		throw failed_conversion(OPKELE_CP_ "failed to curl_escape()");
	    string rv(t);
	    curl_free(t);
	    return rv;
	}

	string long_to_string(long l) {
	    char rv[32];
	    int r=snprintf(rv,sizeof(rv),"%ld",l);
	    if(r<0 || r>=(int)sizeof(rv))
		throw failed_conversion(OPKELE_CP_ "failed to snprintf()");
	    return rv;
	}

	long string_to_long(const string& s) {
	    char *endptr = 0;
	    long rv = strtol(s.c_str(),&endptr,10);
	    if((!endptr) || endptr==s.c_str())
		throw failed_conversion(OPKELE_CP_ "failed to strtol()");
	    return rv;
	}

	/*
	 * Normalize URL according to the rules, described in rfc 3986, section 6
	 *
	 * - uppercase hex triplets (e.g. %ab -> %AB)
	 * - lowercase scheme and host
	 * - decode %-encoded characters, specified as unreserved in rfc 3986, section 2.3,
	 *   that is - [:alpha:][:digit:]._~-
	 * - remove dot segments
	 * - remove empty and default ports
	 * - if there's no path component, add '/'
	 */
	 string rfc_3986_normalize_uri(const string& uri) {
	     static const char *whitespace = " \t\r\n";
	     string rv;
	     string::size_type ns = uri.find_first_not_of(whitespace);
	     if(ns==string::npos)
		 throw bad_input(OPKELE_CP_ "Can't normalize empty URI");
	     string::size_type colon = uri.find(':',ns);
	     if(colon==string::npos)
		 throw bad_input(OPKELE_CP_ "No scheme specified in URI");
	     transform(
		     uri.begin()+ns, uri.begin()+colon+1,
		     back_inserter(rv), ::tolower );
	     bool s;
	     string::size_type ul = uri.find_last_not_of(whitespace)+1;
	     if(ul <= (colon+3))
		 throw bad_input(OPKELE_CP_ "Unexpected end of URI being normalized encountered");
	     if(uri[colon+1]!='/' || uri[colon+2]!='/')
		 throw bad_input(OPKELE_CP_ "Unexpected input in URI being normalized after scheme component");
	     if(rv=="http:")
		 s = false;
	     else if(rv=="https:")
		 s = true;
	     else{
		 /* TODO: support more schemes.
		  * e.g. xri. How do we normalize
		  * xri?
		  */
		 rv.append(uri,colon+1,ul-colon-1);
		 return rv;
	     }
	     rv += "//";
	     string::size_type interesting = uri.find_first_of(":/#?",colon+3);
	     if(interesting==string::npos) {
		 transform(
			 uri.begin()+colon+3,uri.begin()+ul,
			 back_inserter(rv), ::tolower );
		 rv += '/'; return rv;
	     }
	     transform(
		     uri.begin()+colon+3,uri.begin()+interesting,
		     back_inserter(rv), ::tolower );
	     bool qf = false;
	     char ic = uri[interesting];
	     if(ic==':') {
		 string::size_type ni = uri.find_first_of("/#?%",interesting+1);
		 const char *nptr = uri.data()+interesting+1;
		 char *eptr = 0;
		 long port = strtol(nptr,&eptr,10);
		 if( (port>0) && (port<65535) && port!=(s?443:80) ) {
		     char tmp[8];
		     snprintf(tmp,sizeof(tmp),":%ld",port);
		     rv += tmp;
		 }
		 if(ni==string::npos) {
		     rv += '/'; return rv;
		 }
		 interesting = ni;
	     }else if(ic!='/') {
		 rv += '/'; rv += ic;
		 qf = true;
		 ++interesting;
	     }
	     string::size_type n = interesting;
	     char tmp[3] = { 0,0,0 };
	     stack<string::size_type> psegs; psegs.push(rv.length());
	     string pseg;
	     for(;n<ul;) {
		 string::size_type unsafe = uri.find_first_of(qf?"%":"%/?#",n);
		 if(unsafe==string::npos) {
		     pseg.append(uri,n,ul-n-1); n = ul-1;
		 }else{
		     pseg.append(uri,n,unsafe-n);
		     n = unsafe;
		 }
		 char c = uri[n++];
		 if(c=='%') {
		     if((n+1)>=ul)
			 throw bad_input(OPKELE_CP_ "Unexpected end of URI encountered while parsing percent-encoded character");
		     tmp[0] = uri[n++];
		     tmp[1] = uri[n++];
		     if(!( isxdigit(tmp[0]) && isxdigit(tmp[1]) ))
			 throw bad_input(OPKELE_CP_ "Invalid percent-encoded character in URI being normalized");
		     int cc = strtol(tmp,0,16);
		     if( isalpha(cc) || isdigit(cc) || strchr("._~-",cc) )
			 pseg += cc;
		     else{
			 pseg += '%';
			 pseg += toupper(tmp[0]); pseg += toupper(tmp[1]);
		     }
		 }else if(qf) {
		     rv += pseg; rv += c;
		     pseg.clear();
		 }else if(n>=ul || strchr("?/#",c)) {
		     if(pseg.empty() || pseg==".") {
		     }else if(pseg=="..") {
			 if(psegs.size()>1) {
			     rv.resize(psegs.top()); psegs.pop();
			 }
		     }else{
			 psegs.push(rv.length());
			 if(c!='/') {
			     pseg += c;
			     qf = true;
			 }
			 rv += '/'; rv += pseg;
		     }
		     if(c=='/' && (n>=ul || strchr("?#",uri[n])) ) {
			 rv += '/';
			 if(n<ul)
			     qf = true;
		     }else if(strchr("?#",c)) {
			 if(psegs.size()==1 && psegs.top()==rv.length())
			     rv += '/';
			 if(pseg.empty())
			     rv += c;
			 qf = true;
		     }
		     pseg.clear();
		 }else{
		     pseg += c;
		 }
	     }
	     if(!pseg.empty()) {
		 rv += '/'; rv += pseg;
	     }
	     return rv;
	 }

    }

}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Red Hat, Inc.
 *
 * Licensed under the GNU Lesser General Public License Version 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "../utils/bgettext/bgettext-lib.h"

#include "TransactionItem.hpp"

namespace libdnf {

// TODO: translations
static const std::map< TransactionItemAction, std::string > transactionItemActionName = {
    {TransactionItemAction::INSTALL, "Install"},
    {TransactionItemAction::DOWNGRADE, "Downgrade"},
    {TransactionItemAction::DOWNGRADED, "Downgraded"},
    {TransactionItemAction::OBSOLETE, "Obsolete"},
    {TransactionItemAction::OBSOLETED, "Obsoleted"},
    {TransactionItemAction::UPGRADE, "Upgrade"},
    {TransactionItemAction::UPGRADED, "Upgraded"},
    {TransactionItemAction::REMOVE, "Removed"},
    {TransactionItemAction::REINSTALL, "Reinstall"},
    {TransactionItemAction::REINSTALLED, "Reinstalled"},
    {TransactionItemAction::REASON_CHANGE, "Reason Change"},
};

static const std::map< TransactionItemAction, std::string > transactionItemActionShort = {
    {TransactionItemAction::INSTALL, "I"},
    {TransactionItemAction::DOWNGRADE, "D"},
    {TransactionItemAction::DOWNGRADED, "D"},
    {TransactionItemAction::OBSOLETE, "O"},
    {TransactionItemAction::OBSOLETED, "O"},
    {TransactionItemAction::UPGRADE, "U"},
    {TransactionItemAction::UPGRADED, "U"},
    // "R" is for Reinstall, therefore use "E" for rEmove (or Erase)
    {TransactionItemAction::REMOVE, "E"},
    {TransactionItemAction::REINSTALL, "R"},
    {TransactionItemAction::REINSTALLED, "R"},
    // TODO: replace "?" with something better
    {TransactionItemAction::REASON_CHANGE, "?"},
};

/*
static const std::map<std::string, TransactionItemReason> nameTransactionItemReason = {
    {, "I"},
    {TransactionItemAction::DOWNGRADE, "D"},
    {TransactionItemAction::DOWNGRADED, "D"},
    {TransactionItemAction::OBSOLETE, "O"},
    {TransactionItemAction::OBSOLETED, "O"},
    {TransactionItemAction::UPGRADE, "U"},
    {TransactionItemAction::UPGRADED, "U"},
    // "R" is for Reinstall, therefore use "E" for rEmove (or Erase)
    {TransactionItemAction::REMOVE, "E"},
    {TransactionItemAction::REINSTALL, "R"},
};
TransactionItemReason to_TransactionItemReason(const std::string & s) {
    return
*/

const std::string &
TransactionItemBase::getActionName()
{
    return transactionItemActionName.at(getAction());
}

const std::string &
TransactionItemBase::getActionShort()
{
    return transactionItemActionShort.at(getAction());
}

TransactionItem::TransactionItem(Transaction *trans)
  : trans(trans)
  , transID(0)
  , conn(trans->conn)
{
}

bool
TransactionItemBase::isForwardAction() const
{
    switch (action) {
        case TransactionItemAction::INSTALL:
        case TransactionItemAction::DOWNGRADE:
        case TransactionItemAction::OBSOLETE:
        case TransactionItemAction::UPGRADE:
        case TransactionItemAction::REINSTALL:
            return true;
        default:
            return false;
    }
}

bool
TransactionItemBase::isBackwardAction() const
{
    switch (action) {
        case TransactionItemAction::REMOVE:
        case TransactionItemAction::DOWNGRADED:
        case TransactionItemAction::OBSOLETED:
        case TransactionItemAction::UPGRADED:
        case TransactionItemAction::REINSTALLED:
            return true;
        default:
            return false;
    }
}

TransactionItem::TransactionItem(SQLite3Ptr conn, int64_t transID)
  : trans(nullptr)
  , transID(transID)
  , conn(conn)
{
}

void
TransactionItem::save()
{
    getItem()->save();
    if (getId() == 0) {
        dbInsert();
    } else {
        dbUpdate();
    }
}

void
TransactionItem::dbInsert()
{
    if (trans == nullptr) {
        throw std::runtime_error(
            _("Attempt to insert transaction item into completed transaction"));
    }

    const char *sql = R"**(
        INSERT INTO
          trans_item (
            id,
            trans_id,
            item_id,
            repo_id,
            action,
            reason,
            state
          )
        VALUES
          (null, ?, ?, ?, ?, ?, ?)
    )**";

    // save the transaction item
    SQLite3::Statement query(*(conn.get()), sql);
    query.bindv(trans->getId(),
                getItem()->getId(),
                swdb_private::Repo::getCached(conn, getRepoid())->getId(),
                static_cast< int >(getAction()),
                static_cast< int >(getReason()),
                static_cast< int >(getState()));
    query.step();
    setId(conn->lastInsertRowID());
}

void
TransactionItem::saveReplacedBy()
{
    if (replacedBy.empty()) {
        return;
    }
    const char *sql = "INSERT OR REPLACE INTO item_replaced_by VALUES (?, ?)";
    SQLite3::Statement replacedByQuery(*(conn.get()), sql);
    bool first = true;
    for (const auto &newItem : replacedBy) {
        if (!first) {
            // reset the prepared statement, so it can be executed again
            replacedByQuery.reset();
        }
        replacedByQuery.bindv(getId(), newItem->getId());
        replacedByQuery.step();
        first = false;
    }
}

void
TransactionItem::saveState()
{
    const char *sql = R"**(
        UPDATE
          trans_item
        SET
          state = ?
        WHERE
          id = ?
    )**";

    SQLite3::Statement query(*conn, sql);
    query.bindv(static_cast< int >(getState()), getId());
    query.step();
}

void
TransactionItem::dbUpdate()
{
    if (trans == nullptr) {
        throw std::runtime_error(_("Attempt to update transaction item in completed transaction"));
    }

    const char *sql = R"**(
        UPDATE
          trans_item
        SET
          trans_id=?,
          item_id=?,
          repo_id=?,
          action=?,
          reason=?,
          state=?
        WHERE
          id = ?
    )**";

    SQLite3::Statement query(*(conn.get()), sql);
    query.bindv(trans->getId(),
                getItem()->getId(),
                swdb_private::Repo::getCached(trans->conn, getRepoid())->getId(),
                static_cast< int >(getAction()),
                static_cast< int >(getReason()),
                static_cast< int >(getState()),
                getId());
    query.step();
}

uint32_t
TransactionItem::getInstalledBy() const {
    if (!trans) {
        // null pointer -> create a local instance to return the user id
        Transaction t(conn, transID);
        return t.getUserId();
    }
    return trans->getUserId();
}

} // namespace libdnf
<commit_msg>libdnf/transaction/TransactionItem: Set short action for Reason Change<commit_after>/*
 * Copyright (C) 2017-2018 Red Hat, Inc.
 *
 * Licensed under the GNU Lesser General Public License Version 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "../utils/bgettext/bgettext-lib.h"

#include "TransactionItem.hpp"

namespace libdnf {

// TODO: translations
static const std::map< TransactionItemAction, std::string > transactionItemActionName = {
    {TransactionItemAction::INSTALL, "Install"},
    {TransactionItemAction::DOWNGRADE, "Downgrade"},
    {TransactionItemAction::DOWNGRADED, "Downgraded"},
    {TransactionItemAction::OBSOLETE, "Obsolete"},
    {TransactionItemAction::OBSOLETED, "Obsoleted"},
    {TransactionItemAction::UPGRADE, "Upgrade"},
    {TransactionItemAction::UPGRADED, "Upgraded"},
    {TransactionItemAction::REMOVE, "Removed"},
    {TransactionItemAction::REINSTALL, "Reinstall"},
    {TransactionItemAction::REINSTALLED, "Reinstalled"},
    {TransactionItemAction::REASON_CHANGE, "Reason Change"},
};

static const std::map< TransactionItemAction, std::string > transactionItemActionShort = {
    {TransactionItemAction::INSTALL, "I"},
    {TransactionItemAction::DOWNGRADE, "D"},
    {TransactionItemAction::DOWNGRADED, "D"},
    {TransactionItemAction::OBSOLETE, "O"},
    {TransactionItemAction::OBSOLETED, "O"},
    {TransactionItemAction::UPGRADE, "U"},
    {TransactionItemAction::UPGRADED, "U"},
    // "R" is for Reinstall, therefore use "E" for rEmove (or Erase)
    {TransactionItemAction::REMOVE, "E"},
    {TransactionItemAction::REINSTALL, "R"},
    {TransactionItemAction::REINSTALLED, "R"},
    {TransactionItemAction::REASON_CHANGE, "C"},
};

/*
static const std::map<std::string, TransactionItemReason> nameTransactionItemReason = {
    {, "I"},
    {TransactionItemAction::DOWNGRADE, "D"},
    {TransactionItemAction::DOWNGRADED, "D"},
    {TransactionItemAction::OBSOLETE, "O"},
    {TransactionItemAction::OBSOLETED, "O"},
    {TransactionItemAction::UPGRADE, "U"},
    {TransactionItemAction::UPGRADED, "U"},
    // "R" is for Reinstall, therefore use "E" for rEmove (or Erase)
    {TransactionItemAction::REMOVE, "E"},
    {TransactionItemAction::REINSTALL, "R"},
};
TransactionItemReason to_TransactionItemReason(const std::string & s) {
    return
*/

const std::string &
TransactionItemBase::getActionName()
{
    return transactionItemActionName.at(getAction());
}

const std::string &
TransactionItemBase::getActionShort()
{
    return transactionItemActionShort.at(getAction());
}

TransactionItem::TransactionItem(Transaction *trans)
  : trans(trans)
  , transID(0)
  , conn(trans->conn)
{
}

bool
TransactionItemBase::isForwardAction() const
{
    switch (action) {
        case TransactionItemAction::INSTALL:
        case TransactionItemAction::DOWNGRADE:
        case TransactionItemAction::OBSOLETE:
        case TransactionItemAction::UPGRADE:
        case TransactionItemAction::REINSTALL:
            return true;
        default:
            return false;
    }
}

bool
TransactionItemBase::isBackwardAction() const
{
    switch (action) {
        case TransactionItemAction::REMOVE:
        case TransactionItemAction::DOWNGRADED:
        case TransactionItemAction::OBSOLETED:
        case TransactionItemAction::UPGRADED:
        case TransactionItemAction::REINSTALLED:
            return true;
        default:
            return false;
    }
}

TransactionItem::TransactionItem(SQLite3Ptr conn, int64_t transID)
  : trans(nullptr)
  , transID(transID)
  , conn(conn)
{
}

void
TransactionItem::save()
{
    getItem()->save();
    if (getId() == 0) {
        dbInsert();
    } else {
        dbUpdate();
    }
}

void
TransactionItem::dbInsert()
{
    if (trans == nullptr) {
        throw std::runtime_error(
            _("Attempt to insert transaction item into completed transaction"));
    }

    const char *sql = R"**(
        INSERT INTO
          trans_item (
            id,
            trans_id,
            item_id,
            repo_id,
            action,
            reason,
            state
          )
        VALUES
          (null, ?, ?, ?, ?, ?, ?)
    )**";

    // save the transaction item
    SQLite3::Statement query(*(conn.get()), sql);
    query.bindv(trans->getId(),
                getItem()->getId(),
                swdb_private::Repo::getCached(conn, getRepoid())->getId(),
                static_cast< int >(getAction()),
                static_cast< int >(getReason()),
                static_cast< int >(getState()));
    query.step();
    setId(conn->lastInsertRowID());
}

void
TransactionItem::saveReplacedBy()
{
    if (replacedBy.empty()) {
        return;
    }
    const char *sql = "INSERT OR REPLACE INTO item_replaced_by VALUES (?, ?)";
    SQLite3::Statement replacedByQuery(*(conn.get()), sql);
    bool first = true;
    for (const auto &newItem : replacedBy) {
        if (!first) {
            // reset the prepared statement, so it can be executed again
            replacedByQuery.reset();
        }
        replacedByQuery.bindv(getId(), newItem->getId());
        replacedByQuery.step();
        first = false;
    }
}

void
TransactionItem::saveState()
{
    const char *sql = R"**(
        UPDATE
          trans_item
        SET
          state = ?
        WHERE
          id = ?
    )**";

    SQLite3::Statement query(*conn, sql);
    query.bindv(static_cast< int >(getState()), getId());
    query.step();
}

void
TransactionItem::dbUpdate()
{
    if (trans == nullptr) {
        throw std::runtime_error(_("Attempt to update transaction item in completed transaction"));
    }

    const char *sql = R"**(
        UPDATE
          trans_item
        SET
          trans_id=?,
          item_id=?,
          repo_id=?,
          action=?,
          reason=?,
          state=?
        WHERE
          id = ?
    )**";

    SQLite3::Statement query(*(conn.get()), sql);
    query.bindv(trans->getId(),
                getItem()->getId(),
                swdb_private::Repo::getCached(trans->conn, getRepoid())->getId(),
                static_cast< int >(getAction()),
                static_cast< int >(getReason()),
                static_cast< int >(getState()),
                getId());
    query.step();
}

uint32_t
TransactionItem::getInstalledBy() const {
    if (!trans) {
        // null pointer -> create a local instance to return the user id
        Transaction t(conn, transID);
        return t.getUserId();
    }
    return trans->getUserId();
}

} // namespace libdnf
<|endoftext|>


<commit_before>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008-2011 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

You may redistribute, use, and create derivate works of HOOMD-blue, in source
and binary forms, provided you abide by the following conditions:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer both in the code and
prominently in any materials provided with the distribution.

* 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.

* All publications and presentations based on HOOMD-blue, including any reports
or published results obtained, in whole or in part, with HOOMD-blue, will
acknowledge its use according to the terms posted at the time of submission on:
http://codeblue.umich.edu/hoomd-blue/citations.html

* Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
http://codeblue.umich.edu/hoomd-blue/

* Apart from the above required attributions, neither the name of the copyright
holder nor the names of HOOMD-blue's contributors may be used to endorse or
promote products derived from this software without specific prior written
permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
*/

// Maintainer: joaander

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;
#include <boost/bind.hpp>
using namespace boost;

#include "TwoStepNVTGPU.h"
#include "TwoStepNVTGPU.cuh"

/*! \file TwoStepNVTGPU.h
    \brief Contains code for the TwoStepNVTGPU class
*/

/*! \param sysdef SystemDefinition this method will act on. Must not be NULL.
    \param group The group of particles this integration method is to work on
    \param thermo compute for thermodynamic quantities
    \param tau NVT period
    \param T Temperature set point
    \param suffix Suffix to attach to the end of log quantity names
*/
TwoStepNVTGPU::TwoStepNVTGPU(boost::shared_ptr<SystemDefinition> sysdef,
                             boost::shared_ptr<ParticleGroup> group,
                             boost::shared_ptr<ComputeThermo> thermo,
                             Scalar tau,
                             boost::shared_ptr<Variant> T,
                             const std::string& suffix)
    : TwoStepNVT(sysdef, group, thermo, tau, T, suffix)
    {
    // only one GPU is supported
    if (!exec_conf->isCUDAEnabled())
        {
        cerr << endl << "***Error! Creating a TwoStepNVEGPU when CUDA is disabled" << endl << endl;
        throw std::runtime_error("Error initializing TwoStepNVEGPU");
        }

    m_block_size = 128;
    }

/*! \param timestep Current time step
    \post Particle positions are moved forward to timestep+1 and velocities to timestep+1/2 per the Nose-Hoover method
*/
void TwoStepNVTGPU::integrateStepOne(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumLocalMembers();
    if (group_size == 0)
        return;

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NVT step 1");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];

    // access all the needed data
    ArrayHandle<Scalar4> d_pos(m_pdata->getPositions(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar4> d_vel(m_pdata->getVelocities(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar3> d_accel(m_pdata->getAccelerations(), access_location::device, access_mode::read);
    ArrayHandle<int3> d_image(m_pdata->getImages(), access_location::device, access_mode::readwrite);

    gpu_boxsize box = m_pdata->getBoxGPU();
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_nvt_step_one(d_pos.data,
                     d_vel.data,
                     d_accel.data,
                     d_image.data,
                     d_index_array.data,
                     group_size,
                     box,
                     m_block_size,
                     xi,
                     m_deltaT,
                     m_no_wrap_particles);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

/*! \param timestep Current time step
    \post particle velocities are moved forward to timestep+1 on the GPU
*/
void TwoStepNVTGPU::integrateStepTwo(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumLocalMembers();
    if (group_size == 0)
        return;

    const GPUArray< Scalar4 >& net_force = m_pdata->getNetForce();

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    // compute the current thermodynamic properties
    m_thermo->compute(timestep+1);

    // next, update the state variables Xi and eta
    Scalar xi_prev = xi;
    Scalar curr_T = m_thermo->getTemperature();
    xi += m_deltaT / (m_tau*m_tau) * (curr_T/m_T->getValue(timestep) - Scalar(1.0));
    eta += m_deltaT / Scalar(2.0) * (xi + xi_prev);

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NVT step 2");

    ArrayHandle<Scalar4> d_vel(m_pdata->getVelocities(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar3> d_accel(m_pdata->getAccelerations(), access_location::device, access_mode::readwrite);

    ArrayHandle<Scalar4> d_net_force(net_force, access_location::device, access_mode::read);
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_nvt_step_two(d_vel.data,
                     d_accel.data,
                     d_index_array.data,
                     group_size,
                     d_net_force.data,
                     m_block_size,
                     xi,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    setIntegratorVariables(v);

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

void export_TwoStepNVTGPU()
    {
    class_<TwoStepNVTGPU, boost::shared_ptr<TwoStepNVTGPU>, bases<TwoStepNVT>, boost::noncopyable>
        ("TwoStepNVTGPU", init< boost::shared_ptr<SystemDefinition>,
                          boost::shared_ptr<ParticleGroup>,
                          boost::shared_ptr<ComputeThermo>,
                          Scalar,
                          boost::shared_ptr<Variant>,
                          const std::string&
                          >())
        ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<commit_msg>Added broadcast of IntegratorVariables to TwoStepNVTGPU<commit_after>/*
Highly Optimized Object-oriented Many-particle Dynamics -- Blue Edition
(HOOMD-blue) Open Source Software License Copyright 2008-2011 Ames Laboratory
Iowa State University and The Regents of the University of Michigan All rights
reserved.

HOOMD-blue may contain modifications ("Contributions") provided, and to which
copyright is held, by various Contributors who have granted The Regents of the
University of Michigan the right to modify and/or distribute such Contributions.

You may redistribute, use, and create derivate works of HOOMD-blue, in source
and binary forms, provided you abide by the following conditions:

* Redistributions of source code must retain the above copyright notice, this
list of conditions, and the following disclaimer both in the code and
prominently in any materials provided with the distribution.

* 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.

* All publications and presentations based on HOOMD-blue, including any reports
or published results obtained, in whole or in part, with HOOMD-blue, will
acknowledge its use according to the terms posted at the time of submission on:
http://codeblue.umich.edu/hoomd-blue/citations.html

* Any electronic documents citing HOOMD-Blue will link to the HOOMD-Blue website:
http://codeblue.umich.edu/hoomd-blue/

* Apart from the above required attributions, neither the name of the copyright
holder nor the names of HOOMD-blue's contributors may be used to endorse or
promote products derived from this software without specific prior written
permission.

Disclaimer

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS'' AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND/OR ANY
WARRANTIES THAT THIS SOFTWARE IS FREE OF INFRINGEMENT ARE DISCLAIMED.

IN NO EVENT SHALL THE COPYRIGHT HOLDER 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.
*/

// Maintainer: joaander

#ifdef WIN32
#pragma warning( push )
#pragma warning( disable : 4244 )
#endif

#include <boost/python.hpp>
using namespace boost::python;
#include <boost/bind.hpp>
using namespace boost;

#include "TwoStepNVTGPU.h"
#include "TwoStepNVTGPU.cuh"

#ifdef ENABLE_MPI
#include "Communicator.h"
#endif

/*! \file TwoStepNVTGPU.h
    \brief Contains code for the TwoStepNVTGPU class
*/

/*! \param sysdef SystemDefinition this method will act on. Must not be NULL.
    \param group The group of particles this integration method is to work on
    \param thermo compute for thermodynamic quantities
    \param tau NVT period
    \param T Temperature set point
    \param suffix Suffix to attach to the end of log quantity names
*/
TwoStepNVTGPU::TwoStepNVTGPU(boost::shared_ptr<SystemDefinition> sysdef,
                             boost::shared_ptr<ParticleGroup> group,
                             boost::shared_ptr<ComputeThermo> thermo,
                             Scalar tau,
                             boost::shared_ptr<Variant> T,
                             const std::string& suffix)
    : TwoStepNVT(sysdef, group, thermo, tau, T, suffix)
    {
    // only one GPU is supported
    if (!exec_conf->isCUDAEnabled())
        {
        cerr << endl << "***Error! Creating a TwoStepNVEGPU when CUDA is disabled" << endl << endl;
        throw std::runtime_error("Error initializing TwoStepNVEGPU");
        }

    m_block_size = 128;
    }

/*! \param timestep Current time step
    \post Particle positions are moved forward to timestep+1 and velocities to timestep+1/2 per the Nose-Hoover method
*/
void TwoStepNVTGPU::integrateStepOne(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumLocalMembers();
    if (group_size == 0)
        return;

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NVT step 1");

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];

    // access all the needed data
    ArrayHandle<Scalar4> d_pos(m_pdata->getPositions(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar4> d_vel(m_pdata->getVelocities(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar3> d_accel(m_pdata->getAccelerations(), access_location::device, access_mode::read);
    ArrayHandle<int3> d_image(m_pdata->getImages(), access_location::device, access_mode::readwrite);

    gpu_boxsize box = m_pdata->getBoxGPU();
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_nvt_step_one(d_pos.data,
                     d_vel.data,
                     d_accel.data,
                     d_image.data,
                     d_index_array.data,
                     group_size,
                     box,
                     m_block_size,
                     xi,
                     m_deltaT,
                     m_no_wrap_particles);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

/*! \param timestep Current time step
    \post particle velocities are moved forward to timestep+1 on the GPU
*/
void TwoStepNVTGPU::integrateStepTwo(unsigned int timestep)
    {
    unsigned int group_size = m_group->getNumLocalMembers();
    if (group_size == 0)
        return;

    const GPUArray< Scalar4 >& net_force = m_pdata->getNetForce();

    IntegratorVariables v = getIntegratorVariables();
    Scalar& xi = v.variable[0];
    Scalar& eta = v.variable[1];

    // compute the current thermodynamic properties
    m_thermo->compute(timestep+1);

    // next, update the state variables Xi and eta
    Scalar xi_prev = xi;
    Scalar curr_T = m_thermo->getTemperature();
    xi += m_deltaT / (m_tau*m_tau) * (curr_T/m_T->getValue(timestep) - Scalar(1.0));
    eta += m_deltaT / Scalar(2.0) * (xi + xi_prev);

#ifdef ENABLE_MPI
    if (m_comm)
        {
        assert(m_comm->getMPICommunicator()->size());
        // broadcast integrator variables from rank 0 to other processors
        broadcast(*m_comm->getMPICommunicator(), eta, 0);
        broadcast(*m_comm->getMPICommunicator(), xi, 0);
        }
#endif

    // profile this step
    if (m_prof)
        m_prof->push(exec_conf, "NVT step 2");

    ArrayHandle<Scalar4> d_vel(m_pdata->getVelocities(), access_location::device, access_mode::readwrite);
    ArrayHandle<Scalar3> d_accel(m_pdata->getAccelerations(), access_location::device, access_mode::readwrite);

    ArrayHandle<Scalar4> d_net_force(net_force, access_location::device, access_mode::read);
    ArrayHandle< unsigned int > d_index_array(m_group->getIndexArray(), access_location::device, access_mode::read);

    // perform the update on the GPU
    gpu_nvt_step_two(d_vel.data,
                     d_accel.data,
                     d_index_array.data,
                     group_size,
                     d_net_force.data,
                     m_block_size,
                     xi,
                     m_deltaT);

    if (exec_conf->isCUDAErrorCheckingEnabled())
        CHECK_CUDA_ERROR();

    setIntegratorVariables(v);

    // done profiling
    if (m_prof)
        m_prof->pop(exec_conf);
    }

void export_TwoStepNVTGPU()
    {
    class_<TwoStepNVTGPU, boost::shared_ptr<TwoStepNVTGPU>, bases<TwoStepNVT>, boost::noncopyable>
        ("TwoStepNVTGPU", init< boost::shared_ptr<SystemDefinition>,
                          boost::shared_ptr<ParticleGroup>,
                          boost::shared_ptr<ComputeThermo>,
                          Scalar,
                          boost::shared_ptr<Variant>,
                          const std::string&
                          >())
        ;
    }

#ifdef WIN32
#pragma warning( pop )
#endif

<|endoftext|>