<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) 2015 ScyllaDB
 */

#pragma once

#include <seastar/core/future.hh>
#include <seastar/core/expiring_fifo.hh>

namespace seastar {

/// \addtogroup future-module
/// @{

/// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
template<typename Clock>
struct with_clock {};

/// \cond internal

template <typename... T>
struct future_option_traits;

template <typename Clock, typename... T>
struct future_option_traits<with_clock<Clock>, T...> {
    using clock_type = Clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

template <typename... T>
struct future_option_traits {
    using clock_type = lowres_clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

/// \endcond

/// \brief Like \ref future except the result can be waited for by many fibers.
///
/// Represents a value which may not yet be ready. A fiber can wait for the value using
/// the \ref future obtained by calling \ref get_future() or casting to \ref future type.
/// Multiple fibers are allowed to obtain a \ref future for the result using the same
/// instance of \ref shared_future.
///
/// All futures obtained from shared_future should end up in the same state. However,
/// if the value's copy constructor throws, some of the futures may end up in a failed state
/// with an exception thrown from the copy constructor and end up with a state
/// different than other futures.
///
/// The scope of shared_future instance doesn't have to include scopes of the futures
/// obtained from that instance. In that sense the returned futures are independent.
///
/// shared_future can be copied at any time and all copies will resolve with the same value.
///
/// shared_future can be in a disengaged state when it's default-constructed or moved-from.
/// When it's in such a state we say it's invalid and obtaining futures must not be attempted.
///
/// The types in the parameter pack T must all be copy-constructible.
///
/// When the first type in the parameter pack is \ref with_clock then it has the effect
/// of changing the clock used for timeouts by this instance. This type is omitted from
/// the parameter of the future<> objects.
///
/// Example:
///
///    future<int> f;
///    shared_future<with_clock<manual_clock>, int> sf(std::move(f));
///    future<int> f2 = sf;
///
template<typename... T>
class shared_future {
    template <typename... U> friend class shared_promise;
    using options = future_option_traits<T...>;
public:
    using clock = typename options::clock_type;
    using time_point = typename clock::time_point;
    using future_type = typename future_option_traits<T...>::template parametrize<future>::type;
    using promise_type = typename future_option_traits<T...>::template parametrize<promise>::type;
    using value_tuple_type = typename future_option_traits<T...>::template parametrize<std::tuple>::type;
private:
    using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;

    /// \cond internal
    class shared_state : public enable_lw_shared_from_this<shared_state> {
        future_type _original_future;
        expiring_fifo<promise_type, promise_expiry, clock> _peers;

    public:
        ~shared_state() {
            // Don't warn if the shared future is exceptional. Any
            // warnings will be reported by the futures returned by
            // get_future.
            if (_original_future.failed()) {
                _original_future.ignore_ready_future();
            }
        }
        explicit shared_state(future_type f) noexcept : _original_future(std::move(f)) { }
        void resolve(future_type&& f) noexcept {
            _original_future = std::move(f);
            auto& state = _original_future._state;
            if (_original_future.failed()) {
                while (_peers) {
                    _peers.front().set_exception(state.get_exception());
                    _peers.pop_front();
                }
            } else {
                while (_peers) {
                    auto& p = _peers.front();
                    try {
                        p.set_value(state.get_value());
                    } catch (...) {
                        p.set_exception(std::current_exception());
                    }
                    _peers.pop_front();
                }
            }
        }

        future_type get_future(time_point timeout = time_point::max()) noexcept {
            // Note that some functions called below may throw,
            // like pushing to _peers or copying _original_future's ready value.
            // We'd rather terminate than propagate these errors similar to
            // .then()'s failure to allocate a continuation as the caller cannot
            // distinguish between an error returned by the original future to
            // failing to perform `get_future` itself.
            memory::disable_failure_guard dfg;
            if (!_original_future.available()) {
                promise_type p;
                auto f = p.get_future();
                if (_original_future._state.valid()) {
                    // _original_future's result is forwarded to each peer.
                    (void)_original_future.then_wrapped([s = this->shared_from_this()] (future_type&& f) mutable {
                        s->resolve(std::move(f));
                    });
                }
                _peers.push_back(std::move(p), timeout);
                return f;
            } else if (_original_future.failed()) {
                return future_type(exception_future_marker(), std::exception_ptr(_original_future._state.get_exception()));
            } else {
                return future_type(ready_future_marker(), _original_future._state.get_value());
            }
        }

        bool available() const noexcept {
            return _original_future.available();
        }

        bool failed() const noexcept {
            return _original_future.failed();
        }
    };
    /// \endcond
    lw_shared_ptr<shared_state> _state;
public:
    /// \brief Forwards the result of future \c f into this shared_future.
    shared_future(future_type f)
        : _state(make_lw_shared<shared_state>(std::move(f))) { }

    shared_future() = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future(const shared_future&) = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future& operator=(const shared_future&) = default; // noexcept, based on respective constructor
    shared_future(shared_future&&) = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future& operator=(shared_future&&) = default; // noexcept, based on the respective constructor

    /// \brief Creates a new \c future which will resolve with the result of this shared_future
    ///
    /// \param timeout When engaged, the returned future will resolve with \ref timed_out_error
    /// if this shared_future doesn't resolve before timeout is reached.
    ///
    /// This object must be in a valid state.
    future_type get_future(time_point timeout = time_point::max()) const {
        return _state->get_future(timeout);
    }

    /// \brief Returns true if the future is available (ready or failed)
    ///
    /// \note This object must be in a valid state.
    bool available() const noexcept {
        return _state->available();
    }

    /// \brief Returns true if the future is failed
    ///
    /// \note This object must be in a valid state.
    bool failed() const noexcept {
        return _state->failed();
    }

    /// \brief Equivalent to \ref get_future()
    operator future_type() const {
        return get_future();
    }

