<commit_before>// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// 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
//
// 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.

/**
 * @file StatisticsBase.hpp
 */

#ifndef _STATISTICS_RTPS_STATISTICSBASE_HPP_
#define _STATISTICS_RTPS_STATISTICSBASE_HPP_

#include <mutex>
#include <set>

#include <fastrtps/config.h>

#include <fastdds/rtps/common/Guid.h>
#include <fastdds/rtps/common/Locator.h>

#include <statistics/types/types.h>
#include <statistics/rtps/GuidUtils.hpp>

#include <fastdds/statistics/rtps/StatisticsCommon.hpp>

namespace eprosima {
namespace fastrtps {
namespace rtps {

class PDP;

} // rtps
} // fastrtps

namespace fastdds {
namespace statistics {

#ifdef FASTDDS_STATISTICS

// RTPSWriter and RTPSReader statistics members
struct StatisticsAncillary
{
    std::set<std::shared_ptr<IListener>> listeners;
};

struct StatisticsWriterAncillary
    : public StatisticsAncillary
{
    unsigned long long data_counter = {};
    unsigned long long gap_counter = {};
    unsigned long long resent_counter = {};
};

struct StatisticsReaderAncillary
    : public StatisticsAncillary
{
};

// lambda function to traverse the listener collection
template<class Function>
Function StatisticsListenersImpl::for_each_listener(
        Function f)
{
    // Use a collection copy to prevent locking on traversal
    std::unique_lock<fastrtps::RecursiveTimedMutex> lock(get_statistics_mutex());
    auto listeners = members_->listeners;
    lock.unlock();

    if (members_)
    {
        for (auto& listener : listeners)
        {
            f(listener);
        }
    }

    return f;
}

class StatisticsParticipantImpl
{
    friend class fastrtps::rtps::PDP;

    // statistics members protection only
    std::recursive_mutex statistics_mutex_;

public:

    using GUID_t = fastrtps::rtps::GUID_t;

private:

    // RTPS_SENT ancillary
    struct rtps_sent_data
    {
        unsigned long long packet_count = {};
        unsigned long long byte_count = {};
    };

    std::map<fastrtps::rtps::Locator_t, rtps_sent_data> traffic;

    // PDP_PACKETS ancillary
    unsigned long long pdp_counter_ = {};

    /*
     * Retrieve the GUID_t from derived class
     * @return endpoint GUID_t
     */
    const GUID_t& get_guid() const;

protected:

    class ListenerProxy
        : public IListener
        , public std::enable_shared_from_this<ListenerProxy>
    {
        // the external_ listener is the actual key value
        mutable uint32_t mask_;
        std::shared_ptr<IListener> external_;

    public:

        ListenerProxy(
                std::shared_ptr<IListener> listener,
                uint32_t mask)
            : mask_(mask)
            , external_(listener)
        {
        }

        void on_statistics_data(
                const Data& data) override;

        uint32_t mask() const;
        void mask(
                uint32_t update) const;

        bool operator <(
                const ListenerProxy& right) const;

        std::shared_ptr<IListener> get_shared_ptr() const
        {
            return std::static_pointer_cast<IListener>(const_cast<ListenerProxy*>(this)->shared_from_this());
        }

    };

    using Key = std::shared_ptr<ListenerProxy>;

    // specialized comparison operator, the actual key is the external listener address
    struct CompareProxies
    {
        bool operator ()(
                const Key& left,
                const Key& right) const
        {
            return *left < *right;
        }

    };

    using ProxyCollection = std::set<Key, CompareProxies>;
    ProxyCollection listeners_;

    // retrieve the participant mutex
    std::recursive_mutex& get_statistics_mutex();

    /** Register a listener in participant RTPSWriter entities.
     * @param listener, smart pointer to the listener interface to register
     * @param guid, RTPSWriter identifier. If unknown the listener is registered in all enable ones
     * @return true on success
     */
    virtual bool register_in_writer(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            GUID_t guid = GUID_t::unknown()) = 0;

    /** Register a listener in participant RTPSReader entities.
     * @param listener, smart pointer to the listener interface to register
     * @param guid, RTPSReader identifier. If unknown the listener is registered in all enable ones
     * @return true on success
     */
    virtual bool register_in_reader(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            GUID_t guid = GUID_t::unknown()) = 0;

    /** Unregister a listener in participant RTPSWriter entities.
     * @param listener, smart pointer to the listener interface to unregister
     * @return true on success
     */
    virtual bool unregister_in_writer(
            std::shared_ptr<fastdds::statistics::IListener> listener) = 0;

    /** Unregister a listener in participant RTPSReader entities.
     * @param listener, smart pointer to the listener interface to unregister
     * @return true on success
     */
    virtual bool unregister_in_reader(
            std::shared_ptr<fastdds::statistics::IListener> listener) = 0;

    // lambda function to traverse the listener collection
    template<class Function>
    Function for_each_listener(
            Function f)
    {
        std::unique_lock<std::recursive_mutex> lock(get_statistics_mutex());
        auto temp_listeners = listeners_;
        lock.unlock();

        for (auto& listener : temp_listeners)
        {
            f(listener);
        }

        return f;
    }

    // returns if a mask statistics::EventKind may require participant writers update
    bool are_writers_involved(
            const uint32_t mask) const;

    // returns if a mask statistics::EventKind may require participant readers update
    bool are_readers_involved(
            const uint32_t mask) const;

    // TODO: methods for listeners callbacks

    /*
     * Report a message that is sent by the participant
     * @param loc, destination
     * @param payload_size, size of the current message
     */
    void on_rtps_sent(
            const fastrtps::rtps::Locator_t& loc,
            unsigned long payload_size);

    /*
     * Report a message that is sent by the participant
     * @param sender_guid GUID of the entity producing the message
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     * @param payload_size, size of the current message
     */
    template<class LocatorIteratorT>
    void on_rtps_send(
            const GUID_t& sender_guid,
            const LocatorIteratorT& destination_locators_begin,
            const LocatorIteratorT& destination_locators_end,
            unsigned long payload_size)
    {
        if (false == is_statistics_builtin(sender_guid.entityId))
        {
            auto it = destination_locators_begin;
            while (it != destination_locators_end)
            {
                on_rtps_sent(*it, payload_size);
                ++it;
            }
        }
    }

    /*
     * Report that a new entity is discovered
     * @param id, discovered entity GUID_t
     */
    void on_entity_discovery(
            const GUID_t& id);

    /*
     * Auxiliary method to report PDP message exchange.
     * @param packages number of pdp packages sent
     */
    void on_pdp_packet(
            const uint32_t packages);

    /*
     * Report PDP message exchange.
     * We filtered the non-pdp traffic here to minimize presence of statistics code in endpoints implementation.
     * @param sender_guid GUID_t to filter
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     */
    template<class LocatorIteratorT>
    void on_pdp_packet(
            const GUID_t& sender_guid,
            const LocatorIteratorT& destination_locators_begin,
            const LocatorIteratorT& destination_locators_end)
    {
        if (sender_guid.entityId == fastrtps::rtps::c_EntityId_SPDPWriter
                && destination_locators_begin != destination_locators_end)
        {
            uint32_t packages = 0;
            auto it = destination_locators_begin;
            while (it != destination_locators_end)
            {
                ++it;
                ++packages;
            }

            on_pdp_packet(packages);
        }
    }

public:

    /*
     * Registers a listener in participant's statistics callback queue
     * @param listener smart pointer to the listener being registered
     * @param kind combination of fastdds::statistics::EventKind flags used as a mask
     * @return successfully registered
     */
    bool add_statistics_listener(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            uint32_t kind);

    /*
     * Unregisters a listener in participant's statistics callback queue
     * @param listener smart pointer to the listener being unregistered
     * @param kind combination of fastdds::statistics::EventKind flags used as a mask
     * @return successfully unregistered
     */
    bool remove_statistics_listener(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            uint32_t kind);
};

// auxiliary conversion functions
detail::Locator_s to_statistics_type(fastrtps::rtps::Locator_t);
detail::GUID_s to_statistics_type(fastrtps::rtps::GUID_t);

#else // dummy implementation

struct StatisticsAncillary {};

class StatisticsParticipantImpl
{
    friend class fastrtps::rtps::PDP;

protected:

    // inline methods for listeners callbacks

    /*
     * Report a message that is sent by the participant
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     * @param payload_size, size of the current message
     */
    template<class LocatorIteratorT>
    inline void on_rtps_send(
            const fastrtps::rtps::GUID_t&,
            const LocatorIteratorT&,
            const LocatorIteratorT&,
            unsigned long)
    {
    }

    /*
     * Report that a new entity is discovered
     * @param id, discovered entity GUID_t
     */
    inline void on_entity_discovery(
            const fastrtps::rtps::GUID_t&)
    {
    }

    /*
     * Report PDP message exchange.
     * We filtered the non-pdp traffic here to minimize presence of statistics code in endpoints implementation.
     * @param sender_guid GUID_t to filter
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     */
    template<class LocatorIteratorT>
    inline void on_pdp_packet(
            const fastrtps::rtps::GUID_t& sender_guid,
            const LocatorIteratorT& destination_locators_begin,
            const LocatorIteratorT& destination_locators_end)
    {
    }

};

#endif // FASTDDS_STATISTICS

} // namespace statistics
} // namespace fastdds
} // namespace eprosima

#endif // _STATISTICS_RTPS_STATISTICSBASE_HPP_
<commit_msg>Fix unused-parameter warnings<commit_after>// Copyright 2021 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// 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
//
// 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.

/**
 * @file StatisticsBase.hpp
 */

#ifndef _STATISTICS_RTPS_STATISTICSBASE_HPP_
#define _STATISTICS_RTPS_STATISTICSBASE_HPP_

#include <mutex>
#include <set>

#include <fastrtps/config.h>

#include <fastdds/rtps/common/Guid.h>
#include <fastdds/rtps/common/Locator.h>

#include <statistics/types/types.h>
#include <statistics/rtps/GuidUtils.hpp>

#include <fastdds/statistics/rtps/StatisticsCommon.hpp>

namespace eprosima {
namespace fastrtps {
namespace rtps {

class PDP;

} // rtps
} // fastrtps

namespace fastdds {
namespace statistics {

#ifdef FASTDDS_STATISTICS

// RTPSWriter and RTPSReader statistics members
struct StatisticsAncillary
{
    std::set<std::shared_ptr<IListener>> listeners;
};

struct StatisticsWriterAncillary
    : public StatisticsAncillary
{
    unsigned long long data_counter = {};
    unsigned long long gap_counter = {};
    unsigned long long resent_counter = {};
};

struct StatisticsReaderAncillary
    : public StatisticsAncillary
{
};

// lambda function to traverse the listener collection
template<class Function>
Function StatisticsListenersImpl::for_each_listener(
        Function f)
{
    // Use a collection copy to prevent locking on traversal
    std::unique_lock<fastrtps::RecursiveTimedMutex> lock(get_statistics_mutex());
    auto listeners = members_->listeners;
    lock.unlock();

    if (members_)
    {
        for (auto& listener : listeners)
        {
            f(listener);
        }
    }

    return f;
}

class StatisticsParticipantImpl
{
    friend class fastrtps::rtps::PDP;

