<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) 2017 ScyllaDB Ltd.
 */

#pragma once

#include "future.hh"
#include "chunked_fifo.hh"
#include "function_traits.hh"
#include "util/reference_wrapper.hh"
#include "util/gcc6-concepts.hh"

namespace seastar {

/// \defgroup execution-stages Execution Stages
///
/// \brief
/// Execution stages provide an infrastructure for processing function calls in
/// batches in order to improve instruction cache locality.
///
/// When the application logic becomes more and more complex and the length
/// of the data processing pipeline grows it may happen that the most
/// significant bottleneck are instruction cache misses. The solution for that
/// problem may be processing similar operations in batches so that instruction
/// cache locality is improved at the cost of potentially higher latencies and
/// worse data cache locality.
///
/// Execution stages allow batching calls to the specified function object.
/// Every time concrete_execution_stage::operator()() is used the function call
/// is added to the queue and a future is returned. Once the number of queued
/// calls reaches certain threshold the stage is flushed and a task is which
/// would execute these function calls is scheduled. Execution stages are also
/// flushed when the reactor polls for events.
///
/// When calling a function that is wrapped inside execution stage it is
/// important to remember that the actual function call will happen at some
/// later time and it has to be guaranteed the objects passed by lvalue
/// reference are still alive. In order to avoid accidental passing of a
/// temporary object by lvalue reference the interface of execution stages
/// accepts only lvalue references wrapped in reference_wrapper. It is safe to
/// pass rvalue references, they are decayed and the objects are moved. See
/// concrete_execution_stage::operator()() for more details.

/// \addtogroup execution-stages
/// @{

/// \cond internal
namespace internal {

// Execution wraps lreferences in reference_wrapper so that the caller is forced
// to use seastar::ref(). Then when the function is actually called the
// reference is unwrapped. However, we need to distinguish between functions
// which argument is lvalue reference and functions that take
// reference_wrapper<> as an argument and not unwrap the latter. To solve this
// issue reference_wrapper_for_es type is used for wrappings done automatically
// by execution stage.
template<typename T>
struct reference_wrapper_for_es : reference_wrapper<T> {
    reference_wrapper_for_es(reference_wrapper <T> rw) noexcept
        : reference_wrapper<T>(std::move(rw)) {}
};

template<typename T>
struct wrap_for_es {
    using type = T;
};

template<typename T>
struct wrap_for_es<T&> {
    using type = reference_wrapper_for_es<T>;
};

template<typename T>
struct wrap_for_es<T&&> {
    using type = T;
};

template<typename T>
decltype(auto) unwrap_for_es(T&& object) {
    return std::forward<T>(object);
}

template<typename T>
std::reference_wrapper<T> unwrap_for_es(reference_wrapper_for_es<T> ref) {
    return std::reference_wrapper<T>(ref.get());
}

}
/// \endcond

/// Base execution stage class
class execution_stage {
protected:
    bool _empty = true;
    bool _flush_scheduled = false;
protected:
    virtual void do_flush() noexcept = 0;
public:
    execution_stage();
    virtual ~execution_stage();

    execution_stage(const execution_stage&) = delete;

    /// Move constructor
    ///
    /// \warning It is illegal to move execution_stage after any operation has
    /// been pushed to it. The only reason why the move constructor is not
    /// deleted is the fact that C++14 does not guarantee return value
    /// optimisation which is required by make_execution_stage().
    execution_stage(execution_stage&&);

    /// Flushes execution stage
    ///
    /// Ensures that a task which would execute all queued operations is
    /// scheduled. Does not schedule a new task if there is one already pending
    /// or the queue is empty.
    ///
    /// \return true if a new task has been scheduled
    bool flush() noexcept {
        if (_empty || _flush_scheduled) {
            return false;
        }
        schedule(make_task([this] {
            do_flush();
            _flush_scheduled = false;
        }));
        _flush_scheduled = true;
        return true;
    };

    /// Checks whether there are pending operations.
    ///
    /// \return true if there is at least one queued operation
    bool poll() const noexcept {
        return !_empty;
    }
};

/// \cond internal
namespace internal {

class execution_stage_manager {
    std::vector<execution_stage*> _execution_stages;
private:
    execution_stage_manager() = default;
    execution_stage_manager(const execution_stage_manager&) = delete;
    execution_stage_manager(execution_stage_manager&&) = delete;
public:
    void register_execution_stage(execution_stage& stage) {
        _execution_stages.push_back(&stage);
    }
    void unregister_execution_stage(execution_stage& stage) noexcept {
        auto it = std::find(_execution_stages.begin(), _execution_stages.end(), &stage);
        _execution_stages.erase(it);
    }
    void update_execution_stage_registration(execution_stage& old_es, execution_stage& new_es) noexcept {
        auto it = std::find(_execution_stages.begin(), _execution_stages.end(), &old_es);
        *it = &new_es;
    }

    bool flush() noexcept {
        bool did_work = false;
        for (auto&& stage : _execution_stages) {
            did_work |= stage->flush();
        }
        return did_work;
    }
    bool poll() const noexcept {
        for (auto&& stage : _execution_stages) {
            if (stage->poll()) {
                return true;
            }
        }
        return false;
    }
public:
    static execution_stage_manager& get() noexcept {
        static thread_local execution_stage_manager instance;
        return instance;
    }
};

}
/// \endcond

/// \brief Concrete execution stage class
///
/// \note The recommended way of creating execution stages is to use
/// make_execution_stage().
///
/// \tparam Function function object to be executed by the stage
/// \tparam ReturnType return type of the function object
/// \tparam ArgsTuple tuple containing arguments to the function object, needs
///                   to have move constructor that doesn't throw
template<typename Function, typename ReturnType, typename ArgsTuple>
GCC6_CONCEPT(requires std::is_nothrow_move_constructible<ArgsTuple>::value)
class concrete_execution_stage final : public execution_stage {
    static_assert(std::is_nothrow_move_constructible<ArgsTuple>::value,
                  "Function arguments need to be nothrow move constructible");

    static constexpr size_t flush_threshold = 128;

    using return_type = futurize_t<ReturnType>;
    using promise_type = typename return_type::promise_type;
    using input_type = typename tuple_map_types<internal::wrap_for_es, ArgsTuple>::type;

    struct work_item {
        input_type _in;
        promise_type _ready;

        template<typename... Args>
        work_item(Args&&... args) : _in(std::forward<Args>(args)...) { }

        work_item(work_item&& other) = delete;
        work_item(const work_item&) = delete;
        work_item(work_item&) = delete;
    };
    chunked_fifo<work_item, flush_threshold> _queue;

    Function _function;
private:
    auto unwrap(input_type&& in) {
        return tuple_map(std::move(in), [] (auto&& obj) {
            return internal::unwrap_for_es(std::forward<decltype(obj)>(obj));
        });
    }

    virtual void do_flush() noexcept override {
        while (!_queue.empty()) {
            auto& wi = _queue.front();
            futurize<ReturnType>::apply(_function, unwrap(std::move(wi._in))).forward_to(std::move(wi._ready));
            _queue.pop_front();

            if (need_preempt()) {
                break;
            }
        }
        _empty = _queue.empty();
    }
public:
    explicit concrete_execution_stage(Function f) : _function(std::move(f)) {
        _queue.reserve(flush_threshold);
    }

    /// Enqueues a call to the stage's function
    ///
    /// Adds a function call to the queue. Objects passed by value are moved,
    /// rvalue references are decayed and the objects are moved, lvalue
    /// references need to be explicitly wrapped using seastar::ref().
    ///
    /// Usage example:
    /// ```
    /// void do_something(int&, int, std::vector<int>&&);
    /// thread_local auto stage = seastar::make_execution_stage(do_something);
    ///
    /// int global_value;
    ///
    /// future<> func(std::vector<int> vec) {
    ///     //return stage(global_value, 42, std::move(vec)); // fail: use seastar::ref to pass references
    ///     return stage(seastar::ref(global_value), 42, std::move(vec)); // ok
    /// }
    /// ```
    ///
    /// \param args arguments passed to the stage's function
    /// \return future containing the result of the call to the stage's function
    template<typename... Args>
    return_type operator()(Args&&... args) {
        _queue.emplace_back(std::forward<Args>(args)...);
        _empty = false;
        auto f = _queue.back()._ready.get_future();
        if (_queue.size() > flush_threshold) {
            flush();
        }
        return f;
    }
};

/// Creates a new execution stage
///
/// Wraps given function object in a concrete_execution_stage. All arguments
/// of the function object are required to have move constructors that do not
/// throw. Function object may return a future or an immediate object or void.
///
/// Moving execution stages is discouraged and illegal after first function
/// call is enqueued.
///
/// Usage example:
/// ```
/// double do_something(int);
/// thread_local auto stage1 = seastar::make_execution_stage(do_something);
///
/// future<double> func1(int val) {
///     return stage1(val);
/// }
///
/// future<double> do_some_io(int);
/// thread_local auto stage2 = seastar::make_execution_stage(do_some_io);
///
/// future<double> func2(int val) {
///     return stage2(val);
/// }
/// ```
///
/// \param fn function to be executed by the stage
/// \return concrete_execution_stage
template<typename Function>
auto make_execution_stage(Function&& fn) {
    using traits = function_traits<Function>;
    return concrete_execution_stage<Function, typename traits::return_type,
                                    typename traits::args_as_tuple>(std::forward<Function>(fn));
}

/// Creates a new execution stage from a member function
///
/// Wraps a pointer to member function in a concrete_execution_stage. When
/// a function call is pushed to the stage the first argument should be a
/// pointer to the object the function is a member of.
///
/// Usage example:
/// ```
/// struct foo {
///     void do_something(int);
/// };
///
/// thread_local auto stage = seastar::make_execution_stage(&foo::do_something);
///
/// future<> func(foo& obj, int val) {
///     return stage(&obj, val);
/// }
/// ```
///
/// \see make_execution_stage(Function&&)
/// \param fn member function to be executed by the stage
/// \return concrete_execution_stage
template<typename Ret, typename Object, typename... Args>
auto make_execution_stage(Ret (Object::*fn)(Args...)) {
    return concrete_execution_stage<decltype(std::mem_fn(fn)), Ret, std::tuple<Object*, Args...>>(std::mem_fn(fn));
}

template<typename Ret, typename Object, typename... Args>
auto make_execution_stage(Ret (Object::*fn)(Args...) const) {
    return concrete_execution_stage<decltype(std::mem_fn(fn)), Ret, std::tuple<const Object*, Args...>>(std::mem_fn(fn));
}

/// @}

inline execution_stage::execution_stage()
{
    internal::execution_stage_manager::get().register_execution_stage(*this);
}

inline execution_stage::~execution_stage()
{
    internal::execution_stage_manager::get().unregister_execution_stage(*this);
}

inline execution_stage::execution_stage(execution_stage&& other)
{
    internal::execution_stage_manager::get().update_execution_stage_registration(other, *this);
}

}
<commit_msg>execution_stage: add concept restraint for operator()<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) 2017 ScyllaDB Ltd.
 */

#pragma once

#include "future.hh"
#include "chunked_fifo.hh"
#include "function_traits.hh"
#include "util/reference_wrapper.hh"
#include "util/gcc6-concepts.hh"