    /// \brief Returns true if the instance is in valid state
    bool valid() const noexcept {
        return bool(_state);
    }
};

/// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
///
/// When the shared_promise is made ready, every waiter is also made ready.
///
/// Like the shared_future, the types in the parameter pack T must all be copy-constructible.
template <typename... T>
class shared_promise {
public:
    using shared_future_type = shared_future<T...>;
    using future_type = typename shared_future_type::future_type;
    using promise_type = typename shared_future_type::promise_type;
    using clock = typename shared_future_type::clock;
    using time_point = typename shared_future_type::time_point;
    using value_tuple_type = typename shared_future_type::value_tuple_type;
private:
    promise_type _promise;
    shared_future_type _shared_future;
    static constexpr bool copy_noexcept = future_type::copy_noexcept;
public:
    shared_promise(const shared_promise&) = delete;
    shared_promise(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
    shared_promise& operator=(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
    shared_promise() : _promise(), _shared_future(_promise.get_future()) {
    }

    /// \brief Gets new future associated with this promise.
    /// If the promise is not resolved before timeout the returned future will resolve with \ref timed_out_error.
    /// This instance doesn't have to be kept alive until the returned future resolves.
    future_type get_shared_future(time_point timeout = time_point::max()) const {
        return _shared_future.get_future(timeout);
    }

    /// \brief Sets the shared_promise's value (as tuple; by copying), same as normal promise
    void set_value(const value_tuple_type& result) noexcept(copy_noexcept) {
        _promise.set_value(result);
    }

    /// \brief Sets the shared_promise's value (as tuple; by moving), same as normal promise
    void set_value(value_tuple_type&& result) noexcept {
        _promise.set_value(std::move(result));
    }

    /// \brief Sets the shared_promise's value (variadic), same as normal promise
    template <typename... A>
    void set_value(A&&... a) noexcept {
        _promise.set_value(std::forward<A>(a)...);
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    void set_exception(std::exception_ptr ex) noexcept {
        _promise.set_exception(std::move(ex));
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    template<typename Exception>
    void set_exception(Exception&& e) noexcept {
        set_exception(make_exception_ptr(std::forward<Exception>(e)));
    }

    /// \brief Returns true if the underlying future is available (ready or failed)
    bool available() const noexcept {
        return _shared_future.available();
    }

    /// \brief Returns true if the underlying future is  failed
    bool failed() const noexcept {
        return _shared_future.failed();
    }
};

/// @}

}
<commit_msg>shared_future, shared_promise: mark methods depnding on shared_state::get_future noexcept<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) 2015 ScyllaDB
 */

#pragma once

#include <seastar/core/future.hh>
#include <seastar/core/expiring_fifo.hh>

namespace seastar {

/// \addtogroup future-module
/// @{

/// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
template<typename Clock>
struct with_clock {};

/// \cond internal

template <typename... T>
struct future_option_traits;

template <typename Clock, typename... T>
struct future_option_traits<with_clock<Clock>, T...> {
    using clock_type = Clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

template <typename... T>
struct future_option_traits {
    using clock_type = lowres_clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

/// \endcond

/// \brief Like \ref future except the result can be waited for by many fibers.
///
/// Represents a value which may not yet be ready. A fiber can wait for the value using
/// the \ref future obtained by calling \ref get_future() or casting to \ref future type.
/// Multiple fibers are allowed to obtain a \ref future for the result using the same
/// instance of \ref shared_future.
///
/// All futures obtained from shared_future should end up in the same state. However,
/// if the value's copy constructor throws, some of the futures may end up in a failed state
/// with an exception thrown from the copy constructor and end up with a state
/// different than other futures.
///
/// The scope of shared_future instance doesn't have to include scopes of the futures
/// obtained from that instance. In that sense the returned futures are independent.
///
/// shared_future can be copied at any time and all copies will resolve with the same value.
///
/// shared_future can be in a disengaged state when it's default-constructed or moved-from.
/// When it's in such a state we say it's invalid and obtaining futures must not be attempted.
///
/// The types in the parameter pack T must all be copy-constructible.
///
/// When the first type in the parameter pack is \ref with_clock then it has the effect
/// of changing the clock used for timeouts by this instance. This type is omitted from
/// the parameter of the future<> objects.
///
/// Example:
///
///    future<int> f;
///    shared_future<with_clock<manual_clock>, int> sf(std::move(f));
///    future<int> f2 = sf;
///
template<typename... T>
class shared_future {
    template <typename... U> friend class shared_promise;
    using options = future_option_traits<T...>;
public:
    using clock = typename options::clock_type;
    using time_point = typename clock::time_point;
    using future_type = typename future_option_traits<T...>::template parametrize<future>::type;
    using promise_type = typename future_option_traits<T...>::template parametrize<promise>::type;
    using value_tuple_type = typename future_option_traits<T...>::template parametrize<std::tuple>::type;
private:
    using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;

    /// \cond internal
    class shared_state : public enable_lw_shared_from_this<shared_state> {
        future_type _original_future;
        expiring_fifo<promise_type, promise_expiry, clock> _peers;

    public:
        ~shared_state() {
            // Don't warn if the shared future is exceptional. Any
            // warnings will be reported by the futures returned by
            // get_future.
            if (_original_future.failed()) {
                _original_future.ignore_ready_future();
            }
        }
        explicit shared_state(future_type f) noexcept : _original_future(std::move(f)) { }
        void resolve(future_type&& f) noexcept {
            _original_future = std::move(f);
            auto& state = _original_future._state;
            if (_original_future.failed()) {
                while (_peers) {
                    _peers.front().set_exception(state.get_exception());
                    _peers.pop_front();
                }
            } else {
                while (_peers) {
                    auto& p = _peers.front();
                    try {
                        p.set_value(state.get_value());
                    } catch (...) {
                        p.set_exception(std::current_exception());
                    }
                    _peers.pop_front();
                }
            }
        }

        future_type get_future(time_point timeout = time_point::max()) noexcept {
            // Note that some functions called below may throw,
            // like pushing to _peers or copying _original_future's ready value.
            // We'd rather terminate than propagate these errors similar to
            // .then()'s failure to allocate a continuation as the caller cannot
            // distinguish between an error returned by the original future to
            // failing to perform `get_future` itself.
            memory::disable_failure_guard dfg;
            if (!_original_future.available()) {
                promise_type p;
                auto f = p.get_future();
                if (_original_future._state.valid()) {
                    // _original_future's result is forwarded to each peer.
                    (void)_original_future.then_wrapped([s = this->shared_from_this()] (future_type&& f) mutable {
                        s->resolve(std::move(f));
                    });
                }
                _peers.push_back(std::move(p), timeout);
                return f;
            } else if (_original_future.failed()) {
                return future_type(exception_future_marker(), std::exception_ptr(_original_future._state.get_exception()));
            } else {
                return future_type(ready_future_marker(), _original_future._state.get_value());
            }
        }

        bool available() const noexcept {
            return _original_future.available();
        }

        bool failed() const noexcept {
            return _original_future.failed();
        }
    };
    /// \endcond
    lw_shared_ptr<shared_state> _state;
public:
    /// \brief Forwards the result of future \c f into this shared_future.
    shared_future(future_type f)
        : _state(make_lw_shared<shared_state>(std::move(f))) { }

    shared_future() = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future(const shared_future&) = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future& operator=(const shared_future&) = default; // noexcept, based on respective constructor
    shared_future(shared_future&&) = default; // noexcept, based on the respective lw_shared_ptr constructor
    shared_future& operator=(shared_future&&) = default; // noexcept, based on the respective constructor

    /// \brief Creates a new \c future which will resolve with the result of this shared_future
    ///
    /// \param timeout When engaged, the returned future will resolve with \ref timed_out_error
    /// if this shared_future doesn't resolve before timeout is reached.
    ///
    /// This object must be in a valid state.
    future_type get_future(time_point timeout = time_point::max()) const noexcept {
        return _state->get_future(timeout);
    }

    /// \brief Returns true if the future is available (ready or failed)
    ///
    /// \note This object must be in a valid state.
    bool available() const noexcept {
        return _state->available();
    }

    /// \brief Returns true if the future is failed
    ///
    /// \note This object must be in a valid state.
    bool failed() const noexcept {
        return _state->failed();
    }

    /// \brief Equivalent to \ref get_future()
    operator future_type() const noexcept {
        return get_future();
    }

    /// \brief Returns true if the instance is in valid state
    bool valid() const noexcept {
        return bool(_state);
    }
};

/// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
///
/// When the shared_promise is made ready, every waiter is also made ready.
///
/// Like the shared_future, the types in the parameter pack T must all be copy-constructible.
template <typename... T>
class shared_promise {
public:
    using shared_future_type = shared_future<T...>;
    using future_type = typename shared_future_type::future_type;
    using promise_type = typename shared_future_type::promise_type;
    using clock = typename shared_future_type::clock;
    using time_point = typename shared_future_type::time_point;
    using value_tuple_type = typename shared_future_type::value_tuple_type;
private:
    promise_type _promise;
    shared_future_type _shared_future;
    static constexpr bool copy_noexcept = future_type::copy_noexcept;
public:
    shared_promise(const shared_promise&) = delete;
    shared_promise(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
    shared_promise& operator=(shared_promise&&) = default; // noexcept, based on the respective promise and shared_future constructors
    shared_promise() : _promise(), _shared_future(_promise.get_future()) {
    }

    /// \brief Gets new future associated with this promise.
    /// If the promise is not resolved before timeout the returned future will resolve with \ref timed_out_error.
    /// This instance doesn't have to be kept alive until the returned future resolves.
    future_type get_shared_future(time_point timeout = time_point::max()) const noexcept {
        return _shared_future.get_future(timeout);
    }

    /// \brief Sets the shared_promise's value (as tuple; by copying), same as normal promise
    void set_value(const value_tuple_type& result) noexcept(copy_noexcept) {
        _promise.set_value(result);
    }

    /// \brief Sets the shared_promise's value (as tuple; by moving), same as normal promise
    void set_value(value_tuple_type&& result) noexcept {
        _promise.set_value(std::move(result));
    }

    /// \brief Sets the shared_promise's value (variadic), same as normal promise
    template <typename... A>
    void set_value(A&&... a) noexcept {
        _promise.set_value(std::forward<A>(a)...);
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    void set_exception(std::exception_ptr ex) noexcept {
        _promise.set_exception(std::move(ex));
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    template<typename Exception>
    void set_exception(Exception&& e) noexcept {
        set_exception(make_exception_ptr(std::forward<Exception>(e)));
    }

    /// \brief Returns true if the underlying future is available (ready or failed)
    bool available() const noexcept {
        return _shared_future.available();
    }

    /// \brief Returns true if the underlying future is  failed
    bool failed() const noexcept {
        return _shared_future.failed();
    }
};

/// @}

}
<|endoftext|>


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

#pragma once

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

// Shard
#include "shard/Path.hpp"
#include "shard/String.hpp"
#include "shard/ViewPtr.hpp"
#include "shard/tokenizer/Source.hpp"
#include "shard/tokenizer/KeywordType.hpp"
#include "shard/tokenizer/TokenType.hpp"
#include "shard/tokenizer/Token.hpp"
#include "shard/tokenizer/TokenizerException.hpp"

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

namespace shard {
inline namespace v1 {
namespace tokenizer {

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

class Tokenizer;

class TokenizerIterator
{

protected:

    ViewPtr<Tokenizer> m_tokenizer;

public:

    TokenizerIterator() = default;
    TokenizerIterator(Tokenizer* tokenizer):
        m_tokenizer(tokenizer) {}
    TokenizerIterator(TokenizerIterator&) = default;
    TokenizerIterator(TokenizerIterator&&) = default;

    const Token& operator*() const;
    TokenizerIterator& operator++();
    TokenizerIterator operator++(int);

    inline Tokenizer* getTokenizer() const
    {
        return m_tokenizer;
    }
};

inline bool operator==(const TokenizerIterator& lhs, const TokenizerIterator& rhs)
{
    return (lhs.getTokenizer() == nullptr || (*lhs).getType() == TokenType::End) && (rhs.getTokenizer() == nullptr || (*rhs).getType() == TokenType::End);
}

inline bool operator!=(const TokenizerIterator& lhs, const TokenizerIterator& rhs)
{
    return !(lhs == rhs);
}

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

class Tokenizer
{

protected:

    Source m_src;
    Token m_current;

    static constexpr char string_literal_border = '"';
    static constexpr char char_literal_border = '\'';

// ===================================================================================== //

public:

    Tokenizer(const Path& path):
            m_src(path){next();}

    Tokenizer(const String& source):
            m_src(source){next();}

// ===================================================================================== //

protected:

    /**
     * @brief returns if current character is between two chars in the ASCII table.
     */
    inline bool isBetween(char value1, char value2) noexcept
    {
        return !empty() && (m_src.getCurrent() >= value1) && (m_src.getCurrent() <= value2);
    }

    /**
    * @brief returns if current character is whitespace.
    */
    inline bool isWhitespace() noexcept
    {
        return isBetween('\1', ' ');
    }

    /**
     * @brief returns if current character is a letter.
     */
    inline bool isLetter() noexcept
    {
        return isBetween('a', 'z') || isBetween('A', 'Z');
    }

    /**
     * @brief returns if current character is a letter, number or '_'.
     */
    inline bool isIdentifier() noexcept
    {
        return isBetween('a', 'z') || isBetween('A', 'Z') || isDigit() || is('_');
    }

    /**
     * @brief returns if current character is a number.
     */
    inline bool isDigit() noexcept
    {
        return isBetween('0', '9');
    }

    /**
     * @brief returns if current character is tested character.
     */
    inline bool is(char value) noexcept
    {
        return !empty() && m_src.getCurrent() == value;
    }

    /**
     * @brief returns if current character is one of tested characters.
     */
    template<typename... Chars>
    inline bool is(char a, Chars... options) noexcept
    {
        char chars[]{a, options...};
        for (const char option : chars)
        {
            if (is(option))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * @brief returns if current character is tested character.
     * If so, moves to next character.
     */
    inline bool match(char value) noexcept
    {
        if (is(value))
        {
            m_src.extract();
            return true;
        }
        return false;
    }

    /**
     * @brief returns if current character is one of tested characters.
     * If so, moves to next character.
     */
    template<typename... Chars>
    inline bool match(Chars... options) noexcept
    {
        const char chars[]{options...};
        for (const char option : chars)
        {
            if (is(option))
            {
                m_src.extract();
                return true;
            }
        }
        return false;
    }

// ===================================================================================== //

protected:

    /**
     * @brief Iterates to next non-whitespace char.
     */
    inline void skipWhitespace() noexcept
    {
        while (isWhitespace())
        {
            m_src.extract();
        }
    }

    /**
     * @brief chcecks if source is empty.
     */
    inline bool empty()
    {
        return m_src.empty();
    }

// ===================================================================================== //

protected:

    /**
     * @brief tokenizes tokentype number.
     */
    void tokenizeNumber();

    /**
     * @brief tokenizes string into identifier.
     */
    void tokenizeIdentifier() noexcept;

    /**
     * @brief tokenizes string value into keyword, function or identifier.
     */
    void tokenizeString();

    /**
     * @brief tokenizes string value into keyword, function or identifier.
     */
    void tokenizeChar();

    /**
     * @brief tokenizes operators.
     */
    void tokenizeOperator();

// ===================================================================================== //

public:

    /**
     * @brief go to next token.
     */
    void next();

    /**
     * @brief get current token.
     */
    inline const Token& get() const noexcept
    {
        return m_current;
    }

    /**
     * @brief get current token an move to next.
     */
    inline const Token extract()
    {
        auto tmp = m_current;
        next();
        return tmp;
    }

    /**
     * @brief checks if there is token available.
     */
    inline bool isEof()
    {
        return m_current.getType() == TokenType::End;
    }

// ===================================================================================== //

public:

    /**
     * @brief returns tokenizer iterator pointing to currect token.
     */
    inline TokenizerIterator begin() noexcept
    {
        return TokenizerIterator(this);
    }

    /**
     * @brief returns tokenizer iterator pointing to token with end.
     */
    inline TokenizerIterator end() noexcept
    {
        return TokenizerIterator(nullptr);
    }
};

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

}
}
}

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

<commit_msg>return by value shouldnt be const<commit_after>/* ************************************************************************* */
/* This file is part of Shard.                                               */
/*                                                                           */
/* Shard is free software: you can redistribute it and/or modify             */
/* it under the terms of the GNU Affero General Public License as            */
/* published by the Free Software Foundation.                                */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU Affero General Public License for more details.                       */
/*                                                                           */
/* You should have received a copy of the GNU Affero General Public License  */
/* along with this program. If not, see <http://www.gnu.org/licenses/>.      */
/* ************************************************************************* */

#pragma once

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

// Shard
#include "shard/Path.hpp"
#include "shard/String.hpp"
#include "shard/ViewPtr.hpp"
#include "shard/tokenizer/Source.hpp"
#include "shard/tokenizer/KeywordType.hpp"
#include "shard/tokenizer/TokenType.hpp"
#include "shard/tokenizer/Token.hpp"
#include "shard/tokenizer/TokenizerException.hpp"

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

namespace shard {
inline namespace v1 {
namespace tokenizer {

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

class Tokenizer;

class TokenizerIterator
{

protected:

    ViewPtr<Tokenizer> m_tokenizer;

public:

    TokenizerIterator() = default;
    TokenizerIterator(Tokenizer* tokenizer):
        m_tokenizer(tokenizer) {}
    TokenizerIterator(TokenizerIterator&) = default;
    TokenizerIterator(TokenizerIterator&&) = default;

    const Token& operator*() const;
    TokenizerIterator& operator++();
    TokenizerIterator operator++(int);

    inline Tokenizer* getTokenizer() const
    {
        return m_tokenizer;
    }
};

inline bool operator==(const TokenizerIterator& lhs, const TokenizerIterator& rhs)
{
    return (lhs.getTokenizer() == nullptr || (*lhs).getType() == TokenType::End) && (rhs.getTokenizer() == nullptr || (*rhs).getType() == TokenType::End);
}

inline bool operator!=(const TokenizerIterator& lhs, const TokenizerIterator& rhs)
{
    return !(lhs == rhs);
}

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

class Tokenizer
{

protected:

    Source m_src;
    Token m_current;

    static constexpr char string_literal_border = '"';
    static constexpr char char_literal_border = '\'';

// ===================================================================================== //

public:

    Tokenizer(const Path& path):
            m_src(path){next();}

    Tokenizer(const String& source):
            m_src(source){next();}

// ===================================================================================== //

protected:

    /**
     * @brief returns if current character is between two chars in the ASCII table.
     */
    inline bool isBetween(char value1, char value2) noexcept
    {
        return !empty() && (m_src.getCurrent() >= value1) && (m_src.getCurrent() <= value2);
    }

    /**
    * @brief returns if current character is whitespace.
    */
    inline bool isWhitespace() noexcept
    {
        return isBetween('\1', ' ');
    }

    /**
     * @brief returns if current character is a letter.
     */
    inline bool isLetter() noexcept
    {
        return isBetween('a', 'z') || isBetween('A', 'Z');
    }

    /**
     * @brief returns if current character is a letter, number or '_'.
     */
    inline bool isIdentifier() noexcept
    {
        return isBetween('a', 'z') || isBetween('A', 'Z') || isDigit() || is('_');
    }

    /**
     * @brief returns if current character is a number.
     */
    inline bool isDigit() noexcept
    {
        return isBetween('0', '9');
    }

    /**
     * @brief returns if current character is tested character.
     */
    inline bool is(char value) noexcept
    {
        return !empty() && m_src.getCurrent() == value;
    }

    /**
     * @brief returns if current character is one of tested characters.
     */
    template<typename... Chars>
    inline bool is(char a, Chars... options) noexcept
    {
        char chars[]{a, options...};
        for (const char option : chars)
        {
            if (is(option))
            {
                return true;
            }
        }
        return false;
    }

    /**
     * @brief returns if current character is tested character.
     * If so, moves to next character.
     */
    inline bool match(char value) noexcept
    {
        if (is(value))
        {
            m_src.extract();
            return true;
        }
        return false;
    }

    /**
     * @brief returns if current character is one of tested characters.
     * If so, moves to next character.
     */
    template<typename... Chars>
    inline bool match(Chars... options) noexcept
    {
        const char chars[]{options...};
        for (const char option : chars)
        {
            if (is(option))
            {
                m_src.extract();
                return true;
            }
        }
        return false;
    }

// ===================================================================================== //

protected:

    /**
     * @brief Iterates to next non-whitespace char.
     */
    inline void skipWhitespace() noexcept
    {
        while (isWhitespace())
        {
            m_src.extract();
        }
    }

    /**
     * @brief chcecks if source is empty.
     */
    inline bool empty()
    {
        return m_src.empty();
    }

// ===================================================================================== //

protected:

    /**
     * @brief tokenizes tokentype number.
     */
    void tokenizeNumber();

    /**
     * @brief tokenizes string into identifier.
     */
    void tokenizeIdentifier() noexcept;

    /**
     * @brief tokenizes string value into keyword, function or identifier.
     */
    void tokenizeString();

    /**
     * @brief tokenizes string value into keyword, function or identifier.
     */
    void tokenizeChar();

    /**
     * @brief tokenizes operators.
     */
    void tokenizeOperator();

// ===================================================================================== //

public:

    /**
     * @brief go to next token.
     */
    void next();

    /**
     * @brief get current token.
     */
    inline const Token& get() const noexcept
    {
        return m_current;
    }

    /**
     * @brief get current token an move to next.
     */
    inline Token extract()
    {
        auto tmp = m_current;
        next();
        return tmp;
    }

    /**
     * @brief checks if there is token available.
     */
    inline bool isEof()
    {
        return m_current.getType() == TokenType::End;
    }

// ===================================================================================== //

public:

    /**
     * @brief returns tokenizer iterator pointing to currect token.
     */
    inline TokenizerIterator begin() noexcept
    {
        return TokenizerIterator(this);
    }

    /**
     * @brief returns tokenizer iterator pointing to token with end.
     */
    inline TokenizerIterator end() noexcept
    {
        return TokenizerIterator(nullptr);
    }
};

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

}
}
}

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

<|endoftext|>


<commit_before>//
//  src/macho/container.cc
//  tbd
//
//  Created by inoahdev on 4/24/17.
//  Copyright © 2017 inoahdev. All rights reserved.
//

#include "header/symbol_table.h"
#include "container.h"

namespace macho {
    container::container(FILE *file, long base)
    : file_(file), base_(base) {
        const auto position = ftell(file);
        fseek(file, 0, SEEK_END);

        this->size_ = ftell(file);

        fseek(file, position, SEEK_SET);
        this->validate();
    }

    container::container(FILE *file, long base, size_t size)
    : file_(file), base_(base), size_(size) {
        this->validate();
    }

    container::~container() {
        auto &cached = cached_;
        if (cached) {
            delete[] cached;
        }

        auto &string_table = string_table_;
        if (string_table) {
            delete[] string_table;
        }
    }

    void container::validate() {
        auto &base = base_;
        auto &file = file_;

        auto &header = header_;
        auto &magic = header.magic;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        fseek(file, base, SEEK_SET);
        fread(&magic, sizeof(uint32_t), 1, file);

        const auto macho_file_is_regular = magic_is_thin(magic);
        if (macho_file_is_regular) {
            fread(&header.cputype, sizeof(header) - sizeof(uint32_t), 1, file);

            if (is_big_endian) {
                swap_mach_header(&header);
            }
        } else {
            const auto macho_file_is_fat = magic_is_fat(magic);
            if (macho_file_is_fat) {
                fprintf(stderr, "Architecture at location (0x%.8lX) cannot be a fat mach-o file itself\n", base);
                exit(1);
            } else {
                fprintf(stderr, "Architecture at location (0x%.8lX) is not a valid mach-o base\n", base);
                exit(1);
            }
        }

        fseek(file, position, SEEK_SET);
    }

    void container::iterate_load_commands(const std::function<bool (const struct load_command *)> &callback) {
        const auto &base = base_;
        const auto &file = file_;

        const auto &header = header_;
        const auto is_big_endian = this->is_big_endian();

        const auto &ncmds = header.ncmds;
        const auto &sizeofcmds = header.sizeofcmds;

        auto &cached = cached_;
        auto created_cache = false;

        if (!cached) {
            cached = new char[sizeofcmds];
            created_cache = true;

            auto load_command_base = base + sizeof(header);
            if (this->is_64_bit()) {
                load_command_base += sizeof(uint32_t);
            }

            const auto position = ftell(file);

            fseek(file, load_command_base, SEEK_SET);
            fread(cached, sizeofcmds, 1, file);

            fseek(file, position, SEEK_SET);
        }

        auto size_used = 0;
        auto cached_index = 0;

        auto should_callback = true;
        for (auto i = 0; i < ncmds; i++) {
            auto load_cmd = (struct load_command *)&cached[cached_index];
            const auto &cmdsize = load_cmd->cmdsize;

            if (created_cache) {
                if (is_big_endian) {
                    swap_load_command(load_cmd);
                }

                if (cmdsize < sizeof(struct load_command)) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is too small to be valid\n", i, base);
                    exit(1);
                }

                if (cmdsize >= sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is larger than/or equal to entire area allocated for load-commands\n", i, base);
                    exit(1);
                }

                size_used += cmdsize;
                if (size_used > sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) goes past end of area allocated for load-commands\n", i, base);
                    exit(1);
                } else if (size_used == sizeofcmds && i != ncmds - 1) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) takes up all of the remaining space allocated for load-commands\n", i, base);
                    exit(1);
                }
            }

            if (should_callback) {
                auto result = callback(load_cmd);
                if (!result) {
                    should_callback = false;
                }
            }

            if (!should_callback && !created_cache) {
                break;
            }

            cached_index += cmdsize;
        }
    }

    void container::iterate_symbols(const std::function<bool (const struct nlist_64 &, const char *)> &callback) {
        auto &base = base_;
        auto &file = file_;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        struct symtab_command *symtab_command = nullptr;
        iterate_load_commands([&](const struct load_command *load_cmd) {
            if (load_cmd->cmd != load_commands::symbol_table) {
                return true;
            }

            symtab_command = (struct symtab_command *)load_cmd;
            if (is_big_endian) {
                swap_symtab_command(symtab_command);
            }

            return false;
        });

        if (!symtab_command) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) does not have a symbol-table\n", base);
            exit(1);
        }

        const auto &string_table_location = symtab_command->stroff;
        if (string_table_location > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table outside of its container\n", base);
            exit(1);
        }

        const auto &string_table_size = symtab_command->strsize;
        if (string_table_size > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table with a size larger than that of itself\n", base);
            exit(1);
        }

        const auto string_table_end = string_table_location + string_table_size;
        if (string_table_end > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table that goes outside of its container\n", base);
            exit(1);
        }

        const auto &symbol_table_location = symtab_command->symoff;
        if (symbol_table_location > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that is outside of its container\n", base);
            exit(1);
        }

        auto &string_table = string_table_;
        if (!string_table) {
            string_table = new char[string_table_size];

            fseek(file, base + string_table_location, SEEK_SET);
            fread(string_table, string_table_size, 1, file);
        }

        const auto &symbol_table_count = symtab_command->nsyms;
        const auto string_table_max_index = string_table_size - 1;

        fseek(file, base + symbol_table_location, SEEK_SET);

        const auto is_64_bit = this->is_64_bit();
        if (is_64_bit) {
            const auto symbol_table_size = sizeof(struct nlist_64) * symbol_table_count;
            if (symbol_table_size > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table with a size larger than itself\n", base);
                exit(1);
            }

            const auto symbol_table_end = symbol_table_location + symbol_table_size;
            if (symbol_table_end > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that goes outside of its boundaries\n", base);
                exit(1);
            }

            const auto symbol_table = new struct nlist_64[symbol_table_count];
            fread(symbol_table, symbol_table_size, 1, file);

            if (is_big_endian) {
                swap_nlist_64(symbol_table, symbol_table_count);
            }

            for (auto i = 0; i < symbol_table_count; i++) {
                const auto &symbol_table_entry = symbol_table[i];
                const auto &symbol_table_entry_string_table_index = symbol_table_entry.n_un.n_strx;

                if (symbol_table_entry_string_table_index > string_table_max_index) {
                    fprintf(stderr, "Symbol-table entry (at index %d) has symbol-string past end of string-table of mach-o container (at base 0x%.8lX)\n", i, base);
                    exit(1);
                }

                const auto symbol_table_string_table_string = &string_table[symbol_table_entry_string_table_index];
                const auto result = callback(symbol_table_entry, symbol_table_string_table_string);

                if (!result) {
                    break;
                }
            }

            delete[] symbol_table;
        } else {
            const auto symbol_table_size = sizeof(struct nlist) * symbol_table_count;
            if (symbol_table_size > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table with a size larger than itself\n", base);
                exit(1);
            }

            const auto symbol_table_end = symbol_table_location + symbol_table_size;
            if (symbol_table_end > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that goes outside of its boundaries\n", base);
                exit(1);
            }

            const auto symbol_table = new struct nlist[symbol_table_count];
            fread(symbol_table, symbol_table_size, 1, file);

            if (is_big_endian) {
                swap_nlist(symbol_table, symbol_table_count);
            }

            for (auto i = 0; i < symbol_table_count; i++) {
                const auto &symbol_table_entry = symbol_table[i];
                const auto &symbol_table_entry_string_table_index = symbol_table_entry.n_un.n_strx;

                if (i == 10956) {
                    printf("");
                }

                if (symbol_table_entry_string_table_index > string_table_max_index) {
                    fprintf(stderr, "Symbol-table entry (at index %d) has symbol-string past end of string-table of mach-o container (at base 0x%.8lX)\n", i, base);
                    exit(1);
                }

                const struct nlist_64 symbol_table_entry_64 = { { symbol_table_entry.n_un.n_strx }, symbol_table_entry.n_type, symbol_table_entry.n_sect, (uint16_t)symbol_table_entry.n_desc, symbol_table_entry.n_value };

                const auto symbol_table_string_table_string = &string_table[symbol_table_entry_string_table_index];
                const auto result = callback(symbol_table_entry_64, symbol_table_string_table_string);

                if (!result) {
                    break;
                }
            }

            delete[] symbol_table;
        }

        fseek(file, position, SEEK_SET);
    }
}
<commit_msg>Remove debug code<commit_after>//
//  src/macho/container.cc
//  tbd
//
//  Created by inoahdev on 4/24/17.
//  Copyright © 2017 inoahdev. All rights reserved.
//

#include "header/symbol_table.h"
#include "container.h"

namespace macho {
    container::container(FILE *file, long base)
    : file_(file), base_(base) {
        const auto position = ftell(file);
        fseek(file, 0, SEEK_END);

        this->size_ = ftell(file);

        fseek(file, position, SEEK_SET);
        this->validate();
    }

    container::container(FILE *file, long base, size_t size)
    : file_(file), base_(base), size_(size) {
        this->validate();
    }

    container::~container() {
        auto &cached = cached_;
        if (cached) {
            delete[] cached;
        }

        auto &string_table = string_table_;
        if (string_table) {
            delete[] string_table;
        }
    }

    void container::validate() {
        auto &base = base_;
        auto &file = file_;

        auto &header = header_;
        auto &magic = header.magic;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        fseek(file, base, SEEK_SET);
        fread(&magic, sizeof(uint32_t), 1, file);

        const auto macho_file_is_regular = magic_is_thin(magic);
        if (macho_file_is_regular) {
            fread(&header.cputype, sizeof(header) - sizeof(uint32_t), 1, file);

            if (is_big_endian) {
                swap_mach_header(&header);
            }
        } else {
            const auto macho_file_is_fat = magic_is_fat(magic);
            if (macho_file_is_fat) {
                fprintf(stderr, "Architecture at location (0x%.8lX) cannot be a fat mach-o file itself\n", base);
                exit(1);
            } else {
                fprintf(stderr, "Architecture at location (0x%.8lX) is not a valid mach-o base\n", base);
                exit(1);
            }
        }

        fseek(file, position, SEEK_SET);
    }

    void container::iterate_load_commands(const std::function<bool (const struct load_command *)> &callback) {
        const auto &base = base_;
        const auto &file = file_;

        const auto &header = header_;
        const auto is_big_endian = this->is_big_endian();

        const auto &ncmds = header.ncmds;
        const auto &sizeofcmds = header.sizeofcmds;

        auto &cached = cached_;
        auto created_cache = false;

        if (!cached) {
            cached = new char[sizeofcmds];
            created_cache = true;

            auto load_command_base = base + sizeof(header);
            if (this->is_64_bit()) {
                load_command_base += sizeof(uint32_t);
            }

            const auto position = ftell(file);

            fseek(file, load_command_base, SEEK_SET);
            fread(cached, sizeofcmds, 1, file);

            fseek(file, position, SEEK_SET);
        }

        auto size_used = 0;
        auto cached_index = 0;

        auto should_callback = true;
        for (auto i = 0; i < ncmds; i++) {
            auto load_cmd = (struct load_command *)&cached[cached_index];
            const auto &cmdsize = load_cmd->cmdsize;

            if (created_cache) {
                if (is_big_endian) {
                    swap_load_command(load_cmd);
                }

                if (cmdsize < sizeof(struct load_command)) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is too small to be valid\n", i, base);
                    exit(1);
                }

                if (cmdsize >= sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) is larger than/or equal to entire area allocated for load-commands\n", i, base);
                    exit(1);
                }

                size_used += cmdsize;
                if (size_used > sizeofcmds) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) goes past end of area allocated for load-commands\n", i, base);
                    exit(1);
                } else if (size_used == sizeofcmds && i != ncmds - 1) {
                    fprintf(stderr, "Load-command (at index %d) of mach-o container (at base 0x%.8lX) takes up all of the remaining space allocated for load-commands\n", i, base);
                    exit(1);
                }
            }

            if (should_callback) {
                auto result = callback(load_cmd);
                if (!result) {
                    should_callback = false;
                }
            }

            if (!should_callback && !created_cache) {
                break;
            }

            cached_index += cmdsize;
        }
    }

