<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include "servicedirectoryclient.hpp"
#include <qitype/objecttypebuilder.hpp>
#include "servicedirectory_p.hpp"
#include "tcptransportsocket.hpp"

namespace qi {

  ServiceDirectoryClient::ServiceDirectoryClient()
    : _remoteObject(qi::Message::Service_ServiceDirectory)
  {
    _object = makeDynamicObjectPtr(&_remoteObject, false);
  }

  ServiceDirectoryClient::~ServiceDirectoryClient()
  {
    _object->disconnect(_addLink);
    _object->disconnect(_removeLink);
  }

 void ServiceDirectoryClient::onSDEventConnected(qi::Future<unsigned int> ret,
                                                 qi::Future<unsigned int> fadd,
                                                 qi::Future<unsigned int> frem,
                                                 qi::Promise<bool> fco)
  {
    if (!fadd.isReady() || !frem.isReady())
      return;
    if (fadd.hasError() || frem.hasError()) {
      std::string err;
      err = fadd.error();
      if (!err.empty() && frem.hasError())
        err += ". ";
      err += frem.error();
      fco.setError(err);
      return;
    }
    _addLink = fadd.value();
    _removeLink = frem.value();
    fco.setValue(true);
    connected();
  }

  void ServiceDirectoryClient::onMetaObjectFetched(qi::Future<void> future, qi::Promise<bool> promise) {
    if (future.hasError()) {
      promise.setError(future.error());
      return;
    }
    boost::function<void (unsigned int, std::string)> f;

    //TODO: this should not be async
    f = boost::bind<void>(&ServiceDirectoryClient::onServiceAdded, this, _1, _2);
    qi::Future<unsigned int> fut1 = _object->connect("serviceAdded", f);

    f = boost::bind<void>(&ServiceDirectoryClient::onServiceRemoved, this, _1, _2);
    qi::Future<unsigned int> fut2 = _object->connect("serviceRemoved", f).async();

    fut1.connect(boost::bind<void>(&ServiceDirectoryClient::onSDEventConnected, this, _1, fut1, fut2, promise));
    fut2.connect(boost::bind<void>(&ServiceDirectoryClient::onSDEventConnected, this, _1, fut1, fut2, promise));
  }

  void ServiceDirectoryClient::onSocketConnected(qi::FutureSync<bool> future, qi::Promise<bool> promise) {
    if (future.hasError()) {
      promise.setError(future.error());
      return;
    }
    if (future.value() == false) {
      promise.setValue(false);
      return;
    }
    qi::Future<void> fut = _remoteObject.fetchMetaObject();
    fut.connect(boost::bind<void>(&ServiceDirectoryClient::onMetaObjectFetched, this, _1, promise));
  }

  //we ensure in that function that connect to all events are already setup when we said we are connect.
  //that way we cant be connected without being fully ready.
  qi::FutureSync<bool> ServiceDirectoryClient::connect(const qi::Url &serviceDirectoryURL) {
    if (isConnected()) {
      qiLogInfo("qi.Session") << "Session is already connected";
      return qi::Future<bool>(false);
    }
    _sdSocket = qi::makeTransportSocket(serviceDirectoryURL.protocol());
    if (!_sdSocket)
      return qi::Future<bool>(false);
    _sdSocketDisconnectedLink = _sdSocket->disconnected.connect(boost::bind<void>(&ServiceDirectoryClient::onSocketDisconnected, this, _1));
    _remoteObject.setTransportSocket(_sdSocket);

    qi::Promise<bool> promise;
    qi::Future<bool> fut = _sdSocket->connect(serviceDirectoryURL);
    fut.connect(boost::bind<void>(&ServiceDirectoryClient::onSocketConnected, this, _1, promise));
    return promise.future();
  }

  static void sharedPtrHolder(TransportSocketPtr ptr)
  {
  }

  qi::FutureSync<void> ServiceDirectoryClient::close() {
    if (!_sdSocket)
      return qi::Future<void>(0);
    qi::Future<void> fut = _sdSocket->disconnect();
    // Hold the socket shared ptr alive until the future returns.
    // otherwise, the destructor will block us until disconnect terminates
    fut.connect(boost::bind(&sharedPtrHolder, _sdSocket));
    _sdSocket->disconnected.disconnect(_sdSocketDisconnectedLink);
    _sdSocket.reset();
    return fut;
  }

  bool                 ServiceDirectoryClient::isConnected() const {
    return _sdSocket == 0 ? false : _sdSocket->isConnected();
  }

  qi::Url              ServiceDirectoryClient::url() const {
    return _sdSocket->url();
  }

  void ServiceDirectoryClient::onServiceRemoved(unsigned int idx, const std::string &name) {
    qiLogVerbose("qi.ServiceDirectoryClient") << "ServiceDirectory: Service Removed #" << idx << ": " << name << std::endl;
    serviceRemoved(idx, name);
  }

  void ServiceDirectoryClient::onServiceAdded(unsigned int idx, const std::string &name) {
    qiLogVerbose("qi.ServiceDirectoryClient") << "ServiceDirectory: Service Added #" << idx << ": " << name << std::endl;
    serviceAdded(idx, name);
  }

  void ServiceDirectoryClient::onSocketDisconnected(int error) {
    disconnected(error);
  }

  qi::Future< std::vector<ServiceInfo> > ServiceDirectoryClient::services() {
    return _object->call< std::vector<ServiceInfo> >("services");
  }

  qi::Future<ServiceInfo>              ServiceDirectoryClient::service(const std::string &name) {
    return _object->call< ServiceInfo >("service", name);
  }

  qi::Future<unsigned int>             ServiceDirectoryClient::registerService(const ServiceInfo &svcinfo) {
    return _object->call< unsigned int >("registerService", svcinfo);
  }

  qi::Future<void>                     ServiceDirectoryClient::unregisterService(const unsigned int &idx) {
    return _object->call<void>("unregisterService", idx);
  }

  qi::Future<void>                     ServiceDirectoryClient::serviceReady(const unsigned int &idx) {
    return _object->call<void>("serviceReady", idx);
  }

}
<commit_msg>Fix conditionnal jump or move<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include "servicedirectoryclient.hpp"
#include <qitype/objecttypebuilder.hpp>
#include "servicedirectory_p.hpp"
#include "tcptransportsocket.hpp"

namespace qi {

  ServiceDirectoryClient::ServiceDirectoryClient()
    : _remoteObject(qi::Message::Service_ServiceDirectory)
    , _addLink(0)
    , _removeLink(0)
  {
    _object = makeDynamicObjectPtr(&_remoteObject, false);
  }

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

 void ServiceDirectoryClient::onSDEventConnected(qi::Future<unsigned int> ret,
                                                 qi::Future<unsigned int> fadd,
                                                 qi::Future<unsigned int> frem,
                                                 qi::Promise<bool> fco)
  {
    if (!fadd.isReady() || !frem.isReady())
      return;
    if (fadd.hasError() || frem.hasError()) {
      std::string err;
      err = fadd.error();
      if (!err.empty() && frem.hasError())
        err += ". ";
      err += frem.error();
      fco.setError(err);
      return;
    }
    _addLink = fadd.value();
    _removeLink = frem.value();
    fco.setValue(true);
    connected();
  }

  void ServiceDirectoryClient::onMetaObjectFetched(qi::Future<void> future, qi::Promise<bool> promise) {
    if (future.hasError()) {
      promise.setError(future.error());
      return;
    }
    boost::function<void (unsigned int, std::string)> f;

    //TODO: this should not be async
    f = boost::bind<void>(&ServiceDirectoryClient::onServiceAdded, this, _1, _2);
    qi::Future<unsigned int> fut1 = _object->connect("serviceAdded", f);

    f = boost::bind<void>(&ServiceDirectoryClient::onServiceRemoved, this, _1, _2);
    qi::Future<unsigned int> fut2 = _object->connect("serviceRemoved", f).async();

    fut1.connect(boost::bind<void>(&ServiceDirectoryClient::onSDEventConnected, this, _1, fut1, fut2, promise));
    fut2.connect(boost::bind<void>(&ServiceDirectoryClient::onSDEventConnected, this, _1, fut1, fut2, promise));
  }

  void ServiceDirectoryClient::onSocketConnected(qi::FutureSync<bool> future, qi::Promise<bool> promise) {
    if (future.hasError()) {
      promise.setError(future.error());
      return;
    }
    if (future.value() == false) {
      promise.setValue(false);
      return;
    }
    qi::Future<void> fut = _remoteObject.fetchMetaObject();
    fut.connect(boost::bind<void>(&ServiceDirectoryClient::onMetaObjectFetched, this, _1, promise));
  }

  //we ensure in that function that connect to all events are already setup when we said we are connect.
  //that way we cant be connected without being fully ready.
  qi::FutureSync<bool> ServiceDirectoryClient::connect(const qi::Url &serviceDirectoryURL) {
    if (isConnected()) {
      qiLogInfo("qi.Session") << "Session is already connected";
      return qi::Future<bool>(false);
    }
    _sdSocket = qi::makeTransportSocket(serviceDirectoryURL.protocol());
    if (!_sdSocket)
      return qi::Future<bool>(false);
    _sdSocketDisconnectedLink = _sdSocket->disconnected.connect(boost::bind<void>(&ServiceDirectoryClient::onSocketDisconnected, this, _1));
    _remoteObject.setTransportSocket(_sdSocket);

    qi::Promise<bool> promise;
    qi::Future<bool> fut = _sdSocket->connect(serviceDirectoryURL);
    fut.connect(boost::bind<void>(&ServiceDirectoryClient::onSocketConnected, this, _1, promise));
    return promise.future();
  }

  static void sharedPtrHolder(TransportSocketPtr ptr)
  {
  }

  qi::FutureSync<void> ServiceDirectoryClient::close() {
    if (!_sdSocket)
      return qi::Future<void>(0);
    qi::Future<void> fut = _sdSocket->disconnect();
    // Hold the socket shared ptr alive until the future returns.
    // otherwise, the destructor will block us until disconnect terminates
    fut.connect(boost::bind(&sharedPtrHolder, _sdSocket));
    _sdSocket->disconnected.disconnect(_sdSocketDisconnectedLink);
    _sdSocket.reset();
    return fut;
  }

  bool                 ServiceDirectoryClient::isConnected() const {
    return _sdSocket == 0 ? false : _sdSocket->isConnected();
  }

  qi::Url              ServiceDirectoryClient::url() const {
    return _sdSocket->url();
  }

  void ServiceDirectoryClient::onServiceRemoved(unsigned int idx, const std::string &name) {
    qiLogVerbose("qi.ServiceDirectoryClient") << "ServiceDirectory: Service Removed #" << idx << ": " << name << std::endl;
    serviceRemoved(idx, name);
  }

  void ServiceDirectoryClient::onServiceAdded(unsigned int idx, const std::string &name) {
    qiLogVerbose("qi.ServiceDirectoryClient") << "ServiceDirectory: Service Added #" << idx << ": " << name << std::endl;
    serviceAdded(idx, name);
  }

  void ServiceDirectoryClient::onSocketDisconnected(int error) {
    disconnected(error);
    _object->disconnect(_addLink);
    _object->disconnect(_removeLink);
  }

  qi::Future< std::vector<ServiceInfo> > ServiceDirectoryClient::services() {
    return _object->call< std::vector<ServiceInfo> >("services");
  }

  qi::Future<ServiceInfo>              ServiceDirectoryClient::service(const std::string &name) {
    return _object->call< ServiceInfo >("service", name);
  }

  qi::Future<unsigned int>             ServiceDirectoryClient::registerService(const ServiceInfo &svcinfo) {
    return _object->call< unsigned int >("registerService", svcinfo);
  }

  qi::Future<void>                     ServiceDirectoryClient::unregisterService(const unsigned int &idx) {
    return _object->call<void>("unregisterService", idx);
  }

  qi::Future<void>                     ServiceDirectoryClient::serviceReady(const unsigned int &idx) {
    return _object->call<void>("serviceReady", idx);
  }

}
<|endoftext|>


