<commit_before>// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_UTILS_CXX_VARIANT_QUEUE_INL
#define IOX_UTILS_CXX_VARIANT_QUEUE_INL

#include "iceoryx_utils/error_handling/error_handling.hpp"

namespace iox
{
namespace cxx
{
template <typename ValueType, uint64_t Capacity>
inline VariantQueue<ValueType, Capacity>::VariantQueue(const VariantQueueTypes type) noexcept
    : m_type(type)
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::FiFo<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::SoFi<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
        IOX_FALLTHROUGH;
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::ResizeableLockFreeQueue<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::TriggerQueue<ValueType, Capacity, concurrent::FiFo>>();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::TriggerQueue<ValueType, Capacity, concurrent::ResizeableLockFreeQueue>>();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }
}

template <typename ValueType, uint64_t Capacity>
optional<ValueType> VariantQueue<ValueType, Capacity>::push(const ValueType& value) noexcept
{
    optional<ValueType> ret = cxx::nullopt_t();

    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->push(value);

        if (!hadSpace)
        {
            ValueType droppedValue = value;
            ret = cxx::make_optional<ValueType>(std::move(droppedValue));
        }
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ValueType overriddenValue;
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->push(value, overriddenValue);

        if (!hadSpace)
        {
            ret = cxx::make_optional<ValueType>(std::move(overriddenValue));
        }
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    {
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->tryPush(value);

        if (!hadSpace)
        {
            ValueType droppedValue = value;
            ret = cxx::make_optional<ValueType>(std::move(droppedValue));
        }
        break;
    }
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        auto overriddenValue =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->push(value);
        if (overriddenValue)
        {
            ret = cxx::make_optional<ValueType>(std::move(overriddenValue.value()));
        }
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        auto fifoShouldBeDestroyed = m_fifo
                                         .template get_at_index<static_cast<uint64_t>(
                                             VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                                         ->push(value);

        if (!fifoShouldBeDestroyed)
        {
            ValueType droppedValue = value;
            ret = cxx::make_optional<ValueType>(std::move(droppedValue));
        }
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        auto fifoShouldBeDestroyed = m_fifo
                                         .template get_at_index<static_cast<uint64_t>(
                                             VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                                         ->push(value);

        if (!fifoShouldBeDestroyed)
        {
            ValueType droppedValue = value;
            ret = cxx::make_optional<ValueType>(std::move(droppedValue));
        }
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return ret;
}

template <typename ValueType, uint64_t Capacity>
inline optional<ValueType> VariantQueue<ValueType, Capacity>::pop() noexcept
{
    optional<ValueType> ret = nullopt_t();

    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->pop();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ValueType returnType;
        auto hasReturnType =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->pop(returnType);
        if (hasReturnType)
        {
            ret = returnType;
        }
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        auto maybeReturnType =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->pop();
        if (maybeReturnType)
        {
            ret = maybeReturnType.value();
        }
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                  ->pop();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                  ->pop();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return ret;
}

template <typename ValueType, uint64_t Capacity>
inline bool VariantQueue<ValueType, Capacity>::empty() const noexcept
{
    bool ret = true;

    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->empty();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->empty();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->empty();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                  ->empty();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                  ->empty();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return ret;
}

template <typename ValueType, uint64_t Capacity>
inline uint64_t VariantQueue<ValueType, Capacity>::size() noexcept
{
    uint64_t ret = 0;

    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->size();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->size();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->size();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                  ->size();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                  ->size();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return ret;
}

template <typename ValueType, uint64_t Capacity>
inline bool VariantQueue<ValueType, Capacity>::setCapacity(const uint64_t newCapacity) noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        /// @todo must be implemented for FiFo
        assert(false);
        return false;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
        return true;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        // we may discard elements in the queue if the size is reduced and the fifo contains too many elements
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        /// @todo must be implemented for FiFo
        assert(false);
        return false;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        // we may discard elements in the queue if the size is reduced and the fifo contains too many elements
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }
    return false;
}

template <typename ValueType, uint64_t Capacity>
inline uint64_t VariantQueue<ValueType, Capacity>::capacity() const noexcept
{
    uint64_t ret = 0;

    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->capacity();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->capacity();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        ret =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->capacity();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                  ->capacity();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        ret = m_fifo
                  .template get_at_index<static_cast<uint64_t>(
                      VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                  ->capacity();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return ret;
}

template <typename ValueType, uint64_t Capacity>
inline typename VariantQueue<ValueType, Capacity>::fifo_t&
VariantQueue<ValueType, Capacity>::getUnderlyingFiFo() noexcept
{
    return m_fifo;
}

} // namespace cxx
} // namespace iox

#endif // IOX_UTILS_CXX_VARIANT_QUEUE_INL
<commit_msg>iox-#615 refactored and cleaned up variant queue<commit_after>// Copyright (c) 2020 by Robert Bosch GmbH, Apex.AI Inc. All rights reserved.
//
// 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.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_UTILS_CXX_VARIANT_QUEUE_INL
#define IOX_UTILS_CXX_VARIANT_QUEUE_INL

#include "iceoryx_utils/error_handling/error_handling.hpp"

namespace iox
{
namespace cxx
{
template <typename ValueType, uint64_t Capacity>
inline VariantQueue<ValueType, Capacity>::VariantQueue(const VariantQueueTypes type) noexcept
    : m_type(type)
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::FiFo<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::SoFi<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
        IOX_FALLTHROUGH;
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::ResizeableLockFreeQueue<ValueType, Capacity>>();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::TriggerQueue<ValueType, Capacity, concurrent::FiFo>>();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        m_fifo.template emplace<concurrent::TriggerQueue<ValueType, Capacity, concurrent::ResizeableLockFreeQueue>>();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }
}

template <typename ValueType, uint64_t Capacity>
optional<ValueType> VariantQueue<ValueType, Capacity>::push(const ValueType& value) noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
                ->push(value);

        return (hadSpace) ? cxx::nullopt : cxx::make_optional<ValueType>(value);
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ValueType overriddenValue;
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->push(value, overriddenValue);

        return (hadSpace) ? cxx::nullopt : cxx::make_optional<ValueType>(value);
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    {
        auto hadSpace =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
                ->tryPush(value);

        return (hadSpace) ? cxx::nullopt : cxx::make_optional<ValueType>(value);
    }
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->push(value);
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        auto fifoShouldBeDestroyed = m_fifo
                                         .template get_at_index<static_cast<uint64_t>(
                                             VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
                                         ->push(value);

        return (fifoShouldBeDestroyed) ? cxx::nullopt : cxx::make_optional<ValueType>(value);
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        auto fifoShouldBeDestroyed = m_fifo
                                         .template get_at_index<static_cast<uint64_t>(
                                             VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
                                         ->push(value);

        return (fifoShouldBeDestroyed) ? cxx::nullopt : cxx::make_optional<ValueType>(value);
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return cxx::nullopt;
}

template <typename ValueType, uint64_t Capacity>
inline optional<ValueType> VariantQueue<ValueType, Capacity>::pop() noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
            ->pop();
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        ValueType returnType;
        auto hasReturnType =
            m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
                ->pop(returnType);

        return (hasReturnType) ? make_optional<ValueType>(returnType) : cxx::nullopt;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->pop();
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(
                VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
            ->pop();
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->pop();
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return cxx::nullopt;
}

template <typename ValueType, uint64_t Capacity>
inline bool VariantQueue<ValueType, Capacity>::empty() const noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
            ->empty();
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
            ->empty();
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->empty();
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(
                VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
            ->empty();
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->empty();
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return true;
}

template <typename ValueType, uint64_t Capacity>
inline uint64_t VariantQueue<ValueType, Capacity>::size() noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
            ->size();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
            ->size();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->size();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(
                VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
            ->size();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->size();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return 0U;
}

template <typename ValueType, uint64_t Capacity>
inline bool VariantQueue<ValueType, Capacity>::setCapacity(const uint64_t newCapacity) noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        /// @todo must be implemented for FiFo
        assert(false);
        return false;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        m_fifo.template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
        return true;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        // we may discard elements in the queue if the size is reduced and the fifo contains too many elements
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        /// @todo must be implemented for FiFo
        assert(false);
        return false;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        // we may discard elements in the queue if the size is reduced and the fifo contains too many elements
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->setCapacity(newCapacity);
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }
    return false;
}

