<commit_before>#ifndef ENTT_POLY_POLY_HPP
#define ENTT_POLY_POLY_HPP

#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"

namespace entt {

/*! @brief Inspector class used to infer the type of the virtual table. */
struct poly_inspector {
    /**
     * @brief Generic conversion operator (definition only).
     * @tparam Type Type to which conversion is requested.
     */
    template <class Type>
    operator Type &&() const;

    /**
     * @brief Dummy invocation function (definition only).
     * @tparam Member Index of the function to invoke.
     * @tparam Args Types of arguments to pass to the function.
     * @param args The arguments to pass to the function.
     * @return A poly inspector convertible to any type.
     */
    template<auto Member, typename... Args>
    poly_inspector invoke(Args &&... args) const;

    /*! @copydoc invoke */
    template<auto Member, typename... Args>
    poly_inspector invoke(Args &&... args);
};

/**
 * @brief Static virtual table factory.
 * @tparam Concept Concept descriptor.
 */
template<typename Concept>
class poly_vtable {
    using inspector = typename Concept::template type<poly_inspector>;

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const any &, Args...);

    template<auto... Candidate>
    static auto make_vtable(value_list<Candidate...>)
    -> std::tuple<decltype(vtable_entry(Candidate))...>;

    template<typename... Func>
    [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>)  {
        if constexpr(sizeof...(Func) == 0) {
            return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
        } else if constexpr((std::is_function_v<Func> && ...)) {
            return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector:: *>())...)){};
        }
    }

    template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
    static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) {
        entry = +[](Any &any, Args... args) -> Ret {
            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
                return std::invoke(Candidate, std::forward<Args>(args)...);
            } else {
                return std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(any), std::forward<Args>(args)...);
            }
        };
    }

    template<typename Type, auto... Candidate, auto... Index>
    [[nodiscard]] static auto fill_vtable(value_list<Candidate...>, std::index_sequence<Index...>) {
        type impl{};
        (fill_vtable_entry<Type, Candidate>(std::get<Index>(impl)), ...);
        return impl;
    }

public:
    /*! @brief Virtual table type. */
    using type = decltype(make_vtable(Concept{}));

    /**
     * @brief Returns a static virtual table for a specific concept and type.
     * @tparam Type The type for which to generate the virtual table.
     * @return A static virtual table for the given concept and type.
     */
    template<typename Type>
    [[nodiscard]] static const auto * instance() {
        static const auto vtable = fill_vtable<Type>(typename Concept::template impl<Type>{}, std::make_index_sequence<std::tuple_size_v<type>>{});
        return &vtable;
    }
};

/**
 * @brief Poly base class used to inject functionalities into concepts.
 * @tparam Poly The outermost poly class.
 */
template<typename Poly>
struct poly_base {
    /**
     * @brief Invokes a function from the static virtual table.
     * @tparam Member Index of the function to invoke.
     * @tparam Args Types of arguments to pass to the function.
     * @param self A reference to the poly object that made the call.
     * @param args The arguments to pass to the function.
     * @return The return value of the invoked function, if any.
     */
    template<auto Member, typename... Args>
    [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const {
        const auto &poly = static_cast<const Poly &>(self);
        return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
    }

    /*! @copydoc invoke */
    template<auto Member, typename... Args>
    [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) {
        auto &poly = static_cast<Poly &>(self);
        return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
    }
};

/**
 * @brief Shortcut for calling `poly_base<Type>::invoke`.
 * @tparam Member Index of the function to invoke.
 * @tparam Poly A fully defined poly object.
 * @tparam Args Types of arguments to pass to the function.
 * @param self A reference to the poly object that made the call.
 * @param args The arguments to pass to the function.
 * @return The return value of the invoked function, if any.
 */
template<auto Member, typename Poly, typename... Args>
decltype(auto) poly_call(Poly &&self, Args &&... args) {
    return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
}

/**
 * @brief Static polymorphism made simple and within everyone's reach.
 *
 * Static polymorphism is a very powerful tool in C++, albeit sometimes
 * cumbersome to obtain.

 * This class aims to make it simple and easy to use.
 *
 * @note
 * Both deduced and defined static virtual tables are supported.

 * Moreover, the `poly` class template also works with unmanaged objects.
 *
 * @tparam Concept Concept descriptor.
 */
template<typename Concept>
class poly: private Concept::template type<poly_base<poly<Concept>>> {
    /*! @brief A poly base is allowed to snoop into a poly object. */
    friend struct poly_base<poly<Concept>>;

    using vtable_type = typename poly_vtable<Concept>::type;

public:
    /*! @brief Concept interface type. */
    using interface = typename Concept::template type<poly_base<poly<Concept>>>;

    /*! @brief Default constructor. */
    poly() ENTT_NOEXCEPT
        : storage{},
          vtable{}
    {}

    /**
     * @brief Constructs a poly by directly initializing the new object.
     * @tparam Type Type of object to use to initialize the poly.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    explicit poly(std::in_place_type_t<Type>, Args &&... args)
        : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
          vtable{poly_vtable<Concept>::template instance<Type>()}
    {}

    /**
     * @brief Constructs a poly that holds an unmanaged object.
     * @tparam Type Type of object to use to initialize the poly.
     * @param value An instance of an object to use to initialize the poly.
     */
    template<typename Type>
    poly(std::reference_wrapper<Type> value)
        : storage{value},
          vtable{poly_vtable<Concept>::template instance<Type>()}
    {}

    /**
     * @brief Constructs a poly from a given value.
     * @tparam Type Type of object to use to initialize the poly.
     * @param value An instance of an object to use to initialize the poly.
     */
    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, poly>>>
    poly(Type &&value) ENTT_NOEXCEPT
        : poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
    {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    poly(const poly &other) = default;

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    poly(poly &&other) ENTT_NOEXCEPT
        : poly{}
    {
        swap(*this, other);
    }

    /**
     * @brief Assignment operator.
     * @param other The instance to assign from.
     * @return This poly object.
     */
    poly & operator=(poly other) {
        swap(other, *this);
        return *this;
    }

    /**
     * @brief Returns the type of the contained object.
     * @return The type of the contained object, if any.
     */
    [[nodiscard]] type_info type() const ENTT_NOEXCEPT {
        return storage.type();
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] const void * data() const ENTT_NOEXCEPT {
        return storage.data();
    }

    /*! @copydoc data */
    [[nodiscard]] void * data() ENTT_NOEXCEPT {
        return const_cast<void *>(std::as_const(*this).data());
    }

    /**
     * @brief Replaces the contained object by creating a new instance directly.
     * @tparam Type Type of object to use to initialize the poly.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    void emplace(Args &&... args) {
        storage.emplace<Type>(std::forward<Args>(args)...);
        vtable = poly_vtable<Concept>::template instance<Type>();
    }

    /**
     * @brief Returns false if a poly is empty, true otherwise.
     * @return False if the poly is empty, true otherwise.
     */
    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
        return !(vtable == nullptr);
    }

    /**
     * @brief Returns a pointer to the underlying concept.
     * @return A pointer to the underlying concept.
     */
    [[nodiscard]] interface * operator->() ENTT_NOEXCEPT {
        return this;
    }

    /*! @copydoc operator-> */
    [[nodiscard]] const interface * operator->() const ENTT_NOEXCEPT {
        return this;
    }

    /**
     * @brief Swaps two poly objects.
     * @param lhs A valid poly object.
     * @param rhs A valid poly object.
     */
    friend void swap(poly &lhs, poly &rhs) {
        using std::swap;
        swap(lhs.storage, rhs.storage);
        swap(lhs.vtable, rhs.vtable);
    }

    /**
     * @brief Aliasing constructor.
     * @param other A reference to an object that isn't necessarily initialized.
     * @return A poly that shares a reference to an unmanaged object.
     */
    [[nodiscard]] friend poly as_ref(const poly &other) ENTT_NOEXCEPT {
        poly ref;
        ref.storage = as_ref(other.storage);
        ref.vtable = other.vtable;
        return ref;
    }

private:
    any storage;
    const vtable_type *vtable;
};

}

#endif
<commit_msg>poly: minor changes<commit_after>#ifndef ENTT_POLY_POLY_HPP
#define ENTT_POLY_POLY_HPP

#include <cstddef>
#include <functional>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"

namespace entt {

/*! @brief Inspector class used to infer the type of the virtual table. */
struct poly_inspector {
    /**
     * @brief Generic conversion operator (definition only).
     * @tparam Type Type to which conversion is requested.
     */
    template <class Type>
    operator Type &&() const;

    /**
     * @brief Dummy invocation function (definition only).
     * @tparam Member Index of the function to invoke.
     * @tparam Args Types of arguments to pass to the function.
     * @param args The arguments to pass to the function.
     * @return A poly inspector convertible to any type.
     */
    template<auto Member, typename... Args>
    poly_inspector invoke(Args &&... args) const;

    /*! @copydoc invoke */
    template<auto Member, typename... Args>
    poly_inspector invoke(Args &&... args);
};

/**
 * @brief Static virtual table factory.
 * @tparam Concept Concept descriptor.
 */
template<typename Concept>
class poly_vtable {
    using inspector = typename Concept::template type<poly_inspector>;

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(inspector &, Args...)) -> Ret(*)(any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(const inspector &, Args...)) -> Ret(*)(const any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(*)(Args...)) -> Ret(*)(const any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(inspector:: *)(Args...)) -> Ret(*)(any &, Args...);

    template<typename Ret, typename... Args>
    static auto vtable_entry(Ret(inspector:: *)(Args...) const) -> Ret(*)(const any &, Args...);

    template<auto... Candidate>
    static auto make_vtable(value_list<Candidate...>)
    -> std::tuple<decltype(vtable_entry(Candidate))...>;

    template<typename... Func>
    [[nodiscard]] static constexpr auto make_vtable(type_list<Func...>)  {
        if constexpr(sizeof...(Func) == 0) {
            return decltype(make_vtable(typename Concept::template impl<inspector>{})){};
        } else if constexpr((std::is_function_v<Func> && ...)) {
            return decltype(std::make_tuple(vtable_entry(std::declval<Func inspector:: *>())...)){};
        }
    }

    template<typename Type, auto Candidate, typename Ret, typename Any, typename... Args>
    static void fill_vtable_entry(Ret(* &entry)(Any &, Args...)) {
        entry = +[](Any &any, Args... args) -> Ret {
            if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Args...>) {
                return std::invoke(Candidate, std::forward<Args>(args)...);
            } else {
                return std::invoke(Candidate, any_cast<constness_as_t<Type, Any> &>(any), std::forward<Args>(args)...);
            }
        };
    }

    template<typename Type, auto... Index>
    [[nodiscard]] static auto fill_vtable(std::index_sequence<Index...>) {
        type impl{};
        (fill_vtable_entry<Type, value_list_element_v<Index, typename Concept::template impl<Type>>>(std::get<Index>(impl)), ...);
        return impl;
    }

public:
    /*! @brief Virtual table type. */
    using type = decltype(make_vtable(Concept{}));

    /**
     * @brief Returns a static virtual table for a specific concept and type.
     * @tparam Type The type for which to generate the virtual table.
     * @return A static virtual table for the given concept and type.
     */
    template<typename Type>
    [[nodiscard]] static const auto * instance() {
        static const auto vtable = fill_vtable<Type>(std::make_index_sequence<Concept::template impl<Type>::size>{});
        return &vtable;
    }
};

/**
 * @brief Poly base class used to inject functionalities into concepts.
 * @tparam Poly The outermost poly class.
 */
