<commit_before>#include "eval.h"
#include <sstream>
#include <unordered_map>
#include <functional>

namespace Akumuli {
namespace QP {

class ParseError : public std::exception {
    const std::string message_;

public:
    ParseError(const char* msg)
        : message_(msg)
    {
    }

    ParseError(std::string msg)
        : message_(std::move(msg))
    {
    }

    const char* what() const noexcept override {
        return message_.c_str();
    }
};

class ExpressionNode {
public:
    virtual ~ExpressionNode() = default;
    virtual double eval(MutableSample& mut) = 0;
};

class ConstantNode : public ExpressionNode {
    const double cval_;
public:
    ConstantNode(double value)
        : cval_(value)
    {
    }

    double eval(MutableSample&) override {
        return cval_;
    }
};

class ValueNode : public ExpressionNode {
    int ixval_;
public:
    ValueNode(int ixval)
        : ixval_(ixval)
    {
    }

    double eval(MutableSample& mut) override {
        return *mut[static_cast<u32>(ixval_)];
    }
};

struct FunctionCallRegistry {
    typedef std::unique_ptr<ExpressionNode> NodeT;
    typedef std::function<NodeT (std::vector<NodeT>&&)> CtorT;

private:
    std::unordered_map<std::string, CtorT> registry_;
    FunctionCallRegistry() = default;

public:
    static FunctionCallRegistry& get() {
        static FunctionCallRegistry s_registry;
        return s_registry;
    }

    void add(std::string name, CtorT&& ctor) {
        registry_[std::move(name)] = ctor;
    }

    NodeT create(const std::string& fname, std::vector<NodeT>&& args) {
        NodeT res;
        auto it = registry_.find(fname);
        if (it == registry_.end()) {
            return res;
        }
        return it->second(std::move(args));
    }
};

/**
 * Function call expression node.
 * Base interface:
 * - double apply(It begin, It end);
 * - bool check_arity(size_t n, string* errormsg);
 * - static const char* func_name
 */
template<class Base>
struct FunctionCallNode : ExpressionNode, Base
{
    typedef FunctionCallNode<Base> NodeT;

    std::vector<std::unique_ptr<ExpressionNode>> children_;
    std::vector<double> args_;

    FunctionCallNode(FunctionCallNode const&) = delete;
    FunctionCallNode& operator = (FunctionCallNode const&) = delete;

    template<class ArgT>
    FunctionCallNode(ArgT&& args)
        : children_(std::forward<ArgT>(args))
        , args_(children_.size())
    {
        std::string errormsg;
        if (!static_cast<Base*>(this)->check_arity(children_.size(), &errormsg)) {
            ParseError err(std::string("function ") + Base::func_name + " error: " + errormsg);
            BOOST_THROW_EXCEPTION(err);
        }
    }

    double eval(MutableSample& mut) override {
        std::transform(children_.begin(), children_.end(), args_.begin(),
                       [&mut](std::unique_ptr<ExpressionNode>& node) {
                           return node->eval(mut);
                       });
        return static_cast<Base*>(this)->apply(args_.begin(), args_.end());
    }

    static std::unique_ptr<NodeT> create_node(std::vector<std::unique_ptr<ExpressionNode>>&& args) {
        std::unique_ptr<NodeT> result;
        result.reset(new NodeT(std::move(args)));
        return result;
    }

private:
    struct RegistryToken {
        RegistryToken() {
            FunctionCallRegistry::get().add(Base::func_name, &create_node);
        }
    };

    static RegistryToken regtoken_;
};

template<class Base>
typename FunctionCallNode<Base>::RegistryToken FunctionCallNode<Base>::regtoken_;

struct BuiltInFunctions {
    // Arithmetics
    struct Sum {
        template<class It>
        double apply(It begin, It end) {
            auto res = std::accumulate(begin, end, 0.0, [](double a, double b) {
                return a + b;
            });
            return res;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "+";
    };

    struct Mul {
        template<class It>
        double apply(It begin, It end) {
            auto res = std::accumulate(begin, end, 1.0, [](double a, double b) {
                return a * b;
            });
            return res;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "*";
    };

    // General
    struct Min {
        template<class It>
        double apply(It begin, It end) {
            auto it = std::min_element(begin, end);
            if (it != end) {
                return *it;
            }
            return NAN;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "min";
    };

    struct Max {
        template<class It>
        double apply(It begin, It end) {
            auto it = std::max_element(begin, end);
            if (it != end) {
                return *it;
            }
            return NAN;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "max";
    };

    struct Abs {
        template<class It>
        double apply(It begin, It end) {
            return std::abs(*begin);
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 1) {
                return true;
            }
            *error = "single argument expected";
            return false;
        }
        constexpr static const char* func_name = "abs";
    };
};

// Arithmetic
template struct FunctionCallNode<BuiltInFunctions::Sum>;
template struct FunctionCallNode<BuiltInFunctions::Mul>;
// General
template struct FunctionCallNode<BuiltInFunctions::Max>;
template struct FunctionCallNode<BuiltInFunctions::Min>;
template struct FunctionCallNode<BuiltInFunctions::Abs>;

typedef boost::property_tree::ptree PTree;
static const int DEPTH_LIMIT = 10;

template<class LookupFn>
std::unique_ptr<ExpressionNode> buildNode(int depth, const PTree& node, const LookupFn& lookup) {
    if (depth == DEPTH_LIMIT) {
        ParseError err("expression depth limit exceded");
        BOOST_THROW_EXCEPTION(err);
    }
    // Expect array of: [op, arg1, arg2, ...,argN]
    // i-th arg can be a node in which case recursive call
    // have to be made.
    std::string op;
    std::vector<std::unique_ptr<ExpressionNode>> args;
    for (auto it = node.begin(); it != node.end(); it++) {
        if (it == node.begin()) {
            // Parse operator
            if (!it->first.empty()) {
                // Expect value here
                ParseError err("operator or function expected");
                BOOST_THROW_EXCEPTION(err);
            }
            else {
                op = it->second.data();
            }
        }
        else {
            // Parse arguments
            auto value = it->second.data();
            if (value.empty()) {
                // Build sub-node
                auto arg = buildNode(depth + 1, it->second, lookup);
                args.push_back(std::move(arg));
            }
            else {
                std::unique_ptr<ExpressionNode> node;
                std::stringstream str(value);
                double xs;
                str >> xs;
                if (!str.fail()) {
                    node.reset(new ConstantNode(xs));
                }
                else {
                    auto ix = lookup(value);
                    if (ix < 0) {
                        // Field can't be found
                        ParseError err("unknown field '" + value + "'");
                        BOOST_THROW_EXCEPTION(err);
                    }
                    node.reset(new ValueNode(ix));
                }
                args.push_back(std::move(node));
            }
        }
    }
    std::unique_ptr<ExpressionNode> res;
    res = FunctionCallRegistry::get().create(op, std::move(args));
    if (!res) {
        ParseError err("unknown operation '" + op + "'");
        BOOST_THROW_EXCEPTION(err);
    }
    return res;
}

// ----
// Eval
// ----

static std::unordered_map<std::string, int> buildNameToIndexMapping(const QP::ReshapeRequest& req)
{
    std::unordered_map<std::string, int> result;
    const int ncol = static_cast<int>(req.select.columns.size());
    for(int ix = 0; ix < ncol; ix++) {
        if (req.select.columns[ix].ids.empty()) {
            continue;
        }
        auto idcol = req.select.columns[ix].ids.front();
        auto rawstr = req.select.matcher->id2str(idcol);
        // copy metric name from the begining until the ' ' or ':'
        std::string sname(rawstr.first, rawstr.first + rawstr.second);
        auto it = std::find_if(sname.begin(), sname.end(), [](char c) {
            return std::isspace(c) || c == ':';
        });
        result[std::string(sname.begin(), it)] = ix;
    }
    return result;
}

Eval::Eval(const boost::property_tree::ptree& ptree, const ReshapeRequest& req, std::shared_ptr<Node> next)
    : next_(next)
{
    auto const& expr = ptree.get_child_optional("expr");
    if (expr) {
        std::unordered_map<std::string, int> lazyInitMap;
        bool initialized = false;
        auto lookupFn = [&](const std::string& fld) {
            if (!initialized) {
                initialized = true;
                lazyInitMap = buildNameToIndexMapping(req);
            }
            auto it = lazyInitMap.find(fld);
            if (it == lazyInitMap.end()) {
                return -1;
            }
            return it->second;
        };
        expr_ = buildNode(0, *expr, lookupFn);
    }
}

Eval::Eval(const boost::property_tree::ptree& expr, const ReshapeRequest& req, std::shared_ptr<Node> next, bool)
    : next_(next)
{
    std::unordered_map<std::string, int> lazyInitMap;
    bool initialized = false;
    auto lookupFn = [&](const std::string& fld) {
        if (!initialized) {
            initialized = true;
            lazyInitMap = buildNameToIndexMapping(req);
        }
        auto it = lazyInitMap.find(fld);
        if (it == lazyInitMap.end()) {
            return -1;
        }
        return it->second;
    };
    expr_ = buildNode(0, expr, lookupFn);
}

void Eval::complete() {
    next_->complete();
}

bool Eval::put(MutableSample &mut) {
    double val = expr_->eval(mut);
    mut.collapse();
    if (std::isnormal(val)) {
        *mut[0] = val;
        return next_->put(mut);
    }
    return true;
}

void Eval::set_error(aku_Status status) {
    next_->set_error(status);
}

int Eval::get_requirements() const {
    return TERMINAL;
}

static QueryParserToken<Eval> scale_token("eval");

}}  // namespace
<commit_msg>Add carrying mechanism<commit_after>#include "eval.h"
#include <sstream>
#include <unordered_map>
#include <functional>

namespace Akumuli {
namespace QP {

class ParseError : public std::exception {
    const std::string message_;

public:
    ParseError(const char* msg)
        : message_(msg)
    {
    }

    ParseError(std::string msg)
        : message_(std::move(msg))
    {
    }

    const char* what() const noexcept override {
        return message_.c_str();
    }
};

class ExpressionNode {
public:
    virtual ~ExpressionNode() = default;
    virtual double eval(MutableSample& mut) = 0;
};

class ConstantNode : public ExpressionNode {
    const double cval_;
public:
    ConstantNode(double value)
        : cval_(value)
    {
    }

    double eval(MutableSample&) override {
        return cval_;
    }
};

class ValueNode : public ExpressionNode {
    int ixval_;
public:
    ValueNode(int ixval)
        : ixval_(ixval)
    {
    }

    double eval(MutableSample& mut) override {
        return *mut[static_cast<u32>(ixval_)];
    }
};

struct FunctionCallRegistry {
    typedef std::unique_ptr<ExpressionNode> NodeT;
    typedef std::function<NodeT (std::vector<NodeT>&&)> CtorT;

private:
    std::unordered_map<std::string, CtorT> registry_;
    FunctionCallRegistry() = default;

public:
    static FunctionCallRegistry& get() {
        static FunctionCallRegistry s_registry;
        return s_registry;
    }

    void add(std::string name, CtorT&& ctor) {
        registry_[std::move(name)] = ctor;
    }

    NodeT create(const std::string& fname, std::vector<NodeT>&& args) {
        NodeT res;
        auto it = registry_.find(fname);
        if (it == registry_.end()) {
            return res;
        }
        return it->second(std::move(args));
    }
};

/**
 * Function call expression node.
 * Base interface:
 * - double apply(It begin, It end);
 * - bool check_arity(size_t n, string* errormsg);
 * - static const char* func_name
 */
template<class Base>
struct FunctionCallNode : ExpressionNode, Base
{
    typedef FunctionCallNode<Base> NodeT;

