<commit_before>// Copyright 2021 Global Phasing Ltd.

#include "gemmi/topo.hpp"
#include "gemmi/placeh.hpp"  // for adjust_hydrogen_distances

#include "common.h"
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/iostream.h>  // for detail::pythonbuf

namespace py = pybind11;
using namespace gemmi;

PYBIND11_MAKE_OPAQUE(std::vector<Topo::Bond>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Angle>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Torsion>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Chirality>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Plane>)

void add_topo(py::module& m) {
  py::class_<Topo> topo(m, "Topo");

  py::enum_<HydrogenChange>(m, "HydrogenChange")
    .value("NoChange", HydrogenChange::NoChange)
    .value("Shift", HydrogenChange::Shift)
    .value("Remove", HydrogenChange::Remove)
    .value("ReAdd", HydrogenChange::ReAdd)
    .value("ReAddButWater", HydrogenChange::ReAddButWater);

  py::class_<Topo::Bond>(topo, "Bond")
    .def_readonly("restr", &Topo::Bond::restr)
    .def_readonly("atoms", &Topo::Bond::atoms)
    .def("calculate", &Topo::Bond::calculate)
    .def("calculate_z", &Topo::Bond::calculate_z)
    ;
  py::class_<Topo::Angle>(topo, "Angle")
    .def_readonly("restr", &Topo::Angle::restr)
    .def_readonly("atoms", &Topo::Angle::atoms)
    .def("calculate", &Topo::Angle::calculate)
    .def("calculate_z", &Topo::Angle::calculate_z)
    ;
  py::class_<Topo::Torsion>(topo, "Torsion")
    .def_readonly("restr", &Topo::Torsion::restr)
    .def_readonly("atoms", &Topo::Torsion::atoms)
    .def("calculate", &Topo::Torsion::calculate)
    .def("calculate_z", &Topo::Torsion::calculate_z)
    ;
  py::class_<Topo::Chirality>(topo, "Chirality")
    .def_readonly("restr", &Topo::Chirality::restr)
    .def_readonly("atoms", &Topo::Chirality::atoms)
    .def("calculate", &Topo::Chirality::calculate)
    .def("calculate_z", &Topo::Chirality::calculate_z,
         py::arg("ideal_abs_vol"), py::arg("esd"))
    .def("check", &Topo::Chirality::check)
    ;
  py::class_<Topo::Plane>(topo, "Plane")
    .def_readonly("restr", &Topo::Plane::restr)
    .def_readonly("atoms", &Topo::Plane::atoms)
    .def("has", &Topo::Plane::has)
    ;
  py::bind_vector<std::vector<Topo::Bond>>(m, "TopoBonds");
  py::bind_vector<std::vector<Topo::Angle>>(m, "TopoAngles");
  py::bind_vector<std::vector<Topo::Torsion>>(m, "TopoTorsions");
  py::bind_vector<std::vector<Topo::Chirality>>(m, "TopoChirs");
  py::bind_vector<std::vector<Topo::Plane>>(m, "TopoPlanes");

  topo
    .def(py::init<>())
    .def("adjust_hydrogen_distances", &adjust_hydrogen_distances,
         py::arg("of"), py::arg("default_scale")=1.)
    .def_readonly("bonds", &Topo::bonds)
    .def_readonly("angles", &Topo::angles)
    .def_readonly("torsions", &Topo::torsions)
    .def_readonly("chirs", &Topo::chirs)
    .def_readonly("planes", &Topo::planes)
    .def("ideal_chiral_abs_volume", &Topo::ideal_chiral_abs_volume)
    ;

  m.def("prepare_topology",
    [](Structure& st, MonLib& monlib, size_t model_index,
       HydrogenChange h_change, bool reorder,
       const py::object& pywarnings, bool ignore_unknown_links) {
      std::ostream* warnings = nullptr;
      std::ostream os(nullptr);
      std::unique_ptr<py::detail::pythonbuf> buffer;
      if (!pywarnings.is_none()) {
        buffer.reset(new py::detail::pythonbuf(pywarnings));
        os.rdbuf(buffer.get());
        warnings = &os;
      }
      prepare_topology(st, monlib, model_index, h_change, reorder,
                       warnings, ignore_unknown_links);
    }, py::arg("st"), py::arg("monlib"), py::arg("model_index")=0,
       py::arg("h_change")=HydrogenChange::NoChange, py::arg("reorder")=false,
       py::arg("warnings")=py::none(), py::arg("ignore_unknown_links")=false);
}
<commit_msg>fix a previous commit which changed prepare_topology() to return None<commit_after>// Copyright 2021 Global Phasing Ltd.

#include "gemmi/topo.hpp"
#include "gemmi/placeh.hpp"  // for adjust_hydrogen_distances

#include "common.h"
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/iostream.h>  // for detail::pythonbuf

namespace py = pybind11;
using namespace gemmi;

PYBIND11_MAKE_OPAQUE(std::vector<Topo::Bond>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Angle>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Torsion>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Chirality>)
PYBIND11_MAKE_OPAQUE(std::vector<Topo::Plane>)

void add_topo(py::module& m) {
  py::class_<Topo> topo(m, "Topo");

  py::enum_<HydrogenChange>(m, "HydrogenChange")
    .value("NoChange", HydrogenChange::NoChange)
    .value("Shift", HydrogenChange::Shift)
    .value("Remove", HydrogenChange::Remove)
    .value("ReAdd", HydrogenChange::ReAdd)
    .value("ReAddButWater", HydrogenChange::ReAddButWater);

  py::class_<Topo::Bond>(topo, "Bond")
    .def_readonly("restr", &Topo::Bond::restr)
    .def_readonly("atoms", &Topo::Bond::atoms)
    .def("calculate", &Topo::Bond::calculate)
    .def("calculate_z", &Topo::Bond::calculate_z)
    ;
  py::class_<Topo::Angle>(topo, "Angle")
    .def_readonly("restr", &Topo::Angle::restr)
    .def_readonly("atoms", &Topo::Angle::atoms)
    .def("calculate", &Topo::Angle::calculate)
    .def("calculate_z", &Topo::Angle::calculate_z)
    ;
  py::class_<Topo::Torsion>(topo, "Torsion")
    .def_readonly("restr", &Topo::Torsion::restr)
    .def_readonly("atoms", &Topo::Torsion::atoms)
    .def("calculate", &Topo::Torsion::calculate)
    .def("calculate_z", &Topo::Torsion::calculate_z)
    ;
  py::class_<Topo::Chirality>(topo, "Chirality")
    .def_readonly("restr", &Topo::Chirality::restr)
    .def_readonly("atoms", &Topo::Chirality::atoms)
    .def("calculate", &Topo::Chirality::calculate)
    .def("calculate_z", &Topo::Chirality::calculate_z,
         py::arg("ideal_abs_vol"), py::arg("esd"))
    .def("check", &Topo::Chirality::check)
    ;
  py::class_<Topo::Plane>(topo, "Plane")
    .def_readonly("restr", &Topo::Plane::restr)
    .def_readonly("atoms", &Topo::Plane::atoms)
    .def("has", &Topo::Plane::has)
    ;
  py::bind_vector<std::vector<Topo::Bond>>(m, "TopoBonds");
  py::bind_vector<std::vector<Topo::Angle>>(m, "TopoAngles");
  py::bind_vector<std::vector<Topo::Torsion>>(m, "TopoTorsions");
  py::bind_vector<std::vector<Topo::Chirality>>(m, "TopoChirs");
  py::bind_vector<std::vector<Topo::Plane>>(m, "TopoPlanes");

  topo
    .def(py::init<>())
    .def("adjust_hydrogen_distances", &adjust_hydrogen_distances,
         py::arg("of"), py::arg("default_scale")=1.)
    .def_readonly("bonds", &Topo::bonds)
    .def_readonly("angles", &Topo::angles)
    .def_readonly("torsions", &Topo::torsions)
    .def_readonly("chirs", &Topo::chirs)
    .def_readonly("planes", &Topo::planes)
    .def("ideal_chiral_abs_volume", &Topo::ideal_chiral_abs_volume)
    ;

  m.def("prepare_topology",
    [](Structure& st, MonLib& monlib, size_t model_index,
       HydrogenChange h_change, bool reorder,
       const py::object& pywarnings, bool ignore_unknown_links) {
      std::ostream* warnings = nullptr;
      std::ostream os(nullptr);
      std::unique_ptr<py::detail::pythonbuf> buffer;
      if (!pywarnings.is_none()) {
        buffer.reset(new py::detail::pythonbuf(pywarnings));
        os.rdbuf(buffer.get());
        warnings = &os;
      }
      return prepare_topology(st, monlib, model_index, h_change, reorder,
                              warnings, ignore_unknown_links);
    }, py::arg("st"), py::arg("monlib"), py::arg("model_index")=0,
       py::arg("h_change")=HydrogenChange::NoChange, py::arg("reorder")=false,
       py::arg("warnings")=py::none(), py::arg("ignore_unknown_links")=false);
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2017, The OpenThread Authors.
 *  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.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  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
 *   This file implements the border agent proxy.
 */

#define WPP_NAME "border_agent_proxy.tmh"

#ifdef OPENTHREAD_CONFIG_FILE
#include OPENTHREAD_CONFIG_FILE
#else
#include <openthread-config.h>
#endif

#include <openthread/types.h>
#include <coap/coap_header.hpp>
#include <thread/thread_uris.hpp>
#include <thread/thread_tlvs.hpp>
#include <net/ip6_address.hpp>

#include "border_agent_proxy.hpp"

#if OPENTHREAD_FTD && OPENTHREAD_ENABLE_BORDER_AGENT_PROXY

namespace ot {
namespace MeshCoP {

BorderAgentProxy::BorderAgentProxy(const Ip6::Address &aMeshLocal16, Coap::Server &aCoapServer,
                                   Coap::Client &aCoapClient):
    mRelayReceive(OPENTHREAD_URI_RELAY_RX, &BorderAgentProxy::HandleRelayReceive, this),
    mStreamHandler(NULL),
    mContext(NULL),
    mMeshLocal16(aMeshLocal16),
    mCoapServer(aCoapServer),
    mCoapClient(aCoapClient)
{
}

ThreadError BorderAgentProxy::Start(otBorderAgentProxyStreamHandler aStreamHandler, void *aContext)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(!mStreamHandler, error = kThreadError_Already);

    mCoapServer.AddResource(mRelayReceive);
    mStreamHandler = aStreamHandler;
    mContext = aContext;

exit:
    return error;
}

ThreadError BorderAgentProxy::Stop(void)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_Already);

    mCoapServer.RemoveResource(mRelayReceive);

    mStreamHandler = NULL;

exit:
    return error;
}

bool BorderAgentProxy::IsEnabled(void) const
{
    return mStreamHandler != NULL;
}

void BorderAgentProxy::HandleRelayReceive(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
                                          const otMessageInfo *aMessageInfo)
{
    static_cast<BorderAgentProxy *>(aContext)->DeliverMessage(
        *static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage),
        *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}