template <typename ValueType, uint64_t Capacity>
inline uint64_t VariantQueue<ValueType, Capacity>::capacity() const noexcept
{
    switch (m_type)
    {
    case VariantQueueTypes::FiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_SingleProducerSingleConsumer)>()
            ->capacity();
        break;
    }
    case VariantQueueTypes::SoFi_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::SoFi_SingleProducerSingleConsumer)>()
            ->capacity();
        break;
    }
    case VariantQueueTypes::FiFo_MultiProducerSingleConsumer:
    case VariantQueueTypes::SoFi_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::FiFo_MultiProducerSingleConsumer)>()
            ->capacity();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(
                VariantQueueTypes::BlockingFiFo_SingleProducerSingleConsumer)>()
            ->capacity();
        break;
    }
    case VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer:
    {
        return m_fifo
            .template get_at_index<static_cast<uint64_t>(VariantQueueTypes::BlockingFiFo_MultiProducerSingleConsumer)>()
            ->capacity();
        break;
    }
    default:
    {
        errorHandler(Error::kVARIANT_QUEUE__UNSUPPORTED_QUEUE_TYPE);
    }
    }

    return 0U;
}

template <typename ValueType, uint64_t Capacity>
inline typename VariantQueue<ValueType, Capacity>::fifo_t&
VariantQueue<ValueType, Capacity>::getUnderlyingFiFo() noexcept
{
    return m_fifo;
}

} // namespace cxx
} // namespace iox

#endif // IOX_UTILS_CXX_VARIANT_QUEUE_INL
<|endoftext|>


<commit_before>#include <osg/Image>
#include <osg/Geometry>
#include <osg/Texture2D>

#include <osgGA/TrackballManipulator>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <iostream>
#include <osg/io_utils>

extern "C" {
#include <rfb/rfbclient.h>
}

static rfbBool resizeImage(rfbClient* client)
{
    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));

    int width=client->width;
    int height=client->height;
    int depth=client->format.bitsPerPixel;

    std::cout<<"resize "<<width<<", "<<height<<", "<<depth<<" image = "<<image<<std::endl;

    image->allocateImage(width,height,1,GL_RGBA,GL_UNSIGNED_BYTE);

    client->frameBuffer= (uint8_t*)(image->data());

    return TRUE;
}

static void updateImage(rfbClient* client,int x,int y,int w,int h)
{
    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
    image->dirty();
}

class RfbEventHandler : public osgGA::GUIEventHandler
{
public:

    RfbEventHandler(rfbClient* client):
        _client(client) {}

    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv);

    rfbKeySym key2rfbKeySym(int key)
    {
        return rfbKeySym(key);
    }

protected:

    virtual ~RfbEventHandler() {}

    bool mousePosition(osgViewer::View* view, osg::NodeVisitor* nv, const osgGA::GUIEventAdapter& ea, int& x, int &y) const;

    rfbClient*  _client;

};

bool RfbEventHandler::mousePosition(osgViewer::View* view, osg::NodeVisitor* nv, const osgGA::GUIEventAdapter& ea, int& x, int &y) const
{
    osgUtil::LineSegmentIntersector::Intersections intersections;
    bool foundIntersection = view==0 ? false :
        (nv==0 ? view->computeIntersections(ea.getX(), ea.getY(), intersections) :
                 view->computeIntersections(ea.getX(), ea.getY(), nv->getNodePath(), intersections));

    if (foundIntersection)
    {

        osg::Vec2 tc(0.5f,0.5f);

        // use the nearest intersection
        const osgUtil::LineSegmentIntersector::Intersection& intersection = *(intersections.begin());
        osg::Drawable* drawable = intersection.drawable.get();
        osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
        osg::Vec3Array* vertices = geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
        if (vertices)
        {
            // get the vertex indices.
            const osgUtil::LineSegmentIntersector::Intersection::IndexList& indices = intersection.indexList;
            const osgUtil::LineSegmentIntersector::Intersection::RatioList& ratios = intersection.ratioList;

            if (indices.size()==3 && ratios.size()==3)
            {
                unsigned int i1 = indices[0];
                unsigned int i2 = indices[1];
                unsigned int i3 = indices[2];

                float r1 = ratios[0];
                float r2 = ratios[1];
                float r3 = ratios[2];

                osg::Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
                osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
                if (texcoords_Vec2Array)
                {
                    // we have tex coord array so now we can compute the final tex coord at the point of intersection.
                    osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
                    osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
                    osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
                    tc = tc1*r1 + tc2*r2 + tc3*r3;
                }
            }

        }

        x = int( float(_client->width) * tc.x() );
        y = int( float(_client->height) * tc.y() );

        return true;
    }

    return false;
}

bool RfbEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv)
{
    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::MOVE):
        case(osgGA::GUIEventAdapter::DRAG):
        case(osgGA::GUIEventAdapter::PUSH):
        case(osgGA::GUIEventAdapter::RELEASE):
        {
            osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
            int x,y;
            if (mousePosition(view, nv, ea, x, y))
            {
                SendPointerEvent(_client,x,y, ea.getButtonMask());
                return true;
            }
            break;
        }
        case(osgGA::GUIEventAdapter::KEYDOWN):
        case(osgGA::GUIEventAdapter::KEYUP):
        {
            osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
            int x,y;
            bool sendKeyEvent = mousePosition(view, nv, ea, x, y);

            if (sendKeyEvent)
            {
                SendKeyEvent(_client,
                    key2rfbKeySym(ea.getKey()),
                    (ea.getEventType()==osgGA::GUIEventAdapter::KEYDOWN)?TRUE:FALSE);

                return true;
            }
            else
            {
                if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Escape)
                {
                    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
                    if (viewer) viewer->setDone(true);
                }
            }

        }

        default:
            return false;
    }
    return false;
}

class RfbThread : public OpenThreads::Thread
{
public:

    RfbThread(rfbClient* client):
        _client(client),
        _done(false) {}

    virtual ~RfbThread()
    {
        _done = true;
        cancel();
        while(isRunning())
        {
            OpenThreads::Thread::YieldCurrentThread();
        }
    }

    virtual void run()
    {
        do
        {
            int i=WaitForMessage(_client,500);
            if(i<0)
                return;
            if(i)
                if(!HandleRFBServerMessage(_client))
                return;

        } while (!_done && !testCancel());
    }

    rfbClient*  _client;
    bool        _done;

};

int main(int argc,char** argv)
{
    osg::ref_ptr<osg::Image> image = new osg::Image;
    // image->setPixelBufferObject(new osg::PixelBufferObject(image.get()));

    /* 16-bit: client=rfbGetClient(5,3,2); */
    rfbClient* client=rfbGetClient(8,3,4);
    client->canHandleNewFBSize = TRUE;
    client->MallocFrameBuffer = resizeImage;
    client->GotFrameBufferUpdate = updateImage;
    client->HandleKeyboardLedState = 0;
    client->HandleTextChat = 0;

    rfbClientSetClientData(client, 0, image.get());

    if(!rfbInitClient(client,&argc,argv))
        return 1;

    osg::ArgumentParser arguments(&argc, argv);
    osgViewer::Viewer viewer;

    bool xyPlane = false;
    bool flip = true;
    float width = image->s();
    float height = image->t();

    osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(osg::Vec3(0.0f,0.0f,0.0f),
                                       osg::Vec3(width,0.0f,0.0f),
                                       xyPlane ? osg::Vec3(0.0f,height,0.0f) : osg::Vec3(0.0f,0.0f,height),
                                       0.0f, flip ? 1.0f : 0.0f , 1.0f, flip ? 0.0f : 1.0f);

    osg::Texture2D* texture = new osg::Texture2D(image.get());
    texture->setResizeNonPowerOfTwoHint(false);
    texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);

    pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                texture,
                osg::StateAttribute::ON);

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(pictureQuad);

    viewer.setSceneData(geode);

    viewer.addEventHandler(new osgViewer::StatsHandler);
    viewer.addEventHandler(new RfbEventHandler(client));

    RfbThread rfbThread(client);
    rfbThread.startThread();

    viewer.setKeyEventSetsDone(0);

    return viewer.run();

    // rfbClientCleanup(cl);
}

<commit_msg>Wrapped up the various client streaming methods + thread into VncImage classes.<commit_after>#include <osg/Image>
#include <osg/Geometry>
#include <osg/Texture2D>

#include <osgGA/TrackballManipulator>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <iostream>
#include <osg/io_utils>

extern "C" {
#include <rfb/rfbclient.h>
}

class VncImage : public osg::Image
{
    public:

        VncImage();

        bool connect(int* argc, char** argv);

        void close();

        static rfbBool resizeImage(rfbClient* client);

        static void updateImage(rfbClient* client,int x,int y,int w,int h);

    protected:

        virtual ~VncImage();

        class RfbThread : public osg::Referenced, public OpenThreads::Thread
        {
        public:

            RfbThread(rfbClient* client):
                _client(client),
                _done(false) {}

            virtual ~RfbThread()
            {
                _done = true;
                cancel();
                while(isRunning())
                {
                    OpenThreads::Thread::YieldCurrentThread();
                }
            }

            virtual void run()
            {
                do
                {
                    int i=WaitForMessage(_client,500);
                    if(i<0)
                        return;
                    if(i)
                        if(!HandleRFBServerMessage(_client))
                        return;

                } while (!_done && !testCancel());
            }