    std::vector<std::unique_ptr<ExpressionNode>> children_;
    std::vector<double> args_;

    FunctionCallNode(FunctionCallNode const&) = delete;
    FunctionCallNode& operator = (FunctionCallNode const&) = delete;

    template<class ArgT>
    FunctionCallNode(ArgT&& args)
        : children_(std::forward<ArgT>(args))
        , args_(children_.size())
    {
        std::string errormsg;
        if (!static_cast<Base*>(this)->check_arity(children_.size(), &errormsg)) {
            ParseError err(std::string("function ") + Base::func_name + " error: " + errormsg);
            BOOST_THROW_EXCEPTION(err);
        }
        if (!static_cast<Base*>(this)->carry(children_, &errormsg)) {
            ParseError err(std::string("function ") + Base::func_name + " error: " + errormsg);
            BOOST_THROW_EXCEPTION(err);
        }
    }

    double eval(MutableSample& mut) override {
        std::transform(children_.begin(), children_.end(), args_.begin(),
                       [&mut](std::unique_ptr<ExpressionNode>& node) {
                           return node->eval(mut);
                       });
        return static_cast<Base*>(this)->apply(args_.begin(), args_.end());
    }

    static std::unique_ptr<NodeT> create_node(std::vector<std::unique_ptr<ExpressionNode>>&& args) {
        std::unique_ptr<NodeT> result;
        result.reset(new NodeT(std::move(args)));
        return result;
    }

private:
    struct RegistryToken {
        RegistryToken() {
            FunctionCallRegistry::get().add(Base::func_name, &create_node);
        }
    };

    static RegistryToken regtoken_;
};

template<class Base>
typename FunctionCallNode<Base>::RegistryToken FunctionCallNode<Base>::regtoken_;

struct BuiltInFunctions {
    struct BuiltInFn {
        bool carry(std::vector<std::unique_ptr<ExpressionNode>>&, std::string*) {
            // Default implementation
            return true;
        }
    };

    // Arithmetics
    struct Sum : BuiltInFn {
        template<class It>
        double apply(It begin, It end) {
            auto res = std::accumulate(begin, end, 0.0, [](double a, double b) {
                return a + b;
            });
            return res;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "+";
    };

    struct Mul : BuiltInFn {
        template<class It>
        double apply(It begin, It end) {
            auto res = std::accumulate(begin, end, 1.0, [](double a, double b) {
                return a * b;
            });
            return res;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "*";
    };

    // General
    struct Min : BuiltInFn {
        template<class It>
        double apply(It begin, It end) {
            auto it = std::min_element(begin, end);
            if (it != end) {
                return *it;
            }
            return NAN;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "min";
    };

    struct Max : BuiltInFn {
        template<class It>
        double apply(It begin, It end) {
            auto it = std::max_element(begin, end);
            if (it != end) {
                return *it;
            }
            return NAN;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 0) {
                *error = "function require at least one parameter";
                return false;
            }
            return true;
        }
        constexpr static const char* func_name = "max";
    };

    struct Abs : BuiltInFn {
        template<class It>
        double apply(It begin, It end) {
            return std::abs(*begin);
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 1) {
                return true;
            }
            *error = "single argument expected";
            return false;
        }
        constexpr static const char* func_name = "abs";
    };

    // Windowed functions
    struct SMA : BuiltInFn {
        int N;
        int pos;
        double sum;
        std::vector<double> queue_;

        SMA() : N(0), pos(0), sum(0) {}

        bool carry(std::vector<std::unique_ptr<ExpressionNode>>& children, std::string* err) {
            static aku_Sample empty;
            static MutableSample mempty(&empty);
            // First parameter is supposed to be constant
            auto& c = children.front();
            auto cnode = dynamic_cast<ConstantNode*>(c.get());
            if (cnode == nullptr) {
                *err = "first 'sma' parameter should be constant";
                return false;
            }
            double val = cnode->eval(mempty);
            N = static_cast<int>(val);
            queue_.resize(N);
            return true;
        }