void BorderAgentProxy::HandleResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
                                      const otMessageInfo *aMessageInfo, ThreadError aResult)
{
    VerifyOrExit(aResult == kThreadError_None);

    static_cast<BorderAgentProxy *>(aContext)->DeliverMessage(*static_cast<Coap::Header *>(aHeader),
                                                              *static_cast<Message *>(aMessage),
                                                              *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
exit:
    return;
}

void BorderAgentProxy::DeliverMessage(Coap::Header &aHeader, Message &aMessage,
                                      const Ip6::MessageInfo &aMessageInfo)
{
    ThreadError error = kThreadError_None;
    uint16_t rloc;
    uint16_t port;
    Message *message = NULL;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_InvalidState);

    VerifyOrExit((message = aMessage.Clone()) != NULL, error = kThreadError_NoBufs);
    message->RemoveHeader(message->GetOffset() - aHeader.GetLength());

    rloc = HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]);
    port = aMessageInfo.GetPeerPort();
    mStreamHandler(message, rloc, port, mContext);

exit:

    if (error != kThreadError_None && message != NULL)
    {
        message->Free();
    }
}

ThreadError BorderAgentProxy::Send(Message &aMessage, uint16_t aLocator, uint16_t aPort)
{
    ThreadError error = kThreadError_None;
    Ip6::MessageInfo messageInfo;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_InvalidState);

    messageInfo.SetPeerAddr(mMeshLocal16);
    messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(aLocator);
    messageInfo.SetPeerPort(aPort);

    if (aPort == kCoapUdpPort)
    {
        // this is request to server, send with client
        error = mCoapClient.SendMessage(aMessage, messageInfo, BorderAgentProxy::HandleResponse, this);
    }
    else
    {
        error = mCoapServer.SendMessage(aMessage, messageInfo);
    }

exit:

    if (error != kThreadError_None)
    {
        aMessage.Free();
    }

    return error;
}

}  // namespace MeshCoP
}  // namespace ot

#endif // OPENTHREAD_FTD && OPENTHREAD_ENABLE_BORDER_AGENT_PROXY
<commit_msg>explicitly set RLOC as source address (#1704)<commit_after>/*
 *  Copyright (c) 2017, The OpenThread Authors.
 *  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.
 *  3. Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 *  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
 *   This file implements the border agent proxy.
 */

#define WPP_NAME "border_agent_proxy.tmh"

#ifdef OPENTHREAD_CONFIG_FILE
#include OPENTHREAD_CONFIG_FILE
#else
#include <openthread-config.h>
#endif

#include <openthread/types.h>
#include <coap/coap_header.hpp>
#include <thread/thread_uris.hpp>
#include <thread/thread_tlvs.hpp>
#include <net/ip6_address.hpp>

#include "border_agent_proxy.hpp"

#if OPENTHREAD_FTD && OPENTHREAD_ENABLE_BORDER_AGENT_PROXY

namespace ot {
namespace MeshCoP {

BorderAgentProxy::BorderAgentProxy(const Ip6::Address &aMeshLocal16, Coap::Server &aCoapServer,
                                   Coap::Client &aCoapClient):
    mRelayReceive(OPENTHREAD_URI_RELAY_RX, &BorderAgentProxy::HandleRelayReceive, this),
    mStreamHandler(NULL),
    mContext(NULL),
    mMeshLocal16(aMeshLocal16),
    mCoapServer(aCoapServer),
    mCoapClient(aCoapClient)
{
}

ThreadError BorderAgentProxy::Start(otBorderAgentProxyStreamHandler aStreamHandler, void *aContext)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(!mStreamHandler, error = kThreadError_Already);

    mCoapServer.AddResource(mRelayReceive);
    mStreamHandler = aStreamHandler;
    mContext = aContext;

exit:
    return error;
}

ThreadError BorderAgentProxy::Stop(void)
{
    ThreadError error = kThreadError_None;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_Already);

    mCoapServer.RemoveResource(mRelayReceive);

    mStreamHandler = NULL;

exit:
    return error;
}

bool BorderAgentProxy::IsEnabled(void) const
{
    return mStreamHandler != NULL;
}

void BorderAgentProxy::HandleRelayReceive(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
                                          const otMessageInfo *aMessageInfo)
{
    static_cast<BorderAgentProxy *>(aContext)->DeliverMessage(
        *static_cast<Coap::Header *>(aHeader), *static_cast<Message *>(aMessage),
        *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
}

void BorderAgentProxy::HandleResponse(void *aContext, otCoapHeader *aHeader, otMessage *aMessage,
                                      const otMessageInfo *aMessageInfo, ThreadError aResult)
{
    VerifyOrExit(aResult == kThreadError_None);

    static_cast<BorderAgentProxy *>(aContext)->DeliverMessage(*static_cast<Coap::Header *>(aHeader),
                                                              *static_cast<Message *>(aMessage),
                                                              *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
exit:
    return;
}

void BorderAgentProxy::DeliverMessage(Coap::Header &aHeader, Message &aMessage,
                                      const Ip6::MessageInfo &aMessageInfo)
{
    ThreadError error = kThreadError_None;
    uint16_t rloc;
    uint16_t port;
    Message *message = NULL;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_InvalidState);

    VerifyOrExit((message = aMessage.Clone()) != NULL, error = kThreadError_NoBufs);
    message->RemoveHeader(message->GetOffset() - aHeader.GetLength());

    rloc = HostSwap16(aMessageInfo.GetPeerAddr().mFields.m16[7]);
    port = aMessageInfo.GetPeerPort();
    mStreamHandler(message, rloc, port, mContext);

exit:

    if (error != kThreadError_None && message != NULL)
    {
        message->Free();
    }
}

ThreadError BorderAgentProxy::Send(Message &aMessage, uint16_t aLocator, uint16_t aPort)
{
    ThreadError error = kThreadError_None;
    Ip6::MessageInfo messageInfo;

    VerifyOrExit(mStreamHandler != NULL, error = kThreadError_InvalidState);

    messageInfo.SetSockAddr(mMeshLocal16);
    messageInfo.SetPeerAddr(mMeshLocal16);
    messageInfo.GetPeerAddr().mFields.m16[7] = HostSwap16(aLocator);
    messageInfo.SetPeerPort(aPort);

    if (aPort == kCoapUdpPort)
    {
        // this is request to server, send with client
        error = mCoapClient.SendMessage(aMessage, messageInfo, BorderAgentProxy::HandleResponse, this);
    }
    else
    {
        error = mCoapServer.SendMessage(aMessage, messageInfo);
    }

exit:

    if (error != kThreadError_None)
    {
        aMessage.Free();
    }

    return error;
}

}  // namespace MeshCoP
}  // namespace ot

#endif // OPENTHREAD_FTD && OPENTHREAD_ENABLE_BORDER_AGENT_PROXY
<|endoftext|>


<commit_before>/*
 * Algorithm.hpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#ifndef CORE_ALGORITHM_HPP
#define CORE_ALGORITHM_HPP

#include <vector>
#include <algorithm>

#include <boost/type_traits.hpp>
#include <boost/algorithm/string.hpp>

#include <core/Macros.hpp>
#include <core/StringUtils.hpp>
#include <core/type_traits/TypeTraits.hpp>

namespace rstudio {
namespace core {
namespace algorithm {

template<typename InputIterator, typename OutputIterator, typename Predicate>
OutputIterator copy_if(InputIterator begin,
                       InputIterator end,
                       OutputIterator destBegin,
                       Predicate p)
{
   while (begin != end)
   {
      if (p(*begin))
        *destBegin++ = *begin;
      ++begin;
   }
   return destBegin;
}

template<typename InputIterator,
         typename OutputIterator,
         typename Predicate,
         typename UnaryOperator>
OutputIterator copy_transformed_if(InputIterator begin,
                                   InputIterator end,
                                   OutputIterator destBegin,
                                   Predicate p,
                                   UnaryOperator op)
{
   while (begin != end)
   {
      if (p(*begin))
        *destBegin++ = op(*begin);
      ++begin;
   }
   return destBegin;
}

namespace detail {

template <typename Container, typename ValueType>
typename boost::enable_if_c< type_traits::has_key_type<Container>::value, bool >::type
contains(const Container& container, const ValueType& value, boost::true_type)
{
   return container.count(value);
}

template <typename Container, typename ValueType>
typename boost::disable_if_c< type_traits::has_key_type<Container>::value, bool >::type
contains(const Container& container, const ValueType& value, boost::false_type)
{
   return std::find(container.begin(), container.end(), value) != container.end();
}

} // namespace detail

template <typename Container, typename ValueType>
bool contains(const Container& container, const ValueType& value)
{
   return detail::contains(
            container,
            value,
            type_traits::has_key_type<Container>());
}

namespace detail {

template <typename Container, typename ValueType>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, const ValueType& value, boost::true_type)
{
   container.insert(value);
}

template <typename Container, typename ValueType>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, const ValueType& value, boost::false_type)
{
   container.push_back(value);
}

} // namespace detail

template <typename Container, typename ValueType>
void insert(Container& container, const ValueType& value)
{
   detail::insert(container, value, type_traits::has_key_type<Container>());
}

namespace detail {

template <typename Container, typename Iterator>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, Iterator begin, Iterator end, boost::true_type)
{
   container.insert(begin, end);
}

template <typename Container, typename Iterator>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, Iterator begin, Iterator end, boost::false_type)
{
   container.insert(container.end(), begin, end);
}

} // namespace detail

template <typename Container, typename Iterator>
void insert(Container& container, Iterator begin, Iterator end)
{
   detail::insert(container, begin, end,
                  type_traits::has_key_type<Container>());
}

/* Wrappers for the erase-remove idiom */
template <typename Container, typename ValueType>
void expel(Container& container, const ValueType& value)
{
   container.erase(std::remove(container.begin(), container.end(), value), container.end());
}

template <typename Container, typename Predicate>
void expel_if(Container& container, Predicate predicate)
{
   container.erase(std::remove_if(container.begin(), container.end(), predicate), container.end());
}

/* Value-based wrappers for erase-remove idiom */
template <typename Container, typename ValueType>
Container without(Container container, const ValueType& value)
{
   container.erase(std::remove(container.begin(), container.end(), value), container.end());
   return container;
}

template <typename Container, typename Predicate>
Container without_if(Container container, Predicate predicate)
{
   container.erase(std::remove_if(container.begin(), container.end(), predicate), container.end());
   return container;
}

template <typename T>
std::vector<T> seq(T length)
{
   std::vector<T> result;
   result.reserve(length);
   for (std::size_t i = 0; i < length; ++i)
      result.push_back(i);
   return result;
}

template <typename AssociativeContainer,
          typename KeyType,
          typename MappedType>
bool get(const AssociativeContainer& container,
         const KeyType& key,
         MappedType** ppValue)
{
   if (!container.count(key))
      return false;

   *ppValue = &(const_cast<AssociativeContainer&>(container)[key]);
   return true;
}

namespace detail {

template <typename Container, typename Iterator>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
append(Container* pContainer, Iterator begin, Iterator end, boost::true_type)
{
   pContainer->insert(begin, end);
}

template <typename Container, typename Iterator>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
append(Container* pContainer, Iterator begin, Iterator end, boost::false_type)
{
   pContainer->insert(pContainer->end(), begin, end);
}

} // namespace detail

template <typename ContainerType, typename OtherType>
void append(ContainerType* pContainer, const OtherType& other)
{
   detail::append(pContainer,
                  other.begin(),
                  other.end(),
                  type_traits::has_key_type<ContainerType>());
}