    void container::iterate_symbols(const std::function<bool (const struct nlist_64 &, const char *)> &callback) {
        auto &base = base_;
        auto &file = file_;

        const auto is_big_endian = this->is_big_endian();
        const auto position = ftell(file);

        struct symtab_command *symtab_command = nullptr;
        iterate_load_commands([&](const struct load_command *load_cmd) {
            if (load_cmd->cmd != load_commands::symbol_table) {
                return true;
            }

            symtab_command = (struct symtab_command *)load_cmd;
            if (is_big_endian) {
                swap_symtab_command(symtab_command);
            }

            return false;
        });

        if (!symtab_command) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) does not have a symbol-table\n", base);
            exit(1);
        }

        const auto &string_table_location = symtab_command->stroff;
        if (string_table_location > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table outside of its container\n", base);
            exit(1);
        }

        const auto &string_table_size = symtab_command->strsize;
        if (string_table_size > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table with a size larger than that of itself\n", base);
            exit(1);
        }

        const auto string_table_end = string_table_location + string_table_size;
        if (string_table_end > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a string-table that goes outside of its container\n", base);
            exit(1);
        }

        const auto &symbol_table_location = symtab_command->symoff;
        if (symbol_table_location > size_) {
            fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that is outside of its container\n", base);
            exit(1);
        }