        template<class It>
        double apply(It begin, It end) {
            assert(begin != end);
            queue_.at(pos % N) = *begin;
            pos++;
            if (pos > N) {
                double prev = queue_.at(static_cast<size_t>((pos - N) % N));
                sum -= prev;
                return sum / N;
            }
            return sum / pos;
        }
        bool check_arity(size_t n, std::string* error) const {
            if (n == 2) {
                return true;
            }
            *error = "two arguments expected";
            return false;
        }
        constexpr static const char* func_name = "sma";
    };
};

// Arithmetic
template struct FunctionCallNode<BuiltInFunctions::Sum>;
template struct FunctionCallNode<BuiltInFunctions::Mul>;
// General
template struct FunctionCallNode<BuiltInFunctions::Max>;
template struct FunctionCallNode<BuiltInFunctions::Min>;
template struct FunctionCallNode<BuiltInFunctions::Abs>;
// Window methods
template struct FunctionCallNode<BuiltInFunctions::SMA>;

typedef boost::property_tree::ptree PTree;
static const int DEPTH_LIMIT = 10;

template<class LookupFn>
std::unique_ptr<ExpressionNode> buildNode(int depth, const PTree& node, const LookupFn& lookup) {
    if (depth == DEPTH_LIMIT) {
        ParseError err("expression depth limit exceded");
        BOOST_THROW_EXCEPTION(err);
    }
    // Expect array of: [op, arg1, arg2, ...,argN]
    // i-th arg can be a node in which case recursive call
    // have to be made.
    std::string op;
    std::vector<std::unique_ptr<ExpressionNode>> args;
    for (auto it = node.begin(); it != node.end(); it++) {
        if (it == node.begin()) {
            // Parse operator
            if (!it->first.empty()) {
                // Expect value here
                ParseError err("operator or function expected");
                BOOST_THROW_EXCEPTION(err);
            }
            else {
                op = it->second.data();
            }
        }
        else {
            // Parse arguments
            auto value = it->second.data();
            if (value.empty()) {
                // Build sub-node
                auto arg = buildNode(depth + 1, it->second, lookup);
                args.push_back(std::move(arg));
            }
            else {
                std::unique_ptr<ExpressionNode> node;
                std::stringstream str(value);
                double xs;
                str >> xs;
                if (!str.fail()) {
                    node.reset(new ConstantNode(xs));
                }
                else {
                    auto ix = lookup(value);
                    if (ix < 0) {
                        // Field can't be found
                        ParseError err("unknown field '" + value + "'");
                        BOOST_THROW_EXCEPTION(err);
                    }
                    node.reset(new ValueNode(ix));
                }
                args.push_back(std::move(node));
            }
        }
    }
    std::unique_ptr<ExpressionNode> res;
    res = FunctionCallRegistry::get().create(op, std::move(args));
    if (!res) {
        ParseError err("unknown operation '" + op + "'");
        BOOST_THROW_EXCEPTION(err);
    }
    return res;
}

// ----
// Eval
// ----

static std::unordered_map<std::string, int> buildNameToIndexMapping(const QP::ReshapeRequest& req)
{
    std::unordered_map<std::string, int> result;
    const int ncol = static_cast<int>(req.select.columns.size());
    for(int ix = 0; ix < ncol; ix++) {
        if (req.select.columns[ix].ids.empty()) {
            continue;
        }
        auto idcol = req.select.columns[ix].ids.front();
        auto rawstr = req.select.matcher->id2str(idcol);
        // copy metric name from the begining until the ' ' or ':'
        std::string sname(rawstr.first, rawstr.first + rawstr.second);
        auto it = std::find_if(sname.begin(), sname.end(), [](char c) {
            return std::isspace(c) || c == ':';
        });
        result[std::string(sname.begin(), it)] = ix;
    }
    return result;
}

Eval::Eval(const boost::property_tree::ptree& ptree, const ReshapeRequest& req, std::shared_ptr<Node> next)
    : next_(next)
{
    auto const& expr = ptree.get_child_optional("expr");
    if (expr) {
        std::unordered_map<std::string, int> lazyInitMap;
        bool initialized = false;
        auto lookupFn = [&](const std::string& fld) {
            if (!initialized) {
                initialized = true;
                lazyInitMap = buildNameToIndexMapping(req);
            }
            auto it = lazyInitMap.find(fld);
            if (it == lazyInitMap.end()) {
                return -1;
            }
            return it->second;
        };
        expr_ = buildNode(0, *expr, lookupFn);
    }
}

Eval::Eval(const boost::property_tree::ptree& expr, const ReshapeRequest& req, std::shared_ptr<Node> next, bool)
    : next_(next)
{
    std::unordered_map<std::string, int> lazyInitMap;
    bool initialized = false;
    auto lookupFn = [&](const std::string& fld) {
        if (!initialized) {
            initialized = true;
            lazyInitMap = buildNameToIndexMapping(req);
        }
        auto it = lazyInitMap.find(fld);
        if (it == lazyInitMap.end()) {
            return -1;
        }
        return it->second;
    };
    expr_ = buildNode(0, expr, lookupFn);
}

void Eval::complete() {
    next_->complete();
}

bool Eval::put(MutableSample &mut) {
    double val = expr_->eval(mut);
    mut.collapse();
    if (std::isnormal(val)) {
        *mut[0] = val;
        return next_->put(mut);
    }
    return true;
}

void Eval::set_error(aku_Status status) {
    next_->set_error(status);
}

int Eval::get_requirements() const {
    return TERMINAL;
}

static QueryParserToken<Eval> scale_token("eval");

}}  // namespace
<|endoftext|>


<commit_before>// Copyright Joshua Boyce 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// This file is part of HadesMem.
// <http://www.raptorfactor.com/> <raptorfactor@raptorfactor.com>

#include "hadesmem/module_list.hpp"

#include "hadesmem/detail/warning_disable_prefix.hpp"
#include <boost/assert.hpp>
#include "hadesmem/detail/warning_disable_suffix.hpp"

#include "hadesmem/error.hpp"
#include "hadesmem/module.hpp"
#include "hadesmem/process.hpp"
#include "hadesmem/detail/module_iterator_impl.hpp"
#include <windows.h>
#include <tlhelp32.h>

#include "hadesmem/error.hpp"
#include "hadesmem/module.hpp"
#include "hadesmem/process.hpp"
#include "hadesmem/detail/module_iterator_impl.hpp"

namespace hadesmem
{

ModuleIterator::ModuleIterator() BOOST_NOEXCEPT
  : impl_()
{ }

ModuleIterator::ModuleIterator(Process const* process)
  : impl_(new detail::ModuleIteratorImpl)
{
  BOOST_ASSERT(impl_.get());
  BOOST_ASSERT(process != nullptr);

  impl_->process_ = process;

  impl_->snap_ = ::CreateToolhelp32Snapshot(
    TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
    impl_->process_->GetId());
  if (impl_->snap_ == INVALID_HANDLE_VALUE)
  {
    if (GetLastError() == ERROR_BAD_LENGTH)
    {
      impl_->snap_ = ::CreateToolhelp32Snapshot(
        TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
        impl_->process_->GetId());
      if (impl_->snap_ == INVALID_HANDLE_VALUE)
      {
        DWORD const last_error = ::GetLastError();
        BOOST_THROW_EXCEPTION(HadesMemError() <<
          ErrorString("CreateToolhelp32Snapshot failed.") <<
          ErrorCodeWinLast(last_error));
      }
    }
    else
    {
      DWORD const last_error = ::GetLastError();
      BOOST_THROW_EXCEPTION(HadesMemError() <<
        ErrorString("CreateToolhelp32Snapshot failed.") <<
        ErrorCodeWinLast(last_error));
    }
  }

  MODULEENTRY32 entry;
  ::ZeroMemory(&entry, sizeof(entry));
  entry.dwSize = sizeof(entry);
  if (!::Module32First(impl_->snap_, &entry))
  {
    DWORD const last_error = ::GetLastError();
    if (last_error == ERROR_NO_MORE_FILES)
    {
      impl_.reset();
      return;
    }

    BOOST_THROW_EXCEPTION(HadesMemError() <<
      ErrorString("Module32First failed.") <<
      ErrorCodeWinLast(last_error));
  }

  impl_->module_ = Module(impl_->process_, entry);
}

ModuleIterator::ModuleIterator(ModuleIterator const& other) BOOST_NOEXCEPT
  : impl_(other.impl_)
{ }

ModuleIterator& ModuleIterator::operator=(ModuleIterator const& other)
  BOOST_NOEXCEPT
{
  impl_ = other.impl_;

  return *this;
}

ModuleIterator::ModuleIterator(ModuleIterator&& other) BOOST_NOEXCEPT
  : impl_(std::move(other.impl_))
{ }

ModuleIterator& ModuleIterator::operator=(ModuleIterator&& other)
  BOOST_NOEXCEPT
{
  impl_ = std::move(other.impl_);

  return *this;
}

ModuleIterator::~ModuleIterator()
{ }

ModuleIterator::reference ModuleIterator::operator*() const BOOST_NOEXCEPT
{
  BOOST_ASSERT(impl_.get());
  return *impl_->module_;
}

ModuleIterator::pointer ModuleIterator::operator->() const BOOST_NOEXCEPT
{
  BOOST_ASSERT(impl_.get());
  return &*impl_->module_;
}

ModuleIterator& ModuleIterator::operator++()
{
  BOOST_ASSERT(impl_.get());

  MODULEENTRY32 entry;
  ::ZeroMemory(&entry, sizeof(entry));
  entry.dwSize = sizeof(entry);
  if (!::Module32Next(impl_->snap_, &entry))
  {
    DWORD const last_error = ::GetLastError();
    if (last_error == ERROR_NO_MORE_FILES)
    {
      impl_.reset();
      return *this;
    }

    BOOST_THROW_EXCEPTION(HadesMemError() <<
      ErrorString("Module32Next failed.") <<
      ErrorCodeWinLast(last_error));
  }

  impl_->module_ = Module(impl_->process_, entry);

  return *this;
}

ModuleIterator ModuleIterator::operator++(int)
{
  ModuleIterator iter(*this);
  ++*this;
  return iter;
}

bool ModuleIterator::operator==(ModuleIterator const& other) const
  BOOST_NOEXCEPT
{
  return impl_ == other.impl_;
}

bool ModuleIterator::operator!=(ModuleIterator const& other) const
  BOOST_NOEXCEPT
{
  return !(*this == other);
}

ModuleList::ModuleList(Process const* process) BOOST_NOEXCEPT
  : process_(process)
{
  BOOST_ASSERT(process != nullptr);
}

ModuleList::ModuleList(ModuleList const& other) BOOST_NOEXCEPT
  : process_(other.process_)
{ }

ModuleList& ModuleList::operator=(ModuleList const& other) BOOST_NOEXCEPT
{
  process_ = other.process_;

  return *this;
}

ModuleList::ModuleList(ModuleList&& other) BOOST_NOEXCEPT
  : process_(other.process_)
{
  other.process_ = nullptr;
}

ModuleList& ModuleList::operator=(ModuleList&& other) BOOST_NOEXCEPT
{
  process_ = other.process_;

  other.process_ = nullptr;

  return *this;
}

ModuleList::~ModuleList()
{ }

ModuleList::iterator ModuleList::begin()
{
  return ModuleList::iterator(process_);
}

ModuleList::const_iterator ModuleList::begin() const
{
  return ModuleList::iterator(process_);
}

ModuleList::iterator ModuleList::end() BOOST_NOEXCEPT
{
  return ModuleList::iterator();
}

ModuleList::const_iterator ModuleList::end() const BOOST_NOEXCEPT
{
  return ModuleList::iterator();
}

}
<commit_msg>* Remove accidental include.<commit_after>// Copyright Joshua Boyce 2010-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// This file is part of HadesMem.
// <http://www.raptorfactor.com/> <raptorfactor@raptorfactor.com>

#include "hadesmem/module_list.hpp"

#include "hadesmem/detail/warning_disable_prefix.hpp"
#include <boost/assert.hpp>
#include "hadesmem/detail/warning_disable_suffix.hpp"

#include <windows.h>
#include <tlhelp32.h>

#include "hadesmem/error.hpp"
#include "hadesmem/module.hpp"
#include "hadesmem/process.hpp"
#include "hadesmem/detail/module_iterator_impl.hpp"

namespace hadesmem
{

ModuleIterator::ModuleIterator() BOOST_NOEXCEPT
  : impl_()
{ }

ModuleIterator::ModuleIterator(Process const* process)
  : impl_(new detail::ModuleIteratorImpl)
{
  BOOST_ASSERT(impl_.get());
  BOOST_ASSERT(process != nullptr);

  impl_->process_ = process;

  impl_->snap_ = ::CreateToolhelp32Snapshot(
    TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
    impl_->process_->GetId());
  if (impl_->snap_ == INVALID_HANDLE_VALUE)
  {
    if (GetLastError() == ERROR_BAD_LENGTH)
    {
      impl_->snap_ = ::CreateToolhelp32Snapshot(
        TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32,
        impl_->process_->GetId());
      if (impl_->snap_ == INVALID_HANDLE_VALUE)
      {
        DWORD const last_error = ::GetLastError();
        BOOST_THROW_EXCEPTION(HadesMemError() <<
          ErrorString("CreateToolhelp32Snapshot failed.") <<
          ErrorCodeWinLast(last_error));
      }
    }
    else
    {
      DWORD const last_error = ::GetLastError();
      BOOST_THROW_EXCEPTION(HadesMemError() <<
        ErrorString("CreateToolhelp32Snapshot failed.") <<
        ErrorCodeWinLast(last_error));
    }
  }

  MODULEENTRY32 entry;
  ::ZeroMemory(&entry, sizeof(entry));
  entry.dwSize = sizeof(entry);
  if (!::Module32First(impl_->snap_, &entry))
  {
    DWORD const last_error = ::GetLastError();
    if (last_error == ERROR_NO_MORE_FILES)
    {
      impl_.reset();
      return;
    }

    BOOST_THROW_EXCEPTION(HadesMemError() <<
      ErrorString("Module32First failed.") <<
      ErrorCodeWinLast(last_error));
  }

  impl_->module_ = Module(impl_->process_, entry);
}

ModuleIterator::ModuleIterator(ModuleIterator const& other) BOOST_NOEXCEPT
  : impl_(other.impl_)
{ }

ModuleIterator& ModuleIterator::operator=(ModuleIterator const& other)
  BOOST_NOEXCEPT
{
  impl_ = other.impl_;

  return *this;
}

ModuleIterator::ModuleIterator(ModuleIterator&& other) BOOST_NOEXCEPT
  : impl_(std::move(other.impl_))
{ }

ModuleIterator& ModuleIterator::operator=(ModuleIterator&& other)
  BOOST_NOEXCEPT
{
  impl_ = std::move(other.impl_);

  return *this;
}

ModuleIterator::~ModuleIterator()
{ }

ModuleIterator::reference ModuleIterator::operator*() const BOOST_NOEXCEPT
{
  BOOST_ASSERT(impl_.get());
  return *impl_->module_;
}

ModuleIterator::pointer ModuleIterator::operator->() const BOOST_NOEXCEPT
{
  BOOST_ASSERT(impl_.get());
  return &*impl_->module_;
}

ModuleIterator& ModuleIterator::operator++()
{
  BOOST_ASSERT(impl_.get());

  MODULEENTRY32 entry;
  ::ZeroMemory(&entry, sizeof(entry));
  entry.dwSize = sizeof(entry);
  if (!::Module32Next(impl_->snap_, &entry))
  {
    DWORD const last_error = ::GetLastError();
    if (last_error == ERROR_NO_MORE_FILES)
    {
      impl_.reset();
      return *this;
    }

    BOOST_THROW_EXCEPTION(HadesMemError() <<
      ErrorString("Module32Next failed.") <<
      ErrorCodeWinLast(last_error));
  }

  impl_->module_ = Module(impl_->process_, entry);

  return *this;
}

ModuleIterator ModuleIterator::operator++(int)
{
  ModuleIterator iter(*this);
  ++*this;
  return iter;
}

bool ModuleIterator::operator==(ModuleIterator const& other) const
  BOOST_NOEXCEPT
{
  return impl_ == other.impl_;
}

bool ModuleIterator::operator!=(ModuleIterator const& other) const
  BOOST_NOEXCEPT
{
  return !(*this == other);
}

ModuleList::ModuleList(Process const* process) BOOST_NOEXCEPT
  : process_(process)
{
  BOOST_ASSERT(process != nullptr);
}

ModuleList::ModuleList(ModuleList const& other) BOOST_NOEXCEPT
  : process_(other.process_)
{ }

ModuleList& ModuleList::operator=(ModuleList const& other) BOOST_NOEXCEPT
{
  process_ = other.process_;

  return *this;
}

ModuleList::ModuleList(ModuleList&& other) BOOST_NOEXCEPT
  : process_(other.process_)
{
  other.process_ = nullptr;
}

ModuleList& ModuleList::operator=(ModuleList&& other) BOOST_NOEXCEPT
{
  process_ = other.process_;

  other.process_ = nullptr;

  return *this;
}

ModuleList::~ModuleList()
{ }

ModuleList::iterator ModuleList::begin()
{
  return ModuleList::iterator(process_);
}

ModuleList::const_iterator ModuleList::begin() const
{
  return ModuleList::iterator(process_);
}

ModuleList::iterator ModuleList::end() BOOST_NOEXCEPT
{
  return ModuleList::iterator();
}

ModuleList::const_iterator ModuleList::end() const BOOST_NOEXCEPT
{
  return ModuleList::iterator();
}

}
<|endoftext|>


<commit_before>/*  Sirikata Network Utilities
 *  TCPStream.cpp
 *
 *  Copyright (c) 2009, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "util/Platform.hpp"
#include "util/AtomicTypes.hpp"

#include "network/Asio.hpp"
#include "TCPStream.hpp"
#include "util/ThreadSafeQueue.hpp"
#include "ASIOSocketWrapper.hpp"
#include "MultiplexedSocket.hpp"
#include "TCPSetCallbacks.hpp"
#include "network/IOServiceFactory.hpp"
#include "network/IOService.hpp"
#include "options/Options.hpp"
#include <boost/thread.hpp>
namespace Sirikata { namespace Network {

using namespace boost::asio::ip;
TCPStream::TCPStream(const MultiplexedSocketPtr&shared_socket,const Stream::StreamID&sid):mSocket(shared_socket),mID(sid),mSendStatus(new AtomicValue<int>(0)) {
    mNumSimultaneousSockets=shared_socket->numSockets();
    assert(mNumSimultaneousSockets);
    mSendBufferSize=shared_socket->getASIOSocketWrapper(0).getResourceMonitor().maxSize();
}

void TCPStream::readyRead() {
    MultiplexedSocketWPtr mpsocket(mSocket);
    mSocket->getASIOService().post(
                               std::tr1::bind(&MultiplexedSocket::ioReactorThreadResumeRead,
                                              mpsocket,
                                              mID));
}

void TCPStream::pauseSend() {
    MultiplexedSocketWPtr mpsocket(mSocket);
    mSocket->getASIOService().post(
                               std::tr1::bind(&MultiplexedSocket::ioReactorThreadPauseSend,
                                              mpsocket,
                                              mID));
}
bool TCPStream::canSend(const size_t dataSize)const {
    uint8 serializedStreamId[StreamID::MAX_SERIALIZED_LENGTH];
    unsigned int streamIdLength=StreamID::MAX_SERIALIZED_LENGTH;
    unsigned int successLengthNeeded=mID.serialize(serializedStreamId,streamIdLength);
    size_t totalSize=dataSize+successLengthNeeded;
    vuint32 packetLength=vuint32(totalSize);
    uint8 packetLengthSerialized[vuint32::MAX_SERIALIZED_LENGTH];
    unsigned int packetHeaderLength=packetLength.serialize(packetLengthSerialized,vuint32::MAX_SERIALIZED_LENGTH);
    totalSize+=packetHeaderLength;
    return mSocket->canSendBytes(mID,totalSize);
}
bool TCPStream::send(const Chunk&data, StreamReliability reliability) {
    return send(MemoryReference(data),reliability);
}
bool TCPStream::send(MemoryReference firstChunk, StreamReliability reliability) {
    return send(firstChunk,MemoryReference::null(),reliability);
}
bool TCPStream::send(MemoryReference firstChunk, MemoryReference secondChunk, StreamReliability reliability) {
    MultiplexedSocket::RawRequest toBeSent;
    // only allow 3 of the four possibilities because unreliable ordered is tricky and usually useless
    switch(reliability) {
      case Unreliable:
        toBeSent.unordered=true;
        toBeSent.unreliable=true;
        break;
      case ReliableOrdered:
        toBeSent.unordered=false;
        toBeSent.unreliable=false;
        break;
      case ReliableUnordered:
        toBeSent.unordered=true;
        toBeSent.unreliable=false;
        break;
    }
    toBeSent.originStream=getID();
    uint8 serializedStreamId[StreamID::MAX_SERIALIZED_LENGTH];
    unsigned int streamIdLength=StreamID::MAX_SERIALIZED_LENGTH;
    unsigned int successLengthNeeded=toBeSent.originStream.serialize(serializedStreamId,streamIdLength);
    ///this function should never return something larger than the  MAX_SERIALIZED_LEGNTH
    assert(successLengthNeeded<=streamIdLength);
    streamIdLength=successLengthNeeded;
    size_t totalSize=firstChunk.size()+secondChunk.size();
    totalSize+=streamIdLength;
    vuint32 packetLength=vuint32(totalSize);
    uint8 packetLengthSerialized[vuint32::MAX_SERIALIZED_LENGTH];
    unsigned int packetHeaderLength=packetLength.serialize(packetLengthSerialized,vuint32::MAX_SERIALIZED_LENGTH);
    //allocate a packet long enough to take both the length of the packet and the stream id as well as the packet data. totalSize = size of streamID + size of data and
    //packetHeaderLength = the length of the length component of the packet
    toBeSent.data=new Chunk(totalSize+packetHeaderLength);

    uint8 *outputBuffer=&(*toBeSent.data)[0];
    std::memcpy(outputBuffer,packetLengthSerialized,packetHeaderLength);
    std::memcpy(outputBuffer+packetHeaderLength,serializedStreamId,streamIdLength);
    if (firstChunk.size()) {
        std::memcpy(&outputBuffer[packetHeaderLength+streamIdLength],
                    firstChunk.data(),
                    firstChunk.size());
    }
    if (secondChunk.size()) {
        std::memcpy(&outputBuffer[packetHeaderLength+streamIdLength+firstChunk.size()],
                    secondChunk.data(),
                    secondChunk.size());
    }
    bool didsend=false;
    //indicate to other would-be TCPStream::close()ers that we are sending and they will have to wait until we give up control to actually ack the close and shut down the stream
    unsigned int sendStatus=++(*mSendStatus);
    if ((sendStatus&(3*SendStatusClosing))==0) {///max of 3 entities can close the stream at once (FIXME: should implement |= on atomic ints), but as of now at most the recv thread the sender responsible and a user close() is all that is allowed at once...so 3 is fine)
        didsend=MultiplexedSocket::sendBytes(mSocket,toBeSent,mSendBufferSize);
    }
    //relinquish control to a potential closer
    --(*mSendStatus);
    if (!didsend) {
        //if the data was not sent, its our job to clean it up
        delete toBeSent.data;
        if ((mSendStatus->read()&(3*SendStatusClosing))!=0) {///max of 3 entities can close the stream at once (FIXME: should implement |= on atomic ints), but as of now at most the recv thread the sender responsible and a user close() is all that is allowed at once...so 3 is fine)
            SILOG(tcpsst,debug,"printing to closed stream id "<<getID().read());
        }
    }
    return didsend;
}
///This function waits on the sendStatus clearing up so no outstanding sends are being made (and no further ones WILL be made cus of the SendStatusClosing flag that is on
bool TCPStream::closeSendStatus(AtomicValue<int>&vSendStatus) {
    int sendStatus=vSendStatus.read();
    bool incd=false;
    if ((sendStatus&(SendStatusClosing*3))==0) {
        ///FIXME we want to |= here
        incd=((vSendStatus+=SendStatusClosing)==SendStatusClosing);
    }
    //Wait until it's a pure sendStatus value without any 'remainders' caused by outstanding sends
    while ((sendStatus=vSendStatus.read())!=SendStatusClosing&&
           sendStatus!=2*SendStatusClosing&&
           sendStatus!=3*SendStatusClosing) {
    }
    return incd;
}
void TCPStream::close() {
    //set the stream closed as soon as sends are done
    bool justClosed=closeSendStatus(*mSendStatus);
    if (justClosed) {
        //obliterate all incoming callback to this stream
        mSocket->addCallbacks(getID(),NULL);
        //send out that the stream is now closed on all sockets
        MultiplexedSocket::closeStream(mSocket,getID());
    }
}
TCPStream::~TCPStream() {
    close();
}
TCPStream::TCPStream(IOService&io,OptionSet*options):mSendStatus(new AtomicValue<int>(0)) {
    mIO=&io;
    OptionValue *numSimultSockets=options->referenceOption("parallel-sockets");
    OptionValue *sendBufferSize=options->referenceOption("send-buffer-size");
    assert(numSimultSockets&&sendBufferSize);
    mNumSimultaneousSockets=(unsigned char)numSimultSockets->as<unsigned int>();
    assert(mNumSimultaneousSockets);
    mSendBufferSize=sendBufferSize->as<unsigned int>();
}

TCPStream::TCPStream(IOService&io,unsigned char numSimultSockets,unsigned int sendBufferSize):mSendStatus(new AtomicValue<int>(0)) {
    mIO=&io;
    mNumSimultaneousSockets=(unsigned char)numSimultSockets;
    assert(mNumSimultaneousSockets);
    mSendBufferSize=sendBufferSize;
}

void TCPStream::connect(const Address&addy,
                        const SubstreamCallback &substreamCallback,
                        const ConnectionCallback &connectionCallback,
                        const BytesReceivedCallback&bytesReceivedCallback,
                        const ReadySendCallback&readySendCallback) {
    mSocket=MultiplexedSocket::construct<MultiplexedSocket>(mIO,substreamCallback);
    *mSendStatus=0;
    mID=StreamID(1);
    mSocket->addCallbacks(getID(),new Callbacks(connectionCallback,
                                                bytesReceivedCallback,
                                                readySendCallback,
                                                mSendStatus));
    mSocket->connect(addy,mNumSimultaneousSockets,mSendBufferSize);
}

void TCPStream::prepareOutboundConnection(
                        const SubstreamCallback &substreamCallback,
                        const ConnectionCallback &connectionCallback,
                        const BytesReceivedCallback&bytesReceivedCallback,
                        const ReadySendCallback&readySendCallback) {
    mSocket=MultiplexedSocket::construct<MultiplexedSocket>(mIO,substreamCallback);
    *mSendStatus=0;
    mID=StreamID(1);
    mSocket->addCallbacks(getID(),new Callbacks(connectionCallback,
                                                bytesReceivedCallback,
                                                readySendCallback,
                                                mSendStatus));
    mSocket->prepareConnect(mNumSimultaneousSockets,mSendBufferSize);
}
void TCPStream::connect(const Address&addy) {
    assert(mSocket);
    mSocket->connect(addy,0,mSendBufferSize);
}

Stream*TCPStream::factory(){
    return new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
}
Stream* TCPStream::clone(const SubstreamCallback &cloneCallback) {
    if (!mSocket) {
        return NULL;
    }
    TCPStream *retval=new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
    retval->mSocket=mSocket;

    StreamID newID=mSocket->getNewID();
    retval->mID=newID;
    TCPSetCallbacks setCallbackFunctor(&*mSocket,retval);
    cloneCallback(retval,setCallbackFunctor);
    return retval;
}

Stream* TCPStream::clone(const ConnectionCallback &connectionCallback,
                         const BytesReceivedCallback&chunkReceivedCallback,
                         const ReadySendCallback&readySendCallback) {
    if (!mSocket) {
        return NULL;
    }
    TCPStream *retval=new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
    retval->mSocket=mSocket;

    StreamID newID=mSocket->getNewID();
    retval->mID=newID;
    TCPSetCallbacks setCallbackFunctor(&*mSocket,retval);
    setCallbackFunctor(connectionCallback,chunkReceivedCallback, readySendCallback);
    return retval;
}

///Only returns a legitimate address if ConnectionStatus called back, otherwise return Address::null()
Address TCPStream::getRemoteEndpoint() const{
    return mSocket->getRemoteEndpoint(mID);
}
///Only returns a legitimate address if ConnectionStatus called back, otherwise return Address::null()
Address TCPStream::getLocalEndpoint() const{
    return mSocket->getLocalEndpoint(mID);
}

}  }
<commit_msg>Fix thread safety of TCPStream by making a temporary copy of its MultiplexedSocketPtr and checking the copy before using it.<commit_after>/*  Sirikata Network Utilities
 *  TCPStream.cpp
 *
 *  Copyright (c) 2009, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "util/Platform.hpp"
#include "util/AtomicTypes.hpp"

#include "network/Asio.hpp"
#include "TCPStream.hpp"
#include "util/ThreadSafeQueue.hpp"
#include "ASIOSocketWrapper.hpp"
#include "MultiplexedSocket.hpp"
#include "TCPSetCallbacks.hpp"
#include "network/IOServiceFactory.hpp"
#include "network/IOService.hpp"
#include "options/Options.hpp"
#include <boost/thread.hpp>
namespace Sirikata { namespace Network {

using namespace boost::asio::ip;
TCPStream::TCPStream(const MultiplexedSocketPtr&shared_socket,const Stream::StreamID&sid):mSocket(shared_socket),mID(sid),mSendStatus(new AtomicValue<int>(0)) {
    mNumSimultaneousSockets=shared_socket->numSockets();
    assert(mNumSimultaneousSockets);
    mSendBufferSize=shared_socket->getASIOSocketWrapper(0).getResourceMonitor().maxSize();
}

void TCPStream::readyRead() {
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        SILOG(tcpsst,debug,"Called TCPStream::readyRead() on closed stream." << getID().read());
        return;
    }

    MultiplexedSocketWPtr mpsocket(socket_copy);
    socket_copy->getASIOService().post(
                               std::tr1::bind(&MultiplexedSocket::ioReactorThreadResumeRead,
                                              mpsocket,
                                              mID));
}

void TCPStream::pauseSend() {
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        SILOG(tcpsst,debug,"Called TCPStream::pauseSend() on closed stream." << getID().read());
        return;
    }

    MultiplexedSocketWPtr mpsocket(socket_copy);
    socket_copy->getASIOService().post(
                               std::tr1::bind(&MultiplexedSocket::ioReactorThreadPauseSend,
                                              mpsocket,
                                              mID));
}
bool TCPStream::canSend(const size_t dataSize)const {
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        SILOG(tcpsst,debug,"Called TCPStream::canSend() on closed stream." << getID().read());
        return false;
    }

    uint8 serializedStreamId[StreamID::MAX_SERIALIZED_LENGTH];
    unsigned int streamIdLength=StreamID::MAX_SERIALIZED_LENGTH;
    unsigned int successLengthNeeded=mID.serialize(serializedStreamId,streamIdLength);
    size_t totalSize=dataSize+successLengthNeeded;
    vuint32 packetLength=vuint32(totalSize);
    uint8 packetLengthSerialized[vuint32::MAX_SERIALIZED_LENGTH];
    unsigned int packetHeaderLength=packetLength.serialize(packetLengthSerialized,vuint32::MAX_SERIALIZED_LENGTH);
    totalSize+=packetHeaderLength;
    return socket_copy->canSendBytes(mID,totalSize);
}
bool TCPStream::send(const Chunk&data, StreamReliability reliability) {
    return send(MemoryReference(data),reliability);
}
bool TCPStream::send(MemoryReference firstChunk, StreamReliability reliability) {
    return send(firstChunk,MemoryReference::null(),reliability);
}
bool TCPStream::send(MemoryReference firstChunk, MemoryReference secondChunk, StreamReliability reliability) {
    MultiplexedSocket::RawRequest toBeSent;
    // only allow 3 of the four possibilities because unreliable ordered is tricky and usually useless
    switch(reliability) {
      case Unreliable:
        toBeSent.unordered=true;
        toBeSent.unreliable=true;
        break;
      case ReliableOrdered:
        toBeSent.unordered=false;
        toBeSent.unreliable=false;
        break;
      case ReliableUnordered:
        toBeSent.unordered=true;
        toBeSent.unreliable=false;
        break;
    }
    toBeSent.originStream=getID();
    uint8 serializedStreamId[StreamID::MAX_SERIALIZED_LENGTH];
    unsigned int streamIdLength=StreamID::MAX_SERIALIZED_LENGTH;
    unsigned int successLengthNeeded=toBeSent.originStream.serialize(serializedStreamId,streamIdLength);
    ///this function should never return something larger than the  MAX_SERIALIZED_LEGNTH
    assert(successLengthNeeded<=streamIdLength);
    streamIdLength=successLengthNeeded;
    size_t totalSize=firstChunk.size()+secondChunk.size();
    totalSize+=streamIdLength;
    vuint32 packetLength=vuint32(totalSize);
    uint8 packetLengthSerialized[vuint32::MAX_SERIALIZED_LENGTH];
    unsigned int packetHeaderLength=packetLength.serialize(packetLengthSerialized,vuint32::MAX_SERIALIZED_LENGTH);
    //allocate a packet long enough to take both the length of the packet and the stream id as well as the packet data. totalSize = size of streamID + size of data and
    //packetHeaderLength = the length of the length component of the packet
    toBeSent.data=new Chunk(totalSize+packetHeaderLength);

    uint8 *outputBuffer=&(*toBeSent.data)[0];
    std::memcpy(outputBuffer,packetLengthSerialized,packetHeaderLength);
    std::memcpy(outputBuffer+packetHeaderLength,serializedStreamId,streamIdLength);
    if (firstChunk.size()) {
        std::memcpy(&outputBuffer[packetHeaderLength+streamIdLength],
                    firstChunk.data(),
                    firstChunk.size());
    }
    if (secondChunk.size()) {
        std::memcpy(&outputBuffer[packetHeaderLength+streamIdLength+firstChunk.size()],
                    secondChunk.data(),
                    secondChunk.size());
    }
    bool didsend=false;
    //indicate to other would-be TCPStream::close()ers that we are sending and they will have to wait until we give up control to actually ack the close and shut down the stream
    unsigned int sendStatus=++(*mSendStatus);
    if ((sendStatus&(3*SendStatusClosing))==0) {///max of 3 entities can close the stream at once (FIXME: should implement |= on atomic ints), but as of now at most the recv thread the sender responsible and a user close() is all that is allowed at once...so 3 is fine)
        MultiplexedSocketPtr socket_copy = mSocket;
        if (socket_copy.get() == NULL)
            didsend = false;
        else
            didsend=MultiplexedSocket::sendBytes(socket_copy,toBeSent,mSendBufferSize);
    }
    //relinquish control to a potential closer
    --(*mSendStatus);
    if (!didsend) {
        //if the data was not sent, its our job to clean it up
        delete toBeSent.data;
        if ((mSendStatus->read()&(3*SendStatusClosing))!=0) {///max of 3 entities can close the stream at once (FIXME: should implement |= on atomic ints), but as of now at most the recv thread the sender responsible and a user close() is all that is allowed at once...so 3 is fine)
            SILOG(tcpsst,debug,"printing to closed stream id "<<getID().read());
        }
    }
    return didsend;
}
///This function waits on the sendStatus clearing up so no outstanding sends are being made (and no further ones WILL be made cus of the SendStatusClosing flag that is on
bool TCPStream::closeSendStatus(AtomicValue<int>&vSendStatus) {
    int sendStatus=vSendStatus.read();
    bool incd=false;
    if ((sendStatus&(SendStatusClosing*3))==0) {
        ///FIXME we want to |= here
        incd=((vSendStatus+=SendStatusClosing)==SendStatusClosing);
    }
    //Wait until it's a pure sendStatus value without any 'remainders' caused by outstanding sends
    while ((sendStatus=vSendStatus.read())!=SendStatusClosing&&
           sendStatus!=2*SendStatusClosing&&
           sendStatus!=3*SendStatusClosing) {
    }
    return incd;
}
void TCPStream::close() {
    // For thread safety, make a copy so we are ensured it is valid throughout this method
    MultiplexedSocketPtr socket_copy(mSocket);

    // If its already been closed, there's nothing to do
    if (socket_copy.get() == NULL)
        return;

    //Otherwise, set the stream closed as soon as sends are done
    bool justClosed=closeSendStatus(*mSendStatus);
    if (justClosed) {
        //obliterate all incoming callback to this stream
        socket_copy->addCallbacks(getID(),NULL);
        //send out that the stream is now closed on all sockets
        MultiplexedSocket::closeStream(socket_copy,getID());
    }

    // And get rid of the reference to the socket so it can (maybe) be cleaned up
    mSocket.reset();
}
TCPStream::~TCPStream() {
    close();
}
TCPStream::TCPStream(IOService&io,OptionSet*options):mSendStatus(new AtomicValue<int>(0)) {
    mIO=&io;
    OptionValue *numSimultSockets=options->referenceOption("parallel-sockets");
    OptionValue *sendBufferSize=options->referenceOption("send-buffer-size");
    assert(numSimultSockets&&sendBufferSize);
    mNumSimultaneousSockets=(unsigned char)numSimultSockets->as<unsigned int>();
    assert(mNumSimultaneousSockets);
    mSendBufferSize=sendBufferSize->as<unsigned int>();
}

TCPStream::TCPStream(IOService&io,unsigned char numSimultSockets,unsigned int sendBufferSize):mSendStatus(new AtomicValue<int>(0)) {
    mIO=&io;
    mNumSimultaneousSockets=(unsigned char)numSimultSockets;
    assert(mNumSimultaneousSockets);
    mSendBufferSize=sendBufferSize;
}

void TCPStream::connect(const Address&addy,
                        const SubstreamCallback &substreamCallback,
                        const ConnectionCallback &connectionCallback,
                        const BytesReceivedCallback&bytesReceivedCallback,
                        const ReadySendCallback&readySendCallback) {
    MultiplexedSocketPtr socket = MultiplexedSocket::construct<MultiplexedSocket>(mIO,substreamCallback);
    mSocket = socket;
    *mSendStatus=0;
    mID=StreamID(1);
    socket->addCallbacks(getID(),new Callbacks(connectionCallback,
                                                bytesReceivedCallback,
                                                readySendCallback,
                                                mSendStatus));
    socket->connect(addy,mNumSimultaneousSockets,mSendBufferSize);
}

void TCPStream::prepareOutboundConnection(
                        const SubstreamCallback &substreamCallback,
                        const ConnectionCallback &connectionCallback,
                        const BytesReceivedCallback&bytesReceivedCallback,
                        const ReadySendCallback&readySendCallback) {
    MultiplexedSocketPtr socket = MultiplexedSocket::construct<MultiplexedSocket>(mIO,substreamCallback);
    mSocket = socket;
    *mSendStatus=0;
    mID=StreamID(1);
    socket->addCallbacks(getID(),new Callbacks(connectionCallback,
                                                bytesReceivedCallback,
                                                readySendCallback,
                                                mSendStatus));
    socket->prepareConnect(mNumSimultaneousSockets,mSendBufferSize);
}
void TCPStream::connect(const Address&addy) {
    assert(mSocket);
    mSocket->connect(addy,0,mSendBufferSize);
}

Stream*TCPStream::factory(){
    return new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
}
Stream* TCPStream::clone(const SubstreamCallback &cloneCallback) {
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        return NULL;
    }

    TCPStream *retval=new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
    retval->mSocket = socket_copy;

    StreamID newID = socket_copy->getNewID();
    retval->mID=newID;
    TCPSetCallbacks setCallbackFunctor(socket_copy.get(), retval);
    cloneCallback(retval,setCallbackFunctor);
    return retval;
}

Stream* TCPStream::clone(const ConnectionCallback &connectionCallback,
                         const BytesReceivedCallback&chunkReceivedCallback,
                         const ReadySendCallback&readySendCallback) {
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        return NULL;
    }

    TCPStream *retval=new TCPStream(*mIO,mNumSimultaneousSockets,mSendBufferSize);
    retval->mSocket = socket_copy;

    StreamID newID = socket_copy->getNewID();
    retval->mID=newID;
    TCPSetCallbacks setCallbackFunctor(socket_copy.get(), retval);
    setCallbackFunctor(connectionCallback,chunkReceivedCallback, readySendCallback);
    return retval;
}

///Only returns a legitimate address if ConnectionStatus called back, otherwise return Address::null()
Address TCPStream::getRemoteEndpoint() const{
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        SILOG(tcpsst,debug,"Called TCPStream::getRemoveEndpoint() on closed stream." << getID().read());
        return Address::null();
    }

    return socket_copy->getRemoteEndpoint(mID);
}
///Only returns a legitimate address if ConnectionStatus called back, otherwise return Address::null()
Address TCPStream::getLocalEndpoint() const{
    MultiplexedSocketPtr socket_copy = mSocket;
    if (socket_copy.get() == NULL) {
        SILOG(tcpsst,debug,"Called TCPStream::getLocalEndpoint() on closed stream." << getID().read());
        return Address::null();
    }

    return socket_copy->getLocalEndpoint(mID);
}

}  }
<|endoftext|>


<commit_before>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Allows geometry creation using ifcopenshell
*/

#include "repo_ifc_utils_geometry.h"
#include "../../../core/model/bson/repo_bson_factory.h"
#include <boost/filesystem.hpp>

using namespace repo::manipulator::modelconvertor;

IFCUtilsGeometry::IFCUtilsGeometry(const std::string &file) :
file(file)
{
}

IFCUtilsGeometry::~IFCUtilsGeometry()
{
}

repo_material_t IFCUtilsGeometry::createMaterial(
	const IfcGeom::Material &material)
{
	repo_material_t matProp;
	if (material.hasDiffuse())
	{
		auto diffuse = material.diffuse();
		matProp.diffuse = { (float)diffuse[0], (float)diffuse[1], (float)diffuse[2] };
	}

	if (material.hasSpecular())
	{
		auto specular = material.specular();
		matProp.specular = { (float)specular[0], (float)specular[1], (float)specular[2] };
	}

	if (material.hasSpecularity())
	{
		matProp.shininess = material.specularity();
	}
	else
	{
		matProp.shininess = NAN;
	}

	matProp.shininessStrength = NAN;

	if (material.hasTransparency())
	{
		matProp.opacity = 1. - material.transparency();
	}
	else
	{
		matProp.opacity = 1.;
	}
	return matProp;
}

IfcGeom::IteratorSettings IFCUtilsGeometry::createSettings()
{
	IfcGeom::IteratorSettings settings;
	settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true);
	settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, false);
	settings.set(IfcGeom::IteratorSettings::NO_NORMALS, false);
	settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
	settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, true);
	settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);