    // statistics members protection only
    std::recursive_mutex statistics_mutex_;

public:

    using GUID_t = fastrtps::rtps::GUID_t;

private:

    // RTPS_SENT ancillary
    struct rtps_sent_data
    {
        unsigned long long packet_count = {};
        unsigned long long byte_count = {};
    };

    std::map<fastrtps::rtps::Locator_t, rtps_sent_data> traffic;

    // PDP_PACKETS ancillary
    unsigned long long pdp_counter_ = {};

    /*
     * Retrieve the GUID_t from derived class
     * @return endpoint GUID_t
     */
    const GUID_t& get_guid() const;

protected:

    class ListenerProxy
        : public IListener
        , public std::enable_shared_from_this<ListenerProxy>
    {
        // the external_ listener is the actual key value
        mutable uint32_t mask_;
        std::shared_ptr<IListener> external_;

    public:

        ListenerProxy(
                std::shared_ptr<IListener> listener,
                uint32_t mask)
            : mask_(mask)
            , external_(listener)
        {
        }

        void on_statistics_data(
                const Data& data) override;

        uint32_t mask() const;
        void mask(
                uint32_t update) const;

        bool operator <(
                const ListenerProxy& right) const;

        std::shared_ptr<IListener> get_shared_ptr() const
        {
            return std::static_pointer_cast<IListener>(const_cast<ListenerProxy*>(this)->shared_from_this());
        }

    };

    using Key = std::shared_ptr<ListenerProxy>;

    // specialized comparison operator, the actual key is the external listener address
    struct CompareProxies
    {
        bool operator ()(
                const Key& left,
                const Key& right) const
        {
            return *left < *right;
        }

    };

    using ProxyCollection = std::set<Key, CompareProxies>;
    ProxyCollection listeners_;

    // retrieve the participant mutex
    std::recursive_mutex& get_statistics_mutex();

    /** Register a listener in participant RTPSWriter entities.
     * @param listener, smart pointer to the listener interface to register
     * @param guid, RTPSWriter identifier. If unknown the listener is registered in all enable ones
     * @return true on success
     */
    virtual bool register_in_writer(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            GUID_t guid = GUID_t::unknown()) = 0;

    /** Register a listener in participant RTPSReader entities.
     * @param listener, smart pointer to the listener interface to register
     * @param guid, RTPSReader identifier. If unknown the listener is registered in all enable ones
     * @return true on success
     */
    virtual bool register_in_reader(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            GUID_t guid = GUID_t::unknown()) = 0;

    /** Unregister a listener in participant RTPSWriter entities.
     * @param listener, smart pointer to the listener interface to unregister
     * @return true on success
     */
    virtual bool unregister_in_writer(
            std::shared_ptr<fastdds::statistics::IListener> listener) = 0;

    /** Unregister a listener in participant RTPSReader entities.
     * @param listener, smart pointer to the listener interface to unregister
     * @return true on success
     */
    virtual bool unregister_in_reader(
            std::shared_ptr<fastdds::statistics::IListener> listener) = 0;

    // lambda function to traverse the listener collection
    template<class Function>
    Function for_each_listener(
            Function f)
    {
        std::unique_lock<std::recursive_mutex> lock(get_statistics_mutex());
        auto temp_listeners = listeners_;
        lock.unlock();

        for (auto& listener : temp_listeners)
        {
            f(listener);
        }

        return f;
    }

    // returns if a mask statistics::EventKind may require participant writers update
    bool are_writers_involved(
            const uint32_t mask) const;

    // returns if a mask statistics::EventKind may require participant readers update
    bool are_readers_involved(
            const uint32_t mask) const;

    // TODO: methods for listeners callbacks

    /*
     * Report a message that is sent by the participant
     * @param loc, destination
     * @param payload_size, size of the current message
     */
    void on_rtps_sent(
            const fastrtps::rtps::Locator_t& loc,
            unsigned long payload_size);

    /*
     * Report a message that is sent by the participant
     * @param sender_guid GUID of the entity producing the message
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     * @param payload_size, size of the current message
     */
    template<class LocatorIteratorT>
    void on_rtps_send(
            const GUID_t& sender_guid,
            const LocatorIteratorT& destination_locators_begin,
            const LocatorIteratorT& destination_locators_end,
            unsigned long payload_size)
    {
        if (false == is_statistics_builtin(sender_guid.entityId))
        {
            auto it = destination_locators_begin;
            while (it != destination_locators_end)
            {
                on_rtps_sent(*it, payload_size);
                ++it;
            }
        }
    }

    /*
     * Report that a new entity is discovered
     * @param id, discovered entity GUID_t
     */
    void on_entity_discovery(
            const GUID_t& id);

    /*
     * Auxiliary method to report PDP message exchange.
     * @param packages number of pdp packages sent
     */
    void on_pdp_packet(
            const uint32_t packages);

    /*
     * Report PDP message exchange.
     * We filtered the non-pdp traffic here to minimize presence of statistics code in endpoints implementation.
     * @param sender_guid GUID_t to filter
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     */
    template<class LocatorIteratorT>
    void on_pdp_packet(
            const GUID_t& sender_guid,
            const LocatorIteratorT& destination_locators_begin,
            const LocatorIteratorT& destination_locators_end)
    {
        if (sender_guid.entityId == fastrtps::rtps::c_EntityId_SPDPWriter
                && destination_locators_begin != destination_locators_end)
        {
            uint32_t packages = 0;
            auto it = destination_locators_begin;
            while (it != destination_locators_end)
            {
                ++it;
                ++packages;
            }

            on_pdp_packet(packages);
        }
    }

public:

    /*
     * Registers a listener in participant's statistics callback queue
     * @param listener smart pointer to the listener being registered
     * @param kind combination of fastdds::statistics::EventKind flags used as a mask
     * @return successfully registered
     */
    bool add_statistics_listener(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            uint32_t kind);

    /*
     * Unregisters a listener in participant's statistics callback queue
     * @param listener smart pointer to the listener being unregistered
     * @param kind combination of fastdds::statistics::EventKind flags used as a mask
     * @return successfully unregistered
     */
    bool remove_statistics_listener(
            std::shared_ptr<fastdds::statistics::IListener> listener,
            uint32_t kind);
};

// auxiliary conversion functions
detail::Locator_s to_statistics_type(fastrtps::rtps::Locator_t);
detail::GUID_s to_statistics_type(fastrtps::rtps::GUID_t);

#else // dummy implementation

struct StatisticsAncillary {};

class StatisticsParticipantImpl
{
    friend class fastrtps::rtps::PDP;

protected:

    // inline methods for listeners callbacks

    /*
     * Report a message that is sent by the participant
     * @param destination_locators_begin, start of locators range
     * @param destination_locators_end, end of locators range
     * @param payload_size, size of the current message
     */
    template<class LocatorIteratorT>
    inline void on_rtps_send(
            const fastrtps::rtps::GUID_t&,
            const LocatorIteratorT&,
            const LocatorIteratorT&,
            unsigned long)
    {
    }

    /*
     * Report that a new entity is discovered
     * @param id, discovered entity GUID_t
     */
    inline void on_entity_discovery(
            const fastrtps::rtps::GUID_t&)
    {
    }

    /*
     * Report PDP message exchange.
     * We filtered the non-pdp traffic here to minimize presence of statistics code in endpoints implementation.
     * @param GUID_t to filter
     * @param start of locators range
     * @param end of locators range
     */
    template<class LocatorIteratorT>
    inline void on_pdp_packet(
            const fastrtps::rtps::GUID_t&,
            const LocatorIteratorT&,
            const LocatorIteratorT&)
    {
    }

};

#endif // FASTDDS_STATISTICS

} // namespace statistics
} // namespace fastdds
} // namespace eprosima

#endif // _STATISTICS_RTPS_STATISTICSBASE_HPP_
<|endoftext|>