        auto &string_table = string_table_;
        if (!string_table) {
            string_table = new char[string_table_size];

            fseek(file, base + string_table_location, SEEK_SET);
            fread(string_table, string_table_size, 1, file);
        }

        const auto &symbol_table_count = symtab_command->nsyms;
        const auto string_table_max_index = string_table_size - 1;

        fseek(file, base + symbol_table_location, SEEK_SET);

        const auto is_64_bit = this->is_64_bit();
        if (is_64_bit) {
            const auto symbol_table_size = sizeof(struct nlist_64) * symbol_table_count;
            if (symbol_table_size > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table with a size larger than itself\n", base);
                exit(1);
            }

            const auto symbol_table_end = symbol_table_location + symbol_table_size;
            if (symbol_table_end > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that goes outside of its boundaries\n", base);
                exit(1);
            }

            const auto symbol_table = new struct nlist_64[symbol_table_count];
            fread(symbol_table, symbol_table_size, 1, file);

            if (is_big_endian) {
                swap_nlist_64(symbol_table, symbol_table_count);
            }

            for (auto i = 0; i < symbol_table_count; i++) {
                const auto &symbol_table_entry = symbol_table[i];
                const auto &symbol_table_entry_string_table_index = symbol_table_entry.n_un.n_strx;

                if (symbol_table_entry_string_table_index > string_table_max_index) {
                    fprintf(stderr, "Symbol-table entry (at index %d) has symbol-string past end of string-table of mach-o container (at base 0x%.8lX)\n", i, base);
                    exit(1);
                }

                const auto symbol_table_string_table_string = &string_table[symbol_table_entry_string_table_index];
                const auto result = callback(symbol_table_entry, symbol_table_string_table_string);

                if (!result) {
                    break;
                }
            }

            delete[] symbol_table;
        } else {
            const auto symbol_table_size = sizeof(struct nlist) * symbol_table_count;
            if (symbol_table_size > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table with a size larger than itself\n", base);
                exit(1);
            }

            const auto symbol_table_end = symbol_table_location + symbol_table_size;
            if (symbol_table_end > size_) {
                fprintf(stderr, "Mach-o container (at base 0x%.8lX) has a symbol-table that goes outside of its boundaries\n", base);
                exit(1);
            }

            const auto symbol_table = new struct nlist[symbol_table_count];
            fread(symbol_table, symbol_table_size, 1, file);

            if (is_big_endian) {
                swap_nlist(symbol_table, symbol_table_count);
            }

            for (auto i = 0; i < symbol_table_count; i++) {
                const auto &symbol_table_entry = symbol_table[i];
                const auto &symbol_table_entry_string_table_index = symbol_table_entry.n_un.n_strx;

                if (symbol_table_entry_string_table_index > string_table_max_index) {
                    fprintf(stderr, "Symbol-table entry (at index %d) has symbol-string past end of string-table of mach-o container (at base 0x%.8lX)\n", i, base);
                    exit(1);
                }

                const struct nlist_64 symbol_table_entry_64 = { { symbol_table_entry.n_un.n_strx }, symbol_table_entry.n_type, symbol_table_entry.n_sect, (uint16_t)symbol_table_entry.n_desc, symbol_table_entry.n_value };

                const auto symbol_table_string_table_string = &string_table[symbol_table_entry_string_table_index];
                const auto result = callback(symbol_table_entry_64, symbol_table_string_table_string);

                if (!result) {
                    break;
                }
            }

            delete[] symbol_table;
        }

