<commit_before>/*
Copyright (C) 2014-2019 Rodrigo Jose Hernandez Cordoba

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.
*/
#include "aeongames/Scene.h"
#include "aeongames/Node.h"
#include "aeongames/LogLevel.h"
#include "aeongames/Renderer.h"
#include "ProtoBufHelpers.h"
#include <cstring>
#include <cassert>
#include <algorithm>
#include <sstream>
#include <variant>

#include "aeongames/ProtoBufClasses.h"
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : PROTOBUF_WARNINGS )
#endif
#include <google/protobuf/text_format.h>
#include "scene.pb.h"
#ifdef _MSC_VER
#pragma warning( pop )
#endif

namespace AeonGames
{
    /**@todo implement Linear Octrees:
    https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/
    */
    Scene::Scene() :
        mName{ "Scene" },
        mNodes{}
    {
    }

    Scene::~Scene()
    {
        for ( auto & mRootNode : mNodes )
        {
            Remove ( mRootNode );
        }
    }

    void Scene::SetName ( const char* aName )
    {
        mName = aName;
    }

    const char* const Scene::GetName() const
    {
        return mName.c_str();
    }

    void Scene::SetCamera ( Node* aNode )
    {
        mCamera = aNode;
    }

    void Scene::SetCamera ( uint32_t aNodeId )
    {
        SetCamera ( Find ( [aNodeId] ( const Node & aNode ) -> bool { return aNode.GetId() == aNodeId; } ) );
    }

    void Scene::SetCamera ( const std::string& aNodeName )
    {
        SetCamera ( crc32i ( aNodeName.c_str(), aNodeName.size() ) );
    }

    const Node* Scene::GetCamera() const
    {
        return mCamera;
    }

    void Scene::SetFieldOfView ( float aFieldOfView )
    {
        mFieldOfView = aFieldOfView;
    }

    void Scene::SetNear ( float aNear )
    {
        mNear = aNear;
    }

    void Scene::SetFar ( float aFar )
    {
        mFar = aFar;
    }

    float Scene::GetFieldOfView() const
    {
        return mFieldOfView;
    }
    float Scene::GetNear() const
    {
        return mNear;
    }
    float Scene::GetFar() const
    {
        return mFar;
    }

    void Scene::SetViewMatrix ( const Matrix4x4& aMatrix )
    {
        mView = aMatrix;
    }

    const Matrix4x4& Scene::GetViewMatrix() const
    {
        return mViewMatrix;
    }

    size_t Scene::GetChildrenCount() const
    {
        return mNodes.size();
    }

    Node* Scene::GetChild ( size_t aIndex ) const
    {
        return mNodes.at ( aIndex );
    }

    size_t Scene::GetChildIndex ( const Node* aNode ) const
    {
        auto index = std::find_if ( mNodes.begin(), mNodes.end(),
                                    [aNode] ( const Node * node )
        {
            return node == aNode;
        } );
        if ( index != mNodes.end() )
        {
            return index - mNodes.begin();
        }
        throw std::runtime_error ( "Node is not a child of this object." );
    }

    const Node& Scene::operator[] ( const std::size_t index ) const
    {
        return * ( mNodes[index] );
    }

    Node& Scene::operator[] ( const std::size_t index )
    {
        return const_cast<Node&> ( static_cast<const Scene&> ( *this ) [index] );
    }

    void Scene::Update ( const double delta )
    {
        LoopTraverseDFSPreOrder ( [delta] ( Node & aNode )
        {
            aNode.Update ( delta );
        } );
    }

    void Scene::BroadcastMessage ( uint32_t aMessageType, const void* aMessageData )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( [aMessageType, aMessageData] ( Node & node )
            {
                if ( node.mFlags[Node::Enabled] )
                {
                    node.ProcessMessage ( aMessageType, aMessageData );
                }
            } );
        }
    }

    void Scene::LoopTraverseDFSPreOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPreOrder (
        const std::function<void ( Node& ) >& aPreamble,
        const std::function<void ( Node& ) >& aPostamble )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( aPreamble, aPostamble );
        }
    }

    void Scene::LoopTraverseDFSPreOrder ( const std::function<void ( const Node& ) >& aAction ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            static_cast<const Node*> ( mRootNode )->LoopTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPostOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPostOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPostOrder ( const std::function<void ( const Node& ) >& aAction ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            static_cast<const Node*> ( mRootNode )->LoopTraverseDFSPostOrder ( aAction );
        }
    }

    Node* Scene::Find ( const std::function<bool ( const Node& ) >& aUnaryPredicate ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            if ( Node* node = static_cast<Node*> ( mRootNode )->Find ( aUnaryPredicate ) )
            {
                return node;
            }
        }
        return nullptr;
    }

    void Scene::RecursiveTraverseDFSPreOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->RecursiveTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::RecursiveTraverseDFSPostOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->RecursiveTraverseDFSPostOrder ( aAction );
        }
    }

    bool Scene::Insert ( size_t aIndex, Node* aNode )
    {
        // Never append null or duplicate pointers.
        if ( ( aNode != nullptr ) && ( std::find ( mNodes.begin(), mNodes.end(), aNode ) == mNodes.end() ) )
        {
            std::visit ( [aNode] ( auto&& parent )
            {
                if ( parent != nullptr )
                {
                    if ( !parent->Remove ( aNode ) )
                    {
                        std::cout << LogLevel::Warning << "Parent for node " << aNode->GetName() << " did not have it as a child.";
                    }
                }
            },
            aNode->mParent );

            aNode->mParent = this;
            if ( aIndex < mNodes.size() )
            {
                mNodes.insert ( mNodes.begin() + aIndex, aNode );
            }
            else
            {
                mNodes.emplace_back ( aNode );
            }
            // Force a recalculation of the LOCAL transform
            // by setting the GLOBAL transform to itself.
            aNode->SetGlobalTransform ( aNode->mGlobalTransform );
            return true;
        }
        return false;
    }

    bool Scene::Add ( Node* aNode )
    {
        // Never append null or this pointers.
        if ( ( aNode != nullptr ) && ( std::find ( mNodes.begin(), mNodes.end(), aNode ) == mNodes.end() ) )
        {
            std::visit ( [aNode] ( auto&& parent )
            {
                if ( parent != nullptr )
                {
                    if ( !parent->Remove ( aNode ) )
                    {
                        std::cout << LogLevel::Warning << "Parent for node " << aNode->GetName() << " did not have it as a child.";
                    }
                }
            },
            aNode->mParent );
            aNode->mParent = this;
            mNodes.emplace_back ( aNode );
            // Force a recalculation of the LOCAL transform
            // by setting the GLOBAL transform to itself.
            aNode->SetGlobalTransform ( aNode->mGlobalTransform );
            return true;
        }
        return false;
    }

    bool Scene::Remove ( Node* aNode )
    {
        if ( aNode == nullptr )
        {
            return false;
        }
        // If passed a null or this pointer find SHOULD not find it on release builds.
        auto it = std::find_if ( mNodes.begin(), mNodes.end(),
                                 [aNode] ( const Node * node )
        {
            return aNode == node;
        } );
        if ( it != mNodes.end() )
        {
            // Force recalculation of transforms.
            aNode->mParent = static_cast<Node*> ( nullptr );
            aNode->SetLocalTransform ( aNode->mGlobalTransform );
            mNodes.erase ( it );
            return true;
        }
        return false;
    }

    bool Scene::RemoveByIndex ( size_t aIndex )
    {
        if ( aIndex >= mNodes.size() )
        {
            return false;
        }
        mNodes[aIndex]->mParent = static_cast<Node*> ( nullptr );
        mNodes[aIndex]->SetLocalTransform ( mNodes[aIndex]->mGlobalTransform );
        mNodes.erase ( mNodes.begin() + aIndex );
        return true;
    }

    std::string Scene::Serialize ( bool aAsBinary ) const
    {
        static SceneBuffer scene_buffer;
        *scene_buffer.mutable_name() = mName;
        if ( mCamera )
        {
            *scene_buffer.mutable_camera()->mutable_node() = mCamera->GetName();
            scene_buffer.mutable_camera()->set_field_of_view ( mFieldOfView );
            scene_buffer.mutable_camera()->set_near_plane ( mNear );
            scene_buffer.mutable_camera()->set_far_plane ( mFar );
        }
        std::unordered_map<const Node*, NodeBuffer*> node_map;
        LoopTraverseDFSPreOrder (
            [&node_map] ( const Node & node )
        {
            NodeBuffer* node_buffer;
            auto parent = node_map.find ( GetNodePtr ( node.GetParent() ) );
            if ( parent != node_map.end() )
            {
                node_buffer = ( *parent ).second->add_node();
            }
            else
            {
                node_buffer = scene_buffer.add_node();
            }
            node.Serialize ( *node_buffer );
            node_map.emplace ( std::make_pair ( &node, node_buffer ) );
        } );
        std::stringstream serialization;
        if ( aAsBinary )
        {
            serialization << "AEONSCE" << '\0';
            scene_buffer.SerializeToOstream ( &serialization );
        }
        else
        {
            std::string text;
            serialization << "AEONSCE\n";
            google::protobuf::TextFormat::Printer printer;
            printer.PrintToString ( scene_buffer, &text );
            serialization << text;
        }
        scene_buffer.Clear();
        return serialization.str();
    }
    void Scene::Deserialize ( const std::string& aSerializedScene )
    {
        static SceneBuffer scene_buffer;
        LoadProtoBufObject ( scene_buffer, aSerializedScene.data(), aSerializedScene.size(), "AEONSCE" );
        mName = scene_buffer.name();

        std::unordered_map<const NodeBuffer*, std::tuple<const NodeBuffer*, int, Node*>> node_map;
        for ( auto &i : scene_buffer.node() )
        {
            const NodeBuffer* node = &i;
            node_map[node] = std::tuple<const NodeBuffer*, int, Node*> {nullptr, 0, StoreNode ( std::make_unique<Node>() ) };
            Add ( std::get<2> ( node_map[node] ) );
            std::get<2> ( node_map[node] )->Deserialize ( *node );
            while ( node )
            {
                if ( std::get<1> ( node_map[node] ) < node->node().size() )
                {
                    const NodeBuffer* prev = node;
                    node = &node->node ( std::get<1> ( node_map[node] ) );
                    node_map[node] = std::tuple<const NodeBuffer*, int, Node*> {prev, 0, StoreNode ( std::make_unique<Node>() ) };
                    std::get<2> ( node_map[prev] )->Add ( std::get<2> ( node_map[node] ) );
                    std::get<2> ( node_map[node] )->Deserialize ( *node );
                    ++std::get<1> ( node_map[prev] );
                }
                else
                {
                    std::get<1> ( node_map[node] ) = 0;
                    node = std::get<0> ( node_map[node] );
                }
            }
        }
        SetCamera ( scene_buffer.camera().node() );
        mFieldOfView = scene_buffer.camera().field_of_view();
        mFieldOfView = ( mFieldOfView == 0.0f ) ? 60.0f : mFieldOfView;
        mNear = scene_buffer.camera().near_plane();
        mNear = ( mNear == 0.0f ) ? 1.0f : mNear;
        mFar = scene_buffer.camera().far_plane();
        mFar = ( mFar == 0.0f ) ? 1600.0f : mFar;
        if ( mCamera )
        {
            mView = mCamera->GetGlobalTransform().GetInvertedMatrix();
        }
        scene_buffer.Clear();
    }
    Node* Scene::StoreNode ( std::unique_ptr<Node> aNode )
    {
        mNodeStorage.emplace_back ( ( aNode ) ? std::move ( aNode ) : std::make_unique<Node>() );
        return mNodeStorage.back().get();
    }
    std::unique_ptr<Node> Scene::DisposeNode ( const Node* aNode )
    {
        std::unique_ptr<Node> result{};
        auto i = std::find_if ( mNodeStorage.begin(), mNodeStorage.end(), [aNode] ( const std::unique_ptr<Node>& node )
        {
            return aNode == node.get();
        } );
        if ( i != mNodeStorage.end() )
        {
            result = std::move ( *i );
            mNodeStorage.erase ( std::remove ( i, mNodeStorage.end(), *i ), mNodeStorage.end() );
        }
        return result;
    }
}
<commit_msg>Fix for compilation error.<commit_after>/*
Copyright (C) 2014-2019 Rodrigo Jose Hernandez Cordoba

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.
*/
#include "aeongames/Scene.h"
#include "aeongames/Node.h"
#include "aeongames/LogLevel.h"
#include "aeongames/Renderer.h"
#include "ProtoBufHelpers.h"
#include <cstring>
#include <cassert>
#include <algorithm>
#include <sstream>
#include <variant>