	return settings;
}

bool IFCUtilsGeometry::generateGeometry(std::string &errMsg)
{
	repoInfo << "Initialising Geometry....." << std::endl;

	auto settings = createSettings();

	IfcGeom::Iterator<double> contextIterator(settings, file);

	std::set<std::string> exclude_entities;
	//Do not generate geometry for openings and spaces
	exclude_entities.insert("IfcOpeningElement");
	//exclude_entities.insert("IfcSpace");
	contextIterator.excludeEntities(exclude_entities);

	repoTrace << "Initialising Geom iterator";
	if (contextIterator.initialize())
	{
		repoTrace << "Geom Iterator initialized";
	}
	else
	{
		errMsg = "Failed to initialised Geom Iterator";
		return false;
	}

	std::vector<std::vector<repo_face_t>> allFaces;
	std::vector<std::vector<double>> allVertices;
	std::vector<std::vector<double>> allNormals;
	std::vector<std::vector<double>> allUVs;
	std::vector<std::string> allIds, allNames, allMaterials;

	retrieveGeometryFromIterator(contextIterator, settings.get(IfcGeom::IteratorSettings::USE_MATERIAL_NAMES),
		allVertices, allFaces, allNormals, allUVs, allIds, allNames, allMaterials);

	//now we have found all meshes, take the minimum bounding box of the scene as offset
	//and create repo meshes
	repoTrace << "Finished iterating. number of meshes found: " << allVertices.size();
	repoTrace << "Finished iterating. number of materials found: " << materials.size();

	std::map<std::string, std::vector<repoUUID>> materialParent;
	for (int i = 0; i < allVertices.size(); ++i)
	{
		std::vector<repo_vector_t> vertices, normals;
		std::vector<repo_vector2d_t> uvs;
		std::vector<repo_face_t> faces;
		std::vector<std::vector<float>> boundingBox;
		for (int j = 0; j < allVertices[i].size(); j += 3)
		{
			vertices.push_back({ allVertices[i][j] - offset[0], allVertices[i][j + 1] - offset[1], allVertices[i][j + 2] - offset[2] });
			if (allNormals[i].size())
				normals.push_back({ allNormals[i][j], allNormals[i][j + 1], allNormals[i][j + 2] });

			auto vertex = vertices.back();
			if (j == 0)
			{
				boundingBox.push_back({ vertex.x, vertex.y, vertex.z });
				boundingBox.push_back({ vertex.x, vertex.y, vertex.z });
			}
			else
			{
				boundingBox[0][0] = boundingBox[0][0] > vertex.x ? vertex.x : boundingBox[0][0];
				boundingBox[0][1] = boundingBox[0][1] > vertex.y ? vertex.y : boundingBox[0][1];
				boundingBox[0][2] = boundingBox[0][2] > vertex.z ? vertex.z : boundingBox[0][2];

				boundingBox[1][0] = boundingBox[1][0] < vertex.x ? vertex.x : boundingBox[1][0];
				boundingBox[1][1] = boundingBox[1][1] < vertex.y ? vertex.y : boundingBox[1][1];
				boundingBox[1][2] = boundingBox[1][2] < vertex.z ? vertex.z : boundingBox[1][2];
			}
		}

		for (int j = 0; j < allUVs[i].size(); j += 2)
		{
			uvs.push_back({ allUVs[i][j], allUVs[i][j + 1] });
		}

		std::vector < std::vector<repo_vector2d_t>> uvChannels;
		if (uvs.size())
			uvChannels.push_back(uvs);

		auto mesh = repo::core::model::RepoBSONFactory::makeMeshNode(vertices, allFaces[i], normals, boundingBox, uvChannels,
			std::vector<repo_color4d_t>(), std::vector<std::vector<float>>());

		if (meshes.find(allIds[i]) == meshes.end())
		{
			meshes[allIds[i]] = std::vector<repo::core::model::MeshNode*>();
		}
		meshes[allIds[i]].push_back(new repo::core::model::MeshNode(mesh));

		if (allMaterials[i] != "")
		{
			if (materialParent.find(allMaterials[i]) == materialParent.end())
			{
				materialParent[allMaterials[i]] = std::vector<repoUUID>();
			}

			materialParent[allMaterials[i]].push_back(mesh.getSharedID());
		}
	}

	repoTrace << "Meshes constructed. Wiring materials to parents...";

	for (const auto &pair : materialParent)
	{
		auto matIt = materials.find(pair.first);
		if (matIt != materials.end())
		{
			*(matIt->second) = matIt->second->cloneAndAddParent(pair.second);
		}
	}

	return true;
}