            rfbClient*  _client;
            bool        _done;

        };

    public:

        rfbClient* _client;

        osg::ref_ptr<RfbThread> _rfbThread;

};

VncImage::VncImage()
{
    // setPixelBufferObject(new osg::PixelBufferObject(this);

}

VncImage::~VncImage()
{
    close();
}

bool VncImage::connect(int* argc, char** argv)
{
    if (_client) close();

    _client = rfbGetClient(8,3,4);
    _client->canHandleNewFBSize = TRUE;
    _client->MallocFrameBuffer = resizeImage;
    _client->GotFrameBufferUpdate = updateImage;
    _client->HandleKeyboardLedState = 0;
    _client->HandleTextChat = 0;

    rfbClientSetClientData(_client, 0, this);

    if (rfbInitClient(_client,argc,argv))
    {
        _rfbThread = new RfbThread(_client);
        _rfbThread->startThread();
    }
}

void VncImage::close()
{
    if (_rfbThread.valid())
    {
        // stop the client thread
        _rfbThread = 0;
    }

    if (_client)
    {
        // close the client
        rfbClientCleanup(_client);
        _client = 0;
    }
}

rfbBool VncImage::resizeImage(rfbClient* client)
{
    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));

    int width=client->width;
    int height=client->height;
    int depth=client->format.bitsPerPixel;

    std::cout<<"resize "<<width<<", "<<height<<", "<<depth<<" image = "<<image<<std::endl;

    image->allocateImage(width,height,1,GL_RGBA,GL_UNSIGNED_BYTE);

    client->frameBuffer= (uint8_t*)(image->data());

    return TRUE;
}

void VncImage::updateImage(rfbClient* client,int x,int y,int w,int h)
{
    osg::Image* image = (osg::Image*)(rfbClientGetClientData(client, 0));
    image->dirty();
}

class RfbEventHandler : public osgGA::GUIEventHandler
{
public:

    RfbEventHandler(rfbClient* client):
        _client(client) {}

    virtual bool handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv);

    rfbKeySym key2rfbKeySym(int key)
    {
        return rfbKeySym(key);
    }

protected:

    virtual ~RfbEventHandler() {}

    bool mousePosition(osgViewer::View* view, osg::NodeVisitor* nv, const osgGA::GUIEventAdapter& ea, int& x, int &y) const;

    rfbClient*  _client;

};

bool RfbEventHandler::mousePosition(osgViewer::View* view, osg::NodeVisitor* nv, const osgGA::GUIEventAdapter& ea, int& x, int &y) const
{
    osgUtil::LineSegmentIntersector::Intersections intersections;
    bool foundIntersection = view==0 ? false :
        (nv==0 ? view->computeIntersections(ea.getX(), ea.getY(), intersections) :
                 view->computeIntersections(ea.getX(), ea.getY(), nv->getNodePath(), intersections));

    if (foundIntersection)
    {

        osg::Vec2 tc(0.5f,0.5f);

        // use the nearest intersection
        const osgUtil::LineSegmentIntersector::Intersection& intersection = *(intersections.begin());
        osg::Drawable* drawable = intersection.drawable.get();
        osg::Geometry* geometry = drawable ? drawable->asGeometry() : 0;
        osg::Vec3Array* vertices = geometry ? dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray()) : 0;
        if (vertices)
        {
            // get the vertex indices.
            const osgUtil::LineSegmentIntersector::Intersection::IndexList& indices = intersection.indexList;
            const osgUtil::LineSegmentIntersector::Intersection::RatioList& ratios = intersection.ratioList;

            if (indices.size()==3 && ratios.size()==3)
            {
                unsigned int i1 = indices[0];
                unsigned int i2 = indices[1];
                unsigned int i3 = indices[2];

                float r1 = ratios[0];
                float r2 = ratios[1];
                float r3 = ratios[2];

                osg::Array* texcoords = (geometry->getNumTexCoordArrays()>0) ? geometry->getTexCoordArray(0) : 0;
                osg::Vec2Array* texcoords_Vec2Array = dynamic_cast<osg::Vec2Array*>(texcoords);
                if (texcoords_Vec2Array)
                {
                    // we have tex coord array so now we can compute the final tex coord at the point of intersection.
                    osg::Vec2 tc1 = (*texcoords_Vec2Array)[i1];
                    osg::Vec2 tc2 = (*texcoords_Vec2Array)[i2];
                    osg::Vec2 tc3 = (*texcoords_Vec2Array)[i3];
                    tc = tc1*r1 + tc2*r2 + tc3*r3;
                }
            }

        }

        x = int( float(_client->width) * tc.x() );
        y = int( float(_client->height) * tc.y() );

        return true;
    }

    return false;
}

bool RfbEventHandler::handle(const osgGA::GUIEventAdapter& ea,osgGA::GUIActionAdapter& aa, osg::Object*, osg::NodeVisitor* nv)
{
    switch(ea.getEventType())
    {
        case(osgGA::GUIEventAdapter::MOVE):
        case(osgGA::GUIEventAdapter::DRAG):
        case(osgGA::GUIEventAdapter::PUSH):
        case(osgGA::GUIEventAdapter::RELEASE):
        {
            osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
            int x,y;
            if (mousePosition(view, nv, ea, x, y))
            {
                SendPointerEvent(_client,x,y, ea.getButtonMask());
                return true;
            }
            break;
        }
        case(osgGA::GUIEventAdapter::KEYDOWN):
        case(osgGA::GUIEventAdapter::KEYUP):
        {
            osgViewer::View* view = dynamic_cast<osgViewer::View*>(&aa);
            int x,y;
            bool sendKeyEvent = mousePosition(view, nv, ea, x, y);

            if (sendKeyEvent)
            {
                SendKeyEvent(_client,
                    key2rfbKeySym(ea.getKey()),
                    (ea.getEventType()==osgGA::GUIEventAdapter::KEYDOWN)?TRUE:FALSE);

                return true;
            }
            else
            {
                if (ea.getKey()==osgGA::GUIEventAdapter::KEY_Escape)
                {
                    osgViewer::Viewer* viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
                    if (viewer) viewer->setDone(true);
                }
            }

        }

        default:
            return false;
    }
    return false;
}

int main(int argc,char** argv)
{
    osg::ref_ptr<VncImage> image = new VncImage;

    if (image->connect(&argc,argv))
    {
        return 1;
    }

    osg::ArgumentParser arguments(&argc, argv);
    osgViewer::Viewer viewer;

    bool xyPlane = false;
    bool flip = true;
    float width = image->s();
    float height = image->t();

    osg::Geometry* pictureQuad = osg::createTexturedQuadGeometry(osg::Vec3(0.0f,0.0f,0.0f),
                                       osg::Vec3(width,0.0f,0.0f),
                                       xyPlane ? osg::Vec3(0.0f,height,0.0f) : osg::Vec3(0.0f,0.0f,height),
                                       0.0f, flip ? 1.0f : 0.0f , 1.0f, flip ? 0.0f : 1.0f);

    osg::Texture2D* texture = new osg::Texture2D(image.get());
    texture->setResizeNonPowerOfTwoHint(false);
    texture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
    texture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
    texture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);

    pictureQuad->getOrCreateStateSet()->setTextureAttributeAndModes(0,
                texture,
                osg::StateAttribute::ON);

    osg::Geode* geode = new osg::Geode;
    geode->addDrawable(pictureQuad);

    viewer.setSceneData(geode);

    viewer.addEventHandler(new osgViewer::StatsHandler);
    viewer.addEventHandler(new RfbEventHandler(image->_client));

    viewer.setKeyEventSetsDone(0);

    return viewer.run();
}

<|endoftext|>


<commit_before>#pragma once

#include <boost/operators.hpp>

#include <iterator>

#include "defaults.hpp"
#include "tools.hpp"