template<typename Poly>
struct poly_base {
    /**
     * @brief Invokes a function from the static virtual table.
     * @tparam Member Index of the function to invoke.
     * @tparam Args Types of arguments to pass to the function.
     * @param self A reference to the poly object that made the call.
     * @param args The arguments to pass to the function.
     * @return The return value of the invoked function, if any.
     */
    template<auto Member, typename... Args>
    [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&... args) const {
        const auto &poly = static_cast<const Poly &>(self);
        return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
    }

    /*! @copydoc invoke */
    template<auto Member, typename... Args>
    [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&... args) {
        auto &poly = static_cast<Poly &>(self);
        return std::get<Member>(*poly.vtable)(poly.storage, std::forward<Args>(args)...);
    }
};

/**
 * @brief Shortcut for calling `poly_base<Type>::invoke`.
 * @tparam Member Index of the function to invoke.
 * @tparam Poly A fully defined poly object.
 * @tparam Args Types of arguments to pass to the function.
 * @param self A reference to the poly object that made the call.
 * @param args The arguments to pass to the function.
 * @return The return value of the invoked function, if any.
 */
template<auto Member, typename Poly, typename... Args>
decltype(auto) poly_call(Poly &&self, Args &&... args) {
    return std::forward<Poly>(self).template invoke<Member>(self, std::forward<Args>(args)...);
}

/**
 * @brief Static polymorphism made simple and within everyone's reach.
 *
 * Static polymorphism is a very powerful tool in C++, albeit sometimes
 * cumbersome to obtain.

 * This class aims to make it simple and easy to use.
 *
 * @note
 * Both deduced and defined static virtual tables are supported.

 * Moreover, the `poly` class template also works with unmanaged objects.
 *
 * @tparam Concept Concept descriptor.
 */
template<typename Concept>
class poly: private Concept::template type<poly_base<poly<Concept>>> {
    /*! @brief A poly base is allowed to snoop into a poly object. */
    friend struct poly_base<poly<Concept>>;

    using vtable_type = typename poly_vtable<Concept>::type;

public:
    /*! @brief Concept interface type. */
    using interface = typename Concept::template type<poly_base<poly<Concept>>>;

    /*! @brief Default constructor. */
    poly() ENTT_NOEXCEPT
        : storage{},
          vtable{}
    {}

    /**
     * @brief Constructs a poly by directly initializing the new object.
     * @tparam Type Type of object to use to initialize the poly.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    explicit poly(std::in_place_type_t<Type>, Args &&... args)
        : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
          vtable{poly_vtable<Concept>::template instance<Type>()}
    {}

    /**
     * @brief Constructs a poly that holds an unmanaged object.
     * @tparam Type Type of object to use to initialize the poly.
     * @param value An instance of an object to use to initialize the poly.
     */
    template<typename Type>
    poly(std::reference_wrapper<Type> value)
        : storage{value},
          vtable{poly_vtable<Concept>::template instance<Type>()}
    {}

    /**
     * @brief Constructs a poly from a given value.
     * @tparam Type Type of object to use to initialize the poly.
     * @param value An instance of an object to use to initialize the poly.
     */
    template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, poly>>>
    poly(Type &&value) ENTT_NOEXCEPT
        : poly{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
    {}

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    poly(const poly &other) = default;

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    poly(poly &&other) ENTT_NOEXCEPT
        : poly{}
    {
        swap(*this, other);
    }

    /**
     * @brief Assignment operator.
     * @param other The instance to assign from.
     * @return This poly object.
     */
    poly & operator=(poly other) {
        swap(other, *this);
        return *this;
    }

    /**
     * @brief Returns the type of the contained object.
     * @return The type of the contained object, if any.
     */
    [[nodiscard]] type_info type() const ENTT_NOEXCEPT {
        return storage.type();
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] const void * data() const ENTT_NOEXCEPT {
        return storage.data();
    }

    /*! @copydoc data */
    [[nodiscard]] void * data() ENTT_NOEXCEPT {
        return const_cast<void *>(std::as_const(*this).data());
    }

    /**
     * @brief Replaces the contained object by creating a new instance directly.
     * @tparam Type Type of object to use to initialize the poly.
     * @tparam Args Types of arguments to use to construct the new instance.
     * @param args Parameters to use to construct the instance.
     */
    template<typename Type, typename... Args>
    void emplace(Args &&... args) {
        storage.emplace<Type>(std::forward<Args>(args)...);
        vtable = poly_vtable<Concept>::template instance<Type>();
    }

    /**
     * @brief Returns false if a poly is empty, true otherwise.
     * @return False if the poly is empty, true otherwise.
     */
    [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
        return !(vtable == nullptr);
    }

    /**
     * @brief Returns a pointer to the underlying concept.
     * @return A pointer to the underlying concept.
     */
    [[nodiscard]] interface * operator->() ENTT_NOEXCEPT {
        return this;
    }

    /*! @copydoc operator-> */
    [[nodiscard]] const interface * operator->() const ENTT_NOEXCEPT {
        return this;
    }

    /**
     * @brief Swaps two poly objects.
     * @param lhs A valid poly object.
     * @param rhs A valid poly object.
     */
    friend void swap(poly &lhs, poly &rhs) {
        using std::swap;
        swap(lhs.storage, rhs.storage);
        swap(lhs.vtable, rhs.vtable);
    }

    /**
     * @brief Aliasing constructor.
     * @param other A reference to an object that isn't necessarily initialized.
     * @return A poly that shares a reference to an unmanaged object.
     */
    [[nodiscard]] friend poly as_ref(const poly &other) ENTT_NOEXCEPT {
        poly ref;
        ref.storage = as_ref(other.storage);
        ref.vtable = other.vtable;
        return ref;
    }

private:
    any storage;
    const vtable_type *vtable;
};

}

#endif
<|endoftext|>