void IFCUtilsGeometry::retrieveGeometryFromIterator(
	IfcGeom::Iterator<double> &contextIterator,
	const bool useMaterialNames,
	std::vector < std::vector<double>> &allVertices,
	std::vector<std::vector<repo_face_t>> &allFaces,
	std::vector < std::vector<double>> &allNormals,
	std::vector < std::vector<double>> &allUVs,
	std::vector<std::string> &allIds,
	std::vector<std::string> &allNames,
	std::vector<std::string> &allMaterials)
{
	repoTrace << "Iterating through Geom Iterator.";
	do
	{
		IfcGeom::Element<double> *ob = contextIterator.get();
		auto ob_geo = static_cast<const IfcGeom::TriangulationElement<double>*>(ob);
		if (ob_geo)
		{
			auto faces = ob_geo->geometry().faces();
			auto vertices = ob_geo->geometry().verts();
			auto normals = ob_geo->geometry().normals();
			auto uvs = ob_geo->geometry().uvs();
			std::unordered_map<int, std::unordered_map<int, int>>  indexMapping;
			std::unordered_map<int, int> vertexCount;
			std::unordered_map<int, std::vector<double>> post_vertices, post_normals, post_uvs;
			std::unordered_map<int, std::vector<repo_face_t>> post_faces;

			auto matIndIt = ob_geo->geometry().material_ids().begin();

			for (int i = 0; i < vertices.size(); i += 3)
			{
				for (int j = 0; j < 3; ++j)
				{
					int index = j + i;
					if (offset.size() < j + 1)
					{
						offset.push_back(vertices[index]);
					}
					else
					{
						offset[j] = offset[j] > vertices[index] ? vertices[index] : offset[j];
					}
				}
			}

			for (int iface = 0; iface < faces.size(); iface += 3)
			{
				auto matInd = *matIndIt;
				if (indexMapping.find(matInd) == indexMapping.end())
				{
					//new material
					indexMapping[matInd] = std::unordered_map<int, int>();
					vertexCount[matInd] = 0;

					std::unordered_map<int, std::vector<double>> post_vertices, post_normals, post_uvs;
					std::unordered_map<int, std::vector<repo_face_t>> post_faces;

					post_vertices[matInd] = std::vector<double>();
					post_normals[matInd] = std::vector<double>();
					post_uvs[matInd] = std::vector<double>();
					post_faces[matInd] = std::vector<repo_face_t>();

					auto material = ob_geo->geometry().materials()[matInd];
					std::string matName = useMaterialNames ? material.original_name() : material.name();
					allMaterials.push_back(matName);
					if (materials.find(matName) == materials.end())
					{
						//new material, add it to the vector
						repo_material_t matProp = createMaterial(material);
						materials[matName] = new repo::core::model::MaterialNode(repo::core::model::RepoBSONFactory::makeMaterialNode(matProp, matName));
					}
				}

				repo_face_t face;
				for (int j = 0; j < 3; ++j)
				{
					auto vIndex = faces[iface + j];
					if (indexMapping[matInd].find(vIndex) == indexMapping[matInd].end())
					{
						//new index. create a mapping
						indexMapping[matInd][vIndex] = vertexCount[matInd]++;
						for (int ivert = 0; ivert < 3; ++ivert)
						{
							auto bufferInd = ivert + vIndex * 3;
							post_vertices[matInd].push_back(vertices[bufferInd]);

							if (normals.size())
								post_normals[matInd].push_back(normals[bufferInd]);

							if (uvs.size() && ivert < 2)
							{
								auto uvbufferInd = ivert + vIndex * 2;
								post_uvs[matInd].push_back(uvs[uvbufferInd]);
							}
						}
					}
					face.push_back(indexMapping[matInd][vIndex]);
				}

				post_faces[matInd].push_back(face);

				++matIndIt;
			}

			auto guid = ob_geo->guid();
			auto name = ob_geo->name();
			for (const auto& pair : post_faces)
			{
				auto index = pair.first;
				allVertices.push_back(post_vertices[index]);
				allNormals.push_back(post_normals[index]);
				allFaces.push_back(pair.second);
				allUVs.push_back(post_uvs[index]);

				allIds.push_back(guid);
				allNames.push_back(name);
			}
		}
		if (allIds.size() % 100 == 0)
			repoInfo << allIds.size() << " meshes created";
	} while (contextIterator.next());
}<commit_msg>#13 rid of narrowing conversion warning messages<commit_after>/**
*  Copyright (C) 2015 3D Repo Ltd
*
*  This program is free software: you can redistribute it and/or modify
*  it under the terms of the GNU Affero General Public License as
*  published by the Free Software Foundation, either version 3 of the
*  License, or (at your option) any later version.
*
*  This program is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU Affero General Public License for more details.
*
*  You should have received a copy of the GNU Affero General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Allows geometry creation using ifcopenshell
*/