        fseek(file, position, SEEK_SET);
    }
}
<|endoftext|>


<commit_before>#include "data_generator.hpp"
#include "feature_generator.hpp"
#include "feature_sorter.hpp"
#include "update_generator.hpp"
#include "feature_bucketer.hpp"
#include "grid_generator.hpp"

#include "../classif_routine.hpp"
#include "../features_vector.hpp"
#include "../index_builder.hpp"
#include "../osm_decl.hpp"
#include "../data_header.hpp"

#include "../../storage/defines.hpp"

#include "../../platform/platform.hpp"

#include "../../3party/gflags/src/gflags/gflags.h"

#include "../../std/ctime.hpp"
#include "../../std/iostream.hpp"
#include "../../std/iomanip.hpp"
#include "../../std/numeric.hpp"

#include "../../version/version.hpp"

#include "../../base/start_mem_debug.hpp"

DEFINE_bool(version, false, "Display version");
DEFINE_bool(generate_update, false,
              "If specified, update.maps file will be generated from cells in the data path");

DEFINE_bool(sort_features, true, "Sort features data for better cache-friendliness.");
DEFINE_bool(generate_classif, false, "Generate classificator.");
DEFINE_bool(preprocess_xml, false, "1st pass - create nodes/ways/relations data");
DEFINE_bool(generate_features, false, "2nd pass - generate intermediate features");
DEFINE_bool(generate_geometry, false, "3rd pass - split and simplify geometry and triangles for features");
DEFINE_bool(generate_index, false, "4rd pass - generate index");
DEFINE_bool(generate_grid, false, "Generate grid for given bucketing_level");
DEFINE_bool(use_light_nodes, false,
            "If true, use temporary vector of nodes, instead of huge temp file");