<commit_before>/*
  Q Light Controller
  vcxypadfixtureeditor_test.cpp

  Copyright (C) Heikki Junnila

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include <QtTest>
#include <QtXml>

#define private public
#define protected public
#include "vcxypadfixtureeditor.h"
#undef private
#undef protected

#include "vcxypadfixtureeditor_test.h"
#include "qlcfixturemode.h"
#include "qlcfixturedef.h"
#include "qlcfile.h"
#include "doc.h"

#include "../../../engine/test/common/resource_paths.h"

void VCXYPadFixtureEditor_Test::initTestCase()
{
    m_doc = new Doc(this);

    QDir dir(INTERNAL_FIXTUREDIR);
    dir.setFilter(QDir::Files);
    dir.setNameFilters(QStringList() << QString("*%1").arg(KExtFixture));
    QVERIFY(m_doc->fixtureDefCache()->loadMap(dir) == true);

    QLCFixtureDef* def = m_doc->fixtureDefCache()->fixtureDef("Futurelight", "DJScan250");
    QVERIFY(def != NULL);
    QLCFixtureMode* mode = def->modes()[0];
    QVERIFY(mode != NULL);

    Fixture* fxi = new Fixture(m_doc);
    fxi->setName("Fixture 1");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(0);

    fxi = new Fixture(m_doc);
    fxi->setName("Fixture 2");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(10);

    fxi = new Fixture(m_doc);
    fxi->setName("Fixture 3");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(20);
}

void VCXYPadFixtureEditor_Test::initial()
{
    QList <VCXYPadFixture> list;

    VCXYPadFixture fxi(m_doc);

    fxi.setHead(GroupHead(0, 0));
    fxi.setX(0.1, 0.2, false);
    fxi.setY(0.3, 0.4, true);
    list << fxi;

    fxi.setHead(GroupHead(1, 0));
    fxi.setX(0, 1, true);
    fxi.setY(0, 1, false);
    list << fxi;

    VCXYPadFixtureEditor fe(NULL, list);
    QCOMPARE(fe.fixtures(), list);
    QCOMPARE(fe.m_xMin->value(), 10);
    QCOMPARE(fe.m_xMax->value(), 20);
    QCOMPARE(fe.m_xReverse->isChecked(), false);
    QCOMPARE(fe.m_yMin->value(), 30);
    QCOMPARE(fe.m_yMax->value(), 40);
    QCOMPARE(fe.m_yReverse->isChecked(), true);

    list.clear();

    VCXYPadFixtureEditor fe2(NULL, list);
    QCOMPARE(fe2.fixtures().isEmpty(), true);
    QCOMPARE(fe2.m_xMin->value(), 0);
    QCOMPARE(fe2.m_xMax->value(), 100);
    QCOMPARE(fe2.m_xReverse->isChecked(), false);
    QCOMPARE(fe2.m_yMin->value(), 0);
    QCOMPARE(fe2.m_yMax->value(), 100);
    QCOMPARE(fe2.m_yReverse->isChecked(), false);
}

void VCXYPadFixtureEditor_Test::valueSlots()
{
    QList <VCXYPadFixture> list;
    VCXYPadFixtureEditor fe(NULL, list);

    fe.m_xMin->setValue(50);
    fe.m_xMax->setValue(20);
    QCOMPARE(fe.m_xMin->value(), 19);
    QCOMPARE(fe.m_xMax->value(), 20);

    fe.m_xMin->setValue(40);
    QCOMPARE(fe.m_xMin->value(), 40);
    QCOMPARE(fe.m_xMax->value(), 41);

    fe.m_yMin->setValue(50);
    fe.m_yMax->setValue(20);
    QCOMPARE(fe.m_yMin->value(), 19);
    QCOMPARE(fe.m_yMax->value(), 20);

    fe.m_yMin->setValue(40);
    QCOMPARE(fe.m_yMin->value(), 40);
    QCOMPARE(fe.m_yMax->value(), 41);
}

void VCXYPadFixtureEditor_Test::accept()
{
    QList <VCXYPadFixture> list;

    VCXYPadFixture fxi(m_doc);

    fxi.setHead(GroupHead(0, 0));
    fxi.setX(0, 1, false);
    fxi.setY(0, 1, false);
    list << fxi;

    fxi.setHead(GroupHead(1, 0));
    fxi.setX(0.5, 0.6, true);
    fxi.setY(0.5, 0.6, true);
    list << fxi;

    VCXYPadFixtureEditor fe(NULL, list);
    fe.m_xMin->setValue(10);
    fe.m_xMax->setValue(20);
    fe.m_yMin->setValue(30);
    fe.m_yMax->setValue(40);
    fe.accept();
    QCOMPARE(fe.m_xMin->value(), 10);
    QCOMPARE(fe.m_xMax->value(), 20);
    QCOMPARE(fe.m_yMin->value(), 30);
    QCOMPARE(fe.m_yMax->value(), 40);

    list = fe.fixtures();
    QCOMPARE(list[0].head().fxi, quint32(0));
    QCOMPARE(list[0].head().head, 0);
    QCOMPARE(list[0].xMin(), qreal(0.1));
    QCOMPARE(list[0].xMax(), qreal(0.2));
    QCOMPARE(list[0].yMin(), qreal(0.3));
    QCOMPARE(list[0].yMax(), qreal(0.4));
    QCOMPARE(list[1].head().fxi, quint32(1));
    QCOMPARE(list[1].head().head, 0);
    QCOMPARE(list[1].xMin(), qreal(0.1));
    QCOMPARE(list[1].xMax(), qreal(0.2));
    QCOMPARE(list[1].yMin(), qreal(0.3));
    QCOMPARE(list[1].yMax(), qreal(0.4));
}

void VCXYPadFixtureEditor_Test::cleanupTestCase()
{
    delete m_doc;
    m_doc = NULL;
}

QTEST_MAIN(VCXYPadFixtureEditor_Test)
<commit_msg>Fixed VCXYPadFixtureEditor test unit<commit_after>/*
  Q Light Controller
  vcxypadfixtureeditor_test.cpp

  Copyright (C) Heikki Junnila

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0.txt

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#include <QtTest>
#include <QtXml>

#define private public
#define protected public
#include "vcxypadfixtureeditor.h"
#undef private
#undef protected

#include "vcxypadfixtureeditor_test.h"
#include "qlcfixturemode.h"
#include "qlcfixturedef.h"
#include "qlcfile.h"
#include "doc.h"

#include "../../../engine/test/common/resource_paths.h"

void VCXYPadFixtureEditor_Test::initTestCase()
{
    m_doc = new Doc(this);

    QDir dir(INTERNAL_FIXTUREDIR);
    dir.setFilter(QDir::Files);
    dir.setNameFilters(QStringList() << QString("*%1").arg(KExtFixture));
    QVERIFY(m_doc->fixtureDefCache()->loadMap(dir) == true);

    QLCFixtureDef* def = m_doc->fixtureDefCache()->fixtureDef("Futurelight", "DJScan250");
    QVERIFY(def != NULL);
    QLCFixtureMode* mode = def->modes()[0];
    QVERIFY(mode != NULL);

    Fixture* fxi = new Fixture(m_doc);
    fxi->setName("Fixture 1");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(0);

    fxi = new Fixture(m_doc);
    fxi->setName("Fixture 2");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(10);

    fxi = new Fixture(m_doc);
    fxi->setName("Fixture 3");
    fxi->setFixtureDefinition(def, mode);
    fxi->setAddress(20);
}

void VCXYPadFixtureEditor_Test::initial()
{
    QList <VCXYPadFixture> list;

    VCXYPadFixture fxi(m_doc);
    fxi.setDisplayMode(VCXYPadFixture::Percentage);

    fxi.setHead(GroupHead(0, 0));
    fxi.setX(0.1, 0.2, false);
    fxi.setY(0.3, 0.4, true);
    list << fxi;

    fxi.setHead(GroupHead(1, 0));
    fxi.setX(0, 1, true);
    fxi.setY(0, 1, false);
    list << fxi;

    VCXYPadFixtureEditor fe(NULL, list);
    QCOMPARE(fe.fixtures(), list);
    QCOMPARE(fe.m_xMin->value(), 10);
    QCOMPARE(fe.m_xMax->value(), 20);
    QCOMPARE(fe.m_xReverse->isChecked(), false);
    QCOMPARE(fe.m_yMin->value(), 30);
    QCOMPARE(fe.m_yMax->value(), 40);
    QCOMPARE(fe.m_yReverse->isChecked(), true);

    list.clear();

    VCXYPadFixtureEditor fe2(NULL, list);
    QCOMPARE(fe2.fixtures().isEmpty(), true);
    QCOMPARE(fe2.m_xMin->value(), 0);
    QCOMPARE(fe2.m_xMax->value(), 100);
    QCOMPARE(fe2.m_xReverse->isChecked(), false);
    QCOMPARE(fe2.m_yMin->value(), 0);
    QCOMPARE(fe2.m_yMax->value(), 100);
    QCOMPARE(fe2.m_yReverse->isChecked(), false);
}

void VCXYPadFixtureEditor_Test::valueSlots()
{
    QList <VCXYPadFixture> list;
    VCXYPadFixtureEditor fe(NULL, list);

    fe.m_xMin->setValue(50);
    fe.m_xMax->setValue(20);
    QCOMPARE(fe.m_xMin->value(), 19);
    QCOMPARE(fe.m_xMax->value(), 20);

    fe.m_xMin->setValue(40);
    QCOMPARE(fe.m_xMin->value(), 40);
    QCOMPARE(fe.m_xMax->value(), 41);

    fe.m_yMin->setValue(50);
    fe.m_yMax->setValue(20);
    QCOMPARE(fe.m_yMin->value(), 19);
    QCOMPARE(fe.m_yMax->value(), 20);

    fe.m_yMin->setValue(40);
    QCOMPARE(fe.m_yMin->value(), 40);
    QCOMPARE(fe.m_yMax->value(), 41);
}

void VCXYPadFixtureEditor_Test::accept()
{
    QList <VCXYPadFixture> list;

    VCXYPadFixture fxi(m_doc);
    fxi.setDisplayMode(VCXYPadFixture::Percentage);

    fxi.setHead(GroupHead(0, 0));
    fxi.setX(0, 1, false);
    fxi.setY(0, 1, false);
    list << fxi;

    fxi.setHead(GroupHead(1, 0));
    fxi.setX(0.5, 0.6, true);
    fxi.setY(0.5, 0.6, true);
    list << fxi;

    VCXYPadFixtureEditor fe(NULL, list);
    fe.m_xMin->setValue(10);
    fe.m_xMax->setValue(20);
    fe.m_yMin->setValue(30);
    fe.m_yMax->setValue(40);
    fe.accept();
    QCOMPARE(fe.m_xMin->value(), 10);
    QCOMPARE(fe.m_xMax->value(), 20);
    QCOMPARE(fe.m_yMin->value(), 30);
    QCOMPARE(fe.m_yMax->value(), 40);

    list = fe.fixtures();
    QCOMPARE(list[0].head().fxi, quint32(0));
    QCOMPARE(list[0].head().head, 0);
    QCOMPARE(list[0].xMin(), qreal(0.1));
    QCOMPARE(list[0].xMax(), qreal(0.2));
    QCOMPARE(list[0].yMin(), qreal(0.3));
    QCOMPARE(list[0].yMax(), qreal(0.4));
    QCOMPARE(list[1].head().fxi, quint32(1));
    QCOMPARE(list[1].head().head, 0);
    QCOMPARE(list[1].xMin(), qreal(0.1));
    QCOMPARE(list[1].xMax(), qreal(0.2));
    QCOMPARE(list[1].yMin(), qreal(0.3));
    QCOMPARE(list[1].yMax(), qreal(0.4));
}

void VCXYPadFixtureEditor_Test::cleanupTestCase()
{
    delete m_doc;
    m_doc = NULL;
}

QTEST_MAIN(VCXYPadFixtureEditor_Test)
<|endoftext|>


<commit_before>#include "logger.hpp"

#include <cstdarg>
#include <algorithm>

#include <mbed.h>
#include <rtos.h>

const char* LOG_LEVEL_STRING[] = {FOREACH_LEVEL(GENERATE_STRING)};

/* The initial logging level shows startup info along with any
 * warning messages [but hopefully there's none of those :) ].
 */
bool isLogging;  // = RJ_LOGGING_EN;

uint8_t rjLogLevel;

Mutex log_mutex;

LogHelper::LogHelper(uint8_t logLevel, const char* source, int line,
                     const char* func) {
    _logLevel = logLevel;
    _source = source;
    _line = line;
    _func = func;
}

LogHelper::~LogHelper() {
    log(_logLevel, _source, _line, _func, "%s", str().c_str());
}

/**
 * [log The system-wide logging interface function. All log messages go through
 * this.]
 * @param logLevel [The "importance level" of the called log message.]
 * @param source   [The source of the message.]
 * @param format   [The string format for displaying the log message.]
 */
void log(uint8_t logLevel, const char* source, int line, const char* func,
         const char* format, ...) {
    if (isLogging && logLevel <= rjLogLevel) {
        log_mutex.lock();

        va_list args;
        char newFormat[1024];
        char time_buf[25];
        time_t sys_time = time(NULL);
        strftime(time_buf, 25, "%H:%M:%S", localtime(&sys_time));

        snprintf(newFormat, sizeof(newFormat),
                 "%s [%s] [%s:%d] <%s>\r\n  %s\r\n\r\n", time_buf,
                 LOG_LEVEL_STRING[logLevel], source, line, func, format);

        va_start(args, format);

        fflush(stdout);
        vprintf(newFormat, args);
        fflush(stdout);

        va_end(args);
        log_mutex.unlock();
    }
}

int logLvlChange(const std::string& s) {
    int n = 0;

    n += std::count(s.begin(), s.end(), '+');
    n -= std::count(s.begin(), s.end(), '-');

    return n;
}
<commit_msg>fixed out-of-memory issue introduced by PR #567<commit_after>#include "logger.hpp"

#include <cstdarg>
#include <algorithm>

#include <mbed.h>
#include <rtos.h>

const char* LOG_LEVEL_STRING[] = {FOREACH_LEVEL(GENERATE_STRING)};

/* The initial logging level shows startup info along with any
 * warning messages [but hopefully there's none of those :) ].
 */
bool isLogging;  // = RJ_LOGGING_EN;

uint8_t rjLogLevel;

Mutex log_mutex;

LogHelper::LogHelper(uint8_t logLevel, const char* source, int line,
                     const char* func) {
    _logLevel = logLevel;
    _source = source;
    _line = line;
    _func = func;
}

LogHelper::~LogHelper() {
    log(_logLevel, _source, _line, _func, "%s", str().c_str());
}

/**
 * [log The system-wide logging interface function. All log messages go through
 * this.]
 * @param logLevel [The "importance level" of the called log message.]
 * @param source   [The source of the message.]
 * @param format   [The string format for displaying the log message.]
 */
void log(uint8_t logLevel, const char* source, int line, const char* func,
         const char* format, ...) {
    if (isLogging && logLevel <= rjLogLevel) {
        log_mutex.lock();

        va_list args;
        static char newFormat[300];
        char time_buf[25];
        time_t sys_time = time(NULL);
        strftime(time_buf, 25, "%H:%M:%S", localtime(&sys_time));

        snprintf(newFormat, sizeof(newFormat),
                 "%s [%s] [%s:%d] <%s>\r\n  %s\r\n\r\n", time_buf,
                 LOG_LEVEL_STRING[logLevel], source, line, func, format);

        va_start(args, format);

        fflush(stdout);
        vprintf(newFormat, args);
        fflush(stdout);

        va_end(args);
        log_mutex.unlock();
    }
}

int logLvlChange(const std::string& s) {
    int n = 0;

    n += std::count(s.begin(), s.end(), '+');
    n -= std::count(s.begin(), s.end(), '-');

    return n;
}
<|endoftext|>


<commit_before><commit_msg>Adjusted visible area.<commit_after><|endoftext|>


<commit_before>#pragma once

#include <cstdarg>
#include <string>
#include <sstream>

#include "rj-macros.hpp"

// Do weird macro things for logging the filename and line for every call.
// Also allows for disabling all logging through macros so all log calls can be
// removed from production builds.
#ifdef RJ_LOGGING_EN

// Gets curent file name without path
// see http://stackoverflow.com/questions/8487986/file-macro-shows-full-path
#ifdef __FILE_NAME__
#define __BASE_FILE_NAME__                                         \
    (strrchr(__FILE_NAME__, '/') ? strrchr(__FILE_NAME__, '/') + 1 \
                                 : __FILE_NAME__)
#else  // __FILE_NAME__
#define __BASE_FILE_NAME__ \
    (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)
#endif

#define LOG(lvl, ...) \
    log(lvl, __BASE_FILE_NAME__, __LINE__, __func__, __VA_ARGS__)

#define S_LOG(lvl) LogHelper(lvl, __BASE_FILE_NAME__, __LINE__, __func__)

#else             // RJ_LOGGING_EN
#define LOG(...)  // Nothing
#endif