namespace seastar {

/// \defgroup execution-stages Execution Stages
///
/// \brief
/// Execution stages provide an infrastructure for processing function calls in
/// batches in order to improve instruction cache locality.
///
/// When the application logic becomes more and more complex and the length
/// of the data processing pipeline grows it may happen that the most
/// significant bottleneck are instruction cache misses. The solution for that
/// problem may be processing similar operations in batches so that instruction
/// cache locality is improved at the cost of potentially higher latencies and
/// worse data cache locality.
///
/// Execution stages allow batching calls to the specified function object.
/// Every time concrete_execution_stage::operator()() is used the function call
/// is added to the queue and a future is returned. Once the number of queued
/// calls reaches certain threshold the stage is flushed and a task is which
/// would execute these function calls is scheduled. Execution stages are also
/// flushed when the reactor polls for events.
///
/// When calling a function that is wrapped inside execution stage it is
/// important to remember that the actual function call will happen at some
/// later time and it has to be guaranteed the objects passed by lvalue
/// reference are still alive. In order to avoid accidental passing of a
/// temporary object by lvalue reference the interface of execution stages
/// accepts only lvalue references wrapped in reference_wrapper. It is safe to
/// pass rvalue references, they are decayed and the objects are moved. See
/// concrete_execution_stage::operator()() for more details.

/// \addtogroup execution-stages
/// @{

/// \cond internal
namespace internal {

// Execution wraps lreferences in reference_wrapper so that the caller is forced
// to use seastar::ref(). Then when the function is actually called the
// reference is unwrapped. However, we need to distinguish between functions
// which argument is lvalue reference and functions that take
// reference_wrapper<> as an argument and not unwrap the latter. To solve this
// issue reference_wrapper_for_es type is used for wrappings done automatically
// by execution stage.
template<typename T>
struct reference_wrapper_for_es : reference_wrapper<T> {
    reference_wrapper_for_es(reference_wrapper <T> rw) noexcept
        : reference_wrapper<T>(std::move(rw)) {}
};

template<typename T>
struct wrap_for_es {
    using type = T;
};

template<typename T>
struct wrap_for_es<T&> {
    using type = reference_wrapper_for_es<T>;
};

template<typename T>
struct wrap_for_es<T&&> {
    using type = T;
};

template<typename T>
decltype(auto) unwrap_for_es(T&& object) {
    return std::forward<T>(object);
}

template<typename T>
std::reference_wrapper<T> unwrap_for_es(reference_wrapper_for_es<T> ref) {
    return std::reference_wrapper<T>(ref.get());
}

}
/// \endcond

/// Base execution stage class
class execution_stage {
protected:
    bool _empty = true;
    bool _flush_scheduled = false;
protected:
    virtual void do_flush() noexcept = 0;
public:
    execution_stage();
    virtual ~execution_stage();

    execution_stage(const execution_stage&) = delete;

    /// Move constructor
    ///
    /// \warning It is illegal to move execution_stage after any operation has
    /// been pushed to it. The only reason why the move constructor is not
    /// deleted is the fact that C++14 does not guarantee return value
    /// optimisation which is required by make_execution_stage().
    execution_stage(execution_stage&&);

    /// Flushes execution stage
    ///
    /// Ensures that a task which would execute all queued operations is
    /// scheduled. Does not schedule a new task if there is one already pending
    /// or the queue is empty.
    ///
    /// \return true if a new task has been scheduled
    bool flush() noexcept {
        if (_empty || _flush_scheduled) {
            return false;
        }
        schedule(make_task([this] {
            do_flush();
            _flush_scheduled = false;
        }));
        _flush_scheduled = true;
        return true;
    };

    /// Checks whether there are pending operations.
    ///
    /// \return true if there is at least one queued operation
    bool poll() const noexcept {
        return !_empty;
    }
};

/// \cond internal
namespace internal {

class execution_stage_manager {
    std::vector<execution_stage*> _execution_stages;
private:
    execution_stage_manager() = default;
    execution_stage_manager(const execution_stage_manager&) = delete;
    execution_stage_manager(execution_stage_manager&&) = delete;
public:
    void register_execution_stage(execution_stage& stage) {
        _execution_stages.push_back(&stage);
    }
    void unregister_execution_stage(execution_stage& stage) noexcept {
        auto it = std::find(_execution_stages.begin(), _execution_stages.end(), &stage);
        _execution_stages.erase(it);
    }
    void update_execution_stage_registration(execution_stage& old_es, execution_stage& new_es) noexcept {
        auto it = std::find(_execution_stages.begin(), _execution_stages.end(), &old_es);
        *it = &new_es;
    }

    bool flush() noexcept {
        bool did_work = false;
        for (auto&& stage : _execution_stages) {
            did_work |= stage->flush();
        }
        return did_work;
    }
    bool poll() const noexcept {
        for (auto&& stage : _execution_stages) {
            if (stage->poll()) {
                return true;
            }
        }
        return false;
    }
public:
    static execution_stage_manager& get() noexcept {
        static thread_local execution_stage_manager instance;
        return instance;
    }
};

}
/// \endcond

/// \brief Concrete execution stage class
///
/// \note The recommended way of creating execution stages is to use
/// make_execution_stage().
///
/// \tparam Function function object to be executed by the stage
/// \tparam ReturnType return type of the function object
/// \tparam ArgsTuple tuple containing arguments to the function object, needs
///                   to have move constructor that doesn't throw
template<typename Function, typename ReturnType, typename ArgsTuple>
GCC6_CONCEPT(requires std::is_nothrow_move_constructible<ArgsTuple>::value)
class concrete_execution_stage final : public execution_stage {
    static_assert(std::is_nothrow_move_constructible<ArgsTuple>::value,
                  "Function arguments need to be nothrow move constructible");

    static constexpr size_t flush_threshold = 128;

    using return_type = futurize_t<ReturnType>;
    using promise_type = typename return_type::promise_type;
    using input_type = typename tuple_map_types<internal::wrap_for_es, ArgsTuple>::type;

    struct work_item {
        input_type _in;
        promise_type _ready;

        template<typename... Args>
        work_item(Args&&... args) : _in(std::forward<Args>(args)...) { }

        work_item(work_item&& other) = delete;
        work_item(const work_item&) = delete;
        work_item(work_item&) = delete;
    };
    chunked_fifo<work_item, flush_threshold> _queue;

    Function _function;
private:
    auto unwrap(input_type&& in) {
        return tuple_map(std::move(in), [] (auto&& obj) {
            return internal::unwrap_for_es(std::forward<decltype(obj)>(obj));
        });
    }

    virtual void do_flush() noexcept override {
        while (!_queue.empty()) {
            auto& wi = _queue.front();
            futurize<ReturnType>::apply(_function, unwrap(std::move(wi._in))).forward_to(std::move(wi._ready));
            _queue.pop_front();

            if (need_preempt()) {
                break;
            }
        }
        _empty = _queue.empty();
    }
public:
    explicit concrete_execution_stage(Function f) : _function(std::move(f)) {
        _queue.reserve(flush_threshold);
    }

    /// Enqueues a call to the stage's function
    ///
    /// Adds a function call to the queue. Objects passed by value are moved,
    /// rvalue references are decayed and the objects are moved, lvalue
    /// references need to be explicitly wrapped using seastar::ref().
    ///
    /// Usage example:
    /// ```
    /// void do_something(int&, int, std::vector<int>&&);
    /// thread_local auto stage = seastar::make_execution_stage(do_something);
    ///
    /// int global_value;
    ///
    /// future<> func(std::vector<int> vec) {
    ///     //return stage(global_value, 42, std::move(vec)); // fail: use seastar::ref to pass references
    ///     return stage(seastar::ref(global_value), 42, std::move(vec)); // ok
    /// }
    /// ```
    ///
    /// \param args arguments passed to the stage's function
    /// \return future containing the result of the call to the stage's function
    template<typename... Args>
    GCC6_CONCEPT(requires std::is_constructible<input_type, Args...>::value)
    return_type operator()(Args&&... args) {
        _queue.emplace_back(std::forward<Args>(args)...);
        _empty = false;
        auto f = _queue.back()._ready.get_future();
        if (_queue.size() > flush_threshold) {
            flush();
        }
        return f;
    }
};

/// Creates a new execution stage
///
/// Wraps given function object in a concrete_execution_stage. All arguments
/// of the function object are required to have move constructors that do not
/// throw. Function object may return a future or an immediate object or void.
///
/// Moving execution stages is discouraged and illegal after first function
/// call is enqueued.
///
/// Usage example:
/// ```
/// double do_something(int);
/// thread_local auto stage1 = seastar::make_execution_stage(do_something);
///
/// future<double> func1(int val) {
///     return stage1(val);
/// }
///
/// future<double> do_some_io(int);
/// thread_local auto stage2 = seastar::make_execution_stage(do_some_io);
///
/// future<double> func2(int val) {
///     return stage2(val);
/// }
/// ```
///
/// \param fn function to be executed by the stage
/// \return concrete_execution_stage
template<typename Function>
auto make_execution_stage(Function&& fn) {
    using traits = function_traits<Function>;
    return concrete_execution_stage<Function, typename traits::return_type,
                                    typename traits::args_as_tuple>(std::forward<Function>(fn));
}

/// Creates a new execution stage from a member function
///
/// Wraps a pointer to member function in a concrete_execution_stage. When
/// a function call is pushed to the stage the first argument should be a
/// pointer to the object the function is a member of.
///
/// Usage example:
/// ```
/// struct foo {
///     void do_something(int);
/// };
///
/// thread_local auto stage = seastar::make_execution_stage(&foo::do_something);
///
/// future<> func(foo& obj, int val) {
///     return stage(&obj, val);
/// }
/// ```
///
/// \see make_execution_stage(Function&&)
/// \param fn member function to be executed by the stage
/// \return concrete_execution_stage
template<typename Ret, typename Object, typename... Args>
auto make_execution_stage(Ret (Object::*fn)(Args...)) {
    return concrete_execution_stage<decltype(std::mem_fn(fn)), Ret, std::tuple<Object*, Args...>>(std::mem_fn(fn));
}

template<typename Ret, typename Object, typename... Args>
auto make_execution_stage(Ret (Object::*fn)(Args...) const) {
    return concrete_execution_stage<decltype(std::mem_fn(fn)), Ret, std::tuple<const Object*, Args...>>(std::mem_fn(fn));
}

/// @}

inline execution_stage::execution_stage()
{
    internal::execution_stage_manager::get().register_execution_stage(*this);
}

inline execution_stage::~execution_stage()
{
    internal::execution_stage_manager::get().unregister_execution_stage(*this);
}

inline execution_stage::execution_stage(execution_stage&& other)
{
    internal::execution_stage_manager::get().update_execution_stage_registration(other, *this);
}

}
<|endoftext|>


<commit_before>#include "Player.h"
#include "Input.h"

Player::Player()
{
	m_Length = 3;
	m_Speed = 1;
}

Player::~Player()
{

}

void Player::Update()
{
	TranslatePlayer();
}

void Player::TranslatePlayer()
{
	//headpos += m_Direction;
}