DEFINE_string(data_path, "", "Working directory, 'path_to_exe/../../data' if empty.");
DEFINE_string(output, "", "Prefix of filenames of outputted .dat and .idx files.");
DEFINE_string(intermediate_data_path, "", "Path to store nodes, ways, relations.");
DEFINE_int32(bucketing_level, 7, "Level of cell ids for bucketing.");
DEFINE_int32(worldmap_max_zoom, -1, "If specified, features for zoomlevels [0..this_value] "
             " which are enabled in classificator will be added to the separate world.map");

string AddSlashIfNeeded(string const & str)
{
  string result(str);
  size_t const size = result.size();
  if (size)
  {
    if (result.find_last_of('\\') == size - 1)
      result[size - 1] = '/';
    else
      if (result.find_last_of('/') != size - 1)
        result.push_back('/');
  }
  return result;
}

int main(int argc, char ** argv)
{
  google::SetUsageMessage(
      "Takes OSM XML data from stdin and creates data and index files in several passes.");

  google::ParseCommandLineFlags(&argc, &argv, true);

  string const path =
      FLAGS_data_path.empty() ? GetPlatform().WritableDir() : AddSlashIfNeeded(FLAGS_data_path);

  if (FLAGS_version)
  {
    cout << "Tool version: " << VERSION_STRING << endl;
    cout << "Built on: " << VERSION_DATE_STRING << endl;
  }

  // Make a classificator
  if (FLAGS_generate_classif)
  {
    classificator::GenerateAndWrite(path);
  }

  if (FLAGS_generate_grid)
  {
    grid::GenerateGridToStdout(FLAGS_bucketing_level);
  }

  // Generating intermediate files
  if (FLAGS_preprocess_xml)
  {
    LOG(LINFO, ("Generating intermediate data ...."));
    if (!data::GenerateToFile(FLAGS_intermediate_data_path, FLAGS_use_light_nodes))
      return -1;
  }

  feature::GenerateInfo genInfo;
  genInfo.dir = FLAGS_intermediate_data_path;

  // Generate dat file
  if (FLAGS_generate_features)
  {
    LOG(LINFO, ("Generating final data ..."));

    classificator::Read(path + "drawing_rules.bin",
                        path + "classificator.txt",
                        path + "visibility.txt");
    classificator::PrepareForFeatureGeneration();

    if (FLAGS_output.empty())
      genInfo.datFilePrefix = path;
    else
      genInfo.datFilePrefix = path + FLAGS_output + (FLAGS_bucketing_level > 0 ? "-" : "");
    genInfo.datFileSuffix = DATA_FILE_EXTENSION;
    genInfo.cellBucketingLevel = FLAGS_bucketing_level;
    genInfo.m_maxScaleForWorldFeatures = FLAGS_worldmap_max_zoom;

    if (!feature::GenerateFeatures(genInfo, FLAGS_use_light_nodes))
    {
      return -1;
    }

    for (size_t i = 0; i < genInfo.bucketNames.size(); ++i)
      genInfo.bucketNames[i] = genInfo.datFilePrefix + genInfo.bucketNames[i] + genInfo.datFileSuffix;
    if (FLAGS_worldmap_max_zoom >= 0)
      genInfo.bucketNames.push_back(genInfo.datFilePrefix + WORLD_FILE_NAME + genInfo.datFileSuffix);
  }
  else
  {
    genInfo.bucketNames.push_back(path + FLAGS_output + DATA_FILE_EXTENSION);
  }

  // Enumerate over all dat files that were created.
  for (size_t i = 0; i < genInfo.bucketNames.size(); ++i)
  {
    string const & datFile = genInfo.bucketNames[i];

    if (FLAGS_generate_geometry)
    {
      LOG(LINFO, ("Generating result features for ", datFile));
      if (!feature::GenerateFinalFeatures(datFile, FLAGS_sort_features))
      {
        // If error - move to next bucket without index generation
        continue;
      }
    }

    if (FLAGS_generate_index)
    {
      LOG(LINFO, ("Generating index for ", datFile));
      if (!indexer::BuildIndexFromDatFile(datFile, FLAGS_intermediate_data_path + FLAGS_output))
      {
        LOG(LCRITICAL, ("Error generating index."));
      }
    }
  }

  // Create http update list for countries and corresponding files
  if (FLAGS_generate_update)
  {
    LOG(LINFO, ("Creating maps.update file..."));
    update::GenerateFilesList(path);
  }

  return 0;
}
<commit_msg>[indexer_tool] Fixed classificator loading<commit_after>#include "data_generator.hpp"
#include "feature_generator.hpp"
#include "feature_sorter.hpp"
#include "update_generator.hpp"
#include "feature_bucketer.hpp"
#include "grid_generator.hpp"

#include "../classif_routine.hpp"
#include "../features_vector.hpp"
#include "../index_builder.hpp"
#include "../osm_decl.hpp"
#include "../data_header.hpp"

#include "../../storage/defines.hpp"

#include "../../platform/platform.hpp"

#include "../../3party/gflags/src/gflags/gflags.h"

#include "../../std/ctime.hpp"
#include "../../std/iostream.hpp"
#include "../../std/iomanip.hpp"
#include "../../std/numeric.hpp"

#include "../../version/version.hpp"

#include "../../base/start_mem_debug.hpp"

DEFINE_bool(version, false, "Display version");
DEFINE_bool(generate_update, false,
              "If specified, update.maps file will be generated from cells in the data path");

DEFINE_bool(sort_features, true, "Sort features data for better cache-friendliness.");
DEFINE_bool(generate_classif, false, "Generate classificator.");
DEFINE_bool(preprocess_xml, false, "1st pass - create nodes/ways/relations data");
DEFINE_bool(generate_features, false, "2nd pass - generate intermediate features");
DEFINE_bool(generate_geometry, false, "3rd pass - split and simplify geometry and triangles for features");
DEFINE_bool(generate_index, false, "4rd pass - generate index");
DEFINE_bool(generate_grid, false, "Generate grid for given bucketing_level");
DEFINE_bool(use_light_nodes, false,
            "If true, use temporary vector of nodes, instead of huge temp file");
DEFINE_string(data_path, "", "Working directory, 'path_to_exe/../../data' if empty.");
DEFINE_string(output, "", "Prefix of filenames of outputted .dat and .idx files.");
DEFINE_string(intermediate_data_path, "", "Path to store nodes, ways, relations.");
DEFINE_int32(bucketing_level, 7, "Level of cell ids for bucketing.");
DEFINE_int32(worldmap_max_zoom, -1, "If specified, features for zoomlevels [0..this_value] "
             " which are enabled in classificator will be added to the separate world.map");

string AddSlashIfNeeded(string const & str)
{
  string result(str);
  size_t const size = result.size();
  if (size)
  {
    if (result.find_last_of('\\') == size - 1)
      result[size - 1] = '/';
    else
      if (result.find_last_of('/') != size - 1)
        result.push_back('/');
  }
  return result;
}

int main(int argc, char ** argv)
{
  google::SetUsageMessage(
      "Takes OSM XML data from stdin and creates data and index files in several passes.");

  google::ParseCommandLineFlags(&argc, &argv, true);

  string const path =
      FLAGS_data_path.empty() ? GetPlatform().WritableDir() : AddSlashIfNeeded(FLAGS_data_path);

  if (FLAGS_version)
  {
    cout << "Tool version: " << VERSION_STRING << endl;
    cout << "Built on: " << VERSION_DATE_STRING << endl;
  }

  // Make a classificator
  if (FLAGS_generate_classif)
  {
    classificator::GenerateAndWrite(path);
  }

  if (FLAGS_generate_grid)
  {
    grid::GenerateGridToStdout(FLAGS_bucketing_level);
  }

  // Generating intermediate files
  if (FLAGS_preprocess_xml)
  {
    LOG(LINFO, ("Generating intermediate data ...."));
    if (!data::GenerateToFile(FLAGS_intermediate_data_path, FLAGS_use_light_nodes))
      return -1;
  }

  feature::GenerateInfo genInfo;
  genInfo.dir = FLAGS_intermediate_data_path;

  // load classificator only if necessary
  if (FLAGS_generate_features || FLAGS_generate_geometry || FLAGS_generate_index)
  {
    classificator::Read(path + "drawing_rules.bin",
                        path + "classificator.txt",
                        path + "visibility.txt");
    classificator::PrepareForFeatureGeneration();
  }

  // Generate dat file
  if (FLAGS_generate_features)
  {
    LOG(LINFO, ("Generating final data ..."));

    if (FLAGS_output.empty())
      genInfo.datFilePrefix = path;
    else
      genInfo.datFilePrefix = path + FLAGS_output + (FLAGS_bucketing_level > 0 ? "-" : "");
    genInfo.datFileSuffix = DATA_FILE_EXTENSION;
    genInfo.cellBucketingLevel = FLAGS_bucketing_level;
    genInfo.m_maxScaleForWorldFeatures = FLAGS_worldmap_max_zoom;

    if (!feature::GenerateFeatures(genInfo, FLAGS_use_light_nodes))
    {
      return -1;
    }

    for (size_t i = 0; i < genInfo.bucketNames.size(); ++i)
      genInfo.bucketNames[i] = genInfo.datFilePrefix + genInfo.bucketNames[i] + genInfo.datFileSuffix;
    if (FLAGS_worldmap_max_zoom >= 0)
      genInfo.bucketNames.push_back(genInfo.datFilePrefix + WORLD_FILE_NAME + genInfo.datFileSuffix);
  }
  else
  {
    genInfo.bucketNames.push_back(path + FLAGS_output + DATA_FILE_EXTENSION);
  }

  // Enumerate over all dat files that were created.
  for (size_t i = 0; i < genInfo.bucketNames.size(); ++i)
  {
    string const & datFile = genInfo.bucketNames[i];

    if (FLAGS_generate_geometry)
    {
      LOG(LINFO, ("Generating result features for ", datFile));
      if (!feature::GenerateFinalFeatures(datFile, FLAGS_sort_features))
      {
        // If error - move to next bucket without index generation
        continue;
      }
    }

    if (FLAGS_generate_index)
    {
      LOG(LINFO, ("Generating index for ", datFile));
      if (!indexer::BuildIndexFromDatFile(datFile, FLAGS_intermediate_data_path + FLAGS_output))
      {
        LOG(LCRITICAL, ("Error generating index."));
      }
    }
  }

  // Create http update list for countries and corresponding files
  if (FLAGS_generate_update)
  {
    LOG(LINFO, ("Creating maps.update file..."));
    update::GenerateFilesList(path);
  }

  return 0;
}
<|endoftext|>