#define FOREACH_LEVEL(LEVEL) \
    LEVEL(LOG_LEVEL_START)   \
    LEVEL(FATAL)             \
    LEVEL(SEVERE)            \
    LEVEL(WARN)              \
    LEVEL(INIT)              \
    LEVEL(OK)                \
    LEVEL(INF1)              \
    LEVEL(INF2)              \
    LEVEL(INF3)              \
    LEVEL(LOG_LEVEL_END)

/**
 * Log levels.
 */
enum LOG_LEVEL { FOREACH_LEVEL(GENERATE_ENUM) };

/**
 * Enumeration -> String conversions.
 */
extern const char* LOG_LEVEL_STRING[];

/**
 * Active logging.
 */
extern bool isLogging;

/**
 * Current log level.
 */
extern uint8_t rjLogLevel;

/**
 * Collects the stream log message into a single string to print
 * @param logLevel The "importance level" of the called log message.
 * @param source   The source of the message.
 * @param format   The string format for displaying the log message.
 */
class LogHelper : public std::stringstream {
public:
    LogHelper(uint8_t logLevel, const char* source, int line, const char* func);
    ~LogHelper();

private:
    uint8_t _logLevel;
    const char* _source;
    int _line;
    const char* _func;
};

/**
 * [log The system-wide logging interface function. All log messages go through
 * this.]
 * @param logLevel [The "importance level" of the called log message.]
 * @param source   [The source of the message.]
 * @param format   [The string format for displaying the log message.]
 */
void log(uint8_t logLevel, const char* source, int line, const char* func,
         const char* format, ...);

int logLvlChange(const std::string& s);
<commit_msg>Added example usage<commit_after>#pragma once

#include <cstdarg>
#include <string>
#include <sstream>

#include "rj-macros.hpp"

// Do weird macro things for logging the filename and line for every call.
// Also allows for disabling all logging through macros so all log calls can be
// removed from production builds.
#ifdef RJ_LOGGING_EN

// Gets curent file name without path
// see http://stackoverflow.com/questions/8487986/file-macro-shows-full-path
#ifdef __FILE_NAME__
#define __BASE_FILE_NAME__                                         \
    (strrchr(__FILE_NAME__, '/') ? strrchr(__FILE_NAME__, '/') + 1 \
                                 : __FILE_NAME__)
#else  // __FILE_NAME__
#define __BASE_FILE_NAME__ \
    (strrchr(__FILE__, '/') ? (strrchr(__FILE__, '/') + 1) : __FILE__)
#endif

#define LOG(lvl, ...) \
    log(lvl, __BASE_FILE_NAME__, __LINE__, __func__, __VA_ARGS__)

// Example usage:
//   S_LOG(INIT) << "Example" << std::endl;
#define S_LOG(lvl) LogHelper(lvl, __BASE_FILE_NAME__, __LINE__, __func__)

#else             // RJ_LOGGING_EN
#define LOG(...)  // Nothing
#endif

#define FOREACH_LEVEL(LEVEL) \
    LEVEL(LOG_LEVEL_START)   \
    LEVEL(FATAL)             \
    LEVEL(SEVERE)            \
    LEVEL(WARN)              \
    LEVEL(INIT)              \
    LEVEL(OK)                \
    LEVEL(INF1)              \
    LEVEL(INF2)              \
    LEVEL(INF3)              \
    LEVEL(LOG_LEVEL_END)

/**
 * Log levels.
 */
enum LOG_LEVEL { FOREACH_LEVEL(GENERATE_ENUM) };

/**
 * Enumeration -> String conversions.
 */
extern const char* LOG_LEVEL_STRING[];

/**
 * Active logging.
 */
extern bool isLogging;

/**
 * Current log level.
 */
extern uint8_t rjLogLevel;

/**
 * Collects the stream log message into a single string to print
 * @param logLevel The "importance level" of the called log message.
 * @param source   The source of the message.
 * @param format   The string format for displaying the log message.
 */
class LogHelper : public std::stringstream {
public:
    LogHelper(uint8_t logLevel, const char* source, int line, const char* func);
    ~LogHelper();

private:
    uint8_t _logLevel;
    const char* _source;
    int _line;
    const char* _func;
};

/**
 * [log The system-wide logging interface function. All log messages go through
 * this.]
 * @param logLevel [The "importance level" of the called log message.]
 * @param source   [The source of the message.]
 * @param format   [The string format for displaying the log message.]
 */
void log(uint8_t logLevel, const char* source, int line, const char* func,
         const char* format, ...);

int logLvlChange(const std::string& s);
<|endoftext|>


<commit_before>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/shell/common/animator.h"

#include "flutter/fml/trace_event.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"

namespace flutter {

namespace {

// Wait 51 milliseconds (which is 1 more milliseconds than 3 frames at 60hz)
// before notifying the engine that we are idle.  See comments in |BeginFrame|
// for further discussion on why this is necessary.
constexpr fml::TimeDelta kNotifyIdleTaskWaitTime =
    fml::TimeDelta::FromMilliseconds(51);

}  // namespace

Animator::Animator(Delegate& delegate,
                   TaskRunners task_runners,
                   std::unique_ptr<VsyncWaiter> waiter)
    : delegate_(delegate),
      task_runners_(std::move(task_runners)),
      waiter_(std::move(waiter)),
      last_begin_frame_time_(),
      dart_frame_deadline_(0),
      layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(2)),
      pending_frame_semaphore_(1),
      frame_number_(1),
      paused_(false),
      regenerate_layer_tree_(false),
      frame_scheduled_(false),
      notify_idle_task_id_(0),
      dimension_change_pending_(false),
      weak_factory_(this) {}

Animator::~Animator() = default;

float Animator::GetDisplayRefreshRate() const {
  return waiter_->GetDisplayRefreshRate();
}

void Animator::Stop() {
  paused_ = true;
}

void Animator::Start() {
  if (!paused_) {
    return;
  }

  paused_ = false;
  RequestFrame();
}

// Indicate that screen dimensions will be changing in order to force rendering
// of an updated frame even if the animator is currently paused.
void Animator::SetDimensionChangePending() {
  dimension_change_pending_ = true;
}

void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
  fml::TaskRunner::RunNowOrPostTask(
      task_runners_.GetUITaskRunner(),
      [self = weak_factory_.GetWeakPtr(), trace_flow_id] {
        if (!self) {
          return;
        }
        self->trace_flow_ids_.push_back(trace_flow_id);
      });
}

// This Parity is used by the timeline component to correctly align
// GPU Workloads events with their respective Framework Workload.
const char* Animator::FrameParity() {
  return (frame_number_ % 2) ? "even" : "odd";
}

static int64_t FxlToDartOrEarlier(fml::TimePoint time) {
  int64_t dart_now = Dart_TimelineGetMicros();
  fml::TimePoint fxl_now = fml::TimePoint::Now();
  return (time - fxl_now).ToMicroseconds() + dart_now;
}

void Animator::BeginFrame(fml::TimePoint frame_start_time,
                          fml::TimePoint frame_target_time) {
  TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);

  TRACE_EVENT0("flutter", "Animator::BeginFrame");
  while (!trace_flow_ids_.empty()) {
    uint64_t trace_flow_id = trace_flow_ids_.front();
    TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
    trace_flow_ids_.pop_front();
  }

  frame_scheduled_ = false;
  notify_idle_task_id_++;
  regenerate_layer_tree_ = false;
  pending_frame_semaphore_.Signal();

  if (!producer_continuation_) {
    // We may already have a valid pipeline continuation in case a previous
    // begin frame did not result in an Animation::Render. Simply reuse that
    // instead of asking the pipeline for a fresh continuation.
    producer_continuation_ = layer_tree_pipeline_->Produce();

    if (!producer_continuation_) {
      // If we still don't have valid continuation, the pipeline is currently
      // full because the consumer is being too slow. Try again at the next
      // frame interval.
      RequestFrame();
      return;
    }
  }

  // We have acquired a valid continuation from the pipeline and are ready
  // to service potential frame.
  FML_DCHECK(producer_continuation_);

  last_begin_frame_time_ = frame_start_time;
  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
  {
    TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
                 FrameParity());
    delegate_.OnAnimatorBeginFrame(last_begin_frame_time_);
  }

  if (!frame_scheduled_) {
    // Under certain workloads (such as our parent view resizing us, which is
    // communicated to us by repeat viewport metrics events), we won't
    // actually have a frame scheduled yet, despite the fact that we *will* be
    // producing a frame next vsync (it will be scheduled once we receive the
    // viewport event).  Because of this, we hold off on calling
    // |OnAnimatorNotifyIdle| for a little bit, as that could cause garbage
    // collection to trigger at a highly undesirable time.
    task_runners_.GetUITaskRunner()->PostDelayedTask(
        [self = weak_factory_.GetWeakPtr(),
         notify_idle_task_id = notify_idle_task_id_]() {
          if (!self.get()) {
            return;
          }
          // If our (this task's) task id is the same as the current one
          // (meaning there were no follow up frames to the |BeginFrame| call
          // that posted this task) and no frame is currently scheduled, then
          // assume that we are idle, and notify the engine of this.
          if (notify_idle_task_id == self->notify_idle_task_id_ &&
              !self->frame_scheduled_) {
            TRACE_EVENT0("flutter", "BeginFrame idle callback");
            self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
                                                 100000);
          }
        },
        kNotifyIdleTaskWaitTime);
  }
}

void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
  if (dimension_change_pending_ &&
      layer_tree->frame_size() != last_layer_tree_size_) {
    dimension_change_pending_ = false;
  }
  last_layer_tree_size_ = layer_tree->frame_size();

  if (layer_tree) {
    // Note the frame time for instrumentation.
    layer_tree->set_construction_time(fml::TimePoint::Now() -
                                      last_begin_frame_time_);
  }

  // Commit the pending continuation.
  producer_continuation_.Complete(std::move(layer_tree));

  delegate_.OnAnimatorDraw(layer_tree_pipeline_);
}

bool Animator::CanReuseLastLayerTree() {
  return !regenerate_layer_tree_;
}

void Animator::DrawLastLayerTree() {
  pending_frame_semaphore_.Signal();
  delegate_.OnAnimatorDrawLastLayerTree();
}

void Animator::RequestFrame(bool regenerate_layer_tree) {
  if (regenerate_layer_tree) {
    regenerate_layer_tree_ = true;
  }
  if (paused_ && !dimension_change_pending_) {
    return;
  }

  if (!pending_frame_semaphore_.TryWait()) {
    // Multiple calls to Animator::RequestFrame will still result in a
    // single request to the VsyncWaiter.
    return;
  }

  // The AwaitVSync is going to call us back at the next VSync. However, we want
  // to be reasonably certain that the UI thread is not in the middle of a
  // particularly expensive callout. We post the AwaitVSync to run right after
  // an idle. This does NOT provide a guarantee that the UI thread has not
  // started an expensive operation right after posting this message however.
  // To support that, we need edge triggered wakes on VSync.

  task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
                                             frame_number = frame_number_]() {
    if (!self.get()) {
      return;
    }
    TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
    self->AwaitVSync();
  });
  frame_scheduled_ = true;
}