void Player::SetDirection(char direction)
{
	if (direction == 'u')
	{
		m_Direction = glm::vec2(0,1);
	}

	else if (direction == 'd')
	{
		m_Direction = glm::vec2(0,-1);
	}

	else if (direction == 'l')
	{
		m_Direction = glm::vec2(-1,0);
	}

	else if (direction == 'r')
	{
		m_Direction = glm::vec2(1,0);
	}

	else
	{
		//LUL
	}
}

void Player::SetSpeed(float Speed)
{

}

void Player::Eat() // add to list and increment length
{
	m_Length += 1;
}

//bool Player::Check() // Check if player is colliding with wall or itself
//{
//	if ()
//	{
//
//	}
//
//	else
//	{
//		return false;
//	}
//}

void Player::Reset()
{
	m_Length = 2;
}

void Player::Die()
{
	// Reset the game, set pos, play sound?
	Reset();
}

void Player::SetTail(int tailpos)
{

}<commit_msg>Revert "f"<commit_after>#include "Player.h"
#include "Input.h"

Player::Player()
{
	m_Length = 3;
	m_Speed = 1;
}

Player::~Player()
{

}

void Player::Update()
{
	TranslatePlayer();
}

void Player::TranslatePlayer()
{
	headpos += m_Direction;
}

void Player::SetDirection(char direction)
{
	if (direction == 'u')
	{
		m_Direction = glm::vec2(0,1);
	}

	else if (direction == 'd')
	{
		m_Direction = glm::vec2(0,-1);
	}

	else if (direction == 'l')
	{
		m_Direction = glm::vec2(-1,0);
	}

	else if (direction == 'r')
	{
		m_Direction = glm::vec2(1,0);
	}

	else
	{
		//LUL
	}
}

void Player::SetSpeed(float Speed)
{

}

void Player::Eat() // add to list and increment length
{
	m_Length += 1;
}

//bool Player::Check() // Check if player is colliding with wall or itself
//{
//	if ()
//	{
//
//	}
//
//	else
//	{
//		return false;
//	}
//}

void Player::Reset()
{
	m_Length = 2;
}

void Player::Die()
{
	// Reset the game, set pos, play sound?
	Reset();
}

void Player::SetTail(int tailpos)
{

}<|endoftext|>


<commit_before>/**
 * @file xdmf_stream.cpp.cpp
 * @author salmon
 * @date 2015-12-02.
 */
#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>
#include "xdmf_stream.h"

#include "io.h"
#include "../gtl/utilities/log.h"
#include "../data_model/dataset.h"
#include "../parallel/parallel.h"

namespace simpla { namespace io
{

XDMFStream::XDMFStream()
{
    m_prefix_ = "";
}

XDMFStream::~XDMFStream()
{

}

std::string XDMFStream::path() const
{
    std::ostringstream buffer;
    buffer << "/";
    for (auto const &g_name: m_path_)
    {
        buffer << g_name << "/";
    }
    return buffer.str();
}

void XDMFStream::open(std::string const &prefix, std::string const &grid_name)
{

    close();

    m_prefix_ = prefix;

    m_file_stream_.open(m_prefix_ + ".xdmf");

    io::cd(m_prefix_ + ".h5:/");

    m_file_stream_
    << std::endl
    << "<Xdmf xmlns:xi=\"http://www.w3.org/2003/XInclude\" Version=\"2\">\n"
    << "<Domain>\n";

}

void XDMFStream::close()
{
    while (!m_path_.empty()) { close_grid(); }

    m_file_stream_
    << "</Domain>" << std::endl
    << "</Xdmf>" << std::endl;

    m_file_stream_.close();

}

void XDMFStream::open_grid(const std::string &g_name, int TAG)
{
    m_path_.push_back(g_name);

    io::cd(path());

    int level = static_cast<int>( m_path_.size());

    switch (TAG)
    {

        case COLLECTION_TEMPORAL:

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name <<
            "\" GridType=\"Collection\" CollectionType=\"Temporal\">" << std::endl;

            break;
        case TREE:

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name <<
            "\" GridType=\"Collection\" CollectionType=\"Tree\">" << std::endl;

            break;
        case UNIFORM://

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name << "\" GridType=\"Uniform\">" << std::endl;
            break;

    }

}

void XDMFStream::time(Real t)
{
    int level = static_cast<int>( m_path_.size());
    m_file_stream_ << std::setw(level * 2) << "" << "   <Time Value=\"" << t << "\"/>" << std::endl;

}

void XDMFStream::close_grid()
{
    if (!m_path_.empty())
    {
        int level = static_cast<int>( m_path_.size());
        m_file_stream_ << std::setw(level * 2) << "" << "</Grid> <!--" << path() << " --> " << std::endl;
        m_path_.pop_back();
    }

}

void _str_replace(std::string *s, std::string const &place_holder, std::string const &txt)
{
    s->replace(s->find(place_holder), place_holder.size(), txt);
}

void XDMFStream::write(std::string const &ds_name, DataSet const &ds)
{
    if (ds.empty())
    {
        VERBOSE << "Try to write empty dataset: [" << ds_name << "] Ignored!" << std::endl;
        return;
    }

    std::string url;

    url = io::write(ds_name, ds);

    int level = static_cast<int>( m_path_.size());

    int ndims;

    nTuple<size_t, MAX_NDIMS_OF_ARRAY> dims;

    std::tie(ndims, dims, std::ignore, std::ignore, std::ignore, std::ignore) = ds.dataspace.shape();

    if (ds.datatype.is_array())
    {
        int n = ds.datatype.rank();
        for (int i = 0; i < n; ++i)
        {
            dims[ndims + i] = ds.datatype.extent(i);
        }
        ndims += n;
    }

    m_file_stream_ << std::setw(level * 2 + 4) << "" << "<DataItem Dimensions=\"";

    for (int i = 0; i < ndims; ++i) { m_file_stream_ << dims[i] << " "; }

    m_file_stream_ << "\" " << "NumberType=\"Float\" Precision=\"4\" Format=\"HDF\">\n"

    << std::setw(level * 2 + 6) << "" << url << std::endl
    << std::setw(level * 2 + 4) << "" << "</DataItem>\n";

}

void XDMFStream::write(std::string const &ds_name,
                       AttributeBase const &attr)
{
    if (attr.dataset().empty())
    {
        VERBOSE << "Try to write empty Attribute: [" << ds_name << "] Ignored!" << std::endl;

        return;
    }
    static const char a_Center_str[][10] = {
            "Node",
            "Edge",
            "Face",
            "Cell"
    };

    static const char a_AttributeType_str[][10] = {
            "Scalar",
            "Vector",
            "Tensor",
            "Cell"
    };

    int level = static_cast<int>( m_path_.size());

    std::string center_type = a_Center_str[attr.center_type()];
    std::string attr_type = a_AttributeType_str[attr.rank()];

// ParaView do not support EDEG or FACE
//    std::string center_type = "Node";
//    std::string attr_type = a_AttributeType_str[
//            attr.rank()
//            + ((attr.center_type() == 1 || attr.center_type() == 2) ? 1 : 0)
//    ];

    m_file_stream_ << ""
    << std::setw(level * 2 + 2) << "" << "<Attribute Name=\"" << ds_name << " \" "
    << "AttributeType=\"" << attr_type << "\" "
    << "Center=\"" << center_type << "\">" << std::endl;

    write(ds_name, attr.dataset());

    m_file_stream_ << std::setw(level * 2 + 2) << "" << "</Attribute>" << std::endl;

    VERBOSE << "DataSet [" << ds_name << "] is saved in [" << path() << "]!" << std::endl;
}

/**
 *
 *
 *    XML Attribute : TopologyType = Polyvertex | Polyline | Polygon |
                                  Triangle | Quadrilateral | Tetrahedron | Pyramid| Wedge | Hexahedron |
                                  Edge_3 | Triangle_6 | Quadrilateral_8 | Tetrahedron_10 | Pyramid_13 |
                                  Wedge_15 | Wedge_18 | Hexahedron_20 | Hexahedron_24 | Hexahedron_27 |
                                  Mixed |
                                  2DSMesh | 2DRectMesh | 2DCoRectMesh |
                                  3DSMesh | 3DRectMesh | 3DCoRectMesh
 */

void XDMFStream::set_topology_geometry(std::string const &name, int ndims, size_t const *dims, Real const *xmin,
                                       Real const *dx)
{

    int level = static_cast<int>(m_path_.size());

    if (ndims == 3)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology TopologyType=\"3DCoRectMesh\""
        << std::setw(level * 2 + 2) << "" << " Dimensions=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry GeometryType=\"ORIGIN_DXDYDZ\">\n"
        << std::setw(level * 2 + 2) << "" << "  <DataItem Name=\"Origin\" Dimensions=\"3\" NumberType=\"Float\" "
                " Precision=\"4\" Format=\"XML\">" << xmin[0] << " " << xmin[1] << " " << xmin[2] << "</DataItem>\n"
        << std::setw(level * 2 + 2) << "" << "  <DataItem Name=\"Spacing\" Dimensions=\"3\" NumberType=\"Float\""
                " Precision=\"4\" Format=\"XML\">" << dx[0] << " " << dx[1] << " " << dx[2] << "  </DataItem >\n"
        << std::setw(level * 2 + 2) << "" << "</Geometry>";

    }
    else if (ndims == 2)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology TopologyType=\"2DCoRectMesh\""
        << std::setw(level * 2 + 2) << "" << "      Dimensions=\"" << dims[0] << " " << dims[1] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry GeometryType=\"ORIGIN_DXDY\">\n"
        << std::setw(level * 2 + 2) << "" << "   <DataItem Name=\"Origin\" Dimensions=\"2\" NumberType=\"Float\" "
                " Precision=\"4\" Format=\"XML\">" << xmin[0] << " " << xmin[1] << "</DataItem>\n"
        << std::setw(level * 2 + 2) << "" << "   <DataItem Name=\"Spacing\" Dimensions=\"2\" NumberType=\"Float\""
                " Precision=\"4\" Format=\"XML\">" << dx[0] << " " << dx[1] << "  </DataItem >\n"
        << std::setw(level * 2 + 2) << "" << "</Geometry>";

    }
    else
    {
        THROW_EXCEPTION_RUNTIME_ERROR(" number of dimension is not 2 or 3");
    }

}

void XDMFStream::set_topology_geometry(std::string const &name, DataSet const &ds)
{

    int level = static_cast<int>(m_path_.size());

    int ndims;

    nTuple<size_t, MAX_NDIMS_OF_ARRAY> dims;

    std::tie(ndims, dims, std::ignore, std::ignore, std::ignore, std::ignore) = ds.dataspace.shape();

    --ndims;
    if (ndims == 2)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "<Topology Name=\"" << name << "\" "
        << " TopologyType=\"2DSMesh\""
        << " NumberOfElements=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "<Geometry Name=  \"" << name << "\" GeometryType=\"XY\">" << std::endl;
        write("/Grid/points", ds);

        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << " </Geometry>\n";
    }

    else if (ndims == 3)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology Name=\"" << name << "\" "
        << "TopologyType=\"3DSMesh\"  NumberOfElements=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry Name=\"" << name << "\" GeometryType=\"XYZ\">\n";

        write("/Grid/points", ds);

        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "</Geometry>\n";
    }
    else
    {
        THROW_EXCEPTION_RUNTIME_ERROR("unsportted grid type");
    }
}