<commit_before>/**
 * @file /cost_map_visualisations/src/applications/service_relay.cpp
 */
/*****************************************************************************
** Includes
*****************************************************************************/

#include <cstdlib>
#include <ecl/command_line.hpp>
#include <ros/ros.h>
#include <cost_map.hpp>
#include <cost_map_msgs/GetCostMap.h>
#include <nav_msgs/OccupancyGrid.h>
#include <string>

/*****************************************************************************
** Main
*****************************************************************************/

/**
 * Tunes into a cost map service and relays it as an Occupancy Grid.
 *
 * Mostly for demonstration/debugging purposes as you are usually
 * viewing a whole global costmap in rviz anyway.
 */
int main(int argc, char **argv) {
  /****************************************
  ** Parsing
  ****************************************/
  ecl::CmdLine cmd("Relays service provided cost maps to an occupancy grid publisher.");
  ecl::ValueArg<float> lengthArg("l","length","Length of a side of the costmap subwindow.", false, 2, "float", cmd);
  ecl::ValueArg<std::string> serviceNameArg("s","service_name","Service name to call.", false, "get_cost_map", "string", cmd);
  ecl::ValueArg<float> rateArg("r","rate","Rate at which to relay (hz).", false, 1, "float", cmd);
  ecl::ValueArg<std::string> layerNameArg("n","layer_name","Layer name to convert.", false, "obstacle_costs", "string", cmd);
  cmd.parse(argc, argv);

  /****************************************
  ** Ros
  ****************************************/
  ros::init(argc, argv, "cost_map_client");
  ros::NodeHandle nodehandle("~");
  bool persistent = true, latched = true;
  std::cout << "Looking up service: " << serviceNameArg.getValue() << std::endl;
  ros::ServiceClient get_cost_map = nodehandle.serviceClient<cost_map_msgs::GetCostMap>(serviceNameArg.getValue(), persistent);
  if ( !get_cost_map.waitForExistence(ros::Duration(3.0)) ) {
    ROS_ERROR_STREAM("Cost Map Service Relay : failed to find the GetCostMap service on " << nodehandle.resolveName(serviceNameArg.getValue(), true));
    return EXIT_FAILURE;
  }
  ros::Publisher occupancy_grid_publisher = nodehandle.advertise<nav_msgs::OccupancyGrid>("occupancy_grid", 5, latched);

  /****************************************
  ** Service Call/Relay Loop
  ****************************************/
  cost_map_msgs::GetCostMap srv;
  srv.request.length_x = lengthArg.getValue();
  srv.request.length_y = lengthArg.getValue();
  std::string layer_name = layerNameArg.getValue();
  ros::Rate rate(rateArg.getValue());
  while ( ros::ok() ) {
    if (!get_cost_map.call(srv))
    {
      ROS_ERROR_STREAM("Cost Map Service Relay : service call failed.");
      return EXIT_FAILURE;
    }
    nav_msgs::OccupancyGrid occupancy_grid_msg;
    cost_map::CostMap cost_map;
    cost_map::fromMessage(srv.response.map, cost_map);
    if ( cost_map.getLayers().size() == 1 ) {
      layer_name = cost_map.getLayers()[0];
    }
    cost_map::toOccupancyGrid(cost_map, layer_name, occupancy_grid_msg);
    occupancy_grid_publisher.publish(occupancy_grid_msg);
    rate.sleep();
    ros::spinOnce();
  }
  return EXIT_SUCCESS;
}

<commit_msg>[cost_map] fix header reference<commit_after>/**
 * @file /cost_map_visualisations/src/applications/service_relay.cpp
 */
/*****************************************************************************
** Includes
*****************************************************************************/

#include <cstdlib>
#include <ecl/command_line.hpp>
#include <ros/ros.h>
#include <cost_map/cost_map.hpp>
#include <cost_map_msgs/GetCostMap.h>
#include <nav_msgs/OccupancyGrid.h>
#include <string>

/*****************************************************************************
** Main
*****************************************************************************/

/**
 * Tunes into a cost map service and relays it as an Occupancy Grid.
 *
 * Mostly for demonstration/debugging purposes as you are usually
 * viewing a whole global costmap in rviz anyway.
 */
int main(int argc, char **argv) {
  /****************************************
  ** Parsing
  ****************************************/
  ecl::CmdLine cmd("Relays service provided cost maps to an occupancy grid publisher.");
  ecl::ValueArg<float> lengthArg("l","length","Length of a side of the costmap subwindow.", false, 2, "float", cmd);
  ecl::ValueArg<std::string> serviceNameArg("s","service_name","Service name to call.", false, "get_cost_map", "string", cmd);
  ecl::ValueArg<float> rateArg("r","rate","Rate at which to relay (hz).", false, 1, "float", cmd);
  ecl::ValueArg<std::string> layerNameArg("n","layer_name","Layer name to convert.", false, "obstacle_costs", "string", cmd);
  cmd.parse(argc, argv);

  /****************************************
  ** Ros
  ****************************************/
  ros::init(argc, argv, "cost_map_client");
  ros::NodeHandle nodehandle("~");
  bool persistent = true, latched = true;
  std::cout << "Looking up service: " << serviceNameArg.getValue() << std::endl;
  ros::ServiceClient get_cost_map = nodehandle.serviceClient<cost_map_msgs::GetCostMap>(serviceNameArg.getValue(), persistent);
  if ( !get_cost_map.waitForExistence(ros::Duration(3.0)) ) {
    ROS_ERROR_STREAM("Cost Map Service Relay : failed to find the GetCostMap service on " << nodehandle.resolveName(serviceNameArg.getValue(), true));
    return EXIT_FAILURE;
  }
  ros::Publisher occupancy_grid_publisher = nodehandle.advertise<nav_msgs::OccupancyGrid>("occupancy_grid", 5, latched);

  /****************************************
  ** Service Call/Relay Loop
  ****************************************/
  cost_map_msgs::GetCostMap srv;
  srv.request.length_x = lengthArg.getValue();
  srv.request.length_y = lengthArg.getValue();
  std::string layer_name = layerNameArg.getValue();
  ros::Rate rate(rateArg.getValue());
  while ( ros::ok() ) {
    if (!get_cost_map.call(srv))
    {
      ROS_ERROR_STREAM("Cost Map Service Relay : service call failed.");
      return EXIT_FAILURE;
    }
    nav_msgs::OccupancyGrid occupancy_grid_msg;
    cost_map::CostMap cost_map;
    cost_map::fromMessage(srv.response.map, cost_map);
    if ( cost_map.getLayers().size() == 1 ) {
      layer_name = cost_map.getLayers()[0];
    }
    cost_map::toOccupancyGrid(cost_map, layer_name, occupancy_grid_msg);
    occupancy_grid_publisher.publish(occupancy_grid_msg);
    rate.sleep();
    ros::spinOnce();
  }
  return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#include "CAQrencode.h"
#include "images/qrencode/qrencode.h"
#include "platform/CADensityDpi.h"

NS_CC_BEGIN

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength)
{
    return CAQrencode::createWithQRString(string, sideLength, CAColor4B::WHITE, CAColor4B::BLACK);
}

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength, const CAColor4B& backgroundColor)
{
    return CAQrencode::createWithQRString(string, sideLength, backgroundColor, CAColor4B::BLACK);
}

#define PRINT_QRCODE(code, FUNC)   for(int i = 0; i < code->width; ++i){for(int j = 0; j < code->width; ++j){if (code->data[j + i * code->width] & 1){FUNC(i, j);}}}

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength, const CAColor4B& backgroundColor, const CAColor4B& qrColor)
{
    QRcode *code = QRcode_encodeString(string.c_str(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);

    float side = CrossApp::s_dip_to_px(1) > 1 ? CrossApp::s_dip_to_px(sideLength) : sideLength;
    int scale = std::ceil(side / code->width);
    int off = 0;
    int width = (code->width + off * 2) * scale;

    ssize_t length = width * width * 4;
    unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * (length + 1));
    for (ssize_t i = 0; i < length / 4; ++i)
    {
        data[i * 4 + 0] = backgroundColor.r;        //R
        data[i * 4 + 1] = backgroundColor.g;        //G
        data[i * 4 + 2] = backgroundColor.b;        //B
        data[i * 4 + 3] = backgroundColor.a;        //A
    }
    data[length] = '\0';

    std::function<void(int i, int j)> func;

    if (qrColor.a != backgroundColor.a)
    {
        func = [&](int i, int j)
        {
            int h = 0, w = 0;
            do {
                unsigned long index = (w + (j + off) * scale) + (h + (i + off) * scale) * width;
                data[index * 4 + 0] = qrColor.r;        //R
                data[index * 4 + 1] = qrColor.g;        //G
                data[index * 4 + 2] = qrColor.b;        //B
                data[index * 4 + 3] = qrColor.a;        //A
                if (++w == scale) ++h, w = 0;
                if (h == scale) break;;
            } while (1);
        };

    }
    else
    {
        func = [&](int i, int j)
        {
            int h = 0, w = 0;
            do {
                unsigned long index = (w + (j + off) * scale) + (h + (i + off) * scale) * width;
                data[index * 4 + 0] = qrColor.r;        //R
                data[index * 4 + 1] = qrColor.g;        //G
                data[index * 4 + 2] = qrColor.b;        //B
                if (++w == scale) ++h, w = 0;
                if (h == scale) break;;
            } while (1);
        };
    }
    PRINT_QRCODE(code, func);
    QRcode_free(code);

    CrossApp::CAData* ca_data = CrossApp::CAData::create();
    ca_data->fastSet(data, length);

    return CAImage::createWithRawDataNoCache(ca_data, CAImage::PixelFormat::RGBA8888, width, width);
}

NS_CC_END
<commit_msg>no message<commit_after>#include "CAQrencode.h"
#include "images/qrencode/qrencode.h"
#include "platform/CADensityDpi.h"

NS_CC_BEGIN

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength)
{
    return CAQrencode::createWithQRString(string, sideLength, CAColor4B::WHITE, CAColor4B::BLACK);
}

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength, const CAColor4B& backgroundColor)
{
    return CAQrencode::createWithQRString(string, sideLength, backgroundColor, CAColor4B::BLACK);
}

inline void QRcode_print(QRcode* code, const std::function<void(int i, int j)>& func)
{
    int i = 0, j = 0;
    do {
        int index = j + i * code->width;
        if (code->data[index] & 1) func(i, j);
        if (++j == code->width) ++i, j = 0;
        if (i == code->width) break;
    } while (1);
}

CAImage* CAQrencode::createWithQRString(const std::string& string, unsigned int sideLength, const CAColor4B& backgroundColor, const CAColor4B& qrColor)
{
    QRcode *code = QRcode_encodeString(string.c_str(), 0, QR_ECLEVEL_L, QR_MODE_8, 1);

    float side = CrossApp::s_dip_to_px(1) > 1 ? CrossApp::s_dip_to_px(sideLength) : sideLength;
    int scale = std::ceil(side / code->width);
    int off = 0;
    int width = (code->width + off * 2) * scale;

    ssize_t length = width * width * 4;
    unsigned char* data = (unsigned char*)malloc(sizeof(unsigned char) * (length + 1));
    for (ssize_t i = 0; i < length / 4; ++i)
    {
        data[i * 4 + 0] = backgroundColor.r;        //R
        data[i * 4 + 1] = backgroundColor.g;        //G
        data[i * 4 + 2] = backgroundColor.b;        //B
        data[i * 4 + 3] = backgroundColor.a;        //A
    }
    data[length] = '\0';

    std::function<void(int i, int j)> func;

    if (qrColor.a != backgroundColor.a)
    {
        func = [&](int i, int j)
        {
            int h = 0, w = 0;
            do {
                unsigned long index = (w + (j + off) * scale) + (h + (i + off) * scale) * width;
                data[index * 4 + 0] = qrColor.r;        //R
                data[index * 4 + 1] = qrColor.g;        //G
                data[index * 4 + 2] = qrColor.b;        //B
                data[index * 4 + 3] = qrColor.a;        //A
                if (++w == scale) ++h, w = 0;
                if (h == scale) break;;
            } while (1);
        };

    }
    else
    {
        func = [&](int i, int j)
        {
            int h = 0, w = 0;
            do {
                unsigned long index = (w + (j + off) * scale) + (h + (i + off) * scale) * width;
                data[index * 4 + 0] = qrColor.r;        //R
                data[index * 4 + 1] = qrColor.g;        //G
                data[index * 4 + 2] = qrColor.b;        //B
                if (++w == scale) ++h, w = 0;
                if (h == scale) break;
            } while (1);
        };
    }
    QRcode_print(code, func);
    QRcode_free(code);

    CrossApp::CAData* ca_data = CrossApp::CAData::create();
    ca_data->fastSet(data, length);

    return CAImage::createWithRawDataNoCache(ca_data, CAImage::PixelFormat::RGBA8888, width, width);
}