<commit_before>/***********************************************************************
    created:    Wed, 8th Feb 2012
    author:     Lukas E Meindl (based on code by Paul D Turner)
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2012 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUI/RendererModules/OpenGL/RenderTarget.h"
#include "CEGUI/RendererModules/OpenGL/GeometryBufferBase.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <cmath>

// Start of CEGUI namespace section
namespace CEGUI
{
//----------------------------------------------------------------------------//
template <typename T>
OpenGLRenderTarget<T>::OpenGLRenderTarget(OpenGLRendererBase& owner) :
    d_owner(owner)
{
}

//----------------------------------------------------------------------------//
template <typename T>
OpenGLRenderTarget<T>::~OpenGLRenderTarget()
{
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::activate()
{
    glViewport(static_cast<GLsizei>(RenderTarget::d_area.left()),
               static_cast<GLsizei>(RenderTarget::d_area.top()),
               static_cast<GLsizei>(RenderTarget::d_area.getWidth()),
               static_cast<GLsizei>(RenderTarget::d_area.getHeight()));

    if (!RenderTarget::d_matrixValid)
        updateMatrix();

    d_owner.setViewProjectionMatrix(RenderTarget::d_matrix);

    RenderTarget::activate();
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::unprojectPoint(const GeometryBuffer& buff,
    const glm::vec2& p_in, glm::vec2& p_out) const
{
    if (!RenderTarget::d_matrixValid)
        updateMatrix();

    const OpenGLGeometryBufferBase& gb =
        static_cast<const OpenGLGeometryBufferBase&>(buff);

    const GLint vp[4] = {
        static_cast<GLint>(RenderTarget::d_area.left()),
        static_cast<GLint>(RenderTarget::d_area.top()),
        static_cast<GLint>(RenderTarget::d_area.getWidth()),
        static_cast<GLint>(RenderTarget::d_area.getHeight())
    };

    GLdouble in_x, in_y, in_z;

    glm::ivec4 viewPort = glm::ivec4(vp[0], vp[1], vp[2], vp[3]);
    const glm::mat4& projMatrix = RenderTarget::d_matrix;
    const glm::mat4& modelMatrix = gb.getModelMatrix();

    // unproject the ends of the ray
    glm::vec3 unprojected1;
    glm::vec3 unprojected2;
    in_x = vp[2] * 0.5;
    in_y = vp[3] * 0.5;
    in_z = -RenderTarget::d_viewDistance;
    unprojected1 =  glm::unProject(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = p_in.x;
    in_y = vp[3] - p_in.y;
    in_z = 0.0;
    unprojected2 = glm::unProject(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);

    // project points to orientate them with GeometryBuffer plane
    glm::vec3 projected1;
    glm::vec3 projected2;
    glm::vec3 projected3;
    in_x = 0.0;
    in_y = 0.0;
    projected1 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = 1.0;
    in_y = 0.0;
    projected2 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = 0.0;
    in_y = 1.0;
    projected3 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);

    // calculate vectors for generating the plane
    const glm::vec3 pv1 = projected2 - projected1;
    const glm::vec3 pv2 = projected3 - projected1;
    // given the vectors, calculate the plane normal
    const glm::vec3 planeNormal = glm::cross(pv1, pv2);
    // calculate plane
    const glm::vec3 planeNormalNormalized = glm::normalize(planeNormal);
    const double pl_d = - glm::dot(projected1, planeNormalNormalized);
    // calculate vector of picking ray
    const glm::vec3 rv = unprojected1 - unprojected2;
    // calculate intersection of ray and plane
    const double pn_dot_r1 = glm::dot(unprojected1, planeNormal);
    const double pn_dot_rv = glm::dot(rv, planeNormal);
    const double tmp1 = pn_dot_rv != 0.0 ? (pn_dot_r1 + pl_d) / pn_dot_rv : 0.0;
    const double is_x = unprojected1.x - rv.x * tmp1;
    const double is_y = unprojected1.y - rv.y * tmp1;

    p_out.x = static_cast<float>(is_x);
    p_out.y = static_cast<float>(is_y);
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::updateMatrix() const
{
    RenderTarget::updateMatrix( RenderTarget::createViewProjMatrixForOpenGL() );
}

//----------------------------------------------------------------------------//
template <typename T>
OpenGLRendererBase& OpenGLRenderTarget<T>::getOwner()
{
    return d_owner;
}

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

} // End of  CEGUI namespace section
<commit_msg>GLES2/3 does not support GLdouble<commit_after>/***********************************************************************
    created:    Wed, 8th Feb 2012
    author:     Lukas E Meindl (based on code by Paul D Turner)
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2012 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#include "CEGUI/RendererModules/OpenGL/RenderTarget.h"
#include "CEGUI/RendererModules/OpenGL/GeometryBufferBase.h"

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <cmath>

// Start of CEGUI namespace section
namespace CEGUI
{
//----------------------------------------------------------------------------//
template <typename T>
OpenGLRenderTarget<T>::OpenGLRenderTarget(OpenGLRendererBase& owner) :
    d_owner(owner)
{
}

//----------------------------------------------------------------------------//
template <typename T>
OpenGLRenderTarget<T>::~OpenGLRenderTarget()
{
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::activate()
{
    glViewport(static_cast<GLsizei>(RenderTarget::d_area.left()),
               static_cast<GLsizei>(RenderTarget::d_area.top()),
               static_cast<GLsizei>(RenderTarget::d_area.getWidth()),
               static_cast<GLsizei>(RenderTarget::d_area.getHeight()));

    if (!RenderTarget::d_matrixValid)
        updateMatrix();

    d_owner.setViewProjectionMatrix(RenderTarget::d_matrix);

    RenderTarget::activate();
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::unprojectPoint(const GeometryBuffer& buff,
    const glm::vec2& p_in, glm::vec2& p_out) const
{
    if (!RenderTarget::d_matrixValid)
        updateMatrix();

    const OpenGLGeometryBufferBase& gb =
        static_cast<const OpenGLGeometryBufferBase&>(buff);

    const GLint vp[4] = {
        static_cast<GLint>(RenderTarget::d_area.left()),
        static_cast<GLint>(RenderTarget::d_area.top()),
        static_cast<GLint>(RenderTarget::d_area.getWidth()),
        static_cast<GLint>(RenderTarget::d_area.getHeight())
    };

    GLfloat in_x, in_y, in_z;

    glm::ivec4 viewPort = glm::ivec4(vp[0], vp[1], vp[2], vp[3]);
    const glm::mat4& projMatrix = RenderTarget::d_matrix;
    const glm::mat4& modelMatrix = gb.getModelMatrix();

    // unproject the ends of the ray
    glm::vec3 unprojected1;
    glm::vec3 unprojected2;
    in_x = vp[2] * 0.5;
    in_y = vp[3] * 0.5;
    in_z = -RenderTarget::d_viewDistance;
    unprojected1 =  glm::unProject(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = p_in.x;
    in_y = vp[3] - p_in.y;
    in_z = 0.0;
    unprojected2 = glm::unProject(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);

    // project points to orientate them with GeometryBuffer plane
    glm::vec3 projected1;
    glm::vec3 projected2;
    glm::vec3 projected3;
    in_x = 0.0;
    in_y = 0.0;
    projected1 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = 1.0;
    in_y = 0.0;
    projected2 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);
    in_x = 0.0;
    in_y = 1.0;
    projected3 = glm::project(glm::vec3(in_x, in_y, in_z), modelMatrix, projMatrix, viewPort);

    // calculate vectors for generating the plane
    const glm::vec3 pv1 = projected2 - projected1;
    const glm::vec3 pv2 = projected3 - projected1;
    // given the vectors, calculate the plane normal
    const glm::vec3 planeNormal = glm::cross(pv1, pv2);
    // calculate plane
    const glm::vec3 planeNormalNormalized = glm::normalize(planeNormal);
    const double pl_d = - glm::dot(projected1, planeNormalNormalized);
    // calculate vector of picking ray
    const glm::vec3 rv = unprojected1 - unprojected2;
    // calculate intersection of ray and plane
    const double pn_dot_r1 = glm::dot(unprojected1, planeNormal);
    const double pn_dot_rv = glm::dot(rv, planeNormal);
    const double tmp1 = pn_dot_rv != 0.0 ? (pn_dot_r1 + pl_d) / pn_dot_rv : 0.0;
    const double is_x = unprojected1.x - rv.x * tmp1;
    const double is_y = unprojected1.y - rv.y * tmp1;

    p_out.x = static_cast<float>(is_x);
    p_out.y = static_cast<float>(is_y);
}

//----------------------------------------------------------------------------//
template <typename T>
void OpenGLRenderTarget<T>::updateMatrix() const
{
    RenderTarget::updateMatrix( RenderTarget::createViewProjMatrixForOpenGL() );
}

//----------------------------------------------------------------------------//
template <typename T>
OpenGLRendererBase& OpenGLRenderTarget<T>::getOwner()
{
    return d_owner;
}

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

} // End of  CEGUI namespace section
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: PlottingPositionHelper.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: bm $ $Date: 2003-10-06 09:58:33 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2003 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _CHART2_PLOTTINGPOSITIONHELPER_HXX
#define _CHART2_PLOTTINGPOSITIONHELPER_HXX

#ifndef _DRAFTS_COM_SUN_STAR_CHART2_EXPLICITSCALEDATA_HPP_
#include <drafts/com/sun/star/chart2/ExplicitScaleData.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_CHART2_XTRANSFORMATION_HPP_
#include <drafts/com/sun/star/chart2/XTransformation.hpp>
#endif

#ifndef _COM_SUN_STAR_DRAWING_HOMOGENMATRIX_HPP_
#include <com/sun/star/drawing/HomogenMatrix.hpp>
#endif

#ifndef _B3D_HMATRIX_HXX
#include <goodies/hmatrix.hxx>
#endif

//.............................................................................
namespace chart
{
//.............................................................................

//-----------------------------------------------------------------------------
/**
*/

class PlottingPositionHelper
{
public:
    PlottingPositionHelper();
    virtual ~PlottingPositionHelper();

    void setTransformationSceneToScreen( const ::com::sun::star::drawing::HomogenMatrix& rMatrix);

    void setScales( const ::com::sun::star::uno::Sequence<
            ::drafts::com::sun::star::chart2::ExplicitScaleData >& rScales );

    inline bool   isLogicVisible( double fX, double fY, double fZ ) const;
    inline void   doLogicScaling( double* pX, double* pY, double* pZ ) const;
    inline void   clipLogicValues( double* pX, double* pY, double* pZ ) const;

    virtual ::com::sun::star::uno::Reference< ::drafts::com::sun::star::chart2::XTransformation >
                  getTransformationLogicToScene() const;

    inline double getLogicMinX() const;
    inline double getLogicMinY() const;
    inline double getLogicMinZ() const;
    inline double getLogicMaxX() const;
    inline double getLogicMaxY() const;
    inline double getLogicMaxZ() const;

    void getLogicMinimum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;
    void getLogicMaximum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;

    void getScreenValuesForMinimum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;
    void getScreenValuesForMaximum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;

protected: //member
    ::com::sun::star::uno::Sequence<
            ::drafts::com::sun::star::chart2::ExplicitScaleData > m_aScales;
    Matrix4D                                                      m_aMatrixScreenToScene;

    //this is calculated based on m_aScales and m_aMatrixScreenToScene
    mutable ::com::sun::star::uno::Reference<
        ::drafts::com::sun::star::chart2::XTransformation >     m_xTransformationLogicToScene;
};

bool PlottingPositionHelper::isLogicVisible(
    double fX, double fY, double fZ ) const
{
    return fX >= m_aScales[0].Minimum && fX <= m_aScales[0].Maximum
        && fY >= m_aScales[1].Minimum && fY <= m_aScales[1].Maximum
        && fZ >= m_aScales[2].Minimum && fZ <= m_aScales[2].Maximum;
}

void PlottingPositionHelper::doLogicScaling( double* pX, double* pY, double* pZ ) const
{
    if(pX && m_aScales[0].Scaling.is())
        *pX = m_aScales[0].Scaling->doScaling(*pX);
    if(pY && m_aScales[1].Scaling.is())
        *pY = m_aScales[1].Scaling->doScaling(*pY);
    if(pZ && m_aScales[2].Scaling.is())
        *pZ = m_aScales[2].Scaling->doScaling(*pZ);
}

void PlottingPositionHelper::clipLogicValues( double* pX, double* pY, double* pZ ) const
{
    if(pX)
    {
        if( *pX < m_aScales[0].Minimum )
            *pX = m_aScales[0].Minimum;
        else if( *pX > m_aScales[0].Maximum )
            *pX = m_aScales[0].Maximum;
    }
    if(pY)
    {
        if( *pY < m_aScales[1].Minimum )
            *pY = m_aScales[1].Minimum;
        else if( *pY > m_aScales[1].Maximum )
            *pY = m_aScales[1].Maximum;
    }
    if(pZ)
    {
        if( *pZ < m_aScales[2].Minimum )
            *pZ = m_aScales[2].Minimum;
        else if( *pZ > m_aScales[2].Maximum )
            *pZ = m_aScales[2].Maximum;
    }
}