void Animator::AwaitVSync() {
  waiter_->AsyncWaitForVsync(
      [self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
                                          fml::TimePoint frame_target_time) {
        if (self) {
          if (self->CanReuseLastLayerTree()) {
            self->DrawLastLayerTree();
          } else {
            self->BeginFrame(frame_start_time, frame_target_time);
          }
        }
      });

  delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}

}  // namespace flutter
<commit_msg>Reduce pipeline depth when GPU and Platform are same thread (#9132)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/shell/common/animator.h"

#include "flutter/fml/trace_event.h"
#include "third_party/dart/runtime/include/dart_tools_api.h"

namespace flutter {

namespace {

// Wait 51 milliseconds (which is 1 more milliseconds than 3 frames at 60hz)
// before notifying the engine that we are idle.  See comments in |BeginFrame|
// for further discussion on why this is necessary.
constexpr fml::TimeDelta kNotifyIdleTaskWaitTime =
    fml::TimeDelta::FromMilliseconds(51);

}  // namespace

Animator::Animator(Delegate& delegate,
                   TaskRunners task_runners,
                   std::unique_ptr<VsyncWaiter> waiter)
    : delegate_(delegate),
      task_runners_(std::move(task_runners)),
      waiter_(std::move(waiter)),
      last_begin_frame_time_(),
      dart_frame_deadline_(0),
      // TODO(dnfield): We should remove this logic and set the pipeline depth
      // back to 2 in this case. See https://github.com/flutter/engine/pull/9132
      // for discussion.
      layer_tree_pipeline_(fml::MakeRefCounted<LayerTreePipeline>(
          task_runners.GetPlatformTaskRunner() ==
                  task_runners.GetGPUTaskRunner()
              ? 1
              : 2)),
      pending_frame_semaphore_(1),
      frame_number_(1),
      paused_(false),
      regenerate_layer_tree_(false),
      frame_scheduled_(false),
      notify_idle_task_id_(0),
      dimension_change_pending_(false),
      weak_factory_(this) {}

Animator::~Animator() = default;

float Animator::GetDisplayRefreshRate() const {
  return waiter_->GetDisplayRefreshRate();
}

void Animator::Stop() {
  paused_ = true;
}

void Animator::Start() {
  if (!paused_) {
    return;
  }

  paused_ = false;
  RequestFrame();
}

// Indicate that screen dimensions will be changing in order to force rendering
// of an updated frame even if the animator is currently paused.
void Animator::SetDimensionChangePending() {
  dimension_change_pending_ = true;
}

void Animator::EnqueueTraceFlowId(uint64_t trace_flow_id) {
  fml::TaskRunner::RunNowOrPostTask(
      task_runners_.GetUITaskRunner(),
      [self = weak_factory_.GetWeakPtr(), trace_flow_id] {
        if (!self) {
          return;
        }
        self->trace_flow_ids_.push_back(trace_flow_id);
      });
}

// This Parity is used by the timeline component to correctly align
// GPU Workloads events with their respective Framework Workload.
const char* Animator::FrameParity() {
  return (frame_number_ % 2) ? "even" : "odd";
}

static int64_t FxlToDartOrEarlier(fml::TimePoint time) {
  int64_t dart_now = Dart_TimelineGetMicros();
  fml::TimePoint fxl_now = fml::TimePoint::Now();
  return (time - fxl_now).ToMicroseconds() + dart_now;
}

void Animator::BeginFrame(fml::TimePoint frame_start_time,
                          fml::TimePoint frame_target_time) {
  TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);

  TRACE_EVENT0("flutter", "Animator::BeginFrame");
  while (!trace_flow_ids_.empty()) {
    uint64_t trace_flow_id = trace_flow_ids_.front();
    TRACE_FLOW_END("flutter", "PointerEvent", trace_flow_id);
    trace_flow_ids_.pop_front();
  }

  frame_scheduled_ = false;
  notify_idle_task_id_++;
  regenerate_layer_tree_ = false;
  pending_frame_semaphore_.Signal();

  if (!producer_continuation_) {
    // We may already have a valid pipeline continuation in case a previous
    // begin frame did not result in an Animation::Render. Simply reuse that
    // instead of asking the pipeline for a fresh continuation.
    producer_continuation_ = layer_tree_pipeline_->Produce();

    if (!producer_continuation_) {
      // If we still don't have valid continuation, the pipeline is currently
      // full because the consumer is being too slow. Try again at the next
      // frame interval.
      RequestFrame();
      return;
    }
  }

  // We have acquired a valid continuation from the pipeline and are ready
  // to service potential frame.
  FML_DCHECK(producer_continuation_);

  last_begin_frame_time_ = frame_start_time;
  dart_frame_deadline_ = FxlToDartOrEarlier(frame_target_time);
  {
    TRACE_EVENT2("flutter", "Framework Workload", "mode", "basic", "frame",
                 FrameParity());
    delegate_.OnAnimatorBeginFrame(last_begin_frame_time_);
  }

  if (!frame_scheduled_) {
    // Under certain workloads (such as our parent view resizing us, which is
    // communicated to us by repeat viewport metrics events), we won't
    // actually have a frame scheduled yet, despite the fact that we *will* be
    // producing a frame next vsync (it will be scheduled once we receive the
    // viewport event).  Because of this, we hold off on calling
    // |OnAnimatorNotifyIdle| for a little bit, as that could cause garbage
    // collection to trigger at a highly undesirable time.
    task_runners_.GetUITaskRunner()->PostDelayedTask(
        [self = weak_factory_.GetWeakPtr(),
         notify_idle_task_id = notify_idle_task_id_]() {
          if (!self.get()) {
            return;
          }
          // If our (this task's) task id is the same as the current one
          // (meaning there were no follow up frames to the |BeginFrame| call
          // that posted this task) and no frame is currently scheduled, then
          // assume that we are idle, and notify the engine of this.
          if (notify_idle_task_id == self->notify_idle_task_id_ &&
              !self->frame_scheduled_) {
            TRACE_EVENT0("flutter", "BeginFrame idle callback");
            self->delegate_.OnAnimatorNotifyIdle(Dart_TimelineGetMicros() +
                                                 100000);
          }
        },
        kNotifyIdleTaskWaitTime);
  }
}

void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
  if (dimension_change_pending_ &&
      layer_tree->frame_size() != last_layer_tree_size_) {
    dimension_change_pending_ = false;
  }
  last_layer_tree_size_ = layer_tree->frame_size();

  if (layer_tree) {
    // Note the frame time for instrumentation.
    layer_tree->set_construction_time(fml::TimePoint::Now() -
                                      last_begin_frame_time_);
  }

  // Commit the pending continuation.
  producer_continuation_.Complete(std::move(layer_tree));

  delegate_.OnAnimatorDraw(layer_tree_pipeline_);
}

bool Animator::CanReuseLastLayerTree() {
  return !regenerate_layer_tree_;
}

void Animator::DrawLastLayerTree() {
  pending_frame_semaphore_.Signal();
  delegate_.OnAnimatorDrawLastLayerTree();
}

void Animator::RequestFrame(bool regenerate_layer_tree) {
  if (regenerate_layer_tree) {
    regenerate_layer_tree_ = true;
  }
  if (paused_ && !dimension_change_pending_) {
    return;
  }

  if (!pending_frame_semaphore_.TryWait()) {
    // Multiple calls to Animator::RequestFrame will still result in a
    // single request to the VsyncWaiter.
    return;
  }

  // The AwaitVSync is going to call us back at the next VSync. However, we want
  // to be reasonably certain that the UI thread is not in the middle of a
  // particularly expensive callout. We post the AwaitVSync to run right after
  // an idle. This does NOT provide a guarantee that the UI thread has not
  // started an expensive operation right after posting this message however.
  // To support that, we need edge triggered wakes on VSync.

  task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
                                             frame_number = frame_number_]() {
    if (!self.get()) {
      return;
    }
    TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
    self->AwaitVSync();
  });
  frame_scheduled_ = true;
}

void Animator::AwaitVSync() {
  waiter_->AsyncWaitForVsync(
      [self = weak_factory_.GetWeakPtr()](fml::TimePoint frame_start_time,
                                          fml::TimePoint frame_target_time) {
        if (self) {
          if (self->CanReuseLastLayerTree()) {
            self->DrawLastLayerTree();
          } else {
            self->BeginFrame(frame_start_time, frame_target_time);
          }
        }
      });

  delegate_.OnAnimatorNotifyIdle(dart_frame_deadline_);
}

}  // namespace flutter
<|endoftext|>


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

/*
 * $Id$
 */

#if !defined(XERCESC_INCLUDE_GUARD_SCHEMAINFO_HPP)
#define XERCESC_INCLUDE_GUARD_SCHEMAINFO_HPP

/** When in a <redefine>, type definitions being used (and indeed
  * refs to <group>'s and <attributeGroup>'s) may refer to info
  * items either in the schema being redefined, in the <redefine>,
  * or else in the schema doing the redefining.  Because of this
  * latter we have to be prepared sometimes to look for our type
  * definitions outside the schema stored in fSchemaRootElement.
  * This simple class does this; it's just a linked list that
  * lets us look at the <schema>'s on the queue; note also that this
  * should provide us with a mechanism to handle nested <redefine>'s.
  * It's also a handy way of saving schema info when importing/including.
  */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/dom/DOMElement.hpp>
#include <xercesc/util/RefVectorOf.hpp>
#include <xercesc/util/ValueVectorOf.hpp>
#include <xercesc/util/RefHashTableOf.hpp>

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  Forward Declarations
// ---------------------------------------------------------------------------
class ValidationContext;
class NamespaceScope;

class VALIDATORS_EXPORT SchemaInfo : public XMemory
{
public:

    enum ListType {
        // Redefine is treated as an include
        IMPORT = 1,
        INCLUDE = 2
    };

    enum {
        C_ComplexType,
        C_SimpleType,
        C_Group,
        C_Attribute,
        C_AttributeGroup,
        C_Element,
        C_Notation,

        C_Count
    };

    // -----------------------------------------------------------------------
    //  Constructor/Destructor
    // -----------------------------------------------------------------------
    SchemaInfo(const unsigned short fElemAttrDefaultQualified,
               const int blockDefault,
               const int finalDefault,
               const int targetNSURI,
               const int scopeCount,
               const NamespaceScope* const currNamespaceScope,
               const XMLCh* const schemaURL,
               const XMLCh* const targetNSURIString,
               const DOMElement* const root,
               MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager);
    ~SchemaInfo();

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    XMLCh*                            getCurrentSchemaURL() const;
    const XMLCh*                      getTargetNSURIString() const;
    const DOMElement*                 getRoot() const;
    bool                              getProcessed() const;
    int                               getBlockDefault() const;
    int                               getFinalDefault() const;
    int                               getTargetNSURI() const;
    int                               getScopeCount() const;
    NamespaceScope*                   getNamespaceScope() const;
    unsigned short                    getElemAttrDefaultQualified() const;
    BaseRefVectorEnumerator<SchemaInfo>   getImportingListEnumerator() const;
    ValueVectorOf<const DOMElement*>* getRecursingAnonTypes() const;
    ValueVectorOf<const XMLCh*>*      getRecursingTypeNames() const;
    ValueVectorOf<DOMNode*>*          getNonXSAttList() const;
    ValidationContext*                getValidationContext() const;

    // -----------------------------------------------------------------------
    //  Setter methods
    // -----------------------------------------------------------------------
    void setProcessed(const bool aValue = true);
    void setScopeCount(const int aValue);
    void setBlockDefault(const int aValue);
    void setFinalDefault(const int aValue);
    void setElemAttrDefaultQualified(const unsigned short aValue);