void XDMFStream::reference_topology_geometry(std::string const &id)
{
    int level = static_cast<int>( m_path_.size());
    m_file_stream_
    << std::setw(level * 2) << ""
    << "   <Topology Reference=\"XML\" >/Xdmf/Domain/Topology[@Name=\"" << id << "\"]</Topology>" << std::endl
    << std::setw(level * 2) << ""
    << "   <Geometry Reference=\"XML\" >/Xdmf/Domain/Geometry[@Name=\"" << id << "\"]</Geometry>" << std::endl;

}

//void XDMFStream::set_grid(mesh::MeshPatch const &m)
//{
//    auto b = m.get_box();
//    auto dims = m.get_dimensions();
//    nTuple<Real, 3> dx;
//    dx = (std::get<1>(b) - std::get<0>(b)) / dims;
//    set_grid(3, &dims[0], &std::get<0>(b)[0], &dx[0]);
//
//    for (auto const &item:m.patches())
//    {
//        int level = static_cast<int>(m_path_.size());
//
//        if (item.second->patches().size() > 0)
//        {
//            open_grid(type_cast<std::string>(item.first), item.second->time(), TREE);
//
//            set_grid(*item.second);
//
//            close_grid();
//        }
//    }
//
//
//}
}}<commit_msg>tokamak  em test pass<commit_after>/**
 * @file xdmf_stream.cpp.cpp
 * @author salmon
 * @date 2015-12-02.
 */
#include <fstream>
#include <iomanip>
#include <iostream>
#include <list>
#include "xdmf_stream.h"

#include "io.h"
#include "../gtl/utilities/log.h"
#include "../data_model/dataset.h"
#include "../parallel/parallel.h"

namespace simpla { namespace io
{

XDMFStream::XDMFStream()
{
    m_prefix_ = "";
}

XDMFStream::~XDMFStream()
{

}

std::string XDMFStream::path() const
{
    std::ostringstream buffer;
    buffer << "/";
    for (auto const &g_name: m_path_)
    {
        buffer << g_name << "/";
    }
    return buffer.str();
}

void XDMFStream::open(std::string const &prefix, std::string const &grid_name)
{

    close();

    m_prefix_ = prefix;

    m_file_stream_.open(m_prefix_ + ".xdmf");

    io::cd(m_prefix_ + ".h5:/");

    m_file_stream_
    << std::endl
    << "<Xdmf xmlns:xi=\"http://www.w3.org/2003/XInclude\" Version=\"2\">\n"
    << "<Domain>\n";

}

void XDMFStream::close()
{
    while (!m_path_.empty()) { close_grid(); }

    m_file_stream_
    << "</Domain>" << std::endl
    << "</Xdmf>" << std::endl;

    m_file_stream_.close();

}

void XDMFStream::open_grid(const std::string &g_name, int TAG)
{
    m_path_.push_back(g_name);

    io::cd(path());

    int level = static_cast<int>( m_path_.size());

    switch (TAG)
    {

        case COLLECTION_TEMPORAL:

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name <<
            "\" GridType=\"Collection\" CollectionType=\"Temporal\">" << std::endl;

            break;
        case TREE:

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name <<
            "\" GridType=\"Collection\" CollectionType=\"Tree\">" << std::endl;

            break;
        case UNIFORM://

            m_file_stream_
            << std::endl
            << std::setw(level * 2) << "" << "<Grid Name=\"" << g_name << "\" GridType=\"Uniform\">" << std::endl;
            break;

    }

}

void XDMFStream::time(Real t)
{
    int level = static_cast<int>( m_path_.size());
    m_file_stream_ << std::setw(level * 2) << "" << "   <Time Value=\"" << t << "\"/>" << std::endl;

}

void XDMFStream::close_grid()
{
    if (!m_path_.empty())
    {
        int level = static_cast<int>( m_path_.size());
        m_file_stream_ << std::setw(level * 2) << "" << "</Grid> <!--" << path() << " --> " << std::endl;
        m_path_.pop_back();
    }

}

void _str_replace(std::string *s, std::string const &place_holder, std::string const &txt)
{
    s->replace(s->find(place_holder), place_holder.size(), txt);
}

void XDMFStream::write(std::string const &ds_name, DataSet const &ds)
{
    if (ds.empty())
    {
        VERBOSE << "Try to write empty dataset: [" << ds_name << "] Ignored!" << std::endl;
        return;
    }

    std::string url;

    url = io::write(ds_name, ds);

    int level = static_cast<int>( m_path_.size());

    int ndims;

    nTuple<size_t, MAX_NDIMS_OF_ARRAY> dims;

    std::tie(ndims, dims, std::ignore, std::ignore, std::ignore, std::ignore) = ds.dataspace.shape();

    if (ds.datatype.is_array())
    {
        int n = ds.datatype.rank();
        for (int i = 0; i < n; ++i)
        {
            dims[ndims + i] = ds.datatype.extent(i);
        }
        ndims += n;
    }

    m_file_stream_ << std::setw(level * 2 + 4) << "" << "<DataItem Dimensions=\"";

    for (int i = 0; i < ndims; ++i) { m_file_stream_ << dims[i] << " "; }

    m_file_stream_ << "\" " << "NumberType=\"Float\" Precision=\"4\" Format=\"HDF\">\n"

    << std::setw(level * 2 + 6) << "" << url << std::endl
    << std::setw(level * 2 + 4) << "" << "</DataItem>\n";

}

void XDMFStream::write(std::string const &ds_name,
                       AttributeBase const &attr)
{
    if (attr.dataset().empty())
    {
        VERBOSE << "Try to write empty Attribute: [" << ds_name << "] Ignored!" << std::endl;

        return;
    }
    static const char a_Center_str[][10] = {
            "Node",
            "Edge",
            "Face",
            "Cell"
    };

    static const char a_AttributeType_str[][10] = {
            "Scalar",
            "Vector",
            "Tensor",
            "Cell"
    };

    int level = static_cast<int>( m_path_.size());

// @FIXME ParaView do not support EDEG or FACE

//    std::string center_type = a_Center_str[attr.center_type()];
//    std::string attr_type = a_AttributeType_str[attr.rank()];

    std::string center_type = "Node";
    std::string attr_type = a_AttributeType_str[
            attr.rank()
            + ((attr.center_type() == 1 || attr.center_type() == 2) ? 1 : 0)
    ];

    m_file_stream_ << ""
    << std::setw(level * 2 + 2) << "" << "<Attribute Name=\"" << ds_name << " \" "
    << "AttributeType=\"" << attr_type << "\" "
    << "Center=\"" << center_type << "\">" << std::endl;

    write(ds_name, attr.dataset());

    m_file_stream_ << std::setw(level * 2 + 2) << "" << "</Attribute>" << std::endl;

    VERBOSE << "DataSet [" << ds_name << "] is saved in [" << path() << "]!" << std::endl;
}

/**
 *
 *
 *    XML Attribute : TopologyType = Polyvertex | Polyline | Polygon |
                                  Triangle | Quadrilateral | Tetrahedron | Pyramid| Wedge | Hexahedron |
                                  Edge_3 | Triangle_6 | Quadrilateral_8 | Tetrahedron_10 | Pyramid_13 |
                                  Wedge_15 | Wedge_18 | Hexahedron_20 | Hexahedron_24 | Hexahedron_27 |
                                  Mixed |
                                  2DSMesh | 2DRectMesh | 2DCoRectMesh |
                                  3DSMesh | 3DRectMesh | 3DCoRectMesh
 */

void XDMFStream::set_topology_geometry(std::string const &name, int ndims, size_t const *dims, Real const *xmin,
                                       Real const *dx)
{

    int level = static_cast<int>(m_path_.size());

    if (ndims == 3)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology TopologyType=\"3DCoRectMesh\""
        << std::setw(level * 2 + 2) << "" << " Dimensions=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry GeometryType=\"ORIGIN_DXDYDZ\">\n"
        << std::setw(level * 2 + 2) << "" << "  <DataItem Name=\"Origin\" Dimensions=\"3\" NumberType=\"Float\" "
                " Precision=\"4\" Format=\"XML\">" << xmin[0] << " " << xmin[1] << " " << xmin[2] << "</DataItem>\n"
        << std::setw(level * 2 + 2) << "" << "  <DataItem Name=\"Spacing\" Dimensions=\"3\" NumberType=\"Float\""
                " Precision=\"4\" Format=\"XML\">" << dx[0] << " " << dx[1] << " " << dx[2] << "  </DataItem >\n"
        << std::setw(level * 2 + 2) << "" << "</Geometry>";

    }
    else if (ndims == 2)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology TopologyType=\"2DCoRectMesh\""
        << std::setw(level * 2 + 2) << "" << "      Dimensions=\"" << dims[0] << " " << dims[1] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry GeometryType=\"ORIGIN_DXDY\">\n"
        << std::setw(level * 2 + 2) << "" << "   <DataItem Name=\"Origin\" Dimensions=\"2\" NumberType=\"Float\" "
                " Precision=\"4\" Format=\"XML\">" << xmin[0] << " " << xmin[1] << "</DataItem>\n"
        << std::setw(level * 2 + 2) << "" << "   <DataItem Name=\"Spacing\" Dimensions=\"2\" NumberType=\"Float\""
                " Precision=\"4\" Format=\"XML\">" << dx[0] << " " << dx[1] << "  </DataItem >\n"
        << std::setw(level * 2 + 2) << "" << "</Geometry>";

    }
    else
    {
        THROW_EXCEPTION_RUNTIME_ERROR(" number of dimension is not 2 or 3");
    }

}

void XDMFStream::set_topology_geometry(std::string const &name, DataSet const &ds)
{

    int level = static_cast<int>(m_path_.size());

    int ndims;

    nTuple<size_t, MAX_NDIMS_OF_ARRAY> dims;

    std::tie(ndims, dims, std::ignore, std::ignore, std::ignore, std::ignore) = ds.dataspace.shape();

    --ndims;
    if (ndims == 2)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "<Topology Name=\"" << name << "\" "
        << " TopologyType=\"2DSMesh\""
        << " NumberOfElements=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "<Geometry Name=  \"" << name << "\" GeometryType=\"XY\">" << std::endl;
        write("/Grid/points", ds);

        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << " </Geometry>\n";
    }

    else if (ndims == 3)
    {
        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "<Topology Name=\"" << name << "\" "
        << "TopologyType=\"3DSMesh\"  NumberOfElements=\"" << dims[0] << " " << dims[1] << " " << dims[2] << "\"/>\n"
        << std::setw(level * 2 + 2) << "" << "<Geometry Name=\"" << name << "\" GeometryType=\"XYZ\">\n";

        write("/Grid/points", ds);

        m_file_stream_ << ""
        << std::setw(level * 2 + 2) << "" << "</Geometry>\n";
    }
    else
    {
        THROW_EXCEPTION_RUNTIME_ERROR("unsportted grid type");
    }
}