inline double PlottingPositionHelper::getLogicMinX() const
{
    return m_aScales[0].Minimum;
}
inline double PlottingPositionHelper::getLogicMinY() const
{
    return m_aScales[1].Minimum;
}
inline double PlottingPositionHelper::getLogicMinZ() const
{
    return m_aScales[2].Minimum;
}

inline double PlottingPositionHelper::getLogicMaxX() const
{
    return m_aScales[0].Maximum;
}
inline double PlottingPositionHelper::getLogicMaxY() const
{
    return m_aScales[1].Maximum;
}
inline double PlottingPositionHelper::getLogicMaxZ() const
{
    return m_aScales[2].Maximum;
}

//.............................................................................
} //namespace chart
//.............................................................................
#endif
<commit_msg>added methods isMathematicalOrientationX/Y/Z<commit_after>/*************************************************************************
 *
 *  $RCSfile: PlottingPositionHelper.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: iha $ $Date: 2003-11-19 13:13:28 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2003 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _CHART2_PLOTTINGPOSITIONHELPER_HXX
#define _CHART2_PLOTTINGPOSITIONHELPER_HXX

#ifndef _DRAFTS_COM_SUN_STAR_CHART2_EXPLICITSCALEDATA_HPP_
#include <drafts/com/sun/star/chart2/ExplicitScaleData.hpp>
#endif
#ifndef _DRAFTS_COM_SUN_STAR_CHART2_XTRANSFORMATION_HPP_
#include <drafts/com/sun/star/chart2/XTransformation.hpp>
#endif

#ifndef _COM_SUN_STAR_DRAWING_HOMOGENMATRIX_HPP_
#include <com/sun/star/drawing/HomogenMatrix.hpp>
#endif

#ifndef _B3D_HMATRIX_HXX
#include <goodies/hmatrix.hxx>
#endif

//.............................................................................
namespace chart
{
//.............................................................................

//-----------------------------------------------------------------------------
/**
*/

class PlottingPositionHelper
{
public:
    PlottingPositionHelper();
    virtual ~PlottingPositionHelper();

    void setTransformationSceneToScreen( const ::com::sun::star::drawing::HomogenMatrix& rMatrix);

    void setScales( const ::com::sun::star::uno::Sequence<
            ::drafts::com::sun::star::chart2::ExplicitScaleData >& rScales );

    inline bool   isLogicVisible( double fX, double fY, double fZ ) const;
    inline void   doLogicScaling( double* pX, double* pY, double* pZ ) const;
    inline void   clipLogicValues( double* pX, double* pY, double* pZ ) const;

    virtual ::com::sun::star::uno::Reference< ::drafts::com::sun::star::chart2::XTransformation >
                  getTransformationLogicToScene() const;

    inline double getLogicMinX() const;
    inline double getLogicMinY() const;
    inline double getLogicMinZ() const;
    inline double getLogicMaxX() const;
    inline double getLogicMaxY() const;
    inline double getLogicMaxZ() const;

    inline bool isMathematicalOrientationX() const;
    inline bool isMathematicalOrientationY() const;
    inline bool isMathematicalOrientationZ() const;

    void getLogicMinimum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;
    void getLogicMaximum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;

    void getScreenValuesForMinimum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;
    void getScreenValuesForMaximum( ::com::sun::star::uno::Sequence< double >& rSeq ) const;

protected: //member
    ::com::sun::star::uno::Sequence<
            ::drafts::com::sun::star::chart2::ExplicitScaleData > m_aScales;
    Matrix4D                                                      m_aMatrixScreenToScene;

    //this is calculated based on m_aScales and m_aMatrixScreenToScene
    mutable ::com::sun::star::uno::Reference<
        ::drafts::com::sun::star::chart2::XTransformation >     m_xTransformationLogicToScene;
};

bool PlottingPositionHelper::isLogicVisible(
    double fX, double fY, double fZ ) const
{
    return fX >= m_aScales[0].Minimum && fX <= m_aScales[0].Maximum
        && fY >= m_aScales[1].Minimum && fY <= m_aScales[1].Maximum
        && fZ >= m_aScales[2].Minimum && fZ <= m_aScales[2].Maximum;
}

void PlottingPositionHelper::doLogicScaling( double* pX, double* pY, double* pZ ) const
{
    if(pX && m_aScales[0].Scaling.is())
        *pX = m_aScales[0].Scaling->doScaling(*pX);
    if(pY && m_aScales[1].Scaling.is())
        *pY = m_aScales[1].Scaling->doScaling(*pY);
    if(pZ && m_aScales[2].Scaling.is())
        *pZ = m_aScales[2].Scaling->doScaling(*pZ);
}

void PlottingPositionHelper::clipLogicValues( double* pX, double* pY, double* pZ ) const
{
    if(pX)
    {
        if( *pX < m_aScales[0].Minimum )
            *pX = m_aScales[0].Minimum;
        else if( *pX > m_aScales[0].Maximum )
            *pX = m_aScales[0].Maximum;
    }
    if(pY)
    {
        if( *pY < m_aScales[1].Minimum )
            *pY = m_aScales[1].Minimum;
        else if( *pY > m_aScales[1].Maximum )
            *pY = m_aScales[1].Maximum;
    }
    if(pZ)
    {
        if( *pZ < m_aScales[2].Minimum )
            *pZ = m_aScales[2].Minimum;
        else if( *pZ > m_aScales[2].Maximum )
            *pZ = m_aScales[2].Maximum;
    }
}

inline double PlottingPositionHelper::getLogicMinX() const
{
    return m_aScales[0].Minimum;
}
inline double PlottingPositionHelper::getLogicMinY() const
{
    return m_aScales[1].Minimum;
}
inline double PlottingPositionHelper::getLogicMinZ() const
{
    return m_aScales[2].Minimum;
}

inline double PlottingPositionHelper::getLogicMaxX() const
{
    return m_aScales[0].Maximum;
}
inline double PlottingPositionHelper::getLogicMaxY() const
{
    return m_aScales[1].Maximum;
}
inline double PlottingPositionHelper::getLogicMaxZ() const
{
    return m_aScales[2].Maximum;
}
inline bool PlottingPositionHelper::isMathematicalOrientationX() const
{
    return ::drafts::com::sun::star::chart2::AxisOrientation_MATHEMATICAL == m_aScales[0].Orientation;
}
inline bool PlottingPositionHelper::isMathematicalOrientationY() const
{
    return ::drafts::com::sun::star::chart2::AxisOrientation_MATHEMATICAL == m_aScales[1].Orientation;
}
inline bool PlottingPositionHelper::isMathematicalOrientationZ() const
{
    return ::drafts::com::sun::star::chart2::AxisOrientation_MATHEMATICAL == m_aScales[2].Orientation;
}

//.............................................................................
} //namespace chart
//.............................................................................
#endif
<|endoftext|>


<commit_before>#include <chord.h>
#include "merkle_syncer.h"
#include "qhash.h"
#include "async.h"
#include "bigint.h"
#include <id_utils.h>
#include <comm.h>

//#define MERKLE_SYNC_TRACE
//#define MERKLE_SYNC_DETAILED_TRACE

// ---------------------------------------------------------------------------
// util junk

// Check whether [l1, r1] overlaps [l2, r2] on the circle.
static bool
overlap (const bigint &l1, const bigint &r1, const bigint &l2, const bigint &r2)
{
  // There might be a more efficient way to do this..
  return (betweenbothincl (l1, r1, l2) || betweenbothincl (l1, r1, r2)
	  || betweenbothincl (l2, r2, l1) || betweenbothincl (l2, r2, r1));
}

// ---------------------------------------------------------------------------
// merkle_syncer

merkle_syncer::merkle_syncer (merkle_tree *ltree, rpcfnc_t rpcfnc, missingfnc_t missingfnc)
  : ltree (ltree), rpcfnc (rpcfnc), missingfnc (missingfnc)
{
  fatal_err = NULL;
  sync_done = false;
}

void
merkle_syncer::sync (bigint rngmin, bigint rngmax)
{
  local_rngmin = rngmin;
  local_rngmax = rngmax;

  // start at the root of the merkle tree
  sendnode (0, 0);
}

void
merkle_syncer::dump ()
{
  warn << "THIS: " << (u_int)this << "\n";
  warn << "  st.size () " << st.size () << "\n";
}

void
merkle_syncer::doRPC (int procno, ptr<void> in, void *out, aclnt_cb cb)
{
  // Must resort to bundling all values into one argument since
  // async/callback.h is configured with too few parameters.
  struct RPC_delay_args args (merklesync_program_1, procno, in, out, cb,
			      NULL);
  (*rpcfnc) (&args);
}

void
merkle_syncer::setdone ()
{
  sync_done = true;
}

void
merkle_syncer::error (str err)
{
  warn << (u_int)this << ": SYNCER ERROR: " << err << "\n";
  fatal_err = err;
  setdone ();
}

str
merkle_syncer::getsummary ()
{
  assert (sync_done);
  strbuf sb;

  sb << "[" << local_rngmin << "," << local_rngmax << "] ";

  if (fatal_err)
    sb << fatal_err;

  if (0)
    sb  << "<log " << log << ">\n";

  return sb;
}

void
merkle_syncer::next (void)
{
#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "local range [" <<  local_rngmin << "," << local_rngmax << "]\n";
#endif
  assert (!sync_done);
  assert (!fatal_err);

  // st is queue of pending index nodes
  while (st.size ()) {
    pair<merkle_rpc_node, int> &p = st.back ();
    merkle_rpc_node *rnode = &p.first;
    assert (!rnode->isleaf);

    merkle_node *lnode = ltree->lookup_exact (rnode->depth, rnode->prefix);

    if (!lnode) {
      fatal << "lookup_exact didn't match for " << rnode->prefix << " at depth " << rnode->depth << "\n";
    }

#ifdef MERKLE_SYNC_DETAILED_TRACE
    warn << "starting from slot " << p.second << "\n";
#endif

    // XXX not clear p.second will ever enter > 0
    while (p.second < 64) {
      u_int i = p.second;
      p.second += 1;
#ifdef MERKLE_SYNC_DETAILED_TRACE
      warn << "CHECKING: " << i;
#endif

      bigint remote = tobigint (rnode->child_hash[i]);
      bigint local = tobigint (lnode->child (i)->hash);

      u_int depth = rnode->depth + 1;

      //prefix is the high bits of the first key
      // the node is responsible for.
      merkle_hash prefix = rnode->prefix;
      prefix.write_slot (rnode->depth, i);
      bigint slot_rngmin = tobigint (prefix);
      bigint slot_width = bigint (1) << (160 - 6*depth);
      bigint slot_rngmax = slot_rngmin + slot_width - 1;

      bool overlaps = overlap (local_rngmin, local_rngmax, slot_rngmin, slot_rngmax);

      if (remote != local) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	warnx << " differ. local " << local << " != remote " << remote;
#endif
	if (overlaps) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	  warnx << " .. sending\n";
#endif
	  sendnode (depth, prefix);
	  return;
	} else {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	  warnx << " .. not sending\n";
#endif
	}
      } else {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	warnx << " same. local " << local << " == remote " << remote << "\n";
#endif
      }
    }

    assert (p.second == 64);
    st.pop_back ();
  }

