<commit_before>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "Node.h"
#include "Engine.h"
#include "SceneManager.h"
#include "Layer.h"
#include "Animator.h"
#include "Camera.h"
#include "Utils.h"
#include "MathUtils.h"

namespace ouzel
{
    Node::Node()
    {

    }

    Node::~Node()
    {

    }

    void Node::visit(const Matrix4& parentTransform, bool parentTransformDirty)
    {
        if (_visible)
        {
            if (parentTransformDirty)
            {
                _parentTransform = parentTransform;
                _transformDirty = true;
            }

            bool dirty = _transformDirty;
            if (dirty)
            {
                calculateTransform();
            }

            LayerPtr layer = _layer.lock();
            // check if _parent is _layer
            bool isRoot = !_parent.owner_before(_layer) && !_layer.owner_before(_parent);

            if (_children.empty())
            {
                if (layer && (_globalOrder || isRoot) && checkVisibility())
                {
                    layer->addToDrawQueue(std::static_pointer_cast<Node>(shared_from_this()));
                }
            }
            else
            {
                lock();

                std::stable_sort(_children.begin(), _children.end(), [](const NodePtr& a, const NodePtr& b) {
                    return a->getZ() > b->getZ();
                });

                auto i = _children.begin();
                NodePtr node;

                for (; i != _children.end(); ++i)
                {
                    node = *i;

                    if (!node->_remove)
                    {
                        if (node->getZ() < 0.0f)
                        {
                            node->visit(_transform, dirty);
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                if (layer && (_globalOrder || isRoot) && checkVisibility())
                {
                    layer->addToDrawQueue(std::static_pointer_cast<Node>(shared_from_this()));
                }

                for (; i != _children.end(); ++i)
                {
                    if (!node->_remove)
                    {
                        node = *i;
                        node->visit(_transform, dirty);
                    }
                }

                unlock();
            }
        }
    }

    void Node::process()
    {
        lock();

        auto i = _children.begin();
        NodePtr node;

        for (; i != _children.end(); ++i)
        {
            node = *i;

            if (node->getZ() < 0.0f)
            {
                if (!node->isGlobalOrder() && node->isVisible() && node->checkVisibility())
                {
                    node->draw();
                }
            }
            else
            {
                break;
            }
        }

        draw();

        for (; i != _children.end(); ++i)
        {
            node = *i;

            if (!node->isGlobalOrder() && node->isVisible() && node->checkVisibility())
            {
                node->draw();
            }
        }

        unlock();
    }

    void Node::draw()
    {
        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    bool Node::addChild(const NodePtr& node)
    {
        if (NodeContainer::addChild(node))
        {
            node->addToLayer(_layer);

            if (_transformDirty)
            {
                calculateTransform();
            }
            else
            {
                node->updateTransform(_transform);
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    bool Node::removeFromParent()
    {
        if (NodeContainerPtr parent = _parent.lock())
        {
            parent->removeChild(std::static_pointer_cast<Node>(shared_from_this()));
            return true;
        }

        return false;
    }

    void Node::setZ(float z)
    {
        _z = z;

        // Currently z does not affect transformation
        //_localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setGlobalOrder(bool globalOrder)
    {
        _globalOrder = globalOrder;
    }

    void Node::setPosition(const Vector2& position)
    {
        _position = position;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setRotation(float rotation)
    {
        _rotation = rotation;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setScale(const Vector2& scale)
    {
        _scale = scale;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setOpacity(float opacity)
    {
        _opacity = clamp(opacity, 0.0f, 1.0f);
    }

    void Node::setFlipX(bool flipX)
    {
        _flipX = flipX;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setFlipY(bool flipY)
    {
        _flipY = flipY;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setVisible(bool visible)
    {
        _visible = visible;
    }

    void Node::addToLayer(const LayerWeakPtr& layer)
    {
        _layer = layer;

        if (LayerPtr layer = _layer.lock())
        {
            for (const NodePtr& child : _children)
            {
                child->addToLayer(layer);
            }
        }
    }

    void Node::removeFromLayer()
    {
        for (const NodePtr& child : _children)
        {
            child->removeFromLayer();
        }

        _layer.reset();
    }

    bool Node::pointOn(const Vector2& position) const
    {
        return _boundingBox.containPoint(convertWorldToLocal(position));
    }

    bool Node::rectangleOverlaps(const Rectangle& rectangle) const
    {
        Matrix4 inverse = getInverseTransform();

        Vector3 corners[4] = {
            Vector3(rectangle.left(), rectangle.bottom(), 0.0f),
            Vector3(rectangle.right(), rectangle.bottom(), 0.0f),
            Vector3(rectangle.right(), rectangle.top(), 0.0f),
            Vector3(rectangle.left(), rectangle.top(), 0.0f)
        };

        uint8_t inCorners = 0;

        for (Vector3& corner : corners)
        {
            inverse.transformPoint(corner);

            if (corner.x >= _boundingBox.min.x &&
                corner.x <= _boundingBox.max.x &&
                corner.y >= _boundingBox.min.y &&
                corner.y <= _boundingBox.max.y)
            {
                return true;
            }

            if (corner.x < _boundingBox.min.x && corner.y < _boundingBox.min.y) inCorners |= 0x01;
            if (corner.x > _boundingBox.max.x && corner.y < _boundingBox.min.y) inCorners |= 0x02;
            if (corner.x > _boundingBox.max.x && corner.y > _boundingBox.max.y) inCorners |= 0x04;
            if (corner.x < _boundingBox.min.x && corner.y > _boundingBox.max.y) inCorners |= 0x08;
        }

        // bounding box is bigger than rectangle
        if (inCorners == 0x0F)
        {
            return true;
        }

        Vector2 boundingBoxCorners[4] = {
            Vector2(_boundingBox.min),
            Vector2(_boundingBox.max.x, _boundingBox.min.y),
            Vector2(_boundingBox.max),
            Vector2(_boundingBox.min.x, _boundingBox.max.y)
        };

        for (uint32_t current = 0; current < 4; ++current)
        {
            uint32_t next = (current == 3) ? 0 : current + 1;

            if (linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[0], boundingBoxCorners[1]) || // left
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[1], boundingBoxCorners[2]) || // top
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[2], boundingBoxCorners[3]) || // right
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[3], boundingBoxCorners[0])) // bottom
            {
                return true;
            }
        }

        return false;
    }

    const Matrix4& Node::getTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        return _transform;
    }

    const Matrix4& Node::getInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        if (_inverseTransformDirty)
        {
            calculateInverseTransform();
        }

        return _inverseTransform;
    }

    void Node::updateTransform(const Matrix4& parentTransform)
    {
        _parentTransform = parentTransform;
        _transformDirty = _inverseTransformDirty = true;
    }

    Vector2 Node::convertWorldToLocal(const Vector2& position) const
    {
        Vector3 localPosition = position;

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(localPosition);

        return Vector2(localPosition.x, localPosition.y);
    }

    Vector2 Node::convertLocalToWorld(const Vector2& position) const
    {
        Vector3 worldPosition = position;

        const Matrix4& transform = getTransform();
        transform.transformPoint(worldPosition);

        return Vector2(worldPosition.x, worldPosition.y);
    }

    bool Node::checkVisibility() const
    {
        if (const LayerPtr& layer = _layer.lock())
        {
            if (_boundingBox.isEmpty())
            {
                return true;
            }

            return Engine::getInstance()->getRenderer()->checkVisibility(getTransform(), _boundingBox, layer->getCamera());
        }

        return false;
    }

    void Node::animate(const AnimatorPtr& animator)
    {
        _currentAnimator = animator;

        if (_currentAnimator)
        {
            _currentAnimator->start(std::static_pointer_cast<Node>(shared_from_this()));
        }
    }

    void Node::stopAnimation()
    {
        if (_currentAnimator)
        {
            _currentAnimator->stop();
            _currentAnimator.reset();
        }
    }

    void Node::calculateLocalTransform() const
    {
        Matrix4 translation;
        translation.translate(Vector3(_position.x, _position.y, 0.0f));

        Matrix4 rotation;
        rotation.rotate(Vector3(0.0f, 0.0f, -1.0f), _rotation);

        Vector3 realScale = Vector3(_scale.x * (_flipX ? -1.0f : 1.0f),
                                    _scale.y * (_flipY ? -1.0f : 1.0f),
                                    1.0f);

        Matrix4 scale;
        scale.scale(realScale);

        _localTransform = translation * rotation * scale;

        _localTransformDirty = false;
    }

    void Node::calculateTransform() const
    {
        if (_localTransformDirty)
        {
            calculateLocalTransform();
        }

        _transform = _parentTransform * _localTransform;
        _transformDirty = false;

        for (const NodePtr& child : _children)
        {
            child->updateTransform(_transform);
        }
    }

    void Node::calculateInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        _inverseTransform = _transform;
        _inverseTransform.invert();
        _inverseTransformDirty = false;
    }
}
<commit_msg>Don't draw children if Node child vector is empty<commit_after>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <algorithm>
#include "Node.h"
#include "Engine.h"
#include "SceneManager.h"
#include "Layer.h"
#include "Animator.h"
#include "Camera.h"
#include "Utils.h"
#include "MathUtils.h"

namespace ouzel
{
    Node::Node()
    {

    }

    Node::~Node()
    {

    }

    void Node::visit(const Matrix4& parentTransform, bool parentTransformDirty)
    {
        if (_visible)
        {
            if (parentTransformDirty)
            {
                _parentTransform = parentTransform;
                _transformDirty = true;
            }

            bool dirty = _transformDirty;
            if (dirty)
            {
                calculateTransform();
            }

            LayerPtr layer = _layer.lock();
            // check if _parent is _layer
            bool isRoot = !_parent.owner_before(_layer) && !_layer.owner_before(_parent);

            if (_children.empty())
            {
                if (layer && (_globalOrder || isRoot) && checkVisibility())
                {
                    layer->addToDrawQueue(std::static_pointer_cast<Node>(shared_from_this()));
                }
            }
            else
            {
                lock();

                std::stable_sort(_children.begin(), _children.end(), [](const NodePtr& a, const NodePtr& b) {
                    return a->getZ() > b->getZ();
                });

                auto i = _children.begin();
                NodePtr node;

                for (; i != _children.end(); ++i)
                {
                    node = *i;

                    if (!node->_remove)
                    {
                        if (node->getZ() < 0.0f)
                        {
                            node->visit(_transform, dirty);
                        }
                        else
                        {
                            break;
                        }
                    }
                }

                if (layer && (_globalOrder || isRoot) && checkVisibility())
                {
                    layer->addToDrawQueue(std::static_pointer_cast<Node>(shared_from_this()));
                }

                for (; i != _children.end(); ++i)
                {
                    if (!node->_remove)
                    {
                        node = *i;
                        node->visit(_transform, dirty);
                    }
                }

                unlock();
            }
        }
    }

    void Node::process()
    {
        if (_children.empty())
        {
            draw();
        }
        else
        {
            lock();

            auto i = _children.begin();
            NodePtr node;

            for (; i != _children.end(); ++i)
            {
                node = *i;

                if (node->getZ() < 0.0f)
                {
                    if (!node->isGlobalOrder() && node->isVisible() && node->checkVisibility())
                    {
                        node->draw();
                    }
                }
                else
                {
                    break;
                }
            }

            draw();

            for (; i != _children.end(); ++i)
            {
                node = *i;

                if (!node->isGlobalOrder() && node->isVisible() && node->checkVisibility())
                {
                    node->draw();
                }
            }

            unlock();
        }
    }

    void Node::draw()
    {
        if (_transformDirty)
        {
            calculateTransform();
        }
    }

    bool Node::addChild(const NodePtr& node)
    {
        if (NodeContainer::addChild(node))
        {
            node->addToLayer(_layer);

            if (_transformDirty)
            {
                calculateTransform();
            }
            else
            {
                node->updateTransform(_transform);
            }

            return true;
        }
        else
        {
            return false;
        }
    }

    bool Node::removeFromParent()
    {
        if (NodeContainerPtr parent = _parent.lock())
        {
            parent->removeChild(std::static_pointer_cast<Node>(shared_from_this()));
            return true;
        }

        return false;
    }

    void Node::setZ(float z)
    {
        _z = z;

        // Currently z does not affect transformation
        //_localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setGlobalOrder(bool globalOrder)
    {
        _globalOrder = globalOrder;
    }

    void Node::setPosition(const Vector2& position)
    {
        _position = position;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setRotation(float rotation)
    {
        _rotation = rotation;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setScale(const Vector2& scale)
    {
        _scale = scale;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setOpacity(float opacity)
    {
        _opacity = clamp(opacity, 0.0f, 1.0f);
    }

    void Node::setFlipX(bool flipX)
    {
        _flipX = flipX;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setFlipY(bool flipY)
    {
        _flipY = flipY;

        _localTransformDirty = _transformDirty = _inverseTransformDirty = true;
    }

    void Node::setVisible(bool visible)
    {
        _visible = visible;
    }

    void Node::addToLayer(const LayerWeakPtr& layer)
    {
        _layer = layer;

        if (LayerPtr layer = _layer.lock())
        {
            for (const NodePtr& child : _children)
            {
                child->addToLayer(layer);
            }
        }
    }

    void Node::removeFromLayer()
    {
        for (const NodePtr& child : _children)
        {
            child->removeFromLayer();
        }

        _layer.reset();
    }

    bool Node::pointOn(const Vector2& position) const
    {
        return _boundingBox.containPoint(convertWorldToLocal(position));
    }

    bool Node::rectangleOverlaps(const Rectangle& rectangle) const
    {
        Matrix4 inverse = getInverseTransform();

        Vector3 corners[4] = {
            Vector3(rectangle.left(), rectangle.bottom(), 0.0f),
            Vector3(rectangle.right(), rectangle.bottom(), 0.0f),
            Vector3(rectangle.right(), rectangle.top(), 0.0f),
            Vector3(rectangle.left(), rectangle.top(), 0.0f)
        };

        uint8_t inCorners = 0;

        for (Vector3& corner : corners)
        {
            inverse.transformPoint(corner);

            if (corner.x >= _boundingBox.min.x &&
                corner.x <= _boundingBox.max.x &&
                corner.y >= _boundingBox.min.y &&
                corner.y <= _boundingBox.max.y)
            {
                return true;
            }

            if (corner.x < _boundingBox.min.x && corner.y < _boundingBox.min.y) inCorners |= 0x01;
            if (corner.x > _boundingBox.max.x && corner.y < _boundingBox.min.y) inCorners |= 0x02;
            if (corner.x > _boundingBox.max.x && corner.y > _boundingBox.max.y) inCorners |= 0x04;
            if (corner.x < _boundingBox.min.x && corner.y > _boundingBox.max.y) inCorners |= 0x08;
        }

        // bounding box is bigger than rectangle
        if (inCorners == 0x0F)
        {
            return true;
        }

        Vector2 boundingBoxCorners[4] = {
            Vector2(_boundingBox.min),
            Vector2(_boundingBox.max.x, _boundingBox.min.y),
            Vector2(_boundingBox.max),
            Vector2(_boundingBox.min.x, _boundingBox.max.y)
        };

        for (uint32_t current = 0; current < 4; ++current)
        {
            uint32_t next = (current == 3) ? 0 : current + 1;

            if (linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[0], boundingBoxCorners[1]) || // left
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[1], boundingBoxCorners[2]) || // top
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[2], boundingBoxCorners[3]) || // right
                linesIntersect(Vector2(corners[current].x, corners[current].y), Vector2(corners[next].x, corners[next].y), boundingBoxCorners[3], boundingBoxCorners[0])) // bottom
            {
                return true;
            }
        }

        return false;
    }

    const Matrix4& Node::getTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        return _transform;
    }

    const Matrix4& Node::getInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        if (_inverseTransformDirty)
        {
            calculateInverseTransform();
        }

        return _inverseTransform;
    }

    void Node::updateTransform(const Matrix4& parentTransform)
    {
        _parentTransform = parentTransform;
        _transformDirty = _inverseTransformDirty = true;
    }

    Vector2 Node::convertWorldToLocal(const Vector2& position) const
    {
        Vector3 localPosition = position;

        const Matrix4& inverseTransform = getInverseTransform();
        inverseTransform.transformPoint(localPosition);

        return Vector2(localPosition.x, localPosition.y);
    }

    Vector2 Node::convertLocalToWorld(const Vector2& position) const
    {
        Vector3 worldPosition = position;

        const Matrix4& transform = getTransform();
        transform.transformPoint(worldPosition);

        return Vector2(worldPosition.x, worldPosition.y);
    }

    bool Node::checkVisibility() const
    {
        if (const LayerPtr& layer = _layer.lock())
        {
            if (_boundingBox.isEmpty())
            {
                return true;
            }

            return Engine::getInstance()->getRenderer()->checkVisibility(getTransform(), _boundingBox, layer->getCamera());
        }

        return false;
    }

    void Node::animate(const AnimatorPtr& animator)
    {
        _currentAnimator = animator;

        if (_currentAnimator)
        {
            _currentAnimator->start(std::static_pointer_cast<Node>(shared_from_this()));
        }
    }

    void Node::stopAnimation()
    {
        if (_currentAnimator)
        {
            _currentAnimator->stop();
            _currentAnimator.reset();
        }
    }

    void Node::calculateLocalTransform() const
    {
        Matrix4 translation;
        translation.translate(Vector3(_position.x, _position.y, 0.0f));

        Matrix4 rotation;
        rotation.rotate(Vector3(0.0f, 0.0f, -1.0f), _rotation);

        Vector3 realScale = Vector3(_scale.x * (_flipX ? -1.0f : 1.0f),
                                    _scale.y * (_flipY ? -1.0f : 1.0f),
                                    1.0f);

        Matrix4 scale;
        scale.scale(realScale);

        _localTransform = translation * rotation * scale;

        _localTransformDirty = false;
    }

    void Node::calculateTransform() const
    {
        if (_localTransformDirty)
        {
            calculateLocalTransform();
        }

        _transform = _parentTransform * _localTransform;
        _transformDirty = false;

        for (const NodePtr& child : _children)
        {
            child->updateTransform(_transform);
        }
    }

    void Node::calculateInverseTransform() const
    {
        if (_transformDirty)
        {
            calculateTransform();
        }

        _inverseTransform = _transform;
        _inverseTransform.invert();
        _inverseTransformDirty = false;
    }
}
<|endoftext|>


<commit_before>/*******************************************************************************
 * benchmarks/word_count/word_count.cpp
 *
 * Runner program for WordCount example. See thrill/examples/word_count.hpp for
 * the source to the example.
 *
 * Part of Project Thrill.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <thrill/api/dia.hpp>
#include <thrill/common/cmdline_parser.hpp>
#include <thrill/common/logger.hpp>
#include <thrill/examples/word_count.hpp>

using WordCountPair = std::pair<std::string, size_t>;
using namespace thrill; // NOLINT

int main(int argc, char* argv[]) {

    common::CmdlineParser clp;

    clp.SetVerboseProcess(false);

    std::string input;
    clp.AddParamString("input", input,
                       "input file pattern");

    std::string output;
    clp.AddParamString("output", output,
                       "output file pattern");

    if (!clp.Process(argc, argv)) {
        return -1;
    }

    clp.PrintResult();

    auto start_func =
        [input, output](api::Context& ctx) {
		auto input_dia = ReadLines(ctx, input);
		auto counted_words = examples::WordCount(input_dia);
		counted_words.Map(
        [](const WordCountPair& wc) {
            return wc.first + ": " + std::to_string(wc.second);
			}).WriteLinesMany(output);
        };

    return api::Run(start_func);
}

/******************************************************************************/
<commit_msg>give references to start_func<commit_after>/*******************************************************************************
 * benchmarks/word_count/word_count.cpp
 *
 * Runner program for WordCount example. See thrill/examples/word_count.hpp for
 * the source to the example.
 *
 * Part of Project Thrill.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#include <thrill/api/dia.hpp>
#include <thrill/common/cmdline_parser.hpp>
#include <thrill/common/logger.hpp>
#include <thrill/examples/word_count.hpp>

using WordCountPair = std::pair<std::string, size_t>;
using namespace thrill; // NOLINT

int main(int argc, char* argv[]) {

    common::CmdlineParser clp;

    clp.SetVerboseProcess(false);

    std::string input;
    clp.AddParamString("input", input,
                       "input file pattern");

    std::string output;
    clp.AddParamString("output", output,
                       "output file pattern");

    if (!clp.Process(argc, argv)) {
        return -1;
    }

    clp.PrintResult();

    auto start_func =
        [&input, &output](api::Context& ctx) {
		auto input_dia = ReadLines(ctx, input);
		auto counted_words = examples::WordCount(input_dia);
		counted_words.Map(
        [](const WordCountPair& wc) {
            return wc.first + ": " + std::to_string(wc.second);
			}).WriteLinesMany(output);
        };

    return api::Run(start_func);
}

/******************************************************************************/
<|endoftext|>


<commit_before>/* The MIT License (MIT)

Copyright (c) 2015 Gabriel Corona

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "dnsfwd.hpp"

#include <cstdint>
#include <cstring>

#include <boost/intrusive/slist.hpp>
#include <boost/intrusive/slist_hook.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/intrusive/set_hook.hpp>

#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/connect.hpp>

#include <memory>

namespace dnsfwd {

client::client(boost::asio::io_service& io_service, service& service)
  : io_service_(&io_service), service_(&service), socket_(io_service)
{
  LOG(DEBUG) << "New client\n";
}

client::~client()
{
  by_client_id_.clear();
  while (!queue_.empty()) {
    auto i = queue_.begin();
    message& c = *i;
    queue_.erase(i);
    delete &c;
  }
  LOG(DEBUG) << "Client deleted\n";
}

void client::clear(std::chrono::steady_clock::time_point time)
{return;
  size_t count = 0;
  while (!queue_.empty()) {
    auto i = queue_.begin();
    message& c = *i;
    if (c.timestamp_ > time)
      break;
    queue_.erase(i);
    by_client_id_.erase(by_client_id_.iterator_to(c));
    delete &c;
    count++;
  }
  if (count)
    LOG(DEBUG) << count << " requests dropped, "
      << by_client_id_.size() << " remaining\n";
}

bool client::add_request(std::unique_ptr<message>& context)
{
  if (this->context_) {
    return false;
  } else {
    context_ = std::move(context);
    this->send();
    return true;
  }
}

std::uint16_t client::random_client_id()
{
  while (1) {
    std::uint16_t id = service_->random_id();
    auto i = this->by_client_id_.find(id, order_message_by_client_id());
    if (i == this->by_client_id_.end())
      return id;
  }
}

void client::connect()
{
  // TODO, make this async
  LOG(DEBUG) << "Connecting\n";

  dnsfwd::endpoint const& endpoint = service_->tcp_connect_endpoints().at(0);

  boost::asio::ip::tcp::resolver resolver(*io_service_);
  boost::asio::ip::tcp::resolver::query query(
    endpoint.name, endpoint.port.empty() ? "domain" : endpoint.port);
  boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
    resolver.resolve(query);
  boost::asio::ip::tcp::resolver::iterator end;
  if (boost::asio::connect(socket_, endpoint_iterator) == end) {
    LOG(ERR) << "Could not connect\n";
    this->reset();
  } else {
    LOG(DEBUG) << "Connected\n";

    boost::asio::ip::tcp::no_delay no_delay(true);
    socket_.set_option(no_delay);

    this->start_receive();
    this->send();
  }
}

void client::send()
{
  if (!context_) {
    context_ = service_->unqueue();
    if (!context_)
      return;
  }

  if (socket_.is_open()) {

    this->clear(std::chrono::steady_clock::now() + service_->time_to_live());

    // Choose a client ID:
    context_->client_id_ = this->random_client_id();
    context_->id(context_->client_id_);

    LOG(DEBUG) << "Forwarding request\n";
    boost::asio::async_write(
      socket_,
      context_->vc_buffer(),
      boost::bind(
        &client::on_send,
        this->shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred
      )
    );
  } else {
    reset();
  }
}

void client::on_send(const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error) {
    LOG(ERR) << "Forward request: error " << error << '\n';
    reset();
  } else if (bytes_transferred != context_->size_ + sizeof(uint16_t)) {
    LOG(ERR) << "Forward request: transfer incomplete\n";
    reset();
  } else {
    LOG(DEBUG) << "Request forwarded\n";
    context_->timestamp_ = std::chrono::steady_clock::now();
    this->by_client_id_.insert(*context_);
    this->queue_.push_back(*context_);
    context_.release();
    this->send();
  }
}

void client::start_receive()
{
  boost::asio::async_read(
    socket_,
    boost::asio::buffer(&response_size_, sizeof(response_size_)),
    boost::bind(
      &client::on_message_size,
      this->shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred
    )
  );
}

void client::on_message_size(const boost::system::error_code& error, std::size_t size)
{
  if (error) {
    LOG(DEBUG) << "Reply reception error: " << error << '\n';
    reset();
  } else if (size != sizeof(uint16_t)) {
    LOG(ERR) << "Reply reception incomplete\n";
    reset();
  } else {
    LOG(DEBUG) << "Reply size received\n";
    this->response_size_ = ntohs(this->response_size_);
    buffer_.resize(this->response_size_);
    boost::asio::async_read(
      socket_,
      boost::asio::buffer(buffer_.data(), this->response_size_),
      boost::bind(
        &client::on_message,
        this->shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred
      )
    );
  }
}

void client::on_message(const boost::system::error_code& error, std::size_t size)
{
  if (error) {
    LOG(ERR) << "Reply reception error: " << error << '\n';
    reset();
  } else if (size != this->response_size_) {
    LOG(ERR) << "Reply reception incomplete\n";
    reset();
  } else if (size < MIN_MESSAGE_SIZE) {
    LOG(ERR) << "Reply received but too small\n";
    this->start_receive();
  } else {
    std::uint16_t client_id;
    std::memcpy(&client_id, buffer_.data(), sizeof(client_id));
    by_client_id_type::iterator i = by_client_id_.find(client_id,
      order_message_by_client_id());
    by_client_id_type::iterator end;
    if (i == end) {
      LOG(ERR) << "Reply received not expected\n";
      this->start_receive();
      return;
    }
    LOG(DEBUG) << "Reply received\n";
    message& c = *i;
    assert(c.client_id_ == client_id);
    std::memcpy(buffer_.data(), &c.server_id_, sizeof(c.server_id_));
    c.server_->send_response(std::move(buffer_), c.endpoint_);
    by_client_id_.erase(i);
    queue_.erase(queue_.iterator_to(c));
    delete &c;
    this->start_receive();
  }
}

void client::reset()
{
  if (socket_.is_open()) {
    socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    socket_.close();
  }
  service_->unregister(this->shared_from_this());
}

}
<commit_msg>Fix the removal of old messages<commit_after>/* The MIT License (MIT)

Copyright (c) 2015 Gabriel Corona

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

#include "dnsfwd.hpp"

#include <cstdint>
#include <cstring>

#include <boost/intrusive/slist.hpp>
#include <boost/intrusive/slist_hook.hpp>
#include <boost/intrusive/set.hpp>
#include <boost/intrusive/set_hook.hpp>

#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/connect.hpp>

#include <memory>

namespace dnsfwd {

client::client(boost::asio::io_service& io_service, service& service)
  : io_service_(&io_service), service_(&service), socket_(io_service)
{
  LOG(DEBUG) << "New client\n";
}

client::~client()
{
  by_client_id_.clear();
  while (!queue_.empty()) {
    auto i = queue_.begin();
    message& c = *i;
    queue_.erase(i);
    delete &c;
  }
  LOG(DEBUG) << "Client deleted\n";
}

void client::clear(std::chrono::steady_clock::time_point time)
{
  size_t count = 0;
  while (!queue_.empty()) {
    auto i = queue_.begin();
    message& c = *i;
    if (c.timestamp_ > time)
      break;
    queue_.erase(i);
    by_client_id_.erase(by_client_id_.iterator_to(c));
    delete &c;
    count++;
  }
  if (count)
    LOG(DEBUG) << count << " requests dropped, "
      << by_client_id_.size() << " remaining\n";
}

bool client::add_request(std::unique_ptr<message>& context)
{
  if (this->context_) {
    return false;
  } else {
    context_ = std::move(context);
    this->send();
    return true;
  }
}

std::uint16_t client::random_client_id()
{
  while (1) {
    std::uint16_t id = service_->random_id();
    auto i = this->by_client_id_.find(id, order_message_by_client_id());
    if (i == this->by_client_id_.end())
      return id;
  }
}

void client::connect()
{
  // TODO, make this async
  LOG(DEBUG) << "Connecting\n";

  dnsfwd::endpoint const& endpoint = service_->tcp_connect_endpoints().at(0);

  boost::asio::ip::tcp::resolver resolver(*io_service_);
  boost::asio::ip::tcp::resolver::query query(
    endpoint.name, endpoint.port.empty() ? "domain" : endpoint.port);
  boost::asio::ip::tcp::resolver::iterator endpoint_iterator =
    resolver.resolve(query);
  boost::asio::ip::tcp::resolver::iterator end;
  if (boost::asio::connect(socket_, endpoint_iterator) == end) {
    LOG(ERR) << "Could not connect\n";
    this->reset();
  } else {
    LOG(DEBUG) << "Connected\n";

    boost::asio::ip::tcp::no_delay no_delay(true);
    socket_.set_option(no_delay);

    this->start_receive();
    this->send();
  }
}

void client::send()
{
  if (!context_) {
    context_ = service_->unqueue();
    if (!context_)
      return;
  }

  if (socket_.is_open()) {

    this->clear(std::chrono::steady_clock::now() - service_->time_to_live());

    // Choose a client ID:
    context_->client_id_ = this->random_client_id();
    context_->id(context_->client_id_);

    LOG(DEBUG) << "Forwarding request\n";
    boost::asio::async_write(
      socket_,
      context_->vc_buffer(),
      boost::bind(
        &client::on_send,
        this->shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred
      )
    );
  } else {
    reset();
  }
}

void client::on_send(const boost::system::error_code& error, std::size_t bytes_transferred)
{
  if (error) {
    LOG(ERR) << "Forward request: error " << error << '\n';
    reset();
  } else if (bytes_transferred != context_->size_ + sizeof(uint16_t)) {
    LOG(ERR) << "Forward request: transfer incomplete\n";
    reset();
  } else {
    LOG(DEBUG) << "Request forwarded\n";
    context_->timestamp_ = std::chrono::steady_clock::now();
    this->by_client_id_.insert(*context_);
    this->queue_.push_back(*context_);
    context_.release();
    this->send();
  }
}

void client::start_receive()
{
  boost::asio::async_read(
    socket_,
    boost::asio::buffer(&response_size_, sizeof(response_size_)),
    boost::bind(
      &client::on_message_size,
      this->shared_from_this(),
      boost::asio::placeholders::error,
      boost::asio::placeholders::bytes_transferred
    )
  );
}

void client::on_message_size(const boost::system::error_code& error, std::size_t size)
{
  if (error) {
    LOG(DEBUG) << "Reply reception error: " << error << '\n';
    reset();
  } else if (size != sizeof(uint16_t)) {
    LOG(ERR) << "Reply reception incomplete\n";
    reset();
  } else {
    LOG(DEBUG) << "Reply size received\n";
    this->response_size_ = ntohs(this->response_size_);
    buffer_.resize(this->response_size_);
    boost::asio::async_read(
      socket_,
      boost::asio::buffer(buffer_.data(), this->response_size_),
      boost::bind(
        &client::on_message,
        this->shared_from_this(),
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred
      )
    );
  }
}

void client::on_message(const boost::system::error_code& error, std::size_t size)
{
  if (error) {
    LOG(ERR) << "Reply reception error: " << error << '\n';
    reset();
  } else if (size != this->response_size_) {
    LOG(ERR) << "Reply reception incomplete\n";
    reset();
  } else if (size < MIN_MESSAGE_SIZE) {
    LOG(ERR) << "Reply received but too small\n";
    this->start_receive();
  } else {
    std::uint16_t client_id;
    std::memcpy(&client_id, buffer_.data(), sizeof(client_id));
    by_client_id_type::iterator i = by_client_id_.find(client_id,
      order_message_by_client_id());
    by_client_id_type::iterator end = by_client_id_.end();
    if (i == end) {
      LOG(ERR) << "Reply received not expected\n";
      this->start_receive();
      return;
    }
    LOG(DEBUG) << "Reply received\n";
    message& c = *i;
    assert(c.client_id_ == client_id);
    std::memcpy(buffer_.data(), &c.server_id_, sizeof(c.server_id_));
    c.server_->send_response(std::move(buffer_), c.endpoint_);
    by_client_id_.erase(i);
    queue_.erase(queue_.iterator_to(c));
    delete &c;
    this->start_receive();
  }
}

void client::reset()
{
  if (socket_.is_open()) {
    socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
    socket_.close();
  }
  service_->unregister(this->shared_from_this());
}

}
<|endoftext|>


<commit_before>// Copyright 2013 Sean McKenna
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

// a ray tracer in C++

// libraries, namespace
#include <thread>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <random>
#include "library/loadXML.cpp"
#include "library/scene.cpp"
using namespace std;

// scene to load (project #) + all ray tracing options & settings
string xml = "scenes/prj12.xml";
bool printXML = false;
bool zBuffer = false;
bool sampleCount = false;
int bounceCount = 5;
int sampleMin = 4;
int sampleMax = 32;
float sampleThreshold = 0.001;
int shadowMin = 8;
int shadowMax = 32;
bool gammaCorr = true;
bool globalIllum = false;
bool irradCache = false;
int samplesGI = 128;
bool invSqFO = true;
bool photonMap = true;
int samplesPM = 1000000;

// variables for ray tracing
int w;
int h;
int size;
Color24* img;
float* zImg;
float* sampleImg;
IrradianceMap im;

// variables for anti-aliasing brightness calculations (XYZ, Lab)
float perR = 0.2126;
float perG = 0.7152;
float perB = 0.0722;
float Ycutoff = pow(6.0 / 29.0, 3.0);
float Yprecalc = (1.0 / 3.0) * pow(29.0 / 6.0, 2.0);

// setup threading
static const int numThreads = 8;
void rayTracing(int i);
void irradianceCache(int i, int m, LightList lightCache);

// for camera ray generation
void cameraRayVars();
float imageDistance = 1.0;
Point *imageTopLeftV;
Point *dXV;
Point *dYV;
Point *dVx;
Point *dVy;
Point firstPixel;
Transformation* c;
Point cameraRay(float pX, float pY, Point offset);

// ray tracer
int main(){

  // load scene: root node, camera, image (and set shadow casting variables)
  loadScene(xml, printXML, shadowMin, shadowMax, globalIllum, irradCache, samplesGI, invSqFO);

  // set the scene as the root node
  setScene(rootNode);

  // set variables for ray tracing
  w = render.getWidth();
  h = render.getHeight();
  size = render.getSize();
  img = render.getRender();
  zImg = render.getZBuffer();
  sampleImg = render.getSample();
  if(globalIllum && irradCache)
    im.Initialize(w, h);

  // set variables for generating camera rays
  cameraRayVars();

  // compute an irradiance cache for global illumination
  if(globalIllum && irradCache){

    // caching light list
    LightList lightCache;
    lightCache.deleteAll();
    string name = "indirect";
    IrradianceCacheLight *l = new IrradianceCacheLight();
    Light *light = NULL;
    l->setLightList(&lights);
    l->setEnvironment(environment);
    l->setSamples(samplesGI);
    light = l;
    light->setName(name);
    lightCache.push_back(light);

    // subdivide our image to compute indirect illumination
    bool subdivide = true;
    while(subdivide){

      // check if we are on final subdivide
      if(im.GetSubdivLevel() == 0)
        subdivide = false;

      // calculate indirect illumination
      int cnt = 0;
      for(int i = 0; i < im.GetDataCount(); i++){

        // grab position on image plane
        float px;
        float py;
        im.GetPosition(i, px, py);

        // get the pixel number
        int pixel = px + py * w;

        // compute the ray tracing cache (if needs to be set)
        if(!im.IsValid(i))
          irradianceCache(pixel, i, lightCache);
      }

      // subdivide (if necessary)
      if(subdivide)
        im.Subdivide();
    }
  }

  // start ray tracing loop (in parallel with threads)
  thread t[numThreads];
  for(int i = 0; i < numThreads; i++)
    t[i] = thread(rayTracing, i);

  // when finished, join all threads back to main
  for(int i = 0; i < numThreads; i++)
    t[i].join();

  // output ray-traced image & z-buffer & sample count image (if set)
  render.save("images/image.ppm");
  if(zBuffer){
    render.computeZImage();
    render.saveZImage("images/imageZ.ppm");
  }
  if(sampleCount){
    render.computeSampleImage();
    render.saveSampleImage("images/imageSample.ppm");
  }
}

// ray tracing loop (for an individual pixel)
void rayTracing(int i){

  // initial starting pixel
  int pixel = i;

  // setup random generator for anti-aliasing & depth-of-field
  mt19937 rnd;
  uniform_real_distribution<float> dist{0.0, 1.0};

  // create new light list for thread
  LightList threadLights;
  threadLights.deleteAll();
  threadLights = lights;

  // if necessary, add new irradiance map light
  if(globalIllum && irradCache){
    IrradianceMapLight *l = new IrradianceMapLight();
    string name = "irradianceMap";
    Light *light = NULL;
    light = l;
    light->setName(name);
    threadLights.push_back(light);
  }

  // thread continuation condition
  while(pixel < size){

    // number of samples
    int s = 0;

    // establish pixel location (center)
    float pX = pixel % w;
    float pY = pixel / w;

    // color values to store across samples
    Color col;
    Color colAvg;
    float zAvg = 0.0;
    float rVar = 0.0;
    float gVar = 0.0;
    float bVar = 0.0;
    float var = sampleThreshold;
    float brightness = 0.0;

    // random rotation of Halton sequence on circle of confusion
    float dcR = dist(rnd) * 2.0 * M_PI;

    // if necessary, update irradiance map light with indirect color
    if(globalIllum && irradCache){
      Color c;
      float z;
      Point N;
      ColorIM cim;
      cim.c = c;
      cim.z = z;
      cim.N = N;
      im.Eval(cim, pX, pY);
      int index = threadLights.size() - 1;
      Light *light = threadLights[index];
      light->setColor(cim.c);
    }

    // compute multi-adaptive sampling for each pixel (anti-aliasing)
    while(s < sampleMin || (s != sampleMax && (rVar * perR > var + brightness * var || gVar * perG > var + brightness * var || bVar * perB > var + brightness * var))){

      // grab Halton sequence to shift point by on image plane
      float dpX = centerHalton(Halton(s, 3));
      float dpY = centerHalton(Halton(s, 2));

      // grab Halton sequence to shift point along circle of confusion
      float dcS = sqrt(Halton(s, 2)) * camera.dof;

      // grab Halton sequence to shift point around circle of confusion
      float dcT = Halton(s, 3) * 2.0 * M_PI;

      // compute the offset for depth of field sampling
      Point posOffset = (*dVx * cos(dcR + dcT) + *dVy * sin(dcR + dcT)) * dcS;

      // transform ray into world space (offset by Halton seqeunce for sampling)
      Point rayDir = cameraRay(pX + dpX, pY + dpY, posOffset);
      Cone *ray = new Cone();
      ray->pos = camera.pos + c->transformFrom(posOffset);
      ray->dir = c->transformFrom(rayDir);
      ray->radius = 0.0;
      ray->tan = dXV->x / (2.0 * imageDistance);

      // traverse through scene DOM
      // transform rays into model space
      // detect ray intersections and get back HitInfo
      HitInfo hi = HitInfo();
      bool hit = traceRay(*ray, hi);

      // update z-buffer, if necessary
      if(zBuffer)
        zAvg = (zAvg * s + hi.z) / (float) (s + 1);

      // if hit, get the node's material
      if(hit){
        Node *n = hi.node;
        Material *m;
        if(n)
          m = n->getMaterial();

        // if there is a material, shade the pixel
        // 5-passes for reflections and refractions
        if(m)
          col = m->shade(*ray, hi, threadLights, bounceCount);

        // otherwise color it white (as a hit)
        else
          col.Set(0.929, 0.929, 0.929);

      // if we hit nothing, draw the background
      }else{
        Point p = Point((float) pX / w, (float) pY / h, 0.0);
        Color b = background.sample(p);
        col = b;
      }

      // compute average color
      float rAvg = (colAvg.r * s + col.r) / (float) (s + 1);
      float gAvg = (colAvg.g * s + col.g) / (float) (s + 1);
      float bAvg = (colAvg.b * s + col.b) / (float) (s + 1);
      colAvg.Set(rAvg, gAvg, bAvg);

      // compute color variances
      rVar = (rVar * s + (col.r - rAvg) * (col.r - rAvg)) / (float) (s + 1);
      gVar = (gVar * s + (col.g - gAvg) * (col.g - gAvg)) / (float) (s + 1);
      bVar = (bVar * s + (col.b - bAvg) * (col.b - bAvg)) / (float) (s + 1);

      // calculate and update brightness average using XYZ and Lab space
      float Y = perR * rAvg + perG * gAvg + perB * bAvg;
      float Y13 = Y;
      if(Y13 > Ycutoff)
        Y13 = pow(Y13, 1.0 / 3.0);
      else
        Y13 = Yprecalc * Y13 + (4.0 / 29.0);
      brightness = (116.0 * Y13 - 16.0) / 100.0;

      // increment sample count
      s++;

      // watch for errors at any individual sample, terminate thread if so
      if(colAvg[0] != colAvg[0] || colAvg[1] != colAvg[1] || colAvg[2] != colAvg[2]){
        cout << "ERROR - pixel " << pixel << " & sample " << s << endl;
        s = sampleMax;
        pixel = size;
      }
    }

    // gamma correction
    if(gammaCorr){
      colAvg.r = pow(colAvg.r, 1.0 / 2.2);
      colAvg.g = pow(colAvg.g, 1.0 / 2.2);
      colAvg.b = pow(colAvg.b, 1.0 / 2.2);
    }

    // color the pixel image
    img[pixel] = Color24(colAvg);

    // update the z-buffer image, if necessary
    if(zBuffer)
      zImg[pixel] = zAvg;

    // update the sample count image, if necessary
    if(sampleCount)
      sampleImg[pixel] = s;

    // re-assign next pixel (naive, but works)
    pixel += numThreads;
  }
}

// irradiance cache (for global illumination & indirect lighting at a single pixel)
void irradianceCache(int i, int m, LightList lightCache){

  // establish pixel location (center)
  float pX = i % w;
  float pY = i / w;

  // color value for cache
  Color col;

  // set offset to zero
  Point posOffset = Point(0,0,0);

  // transform ray into world space
  Point rayDir = cameraRay(pX, pY, posOffset);
  Cone *ray = new Cone();
  ray->pos = camera.pos;
  ray->dir = c->transformFrom(rayDir);
  ray->radius = 0.0;
  ray->tan = dXV->x / (2.0 * imageDistance);

  // traverse through scene DOM
  // transform rays into model space
  // detect ray intersections and get back HitInfo
  HitInfo hi = HitInfo();
  bool hit = traceRay(*ray, hi);

  // if hit, get the node's material
  if(hit){
    Node *n = hi.node;
    Material *m;
    if(n)
      m = n->getMaterial();

    // if there is a material, get our indirect light color for cache
    if(m)
      col = m->shade(*ray, hi, lightCache);
  }

  // set our irradiance map variables
  ColorIM cim;
  cim.c = col;
  cim.z = hi.z;
  cim.N = hi.n;
  im.Set(m, cim);
}

// create variables for camera ray generation
void cameraRayVars(){
  float fov = camera.fov * M_PI / 180.0;
  float aspectRatio = (float) w / (float) h;
  imageDistance = camera.focalDist;
  float imageTipY = imageDistance * tan(fov / 2.0);
  float imageTipX = imageTipY * aspectRatio;
  float dX = (2.0 * imageTipX) / (float) w;
  float dY = (2.0 * imageTipY) / (float) h;
  imageTopLeftV = new Point(-imageTipX, imageTipY, -imageDistance);
  dXV = new Point(dX, 0.0, 0.0);
  dYV = new Point(0.0, -dY, 0.0);
  firstPixel = *imageTopLeftV + (*dXV * 0.5) + (*dYV * 0.5);

  // set up camera transformation (only need to rotate coordinates)
  c = new Transformation();
  Matrix *rotate = new cyMatrix3f();
  rotate->Set(camera.cross, camera.up, -camera.dir);
  c->transform(*rotate);

  // get normalized rays on the focal plane
  dVx = new Point(1.0, 0.0, 0.0);
  dVy = new Point(0.0, 1.0, 0.0);
}

// compute camera ray direction
Point cameraRay(float pX, float pY, Point offset){
  Point ray = firstPixel + (*dXV * pX) + (*dYV * pY) - offset;
  ray.Normalize();
  return ray;
}
<commit_msg>begin adding photon map generation to our ray tracer<commit_after>// Copyright 2013 Sean McKenna
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
//

// a ray tracer in C++

// libraries, namespace
#include <thread>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <cmath>
#include <random>
#include "library/loadXML.cpp"
#include "library/scene.cpp"
using namespace std;

// scene to load (project #) + all ray tracing options & settings
string xml = "scenes/prj12.xml";
bool printXML = false;
bool zBuffer = false;
bool sampleCount = false;
int bounceCount = 5;
int sampleMin = 4;
int sampleMax = 32;
float sampleThreshold = 0.001;
int shadowMin = 8;
int shadowMax = 32;
bool gammaCorr = true;
bool globalIllum = false;
bool irradCache = false;
int samplesGI = 128;
bool invSqFO = true;
bool photonMap = true;
int samplesPM = 1000000;

// variables for ray tracing
int w;
int h;
int size;
Color24* img;
float* zImg;
float* sampleImg;
IrradianceMap im;
PhotonMap *pm;

// variables for anti-aliasing brightness calculations (XYZ, Lab)
float perR = 0.2126;
float perG = 0.7152;
float perB = 0.0722;
float Ycutoff = pow(6.0 / 29.0, 3.0);
float Yprecalc = (1.0 / 3.0) * pow(29.0 / 6.0, 2.0);

// setup threading
static const int numThreads = 8;
void rayTracing(int i);
void irradianceCache(int i, int m, LightList lightCache);

// for camera ray generation
void cameraRayVars();
float imageDistance = 1.0;
Point *imageTopLeftV;
Point *dXV;
Point *dYV;
Point *dVx;
Point *dVy;
Point firstPixel;
Transformation* c;
Point cameraRay(float pX, float pY, Point offset);

// ray tracer
int main(){

  // load scene: root node, camera, image (and set shadow casting variables)
  loadScene(xml, printXML, shadowMin, shadowMax, globalIllum, irradCache, samplesGI, invSqFO);

  // set the scene as the root node
  setScene(rootNode);

  // set variables for ray tracing
  w = render.getWidth();
  h = render.getHeight();
  size = render.getSize();
  img = render.getRender();
  zImg = render.getZBuffer();
  sampleImg = render.getSample();
  if(globalIllum && irradCache)
    im.Initialize(w, h);

  // set variables for generating camera rays
  cameraRayVars();

  // compute an irradiance cache for global illumination
  if(globalIllum && irradCache){

    // caching light list
    LightList lightCache;
    lightCache.deleteAll();
    string name = "indirect";
    IrradianceCacheLight *l = new IrradianceCacheLight();
    Light *light = NULL;
    l->setLightList(&lights);
    l->setEnvironment(environment);
    l->setSamples(samplesGI);
    light = l;
    light->setName(name);
    lightCache.push_back(light);

    // subdivide our image to compute indirect illumination
    bool subdivide = true;
    while(subdivide){

      // check if we are on final subdivide
      if(im.GetSubdivLevel() == 0)
        subdivide = false;

      // calculate indirect illumination
      int cnt = 0;
      for(int i = 0; i < im.GetDataCount(); i++){

        // grab position on image plane
        float px;
        float py;
        im.GetPosition(i, px, py);

        // get the pixel number
        int pixel = px + py * w;

        // compute the ray tracing cache (if needs to be set)
        if(!im.IsValid(i))
          irradianceCache(pixel, i, lightCache);
      }

      // subdivide (if necessary)
      if(subdivide)
        im.Subdivide();
    }
  }

  // compute a photon map for global illumination
  if(photonMap){

    // initialize photon map
    pm = createPhotonMap(samplesPM);

    // fill our photon map
    while(pm->stored_photons < samplesPM){

      //

    }
  }

  // start ray tracing loop (in parallel with threads)
  thread t[numThreads];
  for(int i = 0; i < numThreads; i++)
    t[i] = thread(rayTracing, i);

  // when finished, join all threads back to main
  for(int i = 0; i < numThreads; i++)
    t[i].join();

  // output ray-traced image & z-buffer & sample count image (if set)
  render.save("images/image.ppm");
  if(zBuffer){
    render.computeZImage();
    render.saveZImage("images/imageZ.ppm");
  }
  if(sampleCount){
    render.computeSampleImage();
    render.saveSampleImage("images/imageSample.ppm");
  }
}

// ray tracing loop (for an individual pixel)
void rayTracing(int i){

  // initial starting pixel
  int pixel = i;

  // setup random generator for anti-aliasing & depth-of-field
  mt19937 rnd;
  uniform_real_distribution<float> dist{0.0, 1.0};

  // create new light list for thread
  LightList threadLights;
  threadLights.deleteAll();
  threadLights = lights;

  // if necessary, add new irradiance map light
  if(globalIllum && irradCache){
    IrradianceMapLight *l = new IrradianceMapLight();
    string name = "irradianceMap";
    Light *light = NULL;
    light = l;
    light->setName(name);
    threadLights.push_back(light);
  }

  // thread continuation condition
  while(pixel < size){

    // number of samples
    int s = 0;

    // establish pixel location (center)
    float pX = pixel % w;
    float pY = pixel / w;

    // color values to store across samples
    Color col;
    Color colAvg;
    float zAvg = 0.0;
    float rVar = 0.0;
    float gVar = 0.0;
    float bVar = 0.0;
    float var = sampleThreshold;
    float brightness = 0.0;

    // random rotation of Halton sequence on circle of confusion
    float dcR = dist(rnd) * 2.0 * M_PI;

    // if necessary, update irradiance map light with indirect color
    if(globalIllum && irradCache){
      Color c;
      float z;
      Point N;
      ColorIM cim;
      cim.c = c;
      cim.z = z;
      cim.N = N;
      im.Eval(cim, pX, pY);
      int index = threadLights.size() - 1;
      Light *light = threadLights[index];
      light->setColor(cim.c);
    }

    // compute multi-adaptive sampling for each pixel (anti-aliasing)
    while(s < sampleMin || (s != sampleMax && (rVar * perR > var + brightness * var || gVar * perG > var + brightness * var || bVar * perB > var + brightness * var))){

      // grab Halton sequence to shift point by on image plane
      float dpX = centerHalton(Halton(s, 3));
      float dpY = centerHalton(Halton(s, 2));

      // grab Halton sequence to shift point along circle of confusion
      float dcS = sqrt(Halton(s, 2)) * camera.dof;

      // grab Halton sequence to shift point around circle of confusion
      float dcT = Halton(s, 3) * 2.0 * M_PI;

      // compute the offset for depth of field sampling
      Point posOffset = (*dVx * cos(dcR + dcT) + *dVy * sin(dcR + dcT)) * dcS;

      // transform ray into world space (offset by Halton seqeunce for sampling)
      Point rayDir = cameraRay(pX + dpX, pY + dpY, posOffset);
      Cone *ray = new Cone();
      ray->pos = camera.pos + c->transformFrom(posOffset);
      ray->dir = c->transformFrom(rayDir);
      ray->radius = 0.0;
      ray->tan = dXV->x / (2.0 * imageDistance);

      // traverse through scene DOM
      // transform rays into model space
      // detect ray intersections and get back HitInfo
      HitInfo hi = HitInfo();
      bool hit = traceRay(*ray, hi);

      // update z-buffer, if necessary
      if(zBuffer)
        zAvg = (zAvg * s + hi.z) / (float) (s + 1);

      // if hit, get the node's material
      if(hit){
        Node *n = hi.node;
        Material *m;
        if(n)
          m = n->getMaterial();

        // if there is a material, shade the pixel
        // 5-passes for reflections and refractions
        if(m)
          col = m->shade(*ray, hi, threadLights, bounceCount);

        // otherwise color it white (as a hit)
        else
          col.Set(0.929, 0.929, 0.929);

      // if we hit nothing, draw the background
      }else{
        Point p = Point((float) pX / w, (float) pY / h, 0.0);
        Color b = background.sample(p);
        col = b;
      }

      // compute average color
      float rAvg = (colAvg.r * s + col.r) / (float) (s + 1);
      float gAvg = (colAvg.g * s + col.g) / (float) (s + 1);
      float bAvg = (colAvg.b * s + col.b) / (float) (s + 1);
      colAvg.Set(rAvg, gAvg, bAvg);

      // compute color variances
      rVar = (rVar * s + (col.r - rAvg) * (col.r - rAvg)) / (float) (s + 1);
      gVar = (gVar * s + (col.g - gAvg) * (col.g - gAvg)) / (float) (s + 1);
      bVar = (bVar * s + (col.b - bAvg) * (col.b - bAvg)) / (float) (s + 1);

      // calculate and update brightness average using XYZ and Lab space
      float Y = perR * rAvg + perG * gAvg + perB * bAvg;
      float Y13 = Y;
      if(Y13 > Ycutoff)
        Y13 = pow(Y13, 1.0 / 3.0);
      else
        Y13 = Yprecalc * Y13 + (4.0 / 29.0);
      brightness = (116.0 * Y13 - 16.0) / 100.0;

      // increment sample count
      s++;

      // watch for errors at any individual sample, terminate thread if so
      if(colAvg[0] != colAvg[0] || colAvg[1] != colAvg[1] || colAvg[2] != colAvg[2]){
        cout << "ERROR - pixel " << pixel << " & sample " << s << endl;
        s = sampleMax;
        pixel = size;
      }
    }

    // gamma correction
    if(gammaCorr){
      colAvg.r = pow(colAvg.r, 1.0 / 2.2);
      colAvg.g = pow(colAvg.g, 1.0 / 2.2);
      colAvg.b = pow(colAvg.b, 1.0 / 2.2);
    }

    // color the pixel image
    img[pixel] = Color24(colAvg);

    // update the z-buffer image, if necessary
    if(zBuffer)
      zImg[pixel] = zAvg;

    // update the sample count image, if necessary
    if(sampleCount)
      sampleImg[pixel] = s;

    // re-assign next pixel (naive, but works)
    pixel += numThreads;
  }
}

// irradiance cache (for global illumination & indirect lighting at a single pixel)
void irradianceCache(int i, int m, LightList lightCache){

  // establish pixel location (center)
  float pX = i % w;
  float pY = i / w;

  // color value for cache
  Color col;

  // set offset to zero
  Point posOffset = Point(0,0,0);

  // transform ray into world space
  Point rayDir = cameraRay(pX, pY, posOffset);
  Cone *ray = new Cone();
  ray->pos = camera.pos;
  ray->dir = c->transformFrom(rayDir);
  ray->radius = 0.0;
  ray->tan = dXV->x / (2.0 * imageDistance);

  // traverse through scene DOM
  // transform rays into model space
  // detect ray intersections and get back HitInfo
  HitInfo hi = HitInfo();
  bool hit = traceRay(*ray, hi);

  // if hit, get the node's material
  if(hit){
    Node *n = hi.node;
    Material *m;
    if(n)
      m = n->getMaterial();

    // if there is a material, get our indirect light color for cache
    if(m)
      col = m->shade(*ray, hi, lightCache);
  }

  // set our irradiance map variables
  ColorIM cim;
  cim.c = col;
  cim.z = hi.z;
  cim.N = hi.n;
  im.Set(m, cim);
}

// create variables for camera ray generation
void cameraRayVars(){
  float fov = camera.fov * M_PI / 180.0;
  float aspectRatio = (float) w / (float) h;
  imageDistance = camera.focalDist;
  float imageTipY = imageDistance * tan(fov / 2.0);
  float imageTipX = imageTipY * aspectRatio;
  float dX = (2.0 * imageTipX) / (float) w;
  float dY = (2.0 * imageTipY) / (float) h;
  imageTopLeftV = new Point(-imageTipX, imageTipY, -imageDistance);
  dXV = new Point(dX, 0.0, 0.0);
  dYV = new Point(0.0, -dY, 0.0);
  firstPixel = *imageTopLeftV + (*dXV * 0.5) + (*dYV * 0.5);

  // set up camera transformation (only need to rotate coordinates)
  c = new Transformation();
  Matrix *rotate = new cyMatrix3f();
  rotate->Set(camera.cross, camera.up, -camera.dir);
  c->transform(*rotate);

  // get normalized rays on the focal plane
  dVx = new Point(1.0, 0.0, 0.0);
  dVy = new Point(0.0, 1.0, 0.0);
}

// compute camera ray direction
Point cameraRay(float pX, float pY, Point offset){
  Point ray = firstPixel + (*dXV * pX) + (*dYV * pY) - offset;
  ray.Normalize();
  return ray;
}
<|endoftext|>


<commit_before>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <asio.hpp>

#include <ctime>
#include <cstring>
#include <vector>
#include <functional>

#include "common.hxx"
#include "auth.hxx"
#include "service.hxx"
#include "realm.hxx"
#include "client.hxx"
#include "packets.hxx"

using namespace std;

Client::Client(const Realm* realm_,
               const asio::ip::udp::endpoint& ep)
: realm(realm_),
  endpoint(ep),
  online(false),
  lastContact(time(NULL)),
  advert(realm, endpoint),
  advertIterator(realm),
  isIteratingAdverts(false),
  advertIterationTimer(service)
{ }

Client::Client(const Client& that)
: realm(that.realm),
  endpoint(that.endpoint),
  online(that.online),
  lastContact(that.lastContact),
  advert(that.advert),
  advertIterator(that.advertIterator),
  isIteratingAdverts(that.isIteratingAdverts),
  advertIterationTimer(service)
{ }

bool Client::isOnline() const {
  return online && time(NULL) < lastContact + CONNECTION_TIMEOUT;
}

void Client::kill() {
  online = false;
}

const asio::ip::udp::endpoint& Client::getEndpoint() const {
  return endpoint;
}

void (Client::*const Client::messages[256])(const byte*, unsigned) = {
  &Client::connect,
  &Client::ping,
  &Client::proxy,
  &Client::post,
  &Client::list,
  &Client::sign,
  &Client::bye,
  //NULLs to end of array are implicit
};

#define READ(type,from) (*(reinterpret_cast<const type*>(from)))
#define WRITE(type,to) (*(reinterpret_cast<type*>(to)))

void Client::connect(const byte* dat, unsigned len) {
  contact();

  //Check length sanity
  if (len < 2*4 + HMAC_SIZE + 2 ||
      len > 2*4 + HMAC_SIZE + MAX_CLIENT_NAME_LENGTH + 1)
    return;

  //Ensure NUL-terminated
  if (dat[len-1]) return;

  unsigned id = READ(unsigned, dat);
  unsigned timestamp = READ(unsigned, dat+4);
  byte hmac[HMAC_SIZE];
  memcpy(hmac, dat+8, HMAC_SIZE);

  const char* name = (const char*)(dat+8+HMAC_SIZE);
  if (!*name) return; //Empty name

  if (authenticate(realm, id, timestamp, name, hmac)) {
    online = true;

    this->id = id;
    this->name = name;

    //Send response
    byte dontKnowWhoIAm = 1;
    ping(&dontKnowWhoIAm, 1);
  }
}

void Client::ping(const byte* dat, unsigned len) {
  contact();

  if (len == 1 && dat[0]) {
    //Client needs to know its external address/port
    vector<byte> response(1 + realm->addressSize + 2);
    response[0] = PAK_YOUARE;

    realm->encodeAddress(&response[1], endpoint.address());
    WRITE(unsigned short, &response[response.size()-2]) = endpoint.port();

    sendPacket(endpoint, &response[0], response.size());
  } else {
    //Just respond for the sake of two-way traffic
    byte response = PAK_PONG;
    sendPacket(endpoint, &response, 1);
  }
}

void Client::proxy(const byte* dat, unsigned len) {
  contact();
  //TODO
}

void Client::post(const byte* dat, unsigned len) {
  contact();

  if (len > MAX_ADVERTISEMENT_SIZE)
    return;

  advert.setData(dat, len);
}

void Client::list(const byte* dat, unsigned len) {
  contact();

  if (isIteratingAdverts) return;

  isIteratingAdverts = true;
  setIterationTimer();
}
void Client::sign(const byte* dat, unsigned len) {
  contact();
  //TODO
}

void Client::bye(const byte* dat, unsigned len) {
  contact();

  online = false;
}

void Client::contact() {
  lastContact = time(NULL);
}

//G++'s bind1st seems to be broken, as it tries to overload its operator() in a
//forbidden manner.
struct ClientTHBind1st {
  Client* that;
  void (*f)(Client*, const asio::error_code&);

  void operator()(const asio::error_code& code) {
    f(that, code);
  }
};

void Client::setIterationTimer() {
  advertIterationTimer.expires_from_now(boost::posix_time::milliseconds(512));
  ClientTHBind1st f;
  f.that = this;
  f.f = &Client::timerHandler_static;
  advertIterationTimer.async_wait(f);
}

void Client::timerHandler_static(Client* c, const asio::error_code& code) {
  c->timerHandler(code);
}

void Client::timerHandler(const asio::error_code&) {
  asio::ip::udp::endpoint from;
  vector<byte> pack(1 + realm->addressSize + 2);
  if (advertIterator.next(from, pack)) {
    pack[0] = PAK_ADVERT;
    realm->encodeAddress(&pack[1], from.address());
    WRITE(unsigned short, &pack[1+realm->addressSize]) = from.port();
    sendPacket(endpoint, &pack[0], pack.size());
    setIterationTimer();
  } else {
    isIteratingAdverts = false;
  }
}
<commit_msg>Fix Client::timerHandler() not including id, and fix list() not resetting.<commit_after>#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <asio.hpp>

#include <ctime>
#include <cstring>
#include <vector>
#include <functional>

#include "common.hxx"
#include "auth.hxx"
#include "service.hxx"
#include "realm.hxx"
#include "client.hxx"
#include "packets.hxx"

using namespace std;

Client::Client(const Realm* realm_,
               const asio::ip::udp::endpoint& ep)
: realm(realm_),
  endpoint(ep),
  online(false),
  lastContact(time(NULL)),
  advert(realm, endpoint),
  advertIterator(realm),
  isIteratingAdverts(false),
  advertIterationTimer(service)
{ }

Client::Client(const Client& that)
: realm(that.realm),
  endpoint(that.endpoint),
  online(that.online),
  lastContact(that.lastContact),
  advert(that.advert),
  advertIterator(that.advertIterator),
  isIteratingAdverts(that.isIteratingAdverts),
  advertIterationTimer(service)
{ }

bool Client::isOnline() const {
  return online && time(NULL) < lastContact + CONNECTION_TIMEOUT;
}

void Client::kill() {
  online = false;
}

const asio::ip::udp::endpoint& Client::getEndpoint() const {
  return endpoint;
}

void (Client::*const Client::messages[256])(const byte*, unsigned) = {
  &Client::connect,
  &Client::ping,
  &Client::proxy,
  &Client::post,
  &Client::list,
  &Client::sign,
  &Client::bye,
  //NULLs to end of array are implicit
};

#define READ(type,from) (*(reinterpret_cast<const type*>(from)))
#define WRITE(type,to) (*(reinterpret_cast<type*>(to)))

void Client::connect(const byte* dat, unsigned len) {
  contact();

  //Check length sanity
  if (len < 2*4 + HMAC_SIZE + 2 ||
      len > 2*4 + HMAC_SIZE + MAX_CLIENT_NAME_LENGTH + 1)
    return;

  //Ensure NUL-terminated
  if (dat[len-1]) return;

  unsigned id = READ(unsigned, dat);
  unsigned timestamp = READ(unsigned, dat+4);
  byte hmac[HMAC_SIZE];
  memcpy(hmac, dat+8, HMAC_SIZE);

  const char* name = (const char*)(dat+8+HMAC_SIZE);
  if (!*name) return; //Empty name

  if (authenticate(realm, id, timestamp, name, hmac)) {
    online = true;

    this->id = id;
    this->name = name;

    //Send response
    byte dontKnowWhoIAm = 1;
    ping(&dontKnowWhoIAm, 1);
  }
}

void Client::ping(const byte* dat, unsigned len) {
  contact();

  if (len == 1 && dat[0]) {
    //Client needs to know its external address/port
    vector<byte> response(1 + realm->addressSize + 2);
    response[0] = PAK_YOUARE;

    realm->encodeAddress(&response[1], endpoint.address());
    WRITE(unsigned short, &response[response.size()-2]) = endpoint.port();

    sendPacket(endpoint, &response[0], response.size());
  } else {
    //Just respond for the sake of two-way traffic
    byte response = PAK_PONG;
    sendPacket(endpoint, &response, 1);
  }
}

void Client::proxy(const byte* dat, unsigned len) {
  contact();
  //TODO
}

void Client::post(const byte* dat, unsigned len) {
  contact();

  if (len > MAX_ADVERTISEMENT_SIZE)
    return;

  advert.setData(dat, len);
}

void Client::list(const byte* dat, unsigned len) {
  contact();

  if (isIteratingAdverts) return;

  isIteratingAdverts = true;
  advertIterator.reset();
  setIterationTimer();
}
void Client::sign(const byte* dat, unsigned len) {
  contact();
  //TODO
}

void Client::bye(const byte* dat, unsigned len) {
  contact();

  online = false;
}

void Client::contact() {
  lastContact = time(NULL);
}

//G++'s bind1st seems to be broken, as it tries to overload its operator() in a
//forbidden manner.
struct ClientTHBind1st {
  Client* that;
  void (*f)(Client*, const asio::error_code&);

  void operator()(const asio::error_code& code) {
    f(that, code);
  }
};

void Client::setIterationTimer() {
  advertIterationTimer.expires_from_now(boost::posix_time::milliseconds(512));
  ClientTHBind1st f;
  f.that = this;
  f.f = &Client::timerHandler_static;
  advertIterationTimer.async_wait(f);
}

void Client::timerHandler_static(Client* c, const asio::error_code& code) {
  c->timerHandler(code);
}

void Client::timerHandler(const asio::error_code&) {
  asio::ip::udp::endpoint from;
  vector<byte> pack(1 + 4 + realm->addressSize + 2);
  if (advertIterator.next(from, pack)) {
    pack[0] = PAK_ADVERT;
    WRITE(unsigned, &pack[1]) = id;
    realm->encodeAddress(&pack[1+4], from.address());
    WRITE(unsigned short, &pack[1+4+realm->addressSize]) = from.port();
    sendPacket(endpoint, &pack[0], pack.size());
    setIterationTimer();
  } else {
    isIteratingAdverts = false;
  }
}
<|endoftext|>


<commit_before>#ifndef MP_LINKEDLIST_HPP
#define MP_LINKEDLIST_HPP

#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include <assert.h>
#include "IntrusivePtr.hpp"

// lockfree

template<typename T>
class LinkedList {
public:
	struct Item;
	typedef IntrusivePtr<Item> ItemPtr;

	struct Item : public boost::intrusive_ref_counter< Item, boost::thread_safe_counter > {
		ItemPtr next;
		T value;
	};

private:
	ItemPtr first, last;

public:
	// not thread-safe!
	struct Iterator {
		ItemPtr ptr;

		Iterator(const ItemPtr& _ptr) : ptr(_ptr) {}

		T& operator*() {
			return ptr->value;
		}

		Iterator& operator++() {
			ptr = ptr->next;
			return *this;
		}

		bool operator!=(const Iterator& other) const {
			return ptr != other.ptr;
		}
	};

	Iterator begin() {
		return Iterator(first);
	}

	Iterator end() {
		return Iterator(NULL);
	}

	// only single producer supported
	ItemPtr push_back(ItemPtr item = NULL) {
		if(!item) item.reset(new Item());
		ItemPtr oldLast = last;
		if(oldLast) oldLast->next = item;
		last = item;
		if(!first) first = item;
		return item;
	}

	// only single consumer supported
	ItemPtr pop_front() {
		if(first) {
			ItemPtr oldFirst = first;
			first = first->next;
			if(!first) last = NULL;
			return oldFirst;
		}
		return NULL;
	}

	// not multithreading safe at all
	void clear() {
		first.reset();
		last.reset();
	}

	bool empty() {
		return !first;
	}

	// be very careful!
	T& front() { return first->value; }
	T& back() { return last->value; }

	void _check_sanity() {
		if(!first) assert(!last);
		else assert(last);
	}
};

#endif // LINKEDLIST_HPP
<commit_msg>another small fix<commit_after>#ifndef MP_LINKEDLIST_HPP
#define MP_LINKEDLIST_HPP

#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include <assert.h>
#include "IntrusivePtr.hpp"

// lockfree

template<typename T>
class LinkedList {
public:
	struct Item;
	typedef IntrusivePtr<Item> ItemPtr;

	struct Item : public boost::intrusive_ref_counter< Item, boost::thread_safe_counter > {
		ItemPtr next;
		T value;
	};

private:
	ItemPtr first, last;

public:
	// not thread-safe!
	struct Iterator {
		ItemPtr ptr;

		Iterator(const ItemPtr& _ptr) : ptr(_ptr) {}

		T& operator*() {
			return ptr->value;
		}

		Iterator& operator++() {
			ptr = ptr->next;
			return *this;
		}

		bool operator!=(const Iterator& other) const {
			return ptr != other.ptr;
		}
	};

	Iterator begin() {
		return Iterator(first);
	}

	Iterator end() {
		return Iterator(NULL);
	}

	// only single producer supported
	ItemPtr push_back(ItemPtr item = NULL) {
		if(!item) item.reset(new Item());
		ItemPtr oldLast = last;
		if(oldLast) oldLast->next = item;
		last = item;
		if(!first) first = item;
		return item;
	}

	// only single consumer supported
	ItemPtr pop_front() {
		ItemPtr oldFirst = first;
		if(oldFirst) {
			first = oldFirst->next;
			if(!first) last = NULL;
			return oldFirst;
		}
		return NULL;
	}

	// not multithreading safe at all
	void clear() {
		first.reset();
		last.reset();
	}

	bool empty() {
		return !first;
	}

	// be very careful!
	T& front() { return first->value; }
	T& back() { return last->value; }

	void _check_sanity() {
		if(!first) assert(!last);
		else assert(last);
	}
};

#endif // LINKEDLIST_HPP
<|endoftext|>


<commit_before>#include "iotsa.h"
#include "iotsaCapabilities.h"
#include "iotsaConfigFile.h"
#include <ArduinoJWT.h>
#include <ArduinoJson.h>

#define IFDEBUGX if(1)

// Static method to check whether a string exactly matches a Json object,
// or is included in the Json object if it is an array.
static bool stringContainedIn(const char *wanted, JsonVariant& got) {
  if (got.is<char*>()) {
    return strcmp(wanted, got.as<const char *>()) == 0;
  }
  if (!got.is<JsonArray>()) {
    return false;
  }
  JsonArray& gotArray = got.as<JsonArray>();
  for(size_t i=0; i<gotArray.size(); i++) {
    const char *gotItem = gotArray[i];
    if (strcmp(gotItem, wanted) == 0) {
      return true;
    }
  }
  return false;
}

// Get a scope indicator from a JSON variant
static IotsaCapabilityObjectScope getRightFrom(const JsonVariant& arg) {
  if (!arg.is<char*>()) return IOTSA_SCOPE_NONE;
  const char *argStr = arg.as<char*>();
  if (strcmp(argStr, "self") == 0) return IOTSA_SCOPE_SELF;
  if (strcmp(argStr, "descendent-or-self") == 0) return IOTSA_SCOPE_FULL;
  if (strcmp(argStr, "descendent") == 0) return IOTSA_SCOPE_CHILD;
  if (strcmp(argStr, "child") == 0) return IOTSA_SCOPE_CHILD;
  return IOTSA_SCOPE_NONE;
}

bool IotsaCapability::allows(const char *_obj, IotsaApiOperation verb) {
  IotsaCapabilityObjectScope scope = scopes[int(verb)];
  int matchLength = obj.length();
  switch(scope) {
    case IOTSA_SCOPE_NONE:
      break;
    case IOTSA_SCOPE_SELF:
      if (strcmp(obj.c_str(), _obj) == 0)
        return true;
      break;
    case IOTSA_SCOPE_FULL:
      if (strncmp(obj.c_str(), _obj, matchLength) == 0) {
        char nextCh = _obj[matchLength];
        if (nextCh == '\0' || nextCh == '/')
          return true;
      }
      break;
    case IOTSA_SCOPE_CHILD:
      if (strncmp(obj.c_str(), _obj, matchLength) == 0) {
        char nextCh = _obj[matchLength];
        if (nextCh == '/')
          return true;
      }
      break;
  }
  // See if there is a next capabiliy we can check, otherwise we don't have permission.
  if (next) return next->allows(_obj, verb);
  return false;
}

IotsaCapabilityMod::IotsaCapabilityMod(IotsaApplication &_app, IotsaAuthenticationProvider& _chain)
:	IotsaAuthMod(_app),
  capabilities(NULL),
#ifdef IOTSA_WITH_API
  api(this, _app, this),
#endif
  chain(_chain),
  trustedIssuer(""),
  issuerKey("")
{
	configLoad();
}

#ifdef IOTSA_WITH_WEB
void
IotsaCapabilityMod::handler() {
  String _trustedIssuer = server->arg("trustedIssuer");
  String _issuerKey = server->arg("issuerKey");
  if (_trustedIssuer != "" || _issuerKey != "") {
    if (!iotsaConfig.inConfigurationMode()) {
      server->send(401, "text/plain", "401 Unauthorized, not in configuration mode");
      return;
    }
    if (needsAuthentication("capabilities")) return;
    if (_trustedIssuer != "") trustedIssuer = _trustedIssuer;
    if (_issuerKey != "") issuerKey = _issuerKey;
    configSave();
    server->send(200, "text/plain", "ok\r\n");
    return;
  }
  String message = "<title>Capability Authority</title>
Capability Authority
";
  if (!iotsaConfig.inConfigurationMode())
    message += "
Note: You must be in configuration mode to be able to change issuer or key.
";
  message += "<form method='get'>Issuer: <input name='trustedIssuer' value='";
  message += htmlEncode(trustedIssuer);
  message += "'>
Current shared key is secret, but length is ";
  message += String(issuerKey.length());
  message += ".
New key: <input name='issuerKey'>
<input type='Submit'></form>";

  server->send(200, "text/html", message);
}

String IotsaCapabilityMod::info() {
  String message = "
Capabilities enabled.";
  message += " See /capabilities to change settings.";
  message += "
";
  return message;
}
#endif // IOTSA_WITH_WEB

#ifdef IOTSA_WITH_API
bool IotsaCapabilityMod::getHandler(const char *path, JsonObject& reply) {
  if (strcmp(path, "/api/capabilities") != 0) return false;
  reply["trustedIssuer"] = trustedIssuer;
  reply["issuerKeyLength"] = issuerKey.length();
  return true;
}

bool IotsaCapabilityMod::putHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  if (strcmp(path, "/api/capabilities") != 0) return false;
  if (!iotsaConfig.inConfigurationMode()) return false;
  bool anyChanged = false;
  JsonObject& reqObj = request.as<JsonObject>();
  if (reqObj.containsKey("trustedIssuer")) {
    trustedIssuer = reqObj.get<String>("trustedIssuer");
    anyChanged = true;
  }
  if (reqObj.containsKey("issuerKey")) {
    issuerKey = reqObj.get<String>("issuerKey");
    anyChanged = true;
  }
  if (anyChanged) {
    configSave();
  }
  return anyChanged;
}

bool IotsaCapabilityMod::postHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  return false;
}
#endif // IOTSA_WITH_API

void IotsaCapabilityMod::setup() {
  configLoad();
}

void IotsaCapabilityMod::serverSetup() {
#ifdef IOTSA_WITH_WEB
  server->on("/capabilities", std::bind(&IotsaCapabilityMod::handler, this));
#endif
#ifdef IOTSA_WITH_API
  api.setup("/api/capabilities", true, true, false);
  name = "capabilities";
#endif
}

void IotsaCapabilityMod::configLoad() {
  IotsaConfigFileLoad cf("/config/capabilities.cfg");
  cf.get("issuer", trustedIssuer, "");
  cf.get("key", issuerKey, "");
}

void IotsaCapabilityMod::configSave() {
  IotsaConfigFileSave cf("/config/capabilities.cfg");
  cf.put("issuer", trustedIssuer);
  cf.put("key", issuerKey);
  IotsaSerial.print("Saved capabilities.cfg, issuer=");
  IotsaSerial.print(trustedIssuer);
  IotsaSerial.print(", key length=");
  IotsaSerial.println(issuerKey.length());
}

void IotsaCapabilityMod::loop() {
}

bool IotsaCapabilityMod::allows(const char *obj, IotsaApiOperation verb) {
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  loadCapabilitiesFromRequest();
#endif
#ifdef IOTSA_WITH_COAP
  // Need to load capability from coap headers, somehow...
#endif
  if (capabilities) {
    if (capabilities->allows(obj, verb)) {
      IFDEBUGX IotsaSerial.print("Capability allows operation on ");
      IFDEBUGX IotsaSerial.println(obj);
      return true;
    }
    IFDEBUGX IotsaSerial.print("Capability does NOT allow operation on ");
    IFDEBUGX IotsaSerial.println(obj);
  }
  // If no rights fall back to username/password authentication
  return chain.allows(obj, verb);
}

bool IotsaCapabilityMod::allows(const char *right) {
  // If no rights fall back to username/password authentication
  return chain.allows(right);
}
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
void IotsaCapabilityMod::loadCapabilitiesFromRequest() {

  // Free old capabilities
  IotsaCapability **cpp;
  for (cpp=&capabilities; *cpp; cpp=&(*cpp)->next) free(*cpp);
  capabilities = NULL;

  // Check that we can load and verify capabilities
  if (trustedIssuer == "" || issuerKey == "") return;

  // Load the bearer token from the request
  if (!server->hasHeader("Authorization")) {
    IFDEBUGX Serial.println("No authorization header in request");
    return;
  }
  String authHeader = server->header("Authorization");
  if (!authHeader.startsWith("Bearer ")) {
    IFDEBUGX Serial.println("No bearer token in request");
    return;
  }
  String token = authHeader.substring(7);

  // Decode the bearer token
  ArduinoJWT decoder(issuerKey);
  String payload;
  bool ok = decoder.decodeJWT(token, payload);
  // If decode returned false the token wasn't signed with the correct key.
  if (!ok) {
    IFDEBUGX IotsaSerial.println("Did not decode correctly with key");
    return;
  }
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(payload);

  // check that issuer matches
  String issuer = root["iss"];
  if (issuer != trustedIssuer) {
    IFDEBUGX IotsaSerial.print("Issuer did not match, wtd=");
    IFDEBUGX IotsaSerial.print(trustedIssuer);
    IFDEBUGX IotsaSerial.print(", got=");
    IFDEBUGX IotsaSerial.println(issuer);
    return;
  }

  if (root.containsKey("aud")) {
    JsonVariant audience = root["aud"];
    String myFullName = iotsaConfig.hostName + ".local";
    if (audience != "*" && audience != myFullName) {
      IFDEBUGX IotsaSerial.print("Audience did not match, wtd=");
      IFDEBUGX IotsaSerial.print(myFullName);
      IFDEBUGX IotsaSerial.print(", got=");
      IFDEBUGX IotsaSerial.println(audience.as<String>());
      return;
    }
  }
  const char *obj = root.get<char*>("obj");
  IotsaCapabilityObjectScope get = getRightFrom(root["get"]);
  IotsaCapabilityObjectScope put = getRightFrom(root["put"]);
  IotsaCapabilityObjectScope post = getRightFrom(root["post"]);
  IFDEBUGX IotsaSerial.print("capability for ");
  IFDEBUGX IotsaSerial.print(obj);
  IFDEBUGX IotsaSerial.print(", get=");
  IFDEBUGX IotsaSerial.print(int(get));
  IFDEBUGX IotsaSerial.print(", put=");
  IFDEBUGX IotsaSerial.print(int(put));
  IFDEBUGX IotsaSerial.print(", post=");
  IFDEBUGX IotsaSerial.print(int(post));
  IFDEBUGX IotsaSerial.println(" loaded");
  capabilities = new IotsaCapability(obj, get, put, post);
}
#endif // IOTSA_WITH_HTTP_OR_HTTPS<commit_msg>Fixed typos in capability arguments: descendent -> descendant.<commit_after>#include "iotsa.h"
#include "iotsaCapabilities.h"
#include "iotsaConfigFile.h"
#include <ArduinoJWT.h>
#include <ArduinoJson.h>

#define IFDEBUGX if(1)

// Static method to check whether a string exactly matches a Json object,
// or is included in the Json object if it is an array.
static bool stringContainedIn(const char *wanted, JsonVariant& got) {
  if (got.is<char*>()) {
    return strcmp(wanted, got.as<const char *>()) == 0;
  }
  if (!got.is<JsonArray>()) {
    return false;
  }
  JsonArray& gotArray = got.as<JsonArray>();
  for(size_t i=0; i<gotArray.size(); i++) {
    const char *gotItem = gotArray[i];
    if (strcmp(gotItem, wanted) == 0) {
      return true;
    }
  }
  return false;
}

// Get a scope indicator from a JSON variant
static IotsaCapabilityObjectScope getRightFrom(const JsonVariant& arg) {
  if (!arg.is<char*>()) return IOTSA_SCOPE_NONE;
  const char *argStr = arg.as<char*>();
  if (strcmp(argStr, "self") == 0) return IOTSA_SCOPE_SELF;
  if (strcmp(argStr, "descendant-or-self") == 0) return IOTSA_SCOPE_FULL;
  if (strcmp(argStr, "descendant") == 0) return IOTSA_SCOPE_CHILD;
  if (strcmp(argStr, "child") == 0) return IOTSA_SCOPE_CHILD;
  return IOTSA_SCOPE_NONE;
}

bool IotsaCapability::allows(const char *_obj, IotsaApiOperation verb) {
  IotsaCapabilityObjectScope scope = scopes[int(verb)];
  int matchLength = obj.length();
  switch(scope) {
    case IOTSA_SCOPE_NONE:
      break;
    case IOTSA_SCOPE_SELF:
      if (strcmp(obj.c_str(), _obj) == 0)
        return true;
      break;
    case IOTSA_SCOPE_FULL:
      if (strncmp(obj.c_str(), _obj, matchLength) == 0) {
        char nextCh = _obj[matchLength];
        if (nextCh == '\0' || nextCh == '/')
          return true;
      }
      break;
    case IOTSA_SCOPE_CHILD:
      if (strncmp(obj.c_str(), _obj, matchLength) == 0) {
        char nextCh = _obj[matchLength];
        if (nextCh == '/')
          return true;
      }
      break;
  }
  // See if there is a next capabiliy we can check, otherwise we don't have permission.
  if (next) return next->allows(_obj, verb);
  return false;
}

IotsaCapabilityMod::IotsaCapabilityMod(IotsaApplication &_app, IotsaAuthenticationProvider& _chain)
:	IotsaAuthMod(_app),
  capabilities(NULL),
#ifdef IOTSA_WITH_API
  api(this, _app, this),
#endif
  chain(_chain),
  trustedIssuer(""),
  issuerKey("")
{
	configLoad();
}

#ifdef IOTSA_WITH_WEB
void
IotsaCapabilityMod::handler() {
  String _trustedIssuer = server->arg("trustedIssuer");
  String _issuerKey = server->arg("issuerKey");
  if (_trustedIssuer != "" || _issuerKey != "") {
    if (!iotsaConfig.inConfigurationMode()) {
      server->send(401, "text/plain", "401 Unauthorized, not in configuration mode");
      return;
    }
    if (needsAuthentication("capabilities")) return;
    if (_trustedIssuer != "") trustedIssuer = _trustedIssuer;
    if (_issuerKey != "") issuerKey = _issuerKey;
    configSave();
    server->send(200, "text/plain", "ok\r\n");
    return;
  }
  String message = "<title>Capability Authority</title>
Capability Authority
";
  if (!iotsaConfig.inConfigurationMode())
    message += "
Note: You must be in configuration mode to be able to change issuer or key.
";
  message += "<form method='get'>Issuer: <input name='trustedIssuer' value='";
  message += htmlEncode(trustedIssuer);
  message += "'>
Current shared key is secret, but length is ";
  message += String(issuerKey.length());
  message += ".
New key: <input name='issuerKey'>
<input type='Submit'></form>";

  server->send(200, "text/html", message);
}

String IotsaCapabilityMod::info() {
  String message = "
Capabilities enabled.";
  message += " See /capabilities to change settings.";
  message += "
";
  return message;
}
#endif // IOTSA_WITH_WEB

#ifdef IOTSA_WITH_API
bool IotsaCapabilityMod::getHandler(const char *path, JsonObject& reply) {
  if (strcmp(path, "/api/capabilities") != 0) return false;
  reply["trustedIssuer"] = trustedIssuer;
  reply["issuerKeyLength"] = issuerKey.length();
  return true;
}

bool IotsaCapabilityMod::putHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  if (strcmp(path, "/api/capabilities") != 0) return false;
  if (!iotsaConfig.inConfigurationMode()) return false;
  bool anyChanged = false;
  JsonObject& reqObj = request.as<JsonObject>();
  if (reqObj.containsKey("trustedIssuer")) {
    trustedIssuer = reqObj.get<String>("trustedIssuer");
    anyChanged = true;
  }
  if (reqObj.containsKey("issuerKey")) {
    issuerKey = reqObj.get<String>("issuerKey");
    anyChanged = true;
  }
  if (anyChanged) {
    configSave();
  }
  return anyChanged;
}

bool IotsaCapabilityMod::postHandler(const char *path, const JsonVariant& request, JsonObject& reply) {
  return false;
}
#endif // IOTSA_WITH_API

void IotsaCapabilityMod::setup() {
  configLoad();
}

void IotsaCapabilityMod::serverSetup() {
#ifdef IOTSA_WITH_WEB
  server->on("/capabilities", std::bind(&IotsaCapabilityMod::handler, this));
#endif
#ifdef IOTSA_WITH_API
  api.setup("/api/capabilities", true, true, false);
  name = "capabilities";
#endif
}

void IotsaCapabilityMod::configLoad() {
  IotsaConfigFileLoad cf("/config/capabilities.cfg");
  cf.get("issuer", trustedIssuer, "");
  cf.get("key", issuerKey, "");
}

void IotsaCapabilityMod::configSave() {
  IotsaConfigFileSave cf("/config/capabilities.cfg");
  cf.put("issuer", trustedIssuer);
  cf.put("key", issuerKey);
  IotsaSerial.print("Saved capabilities.cfg, issuer=");
  IotsaSerial.print(trustedIssuer);
  IotsaSerial.print(", key length=");
  IotsaSerial.println(issuerKey.length());
}

void IotsaCapabilityMod::loop() {
}

bool IotsaCapabilityMod::allows(const char *obj, IotsaApiOperation verb) {
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
  loadCapabilitiesFromRequest();
#endif
#ifdef IOTSA_WITH_COAP
  // Need to load capability from coap headers, somehow...
#endif
  if (capabilities) {
    if (capabilities->allows(obj, verb)) {
      IFDEBUGX IotsaSerial.print("Capability allows operation on ");
      IFDEBUGX IotsaSerial.println(obj);
      return true;
    }
    IFDEBUGX IotsaSerial.print("Capability does NOT allow operation on ");
    IFDEBUGX IotsaSerial.println(obj);
  }
  // If no rights fall back to username/password authentication
  return chain.allows(obj, verb);
}

bool IotsaCapabilityMod::allows(const char *right) {
  // If no rights fall back to username/password authentication
  return chain.allows(right);
}
#ifdef IOTSA_WITH_HTTP_OR_HTTPS
void IotsaCapabilityMod::loadCapabilitiesFromRequest() {

  // Free old capabilities
  IotsaCapability **cpp;
  for (cpp=&capabilities; *cpp; cpp=&(*cpp)->next) free(*cpp);
  capabilities = NULL;

  // Check that we can load and verify capabilities
  if (trustedIssuer == "" || issuerKey == "") return;

  // Load the bearer token from the request
  if (!server->hasHeader("Authorization")) {
    IFDEBUGX Serial.println("No authorization header in request");
    return;
  }
  String authHeader = server->header("Authorization");
  if (!authHeader.startsWith("Bearer ")) {
    IFDEBUGX Serial.println("No bearer token in request");
    return;
  }
  String token = authHeader.substring(7);

  // Decode the bearer token
  ArduinoJWT decoder(issuerKey);
  String payload;
  bool ok = decoder.decodeJWT(token, payload);
  // If decode returned false the token wasn't signed with the correct key.
  if (!ok) {
    IFDEBUGX IotsaSerial.println("Did not decode correctly with key");
    return;
  }
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(payload);

  // check that issuer matches
  String issuer = root["iss"];
  if (issuer != trustedIssuer) {
    IFDEBUGX IotsaSerial.print("Issuer did not match, wtd=");
    IFDEBUGX IotsaSerial.print(trustedIssuer);
    IFDEBUGX IotsaSerial.print(", got=");
    IFDEBUGX IotsaSerial.println(issuer);
    return;
  }

  if (root.containsKey("aud")) {
    JsonVariant audience = root["aud"];
    String myFullName = iotsaConfig.hostName + ".local";
#ifdef IOTSA_WITH_HTTPS
    String myUrl = "https://" + myFullName;
#else
    String myUrl = "http://" + myFullName;
#endif
    if (audience != "*" && audience != myFullName && audience != myUrl) {
      IFDEBUGX IotsaSerial.print("Audience did not match, wtd=");
      IFDEBUGX IotsaSerial.print(myFullName);
      IFDEBUGX IotsaSerial.print(", got=");
      IFDEBUGX IotsaSerial.println(audience.as<String>());
      return;
    }
  }
  const char *obj = root.get<char*>("obj");
  IotsaCapabilityObjectScope get = getRightFrom(root["get"]);
  IotsaCapabilityObjectScope put = getRightFrom(root["put"]);
  IotsaCapabilityObjectScope post = getRightFrom(root["post"]);
  IFDEBUGX IotsaSerial.print("capability for ");
  IFDEBUGX IotsaSerial.print(obj);
  IFDEBUGX IotsaSerial.print(", get=");
  IFDEBUGX IotsaSerial.print(int(get));
  IFDEBUGX IotsaSerial.print(", put=");
  IFDEBUGX IotsaSerial.print(int(put));
  IFDEBUGX IotsaSerial.print(", post=");
  IFDEBUGX IotsaSerial.print(int(post));
  IFDEBUGX IotsaSerial.println(" loaded");
  capabilities = new IotsaCapability(obj, get, put, post);
}
#endif // IOTSA_WITH_HTTP_OR_HTTPS<|endoftext|>