#include "repo_ifc_utils_geometry.h"
#include "../../../core/model/bson/repo_bson_factory.h"
#include <boost/filesystem.hpp>

using namespace repo::manipulator::modelconvertor;

IFCUtilsGeometry::IFCUtilsGeometry(const std::string &file) :
file(file)
{
}

IFCUtilsGeometry::~IFCUtilsGeometry()
{
}

repo_material_t IFCUtilsGeometry::createMaterial(
	const IfcGeom::Material &material)
{
	repo_material_t matProp;
	if (material.hasDiffuse())
	{
		auto diffuse = material.diffuse();
		matProp.diffuse = { (float)diffuse[0], (float)diffuse[1], (float)diffuse[2] };
	}

	if (material.hasSpecular())
	{
		auto specular = material.specular();
		matProp.specular = { (float)specular[0], (float)specular[1], (float)specular[2] };
	}

	if (material.hasSpecularity())
	{
		matProp.shininess = material.specularity();
	}
	else
	{
		matProp.shininess = NAN;
	}

	matProp.shininessStrength = NAN;

	if (material.hasTransparency())
	{
		matProp.opacity = 1. - material.transparency();
	}
	else
	{
		matProp.opacity = 1.;
	}
	return matProp;
}

IfcGeom::IteratorSettings IFCUtilsGeometry::createSettings()
{
	IfcGeom::IteratorSettings settings;
	settings.set(IfcGeom::IteratorSettings::APPLY_DEFAULT_MATERIALS, true);
	settings.set(IfcGeom::IteratorSettings::DISABLE_OPENING_SUBTRACTIONS, false);
	settings.set(IfcGeom::IteratorSettings::NO_NORMALS, false);
	settings.set(IfcGeom::IteratorSettings::WELD_VERTICES, false);
	settings.set(IfcGeom::IteratorSettings::GENERATE_UVS, true);
	settings.set(IfcGeom::IteratorSettings::USE_WORLD_COORDS, true);

	return settings;
}