namespace gftools {

template <typename ValueType>
struct point_base :
    boost::less_than_comparable<point_base<ValueType>>,
    boost::equality_comparable<point_base<ValueType>>
 {
    static_assert(!std::is_same<ValueType,int>::value, "Can't create a grid of ints");

    typedef ValueType value_type;

    operator ValueType() const { return val_; }
    explicit operator size_t() const { return index_; }
    explicit operator int() const { return index_; }

    point_base(ValueType val, size_t index):val_(val),index_(index){};
    point_base(const point_base& rhs):val_(rhs.val_),index_(rhs.index_){};
    point_base(point_base&& rhs):val_(rhs.val_),index_(rhs.index_) {};
    point_base& operator=(point_base&& rhs) { val_ = rhs.val_, index_ = rhs.index_; return *this;}
    point_base operator=(const point_base& rhs) { val_ = rhs.val_, index_ = rhs.index_; return *this;}
    point_base () = delete;
    bool operator==(const point_base &rhs) const {return (val_ == rhs.val_) && (index_ == rhs.index_);}
    //bool operator!=(const point_base &rhs) const {return !(*this == rhs); }
    bool operator<(const point_base &rhs) const {return this->index_ < rhs.index_;}
    friend std::ostream& operator<<(std::ostream& lhs, const point_base &p){lhs<<"{"<<p.val_<<"<-["<<p.index_<<"]}"; return lhs;};

    ValueType value() const { return val_; }
    size_t index() const { return index_; }

    ValueType val_;
    size_t index_;
};

/** A representation of a one-dimensional grid, which stores an array of the ValueType values. */
//template <typename ValueType, class Derived, typename = typename std::enable_if<!std::is_same<ValueType, int>::value>::type>
template <typename ValueType, class Derived>
class grid_base {
public:
    /** A point combines a point of the grid and it's index. */
    struct point : point_base<ValueType> {
        //point():point_base<ValueType>::point_base(){};
        point(ValueType val, size_t index):point_base<ValueType>::point_base(val,index){};
        point(const point_base<ValueType> &in):point_base<ValueType>::point_base(in){};
        point(point_base<ValueType> &&in):point_base<ValueType>::point_base(std::forward<point_base<ValueType>>(in)){};
        };

    typedef ValueType value_type;

    grid_base(const std::vector<point> & vals);

    /** Empty constructor. */
    grid_base();
    /** Copy from vector. */
    grid_base(const std::vector<ValueType> & vals);
    /** Initialize the values from a given function, that maps the integer values
     * to the ValueType values.
     */
    grid_base(int min, int max, std::function<ValueType (int)> f);
    /** Copy constructor. */
    grid_base(const grid_base& rhs):vals_(rhs.vals_){};
    /** Move constructor. */
    grid_base(grid_base&& rhs){vals_.swap(rhs.vals_);};
    /** rvalue assignment. */
    grid_base& operator=(grid_base &&rhs){vals_.swap(rhs.vals_); return (*this);};
    /** Returns a value at given index. */
    point operator[](size_t in) const;
    /** Returns all values. */
    const std::vector<point> & points() const;
    /** Returns values of all points. */
    std::vector<ValueType> values() const;
    /** Checks if a point is present in a grid. */
    bool check_point(point in, real_type tolerance = std::numeric_limits<real_type>::epsilon()) const;
    /** Returns size of grid. */
    size_t size() const;

    /** Returns the closest point to the given value. */
    point find_nearest(ValueType in) const;
    /** Get a value of an object at the given point, which is defined on a grid. */
    template <class Obj> auto eval(Obj &&in, point x) const ->decltype(in[0]);

    /** Shift a point by the given value. */
    point shift(point in, ValueType shift_arg) const;
    ValueType shift(ValueType in, ValueType shift_arg) const;
    point shift (point in, point shift_arg) const;

    // CFTP forwards
    /** Get a value of an object at the given coordinate, which is defined on a grid. */
    template <class Obj>
        auto eval(Obj &in, ValueType x) const ->decltype(in[0])
        { return static_cast<const Derived*>(this)->eval(in,x); };
    /** Returns a tuple of left closest index, weight, right closest index and weight, which are the closest to input value. */
    /** Integrate over grid. */
    //template <class Obj> auto integrate(const Obj &in) const ->decltype(in[vals_[0]])
    //    { return static_cast<const Derived*>(this)->integrate(in); };
    /** Integrate over grid with extra arguments provided. */
    template <class Obj, typename std::result_of<Obj(point)>::type>
        auto integrate(Obj &&in) const ->
            typename std::remove_reference<typename std::result_of<Obj(point)>::type>::type
                { return static_cast<const Derived*>(this)->integrate(in); };
    /** Make the object printable. */
    template <typename ValType, class Derived2> friend std::ostream& operator<<(std::ostream& lhs, const grid_base<ValType,Derived2> &gr);