void XDMFStream::reference_topology_geometry(std::string const &id)
{
    int level = static_cast<int>( m_path_.size());
    m_file_stream_
    << std::setw(level * 2) << ""
    << "   <Topology Reference=\"XML\" >/Xdmf/Domain/Topology[@Name=\"" << id << "\"]</Topology>" << std::endl
    << std::setw(level * 2) << ""
    << "   <Geometry Reference=\"XML\" >/Xdmf/Domain/Geometry[@Name=\"" << id << "\"]</Geometry>" << std::endl;

}

//void XDMFStream::set_grid(mesh::MeshPatch const &m)
//{
//    auto b = m.get_box();
//    auto dims = m.get_dimensions();
//    nTuple<Real, 3> dx;
//    dx = (std::get<1>(b) - std::get<0>(b)) / dims;
//    set_grid(3, &dims[0], &std::get<0>(b)[0], &dx[0]);
//
//    for (auto const &item:m.patches())
//    {
//        int level = static_cast<int>(m_path_.size());
//
//        if (item.second->patches().size() > 0)
//        {
//            open_grid(type_cast<std::string>(item.first), item.second->time(), TREE);
//
//            set_grid(*item.second);
//
//            close_grid();
//        }
//    }
//
//
//}
}}<|endoftext|>


<commit_before>// Original Author: Brian Bockelman

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "ZipLZ4.h"
#include "lz4.h"
#include "lz4hc.h"
#include <stdio.h>
#include <cinttypes>
#include <cstdint>
#include <cstring>
#include <stdint.h>

#include "RConfig.h"

// Pulled from liblz4; upstream library explicitly exposes the symbol but the default build
// excludes the header.
typedef unsigned long long XXH64_hash_t;
typedef struct {
   unsigned char digest[8];
} XXH64_canonical_t;

#ifdef BUILTIN_LZ4
#define XXH64 LZ4_XXH64
#define XXH64_canonicalFromHash LZ4_XXH64_canonicalFromHash
#define XXH64_hashFromCanonical LZ4_XXH64_hashFromCanonical
#endif

extern "C" XXH64_hash_t XXH64(const void *input, size_t length, unsigned long long seed);
extern "C" void XXH64_canonicalFromHash(XXH64_canonical_t *dst, XXH64_hash_t hash);
extern "C" XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t *src);

// Header consists of:
// - 2 byte identifier "L4"
// - 1 byte LZ4 version string.
// - 3 bytes of uncompressed size
// - 3 bytes of compressed size
// - 8 byte checksum using xxhash 64.
static const int kChecksumOffset = 2 + 1 + 3 + 3;
static const int kChecksumSize = sizeof(XXH64_canonical_t);
static const int kHeaderSize = kChecksumOffset + kChecksumSize;

void R__zipLZ4(int cxlevel, int *srcsize, char *src, int *tgtsize, char *tgt, int *irep)
{
   int LZ4_version = LZ4_versionNumber();
   uint64_t out_size; /* compressed size */
   uint64_t in_size = (unsigned)(*srcsize);

   *irep = 0;

   if (R__unlikely(*tgtsize <= 0)) {
      return;
   }

   // Refuse to compress more than 16MB at a time -- we are only allowed 3 bytes for size info.
   if (R__unlikely(*srcsize > 0xffffff || *srcsize < 0)) {
      return;
   }

   int returnStatus;
   if (cxlevel > 9) {
      cxlevel = 9;
   }
   if (cxlevel >= 4) {
      returnStatus = LZ4_compress_HC(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize, cxlevel);
   } else {
      returnStatus = LZ4_compress_default(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize);
   }

   if (R__unlikely(returnStatus == 0)) { /* LZ4 compression failed */
      return;
   }
   XXH64_hash_t checksumResult = XXH64(tgt + kHeaderSize, returnStatus, 0);

   tgt[0] = 'L';
   tgt[1] = '4';
   tgt[2] = (LZ4_version / (100 * 100));

   out_size = returnStatus + kChecksumSize; /* compressed size, including the checksum. */

   // NOTE: these next 6 bytes are required from the ROOT compressed buffer format;
   // upper layers will assume they are laid out in a specific manner.
   tgt[3] = (char)(out_size & 0xff);
   tgt[4] = (char)((out_size >> 8) & 0xff);
   tgt[5] = (char)((out_size >> 16) & 0xff);

   tgt[6] = (char)(in_size & 0xff); /* decompressed size */
   tgt[7] = (char)((in_size >> 8) & 0xff);
   tgt[8] = (char)((in_size >> 16) & 0xff);

   // Write out checksum.
   XXH64_canonicalFromHash(reinterpret_cast<XXH64_canonical_t *>(tgt + kChecksumOffset), checksumResult);

   *irep = (int)returnStatus + kHeaderSize;
}

void R__unzipLZ4(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
{
   // NOTE: We don't check that srcsize / tgtsize is reasonable or within the ROOT-imposed limits.
   // This is assumed to be handled by the upper layers.

   int LZ4_version = LZ4_versionNumber() / (100 * 100);
   *irep = 0;
   if (R__unlikely(src[0] != 'L' || src[1] != '4')) {
      fprintf(stderr, "R__unzipLZ4: algorithm run against buffer with incorrect header (got %d%d; expected %d%d).\n",
              src[0], src[1], 'L', '4');
      return;
   }
   if (R__unlikely(src[2] != LZ4_version)) {
      fprintf(stderr,
              "R__unzipLZ4: This version of LZ4 is incompatible with the on-disk version (got %d; expected %d).\n",
              src[2], LZ4_version);
      return;
   }

   int inputBufferSize = *srcsize - kHeaderSize;

   // TODO: The checksum followed by the decompression means we iterate through the buffer twice.
   // We should perform some performance tests to see whether we can interleave the two -- i.e., at
   // what size of chunks does interleaving (avoiding two fetches from RAM) improve enough for the
   // extra function call costs?  NOTE that ROOT limits the buffer size to 16MB.
   XXH64_hash_t checksumResult = XXH64(src + kHeaderSize, inputBufferSize, 0);
   XXH64_hash_t checksumFromFile =
      XXH64_hashFromCanonical(reinterpret_cast<XXH64_canonical_t *>(src + kChecksumOffset));

   if (R__unlikely(checksumFromFile != checksumResult)) {
      fprintf(
         stderr,
         "R__unzipLZ4: Buffer corruption error!  Calculated checksum %llu; checksum calculated in the file was %llu.\n",
         checksumResult, checksumFromFile);
      return;
   }
   int returnStatus = LZ4_decompress_safe((char *)(&src[kHeaderSize]), (char *)(tgt), inputBufferSize, *tgtsize);
   if (R__unlikely(returnStatus < 0)) {
      fprintf(stderr, "R__unzipLZ4: error in decompression around byte %d out of maximum %d.\n", -returnStatus,
              *tgtsize);
      return;
   }

   *irep = returnStatus;
}
<commit_msg>Remove cherry-pick's use of R__unlikely.<commit_after>// Original Author: Brian Bockelman

/*************************************************************************
 * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#include "ZipLZ4.h"
#include "lz4.h"
#include "lz4hc.h"
#include <stdio.h>
#include <cstring>
#include <stdint.h>

#include "RConfig.h"

// Pulled from liblz4; upstream library explicitly exposes the symbol but the default build
// excludes the header.
typedef unsigned long long XXH64_hash_t;
typedef struct {
   unsigned char digest[8];
} XXH64_canonical_t;

#ifdef BUILTIN_LZ4
#define XXH64 LZ4_XXH64
#define XXH64_canonicalFromHash LZ4_XXH64_canonicalFromHash
#define XXH64_hashFromCanonical LZ4_XXH64_hashFromCanonical
#endif

extern "C" XXH64_hash_t XXH64(const void *input, size_t length, unsigned long long seed);
extern "C" void XXH64_canonicalFromHash(XXH64_canonical_t *dst, XXH64_hash_t hash);
extern "C" XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t *src);

// Header consists of:
// - 2 byte identifier "L4"
// - 1 byte LZ4 version string.
// - 3 bytes of uncompressed size
// - 3 bytes of compressed size
// - 8 byte checksum using xxhash 64.
static const int kChecksumOffset = 2 + 1 + 3 + 3;
static const int kChecksumSize = sizeof(XXH64_canonical_t);
static const int kHeaderSize = kChecksumOffset + kChecksumSize;

void R__zipLZ4(int cxlevel, int *srcsize, char *src, int *tgtsize, char *tgt, int *irep)
{
   int LZ4_version = LZ4_versionNumber();
   uint64_t out_size; /* compressed size */
   uint64_t in_size = (unsigned)(*srcsize);

   *irep = 0;

   if (*tgtsize <= 0) {
      return;
   }

   // Refuse to compress more than 16MB at a time -- we are only allowed 3 bytes for size info.
   if (*srcsize > 0xffffff || *srcsize < 0) {
      return;
   }

   int returnStatus;
   if (cxlevel > 9) {
      cxlevel = 9;
   }
   if (cxlevel >= 4) {
      returnStatus = LZ4_compress_HC(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize, cxlevel);
   } else {
      returnStatus = LZ4_compress_default(src, &tgt[kHeaderSize], *srcsize, *tgtsize - kHeaderSize);
   }

   if (returnStatus == 0) { /* LZ4 compression failed */
      return;
   }
   XXH64_hash_t checksumResult = XXH64(tgt + kHeaderSize, returnStatus, 0);

   tgt[0] = 'L';
   tgt[1] = '4';
   tgt[2] = (LZ4_version / (100 * 100));

   out_size = returnStatus + kChecksumSize; /* compressed size, including the checksum. */

   // NOTE: these next 6 bytes are required from the ROOT compressed buffer format;
   // upper layers will assume they are laid out in a specific manner.
   tgt[3] = (char)(out_size & 0xff);
   tgt[4] = (char)((out_size >> 8) & 0xff);
   tgt[5] = (char)((out_size >> 16) & 0xff);

   tgt[6] = (char)(in_size & 0xff); /* decompressed size */
   tgt[7] = (char)((in_size >> 8) & 0xff);
   tgt[8] = (char)((in_size >> 16) & 0xff);

   // Write out checksum.
   XXH64_canonicalFromHash(reinterpret_cast<XXH64_canonical_t *>(tgt + kChecksumOffset), checksumResult);

   *irep = (int)returnStatus + kHeaderSize;
}