    // -----------------------------------------------------------------------
    //  Access methods
    // -----------------------------------------------------------------------
    void addSchemaInfo(SchemaInfo* const toAdd, const ListType aListType);
    bool containsInfo(const SchemaInfo* const toCheck, const ListType aListType) const;
    SchemaInfo* getImportInfo(const unsigned int namespaceURI) const;
    DOMElement* getTopLevelComponent(const unsigned short compCategory,
                                     const XMLCh* const compName,
                                     const XMLCh* const name);
    DOMElement* getTopLevelComponent(const unsigned short compCategory,
                                     const XMLCh* const compName,
                                     const XMLCh* const name,
                                     SchemaInfo** enclosingSchema);
    void updateImportingInfo(SchemaInfo* const importingInfo);
    bool circularImportExist(const unsigned int nameSpaceURI);
    bool isFailedRedefine(const DOMElement* const anElem);
    void addFailedRedefine(const DOMElement* const anElem);
    bool isImportingNS(const int namespaceURI);
    void addImportedNS(const int namespaceURI);
    void addRecursingType(const DOMElement* const elem, const XMLCh* const name);

private:
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
    SchemaInfo(const SchemaInfo&);
    SchemaInfo& operator=(const SchemaInfo&);

    // -----------------------------------------------------------------------
    //  Private helper methods
    // -----------------------------------------------------------------------
    void clearTopLevelComponents();

    // -----------------------------------------------------------------------
    //  Private data members
    // -----------------------------------------------------------------------
    bool                              fAdoptInclude;
    bool                              fProcessed;
    unsigned short                    fElemAttrDefaultQualified;
    int                               fBlockDefault;
    int                               fFinalDefault;
    int                               fTargetNSURI;
    int                               fScopeCount;
    NamespaceScope*                   fNamespaceScope;
    XMLCh*                            fCurrentSchemaURL;
    XMLCh*                            fTargetNSURIString;
    const DOMElement*                 fSchemaRootElement;
    RefVectorOf<SchemaInfo>*          fIncludeInfoList;
    RefVectorOf<SchemaInfo>*          fImportedInfoList;
    RefVectorOf<SchemaInfo>*          fImportingInfoList;
    ValueVectorOf<const DOMElement*>* fFailedRedefineList;
    ValueVectorOf<int>*               fImportedNSList;
    ValueVectorOf<const DOMElement*>* fRecursingAnonTypes;
    ValueVectorOf<const XMLCh*>*      fRecursingTypeNames;
    RefHashTableOf<DOMElement>*       fTopLevelComponents[C_Count];
    DOMElement*                       fLastTopLevelComponent[C_Count];
    ValueVectorOf<DOMNode*>*          fNonXSAttList;
    ValidationContext*                fValidationContext;
    MemoryManager*                    fMemoryManager;
};

// ---------------------------------------------------------------------------
//  SchemaInfo: Getter methods
// ---------------------------------------------------------------------------
inline unsigned short SchemaInfo::getElemAttrDefaultQualified() const {

    return fElemAttrDefaultQualified;
}

inline bool SchemaInfo::getProcessed() const {

    return fProcessed;
}

inline int SchemaInfo::getBlockDefault() const {

    return fBlockDefault;
}

inline int SchemaInfo::getFinalDefault() const {

    return fFinalDefault;
}

inline NamespaceScope* SchemaInfo::getNamespaceScope() const {
    return fNamespaceScope;
}

inline XMLCh* SchemaInfo::getCurrentSchemaURL() const {

    return fCurrentSchemaURL;
}

inline const XMLCh* SchemaInfo::getTargetNSURIString() const {

    return fTargetNSURIString;
}

inline const DOMElement* SchemaInfo::getRoot() const {

    return fSchemaRootElement;
}

inline int SchemaInfo::getTargetNSURI() const {

    return fTargetNSURI;
}

inline int SchemaInfo::getScopeCount() const {

    return fScopeCount;
}

inline BaseRefVectorEnumerator<SchemaInfo>
SchemaInfo::getImportingListEnumerator() const {

    return BaseRefVectorEnumerator<SchemaInfo>(fImportingInfoList);
}

inline ValueVectorOf<const DOMElement*>*
SchemaInfo::getRecursingAnonTypes() const {

    return fRecursingAnonTypes;
}

inline ValueVectorOf<const XMLCh*>*
SchemaInfo::getRecursingTypeNames() const {

    return fRecursingTypeNames;
}

inline ValueVectorOf<DOMNode*>* SchemaInfo::getNonXSAttList() const
{
    return fNonXSAttList;
}

// ---------------------------------------------------------------------------
//  Setter methods
// ---------------------------------------------------------------------------
inline void SchemaInfo::setScopeCount(const int aValue) {

    fScopeCount = aValue;
}

inline void SchemaInfo::setBlockDefault(const int aValue) {

    fBlockDefault = aValue;
}

inline void SchemaInfo::setFinalDefault(const int aValue) {

    fFinalDefault = aValue;
}

inline void SchemaInfo::setElemAttrDefaultQualified(const unsigned short aValue) {

    fElemAttrDefaultQualified = aValue;
}

inline void SchemaInfo::setProcessed(const bool aValue) {

    fProcessed = aValue;

/*    if (fProcessed && fIncludeInfoList) {

        unsigned int includeListLen = fIncludeInfoList->size());
        for (unsigned int i = 0; i < includeListLen; i++) {
            fIncludeInfoList->elementAt(i)->clearTopLevelComponents();
        }
    }*/
}

// ---------------------------------------------------------------------------
//  SchemaInfo: Access methods
// ---------------------------------------------------------------------------
inline void SchemaInfo::addImportedNS(const int namespaceURI) {

    if (!fImportedNSList) {
        fImportedNSList = new (fMemoryManager) ValueVectorOf<int>(4, fMemoryManager);
    }

    if (!fImportedNSList->containsElement(namespaceURI))
        fImportedNSList->addElement(namespaceURI);
}

inline void SchemaInfo::addSchemaInfo(SchemaInfo* const toAdd,
                                      const ListType aListType) {

    if (aListType == IMPORT) {

        if (!fImportedInfoList)
            fImportedInfoList = new (fMemoryManager) RefVectorOf<SchemaInfo>(4, false, fMemoryManager);

        if (!fImportedInfoList->containsElement(toAdd)) {

            fImportedInfoList->addElement(toAdd);
            addImportedNS(toAdd->getTargetNSURI());
            toAdd->updateImportingInfo(this);
        }
    }
    else {

        if (!fIncludeInfoList) {

            fIncludeInfoList = new (fMemoryManager) RefVectorOf<SchemaInfo>(8, false, fMemoryManager);
            fAdoptInclude = true;
        }

        if (!fIncludeInfoList->containsElement(toAdd)) {

		    fIncludeInfoList->addElement(toAdd);
            //code was originally:
            //toAdd->fIncludeInfoList = fIncludeInfoList;
            //however for handling multiple imports this was causing
            //to schemaInfo's to have the same fIncludeInfoList which they
            //both owned so when it was deleted it crashed.
			if (toAdd->fIncludeInfoList) {
			   if (toAdd->fIncludeInfoList != fIncludeInfoList) {
                   XMLSize_t size = toAdd->fIncludeInfoList->size();
                   for (XMLSize_t i=0; i<size; i++) {
                       if (!fIncludeInfoList->containsElement(toAdd->fIncludeInfoList->elementAt(i))) {
                            fIncludeInfoList->addElement(toAdd->fIncludeInfoList->elementAt(i));
                       }
                   }
                   size = fIncludeInfoList->size();
                   for (XMLSize_t j=0; j<size; j++) {
                       if (!toAdd->fIncludeInfoList->containsElement(fIncludeInfoList->elementAt(j))) {
                            toAdd->fIncludeInfoList->addElement(fIncludeInfoList->elementAt(j));
                       }
                   }
			   }
			}
			else {
				toAdd->fIncludeInfoList = fIncludeInfoList;
			}
        }
    }
}

inline SchemaInfo* SchemaInfo::getImportInfo(const unsigned int namespaceURI) const {

    XMLSize_t importSize = (fImportedInfoList) ? fImportedInfoList->size() : 0;
    SchemaInfo* currInfo = 0;

    for (XMLSize_t i=0; i < importSize; i++) {

        currInfo = fImportedInfoList->elementAt(i);

        if (currInfo->getTargetNSURI() == (int) namespaceURI)
            break;
    }

    return currInfo;
}

inline ValidationContext* SchemaInfo::getValidationContext() const {

    return fValidationContext;
}

inline bool SchemaInfo::containsInfo(const SchemaInfo* const toCheck,
                                     const ListType aListType) const {

    if ((aListType == INCLUDE) && fIncludeInfoList) {
        return fIncludeInfoList->containsElement(toCheck);
    }
    else if ((aListType == IMPORT) && fImportedInfoList) {
        return fImportedInfoList->containsElement(toCheck);
    }

    return false;
}

inline bool SchemaInfo::circularImportExist(const unsigned int namespaceURI) {

    XMLSize_t importSize = fImportingInfoList->size();

    for (XMLSize_t i=0; i < importSize; i++) {
        if (fImportingInfoList->elementAt(i)->getTargetNSURI() == (int) namespaceURI) {
            return true;
        }
    }

    return false;
}

inline bool SchemaInfo::isFailedRedefine(const DOMElement* const anElem) {

    if (fFailedRedefineList)
        return (fFailedRedefineList->containsElement(anElem));

    return false;
}

inline void SchemaInfo::addFailedRedefine(const DOMElement* const anElem) {

    if (!fFailedRedefineList) {
        fFailedRedefineList = new (fMemoryManager) ValueVectorOf<const DOMElement*>(4, fMemoryManager);
    }

    fFailedRedefineList->addElement(anElem);
}

inline bool SchemaInfo::isImportingNS(const int namespaceURI) {

    if (!fImportedNSList)
        return false;

    return (fImportedNSList->containsElement(namespaceURI));
}

inline void SchemaInfo::addRecursingType(const DOMElement* const elem,
                                         const XMLCh* const name) {

    if (!fRecursingAnonTypes) {
        fRecursingAnonTypes = new (fMemoryManager) ValueVectorOf<const DOMElement*>(8, fMemoryManager);
        fRecursingTypeNames = new (fMemoryManager) ValueVectorOf<const XMLCh*>(8, fMemoryManager);
    }

    fRecursingAnonTypes->addElement(elem);
    fRecursingTypeNames->addElement(name);
}

inline void SchemaInfo::clearTopLevelComponents() {

    for (unsigned int i = 0; i < C_Count; i++) {

        delete fTopLevelComponents[i];
        fTopLevelComponents[i] = 0;
        fLastTopLevelComponent[i] = 0;
    }
}

XERCES_CPP_NAMESPACE_END

#endif

/**
  * End of file SchemaInfo.hpp
  */

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

/*
 * $Id$
 */

#if !defined(XERCESC_INCLUDE_GUARD_SCHEMAINFO_HPP)
#define XERCESC_INCLUDE_GUARD_SCHEMAINFO_HPP

/** When in a <redefine>, type definitions being used (and indeed
  * refs to <group>'s and <attributeGroup>'s) may refer to info
  * items either in the schema being redefined, in the <redefine>,
  * or else in the schema doing the redefining.  Because of this
  * latter we have to be prepared sometimes to look for our type
  * definitions outside the schema stored in fSchemaRootElement.
  * This simple class does this; it's just a linked list that
  * lets us look at the <schema>'s on the queue; note also that this
  * should provide us with a mechanism to handle nested <redefine>'s.
  * It's also a handy way of saving schema info when importing/including.
  */

// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <xercesc/dom/DOMElement.hpp>
#include <xercesc/util/RefVectorOf.hpp>
#include <xercesc/util/ValueVectorOf.hpp>
#include <xercesc/util/RefHashTableOf.hpp>