    std::vector<point> vals_;
    class ex_wrong_index : public std::exception { virtual const char* what() const throw(); };
};

namespace extra {
/// A small helper struct to decorate a function object with [] method.
template <typename F, typename Grid>
struct function_proxy {
    F f_;
    const Grid& grid_;
    function_proxy(F f, Grid const& grid):f_(f),grid_(grid){}
    typedef typename std::result_of<F(typename Grid::value_type)>::type value_type;
    value_type operator[](int i) const {return f_((grid_.points()[i]).val_); };
};
}

//
// grid_base implementation
//

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base()
{};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(const std::vector<point> &vals):vals_(vals)
{
};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(const std::vector<ValueType> &vals)
{
    vals_.reserve(vals.size());
    for (size_t i=0; i<vals.size(); ++i) { vals_.emplace_back(point(vals[i], i)); };
};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(int min, int max, std::function<ValueType (int)> f)
{
    if (max<min) std::swap(min,max);
    size_t n_points = max-min;
    vals_.reserve(n_points);
    for (int i=0; i<n_points; ++i) vals_.emplace_back(f(min+i), i) ;
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::operator[](size_t index) const
{
    if (index>vals_.size()) throw ex_wrong_index();
    return vals_[index];
}

template <typename ValueType, class Derived>
inline const std::vector<typename grid_base<ValueType,Derived>::point> & grid_base<ValueType,Derived>::points() const
{
    return vals_;
}

template <typename ValueType, class Derived>
inline std::vector<ValueType> grid_base<ValueType,Derived>::values() const
{
    std::vector<ValueType> out;
    out.reserve(vals_.size());
    for (const auto& x : vals_) out.emplace_back(x.val_);
    return out;
}

template <typename ValueType, class Derived>
inline size_t grid_base<ValueType,Derived>::size() const
{
    return vals_.size();
}

template <typename ValueType, class Derived>
inline bool grid_base<ValueType,Derived>::check_point(point in, real_type tolerance) const
{
    return (in.index_ < vals_.size() && std::abs(in.val_ - vals_[in.index_].val_) < tolerance);
}

template <typename ValueType, class Derived>
template <class Obj>
inline auto grid_base<ValueType,Derived>::eval(Obj &&in, point x) const ->decltype(in[0])
{
    if (check_point(x)) return in[x.index_];
    else { ERROR ("Point not found"); throw ex_wrong_index(); };
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::find_nearest(ValueType in) const
{
    static_assert(std::is_same<bool,decltype(std::declval<ValueType>() < std::declval<ValueType>())>::value,
        "Default find_nearest is written only for less-comparable types");
    auto nearest_iter = std::lower_bound(vals_.begin(), vals_.end(), in, [](ValueType x, ValueType y){return x<y;});
    size_t dist = std::distance(vals_.begin(), nearest_iter);
    if (dist > 0 && std::abs(complex_type(vals_[dist].val_) - complex_type(in)) > std::abs(complex_type(vals_[dist-1].val_) - complex_type(in)) ) dist--;
    return vals_[dist];
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::shift(point in, ValueType shift_arg) const
{
    if (tools::is_float_equal(shift_arg, 0.0)) return in;
    ValueType out(in.val_);
    out = static_cast<const Derived*>(this)->shift(ValueType(in),shift_arg);
    point p1 = static_cast<const Derived*>(this)->find_nearest(out);
    if (!tools::is_float_equal(p1.val_, out, std::abs(p1.val_ - ((p1.index_!=0)?vals_[p1.index_ - 1]:vals_[p1.index_+1]).val_)/10.)) {
        #ifndef NDEBUG
        ERROR("Couldn't shift point" <<  in << " by " << shift_arg);
        #endif
        throw (ex_wrong_index());
        }
    else return p1;
}

template <typename ValueType, class Derived>
inline ValueType grid_base<ValueType,Derived>::shift(ValueType in, ValueType shift_arg) const
{
    return in+shift_arg;
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::shift(point in, point shift_arg) const
{
    size_t index = (in.index_ + shift_arg.index_)%vals_.size();
    #ifndef NDEBUG
    ValueType val = static_cast<const Derived*>(this)->shift(in.val_, shift_arg.val_);
    if (!tools::is_float_equal(val, vals_[index].val_)) throw (ex_wrong_index());
    #endif
    return vals_[index];

}

template <typename ValueType, class Derived>
std::ostream& operator<<(std::ostream& lhs, const grid_base<ValueType,Derived> &gr)
{
    lhs << "{";
    //lhs << gr.vals_;
    std::ostream_iterator<ValueType> out_it (lhs,", ");
    std::transform(gr.vals_.begin(),gr.vals_.end(), out_it, [](const typename grid_base<ValueType,Derived>::point &x){return ValueType(x);});
    lhs << "}";
    return lhs;
}

template <typename ValueType, class Derived>
const char* grid_base<ValueType,Derived>::ex_wrong_index::what() const throw(){
     return "Index out of bounds";
};

} // end :: namespace gftools

//#include "grid_tools.hpp"

<commit_msg>grid_base : comparison<commit_after>#pragma once

#include <boost/operators.hpp>

#include <iterator>

#include "defaults.hpp"
#include "tools.hpp"

namespace gftools {

template <typename ValueType>
struct point_base :
    boost::less_than_comparable<point_base<ValueType>>,
    boost::equality_comparable<point_base<ValueType>>
 {
    static_assert(!std::is_same<ValueType,int>::value, "Can't create a grid of ints");

    typedef ValueType value_type;

    operator ValueType() const { return val_; }
    explicit operator size_t() const { return index_; }
    explicit operator int() const { return index_; }

    point_base(ValueType val, size_t index):val_(val),index_(index){};
    point_base(const point_base& rhs):val_(rhs.val_),index_(rhs.index_){};
    point_base(point_base&& rhs):val_(rhs.val_),index_(rhs.index_) {};
    point_base& operator=(point_base&& rhs) { val_ = rhs.val_, index_ = rhs.index_; return *this;}
    point_base operator=(const point_base& rhs) { val_ = rhs.val_, index_ = rhs.index_; return *this;}
    point_base () = delete;
    bool operator==(const point_base &rhs) const {return (val_ == rhs.val_) && (index_ == rhs.index_);}
    //bool operator!=(const point_base &rhs) const {return !(*this == rhs); }
    bool operator<(const point_base &rhs) const {return this->index_ < rhs.index_;}
    friend std::ostream& operator<<(std::ostream& lhs, const point_base &p){lhs<<"{"<<p.val_<<"<-["<<p.index_<<"]}"; return lhs;};

    ValueType value() const { return val_; }
    size_t index() const { return index_; }

    ValueType val_;
    size_t index_;
};

/** A representation of a one-dimensional grid, which stores an array of the ValueType values. */
//template <typename ValueType, class Derived, typename = typename std::enable_if<!std::is_same<ValueType, int>::value>::type>
template <typename ValueType, class Derived>
class grid_base : public boost::equality_comparable<grid_base<ValueType, Derived>> {
public:
    /** A point combines a point of the grid and it's index. */
    struct point : point_base<ValueType> {
        //point():point_base<ValueType>::point_base(){};
        point(ValueType val, size_t index):point_base<ValueType>::point_base(val,index){};
        point(const point_base<ValueType> &in):point_base<ValueType>::point_base(in){};
        point(point_base<ValueType> &&in):point_base<ValueType>::point_base(std::forward<point_base<ValueType>>(in)){};
        };

    typedef ValueType value_type;

    grid_base(const std::vector<point> & vals);

    /** Empty constructor. */
    grid_base();
    /** Copy from vector. */
    grid_base(const std::vector<ValueType> & vals);
    /** Initialize the values from a given function, that maps the integer values
     * to the ValueType values.
     */
    grid_base(int min, int max, std::function<ValueType (int)> f);
    /** Copy constructor. */
    grid_base(const grid_base& rhs):vals_(rhs.vals_){};
    /** Move constructor. */
    grid_base(grid_base&& rhs){vals_.swap(rhs.vals_);};
    /** rvalue assignment. */
    grid_base& operator=(grid_base &&rhs){vals_.swap(rhs.vals_); return (*this);};
    /** Returns a value at given index. */
    point operator[](size_t in) const;
    /** Returns all values. */
    const std::vector<point> & points() const;
    /** Returns values of all points. */
    std::vector<ValueType> values() const;
    /** Checks if a point is present in a grid. */
    bool check_point(point in, real_type tolerance = std::numeric_limits<real_type>::epsilon()) const;
    /** Returns size of grid. */
    size_t size() const;

    /** Returns the closest point to the given value. */
    point find_nearest(ValueType in) const;
    /** Get a value of an object at the given point, which is defined on a grid. */
    template <class Obj> auto eval(Obj &&in, point x) const ->decltype(in[0]);

    /** Shift a point by the given value. */
    point shift(point in, ValueType shift_arg) const;
    ValueType shift(ValueType in, ValueType shift_arg) const;
    point shift (point in, point shift_arg) const;

    // CFTP forwards
    /** Get a value of an object at the given coordinate, which is defined on a grid. */
    template <class Obj>
        auto eval(Obj &in, ValueType x) const ->decltype(in[0])
        { return static_cast<const Derived*>(this)->eval(in,x); };
    /** Returns a tuple of left closest index, weight, right closest index and weight, which are the closest to input value. */
    /** Integrate over grid. */
    //template <class Obj> auto integrate(const Obj &in) const ->decltype(in[vals_[0]])
    //    { return static_cast<const Derived*>(this)->integrate(in); };
    /** Integrate over grid with extra arguments provided. */
    template <class Obj, typename std::result_of<Obj(point)>::type>
        auto integrate(Obj &&in) const ->
            typename std::remove_reference<typename std::result_of<Obj(point)>::type>::type
                { return static_cast<const Derived*>(this)->integrate(in); };
    /// Make the object printable.
    template <typename ValType, class Derived2> friend std::ostream& operator<<(std::ostream& lhs, const grid_base<ValType,Derived2> &gr);
    /// Compare 2 grids
    bool operator==(const grid_base &rhs) const;

    std::vector<point> vals_;
    class ex_wrong_index : public std::exception { virtual const char* what() const throw(); };
};

namespace extra {
/// A small helper struct to decorate a function object with [] method.
template <typename F, typename Grid>
struct function_proxy {
    F f_;
    const Grid& grid_;
    function_proxy(F f, Grid const& grid):f_(f),grid_(grid){}
    typedef typename std::result_of<F(typename Grid::value_type)>::type value_type;
    value_type operator[](int i) const {return f_((grid_.points()[i]).val_); };
};
}

//
// grid_base implementation
//

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base()
{};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(const std::vector<point> &vals):vals_(vals)
{
};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(const std::vector<ValueType> &vals)
{
    vals_.reserve(vals.size());
    for (size_t i=0; i<vals.size(); ++i) { vals_.emplace_back(point(vals[i], i)); };
};

template <typename ValueType, class Derived>
grid_base<ValueType,Derived>::grid_base(int min, int max, std::function<ValueType (int)> f)
{
    if (max<min) std::swap(min,max);
    size_t n_points = max-min;
    vals_.reserve(n_points);
    for (int i=0; i<n_points; ++i) vals_.emplace_back(f(min+i), i) ;
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::operator[](size_t index) const
{
    if (index>vals_.size()) throw ex_wrong_index();
    return vals_[index];
}

template <typename ValueType, class Derived>
inline const std::vector<typename grid_base<ValueType,Derived>::point> & grid_base<ValueType,Derived>::points() const
{
    return vals_;
}

template <typename ValueType, class Derived>
inline std::vector<ValueType> grid_base<ValueType,Derived>::values() const
{
    std::vector<ValueType> out;
    out.reserve(vals_.size());
    for (const auto& x : vals_) out.emplace_back(x.val_);
    return out;
}

template <typename ValueType, class Derived>
inline size_t grid_base<ValueType,Derived>::size() const
{
    return vals_.size();
}

template <typename ValueType, class Derived>
inline bool grid_base<ValueType,Derived>::check_point(point in, real_type tolerance) const
{
    return (in.index_ < vals_.size() && std::abs(in.val_ - vals_[in.index_].val_) < tolerance);
}

template <typename ValueType, class Derived>
template <class Obj>
inline auto grid_base<ValueType,Derived>::eval(Obj &&in, point x) const ->decltype(in[0])
{
    if (check_point(x)) return in[x.index_];
    else { ERROR ("Point not found"); throw ex_wrong_index(); };
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::find_nearest(ValueType in) const
{
    static_assert(std::is_same<bool,decltype(std::declval<ValueType>() < std::declval<ValueType>())>::value,
        "Default find_nearest is written only for less-comparable types");
    auto nearest_iter = std::lower_bound(vals_.begin(), vals_.end(), in, [](ValueType x, ValueType y){return x<y;});
    size_t dist = std::distance(vals_.begin(), nearest_iter);
    if (dist > 0 && std::abs(complex_type(vals_[dist].val_) - complex_type(in)) > std::abs(complex_type(vals_[dist-1].val_) - complex_type(in)) ) dist--;
    return vals_[dist];
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::shift(point in, ValueType shift_arg) const
{
    if (tools::is_float_equal(shift_arg, 0.0)) return in;
    ValueType out(in.val_);
    out = static_cast<const Derived*>(this)->shift(ValueType(in),shift_arg);
    point p1 = static_cast<const Derived*>(this)->find_nearest(out);
    if (!tools::is_float_equal(p1.val_, out, std::abs(p1.val_ - ((p1.index_!=0)?vals_[p1.index_ - 1]:vals_[p1.index_+1]).val_)/10.)) {
        #ifndef NDEBUG
        ERROR("Couldn't shift point" <<  in << " by " << shift_arg);
        #endif
        throw (ex_wrong_index());
        }
    else return p1;
}

template <typename ValueType, class Derived>
inline ValueType grid_base<ValueType,Derived>::shift(ValueType in, ValueType shift_arg) const
{
    return in+shift_arg;
}

template <typename ValueType, class Derived>
inline typename grid_base<ValueType,Derived>::point grid_base<ValueType,Derived>::shift(point in, point shift_arg) const
{
    size_t index = (in.index_ + shift_arg.index_)%vals_.size();
    #ifndef NDEBUG
    ValueType val = static_cast<const Derived*>(this)->shift(in.val_, shift_arg.val_);
    if (!tools::is_float_equal(val, vals_[index].val_)) throw (ex_wrong_index());
    #endif
    return vals_[index];

}

template <typename ValueType, class Derived>
inline bool grid_base<ValueType,Derived>::operator==(const grid_base &rhs) const
{
    bool out = (this->size() == rhs.size());
    for (int i=0; i<vals_.size() && out; i++) {
        out = out && vals_[i].value() == rhs.vals_[i].value();
    }
    return out;
}

template <typename ValueType, class Derived>
std::ostream& operator<<(std::ostream& lhs, const grid_base<ValueType,Derived> &gr)
{
    lhs << "{";
    //lhs << gr.vals_;
    std::ostream_iterator<ValueType> out_it (lhs,", ");
    std::transform(gr.vals_.begin(),gr.vals_.end(), out_it, [](const typename grid_base<ValueType,Derived>::point &x){return ValueType(x);});
    lhs << "}";
    return lhs;
}

template <typename ValueType, class Derived>
const char* grid_base<ValueType,Derived>::ex_wrong_index::what() const throw(){
     return "Index out of bounds";
};

} // end :: namespace gftools

//#include "grid_tools.hpp"

<|endoftext|>


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

/**
 * @file heaptrack_print.cpp
 *
 * @brief Evaluate and print the collected heaptrack data.
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <map>
#include <vector>
#include <memory>
#include <tuple>
#include <algorithm>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/program_options.hpp>

using namespace std;
namespace po = boost::program_options;

namespace {

struct AddressInformation
{
    string function;
    string file;
    int line = 0;
};

ostream& operator<<(ostream& out, const AddressInformation& info)
{
    out << info.function;
    if (!info.file.empty()) {
        out << " in " << info.file << ':' << info.line;
    }
    return out;
}

struct InstructionPointer
{
    uintptr_t instructionPointer = 0;
    size_t parentIndex = 0;
    size_t moduleIndex = 0;
    size_t functionIndex = 0;
    size_t fileIndex = 0;
    int line = 0;
};

struct Allocation
{
    // backtrace entry point
    size_t ipIndex;
    // number of allocations
    size_t allocations;
    // bytes allocated in total
    size_t allocated;
    // amount of bytes leaked
    size_t leaked;
    // largest amount of bytes allocated
    size_t peak;
};

/**
 * Information for a single call to an allocation function
 */
struct AllocationInfo
{
    size_t ipIndex;
    size_t size;
};

struct AccumulatedTraceData
{
    AccumulatedTraceData()
    {
        instructionPointers.reserve(65536);
        strings.reserve(16384);
        allocations.reserve(16384);
        activeAllocations.reserve(65536);
    }

    void clear()
    {
        mainIndex = 0;
        instructionPointers.clear();
        strings.clear();
        allocations.clear();
        activeAllocations.clear();
    }

    void printBacktrace(const size_t ipIndex, ostream& out) const
    {
        printBacktrace(findIp(ipIndex), out);
    }

    void printBacktrace(InstructionPointer ip, ostream& out) const
    {
        while (ip.instructionPointer) {
            out << "0x" << hex << ip.instructionPointer << dec;
            if (ip.moduleIndex) {
                out << ' ' << stringify(ip.moduleIndex);
            } else {
                out << " <unknown module>";
            }
            if (ip.functionIndex) {
                out << ' ' << prettyFunction(stringify(ip.functionIndex));
            } else {
                out << " ??";
            }
            if (ip.fileIndex) {
                out << ' ' << stringify(ip.fileIndex) << ':' << ip.line;
            }
            out << '\n';

            if (mainIndex && ip.functionIndex == mainIndex) {
                break;
            }

            ip = findIp(ip.parentIndex);
        };
    }

    Allocation& findAllocation(const size_t ipIndex)
    {
        auto it = lower_bound(allocations.begin(), allocations.end(), ipIndex,
                                [] (const Allocation& allocation, const size_t ipIndex) -> bool {
                                    return allocation.ipIndex < ipIndex;
                                });
        if (it == allocations.end() || it->ipIndex != ipIndex) {
            it = allocations.insert(it, {ipIndex, 0, 0, 0, 0});
        }
        return *it;
    }

    const string& stringify(const size_t stringId) const
    {
        if (!stringId || stringId > strings.size()) {
            static const string empty;
            return empty;
        } else {
            return strings.at(stringId - 1);
        }
    }

    string prettyFunction(const string& function) const
    {
        if (!shortenTemplates) {
            return function;
        }
        string ret;
        ret.reserve(function.size());
        int depth = 0;
        for (size_t i = 0; i < function.size(); ++i) {
            const auto c = function[i];
            if (c == '<') {
                ++depth;
                if (depth == 1) {
                    ret.push_back(c);
                }
            } else if (c == '>') {
                --depth;
            }
            if (depth) {
                continue;
            }
            ret.push_back(c);
        }
        return ret;
    }

    bool read(istream& in)
    {
        clear();

        string line;
        line.reserve(1024);

        stringstream lineIn(ios_base::in);
        lineIn << hex;

        while (in.good()) {
            getline(in, line);
            if (line.empty()) {
                continue;
            }
            const char mode = line[0];
            lineIn.str(line);
            lineIn.clear();
            // skip mode and leading whitespace
            lineIn.seekg(2);
            if (mode == 's') {
                strings.push_back(line.substr(2));
            } else if (mode == 'i') {
                InstructionPointer ip;
                lineIn >> ip.instructionPointer;
                lineIn >> ip.parentIndex;
                lineIn >> ip.moduleIndex;
                lineIn >> ip.functionIndex;
                lineIn >> ip.fileIndex;
                lineIn >> ip.line;
                instructionPointers.push_back(ip);
            } else if (mode == '+') {
                size_t size = 0;
                lineIn >> size;
                size_t ipId = 0;
                lineIn >> ipId;
                uintptr_t ptr = 0;
                lineIn >> ptr;
                if (lineIn.bad()) {
                    cerr << "failed to parse line: " << line << endl;
                    return false;
                }

                activeAllocations[ptr] = {ipId, size};

                auto& allocation = findAllocation(ipId);
                allocation.leaked += size;
                allocation.allocated += size;
                ++allocation.allocations;
                if (allocation.leaked > allocation.peak) {
                    allocation.peak = allocation.leaked;
                }
                totalAllocated += size;
                ++totalAllocations;
                leaked += size;
                if (leaked > peak) {
                    peak = leaked;
                }
                ++sizeHistogram[size];
            } else if (mode == '-') {
                uintptr_t ptr = 0;
                lineIn >> ptr;
                if (lineIn.bad()) {
                    cerr << "failed to parse line: " << line << endl;
                    return false;
                }
                auto ip = activeAllocations.find(ptr);
                if (ip == activeAllocations.end()) {
                    cerr << "unknown pointer in line: " << line << endl;
                    continue;
                }
                const auto info = ip->second;
                activeAllocations.erase(ip);

                auto& allocation = findAllocation(info.ipIndex);
                if (!allocation.allocations || allocation.leaked < info.size) {
                    cerr << "inconsistent allocation info, underflowed allocations of " << info.ipIndex << endl;
                    allocation.leaked = 0;
                    allocation.allocations = 0;
                } else {
                    allocation.leaked -= info.size;
                }
                leaked -= info.size;
            } else {
                cerr << "failed to parse line: " << line << endl;
            }
        }

        // find index of "main" index which can be used to terminate backtraces
        // and prevent printing stuff above main, which is usually uninteresting
        auto it = find(strings.begin(), strings.end(), "main");
        if (it != strings.end()) {
            mainIndex = distance(strings.begin(), it);
        }

        /// these are leaks, but we have the same data in \c allocations as well
        activeAllocations.clear();
        return true;
    }

    bool shortenTemplates = false;

    vector<Allocation> allocations;
    map<size_t, size_t> sizeHistogram;
    size_t totalAllocated = 0;
    size_t totalAllocations = 0;
    size_t peak = 0;
    size_t leaked = 0;

private:
    InstructionPointer findIp(const size_t ipIndex) const
    {
        if (!ipIndex || ipIndex > instructionPointers.size()) {
            return {};
        } else {
            return instructionPointers[ipIndex - 1];
        }
    }

    size_t mainIndex = 0;
    unordered_map<uintptr_t, AllocationInfo> activeAllocations;
    vector<InstructionPointer> instructionPointers;
    vector<string> strings;
};

}

int main(int argc, char** argv)
{
    po::options_description desc("Options");
    desc.add_options()
        ("file,f", po::value<string>()->required(), "The heaptrack data file to print.")
        ("shorten-templates,t", po::value<bool>()->default_value(true), "Shorten template identifiers.")
        ("print-peaks,p", po::value<bool>()->default_value(true), "Print backtraces to top allocators, sorted by peak consumption.")
        ("print-allocators,a", po::value<bool>()->default_value(true), "Print backtraces to top allocators, sorted by number of calls to allocation functions.")
        ("print-leaks,l", po::value<bool>()->default_value(false), "Print backtraces to leaked memory allocations.")
        ("print-histogram,H", po::value<bool>()->default_value(false), "Print allocation size histogram.")
        ("print-overall-allocated,o", po::value<bool>()->default_value(false), "Print top overall allocators, ignoring memory frees.")
        ("help,h", "Show this help message.");
    po::positional_options_description p;
    p.add("file", -1);

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv)
                    .options(desc).positional(p).run(), vm);
        if (vm.count("help")) {
            cout << "heaptrack_print - analyze heaptrack data files.\n"
                << "\n"
                << "heaptrack is a heap memory profiler which records information\n"
                << "about calls to heap allocation functions such as malloc, operator new etc. pp.\n"
                << "This print utility can then be used to analyze the generated data files.\n\n"
                << desc << endl;
            return 1;
        }
        po::notify(vm);
    } catch (const po::error& error) {
        cerr << "ERROR: " << error.what() << endl
             << endl << desc << endl;
        return 1;
    }

    AccumulatedTraceData data;

    // optimize: we only have a single thread
    ios_base::sync_with_stdio(false);

    const auto inputFile = vm["file"].as<string>();
    data.shortenTemplates = vm["shorten-templates"].as<bool>();
    const bool printHistogram = vm["print-histogram"].as<bool>();
    const bool printLeaks = vm["print-leaks"].as<bool>();
    const bool printOverallAlloc = vm["print-overall-allocated"].as<bool>();
    const bool printPeaks = vm["print-peaks"].as<bool>();
    const bool printAllocs = vm["print-allocators"].as<bool>();

    string fileName(inputFile);
    const bool isCompressed = boost::algorithm::ends_with(fileName, ".gz");
    ifstream file(fileName, isCompressed ? ios_base::in | ios_base::binary : ios_base::in);

    if (!file.is_open()) {
        cerr << "Failed to open heaptrack log file: " << inputFile << endl
             << endl << desc << endl;
        return 1;
    }

    boost::iostreams::filtering_istream in;
    if (isCompressed) {
        in.push(boost::iostreams::gzip_decompressor());
    }
    in.push(file);

    cout << "reading file \"" << fileName << "\" - please wait, this might take some time...";
    cout.flush();

    if (!data.read(in)) {
        return 1;
    }

    cout << "finished reading file, now analyzing data:\n" << endl;

    if (printAllocs) {
        // sort by amount of allocations
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.allocations > r.allocations;
        });
        cout << "MOST CALLS TO ALLOCATION FUNCTIONS" << endl;
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.allocations << " allocations at:" << endl;
            data.printBacktrace(allocation.ipIndex, cout);
            cout << endl;
        }
        cout << endl;
    }