NS_CC_END
<|endoftext|>


<commit_before>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

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

******************************************************************************/

#include "ExportDataReaction.h"

#include <pqActiveObjects.h>
#include <pqCoreUtilities.h>
#include <pqProxyWidgetDialog.h>
#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkSMProxyManager.h>
#include <vtkSMSessionProxyManager.h>
#include <vtkSMWriterFactory.h>
#include <vtkSmartPointer.h>
#include <vtkTIFFWriter.h>
#include <vtkTrivialProducer.h>
#include <vtkUnsignedCharArray.h>

#include "ActiveObjects.h"
#include "ConvertToFloatOperator.h"
#include "EmdFormat.h"
#include "Module.h"
#include "Utilities.h"

#include <QDebug>
#include <QDialog>
#include <QFileDialog>
#include <QFileInfo>
#include <QRegularExpression>

namespace tomviz {

ExportDataReaction::ExportDataReaction(QAction* parentAction, Module* module)
  : pqReaction(parentAction), m_module(module)
{
  connect(&ActiveObjects::instance(), SIGNAL(moduleChanged(Module*)),
          SLOT(updateEnableState()));
  updateEnableState();
}

void ExportDataReaction::updateEnableState()
{
  if (!m_module) {
    parentAction()->setEnabled(ActiveObjects::instance().activeModule() !=
                               nullptr);
  } else {
    parentAction()->setEnabled(true);
  }
}

void ExportDataReaction::onTriggered()
{
  Module* module = m_module;
  if (!module) {
    module = ActiveObjects::instance().activeModule();
  }
  if (!module) {
    return;
  }
  QString exportType = module->exportDataTypeString();
  QStringList filters;
  if (exportType == "Volume") {
    filters << "TIFF format (*.tiff)"
            << "EMD format (*.emd *.hdf5)"
            << "CSV File (*.csv)"
            << "Exodus II File (*.e *.ex2 *.ex2v2 *.exo *.exoII *.exoii *.g)"
            << "Legacy VTK Files (*.vtk)"
            << "Meta Image Files (*.mhd)"
            << "ParaView Data Files (*.pvd)"
            << "VTK ImageData Files (*.vti)"
            << "XDMF Data File (*.xmf)"
            << "JSON Image Files (*.json)";
  } else if (exportType == "Mesh") {
    filters << "STL Files (*.stl)"
            << "VTK PolyData files(*.vtp)";
  } else if (exportType == "Image") {
    filters << "PNG Files (*.png)"
            << "JPEG Files (*.jpg *.jpeg)"
            << "TIFF Files (*.tiff)"
            << "VTK ImageData Files (*.vti)";
  }

  QFileDialog dialog(nullptr);
  dialog.setFileMode(QFileDialog::AnyFile);
  dialog.setNameFilters(filters);
  dialog.setObjectName("FileOpenDialog-tomviz"); // avoid name collision?
  dialog.setAcceptMode(QFileDialog::AcceptSave);

  if (dialog.exec() == QDialog::Accepted) {
    QStringList filenames = dialog.selectedFiles();
    QString format = dialog.selectedNameFilter();
    QString filename = filenames[0];
    int startPos = format.indexOf("(") + 1;
    int n = format.indexOf(")") - startPos;
    QString extensionString = format.mid(startPos, n);
    QStringList extensions = extensionString.split(QRegularExpression(" ?\\*"),
                                                   QString::SkipEmptyParts);
    bool hasExtension = false;
    for (QString& str : extensions) {
      if (filename.endsWith(str)) {
        hasExtension = true;
      }
    }
    if (!hasExtension) {
      filename = QString("%1%2").arg(filename, extensions[0]);
    }
    exportData(filename);
  }
}

namespace {

template <typename FromType, typename ToType>
void convert(vtkDataArray* outArray, int nComps, int nTuples, void* data)
{
  FromType* d = static_cast<FromType*>(data);
  ToType* a = static_cast<ToType*>(outArray->GetVoidPointer(0));
  for (int i = 0; i < nComps * nTuples; ++i) {
    a[i] = static_cast<ToType>(d[i]);
  }
}

template <typename FromType>
void convertToUnsignedChar(vtkDataArray* outArray, int nComps, int nTuples,
                           void* data)
{
  convert<FromType, unsigned char>(outArray, nComps, nTuples, data);
}
}

bool ExportDataReaction::exportData(const QString& filename)
{
  auto server = pqActiveObjects::instance().activeServer();

  vtkSmartPointer<vtkDataObject> data = this->m_module->getDataToExport();

  if (!server) {
    qCritical("No active server located.");
    return false;
  }

  QFileInfo info(filename);
  if (info.suffix() == "emd") {
    EmdFormat writer;
    auto image = vtkImageData::SafeDownCast(data);
    if (!image || !writer.write(filename.toLatin1().data(), image)) {
      qCritical() << "Failed to write out data.";
      return false;
    } else {
      return true;
    }
  }

  auto writerFactory = vtkSMProxyManager::GetProxyManager()->GetWriterFactory();

  vtkSMSessionProxyManager* pxm =
    vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager();

  vtkSmartPointer<vtkSMSourceProxy> producer;
  producer.TakeReference(vtkSMSourceProxy::SafeDownCast(
    pxm->NewProxy("sources", "TrivialProducer")));
  vtkSmartPointer<vtkTrivialProducer> trivialProducer =
    vtkTrivialProducer::SafeDownCast(producer->GetClientSideObject());
  trivialProducer->SetOutput(data);
  trivialProducer->UpdateInformation();
  trivialProducer->Update();
  producer->UpdatePipeline();

  vtkSmartPointer<vtkSMProxy> proxy;
  proxy.TakeReference(
    writerFactory->CreateWriter(filename.toLatin1().data(), producer));
  auto writer = vtkSMSourceProxy::SafeDownCast(proxy);
  if (!writer) {
    qCritical() << "Failed to create writer for: " << filename;
    return false;
  }

  // Convert to a data format the file type supports
  const char* writerName = writer->GetClientSideObject()->GetClassName();
  auto imageData =
    vtkImageData::SafeDownCast(trivialProducer->GetOutputDataObject(0));
  auto imageType = imageData->GetPointData()->GetScalars()->GetDataType();
  if (strcmp(writerName, "vtkTIFFWriter") == 0 && imageType == VTK_DOUBLE) {
    vtkNew<vtkImageData> fImage;
    fImage->DeepCopy(imageData);
    ConvertToFloatOperator convertFloat;
    convertFloat.applyTransform(fImage);

    trivialProducer->SetOutput(fImage.Get());
    trivialProducer->UpdateInformation();
    trivialProducer->Update();
    producer->UpdatePipeline();
  }

  if ((strcmp(writerName, "vtkPNGWriter") == 0 &&
       (imageType != VTK_UNSIGNED_CHAR || imageType != VTK_UNSIGNED_SHORT)) ||
      (strcmp(writerName, "vtkJPEGWriter") == 0 &&
       imageType != VTK_UNSIGNED_CHAR)) {
    std::cout << "File type does not support the current data type, converting "
                 "to unsigned char"
              << std::endl;
    vtkNew<vtkImageData> newImage;
    newImage->DeepCopy(imageData);
    vtkDataArray* scalars = imageData->GetPointData()->GetScalars();
    double range[2];
    scalars->GetRange(range);
    vtkNew<vtkUnsignedCharArray> charArray;
    charArray->SetNumberOfComponents(scalars->GetNumberOfComponents());
    charArray->SetNumberOfTuples(scalars->GetNumberOfTuples());
    charArray->SetName(scalars->GetName());
    switch (scalars->GetDataType()) {
      vtkTemplateMacro(convertToUnsignedChar<VTK_TT>(
        charArray.Get(), scalars->GetNumberOfComponents(),
        scalars->GetNumberOfTuples(), scalars->GetVoidPointer(0)));
    }
    newImage->GetPointData()->RemoveArray(scalars->GetName());
    newImage->GetPointData()->SetScalars(charArray.Get());

    trivialProducer->SetOutput(newImage.Get());
    trivialProducer->UpdateInformation();
    trivialProducer->Update();
    producer->UpdatePipeline();
  }

  pqProxyWidgetDialog dialog(writer, pqCoreUtilities::mainWidget());
  dialog.setObjectName("WriterSettingsDialog");
  dialog.setEnableSearchBar(true);
  dialog.setWindowTitle(
    QString("Configure Writer (%1)").arg(writer->GetXMLLabel()));

  // Check to see if this writer has any properties that can be configured by
  // the user. If it does, display the dialog.
  if (dialog.hasVisibleWidgets()) {
    dialog.exec();
    if (dialog.result() == QDialog::Rejected) {
      // The user pressed Cancel so don't write
      return false;
    }
  }
  writer->UpdateVTKObjects();
  writer->UpdatePipeline();

  return true;
}
}
<commit_msg>Rescale normalized floating point when converting to unsigned char<commit_after>/******************************************************************************

  This source file is part of the tomviz project.

  Copyright Kitware, Inc.

  This source code is released under the New BSD License, (the "License").

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

******************************************************************************/

#include "ExportDataReaction.h"

#include <pqActiveObjects.h>
#include <pqCoreUtilities.h>
#include <pqProxyWidgetDialog.h>
#include <vtkArrayCalculator.h>
#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkPointData.h>
#include <vtkSMProxyManager.h>
#include <vtkSMSessionProxyManager.h>
#include <vtkSMWriterFactory.h>
#include <vtkSmartPointer.h>
#include <vtkTIFFWriter.h>
#include <vtkTrivialProducer.h>
#include <vtkUnsignedCharArray.h>

#include "ActiveObjects.h"
#include "ConvertToFloatOperator.h"
#include "EmdFormat.h"
#include "Module.h"
#include "Utilities.h"

#include <QDebug>
#include <QDialog>
#include <QFileDialog>
#include <QFileInfo>
#include <QRegularExpression>