bool IFCUtilsGeometry::generateGeometry(std::string &errMsg)
{
	repoInfo << "Initialising Geometry....." << std::endl;

	auto settings = createSettings();

	IfcGeom::Iterator<double> contextIterator(settings, file);

	std::set<std::string> exclude_entities;
	//Do not generate geometry for openings and spaces
	exclude_entities.insert("IfcOpeningElement");
	//exclude_entities.insert("IfcSpace");
	contextIterator.excludeEntities(exclude_entities);

	repoTrace << "Initialising Geom iterator";
	if (contextIterator.initialize())
	{
		repoTrace << "Geom Iterator initialized";
	}
	else
	{
		errMsg = "Failed to initialised Geom Iterator";
		return false;
	}

	std::vector<std::vector<repo_face_t>> allFaces;
	std::vector<std::vector<double>> allVertices;
	std::vector<std::vector<double>> allNormals;
	std::vector<std::vector<double>> allUVs;
	std::vector<std::string> allIds, allNames, allMaterials;

	retrieveGeometryFromIterator(contextIterator, settings.get(IfcGeom::IteratorSettings::USE_MATERIAL_NAMES),
		allVertices, allFaces, allNormals, allUVs, allIds, allNames, allMaterials);

	//now we have found all meshes, take the minimum bounding box of the scene as offset
	//and create repo meshes
	repoTrace << "Finished iterating. number of meshes found: " << allVertices.size();
	repoTrace << "Finished iterating. number of materials found: " << materials.size();

	std::map<std::string, std::vector<repoUUID>> materialParent;
	for (int i = 0; i < allVertices.size(); ++i)
	{
		std::vector<repo_vector_t> vertices, normals;
		std::vector<repo_vector2d_t> uvs;
		std::vector<repo_face_t> faces;
		std::vector<std::vector<float>> boundingBox;
		for (int j = 0; j < allVertices[i].size(); j += 3)
		{
			vertices.push_back({ (float)(allVertices[i][j] - offset[0]), (float)(allVertices[i][j + 1] - offset[1]), (float)(allVertices[i][j + 2] - offset[2]) });
			if (allNormals[i].size())
				normals.push_back({ (float)allNormals[i][j], (float)allNormals[i][j + 1], (float)allNormals[i][j + 2] });

			auto vertex = vertices.back();
			if (j == 0)
			{
				boundingBox.push_back({ vertex.x, vertex.y, vertex.z });
				boundingBox.push_back({ vertex.x, vertex.y, vertex.z });
			}
			else
			{
				boundingBox[0][0] = boundingBox[0][0] > vertex.x ? vertex.x : boundingBox[0][0];
				boundingBox[0][1] = boundingBox[0][1] > vertex.y ? vertex.y : boundingBox[0][1];
				boundingBox[0][2] = boundingBox[0][2] > vertex.z ? vertex.z : boundingBox[0][2];

				boundingBox[1][0] = boundingBox[1][0] < vertex.x ? vertex.x : boundingBox[1][0];
				boundingBox[1][1] = boundingBox[1][1] < vertex.y ? vertex.y : boundingBox[1][1];
				boundingBox[1][2] = boundingBox[1][2] < vertex.z ? vertex.z : boundingBox[1][2];
			}
		}

		for (int j = 0; j < allUVs[i].size(); j += 2)
		{
			uvs.push_back({ (float)allUVs[i][j], (float)allUVs[i][j + 1] });
		}

		std::vector < std::vector<repo_vector2d_t>> uvChannels;
		if (uvs.size())
			uvChannels.push_back(uvs);

		auto mesh = repo::core::model::RepoBSONFactory::makeMeshNode(vertices, allFaces[i], normals, boundingBox, uvChannels,
			std::vector<repo_color4d_t>(), std::vector<std::vector<float>>());

		if (meshes.find(allIds[i]) == meshes.end())
		{
			meshes[allIds[i]] = std::vector<repo::core::model::MeshNode*>();
		}
		meshes[allIds[i]].push_back(new repo::core::model::MeshNode(mesh));

		if (allMaterials[i] != "")
		{
			if (materialParent.find(allMaterials[i]) == materialParent.end())
			{
				materialParent[allMaterials[i]] = std::vector<repoUUID>();
			}

			materialParent[allMaterials[i]].push_back(mesh.getSharedID());
		}
	}

	repoTrace << "Meshes constructed. Wiring materials to parents...";

	for (const auto &pair : materialParent)
	{
		auto matIt = materials.find(pair.first);
		if (matIt != materials.end())
		{
			*(matIt->second) = matIt->second->cloneAndAddParent(pair.second);
		}
	}

	return true;
}

void IFCUtilsGeometry::retrieveGeometryFromIterator(
	IfcGeom::Iterator<double> &contextIterator,
	const bool useMaterialNames,
	std::vector < std::vector<double>> &allVertices,
	std::vector<std::vector<repo_face_t>> &allFaces,
	std::vector < std::vector<double>> &allNormals,
	std::vector < std::vector<double>> &allUVs,
	std::vector<std::string> &allIds,
	std::vector<std::string> &allNames,
	std::vector<std::string> &allMaterials)
{
	repoTrace << "Iterating through Geom Iterator.";
	do
	{
		IfcGeom::Element<double> *ob = contextIterator.get();
		auto ob_geo = static_cast<const IfcGeom::TriangulationElement<double>*>(ob);
		if (ob_geo)
		{
			auto faces = ob_geo->geometry().faces();
			auto vertices = ob_geo->geometry().verts();
			auto normals = ob_geo->geometry().normals();
			auto uvs = ob_geo->geometry().uvs();
			std::unordered_map<int, std::unordered_map<int, int>>  indexMapping;
			std::unordered_map<int, int> vertexCount;
			std::unordered_map<int, std::vector<double>> post_vertices, post_normals, post_uvs;
			std::unordered_map<int, std::vector<repo_face_t>> post_faces;

			auto matIndIt = ob_geo->geometry().material_ids().begin();

			for (int i = 0; i < vertices.size(); i += 3)
			{
				for (int j = 0; j < 3; ++j)
				{
					int index = j + i;
					if (offset.size() < j + 1)
					{
						offset.push_back(vertices[index]);
					}
					else
					{
						offset[j] = offset[j] > vertices[index] ? vertices[index] : offset[j];
					}
				}
			}

			for (int iface = 0; iface < faces.size(); iface += 3)
			{
				auto matInd = *matIndIt;
				if (indexMapping.find(matInd) == indexMapping.end())
				{
					//new material
					indexMapping[matInd] = std::unordered_map<int, int>();
					vertexCount[matInd] = 0;

					std::unordered_map<int, std::vector<double>> post_vertices, post_normals, post_uvs;
					std::unordered_map<int, std::vector<repo_face_t>> post_faces;

					post_vertices[matInd] = std::vector<double>();
					post_normals[matInd] = std::vector<double>();
					post_uvs[matInd] = std::vector<double>();
					post_faces[matInd] = std::vector<repo_face_t>();

					auto material = ob_geo->geometry().materials()[matInd];
					std::string matName = useMaterialNames ? material.original_name() : material.name();
					allMaterials.push_back(matName);
					if (materials.find(matName) == materials.end())
					{
						//new material, add it to the vector
						repo_material_t matProp = createMaterial(material);
						materials[matName] = new repo::core::model::MaterialNode(repo::core::model::RepoBSONFactory::makeMaterialNode(matProp, matName));
					}
				}

				repo_face_t face;
				for (int j = 0; j < 3; ++j)
				{
					auto vIndex = faces[iface + j];
					if (indexMapping[matInd].find(vIndex) == indexMapping[matInd].end())
					{
						//new index. create a mapping
						indexMapping[matInd][vIndex] = vertexCount[matInd]++;
						for (int ivert = 0; ivert < 3; ++ivert)
						{
							auto bufferInd = ivert + vIndex * 3;
							post_vertices[matInd].push_back(vertices[bufferInd]);

							if (normals.size())
								post_normals[matInd].push_back(normals[bufferInd]);

							if (uvs.size() && ivert < 2)
							{
								auto uvbufferInd = ivert + vIndex * 2;
								post_uvs[matInd].push_back(uvs[uvbufferInd]);
							}
						}
					}
					face.push_back(indexMapping[matInd][vIndex]);
				}

				post_faces[matInd].push_back(face);

				++matIndIt;
			}

			auto guid = ob_geo->guid();
			auto name = ob_geo->name();
			for (const auto& pair : post_faces)
			{
				auto index = pair.first;
				allVertices.push_back(post_vertices[index]);
				allNormals.push_back(post_normals[index]);
				allFaces.push_back(pair.second);
				allUVs.push_back(post_uvs[index]);

				allIds.push_back(guid);
				allNames.push_back(name);
			}
		}
		if (allIds.size() % 100 == 0)
			repoInfo << allIds.size() << " meshes created";
	} while (contextIterator.next());
}<|endoftext|>


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

#include <cstring> // ?
#include <unistd.h> // gethostname

#include "../extension_pacrunner.hpp"
using namespace libproxy;

// Work around a mozjs include bug
#ifndef JS_HAS_FILE_OBJECT
#define JS_HAS_FILE_OBJECT 0
#endif
#ifdef WIN32
#ifndef XP_WIN
#define XP_WIN
#endif
#endif
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#include <jsapi.h>
#pragma GCC diagnostic error "-Winvalid-offsetof"
#include <js/Initialization.h>
#include <js/CallArgs.h>

#include "pacutils.h"

#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
#endif

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif

static void dnsResolve_(JSContext *cx, JSString *hostname, JS::CallArgs *argv) {
	// Get hostname argument
	char *tmp = JS_EncodeString(cx, hostname);

	// Set the default return value
	argv->rval().setNull();

	// Look it up
	struct addrinfo *info = nullptr;
	if (getaddrinfo(tmp, NULL, NULL, &info))
		goto out;

	// Allocate the IP address
	JS_free(cx, tmp);
	tmp = (char *) JS_malloc(cx, INET6_ADDRSTRLEN+1);
	memset(tmp, 0, INET6_ADDRSTRLEN+1);

	// Try for IPv4 and IPv6
	if (getnameinfo(info->ai_addr, info->ai_addrlen,
					tmp, INET6_ADDRSTRLEN+1,
					NULL, 0,
					NI_NUMERICHOST)) goto out;

	// We succeeded
	argv->rval().setString(JS_NewStringCopyZ(cx, tmp));
	tmp = nullptr;

	out:
		if (info) freeaddrinfo(info);
		JS_free(cx, tmp);
}