<commit_before>#include <sstream>

#include "game.h"
#include "../gfx/game_window.h"
#include "../parser_yaml/parser_yaml.h"

Game::Game() :
	exit_p(false), restart_p(false)
{
	std::stringstream message;
	message << "Creating Game " << this;
	Logger::getInstance()->writeInformation(message.str());
}

Game::~Game(){
	std::stringstream message;
	message << "Killing Game " << this;
	Logger::getInstance()->writeInformation(message.str());
	clear();
}

void Game::clear() {
	board = nullptr;
	gameWindow = nullptr;
	restart_p = false;
	exit_p = false;
}

bool Game::setBoard(std::shared_ptr<Board> newBoard) {
	board = newBoard;
}

std::shared_ptr<Board> Game::getBoard() {
	return board;
}

std::shared_ptr<Player> Game::getAvailablePlayer() {
	// TODO: Trazar jugadores disponibles
	auto players = board->getPlayers();
	for(auto& p : players) {
		if(p->human) {
			return p;
		}
	}
	return nullptr;
}

bool Game::addClient(std::shared_ptr<GameWindow> newClient) {
	gameWindow = newClient;
}

void Game::start() {
	while (!willExit()) {
		GameTimer::update();
		board->update(); // Model
		gameWindow->update(); // Controller

		Logger::getInstance()->flush();
		if (!GameTimer::wait(GameTimer::getCurrent() + board->dt)) {
			Logger::getInstance()->writeInformation("Estamos laggeando!");
		}
	}
}

void Game::restart() {
	restart_p = true;
}

bool Game::willRestart() {
	return restart_p;
}

void Game::exit() {
	exit_p = true;
}

bool Game::willExit() {
	return exit_p or restart_p;
}

<commit_msg>control reaches end of non-void function<commit_after>#include <sstream>

#include "game.h"
#include "../gfx/game_window.h"
#include "../parser_yaml/parser_yaml.h"

Game::Game() :
	exit_p(false), restart_p(false)
{
	std::stringstream message;
	message << "Creating Game " << this;
	Logger::getInstance()->writeInformation(message.str());
}

Game::~Game(){
	std::stringstream message;
	message << "Killing Game " << this;
	Logger::getInstance()->writeInformation(message.str());
	clear();
}

void Game::clear() {
	board = nullptr;
	gameWindow = nullptr;
	restart_p = false;
	exit_p = false;
}

bool Game::setBoard(std::shared_ptr<Board> newBoard) {
	if (board) {
		return false;
	}
	board = newBoard;
	return true;
}

std::shared_ptr<Board> Game::getBoard() {
	return board;
}

std::shared_ptr<Player> Game::getAvailablePlayer() {
	// TODO: Trazar jugadores disponibles
	auto players = board->getPlayers();
	for(auto& p : players) {
		if(p->human) {
			return p;
		}
	}
	return nullptr;
}

bool Game::addClient(std::shared_ptr<GameWindow> newClient) {
	gameWindow = newClient;
	return true;
}

void Game::start() {
	while (!willExit()) {
		GameTimer::update();
		board->update(); // Model
		gameWindow->update(); // Controller

		Logger::getInstance()->flush();
		if (!GameTimer::wait(GameTimer::getCurrent() + board->dt)) {
			Logger::getInstance()->writeInformation("Estamos laggeando!");
		}
	}
}

void Game::restart() {
	restart_p = true;
}

bool Game::willRestart() {
	return restart_p;
}

void Game::exit() {
	exit_p = true;
}

bool Game::willExit() {
	return exit_p or restart_p;
}

<|endoftext|>


<commit_before>#include <iostream>
#include <functional>
#include <atomic>
#include <future>
#include "dronecore.h"
#include "system.h"
#include "integration_test_helper.h"
#include "camera_test_helpers.h"

using namespace dronecore;
using namespace std::placeholders; // for `_1`

TEST(CameraTest, ShowSettingsAndOptions)
{
    DroneCore dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    System &system = dc.system();
    ASSERT_TRUE(system.has_camera());
    auto camera = std::make_shared<Camera>(system);

    // Wait for download to happen.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // Set to photo mode
    set_mode(camera, Camera::Mode::PHOTO);

    std::vector<std::string> settings;
    EXPECT_TRUE(camera->get_possible_settings(settings));

    LogDebug() << "Possible settings in photo mode: ";
    for (auto setting : settings) {
        LogDebug() << "-" << setting;
    }
    EXPECT_EQ(settings.size(), 9);

    set_mode(camera, Camera::Mode::VIDEO);

    std::this_thread::sleep_for(std::chrono::seconds(2));

    EXPECT_TRUE(camera->get_possible_settings(settings));

    LogDebug() << "Possible settings in video mode: ";
    for (auto setting : settings) {
        LogDebug() << "-" << setting;
    }
    EXPECT_EQ(settings.size(), 6);

    std::vector<std::string> options;

    // Try something that is specific to the camera mode.
    EXPECT_TRUE(camera->get_possible_options("CAM_VIDRES", options));
    EXPECT_EQ(options.size(), 32);

    // This param is not applicable, so we should get an empty vector back.
    EXPECT_FALSE(camera->get_possible_options("CAM_PHOTOQUAL", options));
    EXPECT_EQ(options.size(), 0);

    // The same should happen with a param that does not exist at all.
    EXPECT_FALSE(camera->get_possible_options("CAM_BLABLA", options));
    EXPECT_EQ(options.size(), 0);

    set_mode(camera, Camera::Mode::PHOTO);

    // Try something that is specific to the camera mode.
    EXPECT_TRUE(camera->get_possible_options("CAM_PHOTOQUAL", options));
    EXPECT_EQ(options.size(), 4);

    // This param is not applicable, so we should get an empty vector back.
    EXPECT_FALSE(camera->get_possible_options("CAM_VIDRES", options));
    EXPECT_EQ(options.size(), 0);

    // The same should happen with a param that does not exist at all.
    EXPECT_FALSE(camera->get_possible_options("CAM_BLABLA", options));
    EXPECT_EQ(options.size(), 0);
}