    if (printOverallAlloc) {
        // sort by amount of bytes allocated
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.allocated > r.allocated;
        });
        cout << "MOST BYTES ALLOCATED OVER TIME (ignoring deallocations)" << endl;
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.allocated << " bytes allocated at:" << endl;
            data.printBacktrace(allocation.ipIndex, cout);
            cout << endl;
        }
        cout << endl;
    }

    if (printPeaks) {
        // sort by peak memory consumption
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.peak > r.peak;
        });
        cout << "PEAK MEMORY CONSUMERS" << endl;
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.peak << " bytes allocated at peak:" << endl;
            data.printBacktrace(allocation.ipIndex, cout);
            cout << endl;
        }
        cout << endl;
    }

    if (printLeaks) {
        // sort by amount of leaks
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.leaked < r.leaked;
        });

        size_t totalLeakAllocations = 0;
        cout << "MEMORY LEAKS" << endl;
        for (const auto& allocation : data.allocations) {
            if (!allocation.leaked) {
                continue;
            }
            totalLeakAllocations += allocation.allocations;

            cout << allocation.leaked << " bytes leaked in " << allocation.allocations << " allocations at:" << endl;
            data.printBacktrace(allocation.ipIndex, cout);
            cout << endl;
        }
        cout << data.leaked << " bytes leaked in total from " << totalLeakAllocations << " allocations" << endl;
    }

    cout << data.totalAllocated << " bytes allocated in total over " << data.totalAllocations
         << " allocations, peak consumption: " << data.peak << " bytes" << endl;
    cout << endl;

    if (printHistogram) {
        cout << "size histogram: " << endl;
        for (auto entry : data.sizeHistogram) {
            cout << entry.first << "\t" << entry.second << endl;
        }
    }

    return 0;
}
<commit_msg>Reduce amount of cout flushing.<commit_after>/*
 * Copyright 2014 Milian Wolff <mail@milianw.de>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Library General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

/**
 * @file heaptrack_print.cpp
 *
 * @brief Evaluate and print the collected heaptrack data.
 */

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>
#include <map>
#include <vector>
#include <memory>
#include <tuple>
#include <algorithm>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/program_options.hpp>