inline std::vector<std::string> split(const std::string& string,
                                      const std::string& delim)
{
   std::vector<std::string> result;

   if (UNLIKELY(delim.size() == 0))
   {
      std::size_t n = string.size();
      result.reserve(n);
      for (std::string::size_type i = 0; i < n; ++i)
         result.push_back(std::string(string[i], 1));
      return result;
   }

   std::string::size_type start = 0;
   std::string::size_type end   = string.find(delim, start);

   // Add all of the initial split pieces
   while (end != std::string::npos)
   {
      result.push_back(string_utils::substring(string, start, end));

      start = end + delim.size();
      end   = string.find(delim, start);
   }

   // Add the final piece
   result.push_back(string_utils::substring(string, start));

   // And return!
   return result;
}

template <std::size_t N>
inline std::vector<std::string> split(const std::string& string,
                                      const char (&delim)[N])
{
   return split(string, std::string(delim, N - 1));
}

inline std::string join(const std::vector<std::string>& container, const std::string& delim)
{
   return boost::algorithm::join(container, delim);
}

template <typename Iterator, typename F>
inline std::string join(Iterator begin,
                        Iterator end,
                        const std::string& delim,
                        F&& f)
{
   if (begin >= end)
      return std::string();

   std::string result;
   result += f(*begin);
   for (Iterator it = begin + 1; it != end; ++it)
   {
      result += delim;
      result += f(*it);
   }
   return result;

}

template <typename Iterator>
inline std::string join(Iterator begin,
                        Iterator end,
                        const std::string& delim)
{
   auto callback = [](const std::string& string) { return string; };
   return join(begin, end, delim, callback);
}

} // namespace algorithm
} // namespace core
} // namespace rstudio

#endif // CORE_ALGORITHM_HPP
<commit_msg>use std::move<commit_after>/*
 * Algorithm.hpp
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#ifndef CORE_ALGORITHM_HPP
#define CORE_ALGORITHM_HPP

#include <vector>
#include <algorithm>

#include <boost/type_traits.hpp>
#include <boost/algorithm/string.hpp>

#include <core/Macros.hpp>
#include <core/StringUtils.hpp>
#include <core/type_traits/TypeTraits.hpp>

namespace rstudio {
namespace core {
namespace algorithm {

template<typename InputIterator, typename OutputIterator, typename Predicate>
OutputIterator copy_if(InputIterator begin,
                       InputIterator end,
                       OutputIterator destBegin,
                       Predicate p)
{
   while (begin != end)
   {
      if (p(*begin))
        *destBegin++ = *begin;
      ++begin;
   }
   return destBegin;
}

template<typename InputIterator,
         typename OutputIterator,
         typename Predicate,
         typename UnaryOperator>
OutputIterator copy_transformed_if(InputIterator begin,
                                   InputIterator end,
                                   OutputIterator destBegin,
                                   Predicate p,
                                   UnaryOperator op)
{
   while (begin != end)
   {
      if (p(*begin))
        *destBegin++ = op(*begin);
      ++begin;
   }
   return destBegin;
}

namespace detail {

template <typename Container, typename ValueType>
typename boost::enable_if_c< type_traits::has_key_type<Container>::value, bool >::type
contains(const Container& container, const ValueType& value, boost::true_type)
{
   return container.count(value);
}

template <typename Container, typename ValueType>
typename boost::disable_if_c< type_traits::has_key_type<Container>::value, bool >::type
contains(const Container& container, const ValueType& value, boost::false_type)
{
   return std::find(container.begin(), container.end(), value) != container.end();
}

} // namespace detail

template <typename Container, typename ValueType>
bool contains(const Container& container, const ValueType& value)
{
   return detail::contains(
            container,
            value,
            type_traits::has_key_type<Container>());
}

namespace detail {

template <typename Container, typename ValueType>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, const ValueType& value, boost::true_type)
{
   container.insert(value);
}

template <typename Container, typename ValueType>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, const ValueType& value, boost::false_type)
{
   container.push_back(value);
}

} // namespace detail

template <typename Container, typename ValueType>
void insert(Container& container, const ValueType& value)
{
   detail::insert(container, value, type_traits::has_key_type<Container>());
}

namespace detail {

template <typename Container, typename Iterator>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, Iterator begin, Iterator end, boost::true_type)
{
   container.insert(begin, end);
}

template <typename Container, typename Iterator>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
insert(Container& container, Iterator begin, Iterator end, boost::false_type)
{
   container.insert(container.end(), begin, end);
}

} // namespace detail

template <typename Container, typename Iterator>
void insert(Container& container, Iterator begin, Iterator end)
{
   detail::insert(container, begin, end,
                  type_traits::has_key_type<Container>());
}

/* Wrappers for the erase-remove idiom */
template <typename Container, typename ValueType>
void expel(Container& container, const ValueType& value)
{
   container.erase(std::remove(container.begin(), container.end(), value), container.end());
}

template <typename Container, typename Predicate>
void expel_if(Container& container, Predicate predicate)
{
   container.erase(std::remove_if(container.begin(), container.end(), predicate), container.end());
}

/* Value-based wrappers for erase-remove idiom */
template <typename Container, typename ValueType>
Container without(Container container, const ValueType& value)
{
   container.erase(std::remove(container.begin(), container.end(), value), container.end());
   return container;
}

template <typename Container, typename Predicate>
Container without_if(Container container, Predicate predicate)
{
   container.erase(std::remove_if(container.begin(), container.end(), predicate), container.end());
   return container;
}

template <typename T>
std::vector<T> seq(T length)
{
   std::vector<T> result;
   result.reserve(length);
   for (std::size_t i = 0; i < length; ++i)
      result.push_back(i);
   return result;
}

template <typename AssociativeContainer,
          typename KeyType,
          typename MappedType>
bool get(const AssociativeContainer& container,
         const KeyType& key,
         MappedType** ppValue)
{
   if (!container.count(key))
      return false;

   *ppValue = &(const_cast<AssociativeContainer&>(container)[key]);
   return true;
}

namespace detail {

template <typename Container, typename Iterator>
typename boost::enable_if_c<type_traits::has_key_type<Container>::value, void>::type
append(Container* pContainer, Iterator begin, Iterator end, boost::true_type)
{
   pContainer->insert(begin, end);
}

template <typename Container, typename Iterator>
typename boost::disable_if_c<type_traits::has_key_type<Container>::value, void>::type
append(Container* pContainer, Iterator begin, Iterator end, boost::false_type)
{
   pContainer->insert(pContainer->end(), begin, end);
}

} // namespace detail

template <typename ContainerType, typename OtherType>
void append(ContainerType* pContainer, const OtherType& other)
{
   detail::append(pContainer,
                  other.begin(),
                  other.end(),
                  type_traits::has_key_type<ContainerType>());
}

inline std::vector<std::string> split(const std::string& string,
                                      const std::string& delim)
{
   std::vector<std::string> result;

   if (UNLIKELY(delim.size() == 0))
   {
      std::size_t n = string.size();
      result.reserve(n);
      for (std::string::size_type i = 0; i < n; ++i)
         result.push_back(std::string(string[i], 1));
      return result;
   }

   std::string::size_type start = 0;
   std::string::size_type end   = string.find(delim, start);

   // Add all of the initial split pieces
   while (end != std::string::npos)
   {
      result.push_back(string_utils::substring(string, start, end));

      start = end + delim.size();
      end   = string.find(delim, start);
   }

   // Add the final piece
   result.push_back(string_utils::substring(string, start));

   // And return!
   return result;
}

template <std::size_t N>
inline std::vector<std::string> split(const std::string& string,
                                      const char (&delim)[N])
{
   return split(string, std::string(delim, N - 1));
}

inline std::string join(const std::vector<std::string>& container, const std::string& delim)
{
   return boost::algorithm::join(container, delim);
}

template <typename Iterator, typename F>
inline std::string join(Iterator begin,
                        Iterator end,
                        const std::string& delim,
                        F&& f)
{
   if (begin >= end)
      return std::string();

   std::string result;
   result += f(*begin);
   for (Iterator it = begin + 1; it != end; ++it)
   {
      result += delim;
      result += f(*it);
   }
   return result;

}

template <typename Iterator>
inline std::string join(Iterator begin,
                        Iterator end,
                        const std::string& delim)
{
   auto callback = [](const std::string& string) { return string; };
   return join(begin, end, delim, std::move(callback));
}

} // namespace algorithm
} // namespace core
} // namespace rstudio

#endif // CORE_ALGORITHM_HPP
<|endoftext|>