#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "DONE .. in NEXT\n";
#endif
  setdone ();
#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "OK!\n";
#endif
}

void
merkle_syncer::sendnode (u_int depth, const merkle_hash &prefix)
{

  ref<sendnode_arg> arg = New refcounted<sendnode_arg> ();
  ref<sendnode_res> res = New refcounted<sendnode_res> ();

  u_int lnode_depth;
  merkle_node *lnode = ltree->lookup (&lnode_depth, depth, prefix);
  assert (lnode);
  assert (lnode_depth == depth);

  format_rpcnode (ltree, depth, prefix, lnode, &arg->node);
  arg->rngmin = local_rngmin;
  arg->rngmax = local_rngmax;
  doRPC (MERKLESYNC_SENDNODE, arg, res,
	 wrap (this, &merkle_syncer::sendnode_cb, arg, res));
}

void
merkle_syncer::sendnode_cb (ref<sendnode_arg> arg, ref<sendnode_res> res,
			    clnt_stat err)
{

  if (err) {
    error (strbuf () << "SENDNODE: rpc error " << err);
    return;
  } else if (res->status != MERKLE_OK) {
    error (strbuf () << "SENDNODE: protocol error " << err2str (res->status));
    return;
  }

  merkle_rpc_node *rnode = &res->resok->node;

  merkle_node *lnode = ltree->lookup_exact (rnode->depth, rnode->prefix);
  if (!lnode) {
    fatal << "lookup failed: " << rnode->prefix << " at " << rnode->depth << "\n";
  }

  compare_nodes (ltree, local_rngmin, local_rngmax, lnode, rnode,
		 missingfnc, rpcfnc);

  if (!lnode->isleaf () && !rnode->isleaf) {
#ifdef MERKLE_SYNC_TRACE
    warn << "I vs I\n";
#endif
    st.push_back (pair<merkle_rpc_node, int> (*rnode, 0));
  }

  next ();
}

// ---------------------------------------------------------------------------
// merkle_getkeyrange

void
merkle_getkeyrange::go ()
{
  if (between (rngmax, incID (current), current)) {
#ifdef MERKLE_SYNC_TRACE
    warn << "merkle_getkeyrange::go () ==> DONE\n";
#endif
    delete this;
    return;
  }

  ref<getkeys_arg> arg = New refcounted<getkeys_arg> ();
  arg->rngmin = current;
  arg->rngmax = rngmax;
  ref<getkeys_res> res = New refcounted<getkeys_res> ();
  doRPC (MERKLESYNC_GETKEYS, arg, res,
	 wrap (this, &merkle_getkeyrange::getkeys_cb, arg, res));
}

void
merkle_getkeyrange::getkeys_cb (ref<getkeys_arg> arg, ref<getkeys_res> res,
				clnt_stat err)

{
  if (err) {
    warn << "GETKEYS: rpc error " << err << "\n";
    delete this;
    return;
  } else if (res->status != MERKLE_OK) {
    warn << "GETKEYS: protocol error " << err2str (res->status) << "\n";
    delete this;
    return;
  }

  vec<merkle_hash> rkeys;
  chordID round_max = current;
  chordID next_cur = current;
  for (u_int i = 0; i < res->resok->keys.size (); i++) {
    const merkle_hash &key = res->resok->keys[i];
    rkeys.push_back (key);

    bigint bkey = tobigint(key);
    if (round_max < bkey) round_max = bkey;
    if (betweenbothincl (next_cur, incID (bkey), bkey))
      next_cur = incID (bkey);
  }

  compare_keylists (lkeys, rkeys, current, round_max, missing);

  current = next_cur;

  if (!res->resok->morekeys)
    current = incID (rngmax);  // set done

  go ();
}

void
merkle_getkeyrange::doRPC (int procno, ptr<void> in, void *out, aclnt_cb cb)
{
  // Must resort to bundling all values into one argument since
  // async/callback.h is configured with too few parameters.
  struct RPC_delay_args args (merklesync_program_1, procno, in, out, cb,
			      NULL);
  (*rpcfnc) (&args);
}

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

void
compare_keylists (vec<merkle_hash> lkeys,
		  vec<merkle_hash> vrkeys,
		  chordID rngmin, chordID rngmax,
		  missingfnc_t missingfnc)
{
  // populate a hash table with the remote keys
  qhash<merkle_hash, int> rkeys;
  for (u_int i = 0; i < vrkeys.size (); i++) {
    if (betweenbothincl (rngmin, rngmax, tobigint(vrkeys[i])))
      rkeys.insert (vrkeys[i], 1);
  }

  // do I have something he doesn't have?
  for (unsigned int i = 0; i < lkeys.size (); i++) {
    if (!rkeys[lkeys[i]]) {
      (*missingfnc) (tobigint(lkeys[i]), false);
    } else {
      rkeys.remove (lkeys[i]);
    }
  }

  //anything left: he has and I don't
  qhash_slot<merkle_hash, int> *slot = rkeys.first ();
  while (slot) {
    warn << "local missing [" << rngmin << ", " << rngmax << "] key=" << slot->key << "\n";
    (*missingfnc) (tobigint(slot->key), true);
    slot = rkeys.next (slot);
  }

}

void
compare_nodes (merkle_tree *ltree, bigint rngmin, bigint rngmax,
	       merkle_node *lnode, merkle_rpc_node *rnode,
	       missingfnc_t missingfnc, rpcfnc_t rpcfnc)
{
#ifdef MERKLE_SYNC_TRACE
  warn << (lnode->isleaf ()  ? "L" : "I")
       << " vs "
       << (rnode->isleaf ? "L" : "I")
       << "\n";
#endif

  if (rnode->isleaf) {
    vec<merkle_hash> lkeys = database_get_keys (ltree->db, rnode->depth, rnode->prefix);

    vec<merkle_hash> rkeys;
    for (u_int i = 0; i < rnode->child_hash.size (); i++) {
      assert (betweenbothincl (rngmin, rngmax, tobigint (rnode->child_hash[i])));
      rkeys.push_back (rnode->child_hash[i]);
    }

    compare_keylists (lkeys, rkeys, rngmin, rngmax, missingfnc);

  } else if (lnode->isleaf () && !rnode->isleaf) {
    bigint tmpmin = tobigint (rnode->prefix);
    bigint node_width = bigint (1) << (160 - rnode->depth);
    bigint tmpmax = tmpmin + node_width - 1;

    // further constrain to be within the host's range of interest
    if (between (tmpmin, tmpmax, rngmin))
      tmpmin = rngmin;
    if (between (tmpmin, tmpmax, rngmax))
      tmpmax = rngmax;

    vec<merkle_hash> lkeys = database_get_keys (ltree->db, rnode->depth, rnode->prefix);
    vNew merkle_getkeyrange (ltree->db, tmpmin, tmpmax, lkeys, missingfnc, rpcfnc);
  }
}

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

void
format_rpcnode (merkle_tree *ltree, u_int depth, const merkle_hash &prefix,
		const merkle_node *node, merkle_rpc_node *rpcnode)
{
  rpcnode->depth = depth;
  rpcnode->prefix = prefix;
  rpcnode->count = node->count;
  rpcnode->hash = node->hash;
  rpcnode->isleaf = node->isleaf ();

  if (!node->isleaf ()) {
    rpcnode->child_hash.setsize (64);
    for (int i = 0; i < 64; i++)
      rpcnode->child_hash[i] = node->child (i)->hash;
  } else {
    vec<merkle_hash> keys = database_get_keys (ltree->db, depth, prefix);

    if (keys.size () != rpcnode->count) {
      warn << "\n\n\n----------------------------------------------------------\n";
      warn << "BUG BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG BUG\n";
      warn << "Send this output to chord@pdos.lcs.mit.edu\n";
      warn << "BUG: " << depth << " " << prefix << "\n";
      warn << "BUG: " << keys.size () << " != " << rpcnode->count << "\n";
      ltree->check_invariants ();
      warn << "BUG BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG BUG\n";
      panic << "----------------------------------------------------------\n\n\n";
    }

    rpcnode->child_hash.setsize (keys.size ());
    for (u_int i = 0; i < keys.size (); i++) {
      rpcnode->child_hash[i] = keys[i];
    }
  }
}
<commit_msg>Filter keys pushed onto compare_keyslists lists based on range. Reduce warn verbosity.<commit_after>#include <chord.h>
#include "merkle_syncer.h"
#include "qhash.h"
#include "async.h"
#include "bigint.h"
#include <id_utils.h>
#include <comm.h>

//#define MERKLE_SYNC_TRACE
//#define MERKLE_SYNC_DETAILED_TRACE

// ---------------------------------------------------------------------------
// util junk

// Check whether [l1, r1] overlaps [l2, r2] on the circle.
static bool
overlap (const bigint &l1, const bigint &r1, const bigint &l2, const bigint &r2)
{
  // There might be a more efficient way to do this..
  return (betweenbothincl (l1, r1, l2) || betweenbothincl (l1, r1, r2)
	  || betweenbothincl (l2, r2, l1) || betweenbothincl (l2, r2, r1));
}

// ---------------------------------------------------------------------------
// merkle_syncer

merkle_syncer::merkle_syncer (merkle_tree *ltree, rpcfnc_t rpcfnc, missingfnc_t missingfnc)
  : ltree (ltree), rpcfnc (rpcfnc), missingfnc (missingfnc)
{
  fatal_err = NULL;
  sync_done = false;
}

void
merkle_syncer::sync (bigint rngmin, bigint rngmax)
{
  local_rngmin = rngmin;
  local_rngmax = rngmax;

  // start at the root of the merkle tree
  sendnode (0, 0);
}

void
merkle_syncer::dump ()
{
  warn << "THIS: " << (u_int)this << "\n";
  warn << "  st.size () " << st.size () << "\n";
}

void
merkle_syncer::doRPC (int procno, ptr<void> in, void *out, aclnt_cb cb)
{
  // Must resort to bundling all values into one argument since
  // async/callback.h is configured with too few parameters.
  struct RPC_delay_args args (merklesync_program_1, procno, in, out, cb,
			      NULL);
  (*rpcfnc) (&args);
}

void
merkle_syncer::setdone ()
{
  sync_done = true;
}