TEST(CameraTest, SetSettings)
{
    DroneCore dc;

    ConnectionResult connection_ret = dc.add_udp_connection();
    ASSERT_EQ(connection_ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    System &system = dc.system();
    ASSERT_TRUE(system.has_camera());
    auto camera = std::make_shared<Camera>(system);

    // We need to wait for the camera definition to be ready
    // because we don't have a check yet.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    set_mode(camera, Camera::Mode::PHOTO);

    // Try setting garbage first
    EXPECT_EQ(set_setting(camera, "DOES_NOT", "EXIST"), Camera::Result::ERROR);

    std::string value_set;
    EXPECT_EQ(set_setting(camera, "CAM_PHOTOQUAL", "1"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_PHOTOQUAL", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("1", value_set.c_str());

    EXPECT_EQ(set_setting(camera, "CAM_COLORMODE", "5"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_COLORMODE", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("5", value_set.c_str());

    // Let's check the manual exposure mode first.
    std::this_thread::sleep_for(std::chrono::seconds(2));
    EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "1"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("1", value_set.c_str());

    // We should now be able to set shutter speed and ISO.
    std::vector<std::string> options;
    // Try something that is specific to the camera mode.
    EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
    EXPECT_EQ(options.size(), 19);
    EXPECT_TRUE(camera->get_possible_options("CAM_ISO", options));
    EXPECT_EQ(options.size(), 10);

    // But not EV and metering
    EXPECT_FALSE(camera->get_possible_options("CAM_EV", options));
    EXPECT_EQ(options.size(), 0);
    EXPECT_FALSE(camera->get_possible_options("CAM_METERING", options));
    EXPECT_EQ(options.size(), 0);

    // Now we'll try the same for Auto exposure mode
    EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "0"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("0", value_set.c_str());

    // Shutter speed and ISO don't have options in Auto mode.
    EXPECT_FALSE(camera->get_possible_options("CAM_SHUTTERSPD", options));
    EXPECT_EQ(options.size(), 0);
    EXPECT_FALSE(camera->get_possible_options("CAM_ISO", options));
    EXPECT_EQ(options.size(), 0);

    // But not EV and metering
    EXPECT_TRUE(camera->get_possible_options("CAM_EV", options));
    EXPECT_EQ(options.size(), 13);
    EXPECT_TRUE(camera->get_possible_options("CAM_METERING", options));
    EXPECT_EQ(options.size(), 3);

    set_mode(camera, Camera::Mode::VIDEO);

    // This should fail in video mode.
    EXPECT_EQ(set_setting(camera, "CAM_PHOTOQUAL", "1"), Camera::Result::ERROR);

    // Let's check the manual exposure mode first.

    EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "1"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("1", value_set.c_str());

    // FIXME: otherwise the camera is too slow
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // But a video setting should work
    EXPECT_EQ(set_setting(camera, "CAM_VIDRES", "30"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_VIDRES", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("30", value_set.c_str());

    // Shutter speed and ISO don't have options in Auto mode.
    EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
    EXPECT_EQ(options.size(), 15);

    // FIXME: otherwise the camera is too slow
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // But a video setting should work
    EXPECT_EQ(set_setting(camera, "CAM_VIDRES", "0"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_VIDRES", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("0", value_set.c_str());

    // Shutter speed and ISO don't have options in Auto mode.
    EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
    EXPECT_EQ(options.size(), 12);

    // Back to auto exposure mode
    std::this_thread::sleep_for(std::chrono::seconds(2));
    EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "0"), Camera::Result::SUCCESS);
    EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
    EXPECT_STREQ("0", value_set.c_str());

    // Check human readable strings too.
    std::string description;
    EXPECT_FALSE(camera->get_setting_str("BLABLIBLU", description));
    EXPECT_STREQ(description.c_str(), "");

    EXPECT_TRUE(camera->get_setting_str("CAM_EXPMODE", description));
    EXPECT_STREQ(description.c_str(), "Exposure Mode");

    EXPECT_TRUE(camera->get_setting_str("CAM_SHUTTERSPD", description));
    EXPECT_STREQ(description.c_str(), "Shutter Speed");
}
<commit_msg>camera: don't do E90 specific tests by default<commit_after>#include <iostream>
#include <functional>
#include <atomic>
#include <future>
#include "dronecore.h"
#include "system.h"
#include "integration_test_helper.h"
#include "camera_test_helpers.h"

using namespace dronecore;
using namespace std::placeholders; // for `_1`

// To run specific tests for Yuneec cameras.
const static bool is_e90 = false;

TEST(CameraTest, ShowSettingsAndOptions)
{
    DroneCore dc;

    ConnectionResult ret = dc.add_udp_connection();
    ASSERT_EQ(ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    System &system = dc.system();
    ASSERT_TRUE(system.has_camera());
    auto camera = std::make_shared<Camera>(system);

    // Wait for download to happen.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // Set to photo mode
    set_mode(camera, Camera::Mode::PHOTO);

    std::vector<std::string> settings;
    EXPECT_TRUE(camera->get_possible_settings(settings));

    LogDebug() << "Possible settings in photo mode: ";
    for (auto setting : settings) {
        LogDebug() << "-" << setting;
    }

    if (is_e90) {
        EXPECT_EQ(settings.size(), 9);
    }

    set_mode(camera, Camera::Mode::VIDEO);

    std::this_thread::sleep_for(std::chrono::seconds(2));

    EXPECT_TRUE(camera->get_possible_settings(settings));

    LogDebug() << "Possible settings in video mode: ";
    for (auto setting : settings) {
        LogDebug() << "-" << setting;
    }

    if (is_e90) {
        EXPECT_EQ(settings.size(), 6);
    }

    if (is_e90) {
        std::vector<std::string> options;

        // Try something that is specific to the camera mode.
        EXPECT_TRUE(camera->get_possible_options("CAM_VIDRES", options));
        EXPECT_EQ(options.size(), 32);

        // This param is not applicable, so we should get an empty vector back.
        EXPECT_FALSE(camera->get_possible_options("CAM_PHOTOQUAL", options));
        EXPECT_EQ(options.size(), 0);

        // The same should happen with a param that does not exist at all.
        EXPECT_FALSE(camera->get_possible_options("CAM_BLABLA", options));
        EXPECT_EQ(options.size(), 0);

        set_mode(camera, Camera::Mode::PHOTO);

        // Try something that is specific to the camera mode.
        EXPECT_TRUE(camera->get_possible_options("CAM_PHOTOQUAL", options));
        EXPECT_EQ(options.size(), 4);

        // This param is not applicable, so we should get an empty vector back.
        EXPECT_FALSE(camera->get_possible_options("CAM_VIDRES", options));
        EXPECT_EQ(options.size(), 0);

        // The same should happen with a param that does not exist at all.
        EXPECT_FALSE(camera->get_possible_options("CAM_BLABLA", options));
        EXPECT_EQ(options.size(), 0);
    }
}

TEST(CameraTest, SetSettings)
{
    DroneCore dc;

    ConnectionResult connection_ret = dc.add_udp_connection();
    ASSERT_EQ(connection_ret, ConnectionResult::SUCCESS);

    // Wait for system to connect via heartbeat.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    System &system = dc.system();
    ASSERT_TRUE(system.has_camera());
    auto camera = std::make_shared<Camera>(system);

    // We need to wait for the camera definition to be ready
    // because we don't have a check yet.
    std::this_thread::sleep_for(std::chrono::seconds(2));

    set_mode(camera, Camera::Mode::PHOTO);

    // Try setting garbage first
    EXPECT_EQ(set_setting(camera, "DOES_NOT", "EXIST"), Camera::Result::ERROR);

    if (is_e90) {
        std::string value_set;
        EXPECT_EQ(set_setting(camera, "CAM_PHOTOQUAL", "1"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_PHOTOQUAL", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("1", value_set.c_str());

        EXPECT_EQ(set_setting(camera, "CAM_COLORMODE", "5"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_COLORMODE", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("5", value_set.c_str());

        // Let's check the manual exposure mode first.
        std::this_thread::sleep_for(std::chrono::seconds(2));
        EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "1"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("1", value_set.c_str());

        // We should now be able to set shutter speed and ISO.
        std::vector<std::string> options;
        // Try something that is specific to the camera mode.
        EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
        EXPECT_EQ(options.size(), 19);
        EXPECT_TRUE(camera->get_possible_options("CAM_ISO", options));
        EXPECT_EQ(options.size(), 10);

        // But not EV and metering
        EXPECT_FALSE(camera->get_possible_options("CAM_EV", options));
        EXPECT_EQ(options.size(), 0);
        EXPECT_FALSE(camera->get_possible_options("CAM_METERING", options));
        EXPECT_EQ(options.size(), 0);

        // Now we'll try the same for Auto exposure mode
        EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "0"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("0", value_set.c_str());

        // Shutter speed and ISO don't have options in Auto mode.
        EXPECT_FALSE(camera->get_possible_options("CAM_SHUTTERSPD", options));
        EXPECT_EQ(options.size(), 0);
        EXPECT_FALSE(camera->get_possible_options("CAM_ISO", options));
        EXPECT_EQ(options.size(), 0);

        // But not EV and metering
        EXPECT_TRUE(camera->get_possible_options("CAM_EV", options));
        EXPECT_EQ(options.size(), 13);
        EXPECT_TRUE(camera->get_possible_options("CAM_METERING", options));
        EXPECT_EQ(options.size(), 3);

        set_mode(camera, Camera::Mode::VIDEO);

        // This should fail in video mode.
        EXPECT_EQ(set_setting(camera, "CAM_PHOTOQUAL", "1"), Camera::Result::ERROR);

        // Let's check the manual exposure mode first.

        EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "1"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("1", value_set.c_str());

        // FIXME: otherwise the camera is too slow
        std::this_thread::sleep_for(std::chrono::seconds(1));

        // But a video setting should work
        EXPECT_EQ(set_setting(camera, "CAM_VIDRES", "30"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_VIDRES", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("30", value_set.c_str());

        // Shutter speed and ISO don't have options in Auto mode.
        EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
        EXPECT_EQ(options.size(), 15);

        // FIXME: otherwise the camera is too slow
        std::this_thread::sleep_for(std::chrono::seconds(1));

        // But a video setting should work
        EXPECT_EQ(set_setting(camera, "CAM_VIDRES", "0"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_VIDRES", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("0", value_set.c_str());

        // Shutter speed and ISO don't have options in Auto mode.
        EXPECT_TRUE(camera->get_possible_options("CAM_SHUTTERSPD", options));
        EXPECT_EQ(options.size(), 12);

        // Back to auto exposure mode
        std::this_thread::sleep_for(std::chrono::seconds(2));
        EXPECT_EQ(set_setting(camera, "CAM_EXPMODE", "0"), Camera::Result::SUCCESS);
        EXPECT_EQ(get_setting(camera, "CAM_EXPMODE", value_set), Camera::Result::SUCCESS);
        EXPECT_STREQ("0", value_set.c_str());

        // Check human readable strings too.
        std::string description;
        EXPECT_FALSE(camera->get_setting_str("BLABLIBLU", description));
        EXPECT_STREQ(description.c_str(), "");

        EXPECT_TRUE(camera->get_setting_str("CAM_EXPMODE", description));
        EXPECT_STREQ(description.c_str(), "Exposure Mode");

        EXPECT_TRUE(camera->get_setting_str("CAM_SHUTTERSPD", description));
        EXPECT_STREQ(description.c_str(), "Shutter Speed");
    }
}
<|endoftext|>