namespace tomviz {

ExportDataReaction::ExportDataReaction(QAction* parentAction, Module* module)
  : pqReaction(parentAction), m_module(module)
{
  connect(&ActiveObjects::instance(), SIGNAL(moduleChanged(Module*)),
          SLOT(updateEnableState()));
  updateEnableState();
}

void ExportDataReaction::updateEnableState()
{
  if (!m_module) {
    parentAction()->setEnabled(ActiveObjects::instance().activeModule() !=
                               nullptr);
  } else {
    parentAction()->setEnabled(true);
  }
}

void ExportDataReaction::onTriggered()
{
  Module* module = m_module;
  if (!module) {
    module = ActiveObjects::instance().activeModule();
  }
  if (!module) {
    return;
  }
  QString exportType = module->exportDataTypeString();
  QStringList filters;
  if (exportType == "Volume") {
    filters << "TIFF format (*.tiff)"
            << "EMD format (*.emd *.hdf5)"
            << "CSV File (*.csv)"
            << "Exodus II File (*.e *.ex2 *.ex2v2 *.exo *.exoII *.exoii *.g)"
            << "Legacy VTK Files (*.vtk)"
            << "Meta Image Files (*.mhd)"
            << "ParaView Data Files (*.pvd)"
            << "VTK ImageData Files (*.vti)"
            << "XDMF Data File (*.xmf)"
            << "JSON Image Files (*.json)";
  } else if (exportType == "Mesh") {
    filters << "STL Files (*.stl)"
            << "VTK PolyData files(*.vtp)";
  } else if (exportType == "Image") {
    filters << "PNG Files (*.png)"
            << "JPEG Files (*.jpg *.jpeg)"
            << "TIFF Files (*.tiff)"
            << "VTK ImageData Files (*.vti)";
  }

  QFileDialog dialog(nullptr);
  dialog.setFileMode(QFileDialog::AnyFile);
  dialog.setNameFilters(filters);
  dialog.setObjectName("FileOpenDialog-tomviz"); // avoid name collision?
  dialog.setAcceptMode(QFileDialog::AcceptSave);

  if (dialog.exec() == QDialog::Accepted) {
    QStringList filenames = dialog.selectedFiles();
    QString format = dialog.selectedNameFilter();
    QString filename = filenames[0];
    int startPos = format.indexOf("(") + 1;
    int n = format.indexOf(")") - startPos;
    QString extensionString = format.mid(startPos, n);
    QStringList extensions = extensionString.split(QRegularExpression(" ?\\*"),
                                                   QString::SkipEmptyParts);
    bool hasExtension = false;
    for (QString& str : extensions) {
      if (filename.endsWith(str)) {
        hasExtension = true;
      }
    }
    if (!hasExtension) {
      filename = QString("%1%2").arg(filename, extensions[0]);
    }
    exportData(filename);
  }
}

namespace {

template <typename FromType, typename ToType>
void convert(vtkDataArray* outArray, int nComps, int nTuples, void* data)
{
  FromType* d = static_cast<FromType*>(data);
  ToType* a = static_cast<ToType*>(outArray->GetVoidPointer(0));
  for (int i = 0; i < nComps * nTuples; ++i) {
    a[i] = static_cast<ToType>(d[i]);
  }
}

template <typename FromType>
void convertToUnsignedChar(vtkDataArray* outArray, int nComps, int nTuples,
                           void* data)
{
  convert<FromType, unsigned char>(outArray, nComps, nTuples, data);
}
}

bool ExportDataReaction::exportData(const QString& filename)
{
  auto server = pqActiveObjects::instance().activeServer();

  vtkSmartPointer<vtkDataObject> data = this->m_module->getDataToExport();

  if (!server) {
    qCritical("No active server located.");
    return false;
  }

  QFileInfo info(filename);
  if (info.suffix() == "emd") {
    EmdFormat writer;
    auto image = vtkImageData::SafeDownCast(data);
    if (!image || !writer.write(filename.toLatin1().data(), image)) {
      qCritical() << "Failed to write out data.";
      return false;
    } else {
      return true;
    }
  }

  auto writerFactory = vtkSMProxyManager::GetProxyManager()->GetWriterFactory();

  vtkSMSessionProxyManager* pxm =
    vtkSMProxyManager::GetProxyManager()->GetActiveSessionProxyManager();

  vtkSmartPointer<vtkSMSourceProxy> producer;
  producer.TakeReference(vtkSMSourceProxy::SafeDownCast(
    pxm->NewProxy("sources", "TrivialProducer")));
  vtkSmartPointer<vtkTrivialProducer> trivialProducer =
    vtkTrivialProducer::SafeDownCast(producer->GetClientSideObject());
  trivialProducer->SetOutput(data);
  trivialProducer->UpdateInformation();
  trivialProducer->Update();
  producer->UpdatePipeline();

  vtkSmartPointer<vtkSMProxy> proxy;
  proxy.TakeReference(
    writerFactory->CreateWriter(filename.toLatin1().data(), producer));
  auto writer = vtkSMSourceProxy::SafeDownCast(proxy);
  if (!writer) {
    qCritical() << "Failed to create writer for: " << filename;
    return false;
  }

  // Convert to a data format the file type supports
  const char* writerName = writer->GetClientSideObject()->GetClassName();
  auto imageData =
    vtkImageData::SafeDownCast(trivialProducer->GetOutputDataObject(0));
  auto imageType = imageData->GetPointData()->GetScalars()->GetDataType();
  if (strcmp(writerName, "vtkTIFFWriter") == 0 && imageType == VTK_DOUBLE) {
    vtkNew<vtkImageData> fImage;
    fImage->DeepCopy(imageData);
    ConvertToFloatOperator convertFloat;
    convertFloat.applyTransform(fImage);

    trivialProducer->SetOutput(fImage.Get());
    trivialProducer->UpdateInformation();
    trivialProducer->Update();
    producer->UpdatePipeline();
  }

  if ((strcmp(writerName, "vtkPNGWriter") == 0 &&
       (imageType != VTK_UNSIGNED_CHAR || imageType != VTK_UNSIGNED_SHORT)) ||
      (strcmp(writerName, "vtkJPEGWriter") == 0 &&
       imageType != VTK_UNSIGNED_CHAR)) {
    std::cout << "File type does not support the current data type, converting "
                 "to unsigned char"
              << std::endl;
    vtkNew<vtkImageData> newImage;
    newImage->DeepCopy(imageData);
    vtkSmartPointer<vtkDataArray> scalars =
      imageData->GetPointData()->GetScalars();
    double range[2];
    scalars->GetRange(range);

    if ((imageType == VTK_FLOAT || imageType == VTK_DOUBLE) &&
        (range[0] >= 0 && range[1] <= 1)) {
      std::cout << "Converting normalized floating point values to integers in "
                   "the range 0-255."
                << std::endl;
      vtkNew<vtkArrayCalculator> calc;
      calc->AddScalarVariable("scalars", scalars->GetName());
      calc->SetFunction("floor(scalars*255 + 0.5)");
      calc->SetResultArrayName("result");
      calc->SetInputData(imageData);
      calc->Update();
      scalars = calc->GetDataSetOutput()->GetPointData()->GetArray("result");
      scalars->GetRange(range);
    }
    vtkNew<vtkUnsignedCharArray> charArray;
    charArray->SetNumberOfComponents(scalars->GetNumberOfComponents());
    charArray->SetNumberOfTuples(scalars->GetNumberOfTuples());
    charArray->SetName(scalars->GetName());
    switch (scalars->GetDataType()) {
      vtkTemplateMacro(convertToUnsignedChar<VTK_TT>(
        charArray.Get(), scalars->GetNumberOfComponents(),
        scalars->GetNumberOfTuples(), scalars->GetVoidPointer(0)));
    }
    newImage->GetPointData()->RemoveArray(scalars->GetName());
    newImage->GetPointData()->SetScalars(charArray.Get());

    trivialProducer->SetOutput(newImage.Get());
    trivialProducer->UpdateInformation();
    trivialProducer->Update();
    producer->UpdatePipeline();
  }

  pqProxyWidgetDialog dialog(writer, pqCoreUtilities::mainWidget());
  dialog.setObjectName("WriterSettingsDialog");
  dialog.setEnableSearchBar(true);
  dialog.setWindowTitle(
    QString("Configure Writer (%1)").arg(writer->GetXMLLabel()));

  // Check to see if this writer has any properties that can be configured by
  // the user. If it does, display the dialog.
  if (dialog.hasVisibleWidgets()) {
    dialog.exec();
    if (dialog.result() == QDialog::Rejected) {
      // The user pressed Cancel so don't write
      return false;
    }
  }
  writer->UpdateVTKObjects();
  writer->UpdatePipeline();

  return true;
}
}
<|endoftext|>


<commit_before>//===-- sanitizer_win.cc --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements windows-specific functions from
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <stdlib.h>
#include <io.h>
#include <windows.h>

#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"

namespace __sanitizer {

#include "sanitizer_syscall_generic.inc"

// --------------------- sanitizer_common.h
uptr GetPageSize() {
  return 1U << 14;  // FIXME: is this configurable?
}

uptr GetMmapGranularity() {
  return 1U << 16;  // FIXME: is this configurable?
}

bool FileExists(const char *filename) {
  UNIMPLEMENTED();
}

uptr internal_getpid() {
  return GetProcessId(GetCurrentProcess());
}

// In contrast to POSIX, on Windows GetCurrentThreadId()
// returns a system-unique identifier.
uptr GetTid() {
  return GetCurrentThreadId();
}

uptr GetThreadSelf() {
  return GetTid();
}

void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
                                uptr *stack_bottom) {
  CHECK(stack_top);
  CHECK(stack_bottom);
  MEMORY_BASIC_INFORMATION mbi;
  CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
  // FIXME: is it possible for the stack to not be a single allocation?
  // Are these values what ASan expects to get (reserved, not committed;
  // including stack guard page) ?
  *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
  *stack_bottom = (uptr)mbi.AllocationBase;
}

void *MmapOrDie(uptr size, const char *mem_type) {
  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (rv == 0) {
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
           size, size, mem_type);
    CHECK("unable to mmap" && 0);
  }
  return rv;
}

void UnmapOrDie(void *addr, uptr size) {
  if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
    Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n",
           size, size, addr);
    CHECK("unable to unmap" && 0);
  }
}

void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
  // but on Win64 it does.
  void *p = VirtualAlloc((LPVOID)fixed_addr, size,
      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (p == 0)
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
           size, size, fixed_addr, GetLastError());
  return p;
}

void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
  return MmapFixedNoReserve(fixed_addr, size);
}

void *Mprotect(uptr fixed_addr, uptr size) {
  return VirtualAlloc((LPVOID)fixed_addr, size,
                      MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
}

void FlushUnneededShadowMemory(uptr addr, uptr size) {
  // This is almost useless on 32-bits.
  // FIXME: add madvice-analog when we move to 64-bits.
}

bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
  // FIXME: shall we do anything here on Windows?
  return true;
}

void *MapFileToMemory(const char *file_name, uptr *buff_size) {
  UNIMPLEMENTED();
}

static const int kMaxEnvNameLength = 128;
static const DWORD kMaxEnvValueLength = 32767;

namespace {

struct EnvVariable {
  char name[kMaxEnvNameLength];
  char value[kMaxEnvValueLength];
};

}  // namespace

static const int kEnvVariables = 5;
static EnvVariable env_vars[kEnvVariables];
static int num_env_vars;

const char *GetEnv(const char *name) {
  // Note: this implementation caches the values of the environment variables
  // and limits their quantity.
  for (int i = 0; i < num_env_vars; i++) {
    if (0 == internal_strcmp(name, env_vars[i].name))
      return env_vars[i].value;
  }
  CHECK_LT(num_env_vars, kEnvVariables);
  DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
                                     kMaxEnvValueLength);
  if (rv > 0 && rv < kMaxEnvValueLength) {
    CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
    internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
    num_env_vars++;
    return env_vars[num_env_vars - 1].value;
  }
  return 0;
}

const char *GetPwd() {
  UNIMPLEMENTED();
}

u32 GetUid() {
  UNIMPLEMENTED();
}

void DumpProcessMap() {
  UNIMPLEMENTED();
}

void DisableCoreDumper() {
  UNIMPLEMENTED();
}

void ReExec() {
  UNIMPLEMENTED();
}

void PrepareForSandboxing() {
  // Nothing here for now.
}

bool StackSizeIsUnlimited() {
  UNIMPLEMENTED();
}

void SetStackSizeLimitInBytes(uptr limit) {
  UNIMPLEMENTED();
}

void SleepForSeconds(int seconds) {
  Sleep(seconds * 1000);
}

void SleepForMillis(int millis) {
  Sleep(millis);
}

u64 NanoTime() {
  return 0;
}

u64 NanoTime() {
  return 0;
}

void Abort() {
  abort();
  _exit(-1);  // abort is not NORETURN on Windows.
}

#ifndef SANITIZER_GO
int Atexit(void (*function)(void)) {
  return atexit(function);
}
#endif

// ------------------ sanitizer_libc.h
uptr internal_mmap(void *addr, uptr length, int prot, int flags,
                   int fd, u64 offset) {
  UNIMPLEMENTED();
}

uptr internal_munmap(void *addr, uptr length) {
  UNIMPLEMENTED();
}

uptr internal_close(fd_t fd) {
  UNIMPLEMENTED();
}

int internal_isatty(fd_t fd) {
  return _isatty(fd);
}