<commit_before>/*
 * ServerSessionManager.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ServerSessionManager.hpp"

#include <sys/wait.h>

#include <vector>

#include <boost/foreach.hpp>
#include <boost/format.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>
#include <core/system/PosixUser.hpp>

#include <session/SessionConstants.hpp>

#include <server/ServerOptions.hpp>

#include <server/util/system/User.hpp>

#include <server/auth/ServerValidateUser.hpp>

#include "ServerREnvironment.hpp"

using namespace core;

namespace server {

SessionManager& sessionManager()
{
   static SessionManager instance;
   return instance;
}

Error SessionManager::launchSession(const std::string& username)
{
   using namespace boost::posix_time;

   LOCK_MUTEX(launchesMutex_)
   {
      // check whether we already have a launch pending
      LaunchMap::const_iterator pos = pendingLaunches_.find(username);
      if (pos != pendingLaunches_.end())
      {
         // if the launch is less than one minute old then return success
         if ( (pos->second + boost::posix_time::minutes(1))
               > microsec_clock::universal_time() )
         {
            return Success();
         }
         // otherwise erase it from pending launches and then
         // re-launch (immediately below)
         else
         {
            // very unexpected condition
            LOG_WARNING_MESSAGE("Very long session launch delay for "
                                "user " + username + " (aborting wait)");

            pendingLaunches_.erase(username);
         }
      }

      // record the launch
      pendingLaunches_[username] =  microsec_clock::universal_time();
   }
   END_LOCK_MUTEX

   // launch the session
   PidType pid;
   Error error = server::launchSession(username, &pid);
   if (error)
   {
      removePendingLaunch(username);
      return error;
   }
   else
   {
      // add it to our active pids
      addActivePid(pid);

      // return success
      return Success();
   }
}

void SessionManager::removePendingLaunch(const std::string& username)
{
   LOCK_MUTEX(launchesMutex_)
   {
      pendingLaunches_.erase(username);
   }
   END_LOCK_MUTEX
}

namespace {

// wraper for waitPid which tries again for EINTR
int waitPid(PidType pid, int* pStatus)
{
   for (;;)
   {
      int result = ::waitpid(pid, pStatus, WNOHANG);
      if (result == -1 && errno == EINTR)
         continue;
      return result;
   }
}

} // anonymous namespace

void SessionManager::notifySIGCHLD()
{
   // We make a copy of hte active pids so that we can do the reaping
   // outside of the pidsMutex_. This is an extra conservative precaution
   // in case there is ever an issue with waitpid blocking.
   std::vector<PidType> pids = activePids();

   // reap all that we can
   BOOST_FOREACH(PidType pid, pids)
   {
      // non-blocking wait for the child
      int status;
      int result = waitPid(pid, &status);

      // reaped the child
      if (result == pid)
      {
         // confirm this was a real exit
         bool exited = false;
         if (WIFEXITED(status))
         {
            exited = true;
            status = WEXITSTATUS(status);
         }
         else if (WIFSIGNALED(status))
         {
            exited = true;
         }

         // if it was a real exit (as opposed to a SIGSTOP or SIGCONT)
         // then remove the pid from our table and fire the event
         if (exited)
         {
            // all done with this pid
            removeActivePid(pid);

            // log
            std::cerr << "child exited: " << pid << ", status: " << status << std::endl;
         }
         else
         {
            boost::format fmt("Received SIGCHLD when child did not "
                              "actually exit (pid=%1%, status=%2%");
            LOG_WARNING_MESSAGE(boost::str(fmt % pid % status));
         }
      }
      // error occured
      else if (result == -1)
      {
         Error error = systemError(errno, ERROR_LOCATION);
         error.addProperty("pid", pid);
         LOG_ERROR(error);
      }
   }
}

void SessionManager::addActivePid(PidType pid)
{
   LOCK_MUTEX(pidsMutex_)
   {
      activePids_.push_back(pid);
   }
   END_LOCK_MUTEX
}

void SessionManager::removeActivePid(PidType pid)
{
   LOCK_MUTEX(pidsMutex_)
   {
      activePids_.erase(std::remove(activePids_.begin(),
                                    activePids_.end(),
                                    pid),
                        activePids_.end());
   }
   END_LOCK_MUTEX
}

std::vector<PidType> SessionManager::activePids()
{
   LOCK_MUTEX(pidsMutex_)
   {
      return activePids_;
   }
   END_LOCK_MUTEX

   // keep compiler happy
   return std::vector<PidType>();
}

Error launchSession(const std::string& username,
                    const core::system::Options& extraArgs,
                    PidType* pPid)
{
   // last ditch user validation -- an invalid user should very rarely
   // get to this point since we pre-emptively validate on client_init
   if (!server::auth::validateUser(username))
   {
      Error error = systemError(boost::system::errc::permission_denied,
                                ERROR_LOCATION);
      error.addProperty("username", username);
      return error;
   }

   // prepare command line arguments
   server::Options& options = server::options();
   core::system::Options args ;

   // check for options-specified config file and add to command
   // line if specified
   std::string rsessionConfigFile(options.rsessionConfigFile());
   if (!rsessionConfigFile.empty())
      args.push_back(std::make_pair("--config-file", rsessionConfigFile));

   // pass the user-identity
   args.push_back(std::make_pair("-" kUserIdentitySessionOptionShort,
                                 username));

   // pass our uid to instruct rsession to limit rpc clients to us and itself
   core::system::Options environment;
   uid_t uid = core::system::user::currentUserIdentity().userId;
   environment.push_back(std::make_pair(
                           kRStudioLimitRpcClientUid,
                           boost::lexical_cast<std::string>(uid)));

   // pass extra params
   std::copy(extraArgs.begin(), extraArgs.end(), std::back_inserter(args));

   // append R environment variables
   core::system::Options rEnvVars = r_environment::variables();
   environment.insert(environment.end(), rEnvVars.begin(), rEnvVars.end());

   // launch the session
   *pPid = -1;
   std::string runAsUser = util::system::realUserIsRoot() ? username : "";
   util::system::ProcessConfig config;
   config.args = args;
   config.environment = environment;
   config.stdStreamBehavior = util::system::StdStreamInherit;
   config.memoryLimitBytes = static_cast<RLimitType>(
                               options.rsessionMemoryLimitMb() * 1024L * 1024L);
   config.stackLimitBytes = static_cast<RLimitType>(
                               options.rsessionStackLimitMb() * 1024L * 1024L);
   config.userProcessesLimit = static_cast<RLimitType>(
                               options.rsessionUserProcessLimit());
   return util::system::launchChildProcess(options.rsessionPath(),
                                           runAsUser,
                                           config,
                                           pPid) ;
}

Error launchSession(const std::string& username, PidType* pPid)
{
   return launchSession(username, core::system::Options(), pPid);
}

} // namespace server

<commit_msg>remove child exit logging<commit_after>/*
 * ServerSessionManager.cpp
 *
 * Copyright (C) 2009-11 by RStudio, Inc.
 *
 * This program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "ServerSessionManager.hpp"

#include <sys/wait.h>

#include <vector>

#include <boost/foreach.hpp>
#include <boost/format.hpp>

#include <core/Error.hpp>
#include <core/Log.hpp>
#include <core/system/System.hpp>
#include <core/system/PosixUser.hpp>

#include <session/SessionConstants.hpp>

#include <server/ServerOptions.hpp>

#include <server/util/system/User.hpp>

#include <server/auth/ServerValidateUser.hpp>

#include "ServerREnvironment.hpp"

using namespace core;

namespace server {

SessionManager& sessionManager()
{
   static SessionManager instance;
   return instance;
}

Error SessionManager::launchSession(const std::string& username)
{
   using namespace boost::posix_time;

   LOCK_MUTEX(launchesMutex_)
   {
      // check whether we already have a launch pending
      LaunchMap::const_iterator pos = pendingLaunches_.find(username);
      if (pos != pendingLaunches_.end())
      {
         // if the launch is less than one minute old then return success
         if ( (pos->second + boost::posix_time::minutes(1))
               > microsec_clock::universal_time() )
         {
            return Success();
         }
         // otherwise erase it from pending launches and then
         // re-launch (immediately below)
         else
         {
            // very unexpected condition
            LOG_WARNING_MESSAGE("Very long session launch delay for "
                                "user " + username + " (aborting wait)");

            pendingLaunches_.erase(username);
         }
      }

      // record the launch
      pendingLaunches_[username] =  microsec_clock::universal_time();
   }
   END_LOCK_MUTEX

   // launch the session
   PidType pid;
   Error error = server::launchSession(username, &pid);
   if (error)
   {
      removePendingLaunch(username);
      return error;
   }
   else
   {
      // add it to our active pids
      addActivePid(pid);

      // return success
      return Success();
   }
}

void SessionManager::removePendingLaunch(const std::string& username)
{
   LOCK_MUTEX(launchesMutex_)
   {
      pendingLaunches_.erase(username);
   }
   END_LOCK_MUTEX
}

namespace {

// wraper for waitPid which tries again for EINTR
int waitPid(PidType pid, int* pStatus)
{
   for (;;)
   {
      int result = ::waitpid(pid, pStatus, WNOHANG);
      if (result == -1 && errno == EINTR)
         continue;
      return result;
   }
}

} // anonymous namespace

void SessionManager::notifySIGCHLD()
{
   // We make a copy of hte active pids so that we can do the reaping
   // outside of the pidsMutex_. This is an extra conservative precaution
   // in case there is ever an issue with waitpid blocking.
   std::vector<PidType> pids = activePids();

   // reap all that we can
   BOOST_FOREACH(PidType pid, pids)
   {
      // non-blocking wait for the child
      int status;
      int result = waitPid(pid, &status);

      // reaped the child
      if (result == pid)
      {
         // confirm this was a real exit
         bool exited = false;
         if (WIFEXITED(status))
         {
            exited = true;
            status = WEXITSTATUS(status);
         }
         else if (WIFSIGNALED(status))
         {
            exited = true;
         }

         // if it was a real exit (as opposed to a SIGSTOP or SIGCONT)
         // then remove the pid from our table and fire the event
         if (exited)
         {
            // all done with this pid
            removeActivePid(pid);
         }
         else
         {
            boost::format fmt("Received SIGCHLD when child did not "
                              "actually exit (pid=%1%, status=%2%");
            LOG_WARNING_MESSAGE(boost::str(fmt % pid % status));
         }
      }
      // error occured
      else if (result == -1)
      {
         Error error = systemError(errno, ERROR_LOCATION);
         error.addProperty("pid", pid);
         LOG_ERROR(error);
      }
   }
}

void SessionManager::addActivePid(PidType pid)
{
   LOCK_MUTEX(pidsMutex_)
   {
      activePids_.push_back(pid);
   }
   END_LOCK_MUTEX
}

void SessionManager::removeActivePid(PidType pid)
{
   LOCK_MUTEX(pidsMutex_)
   {
      activePids_.erase(std::remove(activePids_.begin(),
                                    activePids_.end(),
                                    pid),
                        activePids_.end());
   }
   END_LOCK_MUTEX
}

std::vector<PidType> SessionManager::activePids()
{
   LOCK_MUTEX(pidsMutex_)
   {
      return activePids_;
   }
   END_LOCK_MUTEX

   // keep compiler happy
   return std::vector<PidType>();
}

Error launchSession(const std::string& username,
                    const core::system::Options& extraArgs,
                    PidType* pPid)
{
   // last ditch user validation -- an invalid user should very rarely
   // get to this point since we pre-emptively validate on client_init
   if (!server::auth::validateUser(username))
   {
      Error error = systemError(boost::system::errc::permission_denied,
                                ERROR_LOCATION);
      error.addProperty("username", username);
      return error;
   }

   // prepare command line arguments
   server::Options& options = server::options();
   core::system::Options args ;

   // check for options-specified config file and add to command
   // line if specified
   std::string rsessionConfigFile(options.rsessionConfigFile());
   if (!rsessionConfigFile.empty())
      args.push_back(std::make_pair("--config-file", rsessionConfigFile));

   // pass the user-identity
   args.push_back(std::make_pair("-" kUserIdentitySessionOptionShort,
                                 username));

   // pass our uid to instruct rsession to limit rpc clients to us and itself
   core::system::Options environment;
   uid_t uid = core::system::user::currentUserIdentity().userId;
   environment.push_back(std::make_pair(
                           kRStudioLimitRpcClientUid,
                           boost::lexical_cast<std::string>(uid)));

   // pass extra params
   std::copy(extraArgs.begin(), extraArgs.end(), std::back_inserter(args));

   // append R environment variables
   core::system::Options rEnvVars = r_environment::variables();
   environment.insert(environment.end(), rEnvVars.begin(), rEnvVars.end());

   // launch the session
   *pPid = -1;
   std::string runAsUser = util::system::realUserIsRoot() ? username : "";
   util::system::ProcessConfig config;
   config.args = args;
   config.environment = environment;
   config.stdStreamBehavior = util::system::StdStreamInherit;
   config.memoryLimitBytes = static_cast<RLimitType>(
                               options.rsessionMemoryLimitMb() * 1024L * 1024L);
   config.stackLimitBytes = static_cast<RLimitType>(
                               options.rsessionStackLimitMb() * 1024L * 1024L);
   config.userProcessesLimit = static_cast<RLimitType>(
                               options.rsessionUserProcessLimit());
   return util::system::launchChildProcess(options.rsessionPath(),
                                           runAsUser,
                                           config,
                                           pPid) ;
}

Error launchSession(const std::string& username, PidType* pPid)
{
   return launchSession(username, core::system::Options(), pPid);
}

} // namespace server

<|endoftext|>


<commit_before>#include "dronelink_impl.h"
#include "global_include.h"
#include <mutex>

namespace dronelink {

DroneLinkImpl::DroneLinkImpl() :
    _connections_mutex(),
    _connections(),
    _devices_mutex(),
    _devices(),
    _device_impls(),
    _on_discover_callback(nullptr),
    _on_timeout_callback(nullptr)
{}

DroneLinkImpl::~DroneLinkImpl()
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        _should_exit = true;

        for (auto it = _devices.begin(); it != _devices.end(); ++it) {
            delete it->second;
        }

        for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
            delete it->second;
        }
        _devices.clear();
        _device_impls.clear();
    }

    std::vector<Connection *> tmp_connections;
    {
        std::lock_guard<std::mutex> lock(_connections_mutex);

        // We need to copy the connections to a temporary vector. This way they won't
        // get used anymore while they are cleaned up.
        tmp_connections = _connections;
        _connections.clear();
    }

    for (auto connection : tmp_connections) {
        delete connection;
    }

}

void DroneLinkImpl::receive_message(const mavlink_message_t &message)
{
    // Don't ever create a device with sysid 0.
    if (message.sysid == 0) {
        return;
    }

    create_device_if_not_existing(message.sysid, message.compid);

    remove_empty_devices();
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);

        if (_should_exit) {
            // Don't try to call at() if devices have already been destroyed
            // in descructor.
            return;
        }

        if (message.sysid != 1) {
            Debug() << "sysid: " << int(message.sysid);
        }
        _device_impls.at(message.sysid)->process_mavlink_message(message);
    }
}

bool DroneLinkImpl::send_message(const mavlink_message_t &message)
{
    std::lock_guard<std::mutex> lock(_connections_mutex);

    for (auto it = _connections.begin(); it != _connections.end(); ++it) {
        if (!(**it).send_message(message)) {
            Debug() << "send fail";
            return false;
        }
    }

    return true;
}

void DroneLinkImpl::add_connection(Connection *new_connection)
{
    std::lock_guard<std::mutex> lock(_connections_mutex);
    _connections.push_back(new_connection);
}

const std::vector<uint64_t> &DroneLinkImpl::get_device_uuids() const
{
    // This needs to survive the scope but we need to clean it up.
    static std::vector<uint64_t> uuids = {};
    uuids.clear();

    for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
        uint64_t uuid = it->second->get_target_uuid();
        if (uuid != 0) {
            uuids.push_back(uuid);
        }
    }

    return uuids;
}

Device &DroneLinkImpl::get_device()
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        // In get_device withoiut uuid, we expect to have only
        // one device conneted.
        if (_device_impls.size() == 1) {
            return *(_devices.at(_device_impls.begin()->first));
        }

        if (_device_impls.size() > 1) {
            Debug() << "Error: more than one device found:";

            int i = 0;
            for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
                Debug() << "strange: " << i << ": " << int(it->first) << ", " << it->second;
                ++i;
            }

        } else {
            Debug() << "Error: no device found.";
        }
    }

    // In both failure cases, return a dummy.
    uint8_t system_id = 0;
    uint8_t component_id = 0;
    create_device_if_not_existing(system_id, component_id);

    return *_devices[system_id];
}

Device &DroneLinkImpl::get_device(uint64_t uuid)
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        // TODO: make a cache map for this.
        for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
            if (it->second->get_target_uuid() == uuid) {
                return *(_devices.at(it->first));
            }
        }
    }

    // We have not found a device with this UUID.
    // TODO: this is an error condition that we ought to handle properly.
    Debug() << "device with UUID: " << uuid << " not found";

    // Create a dummy
    uint8_t system_id = 0;
    uint8_t component_id = 0;
    create_device_if_not_existing(system_id, component_id);

    return *_devices[system_id];
}

void DroneLinkImpl::create_device_if_not_existing(uint8_t system_id, uint8_t component_id)
{
    std::lock_guard<std::mutex> lock(_devices_mutex);

    if (_should_exit) {
        // When the device_impl got destroyed in the destructor, we have to give up.
        return;
    }

    // existing already.
    if (_devices.find(system_id) != _devices.end()) {
        //Debug() << "ID: " << int(system_id) << " exists already.";
        return;
    }

    // Create both lists in parallel
    DeviceImpl *new_device_impl = new DeviceImpl(this, system_id, component_id);
    _device_impls.insert(std::pair<uint8_t, DeviceImpl *>(system_id, new_device_impl));

    Device *new_device = new Device(new_device_impl);
    _devices.insert(std::pair<uint8_t, Device *>(system_id, new_device));
}

void DroneLinkImpl::remove_empty_devices()
{
    std::lock_guard<std::mutex> lock(_devices_mutex);

    // Remove entries with system and component ID 0.
    if (_devices.find(0) != _devices.end()) {
        _devices.erase(0);
    }
    if (_device_impls.find(0) != _device_impls.end()) {
        _device_impls.erase(0);
    }
}

void DroneLinkImpl::notify_on_discover(uint64_t uuid)
{
    if (_on_discover_callback != nullptr) {
        _on_discover_callback(uuid);
    }
}

void DroneLinkImpl::notify_on_timeout(uint64_t uuid)
{
    if (_on_timeout_callback != nullptr) {
        _on_timeout_callback(uuid);
    }
}

void DroneLinkImpl::register_on_discover(DroneLink::event_callback_t callback)
{
    _on_discover_callback = callback;
}

void DroneLinkImpl::register_on_timeout(DroneLink::event_callback_t callback)
{
    _on_timeout_callback = callback;
}

} // namespace dronelink
<commit_msg>dronelink_impl: don't keep lock for too long<commit_after>#include "dronelink_impl.h"
#include "global_include.h"
#include <mutex>

namespace dronelink {

DroneLinkImpl::DroneLinkImpl() :
    _connections_mutex(),
    _connections(),
    _devices_mutex(),
    _devices(),
    _device_impls(),
    _on_discover_callback(nullptr),
    _on_timeout_callback(nullptr)
{}

DroneLinkImpl::~DroneLinkImpl()
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        _should_exit = true;

        for (auto it = _devices.begin(); it != _devices.end(); ++it) {
            delete it->second;
        }

        for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
            delete it->second;
        }
        _devices.clear();
        _device_impls.clear();
    }

    std::vector<Connection *> tmp_connections;
    {
        std::lock_guard<std::mutex> lock(_connections_mutex);

        // We need to copy the connections to a temporary vector. This way they won't
        // get used anymore while they are cleaned up.
        tmp_connections = _connections;
        _connections.clear();
    }

    for (auto connection : tmp_connections) {
        delete connection;
    }

}

void DroneLinkImpl::receive_message(const mavlink_message_t &message)
{
    // Don't ever create a device with sysid 0.
    if (message.sysid == 0) {
        return;
    }

    create_device_if_not_existing(message.sysid, message.compid);

    remove_empty_devices();

    if (_should_exit) {
        // Don't try to call at() if devices have already been destroyed
        // in descructor.
        return;
    }

    _devices_mutex.lock();

    if (message.sysid != 1) {
        Debug() << "sysid: " << int(message.sysid);
    }
    auto &device_impl = _device_impls.at(message.sysid);

    // We need to release the lock, otherwise we can't send
    // messages inside process_mavlink_message.
    _devices_mutex.unlock();

    device_impl->process_mavlink_message(message);
}

bool DroneLinkImpl::send_message(const mavlink_message_t &message)
{
    std::lock_guard<std::mutex> lock(_connections_mutex);

    for (auto it = _connections.begin(); it != _connections.end(); ++it) {
        if (!(**it).send_message(message)) {
            Debug() << "send fail";
            return false;
        }
    }

    return true;
}

void DroneLinkImpl::add_connection(Connection *new_connection)
{
    std::lock_guard<std::mutex> lock(_connections_mutex);
    _connections.push_back(new_connection);
}

const std::vector<uint64_t> &DroneLinkImpl::get_device_uuids() const
{
    // This needs to survive the scope but we need to clean it up.
    static std::vector<uint64_t> uuids = {};
    uuids.clear();

    for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
        uint64_t uuid = it->second->get_target_uuid();
        if (uuid != 0) {
            uuids.push_back(uuid);
        }
    }

    return uuids;
}

Device &DroneLinkImpl::get_device()
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        // In get_device withoiut uuid, we expect to have only
        // one device conneted.
        if (_device_impls.size() == 1) {
            return *(_devices.at(_device_impls.begin()->first));
        }

        if (_device_impls.size() > 1) {
            Debug() << "Error: more than one device found:";

            int i = 0;
            for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
                Debug() << "strange: " << i << ": " << int(it->first) << ", " << it->second;
                ++i;
            }

        } else {
            Debug() << "Error: no device found.";
        }
    }

    // In both failure cases, return a dummy.
    uint8_t system_id = 0;
    uint8_t component_id = 0;
    create_device_if_not_existing(system_id, component_id);

    return *_devices[system_id];
}

Device &DroneLinkImpl::get_device(uint64_t uuid)
{
    {
        std::lock_guard<std::mutex> lock(_devices_mutex);
        // TODO: make a cache map for this.
        for (auto it = _device_impls.begin(); it != _device_impls.end(); ++it) {
            if (it->second->get_target_uuid() == uuid) {
                return *(_devices.at(it->first));
            }
        }
    }

    // We have not found a device with this UUID.
    // TODO: this is an error condition that we ought to handle properly.
    Debug() << "device with UUID: " << uuid << " not found";

    // Create a dummy
    uint8_t system_id = 0;
    uint8_t component_id = 0;
    create_device_if_not_existing(system_id, component_id);

    return *_devices[system_id];
}

void DroneLinkImpl::create_device_if_not_existing(uint8_t system_id, uint8_t component_id)
{
    std::lock_guard<std::mutex> lock(_devices_mutex);

    if (_should_exit) {
        // When the device_impl got destroyed in the destructor, we have to give up.
        return;
    }

    // existing already.
    if (_devices.find(system_id) != _devices.end()) {
        //Debug() << "ID: " << int(system_id) << " exists already.";
        return;
    }

    // Create both lists in parallel
    DeviceImpl *new_device_impl = new DeviceImpl(this, system_id, component_id);
    _device_impls.insert(std::pair<uint8_t, DeviceImpl *>(system_id, new_device_impl));

    Device *new_device = new Device(new_device_impl);
    _devices.insert(std::pair<uint8_t, Device *>(system_id, new_device));
}

void DroneLinkImpl::remove_empty_devices()
{
    std::lock_guard<std::mutex> lock(_devices_mutex);

    // Remove entries with system and component ID 0.
    if (_devices.find(0) != _devices.end()) {
        _devices.erase(0);
    }
    if (_device_impls.find(0) != _device_impls.end()) {
        _device_impls.erase(0);
    }
}

void DroneLinkImpl::notify_on_discover(uint64_t uuid)
{
    if (_on_discover_callback != nullptr) {
        _on_discover_callback(uuid);
    }
}

void DroneLinkImpl::notify_on_timeout(uint64_t uuid)
{
    if (_on_timeout_callback != nullptr) {
        _on_timeout_callback(uuid);
    }
}

void DroneLinkImpl::register_on_discover(DroneLink::event_callback_t callback)
{
    _on_discover_callback = callback;
}

void DroneLinkImpl::register_on_timeout(DroneLink::event_callback_t callback)
{
    _on_timeout_callback = callback;
}

} // namespace dronelink
<|endoftext|>


<commit_before>/*
  Copyright (C) 2008 by Marten Svanfeldt

  This library 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 library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include "csgeom/math.h"

#include "fsmnode.h"

template <>
class csHashComputer<CS::Plugin::Skeleton2::FSMNodeFactory::StateTransitionKey>
{
public:
  /// Compute a hash value for \a key.
  static uint ComputeHash (const CS::Plugin::Skeleton2::FSMNodeFactory::StateTransitionKey& key)
  {
    return (key.fromState ^ key.toState) ^ 0xABCDEF98;
  }
};

CS_PLUGIN_NAMESPACE_BEGIN(Skeleton2)
{
  AnimationFifo::AnimationFifo (AnimationFifoCb* cb)
    : currentState (STATE_STOPPED), cb (cb)
  {}

  void AnimationFifo::PushAnimation (CS::Animation::iSkeletonAnimNode* node, bool directSwitch,
    float blendInTime, size_t cbData)
  {
    if (!node)
      return;

    AnimationInstruction newInstr = {node, cbData, blendInTime, directSwitch};
    instructions.Push (newInstr);

    if (currentState == STATE_STOPPED)
    {
      currentState = STATE_PLAYING;
      currentAnimation = instructions.PopTop ();
      currentAnimation.node->Play ();
    }
  }

  void AnimationFifo::RemoveAnimations (size_t cbData)
  {
    AnimationInstruction instruction;
    instruction.cbData = cbData;

    // There can be at most two animations
    instructions.Delete (instruction);
    instructions.Delete (instruction);
  }

  void AnimationFifo::BlendState (CS::Animation::csSkeletalState* state, float baseWeight /* = 1.0f */)
  {
    switch (currentState)
    {
    case STATE_STOPPED:
      break;
    case STATE_PLAYING:
      {
        currentAnimation.node->BlendState (state, baseWeight);
      }
      break;
    case STATE_BLENDING:
      {
        if (instructions.GetSize () > 0)
        {
          AnimationInstruction& top = instructions.Top ();

          const float blendAmount = csClamp (blendTime / top.blendInTime, 1.0f, 0.0f);
          currentAnimation.node->BlendState (state, baseWeight);
          top.node->BlendState (state, baseWeight * blendAmount);
        }
        else
        {
          currentAnimation.node->BlendState (state, baseWeight);
        }
      }
      break;
    }
  }

  void AnimationFifo::TickAnimation (float dt)
  {
    switch (currentState)
    {
    case STATE_STOPPED:
      break;
    case STATE_PLAYING:
      {
        if (instructions.GetSize () > 0)
        {
          AnimationInstruction& top = instructions.Top ();

          if (top.directSwitch ||
	      currentAnimation.node->GetPlaybackPosition ()
	      + dt * currentAnimation.node->GetPlaybackSpeed ()
	      + top.blendInTime
	      > currentAnimation.node->GetDuration ())
          {
            if (top.blendInTime > SMALL_EPSILON)
            {
              // Blend
              currentState = STATE_BLENDING;
              blendTime = 0;
              top.node->Play ();
            }
            else
            {
              // Switch
	      if (currentAnimation.node)
		currentAnimation.node->Stop ();
              currentAnimation = instructions.PopTop ();
              currentAnimation.node->Play ();

              if (cb)
              {
                cb->NewAnimation (currentAnimation.cbData);
              }
            }
          }
        }

        currentAnimation.node->TickAnimation (dt);
      }
      break;
    case STATE_BLENDING:
      {
        AnimationInstruction& top = instructions.Top ();

        if (blendTime + dt > top.blendInTime)
        {
          // Blend over, finalize switch
          currentAnimation.node->TickAnimation (dt);
	  currentAnimation.node->Stop ();
          currentAnimation = instructions.PopTop ();
          if (cb)
          {
            cb->NewAnimation (currentAnimation.cbData);
          }

          currentAnimation.node->TickAnimation (dt);

          currentState = STATE_PLAYING;
        }
        else
        {
          // Tick both
          currentAnimation.node->TickAnimation (dt);
          top.node->TickAnimation (dt);
	  blendTime += dt;
        }
      }
      break;
    }

    if (cb && !instructions.GetSize ())
      cb->InstructionQueueEmpty (currentAnimation.cbData);
  }

  void AnimationFifo::Stop ()
  {
    currentState = STATE_STOPPED;
    currentAnimation.node = 0;
    instructions.DeleteAll ();
  }

  CS_LEAKGUARD_IMPLEMENT(FSMNodeFactory);

  FSMNodeFactory::FSMNodeFactory (const char* name)
    : scfImplementationType (this), name (name),
    startState (CS::Animation::InvalidStateID)
  {}

  CS::Animation::StateID FSMNodeFactory::AddState ()
  {
    State newState;

    return (CS::Animation::StateID)stateList.Push (newState);
  }

  CS::Animation::StateID FSMNodeFactory::AddState (const char* name,
						   CS::Animation::iSkeletonAnimNodeFactory *nodeFact)
  {
    State newState;
    CS::Animation::StateID id = (CS::Animation::StateID)stateList.Push (newState);
    stateList[id].name = name;
    stateList[id].nodeFactory = nodeFact;
    return id;
  }

  void FSMNodeFactory::SetStateNode (CS::Animation::StateID id,
    CS::Animation::iSkeletonAnimNodeFactory* nodeFact)
  {
    CS_ASSERT(id < stateList.GetSize ());

    stateList[id].nodeFactory = nodeFact;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNodeFactory::GetStateNode (CS::Animation::StateID id) const
  {
    CS_ASSERT(id < stateList.GetSize ());

    return stateList[id].nodeFactory;
  }

  void FSMNodeFactory::SetStateName (CS::Animation::StateID id, const char* name)
  {
    CS_ASSERT(id < stateList.GetSize ());

    stateList[id].name = name;
  }

  const char* FSMNodeFactory::GetStateName (CS::Animation::StateID id) const
  {
    CS_ASSERT(id < stateList.GetSize ());

    return stateList[id].name;
  }

  CS::Animation::StateID FSMNodeFactory::FindState (const char* name) const
  {
    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      if (stateList[i].name == name)
      {
        return (CS::Animation::StateID)i;
      }
    }

    return CS::Animation::InvalidStateID;
  }

  void FSMNodeFactory::SetStartState (CS::Animation::StateID id)
  {
    CS_ASSERT(id < stateList.GetSize ());
    startState = id;
  }

  CS::Animation::StateID FSMNodeFactory::GetStartState () const
  {
    return startState;
  }

  uint FSMNodeFactory::GetStateCount () const
  {
    return (uint)stateList.GetSize ();
  }

  void FSMNodeFactory::ClearStates ()
  {
    stateList.DeleteAll ();
  }

  csPtr<CS::Animation::iSkeletonAnimNode> FSMNodeFactory::CreateInstance (
    CS::Animation::iSkeletonAnimPacket* packet, CS::Animation::iSkeleton* skeleton)
  {
    csRef<FSMNode> newn;

    newn.AttachNew (new FSMNode (this));

    // Setup nodes
    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      FSMNode::State newState;
      if (stateList[i].nodeFactory)
	newState.stateNode = stateList[i].nodeFactory->CreateInstance (packet, skeleton);
      newn->stateList.Push (newState);
    }

    // Setup all the transitions
    {
      csHash<StateTransitionInfo, StateTransitionKey>::GlobalIterator it =
        transitions.GetIterator ();

      while (it.HasNext ())
      {
        StateTransitionKey key;
        StateTransitionInfo& info = it.Next (key);

        FSMNode::StateTransitionInfo& childInfo = newn->transitions.GetOrCreate (key);

        childInfo.transitionNode = info.nodeFactory ?
          info.nodeFactory->CreateInstance (packet, skeleton) : 0;
        childInfo.time1 = info.time1;
        childInfo.time2 = info.time2;
        //childInfo.directSwitch = info.directSwitch;
      }
    }

    // Setup all the automatic transitions
    {
      csHash<CS::Animation::StateID, CS::Animation::StateID>::GlobalIterator it =
        automaticTransitions.GetIterator ();

      while (it.HasNext ())
      {
	CS::Animation::StateID fromState;
	CS::Animation::StateID toState = it.Next (fromState);

	if (toState != CS::Animation::InvalidStateID)
	  newn->automaticTransitions.Put (fromState, toState);
      }
    }

    return csPtr<CS::Animation::iSkeletonAnimNode> (newn);
  }

  const char* FSMNodeFactory::GetNodeName () const
  {
    return name;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNodeFactory::FindNode (const char* name)
  {
    if (this->name == name)
      return this;

    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      CS::Animation::iSkeletonAnimNodeFactory* r = stateList[i].nodeFactory;
      if (r)
      {
        r = r->FindNode (name);

        if (r)
          return r;
      }
    }

    return 0;
  }

  void FSMNodeFactory::SetStateTransition (CS::Animation::StateID fromState,
    CS::Animation::StateID toState, CS::Animation::iSkeletonAnimNodeFactory* fact)
  {
    StateTransitionKey key = {fromState, toState};
    StateTransitionInfo& info = transitions.GetOrCreate (key);

    info.nodeFactory = fact;
  }

  void FSMNodeFactory::SetTransitionCrossfade (CS::Animation::StateID fromState,
    CS::Animation::StateID toState, float time1, float time2)
  {
    StateTransitionKey key = {fromState, toState};
    StateTransitionInfo& info = transitions.GetOrCreate (key);

    info.time1 = time1;
    info.time2 = time2;
  }

  void FSMNodeFactory::SetAutomaticTransition (CS::Animation::StateID fromState,
					       CS::Animation::StateID toState,
					       bool automatic)
  {
    if (automaticTransitions.Contains (fromState))
    {
      if (*automaticTransitions[fromState] == toState)
      {
	if (!automatic)
	  automaticTransitions.DeleteAll (fromState);
      }

      else
	*automaticTransitions[fromState] = CS::Animation::InvalidStateID;
    }
    else if (automatic)
      automaticTransitions.Put (fromState, toState);
  }

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

  CS_LEAKGUARD_IMPLEMENT(FSMNode);

  FSMNode::FSMNode (FSMNodeFactory* factory)
    : scfImplementationType (this), BaseNodeSingle (this), factory (factory),
    currentState (factory->startState), automaticState (CS::Animation::InvalidStateID),
    playbackSpeed (1.0f), isActive (false), blendFifo (this)
  {}

  void FSMNode::SwitchToState (CS::Animation::StateID newState)
  {
    if (automaticState != CS::Animation::InvalidStateID)
    {
      blendFifo.RemoveAnimations (automaticState);
      automaticState = CS::Animation::InvalidStateID;
    }
    SwitchToState (newState, true);
  }

  void FSMNode::SwitchToState (CS::Animation::StateID newState, bool directSwitch)
  {
    CS_ASSERT(newState < stateList.GetSize ());

    if (currentState == newState)
      return;

    if (isActive)
    {
      FSMNodeFactory::StateTransitionKey key = {currentState, newState};
      StateTransitionInfo* info = transitions.GetElementPointer (key);

      if (info)
      {
        // Not a direct transition
        if (info->transitionNode)
        {
          // Use transition animation
          blendFifo.PushAnimation (info->transitionNode, directSwitch, info->time1, newState);
          blendFifo.PushAnimation (stateList[newState].stateNode, false, info->time2, newState);
        }
        else
        {
          blendFifo.PushAnimation (stateList[newState].stateNode, directSwitch, info->time1, newState);
        }
      }
      else
      {
         blendFifo.PushAnimation (stateList[newState].stateNode, directSwitch, 0, newState);
      }
    }
    else
    {
      currentState = newState;
    }
  }

  CS::Animation::StateID FSMNode::GetCurrentState () const
  {
    return currentState;
  }

  CS::Animation::iSkeletonAnimNode* FSMNode::GetStateNode (CS::Animation::StateID state) const
  {
    CS_ASSERT(state < stateList.GetSize ());
    return stateList[state].stateNode;
  }

  void FSMNode::Play ()
  {
    if (isActive)
      return;

    if (currentState != CS::Animation::InvalidStateID)
    {
      blendFifo.PushAnimation (stateList[currentState].stateNode, true, 0, currentState);
    }

    isActive = true;
  }

  void FSMNode::Stop ()
  {
    if (!isActive)
      return;

    isActive = false;
    blendFifo.Stop ();
  }

  void FSMNode::SetPlaybackPosition (float time)
  {
    stateList[currentState].stateNode->SetPlaybackPosition (time);
  }

  float FSMNode::GetPlaybackPosition () const
  {
    return stateList[currentState].stateNode->GetPlaybackPosition ();
  }

  float FSMNode::GetDuration () const
  {
    return stateList[currentState].stateNode->GetDuration ();
  }

  void FSMNode::SetPlaybackSpeed (float speed)
  {
    playbackSpeed = speed;
  }

  float FSMNode::GetPlaybackSpeed () const
  {
    return playbackSpeed;
  }

  void FSMNode::BlendState (CS::Animation::csSkeletalState* state, float baseWeight)
  {
    if (!isActive)
      return;

    blendFifo.BlendState (state, baseWeight);
  }

  void FSMNode::TickAnimation (float dt)
  {
    if (!isActive)
      return;

    blendFifo.TickAnimation (dt * playbackSpeed);
  }

  bool FSMNode::IsActive () const
  {
    return isActive;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNode::GetFactory () const
  {
    return factory;
  }

  CS::Animation::iSkeletonAnimNode* FSMNode::FindNode (const char* name)
  {
    if (factory->name == name)
      return this;

    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      CS::Animation::iSkeletonAnimNode* r = stateList[i].stateNode;
      if (r)
      {
        r = r->FindNode (name);

        if (r)
          return r;
      }
    }

    return 0;
  }

  void FSMNode::AddAnimationCallback (CS::Animation::iSkeletonAnimCallback* callback)
  {
    BaseNodeSingle::AddAnimationCallback (callback);
  }

  void FSMNode::RemoveAnimationCallback (CS::Animation::iSkeletonAnimCallback* callback)
  {
    BaseNodeSingle::AddAnimationCallback (callback);
  }

  void FSMNode::NewAnimation (size_t data)
  {
    currentState = (CS::Animation::StateID) data;
  }

  void FSMNode::InstructionQueueEmpty (size_t data)
  {
    // Check if we can find an automatic transition to add to the queue
    if (automaticTransitions.Contains ((CS::Animation::StateID) data))
    {
      automaticState = (CS::Animation::StateID) data;
      SwitchToState (*automaticTransitions[(CS::Animation::StateID) data], false);
    }
  }
}
CS_PLUGIN_NAMESPACE_END(Skeleton2)
<commit_msg>Fixed the returned current state<commit_after>/*
  Copyright (C) 2008 by Marten Svanfeldt

  This library 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 library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "cssysdef.h"

#include "csgeom/math.h"

#include "fsmnode.h"

template <>
class csHashComputer<CS::Plugin::Skeleton2::FSMNodeFactory::StateTransitionKey>
{
public:
  /// Compute a hash value for \a key.
  static uint ComputeHash (const CS::Plugin::Skeleton2::FSMNodeFactory::StateTransitionKey& key)
  {
    return (key.fromState ^ key.toState) ^ 0xABCDEF98;
  }
};

CS_PLUGIN_NAMESPACE_BEGIN(Skeleton2)
{
  AnimationFifo::AnimationFifo (AnimationFifoCb* cb)
    : currentState (STATE_STOPPED), cb (cb)
  {}

  void AnimationFifo::PushAnimation (CS::Animation::iSkeletonAnimNode* node, bool directSwitch,
    float blendInTime, size_t cbData)
  {
    if (!node)
      return;

    AnimationInstruction newInstr = {node, cbData, blendInTime, directSwitch};
    instructions.Push (newInstr);

    if (currentState == STATE_STOPPED)
    {
      currentState = STATE_PLAYING;
      currentAnimation = instructions.PopTop ();
      currentAnimation.node->Play ();

      if (cb)
	cb->NewAnimation (currentAnimation.cbData);
    }
  }

  void AnimationFifo::RemoveAnimations (size_t cbData)
  {
    AnimationInstruction instruction;
    instruction.cbData = cbData;

    // There can be at most two animations
    instructions.Delete (instruction);
    instructions.Delete (instruction);
  }

  void AnimationFifo::BlendState (CS::Animation::csSkeletalState* state, float baseWeight /* = 1.0f */)
  {
    switch (currentState)
    {
    case STATE_STOPPED:
      break;
    case STATE_PLAYING:
      {
        currentAnimation.node->BlendState (state, baseWeight);
      }
      break;
    case STATE_BLENDING:
      {
        if (instructions.GetSize () > 0)
        {
          AnimationInstruction& top = instructions.Top ();

          const float blendAmount = csClamp (blendTime / top.blendInTime, 1.0f, 0.0f);
          currentAnimation.node->BlendState (state, baseWeight);
          top.node->BlendState (state, baseWeight * blendAmount);
        }
        else
        {
          currentAnimation.node->BlendState (state, baseWeight);
        }
      }
      break;
    }
  }

  void AnimationFifo::TickAnimation (float dt)
  {
    switch (currentState)
    {
    case STATE_STOPPED:
      break;
    case STATE_PLAYING:
      {
        if (instructions.GetSize () > 0)
        {
          AnimationInstruction& top = instructions.Top ();

          if (top.directSwitch ||
	      currentAnimation.node->GetPlaybackPosition ()
	      + dt * currentAnimation.node->GetPlaybackSpeed ()
	      + top.blendInTime
	      > currentAnimation.node->GetDuration ())
          {
            if (top.blendInTime > SMALL_EPSILON)
            {
              // Blend
              currentState = STATE_BLENDING;
              blendTime = 0;
              top.node->Play ();

              if (cb)
                cb->NewAnimation (top.cbData);
            }
            else
            {
              // Switch
	      if (currentAnimation.node)
		currentAnimation.node->Stop ();
              currentAnimation = instructions.PopTop ();
              currentAnimation.node->Play ();

              if (cb)
                cb->NewAnimation (currentAnimation.cbData);
            }
          }
        }

        currentAnimation.node->TickAnimation (dt);
      }
      break;
    case STATE_BLENDING:
      {
        AnimationInstruction& top = instructions.Top ();

        if (blendTime + dt > top.blendInTime)
        {
          // Blend over, finalize switch
          currentAnimation.node->TickAnimation (dt);
	  currentAnimation.node->Stop ();
          currentAnimation = instructions.PopTop ();
          if (cb)
          {
            cb->NewAnimation (currentAnimation.cbData);
          }

          currentAnimation.node->TickAnimation (dt);

          currentState = STATE_PLAYING;
        }
        else
        {
          // Tick both
          currentAnimation.node->TickAnimation (dt);
          top.node->TickAnimation (dt);
	  blendTime += dt;
        }
      }
      break;
    }

    if (cb && !instructions.GetSize ())
      cb->InstructionQueueEmpty (currentAnimation.cbData);
  }

  void AnimationFifo::Stop ()
  {
    currentState = STATE_STOPPED;
    currentAnimation.node = 0;
    instructions.DeleteAll ();
  }

  CS_LEAKGUARD_IMPLEMENT(FSMNodeFactory);

  FSMNodeFactory::FSMNodeFactory (const char* name)
    : scfImplementationType (this), name (name),
    startState (CS::Animation::InvalidStateID)
  {}

  CS::Animation::StateID FSMNodeFactory::AddState ()
  {
    State newState;

    return (CS::Animation::StateID)stateList.Push (newState);
  }

  CS::Animation::StateID FSMNodeFactory::AddState (const char* name,
						   CS::Animation::iSkeletonAnimNodeFactory *nodeFact)
  {
    State newState;
    CS::Animation::StateID id = (CS::Animation::StateID)stateList.Push (newState);
    stateList[id].name = name;
    stateList[id].nodeFactory = nodeFact;
    return id;
  }

  void FSMNodeFactory::SetStateNode (CS::Animation::StateID id,
    CS::Animation::iSkeletonAnimNodeFactory* nodeFact)
  {
    CS_ASSERT(id < stateList.GetSize ());

    stateList[id].nodeFactory = nodeFact;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNodeFactory::GetStateNode (CS::Animation::StateID id) const
  {
    CS_ASSERT(id < stateList.GetSize ());

    return stateList[id].nodeFactory;
  }

  void FSMNodeFactory::SetStateName (CS::Animation::StateID id, const char* name)
  {
    CS_ASSERT(id < stateList.GetSize ());

    stateList[id].name = name;
  }

  const char* FSMNodeFactory::GetStateName (CS::Animation::StateID id) const
  {
    CS_ASSERT(id < stateList.GetSize ());

    return stateList[id].name;
  }

  CS::Animation::StateID FSMNodeFactory::FindState (const char* name) const
  {
    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      if (stateList[i].name == name)
      {
        return (CS::Animation::StateID)i;
      }
    }

    return CS::Animation::InvalidStateID;
  }

  void FSMNodeFactory::SetStartState (CS::Animation::StateID id)
  {
    CS_ASSERT(id < stateList.GetSize ());
    startState = id;
  }

  CS::Animation::StateID FSMNodeFactory::GetStartState () const
  {
    return startState;
  }

  uint FSMNodeFactory::GetStateCount () const
  {
    return (uint)stateList.GetSize ();
  }

  void FSMNodeFactory::ClearStates ()
  {
    stateList.DeleteAll ();
  }

  csPtr<CS::Animation::iSkeletonAnimNode> FSMNodeFactory::CreateInstance (
    CS::Animation::iSkeletonAnimPacket* packet, CS::Animation::iSkeleton* skeleton)
  {
    csRef<FSMNode> newn;

    newn.AttachNew (new FSMNode (this));

    // Setup nodes
    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      FSMNode::State newState;
      if (stateList[i].nodeFactory)
	newState.stateNode = stateList[i].nodeFactory->CreateInstance (packet, skeleton);
      newn->stateList.Push (newState);
    }

    // Setup all the transitions
    {
      csHash<StateTransitionInfo, StateTransitionKey>::GlobalIterator it =
        transitions.GetIterator ();

      while (it.HasNext ())
      {
        StateTransitionKey key;
        StateTransitionInfo& info = it.Next (key);

        FSMNode::StateTransitionInfo& childInfo = newn->transitions.GetOrCreate (key);

        childInfo.transitionNode = info.nodeFactory ?
          info.nodeFactory->CreateInstance (packet, skeleton) : 0;
        childInfo.time1 = info.time1;
        childInfo.time2 = info.time2;
        //childInfo.directSwitch = info.directSwitch;
      }
    }

    // Setup all the automatic transitions
    {
      csHash<CS::Animation::StateID, CS::Animation::StateID>::GlobalIterator it =
        automaticTransitions.GetIterator ();

      while (it.HasNext ())
      {
	CS::Animation::StateID fromState;
	CS::Animation::StateID toState = it.Next (fromState);

	if (toState != CS::Animation::InvalidStateID)
	  newn->automaticTransitions.Put (fromState, toState);
      }
    }

    return csPtr<CS::Animation::iSkeletonAnimNode> (newn);
  }

  const char* FSMNodeFactory::GetNodeName () const
  {
    return name;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNodeFactory::FindNode (const char* name)
  {
    if (this->name == name)
      return this;

    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      CS::Animation::iSkeletonAnimNodeFactory* r = stateList[i].nodeFactory;
      if (r)
      {
        r = r->FindNode (name);

        if (r)
          return r;
      }
    }

    return 0;
  }

  void FSMNodeFactory::SetStateTransition (CS::Animation::StateID fromState,
    CS::Animation::StateID toState, CS::Animation::iSkeletonAnimNodeFactory* fact)
  {
    StateTransitionKey key = {fromState, toState};
    StateTransitionInfo& info = transitions.GetOrCreate (key);

    info.nodeFactory = fact;
  }

  void FSMNodeFactory::SetTransitionCrossfade (CS::Animation::StateID fromState,
    CS::Animation::StateID toState, float time1, float time2)
  {
    StateTransitionKey key = {fromState, toState};
    StateTransitionInfo& info = transitions.GetOrCreate (key);

    info.time1 = time1;
    info.time2 = time2;
  }

  void FSMNodeFactory::SetAutomaticTransition (CS::Animation::StateID fromState,
					       CS::Animation::StateID toState,
					       bool automatic)
  {
    if (automaticTransitions.Contains (fromState))
    {
      if (*automaticTransitions[fromState] == toState)
      {
	if (!automatic)
	  automaticTransitions.DeleteAll (fromState);
      }

      else
	*automaticTransitions[fromState] = CS::Animation::InvalidStateID;
    }
    else if (automatic)
      automaticTransitions.Put (fromState, toState);
  }

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

  CS_LEAKGUARD_IMPLEMENT(FSMNode);

  FSMNode::FSMNode (FSMNodeFactory* factory)
    : scfImplementationType (this), BaseNodeSingle (this), factory (factory),
    currentState (factory->startState), automaticState (CS::Animation::InvalidStateID),
    playbackSpeed (1.0f), isActive (false), blendFifo (this)
  {}

  void FSMNode::SwitchToState (CS::Animation::StateID newState)
  {
    if (automaticState != CS::Animation::InvalidStateID)
    {
      blendFifo.RemoveAnimations (automaticState);
      automaticState = CS::Animation::InvalidStateID;
    }
    SwitchToState (newState, true);
  }

  void FSMNode::SwitchToState (CS::Animation::StateID newState, bool directSwitch)
  {
    CS_ASSERT(newState < stateList.GetSize ());

    if (currentState == newState)
      return;

    if (isActive)
    {
      FSMNodeFactory::StateTransitionKey key = {currentState, newState};
      StateTransitionInfo* info = transitions.GetElementPointer (key);

      if (info)
      {
        // Not a direct transition
        if (info->transitionNode)
        {
          // Use transition animation
          blendFifo.PushAnimation (info->transitionNode, directSwitch, info->time1, newState);
          blendFifo.PushAnimation (stateList[newState].stateNode, false, info->time2, newState);
        }
        else
        {
          blendFifo.PushAnimation (stateList[newState].stateNode, directSwitch, info->time1, newState);
        }
      }
      else
      {
         blendFifo.PushAnimation (stateList[newState].stateNode, directSwitch, 0, newState);
      }
    }
    else
    {
      currentState = newState;
    }
  }

  CS::Animation::StateID FSMNode::GetCurrentState () const
  {
    return currentState;
  }

  CS::Animation::iSkeletonAnimNode* FSMNode::GetStateNode (CS::Animation::StateID state) const
  {
    CS_ASSERT(state < stateList.GetSize ());
    return stateList[state].stateNode;
  }

  void FSMNode::Play ()
  {
    if (isActive)
      return;

    if (currentState != CS::Animation::InvalidStateID)
    {
      blendFifo.PushAnimation (stateList[currentState].stateNode, true, 0, currentState);
    }

    isActive = true;
  }

  void FSMNode::Stop ()
  {
    if (!isActive)
      return;

    isActive = false;
    blendFifo.Stop ();
  }

  void FSMNode::SetPlaybackPosition (float time)
  {
    stateList[currentState].stateNode->SetPlaybackPosition (time);
  }

  float FSMNode::GetPlaybackPosition () const
  {
    return stateList[currentState].stateNode->GetPlaybackPosition ();
  }

  float FSMNode::GetDuration () const
  {
    return stateList[currentState].stateNode->GetDuration ();
  }

  void FSMNode::SetPlaybackSpeed (float speed)
  {
    playbackSpeed = speed;
  }

  float FSMNode::GetPlaybackSpeed () const
  {
    return playbackSpeed;
  }

  void FSMNode::BlendState (CS::Animation::csSkeletalState* state, float baseWeight)
  {
    if (!isActive)
      return;

    blendFifo.BlendState (state, baseWeight);
  }

  void FSMNode::TickAnimation (float dt)
  {
    if (!isActive)
      return;

    blendFifo.TickAnimation (dt * playbackSpeed);
  }

  bool FSMNode::IsActive () const
  {
    return isActive;
  }

  CS::Animation::iSkeletonAnimNodeFactory* FSMNode::GetFactory () const
  {
    return factory;
  }

  CS::Animation::iSkeletonAnimNode* FSMNode::FindNode (const char* name)
  {
    if (factory->name == name)
      return this;

    for (size_t i = 0; i < stateList.GetSize (); ++i)
    {
      CS::Animation::iSkeletonAnimNode* r = stateList[i].stateNode;
      if (r)
      {
        r = r->FindNode (name);

        if (r)
          return r;
      }
    }

    return 0;
  }

  void FSMNode::AddAnimationCallback (CS::Animation::iSkeletonAnimCallback* callback)
  {
    BaseNodeSingle::AddAnimationCallback (callback);
  }

  void FSMNode::RemoveAnimationCallback (CS::Animation::iSkeletonAnimCallback* callback)
  {
    BaseNodeSingle::AddAnimationCallback (callback);
  }

  void FSMNode::NewAnimation (size_t data)
  {
    currentState = (CS::Animation::StateID) data;
  }

  void FSMNode::InstructionQueueEmpty (size_t data)
  {
    // Check if we can find an automatic transition to add to the queue
    if (automaticTransitions.Contains ((CS::Animation::StateID) data))
    {
      automaticState = (CS::Animation::StateID) data;
      SwitchToState (*automaticTransitions[(CS::Animation::StateID) data], false);
    }
  }
}
CS_PLUGIN_NAMESPACE_END(Skeleton2)
<|endoftext|>