using namespace std;
namespace po = boost::program_options;

namespace {

struct AddressInformation
{
    string function;
    string file;
    int line = 0;
};

ostream& operator<<(ostream& out, const AddressInformation& info)
{
    out << info.function;
    if (!info.file.empty()) {
        out << " in " << info.file << ':' << info.line;
    }
    return out;
}

struct InstructionPointer
{
    uintptr_t instructionPointer = 0;
    size_t parentIndex = 0;
    size_t moduleIndex = 0;
    size_t functionIndex = 0;
    size_t fileIndex = 0;
    int line = 0;
};

struct Allocation
{
    // backtrace entry point
    size_t ipIndex;
    // number of allocations
    size_t allocations;
    // bytes allocated in total
    size_t allocated;
    // amount of bytes leaked
    size_t leaked;
    // largest amount of bytes allocated
    size_t peak;
};

/**
 * Information for a single call to an allocation function
 */
struct AllocationInfo
{
    size_t ipIndex;
    size_t size;
};

struct AccumulatedTraceData
{
    AccumulatedTraceData()
    {
        instructionPointers.reserve(65536);
        strings.reserve(16384);
        allocations.reserve(16384);
        activeAllocations.reserve(65536);
    }

    void clear()
    {
        mainIndex = 0;
        instructionPointers.clear();
        strings.clear();
        allocations.clear();
        activeAllocations.clear();
    }

    void printBacktrace(const size_t ipIndex, ostream& out) const
    {
        printBacktrace(findIp(ipIndex), out);
    }

    void printBacktrace(InstructionPointer ip, ostream& out) const
    {
        while (ip.instructionPointer) {
            out << "0x" << hex << ip.instructionPointer << dec;
            if (ip.moduleIndex) {
                out << ' ' << stringify(ip.moduleIndex);
            } else {
                out << " <unknown module>";
            }
            if (ip.functionIndex) {
                out << ' ' << prettyFunction(stringify(ip.functionIndex));
            } else {
                out << " ??";
            }
            if (ip.fileIndex) {
                out << ' ' << stringify(ip.fileIndex) << ':' << ip.line;
            }
            out << '\n';

            if (mainIndex && ip.functionIndex == mainIndex) {
                break;
            }

            ip = findIp(ip.parentIndex);
        };
    }

    Allocation& findAllocation(const size_t ipIndex)
    {
        auto it = lower_bound(allocations.begin(), allocations.end(), ipIndex,
                                [] (const Allocation& allocation, const size_t ipIndex) -> bool {
                                    return allocation.ipIndex < ipIndex;
                                });
        if (it == allocations.end() || it->ipIndex != ipIndex) {
            it = allocations.insert(it, {ipIndex, 0, 0, 0, 0});
        }
        return *it;
    }

    const string& stringify(const size_t stringId) const
    {
        if (!stringId || stringId > strings.size()) {
            static const string empty;
            return empty;
        } else {
            return strings.at(stringId - 1);
        }
    }

    string prettyFunction(const string& function) const
    {
        if (!shortenTemplates) {
            return function;
        }
        string ret;
        ret.reserve(function.size());
        int depth = 0;
        for (size_t i = 0; i < function.size(); ++i) {
            const auto c = function[i];
            if (c == '<') {
                ++depth;
                if (depth == 1) {
                    ret.push_back(c);
                }
            } else if (c == '>') {
                --depth;
            }
            if (depth) {
                continue;
            }
            ret.push_back(c);
        }
        return ret;
    }