void
merkle_syncer::error (str err)
{
  warn << (u_int)this << ": SYNCER ERROR: " << err << "\n";
  fatal_err = err;
  setdone ();
}

str
merkle_syncer::getsummary ()
{
  assert (sync_done);
  strbuf sb;

  sb << "[" << local_rngmin << "," << local_rngmax << "] ";

  if (fatal_err)
    sb << fatal_err;

  if (0)
    sb  << "<log " << log << ">\n";

  return sb;
}

void
merkle_syncer::next (void)
{
#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "local range [" <<  local_rngmin << "," << local_rngmax << "]\n";
#endif
  assert (!sync_done);
  assert (!fatal_err);

  // st is queue of pending index nodes
  while (st.size ()) {
    pair<merkle_rpc_node, int> &p = st.back ();
    merkle_rpc_node *rnode = &p.first;
    assert (!rnode->isleaf);

    merkle_node *lnode = ltree->lookup_exact (rnode->depth, rnode->prefix);

    if (!lnode) {
      fatal << "lookup_exact didn't match for " << rnode->prefix << " at depth " << rnode->depth << "\n";
    }

#ifdef MERKLE_SYNC_DETAILED_TRACE
    warn << "starting from slot " << p.second << "\n";
#endif

    // XXX not clear p.second will ever enter > 0
    while (p.second < 64) {
      u_int i = p.second;
      p.second += 1;
#ifdef MERKLE_SYNC_DETAILED_TRACE
      warn << "CHECKING: " << i;
#endif

      bigint remote = tobigint (rnode->child_hash[i]);
      bigint local = tobigint (lnode->child (i)->hash);

      u_int depth = rnode->depth + 1;

      //prefix is the high bits of the first key
      // the node is responsible for.
      merkle_hash prefix = rnode->prefix;
      prefix.write_slot (rnode->depth, i);
      bigint slot_rngmin = tobigint (prefix);
      bigint slot_width = bigint (1) << (160 - 6*depth);
      bigint slot_rngmax = slot_rngmin + slot_width - 1;

      bool overlaps = overlap (local_rngmin, local_rngmax, slot_rngmin, slot_rngmax);

      if (remote != local) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	warnx << " differ. local " << local << " != remote " << remote;
#endif
	if (overlaps) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	  warnx << " .. sending\n";
#endif
	  sendnode (depth, prefix);
	  return;
	} else {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	  warnx << " .. not sending\n";
#endif
	}
      } else {
#ifdef MERKLE_SYNC_DETAILED_TRACE
	warnx << " same. local " << local << " == remote " << remote << "\n";
#endif
      }
    }

    assert (p.second == 64);
    st.pop_back ();
  }

#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "DONE .. in NEXT\n";
#endif
  setdone ();
#ifdef MERKLE_SYNC_DETAILED_TRACE
  warn << "OK!\n";
#endif
}

void
merkle_syncer::sendnode (u_int depth, const merkle_hash &prefix)
{

  ref<sendnode_arg> arg = New refcounted<sendnode_arg> ();
  ref<sendnode_res> res = New refcounted<sendnode_res> ();

  u_int lnode_depth;
  merkle_node *lnode = ltree->lookup (&lnode_depth, depth, prefix);
  assert (lnode);
  assert (lnode_depth == depth);

  format_rpcnode (ltree, depth, prefix, lnode, &arg->node);
  arg->rngmin = local_rngmin;
  arg->rngmax = local_rngmax;
  doRPC (MERKLESYNC_SENDNODE, arg, res,
	 wrap (this, &merkle_syncer::sendnode_cb, arg, res));
}

void
merkle_syncer::sendnode_cb (ref<sendnode_arg> arg, ref<sendnode_res> res,
			    clnt_stat err)
{

  if (err) {
    error (strbuf () << "SENDNODE: rpc error " << err);
    return;
  } else if (res->status != MERKLE_OK) {
    error (strbuf () << "SENDNODE: protocol error " << err2str (res->status));
    return;
  }

  merkle_rpc_node *rnode = &res->resok->node;

  merkle_node *lnode = ltree->lookup_exact (rnode->depth, rnode->prefix);
  if (!lnode) {
    fatal << "lookup failed: " << rnode->prefix << " at " << rnode->depth << "\n";
  }

  compare_nodes (ltree, local_rngmin, local_rngmax, lnode, rnode,
		 missingfnc, rpcfnc);

  if (!lnode->isleaf () && !rnode->isleaf) {
#ifdef MERKLE_SYNC_TRACE
    warn << "I vs I\n";
#endif
    st.push_back (pair<merkle_rpc_node, int> (*rnode, 0));
  }

  next ();
}

// ---------------------------------------------------------------------------
// merkle_getkeyrange

void
merkle_getkeyrange::go ()
{
  if (between (rngmax, incID (current), current)) {
#ifdef MERKLE_SYNC_TRACE
    warn << "merkle_getkeyrange::go () ==> DONE\n";
#endif
    delete this;
    return;
  }

  ref<getkeys_arg> arg = New refcounted<getkeys_arg> ();
  arg->rngmin = current;
  arg->rngmax = rngmax;
  ref<getkeys_res> res = New refcounted<getkeys_res> ();
  doRPC (MERKLESYNC_GETKEYS, arg, res,
	 wrap (this, &merkle_getkeyrange::getkeys_cb, arg, res));
}

void
merkle_getkeyrange::getkeys_cb (ref<getkeys_arg> arg, ref<getkeys_res> res,
				clnt_stat err)

{
  if (err) {
    warn << "GETKEYS: rpc error " << err << "\n";
    delete this;
    return;
  } else if (res->status != MERKLE_OK) {
    warn << "GETKEYS: protocol error " << err2str (res->status) << "\n";
    delete this;
    return;
  }

  vec<merkle_hash> rkeys;
  chordID round_max = current;
  chordID next_cur = current;
  for (u_int i = 0; i < res->resok->keys.size (); i++) {
    const merkle_hash &key = res->resok->keys[i];
    rkeys.push_back (key);

    bigint bkey = tobigint(key);
    if (round_max < bkey) round_max = bkey;
    if (betweenbothincl (next_cur, incID (bkey), bkey))
      next_cur = incID (bkey);
  }

  compare_keylists (lkeys, rkeys, current, round_max, missing);

  current = next_cur;

  if (!res->resok->morekeys)
    current = incID (rngmax);  // set done

  go ();
}

void
merkle_getkeyrange::doRPC (int procno, ptr<void> in, void *out, aclnt_cb cb)
{
  // Must resort to bundling all values into one argument since
  // async/callback.h is configured with too few parameters.
  struct RPC_delay_args args (merklesync_program_1, procno, in, out, cb,
			      NULL);
  (*rpcfnc) (&args);
}

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

void
compare_keylists (vec<merkle_hash> lkeys,
		  vec<merkle_hash> vrkeys,
		  chordID rngmin, chordID rngmax,
		  missingfnc_t missingfnc)
{
  // populate a hash table with the remote keys
  qhash<merkle_hash, int> rkeys;
  for (u_int i = 0; i < vrkeys.size (); i++) {
    if (betweenbothincl (rngmin, rngmax, tobigint(vrkeys[i])))
      rkeys.insert (vrkeys[i], 1);
  }

  // do I have something he doesn't have?
  for (unsigned int i = 0; i < lkeys.size (); i++) {
    if (!rkeys[lkeys[i]]) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
    warn << "remote missing [" << rngmin << ", " << rngmax << "] key=" << lkeys[i] << "\n";
#endif
      (*missingfnc) (tobigint(lkeys[i]), false);
    } else {
      rkeys.remove (lkeys[i]);
    }
  }

  //anything left: he has and I don't
  qhash_slot<merkle_hash, int> *slot = rkeys.first ();
  while (slot) {
#ifdef MERKLE_SYNC_DETAILED_TRACE
    warn << "local missing [" << rngmin << ", " << rngmax << "] key=" << slot->key << "\n";
#endif
    (*missingfnc) (tobigint(slot->key), true);
    slot = rkeys.next (slot);
  }

}

void
compare_nodes (merkle_tree *ltree, bigint rngmin, bigint rngmax,
	       merkle_node *lnode, merkle_rpc_node *rnode,
	       missingfnc_t missingfnc, rpcfnc_t rpcfnc)
{
#ifdef MERKLE_SYNC_TRACE
  warn << (lnode->isleaf ()  ? "L" : "I")
       << " vs "
       << (rnode->isleaf ? "L" : "I")
       << "\n";
#endif

  if (rnode->isleaf) {
    vec<merkle_hash> lkeys = database_get_keys (ltree->db, rnode->depth, rnode->prefix);

    vec<merkle_hash> rkeys;
    for (u_int i = 0; i < rnode->child_hash.size (); i++) {
      if (betweenbothincl (rngmin, rngmax, tobigint (rnode->child_hash[i])))
	rkeys.push_back (rnode->child_hash[i]);
    }

    compare_keylists (lkeys, rkeys, rngmin, rngmax, missingfnc);

  } else if (lnode->isleaf () && !rnode->isleaf) {
    bigint tmpmin = tobigint (rnode->prefix);
    bigint node_width = bigint (1) << (160 - rnode->depth);
    bigint tmpmax = tmpmin + node_width - 1;

    // further constrain to be within the host's range of interest
    if (between (tmpmin, tmpmax, rngmin))
      tmpmin = rngmin;
    if (between (tmpmin, tmpmax, rngmax))
      tmpmax = rngmax;

    vec<merkle_hash> lkeys = database_get_keys (ltree->db, rnode->depth, rnode->prefix);
    vNew merkle_getkeyrange (ltree->db, tmpmin, tmpmax, lkeys, missingfnc, rpcfnc);
  }
}

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

void
format_rpcnode (merkle_tree *ltree, u_int depth, const merkle_hash &prefix,
		const merkle_node *node, merkle_rpc_node *rpcnode)
{
  rpcnode->depth = depth;
  rpcnode->prefix = prefix;
  rpcnode->count = node->count;
  rpcnode->hash = node->hash;
  rpcnode->isleaf = node->isleaf ();

  if (!node->isleaf ()) {
    rpcnode->child_hash.setsize (64);
    for (int i = 0; i < 64; i++)
      rpcnode->child_hash[i] = node->child (i)->hash;
  } else {
    vec<merkle_hash> keys = database_get_keys (ltree->db, depth, prefix);

    if (keys.size () != rpcnode->count) {
      warn << "\n\n\n----------------------------------------------------------\n";
      warn << "BUG BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG BUG\n";
      warn << "Send this output to chord@pdos.lcs.mit.edu\n";
      warn << "BUG: " << depth << " " << prefix << "\n";
      warn << "BUG: " << keys.size () << " != " << rpcnode->count << "\n";
      ltree->check_invariants ();
      warn << "BUG BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG  BUG BUG\n";
      panic << "----------------------------------------------------------\n\n\n";
    }

    rpcnode->child_hash.setsize (keys.size ());
    for (u_int i = 0; i < keys.size (); i++) {
      rpcnode->child_hash[i] = keys[i];
    }
  }
}
<|endoftext|>