uptr internal_open(const char *filename, int flags) {
  UNIMPLEMENTED();
}

uptr internal_open(const char *filename, int flags, u32 mode) {
  UNIMPLEMENTED();
}

uptr OpenFile(const char *filename, bool write) {
  UNIMPLEMENTED();
}

uptr internal_read(fd_t fd, void *buf, uptr count) {
  UNIMPLEMENTED();
}

uptr internal_write(fd_t fd, const void *buf, uptr count) {
  if (fd != kStderrFd)
    UNIMPLEMENTED();
  HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
  if (err == 0)
    return 0;  // FIXME: this might not work on some apps.
  DWORD ret;
  if (!WriteFile(err, buf, count, &ret, 0))
    return 0;
  return ret;
}

uptr internal_stat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_lstat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_fstat(fd_t fd, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_filesize(fd_t fd) {
  UNIMPLEMENTED();
}

uptr internal_dup2(int oldfd, int newfd) {
  UNIMPLEMENTED();
}

uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
  UNIMPLEMENTED();
}

uptr internal_sched_yield() {
  Sleep(0);
  return 0;
}

void internal__exit(int exitcode) {
  _exit(exitcode);
}

// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
const uptr LOCK_READY = (uptr)-1;

BlockingMutex::BlockingMutex(LinkerInitialized li) {
  // FIXME: see comments in BlockingMutex::Lock() for the details.
  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);

  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

BlockingMutex::BlockingMutex() {
  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

void BlockingMutex::Lock() {
  if (owner_ == LOCK_UNINITIALIZED) {
    // FIXME: hm, global BlockingMutex objects are not initialized?!?
    // This might be a side effect of the clang+cl+link Frankenbuild...
    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));

    // FIXME: If it turns out the linker doesn't invoke our
    // constructors, we should probably manually Lock/Unlock all the global
    // locks while we're starting in one thread to avoid double-init races.
  }
  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  CHECK_EQ(owner_, LOCK_READY);
  owner_ = GetThreadSelf();
}

void BlockingMutex::Unlock() {
  CHECK_EQ(owner_, GetThreadSelf());
  owner_ = LOCK_READY;
  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
}

void BlockingMutex::CheckLocked() {
  CHECK_EQ(owner_, GetThreadSelf());
}

uptr GetTlsSize() {
  return 0;
}

void InitTlsSize() {
}

void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
                          uptr *tls_addr, uptr *tls_size) {
#ifdef SANITIZER_GO
  *stk_addr = 0;
  *stk_size = 0;
  *tls_addr = 0;
  *tls_size = 0;
#else
  uptr stack_top, stack_bottom;
  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
  *stk_addr = stack_bottom;
  *stk_size = stack_top - stack_bottom;
  *tls_addr = 0;
  *tls_size = 0;
#endif
}

void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
                   uptr stack_top, uptr stack_bottom, bool fast) {
  (void)fast;
  (void)stack_top;
  (void)stack_bottom;
  stack->max_size = max_s;
  void *tmp[kStackTraceMax];

  // FIXME: CaptureStackBackTrace might be too slow for us.
  // FIXME: Compare with StackWalk64.
  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
  uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
  uptr offset = 0;
  // Skip the RTL frames by searching for the PC in the stacktrace.
  // FIXME: this doesn't work well for the malloc/free stacks yet.
  for (uptr i = 0; i < cs_ret; i++) {
    if (pc != (uptr)tmp[i])
      continue;
    offset = i;
    break;
  }

  stack->size = cs_ret - offset;
  for (uptr i = 0; i < stack->size; i++)
    stack->trace[i] = (uptr)tmp[i + offset];
}

}  // namespace __sanitizer

#endif  // _WIN32
<commit_msg>tsan: fix merge bug<commit_after>//===-- sanitizer_win.cc --------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries and implements windows-specific functions from
// sanitizer_libc.h.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS

#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <stdlib.h>
#include <io.h>
#include <windows.h>

#include "sanitizer_common.h"
#include "sanitizer_libc.h"
#include "sanitizer_mutex.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"

namespace __sanitizer {

#include "sanitizer_syscall_generic.inc"

// --------------------- sanitizer_common.h
uptr GetPageSize() {
  return 1U << 14;  // FIXME: is this configurable?
}

uptr GetMmapGranularity() {
  return 1U << 16;  // FIXME: is this configurable?
}

bool FileExists(const char *filename) {
  UNIMPLEMENTED();
}

uptr internal_getpid() {
  return GetProcessId(GetCurrentProcess());
}

// In contrast to POSIX, on Windows GetCurrentThreadId()
// returns a system-unique identifier.
uptr GetTid() {
  return GetCurrentThreadId();
}

uptr GetThreadSelf() {
  return GetTid();
}

void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
                                uptr *stack_bottom) {
  CHECK(stack_top);
  CHECK(stack_bottom);
  MEMORY_BASIC_INFORMATION mbi;
  CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0);
  // FIXME: is it possible for the stack to not be a single allocation?
  // Are these values what ASan expects to get (reserved, not committed;
  // including stack guard page) ?
  *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize;
  *stack_bottom = (uptr)mbi.AllocationBase;
}

void *MmapOrDie(uptr size, const char *mem_type) {
  void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (rv == 0) {
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes of %s\n",
           size, size, mem_type);
    CHECK("unable to mmap" && 0);
  }
  return rv;
}

void UnmapOrDie(void *addr, uptr size) {
  if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) {
    Report("ERROR: Failed to deallocate 0x%zx (%zd) bytes at address %p\n",
           size, size, addr);
    CHECK("unable to unmap" && 0);
  }
}

void *MmapFixedNoReserve(uptr fixed_addr, uptr size) {
  // FIXME: is this really "NoReserve"? On Win32 this does not matter much,
  // but on Win64 it does.
  void *p = VirtualAlloc((LPVOID)fixed_addr, size,
      MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
  if (p == 0)
    Report("ERROR: Failed to allocate 0x%zx (%zd) bytes at %p (%d)\n",
           size, size, fixed_addr, GetLastError());
  return p;
}

void *MmapFixedOrDie(uptr fixed_addr, uptr size) {
  return MmapFixedNoReserve(fixed_addr, size);
}

void *Mprotect(uptr fixed_addr, uptr size) {
  return VirtualAlloc((LPVOID)fixed_addr, size,
                      MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
}

void FlushUnneededShadowMemory(uptr addr, uptr size) {
  // This is almost useless on 32-bits.
  // FIXME: add madvice-analog when we move to 64-bits.
}

bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) {
  // FIXME: shall we do anything here on Windows?
  return true;
}

void *MapFileToMemory(const char *file_name, uptr *buff_size) {
  UNIMPLEMENTED();
}

static const int kMaxEnvNameLength = 128;
static const DWORD kMaxEnvValueLength = 32767;

namespace {

struct EnvVariable {
  char name[kMaxEnvNameLength];
  char value[kMaxEnvValueLength];
};

}  // namespace

static const int kEnvVariables = 5;
static EnvVariable env_vars[kEnvVariables];
static int num_env_vars;

const char *GetEnv(const char *name) {
  // Note: this implementation caches the values of the environment variables
  // and limits their quantity.
  for (int i = 0; i < num_env_vars; i++) {
    if (0 == internal_strcmp(name, env_vars[i].name))
      return env_vars[i].value;
  }
  CHECK_LT(num_env_vars, kEnvVariables);
  DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value,
                                     kMaxEnvValueLength);
  if (rv > 0 && rv < kMaxEnvValueLength) {
    CHECK_LT(internal_strlen(name), kMaxEnvNameLength);
    internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength);
    num_env_vars++;
    return env_vars[num_env_vars - 1].value;
  }
  return 0;
}

const char *GetPwd() {
  UNIMPLEMENTED();
}

u32 GetUid() {
  UNIMPLEMENTED();
}

void DumpProcessMap() {
  UNIMPLEMENTED();
}

void DisableCoreDumper() {
  UNIMPLEMENTED();
}

void ReExec() {
  UNIMPLEMENTED();
}

void PrepareForSandboxing() {
  // Nothing here for now.
}

bool StackSizeIsUnlimited() {
  UNIMPLEMENTED();
}

void SetStackSizeLimitInBytes(uptr limit) {
  UNIMPLEMENTED();
}

void SleepForSeconds(int seconds) {
  Sleep(seconds * 1000);
}

void SleepForMillis(int millis) {
  Sleep(millis);
}

u64 NanoTime() {
  return 0;
}

void Abort() {
  abort();
  _exit(-1);  // abort is not NORETURN on Windows.
}

#ifndef SANITIZER_GO
int Atexit(void (*function)(void)) {
  return atexit(function);
}
#endif

// ------------------ sanitizer_libc.h
uptr internal_mmap(void *addr, uptr length, int prot, int flags,
                   int fd, u64 offset) {
  UNIMPLEMENTED();
}

uptr internal_munmap(void *addr, uptr length) {
  UNIMPLEMENTED();
}

uptr internal_close(fd_t fd) {
  UNIMPLEMENTED();
}

int internal_isatty(fd_t fd) {
  return _isatty(fd);
}

uptr internal_open(const char *filename, int flags) {
  UNIMPLEMENTED();
}

uptr internal_open(const char *filename, int flags, u32 mode) {
  UNIMPLEMENTED();
}

uptr OpenFile(const char *filename, bool write) {
  UNIMPLEMENTED();
}

uptr internal_read(fd_t fd, void *buf, uptr count) {
  UNIMPLEMENTED();
}

uptr internal_write(fd_t fd, const void *buf, uptr count) {
  if (fd != kStderrFd)
    UNIMPLEMENTED();
  HANDLE err = GetStdHandle(STD_ERROR_HANDLE);
  if (err == 0)
    return 0;  // FIXME: this might not work on some apps.
  DWORD ret;
  if (!WriteFile(err, buf, count, &ret, 0))
    return 0;
  return ret;
}

uptr internal_stat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_lstat(const char *path, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_fstat(fd_t fd, void *buf) {
  UNIMPLEMENTED();
}

uptr internal_filesize(fd_t fd) {
  UNIMPLEMENTED();
}

uptr internal_dup2(int oldfd, int newfd) {
  UNIMPLEMENTED();
}

uptr internal_readlink(const char *path, char *buf, uptr bufsize) {
  UNIMPLEMENTED();
}

uptr internal_sched_yield() {
  Sleep(0);
  return 0;
}

void internal__exit(int exitcode) {
  _exit(exitcode);
}

// ---------------------- BlockingMutex ---------------- {{{1
const uptr LOCK_UNINITIALIZED = 0;
const uptr LOCK_READY = (uptr)-1;

BlockingMutex::BlockingMutex(LinkerInitialized li) {
  // FIXME: see comments in BlockingMutex::Lock() for the details.
  CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED);

  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

BlockingMutex::BlockingMutex() {
  CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_));
  InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  owner_ = LOCK_READY;
}

void BlockingMutex::Lock() {
  if (owner_ == LOCK_UNINITIALIZED) {
    // FIXME: hm, global BlockingMutex objects are not initialized?!?
    // This might be a side effect of the clang+cl+link Frankenbuild...
    new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1));

    // FIXME: If it turns out the linker doesn't invoke our
    // constructors, we should probably manually Lock/Unlock all the global
    // locks while we're starting in one thread to avoid double-init races.
  }
  EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
  CHECK_EQ(owner_, LOCK_READY);
  owner_ = GetThreadSelf();
}

void BlockingMutex::Unlock() {
  CHECK_EQ(owner_, GetThreadSelf());
  owner_ = LOCK_READY;
  LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_);
}

void BlockingMutex::CheckLocked() {
  CHECK_EQ(owner_, GetThreadSelf());
}

uptr GetTlsSize() {
  return 0;
}

void InitTlsSize() {
}

void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
                          uptr *tls_addr, uptr *tls_size) {
#ifdef SANITIZER_GO
  *stk_addr = 0;
  *stk_size = 0;
  *tls_addr = 0;
  *tls_size = 0;
#else
  uptr stack_top, stack_bottom;
  GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom);
  *stk_addr = stack_bottom;
  *stk_size = stack_top - stack_bottom;
  *tls_addr = 0;
  *tls_size = 0;