#include "aeongames/ProtoBufClasses.h"
#ifdef _MSC_VER
#pragma warning( push )
#pragma warning( disable : PROTOBUF_WARNINGS )
#endif
#include <google/protobuf/text_format.h>
#include "scene.pb.h"
#ifdef _MSC_VER
#pragma warning( pop )
#endif

namespace AeonGames
{
    /**@todo implement Linear Octrees:
    https://geidav.wordpress.com/2014/08/18/advanced-octrees-2-node-representations/
    */
    Scene::Scene() :
        mName{ "Scene" },
        mNodes{}
    {
    }

    Scene::~Scene()
    {
        for ( auto & mRootNode : mNodes )
        {
            Remove ( mRootNode );
        }
    }

    void Scene::SetName ( const char* aName )
    {
        mName = aName;
    }

    const char* const Scene::GetName() const
    {
        return mName.c_str();
    }

    void Scene::SetCamera ( Node* aNode )
    {
        mCamera = aNode;
    }

    void Scene::SetCamera ( uint32_t aNodeId )
    {
        SetCamera ( Find ( [aNodeId] ( const Node & aNode ) -> bool { return aNode.GetId() == aNodeId; } ) );
    }

    void Scene::SetCamera ( const std::string& aNodeName )
    {
        SetCamera ( crc32i ( aNodeName.c_str(), aNodeName.size() ) );
    }

    const Node* Scene::GetCamera() const
    {
        return mCamera;
    }

    void Scene::SetFieldOfView ( float aFieldOfView )
    {
        mFieldOfView = aFieldOfView;
    }

    void Scene::SetNear ( float aNear )
    {
        mNear = aNear;
    }

    void Scene::SetFar ( float aFar )
    {
        mFar = aFar;
    }

    float Scene::GetFieldOfView() const
    {
        return mFieldOfView;
    }
    float Scene::GetNear() const
    {
        return mNear;
    }
    float Scene::GetFar() const
    {
        return mFar;
    }

    void Scene::SetViewMatrix ( const Matrix4x4& aMatrix )
    {
        mViewMatrix = aMatrix;
    }

    const Matrix4x4& Scene::GetViewMatrix() const
    {
        return mViewMatrix;
    }

    size_t Scene::GetChildrenCount() const
    {
        return mNodes.size();
    }

    Node* Scene::GetChild ( size_t aIndex ) const
    {
        return mNodes.at ( aIndex );
    }

    size_t Scene::GetChildIndex ( const Node* aNode ) const
    {
        auto index = std::find_if ( mNodes.begin(), mNodes.end(),
                                    [aNode] ( const Node * node )
        {
            return node == aNode;
        } );
        if ( index != mNodes.end() )
        {
            return index - mNodes.begin();
        }
        throw std::runtime_error ( "Node is not a child of this object." );
    }

    const Node& Scene::operator[] ( const std::size_t index ) const
    {
        return * ( mNodes[index] );
    }

    Node& Scene::operator[] ( const std::size_t index )
    {
        return const_cast<Node&> ( static_cast<const Scene&> ( *this ) [index] );
    }

    void Scene::Update ( const double delta )
    {
        LoopTraverseDFSPreOrder ( [delta] ( Node & aNode )
        {
            aNode.Update ( delta );
        } );
    }