XERCES_CPP_NAMESPACE_BEGIN

// ---------------------------------------------------------------------------
//  Forward Declarations
// ---------------------------------------------------------------------------
class ValidationContext;
class NamespaceScope;

class VALIDATORS_EXPORT SchemaInfo : public XMemory
{
public:

    enum ListType {
        // Redefine is treated as an include
        IMPORT = 1,
        INCLUDE = 2
    };

    enum {
        C_ComplexType,
        C_SimpleType,
        C_Group,
        C_Attribute,
        C_AttributeGroup,
        C_Element,
        C_Notation,

        C_Count
    };

    // -----------------------------------------------------------------------
    //  Constructor/Destructor
    // -----------------------------------------------------------------------
    SchemaInfo(const unsigned short fElemAttrDefaultQualified,
               const int blockDefault,
               const int finalDefault,
               const int targetNSURI,
               const int scopeCount,
               const NamespaceScope* const currNamespaceScope,
               const XMLCh* const schemaURL,
               const XMLCh* const targetNSURIString,
               const DOMElement* const root,
               MemoryManager* const manager = XMLPlatformUtils::fgMemoryManager);
    ~SchemaInfo();

    // -----------------------------------------------------------------------
    //  Getter methods
    // -----------------------------------------------------------------------
    XMLCh*                            getCurrentSchemaURL() const;
    const XMLCh*                      getTargetNSURIString() const;
    const DOMElement*                 getRoot() const;
    bool                              getProcessed() const;
    int                               getBlockDefault() const;
    int                               getFinalDefault() const;
    int                               getTargetNSURI() const;
    int                               getScopeCount() const;
    NamespaceScope*                   getNamespaceScope() const;
    unsigned short                    getElemAttrDefaultQualified() const;
    BaseRefVectorEnumerator<SchemaInfo>   getImportingListEnumerator() const;
    ValueVectorOf<const DOMElement*>* getRecursingAnonTypes() const;
    ValueVectorOf<const XMLCh*>*      getRecursingTypeNames() const;
    ValueVectorOf<DOMNode*>*          getNonXSAttList() const;
    ValidationContext*                getValidationContext() const;

    // -----------------------------------------------------------------------
    //  Setter methods
    // -----------------------------------------------------------------------
    void setProcessed(const bool aValue = true);
    void setScopeCount(const int aValue);
    void setBlockDefault(const int aValue);
    void setFinalDefault(const int aValue);
    void setElemAttrDefaultQualified(const unsigned short aValue);

    // -----------------------------------------------------------------------
    //  Access methods
    // -----------------------------------------------------------------------
    void addSchemaInfo(SchemaInfo* const toAdd, const ListType aListType);
    bool containsInfo(const SchemaInfo* const toCheck, const ListType aListType) const;
    SchemaInfo* getImportInfo(const unsigned int namespaceURI) const;
    DOMElement* getTopLevelComponent(const unsigned short compCategory,
                                     const XMLCh* const compName,
                                     const XMLCh* const name);
    DOMElement* getTopLevelComponent(const unsigned short compCategory,
                                     const XMLCh* const compName,
                                     const XMLCh* const name,
                                     SchemaInfo** enclosingSchema);
    void updateImportingInfo(SchemaInfo* const importingInfo);
    bool circularImportExist(const unsigned int nameSpaceURI);
    bool isFailedRedefine(const DOMElement* const anElem);
    void addFailedRedefine(const DOMElement* const anElem);
    bool isImportingNS(const int namespaceURI);
    void addImportedNS(const int namespaceURI);
    void addRecursingType(const DOMElement* const elem, const XMLCh* const name);

private:
    // -----------------------------------------------------------------------
    //  Unimplemented constructors and operators
    // -----------------------------------------------------------------------
    SchemaInfo(const SchemaInfo&);
    SchemaInfo& operator=(const SchemaInfo&);

    // -----------------------------------------------------------------------
    //  Private helper methods
    // -----------------------------------------------------------------------
    void clearTopLevelComponents();

    // -----------------------------------------------------------------------
    //  Private data members
    // -----------------------------------------------------------------------
    bool                              fAdoptInclude;
    bool                              fProcessed;
    unsigned short                    fElemAttrDefaultQualified;
    int                               fBlockDefault;
    int                               fFinalDefault;
    int                               fTargetNSURI;
    int                               fScopeCount;
    NamespaceScope*                   fNamespaceScope;
    XMLCh*                            fCurrentSchemaURL;
    XMLCh*                            fTargetNSURIString;
    const DOMElement*                 fSchemaRootElement;
    RefVectorOf<SchemaInfo>*          fIncludeInfoList;
    RefVectorOf<SchemaInfo>*          fImportedInfoList;
    RefVectorOf<SchemaInfo>*          fImportingInfoList;
    ValueVectorOf<const DOMElement*>* fFailedRedefineList;
    ValueVectorOf<int>*               fImportedNSList;
    ValueVectorOf<const DOMElement*>* fRecursingAnonTypes;
    ValueVectorOf<const XMLCh*>*      fRecursingTypeNames;
    RefHashTableOf<DOMElement>*       fTopLevelComponents[C_Count];
    DOMElement*                       fLastTopLevelComponent[C_Count];
    ValueVectorOf<DOMNode*>*          fNonXSAttList;
    ValidationContext*                fValidationContext;
    MemoryManager*                    fMemoryManager;
};

// ---------------------------------------------------------------------------
//  SchemaInfo: Getter methods
// ---------------------------------------------------------------------------
inline unsigned short SchemaInfo::getElemAttrDefaultQualified() const {

    return fElemAttrDefaultQualified;
}

inline bool SchemaInfo::getProcessed() const {

    return fProcessed;
}

inline int SchemaInfo::getBlockDefault() const {

    return fBlockDefault;
}

inline int SchemaInfo::getFinalDefault() const {

    return fFinalDefault;
}

inline NamespaceScope* SchemaInfo::getNamespaceScope() const {
    return fNamespaceScope;
}

inline XMLCh* SchemaInfo::getCurrentSchemaURL() const {

    return fCurrentSchemaURL;
}

inline const XMLCh* SchemaInfo::getTargetNSURIString() const {

    return fTargetNSURIString;
}

inline const DOMElement* SchemaInfo::getRoot() const {

    return fSchemaRootElement;
}

inline int SchemaInfo::getTargetNSURI() const {

    return fTargetNSURI;
}

inline int SchemaInfo::getScopeCount() const {

    return fScopeCount;
}

inline BaseRefVectorEnumerator<SchemaInfo>
SchemaInfo::getImportingListEnumerator() const {

    return BaseRefVectorEnumerator<SchemaInfo>(fImportingInfoList);
}

inline ValueVectorOf<const DOMElement*>*
SchemaInfo::getRecursingAnonTypes() const {

    return fRecursingAnonTypes;
}

inline ValueVectorOf<const XMLCh*>*
SchemaInfo::getRecursingTypeNames() const {

    return fRecursingTypeNames;
}

inline ValueVectorOf<DOMNode*>* SchemaInfo::getNonXSAttList() const
{
    return fNonXSAttList;
}

// ---------------------------------------------------------------------------
//  Setter methods
// ---------------------------------------------------------------------------
inline void SchemaInfo::setScopeCount(const int aValue) {

    fScopeCount = aValue;
}

inline void SchemaInfo::setBlockDefault(const int aValue) {

    fBlockDefault = aValue;
}

inline void SchemaInfo::setFinalDefault(const int aValue) {

    fFinalDefault = aValue;
}

inline void SchemaInfo::setElemAttrDefaultQualified(const unsigned short aValue) {

    fElemAttrDefaultQualified = aValue;
}

inline void SchemaInfo::setProcessed(const bool aValue) {

    fProcessed = aValue;

/*    if (fProcessed && fIncludeInfoList) {

        unsigned int includeListLen = fIncludeInfoList->size());
        for (unsigned int i = 0; i < includeListLen; i++) {
            fIncludeInfoList->elementAt(i)->clearTopLevelComponents();
        }
    }*/
}

// ---------------------------------------------------------------------------
//  SchemaInfo: Access methods
// ---------------------------------------------------------------------------
inline void SchemaInfo::addImportedNS(const int namespaceURI) {

    if (!fImportedNSList) {
        fImportedNSList = new (fMemoryManager) ValueVectorOf<int>(4, fMemoryManager);
    }

    if (!fImportedNSList->containsElement(namespaceURI))
        fImportedNSList->addElement(namespaceURI);
}

inline void SchemaInfo::addSchemaInfo(SchemaInfo* const toAdd,
                                      const ListType aListType) {

    if (aListType == IMPORT) {

        if (!fImportedInfoList)
            fImportedInfoList = new (fMemoryManager) RefVectorOf<SchemaInfo>(4, false, fMemoryManager);

        if (!fImportedInfoList->containsElement(toAdd)) {

            fImportedInfoList->addElement(toAdd);
            addImportedNS(toAdd->getTargetNSURI());
            toAdd->updateImportingInfo(this);
        }
    }
    else {

        if (!fIncludeInfoList) {

            fIncludeInfoList = new (fMemoryManager) RefVectorOf<SchemaInfo>(8, false, fMemoryManager);
            fAdoptInclude = true;
        }

        if (!fIncludeInfoList->containsElement(toAdd)) {

		    fIncludeInfoList->addElement(toAdd);
            //code was originally:
            //toAdd->fIncludeInfoList = fIncludeInfoList;
            //however for handling multiple imports this was causing
            //to schemaInfo's to have the same fIncludeInfoList which they
            //both owned so when it was deleted it crashed.
			if (toAdd->fIncludeInfoList) {
			   if (toAdd->fIncludeInfoList != fIncludeInfoList) {
                   XMLSize_t size = toAdd->fIncludeInfoList->size();
                   for (XMLSize_t i=0; i<size; i++) {
                       if (!fIncludeInfoList->containsElement(toAdd->fIncludeInfoList->elementAt(i))) {
                            fIncludeInfoList->addElement(toAdd->fIncludeInfoList->elementAt(i));
                       }
                   }
                   size = fIncludeInfoList->size();
                   for (XMLSize_t j=0; j<size; j++) {
                       if (!toAdd->fIncludeInfoList->containsElement(fIncludeInfoList->elementAt(j))) {
                            toAdd->fIncludeInfoList->addElement(fIncludeInfoList->elementAt(j));
                       }
                   }
			   }
			}
			else {
				toAdd->fIncludeInfoList = fIncludeInfoList;
			}
        }
    }
}

inline SchemaInfo* SchemaInfo::getImportInfo(const unsigned int namespaceURI) const {

    XMLSize_t importSize = (fImportedInfoList) ? fImportedInfoList->size() : 0;
    SchemaInfo* currInfo = 0;

    for (XMLSize_t i=0; i < importSize; i++) {

        currInfo = fImportedInfoList->elementAt(i);

        if (currInfo->getTargetNSURI() == (int) namespaceURI)
            return currInfo;
    }

    return 0;
}

inline ValidationContext* SchemaInfo::getValidationContext() const {

    return fValidationContext;
}

inline bool SchemaInfo::containsInfo(const SchemaInfo* const toCheck,
                                     const ListType aListType) const {

    if ((aListType == INCLUDE) && fIncludeInfoList) {
        return fIncludeInfoList->containsElement(toCheck);
    }
    else if ((aListType == IMPORT) && fImportedInfoList) {
        return fImportedInfoList->containsElement(toCheck);
    }

    return false;
}