static bool dnsResolve(JSContext *cx, unsigned argc, JS::Value *vp) {
	JS::CallArgs argv=JS::CallArgsFromVp(argc,vp);
	dnsResolve_(cx, argv[0].toString(), &argv);
	return true;
}

static bool myIpAddress(JSContext *cx, unsigned argc, JS::Value *vp) {
	JS::CallArgs argv=JS::CallArgsFromVp(argc,vp);
	char *hostname = (char *) JS_malloc(cx, 1024);

	if (!gethostname(hostname, 1023)) {
		JSString *myhost = JS_NewStringCopyN(cx, hostname, strlen(hostname));
		dnsResolve_(cx, myhost, &argv);
	} else {
		argv.rval().setNull();
	}

	JS_free(cx, hostname);
	return true;
}

// Setup Javascript global class
// This MUST be a static global
static JSClass cls = {
		"global", JSCLASS_GLOBAL_FLAGS,
};

class mozjs_pacrunner : public pacrunner {
public:
	mozjs_pacrunner(string pac, const url& pacurl) throw (bad_alloc) : pacrunner(pac, pacurl) {

		// Set defaults
		this->jsctx = nullptr;
		JS_Init();

		// Initialize Javascript context
		if (!(this->jsctx = JS_NewContext(1024 * 1024)))     goto error;
		{
			JS::RootedValue  rval(this->jsctx);
			JS::CompartmentOptions compart_opts;

			this->jsglb = new JS::Heap<JSObject*>(JS_NewGlobalObject(
								  this->jsctx, &cls,
								  nullptr, JS::DontFireOnNewGlobalHook,
								  compart_opts));

			if (!(this->jsglb)) goto error;
			JS::RootedObject global(this->jsctx,this->jsglb->get());
			if (!(this->jsac = new JSAutoCompartment(this->jsctx,  global))) goto error;
			if (!JS_InitStandardClasses(this->jsctx, global))            goto error;

			// Define Javascript functions
			JS_DefineFunction(this->jsctx, global, "dnsResolve", dnsResolve, 1, 0);
			JS_DefineFunction(this->jsctx, global, "myIpAddress", myIpAddress, 0, 0);
			JS::CompileOptions options(this->jsctx);
			options.setUTF8(true);

			JS::Evaluate(this->jsctx, options, JAVASCRIPT_ROUTINES,
				     strlen(JAVASCRIPT_ROUTINES), JS::MutableHandleValue(&rval));

			// Add PAC to the environment
			JS::Evaluate(this->jsctx, options, pac.c_str(), pac.length(), JS::MutableHandleValue(&rval));
			return;
		}
		error:
			if (this->jsctx) JS_DestroyContext(this->jsctx);
			throw bad_alloc();
	}

	~mozjs_pacrunner() {
		if (this->jsac) delete this->jsac;
		if (this->jsglb) delete this->jsglb;
		if (this->jsctx) JS_DestroyContext(this->jsctx);
		JS_ShutDown();
	}

	string run(const url& url_) throw (bad_alloc) {
		// Build arguments to the FindProxyForURL() function
		char *tmpurl  = JS_strdup(this->jsctx, url_.to_string().c_str());
		char *tmphost = JS_strdup(this->jsctx, url_.get_host().c_str());
		if (!tmpurl || !tmphost) {
			if (tmpurl) JS_free(this->jsctx, tmpurl);
			if (tmphost) JS_free(this->jsctx, tmphost);
			throw bad_alloc();
		}
		JS::AutoValueArray<2> args(this->jsctx);
		args[0].setString(JS_NewStringCopyZ(this->jsctx, tmpurl));
		args[1].setString(JS_NewStringCopyZ(this->jsctx, tmphost));

		// Find the proxy (call FindProxyForURL())
		JS::RootedValue rval(this->jsctx);
		JS::RootedObject global(this->jsctx,this->jsglb->get());
		bool result = JS_CallFunctionName(this->jsctx, global, "FindProxyForURL", args, &rval);
		if (!result) return "";

		char * tmpanswer = JS_EncodeString(this->jsctx, rval.toString());
		string answer = string(tmpanswer);
		JS_free(this->jsctx, tmpanswer);

		if (answer == "undefined") return "";
		return answer;
	}

private:
	JSContext *jsctx;
	JS::Heap<JSObject*> *jsglb;
	JSAutoCompartment *jsac;
};

PX_PACRUNNER_MODULE_EZ(mozjs, "JS_DefineFunction", "mozjs");
<commit_msg>Add call to JS::InitSelfHostedCode()<commit_after>/*******************************************************************************
 * libproxy - A library for proxy configuration
 * Copyright (C) 2006 Nathaniel McCallum <nathaniel@natemccallum.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
 ******************************************************************************/

#include <cstring> // ?
#include <unistd.h> // gethostname

#include "../extension_pacrunner.hpp"
using namespace libproxy;

// Work around a mozjs include bug
#ifndef JS_HAS_FILE_OBJECT
#define JS_HAS_FILE_OBJECT 0
#endif
#ifdef WIN32
#ifndef XP_WIN
#define XP_WIN
#endif
#endif
#pragma GCC diagnostic ignored "-Winvalid-offsetof"
#include <jsapi.h>
#pragma GCC diagnostic error "-Winvalid-offsetof"
#include <js/Initialization.h>
#include <js/CallArgs.h>

#include "pacutils.h"

#ifndef INET_ADDRSTRLEN
#define INET_ADDRSTRLEN 16
#endif

#ifndef INET6_ADDRSTRLEN
#define INET6_ADDRSTRLEN 46
#endif

static void dnsResolve_(JSContext *cx, JSString *hostname, JS::CallArgs *argv) {
	// Get hostname argument
	char *tmp = JS_EncodeString(cx, hostname);

	// Set the default return value
	argv->rval().setNull();

	// Look it up
	struct addrinfo *info = nullptr;
	if (getaddrinfo(tmp, NULL, NULL, &info))
		goto out;

	// Allocate the IP address
	JS_free(cx, tmp);
	tmp = (char *) JS_malloc(cx, INET6_ADDRSTRLEN+1);
	memset(tmp, 0, INET6_ADDRSTRLEN+1);

	// Try for IPv4 and IPv6
	if (getnameinfo(info->ai_addr, info->ai_addrlen,
					tmp, INET6_ADDRSTRLEN+1,
					NULL, 0,
					NI_NUMERICHOST)) goto out;

	// We succeeded
	argv->rval().setString(JS_NewStringCopyZ(cx, tmp));
	tmp = nullptr;

	out:
		if (info) freeaddrinfo(info);
		JS_free(cx, tmp);
}

static bool dnsResolve(JSContext *cx, unsigned argc, JS::Value *vp) {
	JS::CallArgs argv=JS::CallArgsFromVp(argc,vp);
	dnsResolve_(cx, argv[0].toString(), &argv);
	return true;
}

static bool myIpAddress(JSContext *cx, unsigned argc, JS::Value *vp) {
	JS::CallArgs argv=JS::CallArgsFromVp(argc,vp);
	char *hostname = (char *) JS_malloc(cx, 1024);

	if (!gethostname(hostname, 1023)) {
		JSString *myhost = JS_NewStringCopyN(cx, hostname, strlen(hostname));
		dnsResolve_(cx, myhost, &argv);
	} else {
		argv.rval().setNull();
	}

	JS_free(cx, hostname);
	return true;
}

// Setup Javascript global class
// This MUST be a static global
static JSClass cls = {
		"global", JSCLASS_GLOBAL_FLAGS,
};

class mozjs_pacrunner : public pacrunner {
public:
	mozjs_pacrunner(string pac, const url& pacurl) throw (bad_alloc) : pacrunner(pac, pacurl) {

		// Set defaults
		this->jsctx = nullptr;
		JS_Init();

		// Initialize Javascript context
		if (!(this->jsctx = JS_NewContext(1024 * 1024)))     goto error;
		{
			if (!JS::InitSelfHostedCode(this->jsctx)) goto error;

			JS::RootedValue  rval(this->jsctx);
			JS::CompartmentOptions compart_opts;

			this->jsglb = new JS::Heap<JSObject*>(JS_NewGlobalObject(
								  this->jsctx, &cls,
								  nullptr, JS::DontFireOnNewGlobalHook,
								  compart_opts));

			if (!(this->jsglb)) goto error;
			JS::RootedObject global(this->jsctx,this->jsglb->get());
			if (!(this->jsac = new JSAutoCompartment(this->jsctx,  global))) goto error;
			if (!JS_InitStandardClasses(this->jsctx, global))            goto error;

			// Define Javascript functions
			JS_DefineFunction(this->jsctx, global, "dnsResolve", dnsResolve, 1, 0);
			JS_DefineFunction(this->jsctx, global, "myIpAddress", myIpAddress, 0, 0);
			JS::CompileOptions options(this->jsctx);
			options.setUTF8(true);

			JS::Evaluate(this->jsctx, options, JAVASCRIPT_ROUTINES,
				     strlen(JAVASCRIPT_ROUTINES), JS::MutableHandleValue(&rval));

			// Add PAC to the environment
			JS::Evaluate(this->jsctx, options, pac.c_str(), pac.length(), JS::MutableHandleValue(&rval));
			return;
		}
		error:
			if (this->jsctx) JS_DestroyContext(this->jsctx);
			throw bad_alloc();
	}

	~mozjs_pacrunner() {
		if (this->jsac) delete this->jsac;
		if (this->jsglb) delete this->jsglb;
		if (this->jsctx) JS_DestroyContext(this->jsctx);
		JS_ShutDown();
	}

	string run(const url& url_) throw (bad_alloc) {
		// Build arguments to the FindProxyForURL() function
		char *tmpurl  = JS_strdup(this->jsctx, url_.to_string().c_str());
		char *tmphost = JS_strdup(this->jsctx, url_.get_host().c_str());
		if (!tmpurl || !tmphost) {
			if (tmpurl) JS_free(this->jsctx, tmpurl);
			if (tmphost) JS_free(this->jsctx, tmphost);
			throw bad_alloc();
		}
		JS::AutoValueArray<2> args(this->jsctx);
		args[0].setString(JS_NewStringCopyZ(this->jsctx, tmpurl));
		args[1].setString(JS_NewStringCopyZ(this->jsctx, tmphost));

		// Find the proxy (call FindProxyForURL())
		JS::RootedValue rval(this->jsctx);
		JS::RootedObject global(this->jsctx,this->jsglb->get());
		bool result = JS_CallFunctionName(this->jsctx, global, "FindProxyForURL", args, &rval);
		if (!result) return "";

		char * tmpanswer = JS_EncodeString(this->jsctx, rval.toString());
		string answer = string(tmpanswer);
		JS_free(this->jsctx, tmpanswer);

		if (answer == "undefined") return "";
		return answer;
	}

private:
	JSContext *jsctx;
	JS::Heap<JSObject*> *jsglb;
	JSAutoCompartment *jsac;
};

PX_PACRUNNER_MODULE_EZ(mozjs, "JS_DefineFunction", "mozjs");
<|endoftext|>