    void Scene::BroadcastMessage ( uint32_t aMessageType, const void* aMessageData )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( [aMessageType, aMessageData] ( Node & node )
            {
                if ( node.mFlags[Node::Enabled] )
                {
                    node.ProcessMessage ( aMessageType, aMessageData );
                }
            } );
        }
    }

    void Scene::LoopTraverseDFSPreOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPreOrder (
        const std::function<void ( Node& ) >& aPreamble,
        const std::function<void ( Node& ) >& aPostamble )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPreOrder ( aPreamble, aPostamble );
        }
    }

    void Scene::LoopTraverseDFSPreOrder ( const std::function<void ( const Node& ) >& aAction ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            static_cast<const Node*> ( mRootNode )->LoopTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPostOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->LoopTraverseDFSPostOrder ( aAction );
        }
    }

    void Scene::LoopTraverseDFSPostOrder ( const std::function<void ( const Node& ) >& aAction ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            static_cast<const Node*> ( mRootNode )->LoopTraverseDFSPostOrder ( aAction );
        }
    }

    Node* Scene::Find ( const std::function<bool ( const Node& ) >& aUnaryPredicate ) const
    {
        for ( const auto& mRootNode : mNodes )
        {
            if ( Node* node = static_cast<Node*> ( mRootNode )->Find ( aUnaryPredicate ) )
            {
                return node;
            }
        }
        return nullptr;
    }

    void Scene::RecursiveTraverseDFSPreOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->RecursiveTraverseDFSPreOrder ( aAction );
        }
    }

    void Scene::RecursiveTraverseDFSPostOrder ( const std::function<void ( Node& ) >& aAction )
    {
        for ( auto & mRootNode : mNodes )
        {
            mRootNode->RecursiveTraverseDFSPostOrder ( aAction );
        }
    }

    bool Scene::Insert ( size_t aIndex, Node* aNode )
    {
        // Never append null or duplicate pointers.
        if ( ( aNode != nullptr ) && ( std::find ( mNodes.begin(), mNodes.end(), aNode ) == mNodes.end() ) )
        {
            std::visit ( [aNode] ( auto&& parent )
            {
                if ( parent != nullptr )
                {
                    if ( !parent->Remove ( aNode ) )
                    {
                        std::cout << LogLevel::Warning << "Parent for node " << aNode->GetName() << " did not have it as a child.";
                    }
                }
            },
            aNode->mParent );

            aNode->mParent = this;
            if ( aIndex < mNodes.size() )
            {
                mNodes.insert ( mNodes.begin() + aIndex, aNode );
            }
            else
            {
                mNodes.emplace_back ( aNode );
            }
            // Force a recalculation of the LOCAL transform
            // by setting the GLOBAL transform to itself.
            aNode->SetGlobalTransform ( aNode->mGlobalTransform );
            return true;
        }
        return false;
    }

    bool Scene::Add ( Node* aNode )
    {
        // Never append null or this pointers.
        if ( ( aNode != nullptr ) && ( std::find ( mNodes.begin(), mNodes.end(), aNode ) == mNodes.end() ) )
        {
            std::visit ( [aNode] ( auto&& parent )
            {
                if ( parent != nullptr )
                {
                    if ( !parent->Remove ( aNode ) )
                    {
                        std::cout << LogLevel::Warning << "Parent for node " << aNode->GetName() << " did not have it as a child.";
                    }
                }
            },
            aNode->mParent );
            aNode->mParent = this;
            mNodes.emplace_back ( aNode );
            // Force a recalculation of the LOCAL transform
            // by setting the GLOBAL transform to itself.
            aNode->SetGlobalTransform ( aNode->mGlobalTransform );
            return true;
        }
        return false;
    }

    bool Scene::Remove ( Node* aNode )
    {
        if ( aNode == nullptr )
        {
            return false;
        }
        // If passed a null or this pointer find SHOULD not find it on release builds.
        auto it = std::find_if ( mNodes.begin(), mNodes.end(),
                                 [aNode] ( const Node * node )
        {
            return aNode == node;
        } );
        if ( it != mNodes.end() )
        {
            // Force recalculation of transforms.
            aNode->mParent = static_cast<Node*> ( nullptr );
            aNode->SetLocalTransform ( aNode->mGlobalTransform );
            mNodes.erase ( it );
            return true;
        }
        return false;
    }

    bool Scene::RemoveByIndex ( size_t aIndex )
    {
        if ( aIndex >= mNodes.size() )
        {
            return false;
        }
        mNodes[aIndex]->mParent = static_cast<Node*> ( nullptr );
        mNodes[aIndex]->SetLocalTransform ( mNodes[aIndex]->mGlobalTransform );
        mNodes.erase ( mNodes.begin() + aIndex );
        return true;
    }

    std::string Scene::Serialize ( bool aAsBinary ) const
    {
        static SceneBuffer scene_buffer;
        *scene_buffer.mutable_name() = mName;
        if ( mCamera )
        {
            *scene_buffer.mutable_camera()->mutable_node() = mCamera->GetName();
            scene_buffer.mutable_camera()->set_field_of_view ( mFieldOfView );
            scene_buffer.mutable_camera()->set_near_plane ( mNear );
            scene_buffer.mutable_camera()->set_far_plane ( mFar );
        }
        std::unordered_map<const Node*, NodeBuffer*> node_map;
        LoopTraverseDFSPreOrder (
            [&node_map] ( const Node & node )
        {
            NodeBuffer* node_buffer;
            auto parent = node_map.find ( GetNodePtr ( node.GetParent() ) );
            if ( parent != node_map.end() )
            {
                node_buffer = ( *parent ).second->add_node();
            }
            else
            {
                node_buffer = scene_buffer.add_node();
            }
            node.Serialize ( *node_buffer );
            node_map.emplace ( std::make_pair ( &node, node_buffer ) );
        } );
        std::stringstream serialization;
        if ( aAsBinary )
        {
            serialization << "AEONSCE" << '\0';
            scene_buffer.SerializeToOstream ( &serialization );
        }
        else
        {
            std::string text;
            serialization << "AEONSCE\n";
            google::protobuf::TextFormat::Printer printer;
            printer.PrintToString ( scene_buffer, &text );
            serialization << text;
        }
        scene_buffer.Clear();
        return serialization.str();
    }
    void Scene::Deserialize ( const std::string& aSerializedScene )
    {
        static SceneBuffer scene_buffer;
        LoadProtoBufObject ( scene_buffer, aSerializedScene.data(), aSerializedScene.size(), "AEONSCE" );
        mName = scene_buffer.name();

        std::unordered_map<const NodeBuffer*, std::tuple<const NodeBuffer*, int, Node*>> node_map;
        for ( auto &i : scene_buffer.node() )
        {
            const NodeBuffer* node = &i;
            node_map[node] = std::tuple<const NodeBuffer*, int, Node*> {nullptr, 0, StoreNode ( std::make_unique<Node>() ) };
            Add ( std::get<2> ( node_map[node] ) );
            std::get<2> ( node_map[node] )->Deserialize ( *node );
            while ( node )
            {
                if ( std::get<1> ( node_map[node] ) < node->node().size() )
                {
                    const NodeBuffer* prev = node;
                    node = &node->node ( std::get<1> ( node_map[node] ) );
                    node_map[node] = std::tuple<const NodeBuffer*, int, Node*> {prev, 0, StoreNode ( std::make_unique<Node>() ) };
                    std::get<2> ( node_map[prev] )->Add ( std::get<2> ( node_map[node] ) );
                    std::get<2> ( node_map[node] )->Deserialize ( *node );
                    ++std::get<1> ( node_map[prev] );
                }
                else
                {
                    std::get<1> ( node_map[node] ) = 0;
                    node = std::get<0> ( node_map[node] );
                }
            }
        }
        SetCamera ( scene_buffer.camera().node() );
        mFieldOfView = scene_buffer.camera().field_of_view();
        mFieldOfView = ( mFieldOfView == 0.0f ) ? 60.0f : mFieldOfView;
        mNear = scene_buffer.camera().near_plane();
        mNear = ( mNear == 0.0f ) ? 1.0f : mNear;
        mFar = scene_buffer.camera().far_plane();
        mFar = ( mFar == 0.0f ) ? 1600.0f : mFar;
        if ( mCamera )
        {
            mViewMatrix = mCamera->GetGlobalTransform().GetInvertedMatrix();
        }
        scene_buffer.Clear();
    }
    Node* Scene::StoreNode ( std::unique_ptr<Node> aNode )
    {
        mNodeStorage.emplace_back ( ( aNode ) ? std::move ( aNode ) : std::make_unique<Node>() );
        return mNodeStorage.back().get();
    }
    std::unique_ptr<Node> Scene::DisposeNode ( const Node* aNode )
    {
        std::unique_ptr<Node> result{};
        auto i = std::find_if ( mNodeStorage.begin(), mNodeStorage.end(), [aNode] ( const std::unique_ptr<Node>& node )
        {
            return aNode == node.get();
        } );
        if ( i != mNodeStorage.end() )
        {
            result = std::move ( *i );
            mNodeStorage.erase ( std::remove ( i, mNodeStorage.end(), *i ), mNodeStorage.end() );
        }
        return result;
    }
}
<|endoftext|>