    bool read(istream& in)
    {
        clear();

        string line;
        line.reserve(1024);

        stringstream lineIn(ios_base::in);
        lineIn << hex;

        while (in.good()) {
            getline(in, line);
            if (line.empty()) {
                continue;
            }
            const char mode = line[0];
            lineIn.str(line);
            lineIn.clear();
            // skip mode and leading whitespace
            lineIn.seekg(2);
            if (mode == 's') {
                strings.push_back(line.substr(2));
            } else if (mode == 'i') {
                InstructionPointer ip;
                lineIn >> ip.instructionPointer;
                lineIn >> ip.parentIndex;
                lineIn >> ip.moduleIndex;
                lineIn >> ip.functionIndex;
                lineIn >> ip.fileIndex;
                lineIn >> ip.line;
                instructionPointers.push_back(ip);
            } else if (mode == '+') {
                size_t size = 0;
                lineIn >> size;
                size_t ipId = 0;
                lineIn >> ipId;
                uintptr_t ptr = 0;
                lineIn >> ptr;
                if (lineIn.bad()) {
                    cerr << "failed to parse line: " << line << endl;
                    return false;
                }

                activeAllocations[ptr] = {ipId, size};

                auto& allocation = findAllocation(ipId);
                allocation.leaked += size;
                allocation.allocated += size;
                ++allocation.allocations;
                if (allocation.leaked > allocation.peak) {
                    allocation.peak = allocation.leaked;
                }
                totalAllocated += size;
                ++totalAllocations;
                leaked += size;
                if (leaked > peak) {
                    peak = leaked;
                }
                ++sizeHistogram[size];
            } else if (mode == '-') {
                uintptr_t ptr = 0;
                lineIn >> ptr;
                if (lineIn.bad()) {
                    cerr << "failed to parse line: " << line << endl;
                    return false;
                }
                auto ip = activeAllocations.find(ptr);
                if (ip == activeAllocations.end()) {
                    cerr << "unknown pointer in line: " << line << endl;
                    continue;
                }
                const auto info = ip->second;
                activeAllocations.erase(ip);

                auto& allocation = findAllocation(info.ipIndex);
                if (!allocation.allocations || allocation.leaked < info.size) {
                    cerr << "inconsistent allocation info, underflowed allocations of " << info.ipIndex << endl;
                    allocation.leaked = 0;
                    allocation.allocations = 0;
                } else {
                    allocation.leaked -= info.size;
                }
                leaked -= info.size;
            } else {
                cerr << "failed to parse line: " << line << endl;
            }
        }

        // find index of "main" index which can be used to terminate backtraces
        // and prevent printing stuff above main, which is usually uninteresting
        auto it = find(strings.begin(), strings.end(), "main");
        if (it != strings.end()) {
            mainIndex = distance(strings.begin(), it);
        }

        /// these are leaks, but we have the same data in \c allocations as well
        activeAllocations.clear();
        return true;
    }

    bool shortenTemplates = false;

    vector<Allocation> allocations;
    map<size_t, size_t> sizeHistogram;
    size_t totalAllocated = 0;
    size_t totalAllocations = 0;
    size_t peak = 0;
    size_t leaked = 0;

private:
    InstructionPointer findIp(const size_t ipIndex) const
    {
        if (!ipIndex || ipIndex > instructionPointers.size()) {
            return {};
        } else {
            return instructionPointers[ipIndex - 1];
        }
    }

    size_t mainIndex = 0;
    unordered_map<uintptr_t, AllocationInfo> activeAllocations;
    vector<InstructionPointer> instructionPointers;
    vector<string> strings;
};

}

int main(int argc, char** argv)
{
    po::options_description desc("Options");
    desc.add_options()
        ("file,f", po::value<string>()->required(), "The heaptrack data file to print.")
        ("shorten-templates,t", po::value<bool>()->default_value(true), "Shorten template identifiers.")
        ("print-peaks,p", po::value<bool>()->default_value(true), "Print backtraces to top allocators, sorted by peak consumption.")
        ("print-allocators,a", po::value<bool>()->default_value(true), "Print backtraces to top allocators, sorted by number of calls to allocation functions.")
        ("print-leaks,l", po::value<bool>()->default_value(false), "Print backtraces to leaked memory allocations.")
        ("print-histogram,H", po::value<bool>()->default_value(false), "Print allocation size histogram.")
        ("print-overall-allocated,o", po::value<bool>()->default_value(false), "Print top overall allocators, ignoring memory frees.")
        ("help,h", "Show this help message.");
    po::positional_options_description p;
    p.add("file", -1);

    po::variables_map vm;
    try {
        po::store(po::command_line_parser(argc, argv)
                    .options(desc).positional(p).run(), vm);
        if (vm.count("help")) {
            cout << "heaptrack_print - analyze heaptrack data files.\n"
                << "\n"
                << "heaptrack is a heap memory profiler which records information\n"
                << "about calls to heap allocation functions such as malloc, operator new etc. pp.\n"
                << "This print utility can then be used to analyze the generated data files.\n\n"
                << desc << endl;
            return 1;
        }
        po::notify(vm);
    } catch (const po::error& error) {
        cerr << "ERROR: " << error.what() << endl
             << endl << desc << endl;
        return 1;
    }

    AccumulatedTraceData data;

    // optimize: we only have a single thread
    ios_base::sync_with_stdio(false);

    const auto inputFile = vm["file"].as<string>();
    data.shortenTemplates = vm["shorten-templates"].as<bool>();
    const bool printHistogram = vm["print-histogram"].as<bool>();
    const bool printLeaks = vm["print-leaks"].as<bool>();
    const bool printOverallAlloc = vm["print-overall-allocated"].as<bool>();
    const bool printPeaks = vm["print-peaks"].as<bool>();
    const bool printAllocs = vm["print-allocators"].as<bool>();

    string fileName(inputFile);
    const bool isCompressed = boost::algorithm::ends_with(fileName, ".gz");
    ifstream file(fileName, isCompressed ? ios_base::in | ios_base::binary : ios_base::in);

    if (!file.is_open()) {
        cerr << "Failed to open heaptrack log file: " << inputFile << endl
             << endl << desc << endl;
        return 1;
    }

    boost::iostreams::filtering_istream in;
    if (isCompressed) {
        in.push(boost::iostreams::gzip_decompressor());
    }
    in.push(file);

    cout << "reading file \"" << fileName << "\" - please wait, this might take some time...";
    cout.flush();

    if (!data.read(in)) {
        return 1;
    }

    cout << "finished reading file, now analyzing data:\n" << endl;

    if (printAllocs) {
        // sort by amount of allocations
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.allocations > r.allocations;
        });
        cout << "MOST CALLS TO ALLOCATION FUNCTIONS\n";
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.allocations << " allocations at:\n";
            data.printBacktrace(allocation.ipIndex, cout);
            cout << '\n';
        }
        cout << endl;
    }

    if (printOverallAlloc) {
        // sort by amount of bytes allocated
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.allocated > r.allocated;
        });
        cout << "MOST BYTES ALLOCATED OVER TIME (ignoring deallocations)\n";
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.allocated << " bytes allocated at:\n";
            data.printBacktrace(allocation.ipIndex, cout);
            cout << '\n';
        }
        cout << endl;
    }

    if (printPeaks) {
        // sort by peak memory consumption
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.peak > r.peak;
        });
        cout << "PEAK MEMORY CONSUMERS\n";
        for (size_t i = 0; i < min(10lu, data.allocations.size()); ++i) {
            const auto& allocation = data.allocations[i];
            cout << allocation.peak << " bytes allocated at peak:\n";
            data.printBacktrace(allocation.ipIndex, cout);
            cout << '\n';
        }
        cout << endl;
    }

    if (printLeaks) {
        // sort by amount of leaks
        sort(data.allocations.begin(), data.allocations.end(), [] (const Allocation& l, const Allocation &r) {
            return l.leaked < r.leaked;
        });

        size_t totalLeakAllocations = 0;
        cout << "MEMORY LEAKS\n";
        for (const auto& allocation : data.allocations) {
            if (!allocation.leaked) {
                continue;
            }
            totalLeakAllocations += allocation.allocations;

            cout << allocation.leaked << " bytes leaked in " << allocation.allocations << " allocations at:\n";
            data.printBacktrace(allocation.ipIndex, cout);
            cout << '\n';
        }
        cout << data.leaked << " bytes leaked in total from " << totalLeakAllocations << " allocations" << endl;
    }

    cout << data.totalAllocated << " bytes allocated in total over " << data.totalAllocations
         << " allocations, peak consumption: " << data.peak << " bytes\n\n";

    if (printHistogram) {
        cout << "size histogram: " << endl;
        for (auto entry : data.sizeHistogram) {
            cout << entry.first << "\t" << entry.second << endl;
        }
    }

    return 0;
}
<|endoftext|>