#endif
}

void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
                   uptr stack_top, uptr stack_bottom, bool fast) {
  (void)fast;
  (void)stack_top;
  (void)stack_bottom;
  stack->max_size = max_s;
  void *tmp[kStackTraceMax];

  // FIXME: CaptureStackBackTrace might be too slow for us.
  // FIXME: Compare with StackWalk64.
  // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc
  uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0);
  uptr offset = 0;
  // Skip the RTL frames by searching for the PC in the stacktrace.
  // FIXME: this doesn't work well for the malloc/free stacks yet.
  for (uptr i = 0; i < cs_ret; i++) {
    if (pc != (uptr)tmp[i])
      continue;
    offset = i;
    break;
  }

  stack->size = cs_ret - offset;
  for (uptr i = 0; i < stack->size; i++)
    stack->trace[i] = (uptr)tmp[i + offset];
}

}  // namespace __sanitizer

#endif  // _WIN32
<|endoftext|>


<commit_before>#include "vm.h"

#include "opcodes.h"
#include "sections.h"

#include <cassert>
#include <iostream>

#define STR_MASK 0x8000000000000000
#define ARRAY_MASK 0x4000000000000000

#define MASK(MASK, V) ((V) | (MASK))
#define UNMASK(MASK, V) ((V) & ~(MASK))
#define IS_MASK(MASK, V) ((V) & (MASK))

#define MASK_STR(STR) MASK(STR_MASK, STR)
#define UNMASK_STR(STR) UNMASK(STR_MASK, STR)
#define IS_STR(STR) IS_MASK(STR_MASK, STR)

#define MASK_ARRAY(ARRAY) MASK(ARRAY_MASK, ARRAY)
#define UNMASK_ARRAY(ARRAY) UNMASK(ARRAY_MASK, ARRAY)
#define IS_ARRAY(ARRAY) IS_MASK(ARRAY_MASK, ARRAY)

#define JS_FUNCTION(FN_NAME) __used static uintptr_t FN_NAME(ceos::VM &vm, unsigned argv)

#define EACH_ARG(IT) \
  for (uintptr_t I = 0, IT; (IT = vm.arg(I)), I < argv; I++)

#define BASIC_MATH(NAME, OP) \
  JS_FUNCTION(NAME) { \
    assert(argv == 2); \
 \
    return vm.arg(0) OP vm.arg(1); \
  }

BASIC_MATH(add, +)
BASIC_MATH(sub, -)
BASIC_MATH(mul, *)
BASIC_MATH(div, /)
BASIC_MATH(lt, <)
BASIC_MATH(gt, >)
BASIC_MATH(lte, <=)
BASIC_MATH(gte, >=)
BASIC_MATH(equals, ==)

JS_FUNCTION(print) {
  for (unsigned i = 0; i < argv; i++) {
    uintptr_t arg = vm.arg(i);
    if (IS_STR(arg)) {
      std::cout << (reinterpret_cast<std::string *>(UNMASK_STR(arg)))->c_str() << "\n";
    } else if (IS_ARRAY(arg)) {
      std::vector<uintptr_t> *array = reinterpret_cast<__typeof__(array)>(UNMASK_ARRAY(arg));
      for (auto a : *array) {
        std::cout << a << " ";
      }
      std::cout << "\n";
    } else {
      std::cout << static_cast<int>(arg) << "\n";
    }
  }

  return 0;
}

JS_FUNCTION(list) {
  auto list = new std::vector<uintptr_t>();
  EACH_ARG(arg) {
    list->push_back(arg);
  }
  return MASK_ARRAY(reinterpret_cast<uintptr_t>(list));
}

#undef BASIC_MATH

namespace ceos {

  void VM::registerBuiltins() {
#define REGISTER(NAME) JSFunctionType NAME##_ = NAME; m_functionTable[#NAME] = (uintptr_t)NAME##_

    REGISTER(print);
    REGISTER(list);

    REGISTER(add);
    REGISTER(sub);
    REGISTER(mul);
    REGISTER(div);
    REGISTER(lt);
    REGISTER(gt);
    REGISTER(lte);
    REGISTER(gte);
    REGISTER(equals);

#undef REGISTER
  }

  void VM::execute() {
    auto header = read<int>();

    // section marker
    assert(header == Section::Header);

    while (true) {
      auto section = read<int>();

      if (pc > length) {
        return;
      }

      switch (section) {
        case Section::Strings:
          loadStrings();
          break;
        case Section::Functions:
          loadFunctions();

          for (unsigned i = 0; i < m_userFunctions.size(); i++) {
            Function *fn = &m_userFunctions[i];
            m_functionTable[fn->name] = MASK_STR(reinterpret_cast<uintptr_t>(fn));
          }

          break;
        case Section::Text:
          run();
          break;
      }
    }
  }

  void VM::loadStrings() {
    while (true) {
      auto header = read<int>();

      if (header == Section::Header) {
        break;
      }

      pc -= 4;
      char *str = readStr();
      m_stringTable.push_back(str);
    }
  }

  void VM::loadFunctions() {
    auto initialHeader = read<int>();
    assert(initialHeader == Section::FunctionHeader);

    while (true) {
      char *name = readStr();
      auto nargs = read<int>();

      m_userFunctions.push_back(Function(name, nargs, pc));

      while (true) {
        auto opcode = read<int>();
        if (opcode == Section::Header) {
          return;
        } else if (opcode == Section::FunctionHeader) {
          break;
        }
      }
    }
  }

  void VM::run() {
    while (true) {
      auto opcode = read<int>();

      if (pc > length) {
        return;
      }

      switch (opcode) {
        case Opcode::push: {
          auto value = read<int>();
          stack_push(value);
          break;
        }
        case Opcode::call: {
          int nargs = stack_pop();
          uintptr_t fn_address = stack_pop();

          --nargs; //pop'd the callee

          stack_push(pc);
          stack_push(nargs);
          stack_push(ebp);
          ebp = esp;

          uintptr_t ret;
          if (IS_STR(fn_address)) {
            Function *fn = reinterpret_cast<__typeof__(fn)>(UNMASK_STR(fn_address));
            pc = fn->offset;
            break;
          } else {
            JSFunctionType fn = reinterpret_cast<__typeof__(fn)>(fn_address);
            ret = fn(*this, nargs);
            stack_push(ret);
          }
        }
        case Opcode::ret: {
          uintptr_t ret = stack_pop();

          esp = ebp;

          ebp = stack_pop();
          unsigned nargs = stack_pop();
          auto ret_addr = stack_pop();

          esp -= nargs;

          stack_push(ret);
          pc = ret_addr;
          break;
        }
        case Opcode::load_string: {
          auto stringID = read<int>();
          stack_push(MASK_STR(reinterpret_cast<uintptr_t>(&m_stringTable[stringID])));
          break;
        }
        case Opcode::lookup: {
          auto id = read<int>();
          auto fnAddress = read<uintptr_t>();
          if (!fnAddress) {
            auto fnName = m_stringTable[id];
            fnAddress = m_functionTable[fnName];
            memcpy(m_bytecode + pc - sizeof(uintptr_t), &fnAddress, sizeof(uintptr_t));
          }
          stack_push(fnAddress);
          break;
        }
        case Opcode::jmp:  {
          auto target = read<int>();
          pc += target;
          break;
        }
        case Opcode::jz: {
          auto target = read<int>();
          int value = static_cast<int>(stack_pop());
          if (value == 0) {
            pc += target;
          }
          break;
        }
        case Opcode::push_arg: {
          auto index = read<int>();
          stack_push(arg(index));
          break;
        }
        default:
          std::cerr << "Unhandled opcode: " << Opcode::typeName(static_cast<Opcode::Type>(opcode)) << "\n";
          throw;
      }
    }
  }

}
<commit_msg>Add `and` and `or`<commit_after>#include "vm.h"

#include "opcodes.h"
#include "sections.h"

#include <cassert>
#include <iostream>

#define STR_MASK 0x8000000000000000
#define ARRAY_MASK 0x4000000000000000

#define MASK(MASK, V) ((V) | (MASK))
#define UNMASK(MASK, V) ((V) & ~(MASK))
#define IS_MASK(MASK, V) ((V) & (MASK))

#define MASK_STR(STR) MASK(STR_MASK, STR)
#define UNMASK_STR(STR) UNMASK(STR_MASK, STR)
#define IS_STR(STR) IS_MASK(STR_MASK, STR)

#define MASK_ARRAY(ARRAY) MASK(ARRAY_MASK, ARRAY)
#define UNMASK_ARRAY(ARRAY) UNMASK(ARRAY_MASK, ARRAY)
#define IS_ARRAY(ARRAY) IS_MASK(ARRAY_MASK, ARRAY)

#define JS_FUNCTION(FN_NAME) __used static uintptr_t FN_NAME(ceos::VM &vm, unsigned argv)

#define EACH_ARG(IT) \
  for (uintptr_t I = 0, IT; (IT = vm.arg(I)), I < argv; I++)

#define BASIC_MATH(NAME, OP) \
  JS_FUNCTION(NAME) { \
    assert(argv == 2); \
 \
    return vm.arg(0) OP vm.arg(1); \
  }

BASIC_MATH(add, +)
BASIC_MATH(sub, -)
BASIC_MATH(mul, *)
BASIC_MATH(div, /)
BASIC_MATH(lt, <)
BASIC_MATH(gt, >)
BASIC_MATH(lte, <=)
BASIC_MATH(gte, >=)
BASIC_MATH(equals, ==)
BASIC_MATH(_and, &&)
BASIC_MATH(_or, ||)

JS_FUNCTION(print) {
  for (unsigned i = 0; i < argv; i++) {
    uintptr_t arg = vm.arg(i);
    if (IS_STR(arg)) {
      std::cout << (reinterpret_cast<std::string *>(UNMASK_STR(arg)))->c_str() << "\n";
    } else if (IS_ARRAY(arg)) {
      std::vector<uintptr_t> *array = reinterpret_cast<__typeof__(array)>(UNMASK_ARRAY(arg));
      for (auto a : *array) {
        std::cout << a << " ";
      }
      std::cout << "\n";
    } else {
      std::cout << static_cast<int>(arg) << "\n";
    }
  }

  return 0;
}

JS_FUNCTION(list) {
  auto list = new std::vector<uintptr_t>();
  EACH_ARG(arg) {
    list->push_back(arg);
  }
  return MASK_ARRAY(reinterpret_cast<uintptr_t>(list));
}

#undef BASIC_MATH

namespace ceos {

  void VM::registerBuiltins() {
#define REGISTER(NAME, FN) JSFunctionType NAME##_ = FN; m_functionTable[#NAME] = (uintptr_t)NAME##_

    REGISTER(print, print);
    REGISTER(list, list);

    REGISTER(add, add);
    REGISTER(sub, sub);
    REGISTER(mul, mul);
    REGISTER(div, div);
    REGISTER(lt, lt);
    REGISTER(gt, gt);
    REGISTER(lte, lte);
    REGISTER(gte, gte);
    REGISTER(equals, equals);
    REGISTER(and, _and);
    REGISTER(or, _or);

#undef REGISTER
  }

  void VM::execute() {
    auto header = read<int>();

    // section marker
    assert(header == Section::Header);

    while (true) {
      auto section = read<int>();

      if (pc > length) {
        return;
      }

      switch (section) {
        case Section::Strings:
          loadStrings();
          break;
        case Section::Functions:
          loadFunctions();

          for (unsigned i = 0; i < m_userFunctions.size(); i++) {
            Function *fn = &m_userFunctions[i];
            m_functionTable[fn->name] = MASK_STR(reinterpret_cast<uintptr_t>(fn));
          }

          break;
        case Section::Text:
          run();
          break;
      }
    }
  }

  void VM::loadStrings() {
    while (true) {
      auto header = read<int>();

      if (header == Section::Header) {
        break;
      }

      pc -= 4;
      char *str = readStr();
      m_stringTable.push_back(str);
    }
  }