<commit_before>/* Copyright (c) 2013-2015 The Squash Authors
 *
 * 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.
 *
 * Authors:
 *   Evan Nemerson <evan@nemerson.com>
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <squash/squash.h>

#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
 #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
  #define _Thread_local __thread
 #else
  #define _Thread_local __declspec(thread)
 #endif
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
 #define _Thread_local __thread
#endif

#include "libzpaq.h"

#define SQUASH_ZPAQ_DEFAULT_LEVEL 1

#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

typedef struct _SquashZpaqOptions {
  SquashOptions base_object;

  int level;
} SquashZpaqOptions;

typedef struct _SquashZpaqStream SquashZpaqStream;

class SquashZpaqIO: public libzpaq::Reader, public libzpaq::Writer {
public:
  SquashZpaqStream* stream;

  int get ();
  void put (int c);

  void write (const char* buf, int n);

  SquashZpaqIO(SquashZpaqStream* stream);
};

struct _SquashZpaqStream {
  SquashStream base_object;

  SquashZpaqIO* stream;

  SquashOperation operation;
};

extern "C" SquashStatus   squash_plugin_init_codec    (SquashCodec* codec, SquashCodecFuncs* funcs);

static void               squash_zpaq_options_init    (SquashZpaqOptions* options, SquashCodec* codec, SquashDestroyNotify destroy_notify);
static SquashZpaqOptions* squash_zpaq_options_new     (SquashCodec* codec);
static void               squash_zpaq_options_destroy (void* options);
static void               squash_zpaq_options_free    (void* options);

static void               squash_zpaq_stream_init     (SquashZpaqStream* stream,
                                                       SquashCodec* codec,
                                                       SquashStreamType stream_type,
                                                       SquashZpaqOptions* options,
                                                       SquashDestroyNotify destroy_notify);
static SquashZpaqStream*  squash_zpaq_stream_new      (SquashCodec* codec, SquashStreamType stream_type, SquashZpaqOptions* options);
static void               squash_zpaq_stream_destroy  (void* stream);
static void               squash_zpaq_stream_free     (void* stream);

static _Thread_local SquashStream* squash_zpaq_thread_stream = NULL;

void
libzpaq::error (const char* msg) {
  assert (squash_zpaq_thread_stream != NULL);
  squash_stream_yield (squash_zpaq_thread_stream, SQUASH_FAILED);
}

SquashZpaqIO::SquashZpaqIO (SquashZpaqStream* stream) {
  this->stream = stream;
}

int SquashZpaqIO::get () {
  SquashStream* stream = (SquashStream*) this->stream;

  while (stream->avail_in == 0 && this->stream->operation == SQUASH_OPERATION_PROCESS) {
    this->stream->operation = squash_stream_yield (stream, SQUASH_OK);
  }

  if (stream->avail_in == 0) {
    return -1;
  } else {
    int r = stream->next_in[0];
    stream->next_in++;
    stream->avail_in--;
    return r;
  }
}

void SquashZpaqIO::put (int c) {
  uint8_t v = (uint8_t) c;
  this->write ((const char*) &v, 1);
}

void SquashZpaqIO::write (const char* buf, int n) {
  SquashStream* stream = (SquashStream*) this->stream;

  size_t written = 0;
  size_t remaining = (size_t) n;
  while (remaining > 0) {
    const size_t cp_size = (remaining < stream->avail_out) ? remaining : stream->avail_out;

    memcpy (stream->next_out, buf + written, cp_size);

    written += cp_size;
    remaining -= cp_size;
    stream->next_out += cp_size;
    stream->avail_out -= cp_size;

    if (remaining != 0)
      this->stream->operation = squash_stream_yield (stream, SQUASH_PROCESSING);
  }
}

static void
squash_zpaq_options_init (SquashZpaqOptions* options, SquashCodec* codec, SquashDestroyNotify destroy_notify) {
  assert (options != NULL);

  squash_options_init ((SquashOptions*) options, codec, destroy_notify);

	options->level = SQUASH_ZPAQ_DEFAULT_LEVEL;
}

static SquashZpaqOptions*
squash_zpaq_options_new (SquashCodec* codec) {
  SquashZpaqOptions* options;

  options = (SquashZpaqOptions*) malloc (sizeof (SquashZpaqOptions));
  squash_zpaq_options_init (options, codec, squash_zpaq_options_free);

  return options;
}

static void
squash_zpaq_options_destroy (void* options) {
  squash_options_destroy ((SquashOptions*) options);
}

static void
squash_zpaq_options_free (void* options) {
  squash_zpaq_options_destroy ((SquashZpaqOptions*) options);
  free (options);
}

static SquashOptions*
squash_zpaq_create_options (SquashCodec* codec) {
  return (SquashOptions*) squash_zpaq_options_new (codec);
}

static SquashStatus
squash_zpaq_parse_option (SquashOptions* options, const char* key, const char* value) {
  SquashZpaqOptions* opts = (SquashZpaqOptions*) options;
  char* endptr = NULL;

  assert (opts != NULL);

  if (strcasecmp (key, "level") == 0) {
    const int level = strtol (value, &endptr, 0);
    if ( *endptr == '\0' && level >= 1 && level <= 5 ) {
      opts->level = level;
    } else {
      return SQUASH_BAD_VALUE;
    }
  } else {
    return SQUASH_BAD_PARAM;
  }

  return SQUASH_OK;
}

static SquashZpaqStream* squash_zpaq_stream_new (SquashCodec* codec,
                                                 SquashStreamType stream_type,
                                                 SquashZpaqOptions* options) {
  SquashZpaqStream* stream;

  assert (codec != NULL);
  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);

  stream = (SquashZpaqStream*) malloc (sizeof (SquashZpaqStream));
  squash_zpaq_stream_init (stream, codec, stream_type, options, squash_zpaq_stream_free);

  return stream;
}

static void
squash_zpaq_stream_init (SquashZpaqStream* stream,
                         SquashCodec* codec,
                         SquashStreamType stream_type,
                         SquashZpaqOptions* options,
                         SquashDestroyNotify destroy_notify) {
  squash_stream_init ((SquashStream*) stream, codec, stream_type, (SquashOptions*) options, destroy_notify);

  stream->stream = new SquashZpaqIO(stream);
  stream->stream->stream = stream;
}

static void
squash_zpaq_stream_destroy (void* stream) {
  delete ((SquashZpaqStream*) stream)->stream;

  squash_stream_destroy (stream);
}

static void
squash_zpaq_stream_free (void* stream) {
  squash_zpaq_stream_destroy (stream);
  free (stream);
}

static SquashStream*
squash_zpaq_create_stream (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) {
  return (SquashStream*) squash_zpaq_stream_new (codec, stream_type, (SquashZpaqOptions*) options);
}

static SquashStatus
squash_zpaq_process_stream (SquashStream* stream, SquashOperation operation) {
  SquashZpaqStream* s = (SquashZpaqStream*) stream;

  s->operation = operation;

  if (stream->stream_type == SQUASH_STREAM_COMPRESS) {
    char level_s[2];
    snprintf (level_s, sizeof(level_s), "%d", (stream->options != NULL) ? ((SquashZpaqOptions*) stream->options)->level : SQUASH_ZPAQ_DEFAULT_LEVEL);

    squash_zpaq_thread_stream = stream;
    compress (s->stream, s->stream, level_s);
    squash_zpaq_thread_stream = NULL;
  } else {
    squash_zpaq_thread_stream = stream;
    decompress (s->stream, s->stream);
    squash_zpaq_thread_stream = NULL;
  }

  return SQUASH_OK;
}

static size_t
squash_zpaq_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_length) {
  /* 37 byte header + 10 byte footer + 101% data.  The header and
     footer should be okay, but the extra 1% is just based on some
     quick tests with random data.  If this isn't sufficient *please*
     file a bug so we can bump it. */
  return
    uncompressed_length +
    ((uncompressed_length / 100) * 1) + ((uncompressed_length % 100) > 0 ? 1 : 0) +
    47;
}

extern "C" SquashStatus
squash_plugin_init_codec (SquashCodec* codec, SquashCodecFuncs* funcs) {
  const char* name = squash_codec_get_name (codec);

  if (strcmp ("zpaq", name) == 0) {
    funcs->info = SQUASH_CODEC_INFO_RUN_IN_THREAD;
    funcs->create_options = squash_zpaq_create_options;
    funcs->parse_option = squash_zpaq_parse_option;
    funcs->create_stream = squash_zpaq_create_stream;
    funcs->process_stream = squash_zpaq_process_stream;
    funcs->get_max_compressed_size = squash_zpaq_get_max_compressed_size;
  } else {
    return SQUASH_UNABLE_TO_LOAD;
  }

  return SQUASH_OK;
}
<commit_msg>zpaq: increase max_compressed_size<commit_after>/* Copyright (c) 2013-2015 The Squash Authors
 *
 * 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.
 *
 * Authors:
 *   Evan Nemerson <evan@nemerson.com>
 */

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#include <squash/squash.h>