inline bool SchemaInfo::circularImportExist(const unsigned int namespaceURI) {

    XMLSize_t importSize = fImportingInfoList->size();

    for (XMLSize_t i=0; i < importSize; i++) {
        if (fImportingInfoList->elementAt(i)->getTargetNSURI() == (int) namespaceURI) {
            return true;
        }
    }

    return false;
}

inline bool SchemaInfo::isFailedRedefine(const DOMElement* const anElem) {

    if (fFailedRedefineList)
        return (fFailedRedefineList->containsElement(anElem));

    return false;
}

inline void SchemaInfo::addFailedRedefine(const DOMElement* const anElem) {

    if (!fFailedRedefineList) {
        fFailedRedefineList = new (fMemoryManager) ValueVectorOf<const DOMElement*>(4, fMemoryManager);
    }

    fFailedRedefineList->addElement(anElem);
}

inline bool SchemaInfo::isImportingNS(const int namespaceURI) {

    if (!fImportedNSList)
        return false;

    return (fImportedNSList->containsElement(namespaceURI));
}

inline void SchemaInfo::addRecursingType(const DOMElement* const elem,
                                         const XMLCh* const name) {

    if (!fRecursingAnonTypes) {
        fRecursingAnonTypes = new (fMemoryManager) ValueVectorOf<const DOMElement*>(8, fMemoryManager);
        fRecursingTypeNames = new (fMemoryManager) ValueVectorOf<const XMLCh*>(8, fMemoryManager);
    }

    fRecursingAnonTypes->addElement(elem);
    fRecursingTypeNames->addElement(name);
}

inline void SchemaInfo::clearTopLevelComponents() {

    for (unsigned int i = 0; i < C_Count; i++) {

        delete fTopLevelComponents[i];
        fTopLevelComponents[i] = 0;
        fLastTopLevelComponent[i] = 0;
    }
}

XERCES_CPP_NAMESPACE_END

#endif

/**
  * End of file SchemaInfo.hpp
  */

<|endoftext|>


<commit_before>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/util/reusable_set_pool.h>
#include <vespa/vespalib/gtest/gtest.h>

using namespace vespalib;

using Mark = ReusableSet::Mark;

void verify_set(const ReusableSet &set, size_t sz, Mark val, size_t marked) {
    EXPECT_EQ(sz, set.capacity());
    EXPECT_EQ(val, set.generation());
    size_t count = 0;
    for (size_t i = 0; i < set.capacity(); ++i) {
        if (set.is_marked(i)) ++count;
    }
    EXPECT_EQ(marked, count);
}

void verify_handle(const ReusableSetHandle &set, size_t sz, Mark val, size_t marked) {
    EXPECT_EQ(sz, set.capacity());
    EXPECT_EQ(val, set.generation());
    size_t count = 0;
    for (size_t i = 0; i < set.capacity(); ++i) {
        if (set.is_marked(i)) ++count;
    }
    EXPECT_EQ(marked, count);
}

class Pool : public ::testing::Test {
public:
    ReusableSetPool pool;
    Pool() : pool() {}
    ~Pool() {}
    void exercise(ReusableSetHandle &set) {
        size_t sz = set.capacity();
        size_t count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(0, count);
        for (int i = 0; i < 17; ++i) {
            set.mark((i * 711) % sz);
        }
        count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(17, count);
        for (int i = 0; i < 17; ++i) {
            set.mark((i * 711) % sz);
        }
        count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(17, count);
   }
};

TEST(ReusableSetTest, simple_usage)
{
    ReusableSet visited(7);
    verify_set(visited, 7, 1, 0);
    visited.mark(1);
    visited.mark(2);
    visited.mark(4);
    EXPECT_EQ(false, visited.is_marked(0));
    EXPECT_EQ(true, visited.is_marked(1));
    EXPECT_EQ(true, visited.is_marked(2));
    EXPECT_EQ(false, visited.is_marked(3));
    verify_set(visited, 7, 1, 3);
    visited.mark(4);
    visited.mark(1);
    visited.mark(2);
    verify_set(visited, 7, 1, 3);
    EXPECT_EQ(false, visited.is_marked(0));
    EXPECT_EQ(true, visited.is_marked(1));
    EXPECT_EQ(true, visited.is_marked(2));
    EXPECT_EQ(false, visited.is_marked(3));
    visited.clear();
    verify_set(visited, 7, 2, 0);
    visited.clear();
    verify_set(visited, 7, 3, 0);
}

TEST_F(Pool, reuse_works)
{
    for (int i = 0; i < 65535; ++i) {
        auto handle = pool.get(7);
        EXPECT_EQ(i, pool.reuse_count());
        EXPECT_EQ(1, pool.create_count());
        verify_handle(handle, 248, i+1, 0);
        exercise(handle);
    }
    EXPECT_TRUE(500 < pool.memory_usage().allocatedBytes());
    EXPECT_TRUE(1000 > pool.memory_usage().allocatedBytes());
    for (int i = 0; i < 5; ++i) {
        auto handle = pool.get(7);
        EXPECT_EQ(65535+i, pool.reuse_count());
        EXPECT_EQ(1, pool.create_count());
        verify_handle(handle, 248, i+1, 0);
        exercise(handle);
    }
    auto handle3 = pool.get(260);
    EXPECT_EQ(2, pool.create_count());
    verify_handle(handle3, 297, 1, 0);
    exercise(handle3);
    {
        auto handle4 = pool.get(400);
        EXPECT_EQ(3, pool.create_count());
        verify_handle(handle4, 400, 1, 0);
        exercise(handle4);
        EXPECT_TRUE(1000 < pool.memory_usage().usedBytes());
        EXPECT_TRUE(2000 > pool.memory_usage().usedBytes());
    }
    EXPECT_TRUE(500 < pool.memory_usage().usedBytes());
    EXPECT_TRUE(1000 > pool.memory_usage().usedBytes());
    auto handle7 = pool.get(401);
    EXPECT_EQ(4, pool.create_count());
    verify_handle(handle7, 480, 1, 0);
    exercise(handle7);
    EXPECT_TRUE(1000 < pool.memory_usage().allocatedBytes());
    EXPECT_TRUE(3000 > pool.memory_usage().allocatedBytes());
    auto handle8 = pool.get(2500);
    auto handle9 = pool.get(2500);
    EXPECT_TRUE(11000 < pool.memory_usage().allocatedBytes());
    EXPECT_TRUE(13000 > pool.memory_usage().allocatedBytes());
}

GTEST_MAIN_RUN_ALL_TESTS()
<commit_msg>more testing for memory usage tracking<commit_after>// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/util/reusable_set_pool.h>
#include <vespa/vespalib/gtest/gtest.h>

using namespace vespalib;

using Mark = ReusableSet::Mark;

void verify_set(const ReusableSet &set, size_t sz, Mark val, size_t marked) {
    EXPECT_EQ(sz, set.capacity());
    EXPECT_EQ(val, set.generation());
    size_t count = 0;
    for (size_t i = 0; i < set.capacity(); ++i) {
        if (set.is_marked(i)) ++count;
    }
    EXPECT_EQ(marked, count);
}

void verify_handle(const ReusableSetHandle &set, size_t sz, Mark val, size_t marked) {
    EXPECT_EQ(sz, set.capacity());
    EXPECT_EQ(val, set.generation());
    size_t count = 0;
    for (size_t i = 0; i < set.capacity(); ++i) {
        if (set.is_marked(i)) ++count;
    }
    EXPECT_EQ(marked, count);
}

class Pool : public ::testing::Test {
public:
    ReusableSetPool pool;
    Pool() : pool() {}
    ~Pool() {}
    void exercise(ReusableSetHandle &set) {
        size_t sz = set.capacity();
        size_t count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(0, count);
        for (int i = 0; i < 17; ++i) {
            set.mark((i * 711) % sz);
        }
        count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(17, count);
        for (int i = 0; i < 17; ++i) {
            set.mark((i * 711) % sz);
        }
        count = 0;
        for (size_t i = 0; i < sz; ++i) {
            if (set.is_marked(i)) ++count;
        }
        EXPECT_EQ(17, count);
   }
};

TEST(ReusableSetTest, simple_usage)
{
    ReusableSet visited(7);
    verify_set(visited, 7, 1, 0);
    visited.mark(1);
    visited.mark(2);
    visited.mark(4);
    EXPECT_EQ(false, visited.is_marked(0));
    EXPECT_EQ(true, visited.is_marked(1));
    EXPECT_EQ(true, visited.is_marked(2));
    EXPECT_EQ(false, visited.is_marked(3));
    verify_set(visited, 7, 1, 3);
    visited.mark(4);
    visited.mark(1);
    visited.mark(2);
    verify_set(visited, 7, 1, 3);
    EXPECT_EQ(false, visited.is_marked(0));
    EXPECT_EQ(true, visited.is_marked(1));
    EXPECT_EQ(true, visited.is_marked(2));
    EXPECT_EQ(false, visited.is_marked(3));
    visited.clear();
    verify_set(visited, 7, 2, 0);
    visited.clear();
    verify_set(visited, 7, 3, 0);
}

TEST_F(Pool, reuse_works)
{
    for (int i = 0; i < 65535; ++i) {
        auto handle = pool.get(7);
        EXPECT_EQ(i, pool.reuse_count());
        EXPECT_EQ(1, pool.create_count());
        verify_handle(handle, 248, i+1, 0);
        exercise(handle);
    }
    EXPECT_TRUE(500 < pool.memory_usage().allocatedBytes());
    EXPECT_TRUE(1000 > pool.memory_usage().allocatedBytes());
    for (int i = 0; i < 5; ++i) {
        auto handle = pool.get(7);
        EXPECT_EQ(65535+i, pool.reuse_count());
        EXPECT_EQ(1, pool.create_count());
        verify_handle(handle, 248, i+1, 0);
        exercise(handle);
    }
    auto handle3 = pool.get(260);
    EXPECT_EQ(2, pool.create_count());
    verify_handle(handle3, 297, 1, 0);
    exercise(handle3);
    {
        auto handle4 = pool.get(400);
        EXPECT_EQ(3, pool.create_count());
        verify_handle(handle4, 400, 1, 0);
        exercise(handle4);
        EXPECT_TRUE(1000 < pool.memory_usage().usedBytes());
        EXPECT_TRUE(2000 > pool.memory_usage().usedBytes());
    }
    EXPECT_TRUE(500 < pool.memory_usage().usedBytes());
    EXPECT_TRUE(1000 > pool.memory_usage().usedBytes());
    auto handle7 = pool.get(401);
    EXPECT_EQ(4, pool.create_count());
    verify_handle(handle7, 480, 1, 0);
    exercise(handle7);
    EXPECT_TRUE(1000 < pool.memory_usage().allocatedBytes());
    EXPECT_TRUE(3000 > pool.memory_usage().allocatedBytes());
    {
        auto handle8 = pool.get(2500);
        auto handle9 = pool.get(2500);
        EXPECT_TRUE(11000 < pool.memory_usage().allocatedBytes());
        EXPECT_TRUE(13000 > pool.memory_usage().allocatedBytes());
        auto handleA = pool.get(25000);
        auto handleB = pool.get(25000);
        EXPECT_TRUE(111000 < pool.memory_usage().usedBytes());
        EXPECT_TRUE(113000 > pool.memory_usage().usedBytes());
    }
    EXPECT_TRUE(3000 > pool.memory_usage().usedBytes());
}

GTEST_MAIN_RUN_ALL_TESTS()
<|endoftext|>