  void VM::loadFunctions() {
    auto initialHeader = read<int>();
    assert(initialHeader == Section::FunctionHeader);

    while (true) {
      char *name = readStr();
      auto nargs = read<int>();

      m_userFunctions.push_back(Function(name, nargs, pc));

      while (true) {
        auto opcode = read<int>();
        if (opcode == Section::Header) {
          return;
        } else if (opcode == Section::FunctionHeader) {
          break;
        }
      }
    }
  }

  void VM::run() {
    while (true) {
      auto opcode = read<int>();

      if (pc > length) {
        return;
      }

      switch (opcode) {
        case Opcode::push: {
          auto value = read<int>();
          stack_push(value);
          break;
        }
        case Opcode::call: {
          int nargs = stack_pop();
          uintptr_t fn_address = stack_pop();

          --nargs; //pop'd the callee

          stack_push(pc);
          stack_push(nargs);
          stack_push(ebp);
          ebp = esp;

          uintptr_t ret;
          if (IS_STR(fn_address)) {
            Function *fn = reinterpret_cast<__typeof__(fn)>(UNMASK_STR(fn_address));
            pc = fn->offset;
            break;
          } else {
            JSFunctionType fn = reinterpret_cast<__typeof__(fn)>(fn_address);
            ret = fn(*this, nargs);
            stack_push(ret);
          }
        }
        case Opcode::ret: {
          uintptr_t ret = stack_pop();

          esp = ebp;

          ebp = stack_pop();
          unsigned nargs = stack_pop();
          auto ret_addr = stack_pop();

          esp -= nargs;

          stack_push(ret);
          pc = ret_addr;
          break;
        }
        case Opcode::load_string: {
          auto stringID = read<int>();
          stack_push(MASK_STR(reinterpret_cast<uintptr_t>(&m_stringTable[stringID])));
          break;
        }
        case Opcode::lookup: {
          auto id = read<int>();
          auto fnAddress = read<uintptr_t>();
          if (!fnAddress) {
            auto fnName = m_stringTable[id];
            fnAddress = m_functionTable[fnName];
            memcpy(m_bytecode + pc - sizeof(uintptr_t), &fnAddress, sizeof(uintptr_t));
          }
          stack_push(fnAddress);
          break;
        }
        case Opcode::jmp:  {
          auto target = read<int>();
          pc += target;
          break;
        }
        case Opcode::jz: {
          auto target = read<int>();
          int value = static_cast<int>(stack_pop());
          if (value == 0) {
            pc += target;
          }
          break;
        }
        case Opcode::push_arg: {
          auto index = read<int>();
          stack_push(arg(index));
          break;
        }
        default:
          std::cerr << "Unhandled opcode: " << Opcode::typeName(static_cast<Opcode::Type>(opcode)) << "\n";
          throw;
      }
    }
  }

}
<|endoftext|>


<commit_before>#include "Vision/RTSVisionManager.h"

#include "EngineUtils.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/GameplayStatics.h"

#include "RTSGameMode.h"
#include "RTSLog.h"
#include "RTSOwnerComponent.h"
#include "RTSPlayerState.h"
#include "RTSTeamInfo.h"
#include "Vision/RTSFogOfWarActor.h"
#include "Vision/RTSVisibleActor.h"
#include "Vision/RTSVisibleComponent.h"
#include "Vision/RTSVisionComponent.h"
#include "Vision/RTSVisionInfo.h"
#include "Vision/RTSVisionVolume.h"

ARTSVisionManager::ARTSVisionManager(const FObjectInitializer& ObjectInitializer /*= FObjectInitializer::Get()*/)
    : Super(ObjectInitializer)
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = true;
}

void ARTSVisionManager::Initialize()
{
    UWorld* World = GetWorld();

    // Find local player.
    APlayerController* FirstLocalPlayerController = GetGameInstance()->GetFirstLocalPlayerController();

    if (IsValid(FirstLocalPlayerController))
    {
        SetLocalPlayerState(FirstLocalPlayerController->GetPlayerState<ARTSPlayerState>());
    }

    // Find manager and info actors.
    for (TActorIterator<AActor> It(World); It; ++It)
    {
        AActor* Actor = *It;

        if (IsValid(Actor))
        {
            if (Actor->IsA(ARTSFogOfWarActor::StaticClass()))
            {
                FogOfWarActor = Cast<ARTSFogOfWarActor>(Actor);
            }
            else if (Actor->IsA(ARTSVisionInfo::StaticClass()))
            {
                ARTSVisionInfo* VisionInfo = Cast<ARTSVisionInfo>(Actor);
                VisionInfos.Add(VisionInfo);
            }
            else if (Actor->IsA(ARTSVisionVolume::StaticClass()))
            {
                VisionVolume = Cast<ARTSVisionVolume>(Actor);
            }
        }
    }

    if (IsValid(VisionVolume))
    {
        VisionVolume->Initialize();
    }

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        VisionInfo->Initialize(VisionVolume);
    }

    if (IsValid(FogOfWarActor))
    {
        FogOfWarActor->Initialize(VisionVolume);
    }

    // Discover initial vision actors.
    for (TActorIterator<AActor> It(GetWorld()); It; ++It)
    {
        AActor* Actor = *It;

        if (!IsValid(Actor))
        {
            continue;
        }

        URTSVisibleComponent* VisibleComponent = Actor->FindComponentByClass<URTSVisibleComponent>();

        if (IsValid(VisibleComponent))
        {
            AddVisibleActor(Actor);
        }

        URTSVisionComponent* VisionComponent = Actor->FindComponentByClass<URTSVisionComponent>();

        if (IsValid(VisionComponent))
        {
            AddVisionActor(Actor);
        }
    }
}

void ARTSVisionManager::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    // Update unit vision.
    for (FRTSVisionActor& VisionActor : VisionActors)
    {
        UpdateVisionActor(VisionActor);
    }

    // Update unit visibility.
    for (FRTSVisibleActor& VisibleActor : VisibleActors)
    {
        if (!VisibleActor.IsActorValid())
        {
            continue;
        }

        FIntVector TileLocation;

        if (IsValid(VisibleActor.VisionComponent))
        {
            TileLocation = VisibleActor.VisionComponent->ActorLocationTile;
        }
        else
        {
            TileLocation = IsValid(VisionVolume)
                ? VisionVolume->WorldToTile(VisibleActor.Actor->GetActorLocation())
                : FIntVector::ZeroValue;
        }

        if (GetNetMode() <= NM_ListenServer)
        {
            ARTSGameMode* GameMode = Cast<ARTSGameMode>(UGameplayStatics::GetGameMode(this));

            // Update server vision.
            for (ARTSTeamInfo* Team : GameMode->GetTeams())
            {
                // If friendly, always visible.
                if (VisibleActor.OwnerComponent && VisibleActor.OwnerComponent->GetPlayerOwner() &&
                    VisibleActor.OwnerComponent->GetPlayerOwner()->GetTeam() == Team)
                {
                    for (AController* Player : Team->GetTeamMembers())
                    {
                        // Set visibility according to team vision.
                        VisibleActor.VisibleComponent->SetVisionStateForPlayer(Player, ERTSVisionState::VISION_Visible);
                    }

                    continue;
                }

                // Get matching vision info.
                if (VisionInfos.IsValidIndex(Team->GetTeamIndex()))
                {
                    ARTSVisionInfo* TeamVision = VisionInfos[Team->GetTeamIndex()];
                    ERTSVisionState NewVision = TeamVision->GetVision(TileLocation.X, TileLocation.Y);

                    for (AController* Player : Team->GetTeamMembers())
                    {
                        // Set visibility according to team vision.
                        VisibleActor.VisibleComponent->SetVisionStateForPlayer(Player, NewVision);
                    }
                }
                else
                {
                    UE_LOG(LogRTS, Warning, TEXT("No vision info available for team %i."), Team->GetTeamIndex());
                }

            }
        }

        if (GetNetMode() >= NM_ListenServer)
        {
            // Update client vision.
            ERTSVisionState NewVision = IsValid(LocalVisionInfo)
                ? LocalVisionInfo->GetVision(TileLocation.X, TileLocation.Y)
                : ERTSVisionState::VISION_Visible;

            VisibleActor.VisibleComponent->SetClientVisionState(NewVision);
        }
    }
}

void ARTSVisionManager::SetLocalPlayerState(ARTSPlayerState* InLocalPlayerState)
{
    LocalPlayerState = InLocalPlayerState;

    if (IsValid(InLocalPlayerState))
    {
        UE_LOG(LogRTS, Log, TEXT("Using local player state %s."), *InLocalPlayerState->GetName());
    }
}

void ARTSVisionManager::SetLocalVisionInfo(ARTSVisionInfo* InLocalVisionInfo)
{
    if (!IsValid(InLocalVisionInfo))
    {
        return;
    }

    LocalVisionInfo = InLocalVisionInfo;

    if (!VisionInfos.Contains(LocalVisionInfo))
    {
        // It seems like this has been replicated after our initialization.
        VisionInfos.Add(LocalVisionInfo);
        LocalVisionInfo->Initialize(VisionVolume);
    }

    if (IsValid(FogOfWarActor))
    {
        FogOfWarActor->SetupVisionInfo(LocalVisionInfo);
    }

    UE_LOG(LogRTS, Log, TEXT("Using local vision info %s."), *LocalVisionInfo->GetName());
}

void ARTSVisionManager::AddVisibleActor(AActor* Actor)
{
    for (int32 Index = 0; Index < VisibleActors.Num(); ++Index)
    {
        if (VisibleActors[Index].Actor == Actor)
        {
            // Already added.
            return;
        }
    }

    FRTSVisibleActor VisibleActor(Actor);

    if (!VisibleActor.IsActorValid())
    {
        return;
    }

    VisibleActors.Add(VisibleActor);
}

void ARTSVisionManager::RemoveVisibleActor(AActor* Actor)
{
    int32 IndexToRemove = -1;

    for (int32 Index = 0; Index < VisibleActors.Num(); ++Index)
    {
        if (VisibleActors[Index].Actor == Actor)
        {
            IndexToRemove = Index;
            break;
        }
    }

    if (VisibleActors.IsValidIndex(IndexToRemove))
    {
        VisibleActors.RemoveAt(IndexToRemove);
    }
}

void ARTSVisionManager::AddVisionActor(AActor* Actor)
{
    for (int32 Index = 0; Index < VisionActors.Num(); ++Index)
    {
        if (VisionActors[Index].Actor == Actor)
        {
            // Already added.
            return;
        }
    }

    FRTSVisionActor VisionActor(Actor);

    if (!VisionActor.IsActorValid())
    {
        return;
    }

    VisionActors.Add(VisionActor);
}

void ARTSVisionManager::RemoveVisionActor(AActor* Actor)
{
    int32 IndexToRemove = -1;

    for (int32 Index = 0; Index < VisionActors.Num(); ++Index)
    {
        if (VisionActors[Index].Actor == Actor)
        {
            IndexToRemove = Index;
            break;
        }
    }

    if (VisionActors.IsValidIndex(IndexToRemove))
    {
        ResetVisionForActor(VisionActors[IndexToRemove]);
        VisionActors.RemoveAt(IndexToRemove);
    }
}

void ARTSVisionManager::UpdateVisionActor(const FRTSVisionActor& VisionActor)
{
    if (VisionInfos.Num() == 0)
    {
        return;
    }

    if (!IsValid(LocalPlayerState))
    {
        return;
    }

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        if (!VisionInfo->IsInitialized())
        {
            return;
        }
    }

    // We only need to update vision for actors who have moved onto a new tile.
    if (!VisionActor.IsActorValid())
    {
        return;
    }

    FVector ActorLocationWorld = VisionActor.Actor->GetActorLocation();
    FIntVector ActorLocationTile = VisionVolume->WorldToTile(ActorLocationWorld);
    int32 ActorLocationHeightLevel = VisionVolume->GetTileHeight(ActorLocationTile);

    if (VisionActor.VisionComponent->ActorLocationTile.X == ActorLocationTile.X &&
        VisionActor.VisionComponent->ActorLocationTile.Y == ActorLocationTile.Y &&
        VisionActor.VisionComponent->ActorLocationTile.Z == ActorLocationHeightLevel)
    {
        // Hasn't moved.
        return;
    }
    else
    {
        // Moved to new grid cell.
        VisionActor.VisionComponent->ActorLocationTile = ActorLocationTile;
        VisionActor.VisionComponent->ActorLocationTile.Z = ActorLocationHeightLevel;
    }

    ResetVisionForActor(VisionActor);

    // Apply vision.
    TArray<int32> CachedVisionTiles;

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        VisionInfo->ApplyVisionForActor(VisionActor.Actor, CachedVisionTiles);
    }
}