#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
 #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
  #define _Thread_local __thread
 #else
  #define _Thread_local __declspec(thread)
 #endif
#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
 #define _Thread_local __thread
#endif

#include "libzpaq.h"

#define SQUASH_ZPAQ_DEFAULT_LEVEL 1

#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

typedef struct _SquashZpaqOptions {
  SquashOptions base_object;

  int level;
} SquashZpaqOptions;

typedef struct _SquashZpaqStream SquashZpaqStream;

class SquashZpaqIO: public libzpaq::Reader, public libzpaq::Writer {
public:
  SquashZpaqStream* stream;

  int get ();
  void put (int c);

  void write (const char* buf, int n);

  SquashZpaqIO(SquashZpaqStream* stream);
};

struct _SquashZpaqStream {
  SquashStream base_object;

  SquashZpaqIO* stream;

  SquashOperation operation;
};

extern "C" SquashStatus   squash_plugin_init_codec    (SquashCodec* codec, SquashCodecFuncs* funcs);

static void               squash_zpaq_options_init    (SquashZpaqOptions* options, SquashCodec* codec, SquashDestroyNotify destroy_notify);
static SquashZpaqOptions* squash_zpaq_options_new     (SquashCodec* codec);
static void               squash_zpaq_options_destroy (void* options);
static void               squash_zpaq_options_free    (void* options);

static void               squash_zpaq_stream_init     (SquashZpaqStream* stream,
                                                       SquashCodec* codec,
                                                       SquashStreamType stream_type,
                                                       SquashZpaqOptions* options,
                                                       SquashDestroyNotify destroy_notify);
static SquashZpaqStream*  squash_zpaq_stream_new      (SquashCodec* codec, SquashStreamType stream_type, SquashZpaqOptions* options);
static void               squash_zpaq_stream_destroy  (void* stream);
static void               squash_zpaq_stream_free     (void* stream);

static _Thread_local SquashStream* squash_zpaq_thread_stream = NULL;

void
libzpaq::error (const char* msg) {
  assert (squash_zpaq_thread_stream != NULL);
  squash_stream_yield (squash_zpaq_thread_stream, SQUASH_FAILED);
}

SquashZpaqIO::SquashZpaqIO (SquashZpaqStream* stream) {
  this->stream = stream;
}

int SquashZpaqIO::get () {
  SquashStream* stream = (SquashStream*) this->stream;

  while (stream->avail_in == 0 && this->stream->operation == SQUASH_OPERATION_PROCESS) {
    this->stream->operation = squash_stream_yield (stream, SQUASH_OK);
  }

  if (stream->avail_in == 0) {
    return -1;
  } else {
    int r = stream->next_in[0];
    stream->next_in++;
    stream->avail_in--;
    return r;
  }
}

void SquashZpaqIO::put (int c) {
  uint8_t v = (uint8_t) c;
  this->write ((const char*) &v, 1);
}

void SquashZpaqIO::write (const char* buf, int n) {
  SquashStream* stream = (SquashStream*) this->stream;

  size_t written = 0;
  size_t remaining = (size_t) n;
  while (remaining > 0) {
    const size_t cp_size = (remaining < stream->avail_out) ? remaining : stream->avail_out;

    memcpy (stream->next_out, buf + written, cp_size);

    written += cp_size;
    remaining -= cp_size;
    stream->next_out += cp_size;
    stream->avail_out -= cp_size;

    if (remaining != 0)
      this->stream->operation = squash_stream_yield (stream, SQUASH_PROCESSING);
  }
}

static void
squash_zpaq_options_init (SquashZpaqOptions* options, SquashCodec* codec, SquashDestroyNotify destroy_notify) {
  assert (options != NULL);

  squash_options_init ((SquashOptions*) options, codec, destroy_notify);

	options->level = SQUASH_ZPAQ_DEFAULT_LEVEL;
}

static SquashZpaqOptions*
squash_zpaq_options_new (SquashCodec* codec) {
  SquashZpaqOptions* options;

  options = (SquashZpaqOptions*) malloc (sizeof (SquashZpaqOptions));
  squash_zpaq_options_init (options, codec, squash_zpaq_options_free);

  return options;
}

static void
squash_zpaq_options_destroy (void* options) {
  squash_options_destroy ((SquashOptions*) options);
}

static void
squash_zpaq_options_free (void* options) {
  squash_zpaq_options_destroy ((SquashZpaqOptions*) options);
  free (options);
}

static SquashOptions*
squash_zpaq_create_options (SquashCodec* codec) {
  return (SquashOptions*) squash_zpaq_options_new (codec);
}

static SquashStatus
squash_zpaq_parse_option (SquashOptions* options, const char* key, const char* value) {
  SquashZpaqOptions* opts = (SquashZpaqOptions*) options;
  char* endptr = NULL;

  assert (opts != NULL);

  if (strcasecmp (key, "level") == 0) {
    const int level = strtol (value, &endptr, 0);
    if ( *endptr == '\0' && level >= 1 && level <= 5 ) {
      opts->level = level;
    } else {
      return SQUASH_BAD_VALUE;
    }
  } else {
    return SQUASH_BAD_PARAM;
  }

  return SQUASH_OK;
}

static SquashZpaqStream* squash_zpaq_stream_new (SquashCodec* codec,
                                                 SquashStreamType stream_type,
                                                 SquashZpaqOptions* options) {
  SquashZpaqStream* stream;

  assert (codec != NULL);
  assert (stream_type == SQUASH_STREAM_COMPRESS || stream_type == SQUASH_STREAM_DECOMPRESS);

  stream = (SquashZpaqStream*) malloc (sizeof (SquashZpaqStream));
  squash_zpaq_stream_init (stream, codec, stream_type, options, squash_zpaq_stream_free);

  return stream;
}

static void
squash_zpaq_stream_init (SquashZpaqStream* stream,
                         SquashCodec* codec,
                         SquashStreamType stream_type,
                         SquashZpaqOptions* options,
                         SquashDestroyNotify destroy_notify) {
  squash_stream_init ((SquashStream*) stream, codec, stream_type, (SquashOptions*) options, destroy_notify);

  stream->stream = new SquashZpaqIO(stream);
  stream->stream->stream = stream;
}

static void
squash_zpaq_stream_destroy (void* stream) {
  delete ((SquashZpaqStream*) stream)->stream;

  squash_stream_destroy (stream);
}

static void
squash_zpaq_stream_free (void* stream) {
  squash_zpaq_stream_destroy (stream);
  free (stream);
}

static SquashStream*
squash_zpaq_create_stream (SquashCodec* codec, SquashStreamType stream_type, SquashOptions* options) {
  return (SquashStream*) squash_zpaq_stream_new (codec, stream_type, (SquashZpaqOptions*) options);
}

static SquashStatus
squash_zpaq_process_stream (SquashStream* stream, SquashOperation operation) {
  SquashZpaqStream* s = (SquashZpaqStream*) stream;

  s->operation = operation;

  if (stream->stream_type == SQUASH_STREAM_COMPRESS) {
    char level_s[2];
    snprintf (level_s, sizeof(level_s), "%d", (stream->options != NULL) ? ((SquashZpaqOptions*) stream->options)->level : SQUASH_ZPAQ_DEFAULT_LEVEL);

    squash_zpaq_thread_stream = stream;
    compress (s->stream, s->stream, level_s);
    squash_zpaq_thread_stream = NULL;
  } else {
    squash_zpaq_thread_stream = stream;
    decompress (s->stream, s->stream);
    squash_zpaq_thread_stream = NULL;
  }

  return SQUASH_OK;
}

static size_t
squash_zpaq_get_max_compressed_size (SquashCodec* codec, size_t uncompressed_length) {
  return
    uncompressed_length +
    ((uncompressed_length / 100) * 1) + ((uncompressed_length % 100) > 0 ? 1 : 0) +
    377;
}