<commit_before>#include "catch.hpp"

#include "mockPlatform.h"
#include "scene/importer.h"

#include "yaml-cpp/yaml.h"

#include <iostream>
#include <vector>

using namespace Tangram;
using namespace YAML;

std::shared_ptr<MockPlatform> getPlatformWithImportFiles() {

    auto platform = std::make_shared<MockPlatform>();

    platform->putMockUrlContents("/root/a.yaml", R"END(
        import: b.yaml
        value: a
        has_a: true
    )END");

    platform->putMockUrlContents("/root/b.yaml", R"END(
        value: b
        has_b: true
    )END");

    platform->putMockUrlContents("/root/c.yaml", R"END(
        import: [a.yaml, b.yaml]
        value: c
        has_c: true
    )END");

    platform->putMockUrlContents("/root/cycle_simple.yaml", R"END(
        import: cycle_simple.yaml
        value: cyclic
    )END");

    platform->putMockUrlContents("/root/cycle_tricky.yaml", R"END(
        import: imports/cycle_tricky.yaml
        has_cycle_tricky: true
    )END");

    platform->putMockUrlContents("/root/imports/cycle_tricky.yaml", R"END(
        import: ../cycle_tricky.yaml
        has_imports_cycle_tricky: true
    )END");

    platform->putMockUrlContents("/root/urls.yaml", R"END(
        import: imports/urls.yaml
        fonts: { fontA: { url: https://host/font.woff } }
        sources: { sourceA: { url: 'https://host/tiles/{z}/{y}/{x}.mvt' } }
        textures:
            tex1: { url: "path/to/texture.png" }
            tex2: { url: "../up_a_directory.png" }
        styles:
            styleA:
                texture: "path/to/texture.png"
                shaders:
                    uniforms:
                        u_tex1: "/at_root.png"
                        u_tex2: ["path/to/texture.png", tex2]
                        u_tex3: tex3
                        u_bool: true
                        u_float: 0.25
    )END");

    platform->putMockUrlContents("/root/imports/urls.yaml", R"END(
        fonts: { fontB: [ { url: fonts/0.ttf }, { url: fonts/1.ttf } ] }
        sources: { sourceB: { url: "tiles/{z}/{y}/{x}.mvt" } }
        textures:
            tex3: { url: "in_imports.png" }
            tex4: { url: "../not_in_imports.png" }
            tex5: { url: "/at_root.png" }
        styles:
            styleB:
                texture: "in_imports.png"
                shaders:
                    uniforms:
                        u_tex1: "in_imports.png"
                        u_tex2: tex2
    )END");

    platform->putMockUrlContents("/root/globals.yaml", R"END(
        fonts: { aFont: { url: global.fontUrl } }
        sources: { aSource: { url: global.sourceUrl } }
        textures: { aTexture: { url: global.textureUrl } }
        styles: { aStyle: { texture: global.textureUrl, shaders: { uniforms: { aUniform: global.textureUrl } } } }
    )END");

    return platform;
}

TEST_CASE("Imported scenes are merged with the parent scene", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/root/a.yaml"));

    CHECK(root["value"].Scalar() == "a");
    CHECK(root["has_a"].Scalar() == "true");
    CHECK(root["has_b"].Scalar() == "true");
}

TEST_CASE("Nested imports are merged recursively", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/root/c.yaml"));

    CHECK(root["value"].Scalar() == "c");
    CHECK(root["has_a"].Scalar() == "true");
    CHECK(root["has_b"].Scalar() == "true");
    CHECK(root["has_c"].Scalar() == "true");
}

TEST_CASE("Imports that would start a cycle are ignored", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;

    // If import cycles aren't checked for and stopped, this call won't return.
    auto root = importer.applySceneImports(platform, Url("/root/cycle_simple.yaml"));

    // Check that the scene values were applied.
    CHECK(root["value"].Scalar() == "cyclic");
}

TEST_CASE("Tricky import cycles are ignored", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;

    // The nested import should resolve to the same path as the original file,
    // and so the importer should break the cycle.
    auto root = importer.applySceneImports(platform, Url("/root/cycle_tricky.yaml"));

    // Check that the imported scene values were merged.
    CHECK(root["has_cycle_tricky"].Scalar() == "true");
    CHECK(root["has_imports_cycle_tricky"].Scalar() == "true");
}

TEST_CASE("Scene URLs are resolved against their parent during import", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/root/urls.yaml"));

    // Check that global texture URLs are resolved correctly.

    auto textures = root["textures"];

    CHECK(textures["tex1"]["url"].Scalar() == "/root/path/to/texture.png");
    CHECK(textures["tex2"]["url"].Scalar() == "/up_a_directory.png");
    CHECK(textures["tex3"]["url"].Scalar() == "/root/imports/in_imports.png");
    CHECK(textures["tex4"]["url"].Scalar() == "/root/not_in_imports.png");
    CHECK(textures["tex5"]["url"].Scalar() == "/at_root.png");

    // Check that "inline" texture URLs are resolved correctly.

    auto styleA = root["styles"]["styleA"];

    CHECK(styleA["texture"].Scalar() == "/root/path/to/texture.png");

    auto uniformsA = styleA["shaders"]["uniforms"];

    CHECK(uniformsA["u_tex1"].Scalar() == "/at_root.png");
    CHECK(uniformsA["u_tex2"][0].Scalar() == "/root/path/to/texture.png");
    CHECK(uniformsA["u_tex2"][1].Scalar() == "tex2");
    CHECK(uniformsA["u_bool"].Scalar() == "true");
    CHECK(uniformsA["u_float"].Scalar() == "0.25");
    CHECK(uniformsA["u_tex3"].Scalar() == "tex3");

    auto styleB = root["styles"]["styleB"];

    CHECK(styleB["texture"].Scalar() == "/root/imports/in_imports.png");

    auto uniformsB = styleB["shaders"]["uniforms"];

    CHECK(uniformsB["u_tex1"].Scalar() == "/root/imports/in_imports.png");
    // Don't use global textures from importing scene
    CHECK(uniformsB["u_tex2"].Scalar() == "/root/imports/tex2");

    // Check that data source URLs are resolved correctly.

    CHECK(root["sources"]["sourceA"]["url"].Scalar() == "https://host/tiles/{z}/{y}/{x}.mvt");
    CHECK(root["sources"]["sourceB"]["url"].Scalar() == "/root/imports/tiles/{z}/{y}/{x}.mvt");

    // Check that font URLs are resolved correctly.

    CHECK(root["fonts"]["fontA"]["url"].Scalar() == "https://host/font.woff");
    CHECK(root["fonts"]["fontB"][0]["url"].Scalar() == "/root/imports/fonts/0.ttf");
    CHECK(root["fonts"]["fontB"][1]["url"].Scalar() == "/root/imports/fonts/1.ttf");

    // We don't explicitly check that import URLs are resolved correctly because if they were not,
    // the scenes wouldn't be loaded and merged; i.e. we already test it implicitly.
}

TEST_CASE("References to globals are not treated like URLs during importing", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/root/globals.yaml"));

    // Check that font global references are preserved.
    CHECK(root["fonts"]["aFont"]["url"].Scalar() == "global.fontUrl");

    // Check that data source global references are preserved.
    CHECK(root["sources"]["aSource"]["url"].Scalar() == "global.sourceUrl");

    // Check that texture global references are preserved.
    CHECK(root["textures"]["aTexture"]["url"].Scalar() == "global.textureUrl");
    CHECK(root["styles"]["aStyle"]["texture"].Scalar() == "global.textureUrl");
    CHECK(root["styles"]["aStyle"]["shaders"]["uniforms"]["aUniform"].Scalar() == "global.textureUrl");
}

TEST_CASE("Map overwrites sequence", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = getPlatformWithImportFiles();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [roads.yaml, roads-labels.yaml]
    )END");

    platform->putMockUrlContents("/roads.yaml", R"END(
            filter:
                - kind: highway
                - $zoom: { min: 8 }
    )END");

    platform->putMockUrlContents("/roads-labels.yaml", R"END(
                filter: { kind: highway }
    )END");

    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/base.yaml"));

    CHECK(root["filter"].IsMap());
    CHECK(root["filter"].size() == 1);
    CHECK(root["filter"]["kind"].Scalar() == "highway");
}

TEST_CASE("Sequence overwrites map", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = std::make_shared<MockPlatform>();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [map.yaml, sequence.yaml]
    )END");
    platform->putMockUrlContents("/map.yaml", R"END(
            a: { b: c }
    )END");

    platform->putMockUrlContents("/sequence.yaml", R"END(
            a: [ b, c]
    )END");

    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/base.yaml"));

    CHECK(root["a"].IsSequence());
    CHECK(root["a"].size() == 2);
}

TEST_CASE("Scalar and null overwrite correctly", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = std::make_shared<MockPlatform>();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [scalar.yaml, null.yaml]
        scalar_at_end: scalar
        null_at_end: null
    )END");
    platform->putMockUrlContents("/scalar.yaml", R"END(
            null_at_end: scalar
    )END");

    platform->putMockUrlContents("/null.yaml", R"END(
            scalar_at_end: null
    )END");

    Importer importer;
    auto root = importer.applySceneImports(platform, Url("/base.yaml"));

    CHECK(root["scalar_at_end"].Scalar() == "scalar");
    CHECK(root["null_at_end"].IsNull());
}

TEST_CASE("Scene load from source string", "[import][core]") {
    std::shared_ptr<Platform> platform = std::make_shared<MockPlatform>();
    std::unordered_map<Url, std::string> testScenes;
    platform->putMockUrlContents("/resource_root/scalar.yaml", R"END(
            null_at_end: scalar
    )END");
    platform->putMockUrlContents("/resource_root/null.yaml", R"END(
            scalar_at_end: null
    )END");

    std::string base_yaml = R"END(
        import: [scalar.yaml, null.yaml]
        scalar_at_end: scalar
        null_at_end: null
    )END";

    Importer importer;

    auto scene = std::make_shared<Scene>(platform, base_yaml, "/resource_root/");
    auto root = importer.applySceneImports(platform, scene);

    for (auto& s : importer.scenes()) {
        logMsg(":> %s\n", s.first.string().c_str());
    }

    CHECK(root["scalar_at_end"].Scalar() == "scalar");
    CHECK(root["null_at_end"].IsNull());
}
<commit_msg>Fix compilation errors in sceneImportTests<commit_after>#include "catch.hpp"

#include "mockPlatform.h"
#include "scene/importer.h"