void R__unzipLZ4(int *srcsize, unsigned char *src, int *tgtsize, unsigned char *tgt, int *irep)
{
   // NOTE: We don't check that srcsize / tgtsize is reasonable or within the ROOT-imposed limits.
   // This is assumed to be handled by the upper layers.

   int LZ4_version = LZ4_versionNumber() / (100 * 100);
   *irep = 0;
   if (src[0] != 'L' || src[1] != '4') {
      fprintf(stderr, "R__unzipLZ4: algorithm run against buffer with incorrect header (got %d%d; expected %d%d).\n",
              src[0], src[1], 'L', '4');
      return;
   }
   if (src[2] != LZ4_version) {
      fprintf(stderr,
              "R__unzipLZ4: This version of LZ4 is incompatible with the on-disk version (got %d; expected %d).\n",
              src[2], LZ4_version);
      return;
   }

   int inputBufferSize = *srcsize - kHeaderSize;

   // TODO: The checksum followed by the decompression means we iterate through the buffer twice.
   // We should perform some performance tests to see whether we can interleave the two -- i.e., at
   // what size of chunks does interleaving (avoiding two fetches from RAM) improve enough for the
   // extra function call costs?  NOTE that ROOT limits the buffer size to 16MB.
   XXH64_hash_t checksumResult = XXH64(src + kHeaderSize, inputBufferSize, 0);
   XXH64_hash_t checksumFromFile =
      XXH64_hashFromCanonical(reinterpret_cast<XXH64_canonical_t *>(src + kChecksumOffset));

   if (checksumFromFile != checksumResult) {
      fprintf(
         stderr,
         "R__unzipLZ4: Buffer corruption error!  Calculated checksum %llu; checksum calculated in the file was %llu.\n",
         checksumResult, checksumFromFile);
      return;
   }
   int returnStatus = LZ4_decompress_safe((char *)(&src[kHeaderSize]), (char *)(tgt), inputBufferSize, *tgtsize);
   if (returnStatus < 0) {
      fprintf(stderr, "R__unzipLZ4: error in decompression around byte %d out of maximum %d.\n", -returnStatus,
              *tgtsize);
      return;
   }

   *irep = returnStatus;
}
<|endoftext|>


<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2016                                                               *
 *                                                                                *
 * 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 <modules/onscreengui/include/guitimecomponent.h>

#include <openspace/engine/openspaceengine.h>
#include <openspace/util/time.h>

#include <ghoul/misc/assert.h>
#include "imgui.h"

namespace {
    const std::string _loggerCat = "GuiTimeComponent";
}

namespace openspace {
namespace gui {

void GuiTimeComponent::render() {
    float deltaTime = static_cast<float>(Time::ref().deltaTime());

    bool changed = ImGui::SliderFloat("Delta Time", &deltaTime, -100.f, 100.f);
    if (changed)
        OsEng.scriptEngine().queueScript("openspace.time.setDeltaTime(" + std::to_string(deltaTime) + ")");

    //char dateBuffer[512] = {};
    //ImGui::InputText("Date", dateBuffer, 512);
    //bool pressed = ImGui::Button("Set Date");
    //if (pressed)
    //    OsEng.scriptEngine()->queueScript("openspace.time.setTime('" + std::string(dateBuffer) + "')");

    //const SceneGraphNode* currentFocus = OsEng.interactionHandler()->focusNode();

    //std::vector<SceneGraphNode*> nodes = OsEng.renderEngine()->sceneGraph()->allSceneGraphNodes();
    //std::sort(nodes.begin(), nodes.end(), [](SceneGraphNode* lhs, SceneGraphNode* rhs) { return lhs->name() < rhs->name(); });
    //auto it = std::find(nodes.begin(), nodes.end(), currentFocus);
    //ghoul_assert(it != nodes.end(), "Focus node not found");

    //std::string nodeNames = "";
    //for (SceneGraphNode* n : nodes)
    //    nodeNames += n->name() + '\0';

    //int position = it - nodes.begin();

    //bool result = ImGui::Combo("Origin", &position, nodeNames.c_str());

    //if (result) {
    //    LINFO("openspace.setOrigin('" + nodes[position]->name() + "');");
    //    OsEng.scriptEngine()->queueScript("openspace.setOrigin('" + nodes[position]->name() + "');");
    //}

}

} // gui
} // openspace
<commit_msg>Increase value range of delta time in GUI<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2016                                                               *
 *                                                                                *
 * 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 <modules/onscreengui/include/guitimecomponent.h>

#include <openspace/engine/openspaceengine.h>
#include <openspace/util/time.h>

#include <ghoul/misc/assert.h>
#include "imgui.h"

namespace {
    const std::string _loggerCat = "GuiTimeComponent";
}

namespace openspace {
namespace gui {

void GuiTimeComponent::render() {
    float deltaTime = static_cast<float>(Time::ref().deltaTime());

    bool changed = ImGui::SliderFloat("Delta Time", &deltaTime, -500000.f, 500000.f);
    if (changed)
        OsEng.scriptEngine().queueScript("openspace.time.setDeltaTime(" + std::to_string(deltaTime) + ")");

    //char dateBuffer[512] = {};
    //ImGui::InputText("Date", dateBuffer, 512);
    //bool pressed = ImGui::Button("Set Date");
    //if (pressed)
    //    OsEng.scriptEngine()->queueScript("openspace.time.setTime('" + std::string(dateBuffer) + "')");

    //const SceneGraphNode* currentFocus = OsEng.interactionHandler()->focusNode();

    //std::vector<SceneGraphNode*> nodes = OsEng.renderEngine()->sceneGraph()->allSceneGraphNodes();
    //std::sort(nodes.begin(), nodes.end(), [](SceneGraphNode* lhs, SceneGraphNode* rhs) { return lhs->name() < rhs->name(); });
    //auto it = std::find(nodes.begin(), nodes.end(), currentFocus);
    //ghoul_assert(it != nodes.end(), "Focus node not found");

    //std::string nodeNames = "";
    //for (SceneGraphNode* n : nodes)
    //    nodeNames += n->name() + '\0';

    //int position = it - nodes.begin();

    //bool result = ImGui::Combo("Origin", &position, nodeNames.c_str());

    //if (result) {
    //    LINFO("openspace.setOrigin('" + nodes[position]->name() + "');");
    //    OsEng.scriptEngine()->queueScript("openspace.setOrigin('" + nodes[position]->name() + "');");
    //}

}

} // gui
} // openspace
<|endoftext|>


<commit_before><commit_msg>allow destruction to complete socket close<commit_after><|endoftext|>


<commit_before>
#include "simpleWebSocket.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <regex.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>

#include <ostream>
#include <iostream>
#include <iomanip>

//
//   websocket message format:   (see RFC 6455)
//
//   +-7-+-6-+-5-+-4-+-3---2---1---0-+
//   | F | R | R | R |               |  FIN = final of segmented msg
//   | I | S | S | S |    OPCODE     |  OPCODE = 1=text 2=bin 8=close 9=ping
//   | N | V | V | V |               |           a=pong 3-7,b-f=rsvd
//   |   | 1 | 2 | 3 |               |           0=continuation frame
//   +---+---+---+---+---------------+
//   | M |                           |  MASK = 32-bit xor mask is present
//   | A |    PAYLOAD LEN            |  PAYLOAD LEN = 0-125 length
//   | S |                           |     126 = following 2 bytes is length
//   | K |                           |     127 = following 8 bytes is length
//   +---+---------------------------+
//   | extended length, 2 or 8 bytes |
//   +-------------------------------+
//   |    if MASK, 4-byte mask here  |
//   +-------------------------------+
//   |    PAYLOAD                    |
//   +-------------------------------+
//
//  mask is always present client -> server,
//  mask is never present server -> client.
//  we don't support segmentation, so we error out.
//

using namespace std;

namespace SimpleWebSocket {

const std::string websocket_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

////////   WebSocketConn  ////////

WebSocketConn :: WebSocketConn(int _fd, bool _server, bool _verbose)
    : _ok(false), fd(_fd), server(_server),
      verbose(_verbose), readbuf(MAX_READBUF)
{
    got_flags = 0;
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}

WebSocketConn :: ~WebSocketConn(void)
{
    if (fd > 0)
        close(fd);
}

/*virtual*/ WebSocketRet
WebSocketConn :: handle_read(::google::protobuf::Message &msg)
{
    msg.Clear();
    if (readbuf.remaining() == 0)
    {
        cerr << "readbuffer is full!\n";
        return WEBSOCKET_NO_MESSAGE;
    }
    WebSocketRet r = handle_data(msg);
    if (r == WEBSOCKET_NO_MESSAGE)
    {
        int cc = readbuf.readFd(fd);
        if (cc < 0)
        {
            if (errno != EWOULDBLOCK)
            {
                cerr << "read : " << strerror(errno) << endl;
                return WEBSOCKET_CLOSED;
            }
        }
        if (cc == 0)
        {
            // if we're out of data on the socket,
            // and there wasn't enough in the buffer
            // for a message, before, we're done.
            return WEBSOCKET_CLOSED;
        }
        r = handle_data(msg);
    }
    return r;
}

bool
WebSocketConn::sendMessage(const ::google::protobuf::Message &msg)
{
    if (state != STATE_CONNECTED)
        return false;

    send_buffer.resize(0);

    send_buffer += (char) 0x82; // WS_TYPE_BINARY
//  send_buffer += (char) 0x88; // WS_TYPE_CLOSE

    int len = msg.ByteSize();
    int mask_bit = server ? 0 : 0x80;

    if (len < 126)
    {
        send_buffer += (char) ((len & 0x7f) | mask_bit);
    }
    else if (len < 65536)
    {
        send_buffer += (char) (126 | mask_bit); // mask bit
        send_buffer += (char) ((len >> 8) & 0xFF);
        send_buffer += (char) ((len >> 0) & 0xFF);
    }
    else
    {
        send_buffer += (char) (127 | mask_bit); // mask bit
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) ((len >> 24) & 0xFF);
        send_buffer += (char) ((len >> 16) & 0xFF);
        send_buffer += (char) ((len >>  8) & 0xFF);
        send_buffer += (char) ((len >>  0) & 0xFF);
    }

    uint8_t mask[4] = { 0, 0, 0, 0 };

    if (!server)
    {
        uint32_t mask_value = random();

        mask[0] = (mask_value >> 24) & 0xFF;
        mask[1] = (mask_value >> 16) & 0xFF;
        mask[2] = (mask_value >>  8) & 0xFF;
        mask[3] = (mask_value >>  0) & 0xFF;

        send_buffer += (char) mask[0];
        send_buffer += (char) mask[1];
        send_buffer += (char) mask[2];
        send_buffer += (char) mask[3];
    }

    size_t  pos = send_buffer.size();

    if (msg.AppendToString(&send_buffer) == false)
        return false;

    if (!server)
        for (size_t counter = 0; counter < len; counter++)
            send_buffer[pos + counter] =
                send_buffer[pos + counter] ^ mask[counter&3];

    const uint8_t * buf = (const uint8_t *) send_buffer.c_str();
    // repurpose len
    len = send_buffer.size();

    if (verbose)
    {
        printf("** write buffer : ");
        for (int ctr = 0; ctr < len; ctr++)
            printf("%02x ", buf[ctr]);
        printf("\n");
    }

    int cc = ::write(fd, buf, len);
    if (cc != len)
    {
        fprintf(stderr, "WebSocketClient :: sendMessage: "
                "write %d returned %d (err %d: %s)\n",
                len, cc, errno, strerror(errno));
        return false;
    }

    return true;
}

////////    WebSocketServer   ////////

WebSocketServer :: WebSocketServer( uint16_t port, uint32_t addr,
                                    bool _verbose )
    : _ok(false), fd(-1), verbose(_verbose)
{
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
        return;
    struct sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = htonl(addr);
    int v = 1;
    setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void*) &v, sizeof( v ));
    if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
        return;
    listen(fd,1);
    _ok = true;
}

WebSocketServer :: ~WebSocketServer(void)
{
    if (fd > 0)
        close(fd);
}