extern "C" SquashStatus
squash_plugin_init_codec (SquashCodec* codec, SquashCodecFuncs* funcs) {
  const char* name = squash_codec_get_name (codec);

  if (strcmp ("zpaq", name) == 0) {
    funcs->info = SQUASH_CODEC_INFO_RUN_IN_THREAD;
    funcs->create_options = squash_zpaq_create_options;
    funcs->parse_option = squash_zpaq_parse_option;
    funcs->create_stream = squash_zpaq_create_stream;
    funcs->process_stream = squash_zpaq_process_stream;
    funcs->get_max_compressed_size = squash_zpaq_get_max_compressed_size;
  } else {
    return SQUASH_UNABLE_TO_LOAD;
  }

  return SQUASH_OK;
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <file.hpp>
#include <system.hpp>
#include <errors.hpp>
#include <print.hpp>

int main(int, char*[]){
    auto fd = open("/sys/uptime");

    if(fd.valid()){
        auto buffer = new char[64];

        auto content_result = read(*fd, buffer, 64);

        if(content_result.valid()){
            auto chars = *content_result;

            std::string value_str;
            value_str.reserve(chars);

            for(size_t i = 0; i < chars; ++i){
                value_str += buffer[i];
            }

            auto value = std::parse(value_str);

            printf("Uptime: %u:%u:%u\n", value / 3600, (value % 3600) / 60, value % 60);
        } else {
             printf("uptime: error: %s\n", std::error_message(content_result.error()));
        }

        delete[] buffer;

        close(*fd);
    } else {
        printf("uptime: error: %s\n", std::error_message(fd.error()));
    }

    exit(0);
<commit_msg>Clean<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2014.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include <file.hpp>
#include <system.hpp>
#include <errors.hpp>
#include <print.hpp>

int main(int, char*[]){
    auto fd = open("/sys/uptime");

    if(fd.valid()){
        auto buffer = new char[64];

        auto content_result = read(*fd, buffer, 64);

        if(content_result.valid()){
            auto chars = *content_result;

            std::string value_str;
            value_str.reserve(chars);

            for(size_t i = 0; i < chars; ++i){
                value_str += buffer[i];
            }

            auto value = std::parse(value_str);

            printf("Uptime: %u:%u:%u\n", value / 3600, (value % 3600) / 60, value % 60);
        } else {
             printf("uptime: error: %s\n", std::error_message(content_result.error()));
        }

        delete[] buffer;

        close(*fd);
    } else {
        printf("uptime: error: %s\n", std::error_message(fd.error()));
    }

    exit(0);
}
<|endoftext|>


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

#include "assetoperations.h"
#include "assetoperations_p.h"
#include <QDebug>

#include "assethandler.h"
#include "installjob.h"
#include "installjobsmodel.h"
#include "uninstalljob.h"

namespace Bodega
{

class AssetOperations::Private {
public:
    Private(AssetOperations *operations)
        : q(operations),
          handler(0),
          wasInstalled(false),
          progress(0)
    {}

    ~Private()
    {}

    void assetDownloadComplete(NetworkJob *job);
    bool ready();
    void checkInstalled();
    void progressHasChanged(qreal progress);

    AssetOperations *q;
    AssetHandler *handler;
    AssetInfo assetInfo;
    ChangeLog changeLog;
    Tags assetTags;
    QString mimetype;
    bool wasInstalled;
    qreal progress;
    RatingsModel *ratingsModel;
};

void AssetOperations::Private::assetDownloadComplete(NetworkJob *job)
{
    AssetJob *assetJob = qobject_cast<AssetJob *>(job);
    Q_ASSERT(assetJob);

    if (!job->failed()) {
        assetInfo = assetJob->info();
        assetTags = assetJob->tags();
        changeLog = assetJob->changeLog();

        delete handler;
        handler = 0;

        //FIXME: may ever have more than one mimetype?
        QHash<QString, QString> tags = assetJob->tags();
        const QString mimeTag(QLatin1String("mimetype"));
        mimetype = tags.value(mimeTag);
        if (!mimetype.isEmpty()) {
            handler = AssetHandler::create(mimetype, q);
        }
    }

    if (handler) {
        QObject::connect(handler, SIGNAL(ready()), q, SIGNAL(ready()));
        QObject::connect(handler, SIGNAL(ready()), q, SLOT(checkInstalled()));
        if (handler->isReady()) {
            emit q->ready();
        }
        QObject::connect(handler, SIGNAL(installedChanged()), q, SIGNAL(installedChanged()));
    } else {
        emit q->failed();
    }
    checkInstalled();
}

bool AssetOperations::Private::ready()
{
    return handler && handler->isReady();
}

void AssetOperations::Private::checkInstalled()
{
    if (ready() && wasInstalled != handler->isInstalled()) {
        wasInstalled = handler->isInstalled();
        emit q->installedChanged();
    }
}

void AssetOperations::Private::progressHasChanged(qreal prog)
{
    progress = prog;
    emit q->progressChanged(progress);
}

AssetOperations::AssetOperations(const QString &assetId, Session *session)
    : QObject(session),
      d(new AssetOperations::Private(this))
{
    d->ratingsModel->setSession(session);

    AssetJob *aj = session->asset(assetId, AssetJob::ShowChangeLog);
    QObject::connect(aj, SIGNAL(jobFinished(Bodega::NetworkJob*)),
                     this, SLOT(assetDownloadComplete(Bodega::NetworkJob*)));
}

AssetOperations::~AssetOperations()
{
}

const ChangeLog &AssetOperations::changeLog() const
{
    return d->changeLog;
}

const AssetInfo &AssetOperations::assetInfo() const
{
    return d->assetInfo;
}

const Bodega::Tags& AssetOperations::assetTags() const
{
    return d->assetTags;
}

bool AssetOperations::isReady() const
{
    return d->ready();
}

QString AssetOperations::launchText() const
{
    if (d->ready()) {
        return d->handler->launchText();
    }

    return QString();
}

bool AssetOperations::isInstalled() const
{
    if (d->ready()) {
        return d->handler->isInstalled();
    }

    return false;
}

QString AssetOperations::mimetype() const
{
    return d->mimetype;
}

qreal AssetOperations::progress() const
{
    return d->progress;
}

Bodega::InstallJob *AssetOperations::install(QNetworkReply *reply, Session *session)
{
    if (d->ready()) {
        Bodega::InstallJob *job = d->handler->install(reply, session);
        if (job) {
            connect(job, SIGNAL(jobFinished(Bodega::NetworkJob *)), this, SLOT(checkInstalled()));
        }
        session->installJobsModel()->addJob(d->assetInfo, job);
        connect(job, SIGNAL(progressChanged(qreal)), this, SLOT(progressHasChanged(qreal)));
        return job;
    }

    return new InstallJob(reply, session);
}

Bodega::UninstallJob *AssetOperations::uninstall(Session *session)
{
    if (d->ready()) {
        Bodega::UninstallJob *job = d->handler->uninstall(session);
        if (job) {
            connect(job, SIGNAL(jobFinished(Bodega::UninstallJob *)), this, SLOT(checkInstalled()));
            d->checkInstalled();
        }
        return job;
    }

    return 0;
}

void AssetOperations::launch()
{
    if (d->ready()) {
        d->handler->launch();
    }
}

QAbstractItemModel *AssetOperations::ratingsModel()
{
    return qobject_cast<QAbstractItemModel*>(d->ratingsModel);
}

}

#include "assetoperations.moc"
<commit_msg>give instance to the mode<commit_after>/*
 *   Copyright 2012 Coherent Theory LLC
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "assetoperations.h"
#include "assetoperations_p.h"
#include <QDebug>

#include "assethandler.h"
#include "installjob.h"
#include "installjobsmodel.h"
#include "uninstalljob.h"

namespace Bodega
{

class AssetOperations::Private {
public:
    Private(AssetOperations *operations)
        : q(operations),
          handler(0),
          wasInstalled(false),
          progress(0),
          ratingsModel(new RatingsModel(q))
    {}

    ~Private()
    {}

    void assetDownloadComplete(NetworkJob *job);
    bool ready();
    void checkInstalled();
    void progressHasChanged(qreal progress);

    AssetOperations *q;
    AssetHandler *handler;
    AssetInfo assetInfo;
    ChangeLog changeLog;
    Tags assetTags;
    QString mimetype;
    bool wasInstalled;
    qreal progress;
    RatingsModel *ratingsModel;
};

void AssetOperations::Private::assetDownloadComplete(NetworkJob *job)
{
    AssetJob *assetJob = qobject_cast<AssetJob *>(job);
    Q_ASSERT(assetJob);

    ratingsModel->setAssetJob(assetJob);

    if (!job->failed()) {
        assetInfo = assetJob->info();
        assetTags = assetJob->tags();
        changeLog = assetJob->changeLog();

        delete handler;
        handler = 0;

        //FIXME: may ever have more than one mimetype?
        QHash<QString, QString> tags = assetJob->tags();
        const QString mimeTag(QLatin1String("mimetype"));
        mimetype = tags.value(mimeTag);
        if (!mimetype.isEmpty()) {
            handler = AssetHandler::create(mimetype, q);
        }
    }

    if (handler) {
        QObject::connect(handler, SIGNAL(ready()), q, SIGNAL(ready()));
        QObject::connect(handler, SIGNAL(ready()), q, SLOT(checkInstalled()));
        if (handler->isReady()) {
            emit q->ready();
        }
        QObject::connect(handler, SIGNAL(installedChanged()), q, SIGNAL(installedChanged()));
    } else {
        emit q->failed();
    }
    checkInstalled();
}

bool AssetOperations::Private::ready()
{
    return handler && handler->isReady();
}

void AssetOperations::Private::checkInstalled()
{
    if (ready() && wasInstalled != handler->isInstalled()) {
        wasInstalled = handler->isInstalled();
        emit q->installedChanged();
    }
}

void AssetOperations::Private::progressHasChanged(qreal prog)
{
    progress = prog;
    emit q->progressChanged(progress);
}

AssetOperations::AssetOperations(const QString &assetId, Session *session)
    : QObject(session),
      d(new AssetOperations::Private(this))
{
    d->ratingsModel->setSession(session);

    AssetJob *aj = session->asset(assetId, AssetJob::ShowChangeLog);
    QObject::connect(aj, SIGNAL(jobFinished(Bodega::NetworkJob*)),
                     this, SLOT(assetDownloadComplete(Bodega::NetworkJob*)));
}

AssetOperations::~AssetOperations()
{
}

const ChangeLog &AssetOperations::changeLog() const
{
    return d->changeLog;
}

const AssetInfo &AssetOperations::assetInfo() const
{
    return d->assetInfo;
}

const Bodega::Tags& AssetOperations::assetTags() const
{
    return d->assetTags;
}

bool AssetOperations::isReady() const
{
    return d->ready();
}

QString AssetOperations::launchText() const
{
    if (d->ready()) {
        return d->handler->launchText();
    }

    return QString();
}

bool AssetOperations::isInstalled() const
{
    if (d->ready()) {
        return d->handler->isInstalled();
    }

    return false;
}

QString AssetOperations::mimetype() const
{
    return d->mimetype;
}

qreal AssetOperations::progress() const
{
    return d->progress;
}

Bodega::InstallJob *AssetOperations::install(QNetworkReply *reply, Session *session)
{
    if (d->ready()) {
        Bodega::InstallJob *job = d->handler->install(reply, session);
        if (job) {
            connect(job, SIGNAL(jobFinished(Bodega::NetworkJob *)), this, SLOT(checkInstalled()));
        }
        session->installJobsModel()->addJob(d->assetInfo, job);
        connect(job, SIGNAL(progressChanged(qreal)), this, SLOT(progressHasChanged(qreal)));
        return job;
    }

    return new InstallJob(reply, session);
}

Bodega::UninstallJob *AssetOperations::uninstall(Session *session)
{
    if (d->ready()) {
        Bodega::UninstallJob *job = d->handler->uninstall(session);
        if (job) {
            connect(job, SIGNAL(jobFinished(Bodega::UninstallJob *)), this, SLOT(checkInstalled()));
            d->checkInstalled();
        }
        return job;
    }

    return 0;
}

void AssetOperations::launch()
{
    if (d->ready()) {
        d->handler->launch();
    }
}

QAbstractItemModel *AssetOperations::ratingsModel()
{
    return qobject_cast<QAbstractItemModel*>(d->ratingsModel);
}

}

#include "assetoperations.moc"
<|endoftext|>


<commit_before>

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include "GameState.h"
#include "AI.h"

class GameState;

void solve(GameState gs) {
	return;
}

int abprune(GameState curGS, int depth, int a, int b, bool maxi)
{
    if (depth == 0) { // || curGS.isEOG()) {
        //fprintf(stderr, "Entered first if. In abprune now with depth %i\n", depth);
        //if (curGS.isDraw()) {
        //    return 0;
        //}
        return 0;
        //return evalHeur(curGS);
    }

    //fprintf(stderr, "In abprune now with depth %i\n", depth);

    std::set<GameState> s = curGS.findNextMoves();
    std::vector<GameState> lNextStates(s.begin(),s.end());

    int vecSize = lNextStates.size();

    if (maxi) { //Maximizing player's turn
        for (int i = 0;i<vecSize;i++) {
            a = std::max(a, abprune(lNextStates[i], depth-1, a, b, !maxi));
            if (b <= a) {
                break;
            }
        }
        return a;
    } else {    //Minimizing player's turn
        for (int i = 0;i<vecSize;i++) {
            b = std::min(b, abprune(lNextStates[i], depth-1, a, b, !maxi));
            if (b <= a) {
                break;
            }
        }
        return b;
    }
}

<commit_msg>deadlock-alg men inte klar än<commit_after>#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include "GameState.h"
#include "AI.h"

class GameState;

void solve(GameState gs) {
	return;
}

int abprune(GameState curGS, int depth, int a, int b, bool maxi)
{
    if (depth == 0) { // || curGS.isEOG()) {
        //fprintf(stderr, "Entered first if. In abprune now with depth %i\n", depth);
        //if (curGS.isDraw()) {
        //    return 0;
        //}
        return 0;
        //return evalHeur(curGS);
    }

    //fprintf(stderr, "In abprune now with depth %i\n", depth);

    std::set<GameState> s = curGS.findNextMoves();
    std::vector<GameState> lNextStates(s.begin(),s.end());

    int vecSize = lNextStates.size();

    if (maxi) { //Maximizing player's turn
        for (int i = 0;i<vecSize;i++) {
            a = std::max(a, abprune(lNextStates[i], depth-1, a, b, !maxi));
            if (b <= a) {
                break;
            }
        }
        return a;
    } else {    //Minimizing player's turn
        for (int i = 0;i<vecSize;i++) {
            b = std::min(b, abprune(lNextStates[i], depth-1, a, b, !maxi));
            if (b <= a) {
                break;
            }
        }
        return b;
    }
}

<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2016 ScyllaDB
 */

#include "loopback_socket.hh"
#include "rpc/rpc.hh"
#include "test-utils.hh"
#include "core/thread.hh"
#include "core/sleep.hh"

using namespace seastar;

struct serializer {
};

template <typename T, typename Output>
inline
void write_arithmetic_type(Output& out, T v) {
    static_assert(std::is_arithmetic<T>::value, "must be arithmetic type");
    return out.write(reinterpret_cast<const char*>(&v), sizeof(T));
}

template <typename T, typename Input>
inline
T read_arithmetic_type(Input& in) {
    static_assert(std::is_arithmetic<T>::value, "must be arithmetic type");
    T v;
    in.read(reinterpret_cast<char*>(&v), sizeof(T));
    return v;
}

template <typename Output>
inline void write(serializer, Output& output, int32_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, uint32_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, int64_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, uint64_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, double v) { return write_arithmetic_type(output, v); }
template <typename Input>
inline int32_t read(serializer, Input& input, rpc::type<int32_t>) { return read_arithmetic_type<int32_t>(input); }
template <typename Input>
inline uint32_t read(serializer, Input& input, rpc::type<uint32_t>) { return read_arithmetic_type<uint32_t>(input); }
template <typename Input>
inline uint64_t read(serializer, Input& input, rpc::type<uint64_t>) { return read_arithmetic_type<uint64_t>(input); }
template <typename Input>
inline uint64_t read(serializer, Input& input, rpc::type<int64_t>) { return read_arithmetic_type<int64_t>(input); }
template <typename Input>
inline double read(serializer, Input& input, rpc::type<double>) { return read_arithmetic_type<double>(input); }

template <typename Output>
inline void write(serializer, Output& out, const sstring& v) {
    write_arithmetic_type(out, uint32_t(v.size()));
    out.write(v.c_str(), v.size());
}

template <typename Input>
inline sstring read(serializer, Input& in, rpc::type<sstring>) {
    auto size = read_arithmetic_type<uint32_t>(in);
    sstring ret(sstring::initialized_later(), size);
    in.read(ret.begin(), size);
    return ret;
}

using test_rpc_proto = rpc::protocol<serializer>;
using connect_fn = std::function<test_rpc_proto::client (ipv4_addr addr)>;

class rpc_socket_impl : public net::socket_impl {
    promise<connected_socket> _p;
    bool _connect;
    loopback_socket_impl _socket;
public:
    rpc_socket_impl(loopback_connection_factory& factory, bool connect)
            : _connect(connect), _socket(factory) {
    }
    virtual future<connected_socket> connect(socket_address sa, socket_address local, transport proto = transport::TCP) override {
        return _connect ? _socket.connect(sa, local, proto) : _p.get_future();
    }
    virtual void shutdown() override {
        if (_connect) {
            _socket.shutdown();
        } else {
            _p.set_exception(std::make_exception_ptr(std::system_error(ECONNABORTED, std::system_category())));
        }
    }
};

future<>
with_rpc_env(rpc::resource_limits resource_limits, bool connect,
        std::function<future<> (test_rpc_proto& proto, test_rpc_proto::server& server, connect_fn connect)> test_fn) {
    struct state {
        test_rpc_proto proto{serializer()};
        loopback_connection_factory lcf;
        std::unique_ptr<test_rpc_proto::server> server;
    };
    return do_with(state(), [=] (state& s) {
        s.server = std::make_unique<test_rpc_proto::server>(s.proto, s.lcf.get_server_socket(), resource_limits);
        auto make_client = [&s, connect] (ipv4_addr addr) {
            auto socket = seastar::socket(std::make_unique<rpc_socket_impl>(s.lcf, connect));
            return test_rpc_proto::client(s.proto, std::move(socket), addr);
        };
        return test_fn(s.proto, *s.server, make_client).finally([&] {
            return s.server->stop();
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_connect) {
    return with_rpc_env({}, true, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            auto sum = proto.register_handler(1, [](int a, int b) {
                return make_ready_future<int>(a+b);
            });
            auto result = sum(c1, 2, 3).get0();
            BOOST_REQUIRE_EQUAL(result, 2 + 3);
            c1.stop().get();
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_connect_abort) {
    return with_rpc_env({}, false, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            auto f = proto.register_handler(1, []() { return make_ready_future<>(); });
            c1.stop().get0();
            try {
                f(c1).get0();
                BOOST_REQUIRE(false);
            } catch (...) {}
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_cancel) {
    using namespace std::chrono_literals;
    return with_rpc_env({}, true, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            bool rpc_executed = false;
            int good = 0;
            auto call = proto.register_handler(1, [&rpc_executed]() {
                rpc_executed = true; return sleep(1ms);
            });
            rpc::cancellable cancel;
            auto f = call(c1, cancel);
            // cancel send side
            cancel.cancel();
            try {
                f.get();
            } catch(rpc::canceled_error&) {
                good += !rpc_executed;
            };
            f = call(c1, cancel);
            // cancel wait side
            sleep(500us).then([cancel = std::move(cancel)] () mutable {
                cancel.cancel();
            }).get();
            try {
                f.get();
            } catch(rpc::canceled_error&) {
                good += rpc_executed;
            };
            c1.stop().get();
            BOOST_REQUIRE_EQUAL(good, 2);
        });
    });
}
<commit_msg>rpc_test: fix cancel test to not depend on timing.<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */
/*
 * Copyright (C) 2016 ScyllaDB
 */

#include "loopback_socket.hh"
#include "rpc/rpc.hh"
#include "test-utils.hh"
#include "core/thread.hh"
#include "core/sleep.hh"

using namespace seastar;

struct serializer {
};

template <typename T, typename Output>
inline
void write_arithmetic_type(Output& out, T v) {
    static_assert(std::is_arithmetic<T>::value, "must be arithmetic type");
    return out.write(reinterpret_cast<const char*>(&v), sizeof(T));
}

template <typename T, typename Input>
inline
T read_arithmetic_type(Input& in) {
    static_assert(std::is_arithmetic<T>::value, "must be arithmetic type");
    T v;
    in.read(reinterpret_cast<char*>(&v), sizeof(T));
    return v;
}

template <typename Output>
inline void write(serializer, Output& output, int32_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, uint32_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, int64_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, uint64_t v) { return write_arithmetic_type(output, v); }
template <typename Output>
inline void write(serializer, Output& output, double v) { return write_arithmetic_type(output, v); }
template <typename Input>
inline int32_t read(serializer, Input& input, rpc::type<int32_t>) { return read_arithmetic_type<int32_t>(input); }
template <typename Input>
inline uint32_t read(serializer, Input& input, rpc::type<uint32_t>) { return read_arithmetic_type<uint32_t>(input); }
template <typename Input>
inline uint64_t read(serializer, Input& input, rpc::type<uint64_t>) { return read_arithmetic_type<uint64_t>(input); }
template <typename Input>
inline uint64_t read(serializer, Input& input, rpc::type<int64_t>) { return read_arithmetic_type<int64_t>(input); }
template <typename Input>
inline double read(serializer, Input& input, rpc::type<double>) { return read_arithmetic_type<double>(input); }

template <typename Output>
inline void write(serializer, Output& out, const sstring& v) {
    write_arithmetic_type(out, uint32_t(v.size()));
    out.write(v.c_str(), v.size());
}

template <typename Input>
inline sstring read(serializer, Input& in, rpc::type<sstring>) {
    auto size = read_arithmetic_type<uint32_t>(in);
    sstring ret(sstring::initialized_later(), size);
    in.read(ret.begin(), size);
    return ret;
}

using test_rpc_proto = rpc::protocol<serializer>;
using connect_fn = std::function<test_rpc_proto::client (ipv4_addr addr)>;

class rpc_socket_impl : public net::socket_impl {
    promise<connected_socket> _p;
    bool _connect;
    loopback_socket_impl _socket;
public:
    rpc_socket_impl(loopback_connection_factory& factory, bool connect)
            : _connect(connect), _socket(factory) {
    }
    virtual future<connected_socket> connect(socket_address sa, socket_address local, transport proto = transport::TCP) override {
        return _connect ? _socket.connect(sa, local, proto) : _p.get_future();
    }
    virtual void shutdown() override {
        if (_connect) {
            _socket.shutdown();
        } else {
            _p.set_exception(std::make_exception_ptr(std::system_error(ECONNABORTED, std::system_category())));
        }
    }
};

future<>
with_rpc_env(rpc::resource_limits resource_limits, bool connect,
        std::function<future<> (test_rpc_proto& proto, test_rpc_proto::server& server, connect_fn connect)> test_fn) {
    struct state {
        test_rpc_proto proto{serializer()};
        loopback_connection_factory lcf;
        std::unique_ptr<test_rpc_proto::server> server;
    };
    return do_with(state(), [=] (state& s) {
        s.server = std::make_unique<test_rpc_proto::server>(s.proto, s.lcf.get_server_socket(), resource_limits);
        auto make_client = [&s, connect] (ipv4_addr addr) {
            auto socket = seastar::socket(std::make_unique<rpc_socket_impl>(s.lcf, connect));
            return test_rpc_proto::client(s.proto, std::move(socket), addr);
        };
        return test_fn(s.proto, *s.server, make_client).finally([&] {
            return s.server->stop();
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_connect) {
    return with_rpc_env({}, true, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            auto sum = proto.register_handler(1, [](int a, int b) {
                return make_ready_future<int>(a+b);
            });
            auto result = sum(c1, 2, 3).get0();
            BOOST_REQUIRE_EQUAL(result, 2 + 3);
            c1.stop().get();
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_connect_abort) {
    return with_rpc_env({}, false, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            auto f = proto.register_handler(1, []() { return make_ready_future<>(); });
            c1.stop().get0();
            try {
                f(c1).get0();
                BOOST_REQUIRE(false);
            } catch (...) {}
        });
    });
}

SEASTAR_TEST_CASE(test_rpc_cancel) {
    using namespace std::chrono_literals;
    return with_rpc_env({}, true, [] (test_rpc_proto& proto, test_rpc_proto::server& s, connect_fn connect) {
        return seastar::async([&proto, &s, connect] {
            auto c1 = connect(ipv4_addr());
            bool rpc_executed = false;
            int good = 0;
            promise<> handler_called;
            future<> f_handler_called = handler_called.get_future();
            auto call = proto.register_handler(1, [&rpc_executed,  handler_called = std::move(handler_called)] () mutable {
                handler_called.set_value(); rpc_executed = true; return sleep(1ms);
            });
            rpc::cancellable cancel;
            auto f = call(c1, cancel);
            // cancel send side
            cancel.cancel();
            try {
                f.get();
            } catch(rpc::canceled_error&) {
                good += !rpc_executed;
            };
            f = call(c1, cancel);
            // cancel wait side
            f_handler_called.then([cancel = std::move(cancel)] () mutable {
                cancel.cancel();
            }).get();
            try {
                f.get();
            } catch(rpc::canceled_error&) {
                good += 10*rpc_executed;
            };
            c1.stop().get();
            BOOST_REQUIRE_EQUAL(good, 11);
        });
    });
}
<|endoftext|>