#include "yaml-cpp/yaml.h"

#include <iostream>
#include <vector>

using namespace Tangram;
using namespace YAML;

std::shared_ptr<MockPlatform> getPlatformWithImportFiles() {

    auto platform = std::make_shared<MockPlatform>();

    platform->putMockUrlContents("/root/a.yaml", R"END(
        import: b.yaml
        value: a
        has_a: true
    )END");

    platform->putMockUrlContents("/root/b.yaml", R"END(
        value: b
        has_b: true
    )END");

    platform->putMockUrlContents("/root/c.yaml", R"END(
        import: [a.yaml, b.yaml]
        value: c
        has_c: true
    )END");

    platform->putMockUrlContents("/root/cycle_simple.yaml", R"END(
        import: cycle_simple.yaml
        value: cyclic
    )END");

    platform->putMockUrlContents("/root/cycle_tricky.yaml", R"END(
        import: imports/cycle_tricky.yaml
        has_cycle_tricky: true
    )END");

    platform->putMockUrlContents("/root/imports/cycle_tricky.yaml", R"END(
        import: ../cycle_tricky.yaml
        has_imports_cycle_tricky: true
    )END");

    platform->putMockUrlContents("/root/urls.yaml", R"END(
        import: imports/urls.yaml
        fonts: { fontA: { url: https://host/font.woff } }
        sources: { sourceA: { url: 'https://host/tiles/{z}/{y}/{x}.mvt' } }
        textures:
            tex1: { url: "path/to/texture.png" }
            tex2: { url: "../up_a_directory.png" }
        styles:
            styleA:
                texture: "path/to/texture.png"
                shaders:
                    uniforms:
                        u_tex1: "/at_root.png"
                        u_tex2: ["path/to/texture.png", tex2]
                        u_tex3: tex3
                        u_bool: true
                        u_float: 0.25
    )END");

    platform->putMockUrlContents("/root/imports/urls.yaml", R"END(
        fonts: { fontB: [ { url: fonts/0.ttf }, { url: fonts/1.ttf } ] }
        sources: { sourceB: { url: "tiles/{z}/{y}/{x}.mvt" } }
        textures:
            tex3: { url: "in_imports.png" }
            tex4: { url: "../not_in_imports.png" }
            tex5: { url: "/at_root.png" }
        styles:
            styleB:
                texture: "in_imports.png"
                shaders:
                    uniforms:
                        u_tex1: "in_imports.png"
                        u_tex2: tex2
    )END");

    platform->putMockUrlContents("/root/globals.yaml", R"END(
        fonts: { aFont: { url: global.fontUrl } }
        sources: { aSource: { url: global.sourceUrl } }
        textures: { aTexture: { url: global.textureUrl } }
        styles: { aStyle: { texture: global.textureUrl, shaders: { uniforms: { aUniform: global.textureUrl } } } }
    )END");

    return platform;
}

TEST_CASE("Imported scenes are merged with the parent scene", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/a.yaml")));
    auto root = importer.applySceneImports(platform);

    CHECK(root["value"].Scalar() == "a");
    CHECK(root["has_a"].Scalar() == "true");
    CHECK(root["has_b"].Scalar() == "true");
}

TEST_CASE("Nested imports are merged recursively", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/c.yaml")));
    auto root = importer.applySceneImports(platform);

    CHECK(root["value"].Scalar() == "c");
    CHECK(root["has_a"].Scalar() == "true");
    CHECK(root["has_b"].Scalar() == "true");
    CHECK(root["has_c"].Scalar() == "true");
}

TEST_CASE("Imports that would start a cycle are ignored", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/cycle_simple.yaml")));

    // If import cycles aren't checked for and stopped, this call won't return.
    auto root = importer.applySceneImports(platform);

    // Check that the scene values were applied.
    CHECK(root["value"].Scalar() == "cyclic");
}

TEST_CASE("Tricky import cycles are ignored", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/cycle_tricky.yaml")));

    // The nested import should resolve to the same path as the original file,
    // and so the importer should break the cycle.
    auto root = importer.applySceneImports(platform);

    // Check that the imported scene values were merged.
    CHECK(root["has_cycle_tricky"].Scalar() == "true");
    CHECK(root["has_imports_cycle_tricky"].Scalar() == "true");
}

TEST_CASE("Scene URLs are resolved against their parent during import", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/urls.yaml")));
    auto root = importer.applySceneImports(platform);

    // Check that global texture URLs are resolved correctly.

    auto textures = root["textures"];

    CHECK(textures["tex1"]["url"].Scalar() == "/root/path/to/texture.png");
    CHECK(textures["tex2"]["url"].Scalar() == "/up_a_directory.png");
    CHECK(textures["tex3"]["url"].Scalar() == "/root/imports/in_imports.png");
    CHECK(textures["tex4"]["url"].Scalar() == "/root/not_in_imports.png");
    CHECK(textures["tex5"]["url"].Scalar() == "/at_root.png");

    // Check that "inline" texture URLs are resolved correctly.

    auto styleA = root["styles"]["styleA"];

    CHECK(styleA["texture"].Scalar() == "/root/path/to/texture.png");

    auto uniformsA = styleA["shaders"]["uniforms"];

    CHECK(uniformsA["u_tex1"].Scalar() == "/at_root.png");
    CHECK(uniformsA["u_tex2"][0].Scalar() == "/root/path/to/texture.png");
    CHECK(uniformsA["u_tex2"][1].Scalar() == "tex2");
    CHECK(uniformsA["u_bool"].Scalar() == "true");
    CHECK(uniformsA["u_float"].Scalar() == "0.25");
    CHECK(uniformsA["u_tex3"].Scalar() == "tex3");

    auto styleB = root["styles"]["styleB"];

    CHECK(styleB["texture"].Scalar() == "/root/imports/in_imports.png");

    auto uniformsB = styleB["shaders"]["uniforms"];

    CHECK(uniformsB["u_tex1"].Scalar() == "/root/imports/in_imports.png");
    // Don't use global textures from importing scene
    CHECK(uniformsB["u_tex2"].Scalar() == "/root/imports/tex2");

    // Check that data source URLs are resolved correctly.

    CHECK(root["sources"]["sourceA"]["url"].Scalar() == "https://host/tiles/{z}/{y}/{x}.mvt");
    CHECK(root["sources"]["sourceB"]["url"].Scalar() == "/root/imports/tiles/{z}/{y}/{x}.mvt");

    // Check that font URLs are resolved correctly.

    CHECK(root["fonts"]["fontA"]["url"].Scalar() == "https://host/font.woff");
    CHECK(root["fonts"]["fontB"][0]["url"].Scalar() == "/root/imports/fonts/0.ttf");
    CHECK(root["fonts"]["fontB"][1]["url"].Scalar() == "/root/imports/fonts/1.ttf");

    // We don't explicitly check that import URLs are resolved correctly because if they were not,
    // the scenes wouldn't be loaded and merged; i.e. we already test it implicitly.
}

TEST_CASE("References to globals are not treated like URLs during importing", "[import][core]") {
    std::shared_ptr<Platform> platform = getPlatformWithImportFiles();
    Importer importer(std::make_shared<Scene>(platform, Url("/root/globals.yaml")));
    auto root = importer.applySceneImports(platform);

    // Check that font global references are preserved.
    CHECK(root["fonts"]["aFont"]["url"].Scalar() == "global.fontUrl");

    // Check that data source global references are preserved.
    CHECK(root["sources"]["aSource"]["url"].Scalar() == "global.sourceUrl");

    // Check that texture global references are preserved.
    CHECK(root["textures"]["aTexture"]["url"].Scalar() == "global.textureUrl");
    CHECK(root["styles"]["aStyle"]["texture"].Scalar() == "global.textureUrl");
    CHECK(root["styles"]["aStyle"]["shaders"]["uniforms"]["aUniform"].Scalar() == "global.textureUrl");
}

TEST_CASE("Map overwrites sequence", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = getPlatformWithImportFiles();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [roads.yaml, roads-labels.yaml]
    )END");

    platform->putMockUrlContents("/roads.yaml", R"END(
            filter:
                - kind: highway
                - $zoom: { min: 8 }
    )END");

    platform->putMockUrlContents("/roads-labels.yaml", R"END(
                filter: { kind: highway }
    )END");

    Importer importer(std::make_shared<Scene>(platform, Url("/base.yaml")));
    auto root = importer.applySceneImports(platform);

    CHECK(root["filter"].IsMap());
    CHECK(root["filter"].size() == 1);
    CHECK(root["filter"]["kind"].Scalar() == "highway");
}

TEST_CASE("Sequence overwrites map", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = std::make_shared<MockPlatform>();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [map.yaml, sequence.yaml]
    )END");
    platform->putMockUrlContents("/map.yaml", R"END(
            a: { b: c }
    )END");

    platform->putMockUrlContents("/sequence.yaml", R"END(
            a: [ b, c]
    )END");

    Importer importer(std::make_shared<Scene>(platform, Url("/base.yaml")));
    auto root = importer.applySceneImports(platform);

    CHECK(root["a"].IsSequence());
    CHECK(root["a"].size() == 2);
}

TEST_CASE("Scalar and null overwrite correctly", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = std::make_shared<MockPlatform>();
    platform->putMockUrlContents("/base.yaml", R"END(
        import: [scalar.yaml, null.yaml]
        scalar_at_end: scalar
        null_at_end: null
    )END");
    platform->putMockUrlContents("/scalar.yaml", R"END(
            null_at_end: scalar
    )END");

    platform->putMockUrlContents("/null.yaml", R"END(
            scalar_at_end: null
    )END");

    Importer importer(std::make_shared<Scene>(platform, Url("/base.yaml")));
    auto root = importer.applySceneImports(platform);

    CHECK(root["scalar_at_end"].Scalar() == "scalar");
    CHECK(root["null_at_end"].IsNull());
}

TEST_CASE("Scene load from source string", "[import][core]") {
    std::shared_ptr<MockPlatform> platform = std::make_shared<MockPlatform>();
    std::unordered_map<Url, std::string> testScenes;
    platform->putMockUrlContents("/resource_root/scalar.yaml", R"END(
            null_at_end: scalar
    )END");
    platform->putMockUrlContents("/resource_root/null.yaml", R"END(
            scalar_at_end: null
    )END");

    std::string base_yaml = R"END(
        import: [scalar.yaml, null.yaml]
        scalar_at_end: scalar
        null_at_end: null
    )END";

    auto scene = std::make_shared<Scene>(platform, base_yaml, "/resource_root/");

    Importer importer(scene);

    auto root = importer.applySceneImports(platform);

    CHECK(root["scalar_at_end"].Scalar() == "scalar");
    CHECK(root["null_at_end"].IsNull());
}
<|endoftext|>