WebSocketServerConn *
WebSocketServer::handle_accept(void)
{
    struct sockaddr_in sa;
    socklen_t salen = sizeof(sa);
    int new_fd = accept(fd, (struct sockaddr *)&sa, &salen);
    if (new_fd < 0)
        return NULL;
    return new WebSocketServerConn(new_fd, sa, verbose);
}

};
<commit_msg>oops dont mark it as nonblock -- live spin polling!<commit_after>
#include "simpleWebSocket.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <regex.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>

#include <ostream>
#include <iostream>
#include <iomanip>

//
//   websocket message format:   (see RFC 6455)
//
//   +-7-+-6-+-5-+-4-+-3---2---1---0-+
//   | F | R | R | R |               |  FIN = final of segmented msg
//   | I | S | S | S |    OPCODE     |  OPCODE = 1=text 2=bin 8=close 9=ping
//   | N | V | V | V |               |           a=pong 3-7,b-f=rsvd
//   |   | 1 | 2 | 3 |               |           0=continuation frame
//   +---+---+---+---+---------------+
//   | M |                           |  MASK = 32-bit xor mask is present
//   | A |    PAYLOAD LEN            |  PAYLOAD LEN = 0-125 length
//   | S |                           |     126 = following 2 bytes is length
//   | K |                           |     127 = following 8 bytes is length
//   +---+---------------------------+
//   | extended length, 2 or 8 bytes |
//   +-------------------------------+
//   |    if MASK, 4-byte mask here  |
//   +-------------------------------+
//   |    PAYLOAD                    |
//   +-------------------------------+
//
//  mask is always present client -> server,
//  mask is never present server -> client.
//  we don't support segmentation, so we error out.
//

using namespace std;

namespace SimpleWebSocket {

const std::string websocket_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

////////   WebSocketConn  ////////

WebSocketConn :: WebSocketConn(int _fd, bool _server, bool _verbose)
    : _ok(false), fd(_fd), server(_server),
      verbose(_verbose), readbuf(MAX_READBUF)
{
    got_flags = 0;
//    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
}

WebSocketConn :: ~WebSocketConn(void)
{
    if (fd > 0)
        close(fd);
}

/*virtual*/ WebSocketRet
WebSocketConn :: handle_read(::google::protobuf::Message &msg)
{
    msg.Clear();
    if (readbuf.remaining() == 0)
    {
        cerr << "readbuffer is full!\n";
        return WEBSOCKET_NO_MESSAGE;
    }
    WebSocketRet r = handle_data(msg);
    if (r == WEBSOCKET_NO_MESSAGE)
    {
        int cc = readbuf.readFd(fd);
        if (cc < 0)
        {
            if (errno != EWOULDBLOCK)
            {
                cerr << "read : " << strerror(errno) << endl;
                return WEBSOCKET_CLOSED;
            }
        }
        if (cc == 0)
        {
            // if we're out of data on the socket,
            // and there wasn't enough in the buffer
            // for a message, before, we're done.
            return WEBSOCKET_CLOSED;
        }
        r = handle_data(msg);
    }
    return r;
}

bool
WebSocketConn::sendMessage(const ::google::protobuf::Message &msg)
{
    if (state != STATE_CONNECTED)
        return false;

    send_buffer.resize(0);

    send_buffer += (char) 0x82; // WS_TYPE_BINARY
//  send_buffer += (char) 0x88; // WS_TYPE_CLOSE

    int len = msg.ByteSize();
    int mask_bit = server ? 0 : 0x80;

    if (len < 126)
    {
        send_buffer += (char) ((len & 0x7f) | mask_bit);
    }
    else if (len < 65536)
    {
        send_buffer += (char) (126 | mask_bit); // mask bit
        send_buffer += (char) ((len >> 8) & 0xFF);
        send_buffer += (char) ((len >> 0) & 0xFF);
    }
    else
    {
        send_buffer += (char) (127 | mask_bit); // mask bit
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) 0;
        send_buffer += (char) ((len >> 24) & 0xFF);
        send_buffer += (char) ((len >> 16) & 0xFF);
        send_buffer += (char) ((len >>  8) & 0xFF);
        send_buffer += (char) ((len >>  0) & 0xFF);
    }

    uint8_t mask[4] = { 0, 0, 0, 0 };

    if (!server)
    {
        uint32_t mask_value = random();

        mask[0] = (mask_value >> 24) & 0xFF;
        mask[1] = (mask_value >> 16) & 0xFF;
        mask[2] = (mask_value >>  8) & 0xFF;
        mask[3] = (mask_value >>  0) & 0xFF;

        send_buffer += (char) mask[0];
        send_buffer += (char) mask[1];
        send_buffer += (char) mask[2];
        send_buffer += (char) mask[3];
    }

    size_t  pos = send_buffer.size();

    if (msg.AppendToString(&send_buffer) == false)
        return false;

    if (!server)
        for (size_t counter = 0; counter < len; counter++)
            send_buffer[pos + counter] =
                send_buffer[pos + counter] ^ mask[counter&3];

    const uint8_t * buf = (const uint8_t *) send_buffer.c_str();
    // repurpose len
    len = send_buffer.size();

    if (verbose)
    {
        printf("** write buffer : ");
        for (int ctr = 0; ctr < len; ctr++)
            printf("%02x ", buf[ctr]);
        printf("\n");
    }

    int cc = ::write(fd, buf, len);
    if (cc != len)
    {
        fprintf(stderr, "WebSocketClient :: sendMessage: "
                "write %d returned %d (err %d: %s)\n",
                len, cc, errno, strerror(errno));
        return false;
    }

    return true;
}

////////    WebSocketServer   ////////

WebSocketServer :: WebSocketServer( uint16_t port, uint32_t addr,
                                    bool _verbose )
    : _ok(false), fd(-1), verbose(_verbose)
{
    fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
        return;
    struct sockaddr_in sa;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);
    sa.sin_addr.s_addr = htonl(addr);
    int v = 1;
    setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (void*) &v, sizeof( v ));
    if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0)
        return;
    listen(fd,1);
    _ok = true;
}

WebSocketServer :: ~WebSocketServer(void)
{
    if (fd > 0)
        close(fd);
}

WebSocketServerConn *
WebSocketServer::handle_accept(void)
{
    struct sockaddr_in sa;
    socklen_t salen = sizeof(sa);
    int new_fd = accept(fd, (struct sockaddr *)&sa, &salen);
    if (new_fd < 0)
        return NULL;
    return new WebSocketServerConn(new_fd, sa, verbose);
}

};
<|endoftext|>


<commit_before>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "coeio.h"
#include "fiber.h"
#include "exception.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Asynchronous IO Tasks (libeio wrapper).
 * ---------------------------------------
 *
 * libeio request processing is designed in edge-trigger
 * manner, when libeio is ready to process some requests it
 * calls coeio_poller callback.
 *
 * Due to libeio design, want_poll callback is called while
 * locks are being held, so it's not possible to call any libeio
 * function inside this callback. Thus coeio_want_poll raises an
 * async event which will be dealt with normally as part of the
 * main Tarantool event loop.
 *
 * The async event handler, in turn, performs eio_poll(), which
 * will run on_complete callback for all ready eio tasks.
 * In case if some of the requests are not complete by the time
 * eio_poll() has been called, coeio_idle watcher is started, which
 * would periodically invoke eio_poll() until all requests are
 * complete.
 *
 * See for details:
 * http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod
*/

struct coeio_manager {
	ev_loop *loop;
	ev_idle coeio_idle;
	ev_async coeio_async;
} coeio_manager;

static void
coeio_idle_cb(ev_loop *loop, struct ev_idle *w, int /* events */)
{
	if (eio_poll() != -1) {
		/* nothing to do */
		ev_idle_stop(loop, w);
	}
}

static void
coeio_async_cb(ev_loop *loop, struct ev_async *w __attribute__((unused)),
	       int events __attribute__((unused)))
{
	if (eio_poll() == -1) {
		/* not all tasks are complete. */
		ev_idle_start(loop, &coeio_manager.coeio_idle);
	}
}

static void
coeio_want_poll_cb(void)
{
	ev_async_send(coeio_manager.loop, &coeio_manager.coeio_async);
}

/**
 * Init coeio subsystem.
 *
 * Create idle and async watchers, init eio.
 */
void
coeio_init(void)
{
	eio_init(coeio_want_poll_cb, NULL);
	coeio_manager.loop = loop();

	ev_idle_init(&coeio_manager.coeio_idle, coeio_idle_cb);
	ev_async_init(&coeio_manager.coeio_async, coeio_async_cb);

	ev_async_start(loop(), &coeio_manager.coeio_async);
}

/**
 * ReInit coeio subsystem (for example after 'fork')
 */
void
coeio_reinit(void)
{
	eio_init(coeio_want_poll_cb, NULL);
}

/**
 * A single task context.
 */
struct coeio_task {
	/** The calling fiber. */
	struct fiber *fiber;
	/** The callback. */
	ssize_t (*func)(va_list ap);
	/**
	 * If the callback sets errno, it's preserved across the
	 * call.
	 */
	/** Callback arguments. */
	va_list ap;
	/** Callback results. */
	ssize_t result;
	int complete;
	int errorno;
};

static void
coeio_custom_cb(eio_req *req)
{
	struct coeio_task *task = (struct coeio_task *) req->data;
	req->result = task->func(task->ap);
}

/**
 * A callback invoked by eio_poll when associated
 * eio_request is complete.
 */
static int
coeio_on_complete(eio_req *req)
{
	/*
	 * Don't touch the task if the request is cancelled:
	 * the task is allocated on the caller's stack and
	 * may be already gone. Don't wakeup the caller
	 * if the task is cancelled: in this case the caller
	 * is already woken up, avoid double wake-up.
	 */
	if (! EIO_CANCELLED(req)) {
		struct coeio_task *task = (struct coeio_task *) req->data;
		task->result = req->result;
		task->errorno = req->errorno;
		task->complete = 1;
		fiber_wakeup(task->fiber);
	}
	return 0;
}

/**
 * Create new eio task with specified function and
 * arguments. Yield and wait until the task is complete
 * or a timeout occurs.
 *
 * This function doesn't throw exceptions to avoid double error
 * checking: in most cases it's also necessary to check the return
 * value of the called function and perform necessary actions. If
 * func sets errno, the errno is preserved across the call.
 *
 * @retval -1 and errno = ENOMEM if failed to create a task
 * @retval -1 and errno = ETIMEDOUT if timed out
 * @retval the function return (errno is preserved).
 *
 * @code
 *	static ssize_t openfile_cb(va_list ap)
 *	{
 *	         const char *filename = va_arg(ap);
 *	         int flags = va_arg(ap);
 *	         return open(filename, flags);
 *	}
 *
 *	 if (coeio_custom(openfile_cb, 0.10, "/tmp/file", 0) == -1)
 *		// handle errors.
 *	...
 */