<commit_before>#include "Halide.h"

namespace {

  using namespace Halide;

  template <typename T>
  class Enlarge : public Halide::Generator<Enlarge<T>>
  {
  public:
    GeneratorParam<int> tile_x{"tile_x", 32};
    GeneratorParam<int> tile_y{"tile_y", 32};

    Input<Buffer<T>> input{"f", 2};
    Output<Buffer<T>> output{"g", 2};

    Var x{"x"}, y{"y"}, c{"c"}, xo{"xo"}, yo{"yo"}, xi{"xi"}, yi{"yi"};

    void generate()
    {
      const float w_in = input.width();
      const float h_in = input.height();

      const float w_out = input.width();
      const float h_out = input.height();

      auto xx = clamp(x, 0, w_out - 1) * w_in / w_out;
      auto yy = clamp(y, 0, h_out - 1) * h_in / h_out;

      auto wx = xx - floor(xx);
      auto wy = yy - floor(yy);

      Expr xin[2] = {floor(xx), ceil(xx)};
      Expr yin[2] = {floor(yy), ceil(yy)};

      output(x, y, c) = (1 - wx) * (1 - wy) * input(xin[0], yin[0], c) +
                        wx * (1 - wy) * input(xin[1], yin[0], c) +
                        (1 - wx) * wy * input(xin[0], yin[1], c) +
                        wx * wy * input(xin[1], yin[1], c);

      // GPU schedule.
      if (get_target().has_gpu_feature())
        output.gpu_tile(x, y, xo, yo, xi, yi, tile_x, tile_y);

      // Hexagon schedule.
      else if (get_target().features_any_of({Target::HVX_64, Target::HVX_128}))
      {
        const auto vector_size =
            get_target().has_feature(Target::HVX_128) ? 128 : 64;

        output.hexagon()
            .prefetch(input, y, 2)
            .split(y, yo, yi, 128)
            .parallel(yo)
            .vectorize(x, vector_size);
      }

      // CPU schedule.
      else
        output.split(y, yo, yi, 8).parallel(yo).vectorize(x, 8);
    }
  };

}  // namespace

