<commit_before>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2022 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwo/volume/algorithm/volumemap.h>

#include <algorithm>
#include <unordered_map>
#include <inviwo/core/util/exception.h>

namespace inviwo {

void remap(std::shared_ptr<Volume>& volume, std::vector<int> src, std::vector<int> dst,
           int missingValue, bool useMissingValue) {

    if (src.size() == 0 || src.size() != dst.size()) {
        throw Exception(IVW_CONTEXT_CUSTOM("Remap"), "Invalid dataframe size (src = {}, dst = {})",
                        src.size(), dst.size());
    }

    auto uniqueIterator = std::unique(src.begin(), src.end());
    if (uniqueIterator != src.end()) {
        throw Exception(IVW_CONTEXT_CUSTOM("Remap"), "Duplicate elements in source row (numberOfDuplicates = {})",
                        src.size() - std::distance(src.begin(), uniqueIterator));
    }

    auto volRep = volume->getEditableRepresentation<VolumeRAM>();

    volRep->dispatch<void, dispatching::filter::Scalars>([&](auto volram) {
        using ValueType = util::PrecisionValueType<decltype(volram)>;
        ValueType* dataPtr = volram->getDataTyped();
        const auto& dim = volram->getDimensions();

        // Check state of dataframe
        bool sorted = std::is_sorted(src.begin(), src.end());

        if (sorted && (src.back() - src.front()) ==
                          static_cast<int>(src.size()) - 1) {  // Sorted + continuous
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                // Voxel value is inside src range
                if (static_cast<int>(v) >= src.front() && static_cast<int>(v) <= src.back()) {
                    int index = static_cast<int>(v) - src.front();
                    return static_cast<ValueType>(dst[index]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        } else if (sorted) {  // Sorted + non continuous
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                auto index = std::distance(
                    src.begin(), std::lower_bound(src.begin(), src.end(), static_cast<int>(v)));
                if (index < static_cast<int>(src.size())) {
                    return static_cast<ValueType>(dst[index]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        } else {
            std::unordered_map<int, int> unorderedIndexMap;
            for (size_t i = 0; i < src.size(); ++i) {
                unorderedIndexMap[src[i]] = dst[i];
            }
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                if (unorderedIndexMap.count(static_cast<int>(v)) == 1) {
                    return static_cast<ValueType>(unorderedIndexMap[static_cast<int>(v)]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        }
    });
}
}  // namespace inviwo
<commit_msg>Jenkins: Format fixes<commit_after>/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2022 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#include <inviwo/volume/algorithm/volumemap.h>

#include <algorithm>
#include <unordered_map>
#include <inviwo/core/util/exception.h>

namespace inviwo {

void remap(std::shared_ptr<Volume>& volume, std::vector<int> src, std::vector<int> dst,
           int missingValue, bool useMissingValue) {

    if (src.size() == 0 || src.size() != dst.size()) {
        throw Exception(IVW_CONTEXT_CUSTOM("Remap"), "Invalid dataframe size (src = {}, dst = {})",
                        src.size(), dst.size());
    }

    auto uniqueIterator = std::unique(src.begin(), src.end());
    if (uniqueIterator != src.end()) {
        throw Exception(IVW_CONTEXT_CUSTOM("Remap"),
                        "Duplicate elements in source row (numberOfDuplicates = {})",
                        src.size() - std::distance(src.begin(), uniqueIterator));
    }

    auto volRep = volume->getEditableRepresentation<VolumeRAM>();

    volRep->dispatch<void, dispatching::filter::Scalars>([&](auto volram) {
        using ValueType = util::PrecisionValueType<decltype(volram)>;
        ValueType* dataPtr = volram->getDataTyped();
        const auto& dim = volram->getDimensions();

        // Check state of dataframe
        bool sorted = std::is_sorted(src.begin(), src.end());

        if (sorted && (src.back() - src.front()) ==
                          static_cast<int>(src.size()) - 1) {  // Sorted + continuous
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                // Voxel value is inside src range
                if (static_cast<int>(v) >= src.front() && static_cast<int>(v) <= src.back()) {
                    int index = static_cast<int>(v) - src.front();
                    return static_cast<ValueType>(dst[index]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        } else if (sorted) {  // Sorted + non continuous
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                auto index = std::distance(
                    src.begin(), std::lower_bound(src.begin(), src.end(), static_cast<int>(v)));
                if (index < static_cast<int>(src.size())) {
                    return static_cast<ValueType>(dst[index]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        } else {
            std::unordered_map<int, int> unorderedIndexMap;
            for (size_t i = 0; i < src.size(); ++i) {
                unorderedIndexMap[src[i]] = dst[i];
            }
            std::transform(dataPtr, dataPtr + glm::compMul(dim), dataPtr, [&](const ValueType& v) {
                if (unorderedIndexMap.count(static_cast<int>(v)) == 1) {
                    return static_cast<ValueType>(unorderedIndexMap[static_cast<int>(v)]);
                } else if (useMissingValue) {
                    return static_cast<ValueType>(missingValue);
                } else {
                    return v;
                }
            });
        }
    });
}
}  // namespace inviwo
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// This file contains the WebRTC suppressions for ThreadSanitizer.
// Please refer to
// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for more info.

#if defined(THREAD_SANITIZER)

// Please make sure the code below declares a single string variable
// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines.
// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for the instructions on writing suppressions.
char kTSanDefaultSuppressions[] =

// WebRTC specific suppressions.

// Split up suppressions covered previously by thread.cc and messagequeue.cc.
"race:rtc::MessageQueue::Quit\n"
"race:FileVideoCapturerTest::VideoCapturerListener::OnFrameCaptured\n"
"race:vp8cx_remove_encoder_threads\n"
"race:third_party/libvpx/source/libvpx/vp9/common/vp9_scan.h\n"

// Usage of trace callback and trace level is racy in libjingle_media_unittests.
// https://code.google.com/p/webrtc/issues/detail?id=3372
"race:webrtc::TraceImpl::WriteToFile\n"
"race:webrtc::VideoEngine::SetTraceFilter\n"
"race:webrtc::VoiceEngine::SetTraceFilter\n"
"race:webrtc::Trace::set_level_filter\n"
"race:webrtc::GetStaticInstance<webrtc::TraceImpl>\n"

// Audio processing
// https://code.google.com/p/webrtc/issues/detail?id=2521 for details.
"race:webrtc/modules/audio_processing/aec/aec_core.c\n"
"race:webrtc/modules/audio_processing/aec/aec_rdft.c\n"

// rtc_unittest
// https://code.google.com/p/webrtc/issues/detail?id=3911 for details.
"race:rtc::FireAndForgetAsyncClosure<FunctorB>::Execute\n"
"race:rtc::MessageQueueManager::Clear\n"
"race:rtc::Thread::Clear\n"
// https://code.google.com/p/webrtc/issues/detail?id=2080
"race:webrtc/base/logging.cc\n"
"race:webrtc/base/sharedexclusivelock_unittest.cc\n"
"race:webrtc/base/signalthread_unittest.cc\n"

// libjingle_p2p_unittest
// https://code.google.com/p/webrtc/issues/detail?id=2079
"race:webrtc/base/testclient.cc\n"
"race:webrtc/base/virtualsocketserver.cc\n"
"race:talk/p2p/base/stunserver_unittest.cc\n"

// third_party/usrsctp
// TODO(jiayl): https://code.google.com/p/webrtc/issues/detail?id=3492
"race:user_sctp_timer_iterate\n"

// Potential deadlocks detected after roll in r6516.
// https://code.google.com/p/webrtc/issues/detail?id=3509
"deadlock:webrtc::ProcessThreadImpl::RegisterModule\n"
"deadlock:webrtc::RTCPReceiver::SetSsrcs\n"
"deadlock:webrtc::test::UdpSocketManagerPosixImpl::RemoveSocket\n"
"deadlock:webrtc::vcm::VideoReceiver::RegisterPacketRequestCallback\n"
"deadlock:webrtc::ViECaptureImpl::ConnectCaptureDevice\n"
"deadlock:webrtc::ViEChannel::StartSend\n"
"deadlock:webrtc::ViECodecImpl::GetSendSideDelay\n"
"deadlock:webrtc::ViEEncoder::OnLocalSsrcChanged\n"
"deadlock:webrtc::ViESender::RegisterSendTransport\n"

// third_party/libvpx race introduced in 33bbffe..080710f range.
// TODO(kjellander): https://code.google.com/p/webm/issues/detail?id=962
"race:vp9_reconintra.c\n"

// TODO(pbos): Trace events are racy due to lack of proper POD atomics.
// https://code.google.com/p/webrtc/issues/detail?id=2497
"race:*trace_event_unique_catstatic*\n"

// End of suppressions.
;  // Please keep this semicolon.

#endif  // THREAD_SANITIZER
<commit_msg>Remove libvpx suppression due to fixed bug.<commit_after>/*
 *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

// This file contains the WebRTC suppressions for ThreadSanitizer.
// Please refer to
// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for more info.

#if defined(THREAD_SANITIZER)

// Please make sure the code below declares a single string variable
// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines.
// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
// for the instructions on writing suppressions.
char kTSanDefaultSuppressions[] =

// WebRTC specific suppressions.

// Split up suppressions covered previously by thread.cc and messagequeue.cc.
"race:rtc::MessageQueue::Quit\n"
"race:FileVideoCapturerTest::VideoCapturerListener::OnFrameCaptured\n"
"race:vp8cx_remove_encoder_threads\n"
"race:third_party/libvpx/source/libvpx/vp9/common/vp9_scan.h\n"

// Usage of trace callback and trace level is racy in libjingle_media_unittests.
// https://code.google.com/p/webrtc/issues/detail?id=3372
"race:webrtc::TraceImpl::WriteToFile\n"
"race:webrtc::VideoEngine::SetTraceFilter\n"
"race:webrtc::VoiceEngine::SetTraceFilter\n"
"race:webrtc::Trace::set_level_filter\n"
"race:webrtc::GetStaticInstance<webrtc::TraceImpl>\n"

// Audio processing
// https://code.google.com/p/webrtc/issues/detail?id=2521 for details.
"race:webrtc/modules/audio_processing/aec/aec_core.c\n"
"race:webrtc/modules/audio_processing/aec/aec_rdft.c\n"

// rtc_unittest
// https://code.google.com/p/webrtc/issues/detail?id=3911 for details.
"race:rtc::FireAndForgetAsyncClosure<FunctorB>::Execute\n"
"race:rtc::MessageQueueManager::Clear\n"
"race:rtc::Thread::Clear\n"
// https://code.google.com/p/webrtc/issues/detail?id=2080
"race:webrtc/base/logging.cc\n"
"race:webrtc/base/sharedexclusivelock_unittest.cc\n"
"race:webrtc/base/signalthread_unittest.cc\n"

// libjingle_p2p_unittest
// https://code.google.com/p/webrtc/issues/detail?id=2079
"race:webrtc/base/testclient.cc\n"
"race:webrtc/base/virtualsocketserver.cc\n"
"race:talk/p2p/base/stunserver_unittest.cc\n"

// third_party/usrsctp
// TODO(jiayl): https://code.google.com/p/webrtc/issues/detail?id=3492
"race:user_sctp_timer_iterate\n"

// Potential deadlocks detected after roll in r6516.
// https://code.google.com/p/webrtc/issues/detail?id=3509
"deadlock:webrtc::ProcessThreadImpl::RegisterModule\n"
"deadlock:webrtc::RTCPReceiver::SetSsrcs\n"
"deadlock:webrtc::test::UdpSocketManagerPosixImpl::RemoveSocket\n"
"deadlock:webrtc::vcm::VideoReceiver::RegisterPacketRequestCallback\n"
"deadlock:webrtc::ViECaptureImpl::ConnectCaptureDevice\n"
"deadlock:webrtc::ViEChannel::StartSend\n"
"deadlock:webrtc::ViECodecImpl::GetSendSideDelay\n"
"deadlock:webrtc::ViEEncoder::OnLocalSsrcChanged\n"
"deadlock:webrtc::ViESender::RegisterSendTransport\n"

// TODO(pbos): Trace events are racy due to lack of proper POD atomics.
// https://code.google.com/p/webrtc/issues/detail?id=2497
"race:*trace_event_unique_catstatic*\n"

// End of suppressions.
;  // Please keep this semicolon.

#endif  // THREAD_SANITIZER
<|endoftext|>


<commit_before>#ifndef GRAPHHELPER_H
#define GRAPHHELPER_H
#include <string>
#include <algorithm>
#include <functional>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <boost/graph/graphviz.hpp>
#include "vvvstlhelper.hpp"

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
        getAdjacentVertices(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor> ret;
    const auto ir = boost::adjacent_vertices( index, g);
    std::copy( ir.first, ir.second, std::back_inserter(ret) );
    return ret;
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
        getPointingVertices(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    using edge_t = typename boost::graph_traits<GRAPH>::edge_descriptor;
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;

    std::vector<vertex_t> ret;
    const auto es = boost::edges(g);
    std::for_each( es.first, es.second, [&ret,&index](edge_t e)
                   { if(e.m_target == index) ret.push_back(e.m_source); });

    return ret;
}

template<class GRAPH>
inline bool isVertexTerminal(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v)
{
    const auto a = boost::adjacent_vertices( v, g );
    return (std::distance(a.first, a.second) == 0) ? true:
                                                     false;
}

template<class GRAPH>
inline bool isVertexHasPointer(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v)
{
    using edge_t = typename boost::graph_traits<GRAPH>::edge_descriptor;
    const auto es = boost::edges(g);
    return std::any_of( es.first, es.second,
                        [&g,&v](edge_t e){ return e.m_target == v; } );
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
getStartingVertices(const GRAPH& g)
{
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    std::vector<vertex_t> ret;
    const auto vlist = boost::vertices( g );
    std::copy_if( vlist.first, vlist.second,  //from
                  std::back_inserter(ret),    //to
                  [&g](vertex_t v){ return !isVertexHasPointer(g,v);}); //condition

    return ret;
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
getTerminalVertices(const GRAPH& g)
{
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    std::vector<vertex_t> ret;
    const auto vlist = boost::vertices( g );
    std::copy_if( vlist.first, vlist.second, // from
                  std::back_inserter( ret ), // to
                  [&g](vertex_t v){ return isVertexTerminal(g,v);}); // condition
    return ret;
}

template<class GRAPH>
inline typename boost::graph_traits<GRAPH>::vertex_descriptor
insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor newVertex )
{
    const typename boost::graph_traits<GRAPH>::vertex_descriptor ret = newVertex;
    boost::remove_edge( first, second, g);
    boost::add_edge( first, ret,  g);
    boost::add_edge( ret, second, g);
    return ret;
}

template<class GRAPH>
inline typename boost::graph_traits<GRAPH>::vertex_descriptor
insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second )
{
    return insertBetween( g, first, second, add_vertex(g) );
}

template<class GRAPH>
inline void connectMto1(GRAPH& g,
                        const std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>& src,
                        typename boost::graph_traits<GRAPH>::vertex_descriptor dst )
{
    for(auto v: src)
        boost::add_edge(v, dst, g);
}

template<class GRAPH>
inline void insertGraphInsteadOf( GRAPH& dst, const GRAPH& src,
                                  typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    const auto dstSends     = getPointingVertices( dst, index );
    const auto dstReturns   = getAdjacentVertices( dst, index );
    const auto dstVertices  = boost::vertices(dst);
    const auto srcToDstOffset =
                 std::distance( dstVertices.first, dstVertices.second );

    auto srcInputVertices  = getStartingVertices( src );
    auto srcOutputVertices = getTerminalVertices( src );

    boost::copy_graph(src, dst);

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto& v: srcOutputVertices ) // map indeces from src to dst
        v += srcToDstOffset;

    for( auto t: srcInputVertices )   // connect each Send to each Input
        connectMto1( dst, dstSends, t);

    for( auto t: dstReturns )   // connect each Output to each Return
        connectMto1( dst, srcOutputVertices, t );

    for( auto t: dstSends)
        boost::remove_edge( t, index, dst );

    boost::remove_vertex( index, dst );
}

template<class GRAPH>
inline void attachGraph( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor to,
                         GRAPH& newGraph)
{
    auto srcInputVertices     = getStartingVertices( newGraph );
    const auto srcToDstOffset = boost::num_vertices( g );

    boost::copy_graph( newGraph, g );

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto t: srcInputVertices )
        boost::add_edge(to, t, g);
}

template<class GRAPH>
inline void insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second,
                         const GRAPH& newGraph )
{
    auto srcInputVertices  = getStartingVertices( newGraph );
    auto srcOutputVertices = getTerminalVertices( newGraph );
    const auto dstVertices  = boost::vertices(g);
    const auto srcToDstOffset = std::distance( dstVertices.first, dstVertices.second );

    boost::remove_edge( first, second, g );
    boost::copy_graph( newGraph, g );

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto& v: srcOutputVertices ) // map indeces from src to dst
        v += srcToDstOffset;

    for(auto t: srcInputVertices )
        boost::add_edge( first, t, g);

    for(auto t: srcOutputVertices )
        boost::add_edge( t, second, g);
}

template<class GRAPH>
inline void insertAfter( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         const GRAPH& newGraph )
{
    const auto nextVertices = getAdjacentVertices( g, first );
    switch(nextVertices.size()) {
        case  0: attachGraph( g, first, newGraph );                     break;
        case  1: insertBetween( g, first, nextVertices[0], newGraph );  break;
        default: throw std::logic_error("inserAfter can be performed on vertices with only adjacent vertex\n"
                                        "                         or on vertices without adjacent vertices"); }
}

template<class GRAPH>
inline void reverseOutEdgesOrder( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v )
{
    using vertex_t      = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    using edge_t        = typename boost::graph_traits<GRAPH>::edge_descriptor;
    using edgeprop_t    = typename GRAPH::edge_property_type;
    using edgearray_type= std::vector< std::tuple<vertex_t, vertex_t, edgeprop_t>>;
    const auto edges = boost::out_edges( v, g );
    const auto begin = edges.first;
    const auto end   = edges.second;

    edgearray_type edgearray;
    std::for_each( begin, end, [&edgearray, &g](const edge_t e)  // Store edge information (source, target, data)
                               { edgearray.push_back( std::make_tuple( e.m_source, e.m_target, g[e] ) ); }  );

    for( const auto& e : edgearray )    // Remove edges
        boost::remove_edge( std::get<0>(e), std::get<1>(e), g);

    // Reconnect stored edges in reversed order (rbegin, rend)
    std::for_each( edgearray.rbegin(), edgearray.rend(), [&g, &edgearray](const typename edgearray_type::value_type& e ) {
                                                         auto newEdge = boost::add_edge( std::get<0>(e), std::get<1>(e), g).first;
                                                         g[ newEdge ] = std::get<2>(e); } );
}

#endif

<commit_msg>fix insertBetween<commit_after>#ifndef GRAPHHELPER_H
#define GRAPHHELPER_H
#include <string>
#include <algorithm>
#include <functional>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <boost/graph/graphviz.hpp>
#include "vvvstlhelper.hpp"

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
        getAdjacentVertices(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor> ret;
    const auto ir = boost::adjacent_vertices( index, g);
    std::copy( ir.first, ir.second, std::back_inserter(ret) );
    return ret;
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
        getPointingVertices(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    using edge_t = typename boost::graph_traits<GRAPH>::edge_descriptor;
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;

    std::vector<vertex_t> ret;
    const auto es = boost::edges(g);
    std::for_each( es.first, es.second, [&ret,&index](edge_t e)
                   { if(e.m_target == index) ret.push_back(e.m_source); });

    return ret;
}

template<class GRAPH>
inline bool isVertexTerminal(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v)
{
    const auto a = boost::adjacent_vertices( v, g );
    return (std::distance(a.first, a.second) == 0) ? true:
                                                     false;
}

template<class GRAPH>
inline bool isVertexHasPointer(const GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v)
{
    using edge_t = typename boost::graph_traits<GRAPH>::edge_descriptor;
    const auto es = boost::edges(g);
    return std::any_of( es.first, es.second,
                        [&g,&v](edge_t e){ return e.m_target == v; } );
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
getStartingVertices(const GRAPH& g)
{
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    std::vector<vertex_t> ret;
    const auto vlist = boost::vertices( g );
    std::copy_if( vlist.first, vlist.second,  //from
                  std::back_inserter(ret),    //to
                  [&g](vertex_t v){ return !isVertexHasPointer(g,v);}); //condition

    return ret;
}

template<class GRAPH>
inline std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>
getTerminalVertices(const GRAPH& g)
{
    using vertex_t = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    std::vector<vertex_t> ret;
    const auto vlist = boost::vertices( g );
    std::copy_if( vlist.first, vlist.second, // from
                  std::back_inserter( ret ), // to
                  [&g](vertex_t v){ return isVertexTerminal(g,v);}); // condition
    return ret;
}

template<class GRAPH>
inline typename boost::graph_traits<GRAPH>::vertex_descriptor
insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor newVertex )
{
    const typename boost::graph_traits<GRAPH>::vertex_descriptor ret = newVertex;
    boost::remove_edge( first, second, g);
    boost::add_edge( first, ret,  g);
    boost::add_edge( ret, second, g);
    return ret;
}

template<class GRAPH>
inline typename boost::graph_traits<GRAPH>::vertex_descriptor
insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second )
{
    return insertBetween( g, first, second, add_vertex(g) );
}

template<class GRAPH>
inline void connectMto1(GRAPH& g,
                        const std::vector<typename boost::graph_traits<GRAPH>::vertex_descriptor>& src,
                        typename boost::graph_traits<GRAPH>::vertex_descriptor dst )
{
    for(auto v: src)
        boost::add_edge(v, dst, g);
}

template<class GRAPH>
inline void insertGraphInsteadOf( GRAPH& dst, const GRAPH& src,
                                  typename boost::graph_traits<GRAPH>::vertex_descriptor index)
{
    const auto dstSends     = getPointingVertices( dst, index );
    const auto dstReturns   = getAdjacentVertices( dst, index );
    const auto dstVertices  = boost::vertices(dst);
    const auto srcToDstOffset =
                 std::distance( dstVertices.first, dstVertices.second );

    auto srcInputVertices  = getStartingVertices( src );
    auto srcOutputVertices = getTerminalVertices( src );

    boost::copy_graph(src, dst);

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto& v: srcOutputVertices ) // map indeces from src to dst
        v += srcToDstOffset;

    for( auto t: srcInputVertices )   // connect each Send to each Input
        connectMto1( dst, dstSends, t);

    for( auto t: dstReturns )   // connect each Output to each Return
        connectMto1( dst, srcOutputVertices, t );

    for( auto t: dstSends)
        boost::remove_edge( t, index, dst );

    boost::remove_vertex( index, dst );
}

template<class GRAPH>
inline void attachGraph( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor to,
                         const GRAPH& newGraph)
{
    auto srcInputVertices     = getStartingVertices( newGraph );
    const auto srcToDstOffset = boost::num_vertices( g );

    boost::copy_graph( newGraph, g );

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto t: srcInputVertices )
        boost::add_edge(to, t, g);
}

template<class GRAPH>
inline void insertBetween( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         typename boost::graph_traits<GRAPH>::vertex_descriptor second,
                         const GRAPH& newGraph )
{
    auto srcInputVertices  = getStartingVertices( newGraph );
    auto srcOutputVertices = getTerminalVertices( newGraph );
    const auto dstVertices  = boost::vertices(g);
    const auto srcToDstOffset = std::distance( dstVertices.first, dstVertices.second );

    boost::remove_edge( first, second, g );
    boost::copy_graph( newGraph, g );

    for(auto& v: srcInputVertices )  // map indeces from src to dst
        v += srcToDstOffset;

    for(auto& v: srcOutputVertices ) // map indeces from src to dst
        v += srcToDstOffset;

    for(auto t: srcInputVertices )
        boost::add_edge( first, t, g);

    for(auto t: srcOutputVertices )
        boost::add_edge( t, second, g);
}

template<class GRAPH>
inline void insertAfter( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor first,
                         const GRAPH& newGraph )
{
    const auto nextVertices = getAdjacentVertices( g, first );
    switch(nextVertices.size()) {
        case  0: attachGraph( g, first, newGraph );                     break;
        case  1: insertBetween( g, first, nextVertices[0], newGraph );  break;
        default: throw std::logic_error("inserAfter can be performed on vertices with only adjacent vertex\n"
                                        "                         or on vertices without adjacent vertices"); }
}

template<class GRAPH>
inline void reverseOutEdgesOrder( GRAPH& g, typename boost::graph_traits<GRAPH>::vertex_descriptor v )
{
    using vertex_t      = typename boost::graph_traits<GRAPH>::vertex_descriptor;
    using edge_t        = typename boost::graph_traits<GRAPH>::edge_descriptor;
    using edgeprop_t    = typename GRAPH::edge_property_type;
    using edgearray_type= std::vector< std::tuple<vertex_t, vertex_t, edgeprop_t>>;
    const auto edges = boost::out_edges( v, g );
    const auto begin = edges.first;
    const auto end   = edges.second;

    edgearray_type edgearray;
    std::for_each( begin, end, [&edgearray, &g](const edge_t e)  // Store edge information (source, target, data)
                               { edgearray.push_back( std::make_tuple( e.m_source, e.m_target, g[e] ) ); }  );

    for( const auto& e : edgearray )    // Remove edges
        boost::remove_edge( std::get<0>(e), std::get<1>(e), g);

    // Reconnect stored edges in reversed order (rbegin, rend)
    std::for_each( edgearray.rbegin(), edgearray.rend(), [&g, &edgearray](const typename edgearray_type::value_type& e ) {
                                                         auto newEdge = boost::add_edge( std::get<0>(e), std::get<1>(e), g).first;
                                                         g[ newEdge ] = std::get<2>(e); } );
}

#endif

<|endoftext|>


<commit_before>
/*
 * Copyright 2009 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorTable.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkStream.h"
#include "SkTemplates.h"

// As copy constructor is hidden in the class hierarchy, we need to call
// default constructor explicitly to suppress a compiler warning.
SkColorTable::SkColorTable(const SkColorTable& src) : INHERITED() {
    f16BitCache = NULL;
    fAlphaType = src.fAlphaType;
    int count = src.count();
    fCount = SkToU16(count);
    fColors = reinterpret_cast<SkPMColor*>(
                                    sk_malloc_throw(count * sizeof(SkPMColor)));
    memcpy(fColors, src.fColors, count * sizeof(SkPMColor));

    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)
}

SkColorTable::SkColorTable(const SkPMColor colors[], int count, SkAlphaType at)
    : f16BitCache(NULL), fAlphaType(SkToU8(at))
{
    SkASSERT(0 == count || colors);

    if (count < 0) {
        count = 0;
    } else if (count > 256) {
        count = 256;
    }

    fCount = SkToU16(count);
    fColors = reinterpret_cast<SkPMColor*>(
                                    sk_malloc_throw(count * sizeof(SkPMColor)));

    memcpy(fColors, colors, count * sizeof(SkPMColor));

    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)
}

SkColorTable::~SkColorTable()
{
    SkASSERT(fColorLockCount == 0);
    SkASSERT(f16BitCacheLockCount == 0);

    sk_free(fColors);
    sk_free(f16BitCache);
}

void SkColorTable::unlockColors() {
    SkASSERT(fColorLockCount != 0);
    SkDEBUGCODE(sk_atomic_dec(&fColorLockCount);)
}

#include "SkColorPriv.h"

static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[],
                                    int count) {
    while (--count >= 0) {
        *dst++ = SkPixel32ToPixel16_ToU16(*src++);
    }
}

const uint16_t* SkColorTable::lock16BitCache() {
    if (this->isOpaque() && NULL == f16BitCache) {
        f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t));
        build_16bitcache(f16BitCache, fColors, fCount);
    }

    SkDEBUGCODE(f16BitCacheLockCount += 1);
    return f16BitCache;
}

///////////////////////////////////////////////////////////////////////////////

SkColorTable::SkColorTable(SkReadBuffer& buffer) {
    f16BitCache = NULL;
    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)

    fAlphaType = SkToU8(buffer.readUInt());
    fCount = buffer.getArrayCount();
    size_t allocSize = fCount * sizeof(SkPMColor);
    SkDEBUGCODE(bool success = false;)
    if (buffer.validateAvailable(allocSize)) {
        fColors = (SkPMColor*)sk_malloc_throw(allocSize);
        SkDEBUGCODE(success =) buffer.readColorArray(fColors, fCount);
    } else {
        fCount = 0;
        fColors = NULL;
    }
#ifdef SK_DEBUG
    SkASSERT((unsigned)fCount <= 256);
    SkASSERT(success);
#endif
}

void SkColorTable::writeToBuffer(SkWriteBuffer& buffer) const {
    buffer.writeUInt(fAlphaType);
    buffer.writeColorArray(fColors, fCount);
}
<commit_msg>Temp fix for crash when generating 16-bit cache for colorTables.<commit_after>
/*
 * Copyright 2009 The Android Open Source Project
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorTable.h"
#include "SkReadBuffer.h"
#include "SkWriteBuffer.h"
#include "SkStream.h"
#include "SkTemplates.h"

// As copy constructor is hidden in the class hierarchy, we need to call
// default constructor explicitly to suppress a compiler warning.
SkColorTable::SkColorTable(const SkColorTable& src) : INHERITED() {
    f16BitCache = NULL;
    fAlphaType = src.fAlphaType;
    int count = src.count();
    fCount = SkToU16(count);
    fColors = reinterpret_cast<SkPMColor*>(
                                    sk_malloc_throw(count * sizeof(SkPMColor)));
    memcpy(fColors, src.fColors, count * sizeof(SkPMColor));

    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)
}

SkColorTable::SkColorTable(const SkPMColor colors[], int count, SkAlphaType at)
    : f16BitCache(NULL), fAlphaType(SkToU8(at))
{
    SkASSERT(0 == count || colors);

    if (count < 0) {
        count = 0;
    } else if (count > 256) {
        count = 256;
    }

    fCount = SkToU16(count);
    fColors = reinterpret_cast<SkPMColor*>(
                                    sk_malloc_throw(count * sizeof(SkPMColor)));

    memcpy(fColors, colors, count * sizeof(SkPMColor));

    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)
}

SkColorTable::~SkColorTable()
{
    SkASSERT(fColorLockCount == 0);
    SkASSERT(f16BitCacheLockCount == 0);

    sk_free(fColors);
    sk_free(f16BitCache);
}

void SkColorTable::unlockColors() {
    SkASSERT(fColorLockCount != 0);
    SkDEBUGCODE(sk_atomic_dec(&fColorLockCount);)
}

#include "SkColorPriv.h"

static inline void build_16bitcache(uint16_t dst[], const SkPMColor src[],
                                    int count) {
    while (--count >= 0) {
        *dst++ = SkPixel32ToPixel16_ToU16(*src++);
    }
}

const uint16_t* SkColorTable::lock16BitCache() {
    // Assert that we're opaque, since our 16-bit cache will be sort of useless
    // if there is alpha (which we're throwing away)
    SkASSERT(this->isOpaque());
    if (NULL == f16BitCache) {
        f16BitCache = (uint16_t*)sk_malloc_throw(fCount * sizeof(uint16_t));
        build_16bitcache(f16BitCache, fColors, fCount);
    }

    SkDEBUGCODE(f16BitCacheLockCount += 1);
    return f16BitCache;
}

///////////////////////////////////////////////////////////////////////////////

SkColorTable::SkColorTable(SkReadBuffer& buffer) {
    f16BitCache = NULL;
    SkDEBUGCODE(fColorLockCount = 0;)
    SkDEBUGCODE(f16BitCacheLockCount = 0;)

    fAlphaType = SkToU8(buffer.readUInt());
    fCount = buffer.getArrayCount();
    size_t allocSize = fCount * sizeof(SkPMColor);
    SkDEBUGCODE(bool success = false;)
    if (buffer.validateAvailable(allocSize)) {
        fColors = (SkPMColor*)sk_malloc_throw(allocSize);
        SkDEBUGCODE(success =) buffer.readColorArray(fColors, fCount);
    } else {
        fCount = 0;
        fColors = NULL;
    }
#ifdef SK_DEBUG
    SkASSERT((unsigned)fCount <= 256);
    SkASSERT(success);
#endif
}

void SkColorTable::writeToBuffer(SkWriteBuffer& buffer) const {
    buffer.writeUInt(fAlphaType);
    buffer.writeColorArray(fColors, fCount);
}
<|endoftext|>


<commit_before>#include "commandqueue.h"
#include "context.h"
#include "deviceinterface.h"
#include "propertylist.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

using namespace Coal;

/*
 * CommandQueue
 */

CommandQueue::CommandQueue(Context *ctx,
                           DeviceInterface *device,
                           cl_command_queue_properties properties,
                           cl_int *errcode_ret)
: p_ctx(ctx), p_device(device), p_properties(properties), p_references(1)
{
    // Increment the reference count of the context
    // We begin by doing this to be able to unconditionnally release the context
    // in the destructor, being certain that the context was actually retained.
    clRetainContext((cl_context)ctx);

    // Initialize the locking machinery
    pthread_mutex_init(&p_event_list_mutex, 0);

    // Check that the device belongs to the context
    if (!ctx->hasDevice(device))
    {
        *errcode_ret = CL_INVALID_DEVICE;
        return;
    }

    *errcode_ret = checkProperties();
}

CommandQueue::~CommandQueue()
{
    // Free the mutex
    pthread_mutex_destroy(&p_event_list_mutex);

    // Release the parent context
    clReleaseContext((cl_context)p_ctx);
}

void CommandQueue::reference()
{
    p_references++;
}

bool CommandQueue::dereference()
{
    p_references--;
    return (p_references == 0);
}

cl_int CommandQueue::info(cl_context_info param_name,
                          size_t param_value_size,
                          void *param_value,
                          size_t *param_value_size_ret)
{
    void *value = 0;
    int value_length = 0;

    union {
        cl_uint cl_uint_var;
        cl_device_id cl_device_id_var;
        cl_context cl_context_var;
        cl_command_queue_properties cl_command_queue_properties_var;
    };

    switch (param_name)
    {
        case CL_QUEUE_CONTEXT:
            SIMPLE_ASSIGN(cl_context, p_ctx);
            break;

        case CL_QUEUE_DEVICE:
            SIMPLE_ASSIGN(cl_device_id, p_device);
            break;

        case CL_QUEUE_REFERENCE_COUNT:
            SIMPLE_ASSIGN(cl_uint, p_references);
            break;

        case CL_QUEUE_PROPERTIES:
            SIMPLE_ASSIGN(cl_command_queue_properties, p_properties);
            break;

        default:
            return CL_INVALID_VALUE;
    }

    if (param_value && param_value_size < value_length)
        return CL_INVALID_VALUE;

    if (param_value_size_ret)
        *param_value_size_ret = value_length;

    if (param_value)
        memcpy(param_value, value, value_length);

    return CL_SUCCESS;
}

cl_int CommandQueue::setProperty(cl_command_queue_properties properties,
                                 cl_bool enable,
                                 cl_command_queue_properties *old_properties)
{
    if (old_properties)
        *old_properties = p_properties;

    if (enable)
        p_properties |= properties;
    else
        p_properties &= ~properties;

    return checkProperties();
}

cl_int CommandQueue::checkProperties() const
{
    // Check that all the properties are valid
    cl_command_queue_properties properties =
        CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE |
        CL_QUEUE_PROFILING_ENABLE;

    if ((p_properties & properties) != p_properties)
        return CL_INVALID_VALUE;

    // Check that the device handles these properties
    cl_int result;

    result = p_device->info(CL_DEVICE_QUEUE_PROPERTIES,
                            sizeof(cl_command_queue_properties),
                            &properties,
                            0);

    if (result != CL_SUCCESS)
        return result;

    if ((p_properties & properties) != p_properties)
        return CL_INVALID_QUEUE_PROPERTIES;

    return CL_SUCCESS;
}

void CommandQueue::queueEvent(Event *event)
{
    // Append the event at the end of the list
    pthread_mutex_lock(&p_event_list_mutex);

    p_events.push_back(event);

    pthread_mutex_unlock(&p_event_list_mutex);

    // Explore the list for events we can push on the device
    pushEventsOnDevice();
}

void CommandQueue::cleanEvents()
{
    // NOTE: This function must only be called from the main thread
    pthread_mutex_lock(&p_event_list_mutex);

    std::list<Event *>::iterator it = p_events.begin();

    for (; it != p_events.end(); ++it)
    {
        Event *event = *it;

        if (event->status() == Event::Complete)
        {
            // We cannot be deleted from inside us
            event->setReleaseParent(false);

            p_events.erase(it);
            clReleaseEvent((cl_event)event);
        }
    }

    pthread_mutex_unlock(&p_event_list_mutex);

    // Check now if we have to be deleted
    if (p_references == 0)
        delete this;
}

void CommandQueue::pushEventsOnDevice()
{
    pthread_mutex_lock(&p_event_list_mutex);
    // Explore the events in p_events and push on the device all of them that
    // are :
    //
    // - Not already pushed (in Event::Queued state)
    // - Not after a barrier, except if we begin with a barrier
    // - If we are in-order, only the first event in Event::Queued state can
    //   be pushed

    std::list<Event *>::iterator it = p_events.begin();

    for (; it != p_events.end(); ++it)
    {
        // We cannot do out-of-order, so we can only push the first event.
        if ((p_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) == 0 &&
            it != p_events.begin())
            break;

        Event *event = *it;

        // If the event is completed, remove it
        if (event->status() == Event::Complete)
            continue;

        // If we encounter a barrier, check if it's the first in the list
        if (event->type() == Event::Barrier)
        {
            if (it == p_events.begin())
            {
                // Remove the barrier, we don't need it anymore
                p_events.erase(it);
                continue;
            }
            else
            {
                // We have events to wait, stop
                break;
            }
        }

        // If the event is not "pushable" (in Event::Queued state), skip it
        if (event->status() != Event::Queued)
            continue;

        // Check that all the waiting-on events of this event are finished
        cl_uint count;
        const Event **event_wait_list;

        event_wait_list = event->waitEvents(count);

        for (int i=0; i<count; ++i)
            if (event_wait_list[i]->status() != Event::Complete)
                continue;

        // The event can be pushed, if we need to
        if (!event->isDummy())
        {
            event->setStatus(Event::Submitted);
            p_device->pushEvent(event);
        }
        else
        {
            // Set the event as completed. This will call pushEventsOnDevice,
            // again, so release the lock to avoid a deadlock. We also return
            // because the recursive call will continue our work.
            pthread_mutex_unlock(&p_event_list_mutex);
            event->setStatus(Event::Complete);
            return;
        }
    }

    pthread_mutex_unlock(&p_event_list_mutex);
}

/*
 * Event
 */

Event::Event(CommandQueue *parent,
             cl_uint num_events_in_wait_list,
             const Event **event_wait_list,
             cl_int *errcode_ret)
: p_references(1), p_parent(parent),
  p_num_events_in_wait_list(num_events_in_wait_list), p_event_wait_list(0),
  p_device_data(0), p_status(Queued), p_release_parent(true)
{
    // Shared checks
    if (!parent)
    {
        *errcode_ret = CL_INVALID_COMMAND_QUEUE;
        return;
    }

    // Retain our parent
    clRetainCommandQueue((cl_command_queue)p_parent);

    // Initialize the locking machinery
    pthread_cond_init(&p_state_change_cond, 0);
    pthread_mutex_init(&p_state_mutex, 0);

    // Check sanity of parameters
    if (!event_wait_list && num_events_in_wait_list)
    {
        *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
        return;
    }

    if (event_wait_list && !num_events_in_wait_list)
    {
        *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
        return;
    }

    // Check that none of the events in event_wait_list is in an error state
    for (int i=0; i<num_events_in_wait_list; ++i)
    {
        if (event_wait_list[i] == 0)
        {
            *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
            return;
        }
        else if (event_wait_list[i]->status() < 0)
        {
            *errcode_ret = CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST;
            return;
        }
    }

    // Allocate a new buffer for the events to wait
    if (num_events_in_wait_list)
    {
        const unsigned int len = num_events_in_wait_list * sizeof(Event *);
        p_event_wait_list = (const Event **)malloc(len);

        if (!p_event_wait_list)
        {
            *errcode_ret = CL_OUT_OF_HOST_MEMORY;
            return;
        }

        memcpy((void *)p_event_wait_list, (void *)event_wait_list, len);
    }

    // Explore the events we are waiting on and reference them
    for (int i=0; i<num_events_in_wait_list; ++i)
        clRetainEvent((cl_event)event_wait_list[i]);
}

Event::~Event()
{
    if (p_event_wait_list)
        free((void *)p_event_wait_list);

    pthread_mutex_destroy(&p_state_mutex);
    pthread_cond_destroy(&p_state_change_cond);

    if (p_release_parent)
        clReleaseCommandQueue((cl_command_queue)p_parent);
    else
        p_parent->dereference();  // Dereference but don't delete
}

void Event::setReleaseParent(bool release)
{
    p_release_parent = release;
}

bool Event::isSingleShot() const
{
    // NDRangeKernel is a single event that can be executed on several execution
    // units. The other (buffer copying) must be executed in one part.

    return (type() != NDRangeKernel);
}

bool Event::isDummy() const
{
    // A dummy event has nothing to do on an execution device and must be
    // completed directly after being "submitted".

    switch (type())
    {
        case Marker:
        case User:
        case Barrier:
            return true;

        default:
            return false;
    }
}

void Event::reference()
{
    p_references++;
}

bool Event::dereference()
{
    p_references--;
    return (p_references == 0);
}

void Event::setStatus(EventStatus status)
{
    pthread_mutex_lock(&p_state_mutex);
    p_status = status;

    pthread_cond_broadcast(&p_state_change_cond);

    // Call the callbacks
    std::multimap<EventStatus, CallbackData>::const_iterator it;
    std::pair<std::multimap<EventStatus, CallbackData>::const_iterator,
              std::multimap<EventStatus, CallbackData>::const_iterator> ret;

    ret = p_callbacks.equal_range(status > 0 ? status : Complete);

    for (it=ret.first; it!=ret.second; ++it)
    {
        const CallbackData &data = (*it).second;
        data.callback((cl_event)this, p_status, data.user_data);
    }

    pthread_mutex_unlock(&p_state_mutex);

    // If the event is completed, inform our parent so it can push other events
    // to the device.
    if (status == Complete)
        p_parent->pushEventsOnDevice();
}

void Event::setDeviceData(void *data)
{
    p_device_data = data;
}

Event::EventStatus Event::status() const
{
    // HACK : We need const qualifier but we also need to lock a mutex
    Event *me = (Event *)(void *)this;

    pthread_mutex_lock(&me->p_state_mutex);

    EventStatus ret = p_status;

    pthread_mutex_unlock(&me->p_state_mutex);

    return ret;
}

void Event::waitForStatus(EventStatus status)
{
    pthread_mutex_lock(&p_state_mutex);

    while (p_status != status)
    {
        pthread_cond_wait(&p_state_change_cond, &p_state_mutex);
    }

    pthread_mutex_unlock(&p_state_mutex);
}

void *Event::deviceData()
{
    return p_device_data;
}

const Event **Event::waitEvents(cl_uint &count) const
{
    count = p_num_events_in_wait_list;
    return p_event_wait_list;
}

void Event::setCallback(cl_int command_exec_callback_type,
                        event_callback callback,
                        void *user_data)
{
    CallbackData data;

    data.callback = callback;
    data.user_data = user_data;

    pthread_mutex_lock(&p_state_mutex);

    p_callbacks.insert(std::pair<EventStatus, CallbackData>(
        (EventStatus)command_exec_callback_type,
        data));

    pthread_mutex_unlock(&p_state_mutex);
}

cl_int Event::info(cl_context_info param_name,
                   size_t param_value_size,
                   void *param_value,
                   size_t *param_value_size_ret)
{
    void *value = 0;
    int value_length = 0;

    union {
        cl_command_queue cl_command_queue_var;
        cl_context cl_context_var;
        cl_command_type cl_command_type_var;
        cl_int cl_int_var;
        cl_uint cl_uint_var;
    };

    switch (param_name)
    {
        case CL_EVENT_COMMAND_QUEUE:
            SIMPLE_ASSIGN(cl_command_queue, p_parent);
            break;

        case CL_EVENT_CONTEXT:
            // Tail call to CommandQueue
            return p_parent->info(CL_QUEUE_CONTEXT, param_value_size,
                                  param_value, param_value_size_ret);

        case CL_EVENT_COMMAND_TYPE:
            SIMPLE_ASSIGN(cl_command_type, type());
            break;

        case CL_EVENT_COMMAND_EXECUTION_STATUS:
            SIMPLE_ASSIGN(cl_int, status());
            break;

        case CL_EVENT_REFERENCE_COUNT:
            SIMPLE_ASSIGN(cl_uint, p_references);
            break;

        default:
            return CL_INVALID_VALUE;
    }

    if (param_value && param_value_size < value_length)
        return CL_INVALID_VALUE;

    if (param_value_size_ret)
        *param_value_size_ret = value_length;

    if (param_value)
        memcpy(param_value, value, value_length);

    return CL_SUCCESS;
}<commit_msg>Don't freeze the command queue<commit_after>#include "commandqueue.h"
#include "context.h"
#include "deviceinterface.h"
#include "propertylist.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

using namespace Coal;

/*
 * CommandQueue
 */

CommandQueue::CommandQueue(Context *ctx,
                           DeviceInterface *device,
                           cl_command_queue_properties properties,
                           cl_int *errcode_ret)
: p_ctx(ctx), p_device(device), p_properties(properties), p_references(1)
{
    // Increment the reference count of the context
    // We begin by doing this to be able to unconditionnally release the context
    // in the destructor, being certain that the context was actually retained.
    clRetainContext((cl_context)ctx);

    // Initialize the locking machinery
    pthread_mutex_init(&p_event_list_mutex, 0);

    // Check that the device belongs to the context
    if (!ctx->hasDevice(device))
    {
        *errcode_ret = CL_INVALID_DEVICE;
        return;
    }

    *errcode_ret = checkProperties();
}

CommandQueue::~CommandQueue()
{
    // Free the mutex
    pthread_mutex_destroy(&p_event_list_mutex);

    // Release the parent context
    clReleaseContext((cl_context)p_ctx);
}

void CommandQueue::reference()
{
    p_references++;
}

bool CommandQueue::dereference()
{
    p_references--;
    return (p_references == 0);
}

cl_int CommandQueue::info(cl_context_info param_name,
                          size_t param_value_size,
                          void *param_value,
                          size_t *param_value_size_ret)
{
    void *value = 0;
    int value_length = 0;

    union {
        cl_uint cl_uint_var;
        cl_device_id cl_device_id_var;
        cl_context cl_context_var;
        cl_command_queue_properties cl_command_queue_properties_var;
    };

    switch (param_name)
    {
        case CL_QUEUE_CONTEXT:
            SIMPLE_ASSIGN(cl_context, p_ctx);
            break;

        case CL_QUEUE_DEVICE:
            SIMPLE_ASSIGN(cl_device_id, p_device);
            break;

        case CL_QUEUE_REFERENCE_COUNT:
            SIMPLE_ASSIGN(cl_uint, p_references);
            break;

        case CL_QUEUE_PROPERTIES:
            SIMPLE_ASSIGN(cl_command_queue_properties, p_properties);
            break;

        default:
            return CL_INVALID_VALUE;
    }

    if (param_value && param_value_size < value_length)
        return CL_INVALID_VALUE;

    if (param_value_size_ret)
        *param_value_size_ret = value_length;

    if (param_value)
        memcpy(param_value, value, value_length);

    return CL_SUCCESS;
}

cl_int CommandQueue::setProperty(cl_command_queue_properties properties,
                                 cl_bool enable,
                                 cl_command_queue_properties *old_properties)
{
    if (old_properties)
        *old_properties = p_properties;

    if (enable)
        p_properties |= properties;
    else
        p_properties &= ~properties;

    return checkProperties();
}

cl_int CommandQueue::checkProperties() const
{
    // Check that all the properties are valid
    cl_command_queue_properties properties =
        CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE |
        CL_QUEUE_PROFILING_ENABLE;

    if ((p_properties & properties) != p_properties)
        return CL_INVALID_VALUE;

    // Check that the device handles these properties
    cl_int result;

    result = p_device->info(CL_DEVICE_QUEUE_PROPERTIES,
                            sizeof(cl_command_queue_properties),
                            &properties,
                            0);

    if (result != CL_SUCCESS)
        return result;

    if ((p_properties & properties) != p_properties)
        return CL_INVALID_QUEUE_PROPERTIES;

    return CL_SUCCESS;
}

void CommandQueue::queueEvent(Event *event)
{
    // Append the event at the end of the list
    pthread_mutex_lock(&p_event_list_mutex);

    p_events.push_back(event);

    pthread_mutex_unlock(&p_event_list_mutex);

    // Explore the list for events we can push on the device
    pushEventsOnDevice();
}

void CommandQueue::cleanEvents()
{
    // NOTE: This function must only be called from the main thread
    pthread_mutex_lock(&p_event_list_mutex);

    std::list<Event *>::iterator it = p_events.begin();

    for (; it != p_events.end(); ++it)
    {
        Event *event = *it;

        if (event->status() == Event::Complete)
        {
            // We cannot be deleted from inside us
            event->setReleaseParent(false);

            p_events.erase(it);
            clReleaseEvent((cl_event)event);
        }
    }

    pthread_mutex_unlock(&p_event_list_mutex);

    // Check now if we have to be deleted
    if (p_references == 0)
        delete this;
}

void CommandQueue::pushEventsOnDevice()
{
    pthread_mutex_lock(&p_event_list_mutex);
    // Explore the events in p_events and push on the device all of them that
    // are :
    //
    // - Not already pushed (in Event::Queued state)
    // - Not after a barrier, except if we begin with a barrier
    // - If we are in-order, only the first event in Event::Queued state can
    //   be pushed

    std::list<Event *>::iterator it = p_events.begin();
    bool first = true;

    for (; it != p_events.end(); ++it)
    {
        Event *event = *it;

        // If the event is completed, remove it
        if (event->status() == Event::Complete)
            continue;

        // We cannot do out-of-order, so we can only push the first event.
        if ((p_properties & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE) == 0 &&
            !first)
            break;

        // If we encounter a barrier, check if it's the first in the list
        if (event->type() == Event::Barrier)
        {
            if (first)
            {
                // Remove the barrier, we don't need it anymore
                p_events.erase(it);
                continue;
            }
            else
            {
                // We have events to wait, stop
                break;
            }
        }

        // Completed events and first barriers are out, it remains real events
        // that have to block in-order execution.
        first = false;

        // If the event is not "pushable" (in Event::Queued state), skip it
        if (event->status() != Event::Queued)
            continue;

        // Check that all the waiting-on events of this event are finished
        cl_uint count;
        const Event **event_wait_list;

        event_wait_list = event->waitEvents(count);

        for (int i=0; i<count; ++i)
            if (event_wait_list[i]->status() != Event::Complete)
                continue;

        // The event can be pushed, if we need to
        if (!event->isDummy())
        {
            event->setStatus(Event::Submitted);
            p_device->pushEvent(event);
        }
        else
        {
            // Set the event as completed. This will call pushEventsOnDevice,
            // again, so release the lock to avoid a deadlock. We also return
            // because the recursive call will continue our work.
            pthread_mutex_unlock(&p_event_list_mutex);
            event->setStatus(Event::Complete);
            return;
        }
    }

    pthread_mutex_unlock(&p_event_list_mutex);
}

/*
 * Event
 */

Event::Event(CommandQueue *parent,
             cl_uint num_events_in_wait_list,
             const Event **event_wait_list,
             cl_int *errcode_ret)
: p_references(1), p_parent(parent),
  p_num_events_in_wait_list(num_events_in_wait_list), p_event_wait_list(0),
  p_device_data(0), p_status(Queued), p_release_parent(true)
{
    // Shared checks
    if (!parent)
    {
        *errcode_ret = CL_INVALID_COMMAND_QUEUE;
        return;
    }

    // Retain our parent
    clRetainCommandQueue((cl_command_queue)p_parent);

    // Initialize the locking machinery
    pthread_cond_init(&p_state_change_cond, 0);
    pthread_mutex_init(&p_state_mutex, 0);

    // Check sanity of parameters
    if (!event_wait_list && num_events_in_wait_list)
    {
        *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
        return;
    }

    if (event_wait_list && !num_events_in_wait_list)
    {
        *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
        return;
    }

    // Check that none of the events in event_wait_list is in an error state
    for (int i=0; i<num_events_in_wait_list; ++i)
    {
        if (event_wait_list[i] == 0)
        {
            *errcode_ret = CL_INVALID_EVENT_WAIT_LIST;
            return;
        }
        else if (event_wait_list[i]->status() < 0)
        {
            *errcode_ret = CL_EXEC_STATUS_ERROR_FOR_EVENTS_IN_WAIT_LIST;
            return;
        }
    }

    // Allocate a new buffer for the events to wait
    if (num_events_in_wait_list)
    {
        const unsigned int len = num_events_in_wait_list * sizeof(Event *);
        p_event_wait_list = (const Event **)malloc(len);

        if (!p_event_wait_list)
        {
            *errcode_ret = CL_OUT_OF_HOST_MEMORY;
            return;
        }

        memcpy((void *)p_event_wait_list, (void *)event_wait_list, len);
    }

    // Explore the events we are waiting on and reference them
    for (int i=0; i<num_events_in_wait_list; ++i)
        clRetainEvent((cl_event)event_wait_list[i]);
}

Event::~Event()
{
    if (p_event_wait_list)
        free((void *)p_event_wait_list);

    pthread_mutex_destroy(&p_state_mutex);
    pthread_cond_destroy(&p_state_change_cond);

    if (p_release_parent)
        clReleaseCommandQueue((cl_command_queue)p_parent);
    else
        p_parent->dereference();  // Dereference but don't delete
}

void Event::setReleaseParent(bool release)
{
    p_release_parent = release;
}

bool Event::isSingleShot() const
{
    // NDRangeKernel is a single event that can be executed on several execution
    // units. The other (buffer copying) must be executed in one part.

    return (type() != NDRangeKernel);
}

bool Event::isDummy() const
{
    // A dummy event has nothing to do on an execution device and must be
    // completed directly after being "submitted".

    switch (type())
    {
        case Marker:
        case User:
        case Barrier:
            return true;

        default:
            return false;
    }
}

void Event::reference()
{
    p_references++;
}

bool Event::dereference()
{
    p_references--;
    return (p_references == 0);
}

void Event::setStatus(EventStatus status)
{
    pthread_mutex_lock(&p_state_mutex);
    p_status = status;

    pthread_cond_broadcast(&p_state_change_cond);

    // Call the callbacks
    std::multimap<EventStatus, CallbackData>::const_iterator it;
    std::pair<std::multimap<EventStatus, CallbackData>::const_iterator,
              std::multimap<EventStatus, CallbackData>::const_iterator> ret;

    ret = p_callbacks.equal_range(status > 0 ? status : Complete);

    for (it=ret.first; it!=ret.second; ++it)
    {
        const CallbackData &data = (*it).second;
        data.callback((cl_event)this, p_status, data.user_data);
    }

    pthread_mutex_unlock(&p_state_mutex);

    // If the event is completed, inform our parent so it can push other events
    // to the device.
    if (status == Complete)
        p_parent->pushEventsOnDevice();
}

void Event::setDeviceData(void *data)
{
    p_device_data = data;
}

Event::EventStatus Event::status() const
{
    // HACK : We need const qualifier but we also need to lock a mutex
    Event *me = (Event *)(void *)this;

    pthread_mutex_lock(&me->p_state_mutex);

    EventStatus ret = p_status;

    pthread_mutex_unlock(&me->p_state_mutex);

    return ret;
}

void Event::waitForStatus(EventStatus status)
{
    pthread_mutex_lock(&p_state_mutex);

    while (p_status != status)
    {
        pthread_cond_wait(&p_state_change_cond, &p_state_mutex);
    }

    pthread_mutex_unlock(&p_state_mutex);
}

void *Event::deviceData()
{
    return p_device_data;
}

const Event **Event::waitEvents(cl_uint &count) const
{
    count = p_num_events_in_wait_list;
    return p_event_wait_list;
}

void Event::setCallback(cl_int command_exec_callback_type,
                        event_callback callback,
                        void *user_data)
{
    CallbackData data;

    data.callback = callback;
    data.user_data = user_data;

    pthread_mutex_lock(&p_state_mutex);

    p_callbacks.insert(std::pair<EventStatus, CallbackData>(
        (EventStatus)command_exec_callback_type,
        data));

    pthread_mutex_unlock(&p_state_mutex);
}

cl_int Event::info(cl_context_info param_name,
                   size_t param_value_size,
                   void *param_value,
                   size_t *param_value_size_ret)
{
    void *value = 0;
    int value_length = 0;

    union {
        cl_command_queue cl_command_queue_var;
        cl_context cl_context_var;
        cl_command_type cl_command_type_var;
        cl_int cl_int_var;
        cl_uint cl_uint_var;
    };

    switch (param_name)
    {
        case CL_EVENT_COMMAND_QUEUE:
            SIMPLE_ASSIGN(cl_command_queue, p_parent);
            break;

        case CL_EVENT_CONTEXT:
            // Tail call to CommandQueue
            return p_parent->info(CL_QUEUE_CONTEXT, param_value_size,
                                  param_value, param_value_size_ret);

        case CL_EVENT_COMMAND_TYPE:
            SIMPLE_ASSIGN(cl_command_type, type());
            break;

        case CL_EVENT_COMMAND_EXECUTION_STATUS:
            SIMPLE_ASSIGN(cl_int, status());
            break;

        case CL_EVENT_REFERENCE_COUNT:
            SIMPLE_ASSIGN(cl_uint, p_references);
            break;

        default:
            return CL_INVALID_VALUE;
    }

    if (param_value && param_value_size < value_length)
        return CL_INVALID_VALUE;

    if (param_value_size_ret)
        *param_value_size_ret = value_length;

    if (param_value)
        memcpy(param_value, value, value_length);

    return CL_SUCCESS;
}<|endoftext|>


<commit_before>/** @copyright
 * Copyright (c) 2017, Stuart W. Baker
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are strictly prohibited without written consent.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file EntryModel.hxx
 * This file represents a decimal/hex number entry field in text.
 *
 * @author Stuart W. Baker
 * @date 1 December 2017
 */

#ifndef _UTILS_ENTRYMODEL_HXX_
#define _UTILS_ENTRYMODEL_HXX_

#include <algorithm>
#include <cstring>
#include <type_traits>

#include "utils/format_utils.hxx"

/** Implementation of a text entry menu.
 * @tparam T the data type up to 64-bits in size
 * @tparam N the size of the entry in max number of visible digits.
 */
template <class T, size_t N> class EntryModel
{
public:
    /** Constructor.
     * @param transform force characters to be upper case
     */
    EntryModel(bool transform = false)
        : digits_(0)
        , index_(0)
        , hasInitial_(false)
        , transform_(transform)
        , base_(10)
    {
        clear();
        data_[N] = '\0';
    }

    /** Initialize empty.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     */
    void init(unsigned digits, int base)
    {
        HASSERT(digits > 0 && digits <= N);
        digits_ = digits;
        clear();
        hasInitial_ = true;
    }

    /** Initialize with a value.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     * @param value value to initialize with
     */
    void init(unsigned digits, int base, T value)
    {
        HASSERT(digits > 0 && digits <= N);
        digits_ = digits;
        clear();

        string str;
        switch (base)
        {
            default:
                HASSERT(0);
            case 10:
                if (std::is_signed<T>::value)
                {
                    str = int64_to_string(value, digits);
                }
                else
                {
                    str = uint64_to_string(value, digits);
                }
                break;
            case 16:
                if (std::is_signed<T>::value)
                {
                    str = int64_to_string_hex(value, digits);
                }
                else
                {
                    str = uint64_to_string_hex(value, digits);
                }
                if (transform_)
                {
                    /* equires all characters in upper case */
                    transform(str.begin(), str.end(), str.begin(), toupper);
                }
                break;
        }
        strncpy(data_, str.c_str(), sizeof(data_) - 1);
        data_[sizeof(data_) - 1] = '\0';
        hasInitial_ = true;
    }

    /** Clear the entry string.
     * @param data data to fill in the buffer with
     */
    void clear(const char *data = nullptr)
    {
        memset(data_, ' ', digits_);
        if (data)
        {
            memcpy(data_, data, strlen(data));
        }
        data_[digits_] = '\0';
        index_ = 0;
    }

    /** Get the current index.
     * @return current cursor index
     */
    unsigned cursor_index()
    {
        return index_;
    }

    /** Test if cursor is visible.
     * @return true if cursor is visiable, else false
     */
    bool cursor_visible()
    {
        return index_ < digits_;
    }

    /** Put a character at the current index, and increment the index by 1.
     * @param c Character to place
     * @return true if the string was cleared out and an LCD refresh is
     *         may be required.
     */
    bool putc_inc(char c)
    {
        bool refresh = false;
        if (index_ >= digits_)
        {
            refresh = true;
            clear();
        }
        else
        {
            if (hasInitial_)
            {
                hasInitial_ = false;
                refresh = true;
                clear();
            }
            data_[index_++] = transform_ ? toupper(c) : c;
        }
        clamp();
        return refresh;
    }

    /** Delete a character off the end.
     */
    void backspace()
    {
        if (index_ > 0)
        {
            data_[--index_] = ' ';
            hasInitial_ = false;
        }
        clamp();
    }

    /** Set the radix base.
     * @param base new radix base to set.
     */
    void set_base(int base)
    {
        HASSERT(base == 10 || base == 16);
        base_ = base;
    }

    /** Set the value, keep the digits and base the same.
     * @param value value to initialize with
     */
    void set_value(T value)
    {
        init(digits_, base_, value);
    }

    /** Get the entry as an unsigned integer value.
     * @param start_index starting index in string to start conversion
     * @return value representation of the string
     */
    T get_value(unsigned start_index = 0)
    {
        if (std::is_signed<T>::value)
        {
            return strtoll(data_ + start_index, NULL, base_);
        }
        else
        {
            return strtoull(data_ + start_index, NULL, base_);
        }
    }

    /** Get the C style string representing the menu entry.
     * @return the string data representing the menu entry
     */
    const char *c_str()
    {
        return data_;
    }

    /** Get a copy of the string without any whitespace.
     * @param strip_leading true to also strip off leading '0' or ' '
     * @return the string data representing the menu entry
     */
    string parsed(bool strip_leading = false)
    {
        const char *parse = data_;
        string result;
        result.reserve(N);
        if (strip_leading)
        {
            while (*parse == '0' || *parse == ' ')
            {
                ++parse;
            }
        }
        while (*parse != ' ' && *parse != '\0')
        {
            result.push_back(*parse++);
        }
        return result;
    }

    /** Copy the entry data into the middle of a buffer.
     * @param buf pointer to destination buffer
     * @param start_index starting index of buffer for the copy destination
     * @param digits number of digits to copy
     */
    void copy_to_buffer(char *buf, int start_index, int digits)
    {
        HASSERT(digits <= digits_);
        memcpy(buf + start_index, data_, digits);
    }

    /** Change the sign of the data.
     */
    void change_sign()
    {
        HASSERT(std::is_signed<T>::value);
        if (hasInitial_)
        {
            set_value(-get_value());
        }
        else if (data_[0] == '-')
        {
            memmove(data_, data_ + 1, digits_ - 1);
            data_[digits_] = ' ';
        }
        else
        {
            memmove(data_ + 1, data_, digits_ - 1);
            data_[0] = '-';
        }
        clamp();
    }

protected:
    /// Clamp the value at the min or max.
    virtual void clamp()
    {
    }

private:
    unsigned digits_     : 5; /**< number of significant digits */
    unsigned index_      : 5; /**< present write index */
    unsigned hasInitial_ : 1; /**< has an initial value */
    unsigned transform_  : 1; /**< force characters to be upper case */
    unsigned reserved_   : 20; /**< reserved bit space */

    int base_; /**< radix base */
    char data_[N + 1]; /**< data string */

    DISALLOW_COPY_AND_ASSIGN(EntryModel);
};

/** Specialization of EntryModel with upper and lower bounds
 * @tparam T the data type up to 64-bits in size
 * @tparam N the size of the entry in max number of visible digits.
 */
template <class T, size_t N> class EntryModelBounded : public EntryModel<T, N>
{
public:
    /** Constructor.
     * @param transform force characters to be upper case
     */
    EntryModelBounded(bool transform = false)
        : EntryModel<T, N>(transform)
    {
    }

    /** Initialize with a value.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     * @param value unsigned value to initialize with
     * @param min minumum value
     * @param max maximum value
     * @param default_val default value
     */
    void init(unsigned digits, int base, T value, T min, T max, T default_val)
    {
        min_ = min;
        max_ = max;
        default_ = default_val;
        EntryModel<T, N>::init(digits, base, value);
    }

    /// Set the value to the minimum.
    void set_min()
    {
        EntryModel<T, N>::set_value(min_);
    }

    /// Set the value to the maximum.
    void set_max()
    {
        EntryModel<T, N>::set_value(max_);
    }

    /// Set the value to the default.
    void set_default()
    {
        EntryModel<T, N>::set_value(default_);
    }

private:
    /// Clamp the value at the min or max.
    void clamp() override
    {
        T value = EntryModel<T, N>::get_value();
        if (value < min_)
        {
            EntryModel<T, N>::set_value(min_);
        }
        else if (value > max_)
        {
            EntryModel<T, N>::set_value(max_);
        }
    }

    T min_; ///< minimum value
    T max_; ///< maximum value
    T default_; ///< default value

    DISALLOW_COPY_AND_ASSIGN(EntryModelBounded);
};

#endif /* _UTILS_ENTRYMODEL_HXX_ */

<commit_msg>Add accessor methods.<commit_after>/** @copyright
 * Copyright (c) 2017, Stuart W. Baker
 * All rights reserved
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are strictly prohibited without written consent.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * @file EntryModel.hxx
 * This file represents a decimal/hex number entry field in text.
 *
 * @author Stuart W. Baker
 * @date 1 December 2017
 */

#ifndef _UTILS_ENTRYMODEL_HXX_
#define _UTILS_ENTRYMODEL_HXX_

#include <algorithm>
#include <cstring>
#include <type_traits>

#include "utils/format_utils.hxx"

/** Implementation of a text entry menu.
 * @tparam T the data type up to 64-bits in size
 * @tparam N the size of the entry in max number of visible digits.
 */
template <class T, size_t N> class EntryModel
{
public:
    /** Constructor.
     * @param transform force characters to be upper case
     */
    EntryModel(bool transform = false)
        : digits_(0)
        , index_(0)
        , hasInitial_(false)
        , transform_(transform)
        , base_(10)
    {
        clear();
        data_[N] = '\0';
    }

    /** Initialize empty.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     */
    void init(unsigned digits, int base)
    {
        HASSERT(digits > 0 && digits <= N);
        digits_ = digits;
        clear();
        hasInitial_ = true;
    }

    /** Initialize with a value.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     * @param value value to initialize with
     */
    void init(unsigned digits, int base, T value)
    {
        HASSERT(digits > 0 && digits <= N);
        digits_ = digits;
        clear();

        string str;
        switch (base)
        {
            default:
                HASSERT(0);
            case 10:
                if (std::is_signed<T>::value)
                {
                    str = int64_to_string(value, digits);
                }
                else
                {
                    str = uint64_to_string(value, digits);
                }
                break;
            case 16:
                if (std::is_signed<T>::value)
                {
                    str = int64_to_string_hex(value, digits);
                }
                else
                {
                    str = uint64_to_string_hex(value, digits);
                }
                if (transform_)
                {
                    /* equires all characters in upper case */
                    transform(str.begin(), str.end(), str.begin(), toupper);
                }
                break;
        }
        strncpy(data_, str.c_str(), sizeof(data_) - 1);
        data_[sizeof(data_) - 1] = '\0';
        hasInitial_ = true;
    }

    /** Clear the entry string.
     * @param data data to fill in the buffer with
     */
    void clear(const char *data = nullptr)
    {
        memset(data_, ' ', digits_);
        if (data)
        {
            memcpy(data_, data, strlen(data));
        }
        data_[digits_] = '\0';
        index_ = 0;
    }

    /** Get the current index.
     * @return current cursor index
     */
    unsigned cursor_index()
    {
        return index_;
    }

    /** Test if cursor is visible.
     * @return true if cursor is visiable, else false
     */
    bool cursor_visible()
    {
        return index_ < digits_;
    }

    /** Put a character at the current index, and increment the index by 1.
     * @param c Character to place
     * @return true if the string was cleared out and an LCD refresh is
     *         may be required.
     */
    bool putc_inc(char c)
    {
        bool refresh = false;
        if (index_ >= digits_)
        {
            refresh = true;
            clear();
        }
        else
        {
            if (hasInitial_)
            {
                hasInitial_ = false;
                refresh = true;
                clear();
            }
            data_[index_++] = transform_ ? toupper(c) : c;
        }
        clamp();
        return refresh;
    }

    /** Delete a character off the end.
     */
    void backspace()
    {
        if (index_ > 0)
        {
            data_[--index_] = ' ';
            hasInitial_ = false;
        }
        clamp();
    }

    /** Set the radix base.
     * @param base new radix base to set.
     */
    void set_base(int base)
    {
        HASSERT(base == 10 || base == 16);
        base_ = base;
    }

    /** Set the value, keep the digits and base the same.
     * @param value value to initialize with
     */
    void set_value(T value)
    {
        init(digits_, base_, value);
    }

    /** Get the entry as an unsigned integer value.
     * @param start_index starting index in string to start conversion
     * @return value representation of the string
     */
    T get_value(unsigned start_index = 0)
    {
        if (std::is_signed<T>::value)
        {
            return strtoll(data_ + start_index, NULL, base_);
        }
        else
        {
            return strtoull(data_ + start_index, NULL, base_);
        }
    }

    /** Get the C style string representing the menu entry.
     * @return the string data representing the menu entry
     */
    const char *c_str()
    {
        return data_;
    }

    /** Get a copy of the string without any whitespace.
     * @param strip_leading true to also strip off leading '0' or ' '
     * @return the string data representing the menu entry
     */
    string parsed(bool strip_leading = false)
    {
        const char *parse = data_;
        string result;
        result.reserve(N);
        if (strip_leading)
        {
            while (*parse == '0' || *parse == ' ')
            {
                ++parse;
            }
        }
        while (*parse != ' ' && *parse != '\0')
        {
            result.push_back(*parse++);
        }
        return result;
    }

    /** Copy the entry data into the middle of a buffer.
     * @param buf pointer to destination buffer
     * @param start_index starting index of buffer for the copy destination
     * @param digits number of digits to copy
     */
    void copy_to_buffer(char *buf, int start_index, int digits)
    {
        HASSERT(digits <= digits_);
        memcpy(buf + start_index, data_, digits);
    }

    /** Change the sign of the data.
     */
    void change_sign()
    {
        HASSERT(std::is_signed<T>::value);
        if (hasInitial_)
        {
            set_value(-get_value());
        }
        else if (data_[0] == '-')
        {
            memmove(data_, data_ + 1, digits_ - 1);
            data_[digits_] = ' ';
        }
        else
        {
            memmove(data_ + 1, data_, digits_ - 1);
            data_[0] = '-';
        }
        clamp();
    }

    /// Get the number of significant digits
    /// @return number of significant digits
    unsigned digits()
    {
        return digits_;
    }

    /// Determine if this object is holding an initial or modified value.
    /// @return true if holding an initial value, else false if modified
    bool has_initial()
    {
        return hasInitial_;
    }

protected:
    /// Clamp the value at the min or max.
    virtual void clamp()
    {
    }

private:
    unsigned digits_     : 5; /**< number of significant digits */
    unsigned index_      : 5; /**< present write index */
    unsigned hasInitial_ : 1; /**< has an initial value */
    unsigned transform_  : 1; /**< force characters to be upper case */
    unsigned reserved_   : 20; /**< reserved bit space */

    int base_; /**< radix base */
    char data_[N + 1]; /**< data string */

    DISALLOW_COPY_AND_ASSIGN(EntryModel);
};

/** Specialization of EntryModel with upper and lower bounds
 * @tparam T the data type up to 64-bits in size
 * @tparam N the size of the entry in max number of visible digits.
 */
template <class T, size_t N> class EntryModelBounded : public EntryModel<T, N>
{
public:
    /** Constructor.
     * @param transform force characters to be upper case
     */
    EntryModelBounded(bool transform = false)
        : EntryModel<T, N>(transform)
    {
    }

    /** Initialize with a value.
     * @param digits max number of significant digits in the base type
     * @param base base type, 10 or 16
     * @param value unsigned value to initialize with
     * @param min minumum value
     * @param max maximum value
     * @param default_val default value
     */
    void init(unsigned digits, int base, T value, T min, T max, T default_val)
    {
        min_ = min;
        max_ = max;
        default_ = default_val;
        EntryModel<T, N>::init(digits, base, value);
    }

    /// Set the value to the minimum.
    void set_min()
    {
        EntryModel<T, N>::set_value(min_);
    }

    /// Set the value to the maximum.
    void set_max()
    {
        EntryModel<T, N>::set_value(max_);
    }

    /// Set the value to the default.
    void set_default()
    {
        EntryModel<T, N>::set_value(default_);
    }

private:
    /// Clamp the value at the min or max.
    void clamp() override
    {
        T value = EntryModel<T, N>::get_value();
        if (value < min_)
        {
            EntryModel<T, N>::set_value(min_);
        }
        else if (value > max_)
        {
            EntryModel<T, N>::set_value(max_);
        }
    }

    T min_; ///< minimum value
    T max_; ///< maximum value
    T default_; ///< default value

    DISALLOW_COPY_AND_ASSIGN(EntryModelBounded);
};

#endif /* _UTILS_ENTRYMODEL_HXX_ */

<|endoftext|>