void ARTSVisionManager::ResetVisionForActor(const FRTSVisionActor& VisionActor)
{
    if (!VisionActor.IsActorValid())
    {
        return;
    }

    // Reset vision.
    for (const FRTSVisionInfoTileList& VisionInfoTileList : VisionActor.VisionComponent->VisibleTiles)
    {
        VisionInfoTileList.VisionInfo->ResetVisionForActor(VisionActor.Actor);
    }

    VisionActor.VisionComponent->VisibleTiles.Empty();
}
<commit_msg>Correctly calculate client-side vision state in standalone games.<commit_after>#include "Vision/RTSVisionManager.h"

#include "EngineUtils.h"
#include "Engine/GameInstance.h"
#include "Engine/World.h"
#include "GameFramework/PlayerController.h"
#include "Kismet/GameplayStatics.h"

#include "RTSGameMode.h"
#include "RTSLog.h"
#include "RTSOwnerComponent.h"
#include "RTSPlayerState.h"
#include "RTSTeamInfo.h"
#include "Vision/RTSFogOfWarActor.h"
#include "Vision/RTSVisibleActor.h"
#include "Vision/RTSVisibleComponent.h"
#include "Vision/RTSVisionComponent.h"
#include "Vision/RTSVisionInfo.h"
#include "Vision/RTSVisionVolume.h"

ARTSVisionManager::ARTSVisionManager(const FObjectInitializer& ObjectInitializer /*= FObjectInitializer::Get()*/)
    : Super(ObjectInitializer)
{
    PrimaryActorTick.bCanEverTick = true;
    PrimaryActorTick.bStartWithTickEnabled = true;
}

void ARTSVisionManager::Initialize()
{
    UWorld* World = GetWorld();

    // Find local player.
    APlayerController* FirstLocalPlayerController = GetGameInstance()->GetFirstLocalPlayerController();

    if (IsValid(FirstLocalPlayerController))
    {
        SetLocalPlayerState(FirstLocalPlayerController->GetPlayerState<ARTSPlayerState>());
    }

    // Find manager and info actors.
    for (TActorIterator<AActor> It(World); It; ++It)
    {
        AActor* Actor = *It;

        if (IsValid(Actor))
        {
            if (Actor->IsA(ARTSFogOfWarActor::StaticClass()))
            {
                FogOfWarActor = Cast<ARTSFogOfWarActor>(Actor);
            }
            else if (Actor->IsA(ARTSVisionInfo::StaticClass()))
            {
                ARTSVisionInfo* VisionInfo = Cast<ARTSVisionInfo>(Actor);
                VisionInfos.Add(VisionInfo);
            }
            else if (Actor->IsA(ARTSVisionVolume::StaticClass()))
            {
                VisionVolume = Cast<ARTSVisionVolume>(Actor);
            }
        }
    }

    if (IsValid(VisionVolume))
    {
        VisionVolume->Initialize();
    }

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        VisionInfo->Initialize(VisionVolume);
    }

    if (IsValid(FogOfWarActor))
    {
        FogOfWarActor->Initialize(VisionVolume);
    }

    // Discover initial vision actors.
    for (TActorIterator<AActor> It(GetWorld()); It; ++It)
    {
        AActor* Actor = *It;

        if (!IsValid(Actor))
        {
            continue;
        }

        URTSVisibleComponent* VisibleComponent = Actor->FindComponentByClass<URTSVisibleComponent>();

        if (IsValid(VisibleComponent))
        {
            AddVisibleActor(Actor);
        }

        URTSVisionComponent* VisionComponent = Actor->FindComponentByClass<URTSVisionComponent>();

        if (IsValid(VisionComponent))
        {
            AddVisionActor(Actor);
        }
    }
}

void ARTSVisionManager::Tick(float DeltaSeconds)
{
    Super::Tick(DeltaSeconds);

    // Update unit vision.
    for (FRTSVisionActor& VisionActor : VisionActors)
    {
        UpdateVisionActor(VisionActor);
    }

    // Update unit visibility.
    for (FRTSVisibleActor& VisibleActor : VisibleActors)
    {
        if (!VisibleActor.IsActorValid())
        {
            continue;
        }

        FIntVector TileLocation;

        if (IsValid(VisibleActor.VisionComponent))
        {
            TileLocation = VisibleActor.VisionComponent->ActorLocationTile;
        }
        else
        {
            TileLocation = IsValid(VisionVolume)
                ? VisionVolume->WorldToTile(VisibleActor.Actor->GetActorLocation())
                : FIntVector::ZeroValue;
        }

        if (GetNetMode() <= NM_ListenServer)
        {
            ARTSGameMode* GameMode = Cast<ARTSGameMode>(UGameplayStatics::GetGameMode(this));

            // Update server vision.
            for (ARTSTeamInfo* Team : GameMode->GetTeams())
            {
                // If friendly, always visible.
                if (VisibleActor.OwnerComponent && VisibleActor.OwnerComponent->GetPlayerOwner() &&
                    VisibleActor.OwnerComponent->GetPlayerOwner()->GetTeam() == Team)
                {
                    for (AController* Player : Team->GetTeamMembers())
                    {
                        // Set visibility according to team vision.
                        VisibleActor.VisibleComponent->SetVisionStateForPlayer(Player, ERTSVisionState::VISION_Visible);
                    }

                    continue;
                }

                // Get matching vision info.
                if (VisionInfos.IsValidIndex(Team->GetTeamIndex()))
                {
                    ARTSVisionInfo* TeamVision = VisionInfos[Team->GetTeamIndex()];
                    ERTSVisionState NewVision = TeamVision->GetVision(TileLocation.X, TileLocation.Y);

                    for (AController* Player : Team->GetTeamMembers())
                    {
                        // Set visibility according to team vision.
                        VisibleActor.VisibleComponent->SetVisionStateForPlayer(Player, NewVision);
                    }
                }
                else
                {
                    UE_LOG(LogRTS, Warning, TEXT("No vision info available for team %i."), Team->GetTeamIndex());
                }

            }
        }

        if (GetNetMode() != NM_DedicatedServer)
        {
            // Update client vision.
            ERTSVisionState NewVision = IsValid(LocalVisionInfo)
                ? LocalVisionInfo->GetVision(TileLocation.X, TileLocation.Y)
                : ERTSVisionState::VISION_Visible;

            VisibleActor.VisibleComponent->SetClientVisionState(NewVision);
        }
    }
}

void ARTSVisionManager::SetLocalPlayerState(ARTSPlayerState* InLocalPlayerState)
{
    LocalPlayerState = InLocalPlayerState;

    if (IsValid(InLocalPlayerState))
    {
        UE_LOG(LogRTS, Log, TEXT("Using local player state %s."), *InLocalPlayerState->GetName());
    }
}

void ARTSVisionManager::SetLocalVisionInfo(ARTSVisionInfo* InLocalVisionInfo)
{
    if (!IsValid(InLocalVisionInfo))
    {
        return;
    }

    LocalVisionInfo = InLocalVisionInfo;

    if (!VisionInfos.Contains(LocalVisionInfo))
    {
        // It seems like this has been replicated after our initialization.
        VisionInfos.Add(LocalVisionInfo);
        LocalVisionInfo->Initialize(VisionVolume);
    }

    if (IsValid(FogOfWarActor))
    {
        FogOfWarActor->SetupVisionInfo(LocalVisionInfo);
    }

    UE_LOG(LogRTS, Log, TEXT("Using local vision info %s."), *LocalVisionInfo->GetName());
}

void ARTSVisionManager::AddVisibleActor(AActor* Actor)
{
    for (int32 Index = 0; Index < VisibleActors.Num(); ++Index)
    {
        if (VisibleActors[Index].Actor == Actor)
        {
            // Already added.
            return;
        }
    }

    FRTSVisibleActor VisibleActor(Actor);

    if (!VisibleActor.IsActorValid())
    {
        return;
    }

    VisibleActors.Add(VisibleActor);
}

void ARTSVisionManager::RemoveVisibleActor(AActor* Actor)
{
    int32 IndexToRemove = -1;

    for (int32 Index = 0; Index < VisibleActors.Num(); ++Index)
    {
        if (VisibleActors[Index].Actor == Actor)
        {
            IndexToRemove = Index;
            break;
        }
    }

    if (VisibleActors.IsValidIndex(IndexToRemove))
    {
        VisibleActors.RemoveAt(IndexToRemove);
    }
}

void ARTSVisionManager::AddVisionActor(AActor* Actor)
{
    for (int32 Index = 0; Index < VisionActors.Num(); ++Index)
    {
        if (VisionActors[Index].Actor == Actor)
        {
            // Already added.
            return;
        }
    }

    FRTSVisionActor VisionActor(Actor);

    if (!VisionActor.IsActorValid())
    {
        return;
    }

    VisionActors.Add(VisionActor);
}

void ARTSVisionManager::RemoveVisionActor(AActor* Actor)
{
    int32 IndexToRemove = -1;

    for (int32 Index = 0; Index < VisionActors.Num(); ++Index)
    {
        if (VisionActors[Index].Actor == Actor)
        {
            IndexToRemove = Index;
            break;
        }
    }

    if (VisionActors.IsValidIndex(IndexToRemove))
    {
        ResetVisionForActor(VisionActors[IndexToRemove]);
        VisionActors.RemoveAt(IndexToRemove);
    }
}

void ARTSVisionManager::UpdateVisionActor(const FRTSVisionActor& VisionActor)
{
    if (VisionInfos.Num() == 0)
    {
        return;
    }

    if (!IsValid(LocalPlayerState))
    {
        return;
    }

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        if (!VisionInfo->IsInitialized())
        {
            return;
        }
    }

    // We only need to update vision for actors who have moved onto a new tile.
    if (!VisionActor.IsActorValid())
    {
        return;
    }

    FVector ActorLocationWorld = VisionActor.Actor->GetActorLocation();
    FIntVector ActorLocationTile = VisionVolume->WorldToTile(ActorLocationWorld);
    int32 ActorLocationHeightLevel = VisionVolume->GetTileHeight(ActorLocationTile);

    if (VisionActor.VisionComponent->ActorLocationTile.X == ActorLocationTile.X &&
        VisionActor.VisionComponent->ActorLocationTile.Y == ActorLocationTile.Y &&
        VisionActor.VisionComponent->ActorLocationTile.Z == ActorLocationHeightLevel)
    {
        // Hasn't moved.
        return;
    }
    else
    {
        // Moved to new grid cell.
        VisionActor.VisionComponent->ActorLocationTile = ActorLocationTile;
        VisionActor.VisionComponent->ActorLocationTile.Z = ActorLocationHeightLevel;
    }

    ResetVisionForActor(VisionActor);

    // Apply vision.
    TArray<int32> CachedVisionTiles;

    for (ARTSVisionInfo* VisionInfo : VisionInfos)
    {
        VisionInfo->ApplyVisionForActor(VisionActor.Actor, CachedVisionTiles);
    }
}

void ARTSVisionManager::ResetVisionForActor(const FRTSVisionActor& VisionActor)
{
    if (!VisionActor.IsActorValid())
    {
        return;
    }

    // Reset vision.
    for (const FRTSVisionInfoTileList& VisionInfoTileList : VisionActor.VisionComponent->VisibleTiles)
    {
        VisionInfoTileList.VisionInfo->ResetVisionForActor(VisionActor.Actor);
    }

    VisionActor.VisionComponent->VisibleTiles.Empty();
}
<|endoftext|>