HALIDE_REGISTER_GENERATOR(Enlarge, shakti_halide_enlarge)
<commit_msg>WIP: make the code terser.<commit_after>#include "Halide.h"

namespace {

  using namespace Halide;

  template <typename T>
  class Enlarge : public Halide::Generator<Enlarge<T>>
  {
  public:
    GeneratorParam<int> tile_x{"tile_x", 32};
    GeneratorParam<int> tile_y{"tile_y", 32};

    Input<Buffer<T>> input{"f", 2};
    Output<Buffer<T>> output{"g", 2};

    Var x{"x"}, y{"y"}, c{"c"}, xo{"xo"}, yo{"yo"}, xi{"xi"}, yi{"yi"};

    void generate()
    {
      const float w_in = input.width();
      const float h_in = input.height();

      const float w_out = input.width();
      const float h_out = input.height();

      auto xx = x * w_in / w_out;
      auto yy = y * h_in / h_out;

      auto weights = Func{"weights"};
      weights(x, y) = abs(1 - xx - x) * abs(1 - yy - y);

      auto input_padded = BoundaryConditions::repeat_edge(input);

      auto r = RDom{floor(xx), 2, floor(yy), 2};
      output(x, y, c) = sum(weights(r) * input_padded(r.x, r.y, c));

      // GPU schedule.
      if (get_target().has_gpu_feature())
        output.gpu_tile(x, y, xo, yo, xi, yi, tile_x, tile_y);

      // Hexagon schedule.
      else if (get_target().features_any_of({Target::HVX_64, Target::HVX_128}))
      {
        const auto vector_size =
            get_target().has_feature(Target::HVX_128) ? 128 : 64;

        output.hexagon()
            .prefetch(input, y, 2)
            .split(y, yo, yi, 128)
            .parallel(yo)
            .vectorize(x, vector_size);
      }

      // CPU schedule.
      else
        output.split(y, yo, yi, 8).parallel(yo).vectorize(x, 8);
    }
  };

}  // namespace

HALIDE_REGISTER_GENERATOR(Enlarge, shakti_halide_enlarge)
<|endoftext|>