ssize_t
coeio_custom(ssize_t (*func)(va_list ap), ev_tstamp timeout, ...)
{
	struct coeio_task task;
	task.fiber = fiber();
	task.func = func;
	task.result = -1;
	task.complete = 0;
	/*
	 * Cancelling a fiber which is suspended
	 * on coeio_custom task would destroy
	 * its stack and crash the server when
	 * this stack is accessed in the worker thread.
	 */
	bool cancellable = fiber_set_cancellable(false);
	va_start(task.ap, timeout);
	struct eio_req *req = eio_custom(coeio_custom_cb, 0,
					 coeio_on_complete, &task);
	if (req == NULL) {
		errno = ENOMEM;
	} else if (fiber_yield_timeout(timeout) && !task.complete) {
		/* timeout. */
		errno = ETIMEDOUT;
		task.result = -1;
		eio_cancel(req);
	} else {
		/* the task is complete. */
		errno = task.errorno;
	}
	va_end(task.ap);
	fiber_set_cancellable(cancellable);
	return task.result;
}

/*
 * Resolver function, run in separate thread by
 * coeio (libeio).
*/
static ssize_t
getaddrinfo_cb(va_list ap)
{
	const char *host = va_arg(ap, const char *);
	const char *port = va_arg(ap, const char *);
	struct addrinfo *hints = va_arg(ap, struct addrinfo *);
	struct addrinfo **res = va_arg(ap, struct addrinfo **);

	int rc = getaddrinfo(host, port, hints, res);
	/* getaddrinfo can return EAI_ADDRFAMILY on attempt
	 * to resolve ::1, if machine has no public ipv6 addresses
	 * configured. Retry without AI_ADDRCONFIG flag set.
	 *
	 * See for details: https://bugs.launchpad.net/tarantool/+bug/1160877
	 */

	/* EAI_ADDRFAMILY is deprecated on FreeBSD */
	if (rc == EAI_BADFLAGS
#ifdef EAI_ADDRFAMILY
	    || rc == EAI_ADDRFAMILY
#endif
	   ) {
		hints->ai_flags &= ~AI_ADDRCONFIG;
		rc = getaddrinfo(host, port, hints, res);
	}
	if (rc) {
		errno = ERESOLVE;
		return -1;
	}
	return 0;
}

struct addrinfo *
coeio_resolve(int socktype, const char *host, const char *port,
              ev_tstamp timeout)
{
	/* Fill hinting information for use by connect(2) or bind(2). */
	struct addrinfo *result = NULL;
	struct addrinfo hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
	hints.ai_socktype = socktype;
	hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_PASSIVE;
	hints.ai_protocol = 0;
	/* do resolving */
	errno = 0;
	/* make no difference between empty string and NULL for host */
	if (coeio_custom(getaddrinfo_cb, timeout, (host && *host) ? host : NULL,
			 port, &hints, &result))
		return NULL;
	return result;
}

ssize_t
cord_cojoin_cb(va_list ap)
{
	struct cord *cord = va_arg(ap, struct cord *);
	void *retval = NULL;
	int res = tt_pthread_join(cord->id, &retval);
	return res;
}

int
cord_cojoin(struct cord *cord)
{
	assert(cord() != cord); /* Can't join self. */
	int rc = coeio_custom(cord_cojoin_cb, TIMEOUT_INFINITY, cord);
	if (rc == 0 && cord->fiber->exception) {
		Exception::move(&cord->fiber->exception, &fiber()->exception);
		cord_destroy(cord);
		 /* re-throw exception in this fiber */
		fiber()->exception->raise();
	}
	cord_destroy(cord);
	return rc;
}

<commit_msg>coeio: update the source after libeio has been updated with the upstream<commit_after>/*
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "coeio.h"
#include "fiber.h"
#include "exception.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
 * Asynchronous IO Tasks (libeio wrapper).
 * ---------------------------------------
 *
 * libeio request processing is designed in edge-trigger
 * manner, when libeio is ready to process some requests it
 * calls coeio_poller callback.
 *
 * Due to libeio design, want_poll callback is called while
 * locks are being held, so it's not possible to call any libeio
 * function inside this callback. Thus coeio_want_poll raises an
 * async event which will be dealt with normally as part of the
 * main Tarantool event loop.
 *
 * The async event handler, in turn, performs eio_poll(), which
 * will run on_complete callback for all ready eio tasks.
 * In case if some of the requests are not complete by the time
 * eio_poll() has been called, coeio_idle watcher is started, which
 * would periodically invoke eio_poll() until all requests are
 * complete.
 *
 * See for details:
 * http://pod.tst.eu/http://cvs.schmorp.de/libeio/eio.pod
*/

struct coeio_manager {
	ev_loop *loop;
	ev_idle coeio_idle;
	ev_async coeio_async;
} coeio_manager;

static void
coeio_idle_cb(ev_loop *loop, struct ev_idle *w, int /* events */)
{
	if (eio_poll() != -1) {
		/* nothing to do */
		ev_idle_stop(loop, w);
	}
}

static void
coeio_async_cb(ev_loop *loop, struct ev_async *w __attribute__((unused)),
	       int events __attribute__((unused)))
{
	if (eio_poll() == -1) {
		/* not all tasks are complete. */
		ev_idle_start(loop, &coeio_manager.coeio_idle);
	}
}

static void
coeio_want_poll_cb(void)
{
	ev_async_send(coeio_manager.loop, &coeio_manager.coeio_async);
}

/**
 * Init coeio subsystem.
 *
 * Create idle and async watchers, init eio.
 */
void
coeio_init(void)
{
	eio_init(coeio_want_poll_cb, NULL);
	coeio_manager.loop = loop();

	ev_idle_init(&coeio_manager.coeio_idle, coeio_idle_cb);
	ev_async_init(&coeio_manager.coeio_async, coeio_async_cb);

	ev_async_start(loop(), &coeio_manager.coeio_async);
}

/**
 * ReInit coeio subsystem (for example after 'fork')
 */
void
coeio_reinit(void)
{
	eio_init(coeio_want_poll_cb, NULL);
}

/**
 * A single task context.
 */
struct coeio_task {
	/** The calling fiber. */
	struct fiber *fiber;
	/** The callback. */
	ssize_t (*func)(va_list ap);
	/**
	 * If the callback sets errno, it's preserved across the
	 * call.
	 */
	/** Callback arguments. */
	va_list ap;
	/** Callback results. */
	ssize_t result;
	int complete;
	int errorno;
};

static void
coeio_custom_cb(eio_req *req)
{
	struct coeio_task *task = (struct coeio_task *) req->data;
	req->result = task->func(task->ap);
}

/**
 * A callback invoked by eio_poll when associated
 * eio_request is complete.
 */
static int
coeio_on_complete(eio_req *req)
{
	/*
	 * If the request is cancelled, libeio doesn't
	 * invoke finish callback. Indeed, it can't do
	 * anything since its execution context has
	 * already been gone.
	 */
	assert(!EIO_CANCELLED(req));

	struct coeio_task *task = (struct coeio_task *) req->data;
	task->result = req->result;
	task->errorno = req->errorno;
	task->complete = 1;
	fiber_wakeup(task->fiber);

	return 0;
}

/**
 * Create new eio task with specified function and
 * arguments. Yield and wait until the task is complete
 * or a timeout occurs.
 *
 * This function doesn't throw exceptions to avoid double error
 * checking: in most cases it's also necessary to check the return
 * value of the called function and perform necessary actions. If
 * func sets errno, the errno is preserved across the call.
 *
 * @retval -1 and errno = ENOMEM if failed to create a task
 * @retval -1 and errno = ETIMEDOUT if timed out
 * @retval the function return (errno is preserved).
 *
 * @code
 *	static ssize_t openfile_cb(va_list ap)
 *	{
 *	         const char *filename = va_arg(ap);
 *	         int flags = va_arg(ap);
 *	         return open(filename, flags);
 *	}
 *
 *	 if (coeio_custom(openfile_cb, 0.10, "/tmp/file", 0) == -1)
 *		// handle errors.
 *	...
 */
ssize_t
coeio_custom(ssize_t (*func)(va_list ap), ev_tstamp timeout, ...)
{
	struct coeio_task task;
	task.fiber = fiber();
	task.func = func;
	task.result = -1;
	task.complete = 0;
	/*
	 * Cancelling a fiber which is suspended
	 * on coeio_custom task would destroy
	 * its stack and crash the server when
	 * this stack is accessed in the worker thread.
	 */
	bool cancellable = fiber_set_cancellable(false);
	va_start(task.ap, timeout);
	struct eio_req *req = eio_custom(coeio_custom_cb, 0,
					 coeio_on_complete, &task);
	if (req == NULL) {
		errno = ENOMEM;
	} else if (fiber_yield_timeout(timeout) && !task.complete) {
		/* timeout. */
		errno = ETIMEDOUT;
		task.result = -1;
		eio_cancel(req);
	} else {
		/* the task is complete. */
		errno = task.errorno;
	}
	va_end(task.ap);
	fiber_set_cancellable(cancellable);
	return task.result;
}

/*
 * Resolver function, run in separate thread by
 * coeio (libeio).
*/
static ssize_t
getaddrinfo_cb(va_list ap)
{
	const char *host = va_arg(ap, const char *);
	const char *port = va_arg(ap, const char *);
	struct addrinfo *hints = va_arg(ap, struct addrinfo *);
	struct addrinfo **res = va_arg(ap, struct addrinfo **);

	int rc = getaddrinfo(host, port, hints, res);
	/* getaddrinfo can return EAI_ADDRFAMILY on attempt
	 * to resolve ::1, if machine has no public ipv6 addresses
	 * configured. Retry without AI_ADDRCONFIG flag set.
	 *
	 * See for details: https://bugs.launchpad.net/tarantool/+bug/1160877
	 */

	/* EAI_ADDRFAMILY is deprecated on FreeBSD */
	if (rc == EAI_BADFLAGS
#ifdef EAI_ADDRFAMILY
	    || rc == EAI_ADDRFAMILY
#endif
	   ) {
		hints->ai_flags &= ~AI_ADDRCONFIG;
		rc = getaddrinfo(host, port, hints, res);
	}
	if (rc) {
		errno = ERESOLVE;
		return -1;
	}
	return 0;
}

struct addrinfo *
coeio_resolve(int socktype, const char *host, const char *port,
              ev_tstamp timeout)
{
	/* Fill hinting information for use by connect(2) or bind(2). */
	struct addrinfo *result = NULL;
	struct addrinfo hints;
	memset(&hints, 0, sizeof(struct addrinfo));
	hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
	hints.ai_socktype = socktype;
	hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV|AI_PASSIVE;
	hints.ai_protocol = 0;
	/* do resolving */
	errno = 0;
	/* make no difference between empty string and NULL for host */
	if (coeio_custom(getaddrinfo_cb, timeout, (host && *host) ? host : NULL,
			 port, &hints, &result))
		return NULL;
	return result;
}

ssize_t
cord_cojoin_cb(va_list ap)
{
	struct cord *cord = va_arg(ap, struct cord *);
	void *retval = NULL;
	int res = tt_pthread_join(cord->id, &retval);
	return res;
}

int
cord_cojoin(struct cord *cord)
{
	assert(cord() != cord); /* Can't join self. */
	int rc = coeio_custom(cord_cojoin_cb, TIMEOUT_INFINITY, cord);
	if (rc == 0 && cord->fiber->exception) {
		Exception::move(&cord->fiber->exception, &fiber()->exception);
		cord_destroy(cord);
		 /* re-throw exception in this fiber */
		fiber()->exception->raise();
	}
	cord_destroy(cord);
	return rc;
}

<|endoftext|>