<commit_before>#ifndef VRTYPE_HPP
#define VRTYPE_HPP

#include <vector>
#include <string>
#include <exception>
#include <algorithm>
#include <functional>

#include <boost/algorithm/string.hpp>

namespace dicom
{

namespace data
{

namespace vrtype
{

bool rule_n(std::size_t multiplier, std::size_t current, std::size_t new_elements)
{
    return (current + new_elements) % multiplier == 0;
}

bool rule_less(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements <= size;
}

bool rule_more(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements >= size;
}

bool rule_equals(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements == size;
}

template <typename T>
class vrtype
{
    private:
        std::vector<T> value_sequence;
        std::string multiplicity;

        std::vector<std::function<bool(std::size_t, std::size_t)>> multiplicity_rules;

        /**
         * @brief validate_multiplicity verifies that the respective attribute's
         *        multiplicity definitions are satisfied.
         * @param num_new_elements number of new elements to be added
         * @pre multiplicity rule must be satisfied for the current number of
         *      elements.
         * @return
         */
        bool validate_multiplicity(std::size_t num_new_elements) const
        {
            return std::all_of(multiplicity_rules.begin()
                               , multiplicity_rules.end()
                               , [=](std::function<bool(std::size_t, std::size_t)> rule)
                { return rule(value_sequence.size(), num_new_elements); } );
        }

        /**
         * @brief populate_mult_rules parses the multiplicity definiton and adds
         *        all necessary rules.
         */
        void populate_mult_rules()
        {
            auto it = std::remove_if(multiplicity.begin(), multiplicity.end(), ::isspace);
            multiplicity.erase(it, multiplicity.end());

            std::vector<std::string> components;
            boost::split(components, multiplicity, boost::is_any_of("-"));

            if (components.size() > 1) {
                if (std::all_of(components[0].begin(), components[0].end(), ::isdigit)) {
                    std::size_t lower = std::stoull(components[0]);
                    if (std::all_of(components[1].begin(), components[1].end(), ::isdigit)) {
                        std::size_t upper = std::stoull(components[1]);
                        multiplicity_rules.push_back(std::bind1st(rule_more, lower));
                        multiplicity_rules.push_back(std::bind1st(rule_less, upper));
                    } else if (components[1].find('n') != std::string::npos) {
                        std::string multiplier {components[1].begin(), components[1].find_last_of('n')};
                        multiplicity_rules.push_back(std::bind1st(rule_n, std::stoul(multiplier)));
                        multiplicity_rules.push_back(std::bind1st(rule_more, lower));
                    }
                }
            } else {
                if (std::all_of(components[0].begin(), components[0].end(), ::isdigit)) {
                    std::size_t value = std::stoull(components[0]);
                    multiplicity_rules.push_back(std::bind1st(rule_equals, value));
                } else if (components[0].find('n') != std::string::npos) {
                    std::string multiplier {components[0].begin(), components[0].find_last_of('n')};
                    multiplicity_rules.push_back(std::bind1st(rule_n, std::stoul(multiplier)));
                    multiplicity_rules.push_back(std::bind1st(rule_more, 1));
                }
            }
        }

   protected:
        vrtype(std::string multiplicity):
          multiplicity {multiplicity}
      {
          populate_mult_rules();
      }

      vrtype(std::string multiplicity, std::initializer_list<T> values):
          vrtype(multiplicity)
      {
          add(values);
      }

    public:
      virtual ~vrtype() = 0;

      /**
       * @brief is_sequence checks if the attribute can hold more than one value.
       * @return
       */
      bool is_sequence() const
      {
          return !multiplicity == "0" &&
                 !multiplicity == "1";
      }

      /**
       * @brief add adds all specified values to the attribute, if doing so
       *        does not break multiplicity conditions.
       * @param values list of values to be added.
       */
      void add(std::initializer_list<T> values)
      {
          if (!is_sequence()) {
              throw new std::runtime_error("can't add to a non-sequence!");
          }
          if (!validate_multiplicity(values.size())) {
              throw new std::runtime_error("addition of " + values.size() +  " elements would violate the multiplicity rule: " + multiplicity);
          }
          std::copy(values.begin(), values.end(), std::back_inserter(value_sequence));
          value_sequence.push_back(element);
      }
};

template <typename T>
vrtype<T>::~vrtype()
{
}

}

}

}

#endif // VRTYPE_HPP
<commit_msg>added abstract (de)serialization methods<commit_after>#ifndef VRTYPE_HPP
#define VRTYPE_HPP

#include <vector>
#include <string>
#include <exception>
#include <algorithm>
#include <functional>

#include <boost/algorithm/string.hpp>

namespace dicom
{

namespace data
{

namespace vrtype
{

bool rule_n(std::size_t multiplier, std::size_t current, std::size_t new_elements)
{
    return (current + new_elements) % multiplier == 0;
}

bool rule_less(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements <= size;
}

bool rule_more(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements >= size;
}

bool rule_equals(std::size_t size, std::size_t current, std::size_t new_elements)
{
    return current + new_elements == size;
}

/**
 * The vrtype class represents the value field of an attribute. It holds the
 * value(s) of type T and asserts the consistency of the appropriate value
 * multiplicity with the value count.
 * @tparam T type of the underlying data
 */
template <typename T>
class vrtype
{
    private:
        std::vector<T> value_sequence;
        const std::string multiplicity;

        std::vector<std::function<bool(std::size_t, std::size_t)>> multiplicity_rules;

        /**
         * @brief validate_multiplicity verifies that the respective attribute's
         *        multiplicity definitions are satisfied.
         * @param num_new_elements number of new elements to be added
         * @pre multiplicity rule must be satisfied for the current number of
         *      elements.
         * @return
         */
        bool validate_multiplicity(std::size_t num_new_elements) const
        {
            return std::all_of(multiplicity_rules.begin()
                               , multiplicity_rules.end()
                               , [=](std::function<bool(std::size_t, std::size_t)> rule)
                { return rule(value_sequence.size(), num_new_elements); } );
        }

        /**
         * @brief populate_mult_rules parses the multiplicity definiton and adds
         *        all necessary rules.
         */
        void populate_mult_rules()
        {
            auto it = std::remove_if(multiplicity.begin(), multiplicity.end(), ::isspace);
            multiplicity.erase(it, multiplicity.end());

            std::vector<std::string> components;
            boost::split(components, multiplicity, boost::is_any_of("-"));

            if (components.size() > 1) {
                if (std::all_of(components[0].begin(), components[0].end(), ::isdigit)) {
                    std::size_t lower = std::stoull(components[0]);
                    if (std::all_of(components[1].begin(), components[1].end(), ::isdigit)) {
                        std::size_t upper = std::stoull(components[1]);
                        multiplicity_rules.push_back(std::bind1st(rule_more, lower));
                        multiplicity_rules.push_back(std::bind1st(rule_less, upper));
                    } else if (components[1].find('n') != std::string::npos) {
                        std::string multiplier {components[1].begin(), components[1].find_last_of('n')};
                        multiplicity_rules.push_back(std::bind1st(rule_n, std::stoul(multiplier)));
                        multiplicity_rules.push_back(std::bind1st(rule_more, lower));
                    }
                }
            } else {
                if (std::all_of(components[0].begin(), components[0].end(), ::isdigit)) {
                    std::size_t value = std::stoull(components[0]);
                    multiplicity_rules.push_back(std::bind1st(rule_equals, value));
                } else if (components[0].find('n') != std::string::npos) {
                    std::string multiplier {components[0].begin(), components[0].find_last_of('n')};
                    multiplicity_rules.push_back(std::bind1st(rule_n, std::stoul(multiplier)));
                    multiplicity_rules.push_back(std::bind1st(rule_more, 1));
                }
            }
        }

   protected:
        vrtype(std::string multiplicity):
          multiplicity {multiplicity}
      {
          populate_mult_rules();
      }

      vrtype(std::string multiplicity, std::initializer_list<T> values):
          vrtype(multiplicity)
      {
          add(values);
      }

      /**
       * @brief serialize serializes the current instance into a byte stream
       * @return serialized data
       */
      virtual std::vector<unsigned char> serialize() = 0;

      /**
       * @brief deserialize deserializes the raw byte data into a structured
       *        representation.
       * @param data serialized data
       * @return reference to the instance which contains the data
       */
      virtual vrtype<T>& deserialize(std::vector<unsigned char> data) = 0;

    public:
      virtual ~vrtype() = 0;

      /**
       * @brief is_sequence checks if the attribute can hold more than one value.
       * @return
       */
      bool is_sequence() const
      {
          return !multiplicity == "0" &&
                 !multiplicity == "1";
      }

      /**
       * @brief add adds all specified values to the attribute, if doing so
       *        does not break multiplicity conditions.
       * @param values list of values to be added.
       */
      void add(std::initializer_list<T> values)
      {
          if (!is_sequence()) {
              throw new std::runtime_error("can't add to a non-sequence!");
          }
          if (!validate_multiplicity(values.size())) {
              throw new std::runtime_error("addition of " + values.size() +  " elements would violate the multiplicity rule: " + multiplicity);
          }
          std::copy(values.begin(), values.end(), std::back_inserter(value_sequence));
          value_sequence.push_back(element);
      }
};

template <typename T>
vrtype<T>::~vrtype()
{
}

}

}

}

#endif // VRTYPE_HPP
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// ValueSymbol.cpp
// Base class for all value symbols
//
// File is under the MIT license; see LICENSE for details
//------------------------------------------------------------------------------
#include "slang/symbols/ValueSymbol.h"

#include "slang/binding/AssignmentExpressions.h"
#include "slang/binding/Expression.h"
#include "slang/binding/MiscExpressions.h"
#include "slang/binding/SelectExpressions.h"
#include "slang/compilation/Compilation.h"
#include "slang/diagnostics/ExpressionsDiags.h"
#include "slang/symbols/Scope.h"
#include "slang/symbols/VariableSymbols.h"
#include "slang/syntax/AllSyntax.h"
#include "slang/types/NetType.h"

namespace slang {

ValueSymbol::ValueSymbol(SymbolKind kind, string_view name, SourceLocation location,
                         bitmask<DeclaredTypeFlags> flags) :
    Symbol(kind, name, location),
    declaredType(*this, flags) {
}

void ValueSymbol::setFromDeclarator(const DeclaratorSyntax& decl) {
    declaredType.setFromDeclarator(decl);
    setSyntax(decl);
}

bool ValueSymbol::isKind(SymbolKind kind) {
    switch (kind) {
        case SymbolKind::Net:
        case SymbolKind::EnumValue:
        case SymbolKind::Parameter:
        case SymbolKind::PrimitivePort:
        case SymbolKind::ModportPort:
        case SymbolKind::Specparam:
            return true;
        default:
            return VariableSymbol::isKind(kind);
    }
}

ValueSymbol::Driver::Driver(DriverKind kind, const Expression& longestStaticPrefix) :
    longestStaticPrefix(&longestStaticPrefix), kind(kind) {
}

static const Expression* nextPrefix(const Expression& expr) {
    switch (expr.kind) {
        case ExpressionKind::NamedValue:
        case ExpressionKind::HierarchicalValue:
            return nullptr;
        case ExpressionKind::ElementSelect:
            return &expr.as<ElementSelectExpression>().value();
        case ExpressionKind::RangeSelect:
            return &expr.as<RangeSelectExpression>().value();
        case ExpressionKind::MemberAccess:
            return &expr.as<MemberAccessExpression>().value();
        default:
            THROW_UNREACHABLE;
    }
}

static bool prefixOverlaps(EvalContext& ctx, const Expression& left, const Expression& right) {
    // A named value here should always point to the same symbol,
    // so we only need to check if they have the same expression kind.
    if (ValueExpressionBase::isKind(left.kind) && ValueExpressionBase::isKind(right.kind))
        return true;

    auto getRange = [&ctx](const Expression& expr) -> optional<ConstantRange> {
        ConstantValue unused;
        if (expr.kind == ExpressionKind::ElementSelect)
            return expr.as<ElementSelectExpression>().evalIndex(ctx, nullptr, unused);
        if (expr.kind == ExpressionKind::RangeSelect)
            return expr.as<RangeSelectExpression>().evalRange(ctx, nullptr);
        if (expr.kind == ExpressionKind::MemberAccess)
            return expr.as<MemberAccessExpression>().getSelectRange();
        return std::nullopt;
    };

    auto lrange = getRange(left);
    auto rrange = getRange(right);
    if (!lrange || !rrange)
        return false;

    return lrange->overlaps(*rrange);
}

bool ValueSymbol::Driver::overlaps(Compilation& compilation, const Driver& other) const {
    auto buildPath = [](const Expression* expr, SmallVector<const Expression*>& path) {
        do {
            if (expr->kind == ExpressionKind::Conversion) {
                expr = &expr->as<ConversionExpression>().operand();
            }
            else {
                path.append({ expr });
                expr = nextPrefix(*expr);
            }
        } while (expr);
    };

    SmallVectorSized<const Expression*, 4> ourPath;
    buildPath(longestStaticPrefix, ourPath);

    SmallVectorSized<const Expression*, 4> otherPath;
    buildPath(other.longestStaticPrefix, otherPath);

    EvalContext ctx(compilation);
    for (size_t i = ourPath.size(), j = otherPath.size(); i > 0 && j > 0; i--, j--) {
        auto ourElem = ourPath[i - 1];
        auto otherElem = otherPath[j - 1];
        if (!prefixOverlaps(ctx, *ourElem, *otherElem))
            return false;
    }

    return true;
}

void ValueSymbol::addDriver(DriverKind driverKind, const Expression& longestStaticPrefix) const {
    auto scope = getParentScope();
    ASSERT(scope);

    auto& comp = scope->getCompilation();
    auto driver = comp.emplace<Driver>(driverKind, longestStaticPrefix);
    if (!firstDriver) {
        auto makeRef = [&]() -> const Expression& {
            BindContext bindContext(*scope, LookupLocation::min);
            SourceRange range = { location, location + name.length() };
            return ValueExpressionBase::fromSymbol(bindContext, *this, /* isHierarchical */ false,
                                                   range);
        };

        // The first time we add a driver, check whether there is also an
        // initializer expression that should count as a driver as well.
        switch (kind) {
            case SymbolKind::Net:
                if (auto init = getInitializer())
                    firstDriver = comp.emplace<Driver>(DriverKind::Continuous, makeRef());
                break;
            case SymbolKind::Variable:
            case SymbolKind::ClassProperty:
            case SymbolKind::Field:
                if (as<VariableSymbol>().lifetime == VariableLifetime::Static) {
                    if (auto init = getInitializer())
                        firstDriver = comp.emplace<Driver>(DriverKind::Procedural, makeRef());
                }
                break;
            default:
                break;
        }

        if (!firstDriver) {
            firstDriver = driver;
            return;
        }
    }

    const bool checkOverlap =
        (VariableSymbol::isKind(kind) &&
         as<VariableSymbol>().lifetime == VariableLifetime::Static) ||
        (kind == SymbolKind::Net && as<NetSymbol>().netType.netKind == NetType::UWire);

    // Walk the list of drivers to the end and add this one there.
    // Along the way, check that the driver is valid given the ones that already exist.
    auto curr = firstDriver;
    while (true) {
        // Variables can't be driven by multiple continuous assignments or
        // a mix of continuous and procedural assignments.
        if (checkOverlap &&
            (driverKind == DriverKind::Continuous || curr->kind == DriverKind::Continuous) &&
            curr->overlaps(comp, *driver)) {

            auto currRange = curr->longestStaticPrefix->sourceRange;
            auto driverRange = driver->longestStaticPrefix->sourceRange;
            auto code =
                kind == SymbolKind::Net ? diag::MultipleUWireDrivers
                : (driverKind == DriverKind::Continuous && curr->kind == DriverKind::Continuous)
                    ? diag::MultipleContAssigns
                    : diag::MixedVarAssigns;

            auto& diag = scope->addDiag(code, driverRange);
            diag << name;
            diag.addNote(diag::NoteAssignedHere, currRange.start()) << currRange;
            return;
        }

        if (!curr->next) {
            curr->next = driver;
            return;
        }

        curr = curr->next;
    }
}

} // namespace slang
<commit_msg>Fix warning about unused variable<commit_after>//------------------------------------------------------------------------------
// ValueSymbol.cpp
// Base class for all value symbols
//
// File is under the MIT license; see LICENSE for details
//------------------------------------------------------------------------------
#include "slang/symbols/ValueSymbol.h"

#include "slang/binding/AssignmentExpressions.h"
#include "slang/binding/Expression.h"
#include "slang/binding/MiscExpressions.h"
#include "slang/binding/SelectExpressions.h"
#include "slang/compilation/Compilation.h"
#include "slang/diagnostics/ExpressionsDiags.h"
#include "slang/symbols/Scope.h"
#include "slang/symbols/VariableSymbols.h"
#include "slang/syntax/AllSyntax.h"
#include "slang/types/NetType.h"

namespace slang {

ValueSymbol::ValueSymbol(SymbolKind kind, string_view name, SourceLocation location,
                         bitmask<DeclaredTypeFlags> flags) :
    Symbol(kind, name, location),
    declaredType(*this, flags) {
}

void ValueSymbol::setFromDeclarator(const DeclaratorSyntax& decl) {
    declaredType.setFromDeclarator(decl);
    setSyntax(decl);
}

bool ValueSymbol::isKind(SymbolKind kind) {
    switch (kind) {
        case SymbolKind::Net:
        case SymbolKind::EnumValue:
        case SymbolKind::Parameter:
        case SymbolKind::PrimitivePort:
        case SymbolKind::ModportPort:
        case SymbolKind::Specparam:
            return true;
        default:
            return VariableSymbol::isKind(kind);
    }
}

ValueSymbol::Driver::Driver(DriverKind kind, const Expression& longestStaticPrefix) :
    longestStaticPrefix(&longestStaticPrefix), kind(kind) {
}

static const Expression* nextPrefix(const Expression& expr) {
    switch (expr.kind) {
        case ExpressionKind::NamedValue:
        case ExpressionKind::HierarchicalValue:
            return nullptr;
        case ExpressionKind::ElementSelect:
            return &expr.as<ElementSelectExpression>().value();
        case ExpressionKind::RangeSelect:
            return &expr.as<RangeSelectExpression>().value();
        case ExpressionKind::MemberAccess:
            return &expr.as<MemberAccessExpression>().value();
        default:
            THROW_UNREACHABLE;
    }
}

static bool prefixOverlaps(EvalContext& ctx, const Expression& left, const Expression& right) {
    // A named value here should always point to the same symbol,
    // so we only need to check if they have the same expression kind.
    if (ValueExpressionBase::isKind(left.kind) && ValueExpressionBase::isKind(right.kind))
        return true;

    auto getRange = [&ctx](const Expression& expr) -> optional<ConstantRange> {
        ConstantValue unused;
        if (expr.kind == ExpressionKind::ElementSelect)
            return expr.as<ElementSelectExpression>().evalIndex(ctx, nullptr, unused);
        if (expr.kind == ExpressionKind::RangeSelect)
            return expr.as<RangeSelectExpression>().evalRange(ctx, nullptr);
        if (expr.kind == ExpressionKind::MemberAccess)
            return expr.as<MemberAccessExpression>().getSelectRange();
        return std::nullopt;
    };

    auto lrange = getRange(left);
    auto rrange = getRange(right);
    if (!lrange || !rrange)
        return false;

    return lrange->overlaps(*rrange);
}

bool ValueSymbol::Driver::overlaps(Compilation& compilation, const Driver& other) const {
    auto buildPath = [](const Expression* expr, SmallVector<const Expression*>& path) {
        do {
            if (expr->kind == ExpressionKind::Conversion) {
                expr = &expr->as<ConversionExpression>().operand();
            }
            else {
                path.append({ expr });
                expr = nextPrefix(*expr);
            }
        } while (expr);
    };

    SmallVectorSized<const Expression*, 4> ourPath;
    buildPath(longestStaticPrefix, ourPath);

    SmallVectorSized<const Expression*, 4> otherPath;
    buildPath(other.longestStaticPrefix, otherPath);

    EvalContext ctx(compilation);
    for (size_t i = ourPath.size(), j = otherPath.size(); i > 0 && j > 0; i--, j--) {
        auto ourElem = ourPath[i - 1];
        auto otherElem = otherPath[j - 1];
        if (!prefixOverlaps(ctx, *ourElem, *otherElem))
            return false;
    }

    return true;
}

void ValueSymbol::addDriver(DriverKind driverKind, const Expression& longestStaticPrefix) const {
    auto scope = getParentScope();
    ASSERT(scope);

    auto& comp = scope->getCompilation();
    auto driver = comp.emplace<Driver>(driverKind, longestStaticPrefix);
    if (!firstDriver) {
        auto makeRef = [&]() -> const Expression& {
            BindContext bindContext(*scope, LookupLocation::min);
            SourceRange range = { location, location + name.length() };
            return ValueExpressionBase::fromSymbol(bindContext, *this, /* isHierarchical */ false,
                                                   range);
        };

        // The first time we add a driver, check whether there is also an
        // initializer expression that should count as a driver as well.
        switch (kind) {
            case SymbolKind::Net:
                if (getInitializer())
                    firstDriver = comp.emplace<Driver>(DriverKind::Continuous, makeRef());
                break;
            case SymbolKind::Variable:
            case SymbolKind::ClassProperty:
            case SymbolKind::Field:
                if (as<VariableSymbol>().lifetime == VariableLifetime::Static) {
                    if (getInitializer())
                        firstDriver = comp.emplace<Driver>(DriverKind::Procedural, makeRef());
                }
                break;
            default:
                break;
        }

        if (!firstDriver) {
            firstDriver = driver;
            return;
        }
    }

    const bool checkOverlap =
        (VariableSymbol::isKind(kind) &&
         as<VariableSymbol>().lifetime == VariableLifetime::Static) ||
        (kind == SymbolKind::Net && as<NetSymbol>().netType.netKind == NetType::UWire);

    // Walk the list of drivers to the end and add this one there.
    // Along the way, check that the driver is valid given the ones that already exist.
    auto curr = firstDriver;
    while (true) {
        // Variables can't be driven by multiple continuous assignments or
        // a mix of continuous and procedural assignments.
        if (checkOverlap &&
            (driverKind == DriverKind::Continuous || curr->kind == DriverKind::Continuous) &&
            curr->overlaps(comp, *driver)) {

            auto currRange = curr->longestStaticPrefix->sourceRange;
            auto driverRange = driver->longestStaticPrefix->sourceRange;
            auto code =
                kind == SymbolKind::Net ? diag::MultipleUWireDrivers
                : (driverKind == DriverKind::Continuous && curr->kind == DriverKind::Continuous)
                    ? diag::MultipleContAssigns
                    : diag::MixedVarAssigns;

            auto& diag = scope->addDiag(code, driverRange);
            diag << name;
            diag.addNote(diag::NoteAssignedHere, currRange.start()) << currRange;
            return;
        }

        if (!curr->next) {
            curr->next = driver;
            return;
        }

        curr = curr->next;
    }
}

} // namespace slang
<|endoftext|>


<commit_before>// MIT License
//
// Copyright (c) 2016-2017 Simon Ninon <simon.ninon@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <tacopie/error.hpp>
#include <tacopie/logger.hpp>
#include <tacopie/network/tcp_server.hpp>

#include <algorithm>

namespace tacopie {

//!
//! ctor & dtor
//!

tcp_server::tcp_server(void)
: m_io_service(get_default_io_service())
, m_on_new_connection_callback(nullptr) { __TACOPIE_LOG(debug, "create tcp_server"); }

tcp_server::~tcp_server(void) {
  __TACOPIE_LOG(debug, "destroy tcp_server");
  stop();
}

//!
//! start & stop the tcp server
//!

void
tcp_server::start(const std::string& host, std::uint32_t port, const on_new_connection_callback_t& callback) {
  if (is_running()) { __TACOPIE_THROW(warn, "tcp_server is already running"); }

  m_socket.bind(host, port);
  m_socket.listen(__TACOPIE_CONNECTION_QUEUE_SIZE);

  m_io_service->track(m_socket);
  m_io_service->set_rd_callback(m_socket, std::bind(&tcp_server::on_read_available, this, std::placeholders::_1));
  m_on_new_connection_callback = callback;

  m_is_running = true;

  __TACOPIE_LOG(info, "tcp_server running");
}

void
tcp_server::stop(bool wait_for_removal, bool recursive_wait_for_removal) {
  if (!is_running()) { return; }

  m_is_running = false;

  m_io_service->untrack(m_socket);
  if (wait_for_removal) { m_io_service->wait_for_removal(m_socket); }
  m_socket.close();

  std::lock_guard<std::mutex> lock(m_clients_mtx);
  for (auto& client : m_clients) {
    client->disconnect(recursive_wait_for_removal && wait_for_removal);
  }
  m_clients.clear();

  __TACOPIE_LOG(info, "tcp_server stopped");
}

//!
//! io service read callback
//!

void
tcp_server::on_read_available(fd_t) {
  try {
    __TACOPIE_LOG(info, "tcp_server received new connection");

    auto client = std::make_shared<tcp_client>(m_socket.accept());

    if (!m_on_new_connection_callback || m_on_new_connection_callback(client)) {
      __TACOPIE_LOG(info, "tcp_server accepted new connection");

      client->set_on_disconnection_handler(std::bind(&tcp_server::on_client_disconnected, this, client));
      m_clients.push_back(client);
    }
    else {
      __TACOPIE_LOG(info, "tcp_server dismissed new connection");
    }
  }
  catch (const tacopie::tacopie_error&) {
    __TACOPIE_LOG(warn, "accept operation failure");
    stop();
  }
}

//!
//! client disconnected
//!

void
tcp_server::on_client_disconnected(const std::shared_ptr<tcp_client>& client) {
  //! If we are not running the server
  //! Then it means that this function is called by tcp_client::disconnect() at the destruction of all clients
  if (!is_running()) { return; }

  __TACOPIE_LOG(debug, "handle server's client disconnection");

  std::lock_guard<std::mutex> lock(m_clients_mtx);
  auto it = std::find(m_clients.begin(), m_clients.end(), client);

  if (it != m_clients.end()) { m_clients.erase(it); }
}

//!
//! returns whether the server is currently running or not
//!

bool
tcp_server::is_running(void) const {
  return m_is_running;
}

//!
//! get socket
//!

tcp_socket&
tcp_server::get_socket(void) {
  return m_socket;
}

const tcp_socket&
tcp_server::get_socket(void) const {
  return m_socket;
}

//!
//! get client sockets
//!

const std::list<std::shared_ptr<tacopie::tcp_client>>&
tcp_server::get_clients(void) const {
  return m_clients;
}

//!
//! comparison operator
//!
bool
tcp_server::operator==(const tcp_server& rhs) const {
  return m_socket == rhs.m_socket;
}

bool
tcp_server::operator!=(const tcp_server& rhs) const {
  return !operator==(rhs);
}

} //! tacopie
<commit_msg>change behavior of on_new_connection_handler. Returning true means connection is handled by tcp_client wrapper and nothing will be done by tcp_server. Returning false means connection is handled by tcp_server, will be stored in an internal list and tcp_client disconection_handler overriden.<commit_after>// MIT License
//
// Copyright (c) 2016-2017 Simon Ninon <simon.ninon@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#include <tacopie/error.hpp>
#include <tacopie/logger.hpp>
#include <tacopie/network/tcp_server.hpp>

#include <algorithm>

namespace tacopie {

//!
//! ctor & dtor
//!

tcp_server::tcp_server(void)
: m_io_service(get_default_io_service())
, m_on_new_connection_callback(nullptr) { __TACOPIE_LOG(debug, "create tcp_server"); }

tcp_server::~tcp_server(void) {
  __TACOPIE_LOG(debug, "destroy tcp_server");
  stop();
}

//!
//! start & stop the tcp server
//!

void
tcp_server::start(const std::string& host, std::uint32_t port, const on_new_connection_callback_t& callback) {
  if (is_running()) { __TACOPIE_THROW(warn, "tcp_server is already running"); }

  m_socket.bind(host, port);
  m_socket.listen(__TACOPIE_CONNECTION_QUEUE_SIZE);

  m_io_service->track(m_socket);
  m_io_service->set_rd_callback(m_socket, std::bind(&tcp_server::on_read_available, this, std::placeholders::_1));
  m_on_new_connection_callback = callback;

  m_is_running = true;

  __TACOPIE_LOG(info, "tcp_server running");
}

void
tcp_server::stop(bool wait_for_removal, bool recursive_wait_for_removal) {
  if (!is_running()) { return; }

  m_is_running = false;

  m_io_service->untrack(m_socket);
  if (wait_for_removal) { m_io_service->wait_for_removal(m_socket); }
  m_socket.close();

  std::lock_guard<std::mutex> lock(m_clients_mtx);
  for (auto& client : m_clients) {
    client->disconnect(recursive_wait_for_removal && wait_for_removal);
  }
  m_clients.clear();

  __TACOPIE_LOG(info, "tcp_server stopped");
}

//!
//! io service read callback
//!

void
tcp_server::on_read_available(fd_t) {
  try {
    __TACOPIE_LOG(info, "tcp_server received new connection");

    auto client = std::make_shared<tcp_client>(m_socket.accept());

    if (!m_on_new_connection_callback || !m_on_new_connection_callback(client)) {
      __TACOPIE_LOG(info, "connection handling delegated to tcp_server");

      client->set_on_disconnection_handler(std::bind(&tcp_server::on_client_disconnected, this, client));
      m_clients.push_back(client);
    }
    else {
      __TACOPIE_LOG(info, "connection handled by tcp_server wrapper");
    }
  }
  catch (const tacopie::tacopie_error&) {
    __TACOPIE_LOG(warn, "accept operation failure");
    stop();
  }
}

//!
//! client disconnected
//!

void
tcp_server::on_client_disconnected(const std::shared_ptr<tcp_client>& client) {
  //! If we are not running the server
  //! Then it means that this function is called by tcp_client::disconnect() at the destruction of all clients
  if (!is_running()) { return; }

  __TACOPIE_LOG(debug, "handle server's client disconnection");

  std::lock_guard<std::mutex> lock(m_clients_mtx);
  auto it = std::find(m_clients.begin(), m_clients.end(), client);

  if (it != m_clients.end()) { m_clients.erase(it); }
}

//!
//! returns whether the server is currently running or not
//!

bool
tcp_server::is_running(void) const {
  return m_is_running;
}

//!
//! get socket
//!

tcp_socket&
tcp_server::get_socket(void) {
  return m_socket;
}

const tcp_socket&
tcp_server::get_socket(void) const {
  return m_socket;
}

//!
//! get client sockets
//!

const std::list<std::shared_ptr<tacopie::tcp_client>>&
tcp_server::get_clients(void) const {
  return m_clients;
}

//!
//! comparison operator
//!
bool
tcp_server::operator==(const tcp_server& rhs) const {
  return m_socket == rhs.m_socket;
}

bool
tcp_server::operator!=(const tcp_server& rhs) const {
  return !operator==(rhs);
}

} // namespace tacopie
<|endoftext|>


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

#include <cstring>

#include <numeric>
#include <vector>

#include <message.hpp>
#include <parser.hpp>

// Remarks:
// - Do not use BOOST_CHECK_EQUAL() to compare const char* variables as
//   Boost.Test attempts to be smart by calling std::strcmp()
//   [with Boost 1.53, see boost/test/impl/test_tools.ipp:383, equal_impl()].
//   In this file, const char* variables do not always reference null-terminated
//   strings, e.g., when using std::vector<char*>::data(). If this happens,
//   Valgrind may detect invalid reads.

namespace deepstream {
namespace parser
{

BOOST_AUTO_TEST_CASE(empty_string)
{
	deepstream_parser_state state("", 0);

	int token = state.handle_token(TOKEN_EOF, "\0", 1);
	BOOST_CHECK_EQUAL( token, TOKEN_EOF );

	BOOST_CHECK( state.tokenizing_header_ );
	BOOST_CHECK_EQUAL( state.offset_, 1 );
	BOOST_CHECK( state.messages_.empty() );
	BOOST_CHECK( state.errors_.empty() );
}

BOOST_AUTO_TEST_CASE(simple)
{
	const auto input = Message::from_human_readable("A|A+");
	const auto copy(input);
	const char* matches[] = { &input[0], &input[3], "" };
	std::size_t sizes[] = { 3, 1, 1 };

	const deepstream_token tokens[] = {
		TOKEN_A_A, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		int ret = state.handle_token(tokens[i], matches[i], sizes[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), 1 );
		BOOST_CHECK( state.errors_.empty() );

		std::size_t offset = std::accumulate( sizes, sizes+i+1, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );
	}

	Message& msg = state.messages_.front();

	BOOST_CHECK( msg.base_ == copy.data() );
	BOOST_CHECK_EQUAL( msg.offset_, 0 );
	BOOST_CHECK_EQUAL( msg.size_, input.size() );

	BOOST_CHECK_EQUAL( msg.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg.action(), Action::REQUEST );
	BOOST_CHECK( msg.is_ack() );

	BOOST_CHECK( msg.arguments_.empty() );
}

BOOST_AUTO_TEST_CASE(concatenated_messages)
{
	const char STRING[] = "E|L|listen+E|S|event+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token tokens[] = {
		TOKEN_E_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_E_S, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	const std::size_t matchlens[num_tokens] = { 3, 7, 1, 3, 6, 1, 1 };
	const char* matches[num_tokens] = {
		&input[ 0], &input[3], &input[10],
		&input[11], &input[14], &input[20],
		""
	};

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		std::size_t offset = std::accumulate( matchlens, matchlens+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(tokens[i], matches[i], matchlens[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=3) ? 2 : 1 );
		BOOST_CHECK( state.errors_.empty() );

		BOOST_CHECK_EQUAL( state.offset_, offset+matchlens[i] );
	}

	for(const Message& msg : state.messages_)
	{
		BOOST_CHECK( msg.base_ == copy.data() );

		BOOST_CHECK_EQUAL( msg.topic(), Topic::EVENT );
		BOOST_CHECK( !msg.is_ack() );

		BOOST_CHECK_EQUAL( msg.arguments_.size(), 1 );
	}

	BOOST_CHECK_EQUAL( state.messages_.size(), 2 );

	const Message& msg_f = state.messages_.front();
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 11 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::LISTEN );

	const Location& arg_f = msg_f.arguments_.front();

	BOOST_CHECK_EQUAL( arg_f.offset_, 4 );
	BOOST_CHECK_EQUAL( arg_f.length_, 6 );
	BOOST_CHECK( !strncmp(&input[arg_f.offset_], "listen", arg_f.length_) );

	const Message& msg_b = state.messages_.back();
	BOOST_CHECK_EQUAL( msg_b.offset(), 11 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::SUBSCRIBE );

	const Location& arg_b = msg_b.arguments_.front();

	BOOST_CHECK_EQUAL( arg_b.offset_, 15 );
	BOOST_CHECK_EQUAL( arg_b.length_, 5 );
	BOOST_CHECK( !strncmp(&input[arg_b.offset_], "event", arg_b.length_) );
}

BOOST_AUTO_TEST_CASE(invalid_number_of_arguments)
{
	const char STRING[] = "E|A|L|l+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token TOKENS[] = {
		TOKEN_E_A_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t NUM_TOKENS = sizeof(TOKENS) / sizeof(TOKENS[0]);

	const std::size_t MATCHLENS[NUM_TOKENS] = { 5, 2, 1, 1 };
	const char* MATCHES[NUM_TOKENS] = { &input[0], &input[5], &input[7], "" };

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < NUM_TOKENS; ++i)
	{
		std::size_t offset = std::accumulate( MATCHLENS, MATCHLENS+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(TOKENS[i], MATCHES[i], MATCHLENS[i]);

		BOOST_CHECK_EQUAL( ret, TOKENS[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=2) ? 0 : 1 );
		BOOST_CHECK_EQUAL( state.errors_.size(), (i>=2) ? 1 : 0 );

		BOOST_CHECK_EQUAL( state.offset_, offset+MATCHLENS[i] );
	}

	const Error& e = state.errors_.front();

	BOOST_CHECK_EQUAL( e.location_.offset_, 0 );
	BOOST_CHECK_EQUAL( e.location_.length_, 8 );
	BOOST_CHECK_EQUAL( e.tag_, Error::INVALID_NUMBER_OF_ARGUMENTS );
}

}
}
<commit_msg>Parser: more strict tests<commit_after>/*
 * Copyright 2016-2017 deepstreamHub GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>

#include <cstring>

#include <numeric>
#include <vector>

#include <message.hpp>
#include <parser.hpp>

// Remarks:
// - Do not use BOOST_CHECK_EQUAL() to compare const char* variables as
//   Boost.Test attempts to be smart by calling std::strcmp()
//   [with Boost 1.53, see boost/test/impl/test_tools.ipp:383, equal_impl()].
//   In this file, const char* variables do not always reference null-terminated
//   strings, e.g., when using std::vector<char*>::data(). If this happens,
//   Valgrind may detect invalid reads.

namespace deepstream {
namespace parser
{

BOOST_AUTO_TEST_CASE(empty_string)
{
	deepstream_parser_state state("", 0);

	int token = state.handle_token(TOKEN_EOF, "\0", 1);
	BOOST_CHECK_EQUAL( token, TOKEN_EOF );

	BOOST_CHECK( state.tokenizing_header_ );
	BOOST_CHECK_EQUAL( state.offset_, 1 );
	BOOST_CHECK( state.messages_.empty() );
	BOOST_CHECK( state.errors_.empty() );
}

BOOST_AUTO_TEST_CASE(simple)
{
	const auto input = Message::from_human_readable("A|A+");
	const auto copy(input);
	const char* matches[] = { &input[0], &input[3], "" };
	std::size_t sizes[] = { 3, 1, 1 };

	const deepstream_token tokens[] = {
		TOKEN_A_A, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==2) ? tokenizing_header : !tokenizing_header );

		int ret = state.handle_token(tokens[i], matches[i], sizes[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), 1 );
		BOOST_CHECK( state.errors_.empty() );

		std::size_t offset = std::accumulate( sizes, sizes+i+1, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );
	}

	Message& msg = state.messages_.front();

	BOOST_CHECK( msg.base_ == copy.data() );
	BOOST_CHECK_EQUAL( msg.offset_, 0 );
	BOOST_CHECK_EQUAL( msg.size_, input.size() );

	BOOST_CHECK_EQUAL( msg.topic(), Topic::AUTH );
	BOOST_CHECK_EQUAL( msg.action(), Action::REQUEST );
	BOOST_CHECK( msg.is_ack() );

	BOOST_CHECK( msg.arguments_.empty() );
}

BOOST_AUTO_TEST_CASE(concatenated_messages)
{
	const char STRING[] = "E|L|listen+E|S|event+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token tokens[] = {
		TOKEN_E_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_E_S, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR,
		TOKEN_EOF
	};

	const std::size_t num_tokens = sizeof(tokens) / sizeof(tokens[0]);

	const std::size_t matchlens[num_tokens] = { 3, 7, 1, 3, 6, 1, 1 };
	const char* matches[num_tokens] = {
		&input[ 0], &input[3], &input[10],
		&input[11], &input[14], &input[20],
		""
	};

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < num_tokens; ++i)
	{
		BOOST_CHECK( (i==0||i==3||i==6) ?
			state.tokenizing_header_ :
			!state.tokenizing_header_
		);

		std::size_t offset = std::accumulate( matchlens, matchlens+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(tokens[i], matches[i], matchlens[i]);

		BOOST_CHECK_EQUAL( ret, tokens[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=3) ? 2 : 1 );
		BOOST_CHECK( state.errors_.empty() );

		BOOST_CHECK_EQUAL( state.offset_, offset+matchlens[i] );
	}

	BOOST_CHECK( state.tokenizing_header_ );

	for(const Message& msg : state.messages_)
	{
		BOOST_CHECK( msg.base_ == copy.data() );

		BOOST_CHECK_EQUAL( msg.topic(), Topic::EVENT );
		BOOST_CHECK( !msg.is_ack() );

		BOOST_CHECK_EQUAL( msg.arguments_.size(), 1 );
	}

	BOOST_CHECK_EQUAL( state.messages_.size(), 2 );

	const Message& msg_f = state.messages_.front();
	BOOST_CHECK_EQUAL( msg_f.offset(), 0 );
	BOOST_CHECK_EQUAL( msg_f.size(), 11 );
	BOOST_CHECK_EQUAL( msg_f.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_f.action(), Action::LISTEN );

	const Location& arg_f = msg_f.arguments_.front();

	BOOST_CHECK_EQUAL( arg_f.offset_, 4 );
	BOOST_CHECK_EQUAL( arg_f.length_, 6 );
	BOOST_CHECK( !strncmp(&input[arg_f.offset_], "listen", arg_f.length_) );

	const Message& msg_b = state.messages_.back();
	BOOST_CHECK_EQUAL( msg_b.offset(), 11 );
	BOOST_CHECK_EQUAL( msg_b.size(), 10 );
	BOOST_CHECK_EQUAL( msg_b.topic(), Topic::EVENT );
	BOOST_CHECK_EQUAL( msg_b.action(), Action::SUBSCRIBE );

	const Location& arg_b = msg_b.arguments_.front();

	BOOST_CHECK_EQUAL( arg_b.offset_, 15 );
	BOOST_CHECK_EQUAL( arg_b.length_, 5 );
	BOOST_CHECK( !strncmp(&input[arg_b.offset_], "event", arg_b.length_) );
}

BOOST_AUTO_TEST_CASE(invalid_number_of_arguments)
{
	const char STRING[] = "E|A|L|l+";

	const auto input = Message::from_human_readable(STRING);
	const auto copy(input);

	const deepstream_token TOKENS[] = {
		TOKEN_E_A_L, TOKEN_PAYLOAD, TOKEN_MESSAGE_SEPARATOR, TOKEN_EOF
	};

	const std::size_t NUM_TOKENS = sizeof(TOKENS) / sizeof(TOKENS[0]);

	const std::size_t MATCHLENS[NUM_TOKENS] = { 5, 2, 1, 1 };
	const char* MATCHES[NUM_TOKENS] = { &input[0], &input[5], &input[7], "" };

	deepstream_parser_state state( &copy[0], copy.size() );

	for(std::size_t i = 0; i < NUM_TOKENS; ++i)
	{
		bool tokenizing_header = state.tokenizing_header_;
		BOOST_CHECK( (i==0||i==3) ? tokenizing_header : !tokenizing_header );

		std::size_t offset = std::accumulate( MATCHLENS, MATCHLENS+i, 0 );
		BOOST_CHECK_EQUAL( state.offset_, offset );

		int ret = state.handle_token(TOKENS[i], MATCHES[i], MATCHLENS[i]);

		BOOST_CHECK_EQUAL( ret, TOKENS[i] );

		BOOST_CHECK_EQUAL( state.messages_.size(), (i>=2) ? 0 : 1 );
		BOOST_CHECK_EQUAL( state.errors_.size(), (i>=2) ? 1 : 0 );

		BOOST_CHECK_EQUAL( state.offset_, offset+MATCHLENS[i] );
	}

	const Error& e = state.errors_.front();

	BOOST_CHECK_EQUAL( e.location_.offset_, 0 );
	BOOST_CHECK_EQUAL( e.location_.length_, 8 );
	BOOST_CHECK_EQUAL( e.tag_, Error::INVALID_NUMBER_OF_ARGUMENTS );
}

}
}
<|endoftext|>


<commit_before>#include "lextrans.h"

#include <iostream>

#include "filelib.h"
#include "hg.h"
#include "tdict.h"
#include "grammar.h"
#include "sentence_metadata.h"

using namespace std;

struct LexicalTransImpl {
  LexicalTransImpl(const boost::program_options::variables_map& conf) :
      use_null(conf.count("lexcrf_use_null") > 0),
      kXCAT(TD::Convert("X")*-1),
      kNULL(TD::Convert("<eps>")),
      kBINARY(new TRule("[X] ||| [X,1] [X,2] ||| [1] [2]")),
      kGOAL_RULE(new TRule("[Goal] ||| [X,1] ||| [1]")) {
    vector<string> gfiles = conf["grammar"].as<vector<string> >();
    assert(gfiles.size() == 1);
    ReadFile rf(gfiles.front());
    TextGrammar *tg = new TextGrammar;
    grammar.reset(tg);
    istream* in = rf.stream();
    int lc = 0;
    bool flag = false;
    while(*in) {
      string line;
      getline(*in, line);
      if (line.empty()) continue;
      ++lc;
      TRulePtr r(TRule::CreateRulePhrasetable(line));
      tg->AddRule(r);
      if (lc %   50000 == 0) { cerr << '.'; flag = true; }
      if (lc % 2000000 == 0) { cerr << " [" << lc << "]\n"; flag = false; }
    }
    if (flag) cerr << endl;
    cerr << "Loaded " << lc << " rules\n";
  }

  void BuildTrellis(const Lattice& lattice, const SentenceMetadata& smeta, Hypergraph* forest) {
    const int e_len = smeta.GetTargetLength();
    assert(e_len > 0);
    const int f_len = lattice.size();
    // hack to tell the feature function system how big the sentence pair is
    const int f_start = (use_null ? -1 : 0);
    int prev_node_id = -1;
    for (int i = 0; i < e_len; ++i) {  // for each word in the *target*
      Hypergraph::Node* node = forest->AddNode(kXCAT);
      const int new_node_id = node->id_;
      for (int j = f_start; j < f_len; ++j) { // for each word in the source
        const WordID src_sym = (j < 0 ? kNULL : lattice[j][0].label);
        const GrammarIter* gi = grammar->GetRoot()->Extend(src_sym);
        if (!gi) {
          cerr << "No translations found for: " << TD::Convert(src_sym) << "\n";
          abort();
        }
        const RuleBin* rb = gi->GetRules();
        assert(rb);
        for (int k = 0; k < rb->GetNumRules(); ++k) {
          TRulePtr rule = rb->GetIthRule(k);
          Hypergraph::Edge* edge = forest->AddEdge(rule, Hypergraph::TailNodeVector());
          edge->i_ = j;
          edge->j_ = j+1;
          edge->prev_i_ = i;
          edge->prev_j_ = i+1;
          edge->feature_values_ += edge->rule_->GetFeatureValues();
          forest->ConnectEdgeToHeadNode(edge->id_, new_node_id);
        }
      }
      if (prev_node_id >= 0) {
        const int comb_node_id = forest->AddNode(kXCAT)->id_;
        Hypergraph::TailNodeVector tail(2, prev_node_id);
        tail[1] = new_node_id;
        Hypergraph::Edge* edge = forest->AddEdge(kBINARY, tail);
        forest->ConnectEdgeToHeadNode(edge->id_, comb_node_id);
        prev_node_id = comb_node_id;
      } else {
        prev_node_id = new_node_id;
      }
    }
    Hypergraph::TailNodeVector tail(1, forest->nodes_.size() - 1);
    Hypergraph::Node* goal = forest->AddNode(TD::Convert("Goal")*-1);
    Hypergraph::Edge* hg_edge = forest->AddEdge(kGOAL_RULE, tail);
    forest->ConnectEdgeToHeadNode(hg_edge, goal);
  }

 private:
  const bool use_null;
  const WordID kXCAT;
  const WordID kNULL;
  const TRulePtr kBINARY;
  const TRulePtr kGOAL_RULE;
  GrammarPtr grammar;
};

LexicalTrans::LexicalTrans(const boost::program_options::variables_map& conf) :
  pimpl_(new LexicalTransImpl(conf)) {}

bool LexicalTrans::TranslateImpl(const string& input,
                      SentenceMetadata* smeta,
                      const vector<double>& weights,
                      Hypergraph* forest) {
  Lattice& lattice = smeta->src_lattice_;
  LatticeTools::ConvertTextOrPLF(input, &lattice);
  if (!lattice.IsSentence()) {
    // lexical models make independence assumptions
    // that don't work with lattices or conf nets
    cerr << "LexicalTrans: cannot deal with lattice source input!\n";
    abort();
  }
  smeta->SetSourceLength(lattice.size());
  pimpl_->BuildTrellis(lattice, *smeta, forest);
  forest->is_linear_chain_ = true;
  forest->Reweight(weights);
  return true;
}

<commit_msg>fix typo<commit_after>#include "lextrans.h"

#include <iostream>

#include "filelib.h"
#include "hg.h"
#include "tdict.h"
#include "grammar.h"
#include "sentence_metadata.h"

using namespace std;

struct LexicalTransImpl {
  LexicalTransImpl(const boost::program_options::variables_map& conf) :
      use_null(conf.count("lextrans_use_null") > 0),
      kXCAT(TD::Convert("X")*-1),
      kNULL(TD::Convert("<eps>")),
      kBINARY(new TRule("[X] ||| [X,1] [X,2] ||| [1] [2]")),
      kGOAL_RULE(new TRule("[Goal] ||| [X,1] ||| [1]")) {
    vector<string> gfiles = conf["grammar"].as<vector<string> >();
    assert(gfiles.size() == 1);
    ReadFile rf(gfiles.front());
    TextGrammar *tg = new TextGrammar;
    grammar.reset(tg);
    istream* in = rf.stream();
    int lc = 0;
    bool flag = false;
    while(*in) {
      string line;
      getline(*in, line);
      if (line.empty()) continue;
      ++lc;
      TRulePtr r(TRule::CreateRulePhrasetable(line));
      tg->AddRule(r);
      if (lc %   50000 == 0) { cerr << '.'; flag = true; }
      if (lc % 2000000 == 0) { cerr << " [" << lc << "]\n"; flag = false; }
    }
    if (flag) cerr << endl;
    cerr << "Loaded " << lc << " rules\n";
  }

  void BuildTrellis(const Lattice& lattice, const SentenceMetadata& smeta, Hypergraph* forest) {
    const int e_len = smeta.GetTargetLength();
    assert(e_len > 0);
    const int f_len = lattice.size();
    // hack to tell the feature function system how big the sentence pair is
    const int f_start = (use_null ? -1 : 0);
    int prev_node_id = -1;
    for (int i = 0; i < e_len; ++i) {  // for each word in the *target*
      Hypergraph::Node* node = forest->AddNode(kXCAT);
      const int new_node_id = node->id_;
      for (int j = f_start; j < f_len; ++j) { // for each word in the source
        const WordID src_sym = (j < 0 ? kNULL : lattice[j][0].label);
        const GrammarIter* gi = grammar->GetRoot()->Extend(src_sym);
        if (!gi) {
          cerr << "No translations found for: " << TD::Convert(src_sym) << "\n";
          abort();
        }
        const RuleBin* rb = gi->GetRules();
        assert(rb);
        for (int k = 0; k < rb->GetNumRules(); ++k) {
          TRulePtr rule = rb->GetIthRule(k);
          Hypergraph::Edge* edge = forest->AddEdge(rule, Hypergraph::TailNodeVector());
          edge->i_ = j;
          edge->j_ = j+1;
          edge->prev_i_ = i;
          edge->prev_j_ = i+1;
          edge->feature_values_ += edge->rule_->GetFeatureValues();
          forest->ConnectEdgeToHeadNode(edge->id_, new_node_id);
        }
      }
      if (prev_node_id >= 0) {
        const int comb_node_id = forest->AddNode(kXCAT)->id_;
        Hypergraph::TailNodeVector tail(2, prev_node_id);
        tail[1] = new_node_id;
        Hypergraph::Edge* edge = forest->AddEdge(kBINARY, tail);
        forest->ConnectEdgeToHeadNode(edge->id_, comb_node_id);
        prev_node_id = comb_node_id;
      } else {
        prev_node_id = new_node_id;
      }
    }
    Hypergraph::TailNodeVector tail(1, forest->nodes_.size() - 1);
    Hypergraph::Node* goal = forest->AddNode(TD::Convert("Goal")*-1);
    Hypergraph::Edge* hg_edge = forest->AddEdge(kGOAL_RULE, tail);
    forest->ConnectEdgeToHeadNode(hg_edge, goal);
  }

 private:
  const bool use_null;
  const WordID kXCAT;
  const WordID kNULL;
  const TRulePtr kBINARY;
  const TRulePtr kGOAL_RULE;
  GrammarPtr grammar;
};

LexicalTrans::LexicalTrans(const boost::program_options::variables_map& conf) :
  pimpl_(new LexicalTransImpl(conf)) {}

bool LexicalTrans::TranslateImpl(const string& input,
                      SentenceMetadata* smeta,
                      const vector<double>& weights,
                      Hypergraph* forest) {
  Lattice& lattice = smeta->src_lattice_;
  LatticeTools::ConvertTextOrPLF(input, &lattice);
  if (!lattice.IsSentence()) {
    // lexical models make independence assumptions
    // that don't work with lattices or conf nets
    cerr << "LexicalTrans: cannot deal with lattice source input!\n";
    abort();
  }
  smeta->SetSourceLength(lattice.size());
  pimpl_->BuildTrellis(lattice, *smeta, forest);
  forest->is_linear_chain_ = true;
  forest->Reweight(weights);
  return true;
}

<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template<typename MatrixType> void verifySizeOf(const MatrixType&)
{
  typedef typename MatrixType::Scalar Scalar;
  if (MatrixType::RowsAtCompileTime!=Dynamic && MatrixType::ColsAtCompileTime!=Dynamic)
    VERIFY(sizeof(MatrixType)==static_cast<int>(sizeof(Scalar))*MatrixType::SizeAtCompileTime);
  else
    VERIFY(sizeof(MatrixType)==sizeof(Scalar*) + 2 * sizeof(typename MatrixType::Index));
}

void test_sizeof()
{
  CALL_SUBTEST(verifySizeOf(Matrix<float, 1, 1>()) );
  CALL_SUBTEST(verifySizeOf(Matrix4d()) );
  CALL_SUBTEST(verifySizeOf(Matrix<double, 4, 2>()) );
  CALL_SUBTEST(verifySizeOf(Matrix<bool, 7, 5>()) );
  CALL_SUBTEST(verifySizeOf(MatrixXcf(3, 3)) );
  CALL_SUBTEST(verifySizeOf(MatrixXi(8, 12)) );
  CALL_SUBTEST(verifySizeOf(MatrixXcd(20, 20)) );
  CALL_SUBTEST(verifySizeOf(Matrix<float, 100, 100>()) );

  VERIFY(sizeof(std::complex<float>) == 2*sizeof(float));
  VERIFY(sizeof(std::complex<double>) == 2*sizeof(double));
}
<commit_msg>fix icc warning #68<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template<typename MatrixType> void verifySizeOf(const MatrixType&)
{
  typedef typename MatrixType::Scalar Scalar;
  if (MatrixType::RowsAtCompileTime!=Dynamic && MatrixType::ColsAtCompileTime!=Dynamic)
    VERIFY(sizeof(MatrixType)==sizeof(Scalar)*size_t(MatrixType::SizeAtCompileTime));
  else
    VERIFY(sizeof(MatrixType)==sizeof(Scalar*) + 2 * sizeof(typename MatrixType::Index));
}

void test_sizeof()
{
  CALL_SUBTEST(verifySizeOf(Matrix<float, 1, 1>()) );
  CALL_SUBTEST(verifySizeOf(Matrix4d()) );
  CALL_SUBTEST(verifySizeOf(Matrix<double, 4, 2>()) );
  CALL_SUBTEST(verifySizeOf(Matrix<bool, 7, 5>()) );
  CALL_SUBTEST(verifySizeOf(MatrixXcf(3, 3)) );
  CALL_SUBTEST(verifySizeOf(MatrixXi(8, 12)) );
  CALL_SUBTEST(verifySizeOf(MatrixXcd(20, 20)) );
  CALL_SUBTEST(verifySizeOf(Matrix<float, 100, 100>()) );

  VERIFY(sizeof(std::complex<float>) == 2*sizeof(float));
  VERIFY(sizeof(std::complex<double>) == 2*sizeof(double));
}
<|endoftext|>


<commit_before>#include <unordered_map>

#include <lug/System/Logger/Logger.hpp>
#include <lug/Window/Keyboard.hpp>
#include <lug/Window/Mouse.hpp>
#include <lug/Window/Window.hpp>

#if defined(LUG_SYSTEM_ANDROID)
    #include <lug/System/Logger/LogCatHandler.hpp>
#else
    #include <lug/System/Logger/OstreamHandler.hpp>
#endif

auto createKeyEnumMap() {
    std::unordered_map<lug::Window::Keyboard::Key, std::string> returnValue;

    returnValue[lug::Window::Keyboard::Key::Unknown] = "Unknown";

    // Basic keys
    returnValue[lug::Window::Keyboard::Key::A] = "A";
    returnValue[lug::Window::Keyboard::Key::B] = "B";
    returnValue[lug::Window::Keyboard::Key::C] = "C";
    returnValue[lug::Window::Keyboard::Key::D] = "D";
    returnValue[lug::Window::Keyboard::Key::E] = "E";
    returnValue[lug::Window::Keyboard::Key::F] = "F";
    returnValue[lug::Window::Keyboard::Key::G] = "G";
    returnValue[lug::Window::Keyboard::Key::H] = "H";
    returnValue[lug::Window::Keyboard::Key::I] = "I";
    returnValue[lug::Window::Keyboard::Key::J] = "J";
    returnValue[lug::Window::Keyboard::Key::K] = "K";
    returnValue[lug::Window::Keyboard::Key::L] = "L";
    returnValue[lug::Window::Keyboard::Key::M] = "M";
    returnValue[lug::Window::Keyboard::Key::N] = "N";
    returnValue[lug::Window::Keyboard::Key::O] = "O";
    returnValue[lug::Window::Keyboard::Key::P] = "P";
    returnValue[lug::Window::Keyboard::Key::Q] = "Q";
    returnValue[lug::Window::Keyboard::Key::R] = "R";
    returnValue[lug::Window::Keyboard::Key::S] = "S";
    returnValue[lug::Window::Keyboard::Key::T] = "T";
    returnValue[lug::Window::Keyboard::Key::U] = "U";
    returnValue[lug::Window::Keyboard::Key::V] = "V";
    returnValue[lug::Window::Keyboard::Key::W] = "W";
    returnValue[lug::Window::Keyboard::Key::X] = "X";
    returnValue[lug::Window::Keyboard::Key::Y] = "Y";
    returnValue[lug::Window::Keyboard::Key::Z] = "Z";

    returnValue[lug::Window::Keyboard::Key::Num0] = "Num0";
    returnValue[lug::Window::Keyboard::Key::Num1] = "Num1";
    returnValue[lug::Window::Keyboard::Key::Num2] = "Num2";
    returnValue[lug::Window::Keyboard::Key::Num3] = "Num3";
    returnValue[lug::Window::Keyboard::Key::Num4] = "Num4";
    returnValue[lug::Window::Keyboard::Key::Num5] = "Num5";
    returnValue[lug::Window::Keyboard::Key::Num6] = "Num6";
    returnValue[lug::Window::Keyboard::Key::Num7] = "Num7";
    returnValue[lug::Window::Keyboard::Key::Num8] = "Num8";
    returnValue[lug::Window::Keyboard::Key::Num9] = "Num9";

    // Modifiers
    returnValue[lug::Window::Keyboard::Key::LControl] = "LControl";
    returnValue[lug::Window::Keyboard::Key::LShift] = "LShift";
    returnValue[lug::Window::Keyboard::Key::LAlt] = "LAlt";
    returnValue[lug::Window::Keyboard::Key::LSystem] = "LSystem";
    returnValue[lug::Window::Keyboard::Key::RControl] = "RControl";
    returnValue[lug::Window::Keyboard::Key::RShift] = "RShift";
    returnValue[lug::Window::Keyboard::Key::RAlt] = "RAlt";
    returnValue[lug::Window::Keyboard::Key::RSystem] = "RSystem";

    // Advanced keys
    returnValue[lug::Window::Keyboard::Key::Menu] = "Menu";
    returnValue[lug::Window::Keyboard::Key::LBracket] = "LBracket";
    returnValue[lug::Window::Keyboard::Key::RBracket] = "RBracket";
    returnValue[lug::Window::Keyboard::Key::SemiColon] = "SemiColon";
    returnValue[lug::Window::Keyboard::Key::Comma] = "Comma";
    returnValue[lug::Window::Keyboard::Key::Period] = "Period";
    returnValue[lug::Window::Keyboard::Key::Quote] = "Quote";
    returnValue[lug::Window::Keyboard::Key::Slash] = "Slash";
    returnValue[lug::Window::Keyboard::Key::BackSlash] = "BackSlash";
    returnValue[lug::Window::Keyboard::Key::Tilde] = "Tilde";
    returnValue[lug::Window::Keyboard::Key::Equal] = "Equal";
    returnValue[lug::Window::Keyboard::Key::Dash] = "Dash";
    returnValue[lug::Window::Keyboard::Key::Space] = "Space";
    returnValue[lug::Window::Keyboard::Key::Return] = "Return";
    returnValue[lug::Window::Keyboard::Key::BackSpace] = "BackSpace";
    returnValue[lug::Window::Keyboard::Key::Tab] = "Tab";
    returnValue[lug::Window::Keyboard::Key::PageUp] = "PageUp";
    returnValue[lug::Window::Keyboard::Key::PageDown] = "PageDown";
    returnValue[lug::Window::Keyboard::Key::End] = "End";
    returnValue[lug::Window::Keyboard::Key::Home] = "Home";
    returnValue[lug::Window::Keyboard::Key::Insert] = "Insert";
    returnValue[lug::Window::Keyboard::Key::Delete] = "Delete";
    returnValue[lug::Window::Keyboard::Key::Add] = "Add";
    returnValue[lug::Window::Keyboard::Key::Subtract] = "Subtract";
    returnValue[lug::Window::Keyboard::Key::Multiply] = "Multiply";
    returnValue[lug::Window::Keyboard::Key::Divide] = "Divide";
    returnValue[lug::Window::Keyboard::Key::Left] = "Left";
    returnValue[lug::Window::Keyboard::Key::Right] = "Right";
    returnValue[lug::Window::Keyboard::Key::Up] = "Up";
    returnValue[lug::Window::Keyboard::Key::Down] = "Down";
    returnValue[lug::Window::Keyboard::Key::Pause] = "Pause";
    returnValue[lug::Window::Keyboard::Key::CapsLock] = "CapsLock";
    returnValue[lug::Window::Keyboard::Key::Escape] = "Escape";

    // AZERTY Specifics
    returnValue[lug::Window::Keyboard::Key::Twosuperior] = "Twosuperior";
    returnValue[lug::Window::Keyboard::Key::Ampersand] = "Ampersand";
    returnValue[lug::Window::Keyboard::Key::Eacute] = "Eacute";
    returnValue[lug::Window::Keyboard::Key::QuoteDouble] = "QuoteDouble";
    returnValue[lug::Window::Keyboard::Key::LParen] = "LParen";
    returnValue[lug::Window::Keyboard::Key::Egrave] = "Egrave";
    returnValue[lug::Window::Keyboard::Key::Underscore] = "Underscore";
    returnValue[lug::Window::Keyboard::Key::Ccedilla] = "Ccedilla";
    returnValue[lug::Window::Keyboard::Key::Agrave] = "Agrave";
    returnValue[lug::Window::Keyboard::Key::RParen] = "RParen";
    returnValue[lug::Window::Keyboard::Key::DeadCircumflex] = "DeadCircumflex";
    returnValue[lug::Window::Keyboard::Key::Ugrave] = "Ugrave";
    returnValue[lug::Window::Keyboard::Key::Asterisk] = "Asterisk";
    returnValue[lug::Window::Keyboard::Key::Dollar] = "Dollar";
    returnValue[lug::Window::Keyboard::Key::Colon] = "Colon";
    returnValue[lug::Window::Keyboard::Key::Exclam] = "Exclam";
    returnValue[lug::Window::Keyboard::Key::Less] = "Less";
    returnValue[lug::Window::Keyboard::Key::Greater] = "Greater";

    // Numpad
    returnValue[lug::Window::Keyboard::Key::Numpad0] = "Numpad0";
    returnValue[lug::Window::Keyboard::Key::Numpad1] = "Numpad1";
    returnValue[lug::Window::Keyboard::Key::Numpad2] = "Numpad2";
    returnValue[lug::Window::Keyboard::Key::Numpad3] = "Numpad3";
    returnValue[lug::Window::Keyboard::Key::Numpad4] = "Numpad4";
    returnValue[lug::Window::Keyboard::Key::Numpad5] = "Numpad5";
    returnValue[lug::Window::Keyboard::Key::Numpad6] = "Numpad6";
    returnValue[lug::Window::Keyboard::Key::Numpad7] = "Numpad7";
    returnValue[lug::Window::Keyboard::Key::Numpad8] = "Numpad8";
    returnValue[lug::Window::Keyboard::Key::Numpad9] = "Numpad9";

    // Function keys
    returnValue[lug::Window::Keyboard::Key::F1] = "F1";
    returnValue[lug::Window::Keyboard::Key::F2] = "F2";
    returnValue[lug::Window::Keyboard::Key::F3] = "F3";
    returnValue[lug::Window::Keyboard::Key::F4] = "F4";
    returnValue[lug::Window::Keyboard::Key::F5] = "F5";
    returnValue[lug::Window::Keyboard::Key::F6] = "F6";
    returnValue[lug::Window::Keyboard::Key::F7] = "F7";
    returnValue[lug::Window::Keyboard::Key::F8] = "F8";
    returnValue[lug::Window::Keyboard::Key::F9] = "F9";
    returnValue[lug::Window::Keyboard::Key::F10] = "F10";
    returnValue[lug::Window::Keyboard::Key::F11] = "F11";
    returnValue[lug::Window::Keyboard::Key::F12] = "F12";
    returnValue[lug::Window::Keyboard::Key::F13] = "F13";
    returnValue[lug::Window::Keyboard::Key::F14] = "F14";
    returnValue[lug::Window::Keyboard::Key::F15] = "F15";

    return returnValue;
}

auto createButtonEnumMap() {
    std::unordered_map<lug::Window::Mouse::Button, std::string> returnValue;

    // Function keys
    returnValue[lug::Window::Mouse::Button::Left] = "Left";
    returnValue[lug::Window::Mouse::Button::Right] = "Right";
    returnValue[lug::Window::Mouse::Button::Middle] = "Middle";
    returnValue[lug::Window::Mouse::Button::XButton1] = "XButton1";
    returnValue[lug::Window::Mouse::Button::XButton2] = "XButton2";

    return returnValue;
}

int main() {
    auto window = lug::Window::Window::create({
        800,                        // width
        600,                        // height
        "Default Window",           // title
        lug::Window::Style::Default // style
    });

    auto logger = lug::System::Logger::makeLogger("LugdunumSamples");

    auto keyEnumMap = createKeyEnumMap();

    auto buttonEnumMap = createButtonEnumMap();

#if defined(LUG_SYSTEM_ANDROID)
    auto handler = lug::System::Logger::makeHandler<lug::System::Logger::LogCatHandler>("LogCat");
#else
    auto handler = lug::System::Logger::makeHandler<lug::System::Logger::StdoutHandler>("Stdout");
#endif

    logger->addHandler(handler);
    LUG_LOG.addHandler(handler);

    if (!window) {
        logger->fatal("Window was not created");
        return 1;
    }

    logger->info("Window was created successfully");

    window->setKeyRepeat(true);

    // While window is open execute the following
    while (window->isOpen()) {
        lug::Window::Event event;
        while (window->pollEvent(event)) {
            logger->info("Event received: {}", static_cast<uint32_t>(event.type));

            if (event.type == lug::Window::Event::Type::Close) {
                logger->info("Closing the app");
                window->close();
            }

            if (event.type == lug::Window::Event::Type::KeyPressed && event.key.code == lug::Window::Keyboard::Key::Escape) {
                logger->info("Closing the app (escape)");
                window->close();
            }

            if (event.type == lug::Window::Event::Type::KeyPressed
                && event.key.code == lug::Window::Keyboard::Key::Q && event.key.ctrl) {
                logger->info("Closing the app (ctrl+Q)");
                window->close();
            }

//            // Checking to see if mouse position is stored correctly
//            {
//                uint32_t x;
//                uint32_t y;
//
//                window->getMousePos(x, y);
//                logger->info("Mouse is at position {} {}", x, y);
//            }

            // Checking KeyPressed events
            if (event.type == lug::Window::Event::Type::KeyPressed) {
                logger->info(keyEnumMap[event.key.code] + " is pressed");
            }

            // Checking KeyReleased events
            if (event.type == lug::Window::Event::Type::KeyReleased) {
                logger->info(keyEnumMap[event.key.code] + " is released");
            }

            // Checking ButtonPressed events
            if (event.type == lug::Window::Event::Type::ButtonPressed) {
                logger->info(buttonEnumMap[event.button.code] + " is pressed at position {} {}", event.button.coord.x, event.button.coord.y);
            }

            // Checking ButtonReleased events
            if (event.type == lug::Window::Event::Type::ButtonReleased) {
                logger->info(buttonEnumMap[event.button.code] + " is released at position {} {}", event.button.coord.x, event.button.coord.y);
            }

//            // Checking MouseWheel events
//            if (event.type == lug::Window::Event::Type::MouseWheel) {
//                logger->info("Mouse scroll wheel offset {} {}", event.button.scrollOffset.xOffset, event.button.scrollOffset.yOffset);
//                if (event.button.ctrl) {
//                    logger->info("MODIFIER CTRL");
//                }
//                if (event.button.shift) {
//                    logger->info("MODIFIER SHIFT");
//                }
//            }

//            // Checking MouseMoved events
//            if (event.type == lug::Window::Event::Type::MouseMoved) {
//                logger->info("Mouse moved at position {} {}", event.button.coord.x, event.button.coord.y);
//            }

            if (event.type == lug::Window::Event::Type::CharEntered) {
                logger->info("Char event received: {}", static_cast<char>(event.character.val));
            }

            // Checking to see if any special keys are pressed
            if (event.type == lug::Window::Event::Type::KeyPressed ||
                event.type == lug::Window::Event::Type::KeyReleased) {
                if (event.key.ctrl) {
                    logger->info("MODIFIER CTRL");
                }
                if (event.key.alt) {
                    logger->info("MODIFIER ALT");
                }
                if (event.key.shift) {
                    logger->info("MODIFIER SHIFT");
                }
                if (event.key.system) {
                    logger->info("MODIFIER SYSTEM");
                }
            }

        }

//        // Checking that keys states are correctly set
//        for (auto it = keyEnumMap.begin(); it != keyEnumMap.end(); ++it) {
//            if (window->isKeyPressed(it->first)) {
//                logger->info(it->second + " is set to pressed");
//            }
//        }

//        // Checking that mouse button states are correctly set
//        for (auto it = buttonEnumMap.begin(); it != buttonEnumMap.end(); ++it) {
//            if (window->isMousePressed(it->first)) {
//                logger->info(it->second + " is set to pressed");
//            }
//        }
    }

    return 0;
}
<commit_msg>Avoid MouseMoved spam<commit_after>#include <unordered_map>

#include <lug/System/Logger/Logger.hpp>
#include <lug/Window/Keyboard.hpp>
#include <lug/Window/Mouse.hpp>
#include <lug/Window/Window.hpp>

#if defined(LUG_SYSTEM_ANDROID)
    #include <lug/System/Logger/LogCatHandler.hpp>
#else
    #include <lug/System/Logger/OstreamHandler.hpp>
#endif

auto createKeyEnumMap() {
    std::unordered_map<lug::Window::Keyboard::Key, std::string> returnValue;

    returnValue[lug::Window::Keyboard::Key::Unknown] = "Unknown";

    // Basic keys
    returnValue[lug::Window::Keyboard::Key::A] = "A";
    returnValue[lug::Window::Keyboard::Key::B] = "B";
    returnValue[lug::Window::Keyboard::Key::C] = "C";
    returnValue[lug::Window::Keyboard::Key::D] = "D";
    returnValue[lug::Window::Keyboard::Key::E] = "E";
    returnValue[lug::Window::Keyboard::Key::F] = "F";
    returnValue[lug::Window::Keyboard::Key::G] = "G";
    returnValue[lug::Window::Keyboard::Key::H] = "H";
    returnValue[lug::Window::Keyboard::Key::I] = "I";
    returnValue[lug::Window::Keyboard::Key::J] = "J";
    returnValue[lug::Window::Keyboard::Key::K] = "K";
    returnValue[lug::Window::Keyboard::Key::L] = "L";
    returnValue[lug::Window::Keyboard::Key::M] = "M";
    returnValue[lug::Window::Keyboard::Key::N] = "N";
    returnValue[lug::Window::Keyboard::Key::O] = "O";
    returnValue[lug::Window::Keyboard::Key::P] = "P";
    returnValue[lug::Window::Keyboard::Key::Q] = "Q";
    returnValue[lug::Window::Keyboard::Key::R] = "R";
    returnValue[lug::Window::Keyboard::Key::S] = "S";
    returnValue[lug::Window::Keyboard::Key::T] = "T";
    returnValue[lug::Window::Keyboard::Key::U] = "U";
    returnValue[lug::Window::Keyboard::Key::V] = "V";
    returnValue[lug::Window::Keyboard::Key::W] = "W";
    returnValue[lug::Window::Keyboard::Key::X] = "X";
    returnValue[lug::Window::Keyboard::Key::Y] = "Y";
    returnValue[lug::Window::Keyboard::Key::Z] = "Z";

    returnValue[lug::Window::Keyboard::Key::Num0] = "Num0";
    returnValue[lug::Window::Keyboard::Key::Num1] = "Num1";
    returnValue[lug::Window::Keyboard::Key::Num2] = "Num2";
    returnValue[lug::Window::Keyboard::Key::Num3] = "Num3";
    returnValue[lug::Window::Keyboard::Key::Num4] = "Num4";
    returnValue[lug::Window::Keyboard::Key::Num5] = "Num5";
    returnValue[lug::Window::Keyboard::Key::Num6] = "Num6";
    returnValue[lug::Window::Keyboard::Key::Num7] = "Num7";
    returnValue[lug::Window::Keyboard::Key::Num8] = "Num8";
    returnValue[lug::Window::Keyboard::Key::Num9] = "Num9";

    // Modifiers
    returnValue[lug::Window::Keyboard::Key::LControl] = "LControl";
    returnValue[lug::Window::Keyboard::Key::LShift] = "LShift";
    returnValue[lug::Window::Keyboard::Key::LAlt] = "LAlt";
    returnValue[lug::Window::Keyboard::Key::LSystem] = "LSystem";
    returnValue[lug::Window::Keyboard::Key::RControl] = "RControl";
    returnValue[lug::Window::Keyboard::Key::RShift] = "RShift";
    returnValue[lug::Window::Keyboard::Key::RAlt] = "RAlt";
    returnValue[lug::Window::Keyboard::Key::RSystem] = "RSystem";

    // Advanced keys
    returnValue[lug::Window::Keyboard::Key::Menu] = "Menu";
    returnValue[lug::Window::Keyboard::Key::LBracket] = "LBracket";
    returnValue[lug::Window::Keyboard::Key::RBracket] = "RBracket";
    returnValue[lug::Window::Keyboard::Key::SemiColon] = "SemiColon";
    returnValue[lug::Window::Keyboard::Key::Comma] = "Comma";
    returnValue[lug::Window::Keyboard::Key::Period] = "Period";
    returnValue[lug::Window::Keyboard::Key::Quote] = "Quote";
    returnValue[lug::Window::Keyboard::Key::Slash] = "Slash";
    returnValue[lug::Window::Keyboard::Key::BackSlash] = "BackSlash";
    returnValue[lug::Window::Keyboard::Key::Tilde] = "Tilde";
    returnValue[lug::Window::Keyboard::Key::Equal] = "Equal";
    returnValue[lug::Window::Keyboard::Key::Dash] = "Dash";
    returnValue[lug::Window::Keyboard::Key::Space] = "Space";
    returnValue[lug::Window::Keyboard::Key::Return] = "Return";
    returnValue[lug::Window::Keyboard::Key::BackSpace] = "BackSpace";
    returnValue[lug::Window::Keyboard::Key::Tab] = "Tab";
    returnValue[lug::Window::Keyboard::Key::PageUp] = "PageUp";
    returnValue[lug::Window::Keyboard::Key::PageDown] = "PageDown";
    returnValue[lug::Window::Keyboard::Key::End] = "End";
    returnValue[lug::Window::Keyboard::Key::Home] = "Home";
    returnValue[lug::Window::Keyboard::Key::Insert] = "Insert";
    returnValue[lug::Window::Keyboard::Key::Delete] = "Delete";
    returnValue[lug::Window::Keyboard::Key::Add] = "Add";
    returnValue[lug::Window::Keyboard::Key::Subtract] = "Subtract";
    returnValue[lug::Window::Keyboard::Key::Multiply] = "Multiply";
    returnValue[lug::Window::Keyboard::Key::Divide] = "Divide";
    returnValue[lug::Window::Keyboard::Key::Left] = "Left";
    returnValue[lug::Window::Keyboard::Key::Right] = "Right";
    returnValue[lug::Window::Keyboard::Key::Up] = "Up";
    returnValue[lug::Window::Keyboard::Key::Down] = "Down";
    returnValue[lug::Window::Keyboard::Key::Pause] = "Pause";
    returnValue[lug::Window::Keyboard::Key::CapsLock] = "CapsLock";
    returnValue[lug::Window::Keyboard::Key::Escape] = "Escape";

    // AZERTY Specifics
    returnValue[lug::Window::Keyboard::Key::Twosuperior] = "Twosuperior";
    returnValue[lug::Window::Keyboard::Key::Ampersand] = "Ampersand";
    returnValue[lug::Window::Keyboard::Key::Eacute] = "Eacute";
    returnValue[lug::Window::Keyboard::Key::QuoteDouble] = "QuoteDouble";
    returnValue[lug::Window::Keyboard::Key::LParen] = "LParen";
    returnValue[lug::Window::Keyboard::Key::Egrave] = "Egrave";
    returnValue[lug::Window::Keyboard::Key::Underscore] = "Underscore";
    returnValue[lug::Window::Keyboard::Key::Ccedilla] = "Ccedilla";
    returnValue[lug::Window::Keyboard::Key::Agrave] = "Agrave";
    returnValue[lug::Window::Keyboard::Key::RParen] = "RParen";
    returnValue[lug::Window::Keyboard::Key::DeadCircumflex] = "DeadCircumflex";
    returnValue[lug::Window::Keyboard::Key::Ugrave] = "Ugrave";
    returnValue[lug::Window::Keyboard::Key::Asterisk] = "Asterisk";
    returnValue[lug::Window::Keyboard::Key::Dollar] = "Dollar";
    returnValue[lug::Window::Keyboard::Key::Colon] = "Colon";
    returnValue[lug::Window::Keyboard::Key::Exclam] = "Exclam";
    returnValue[lug::Window::Keyboard::Key::Less] = "Less";
    returnValue[lug::Window::Keyboard::Key::Greater] = "Greater";

    // Numpad
    returnValue[lug::Window::Keyboard::Key::Numpad0] = "Numpad0";
    returnValue[lug::Window::Keyboard::Key::Numpad1] = "Numpad1";
    returnValue[lug::Window::Keyboard::Key::Numpad2] = "Numpad2";
    returnValue[lug::Window::Keyboard::Key::Numpad3] = "Numpad3";
    returnValue[lug::Window::Keyboard::Key::Numpad4] = "Numpad4";
    returnValue[lug::Window::Keyboard::Key::Numpad5] = "Numpad5";
    returnValue[lug::Window::Keyboard::Key::Numpad6] = "Numpad6";
    returnValue[lug::Window::Keyboard::Key::Numpad7] = "Numpad7";
    returnValue[lug::Window::Keyboard::Key::Numpad8] = "Numpad8";
    returnValue[lug::Window::Keyboard::Key::Numpad9] = "Numpad9";

    // Function keys
    returnValue[lug::Window::Keyboard::Key::F1] = "F1";
    returnValue[lug::Window::Keyboard::Key::F2] = "F2";
    returnValue[lug::Window::Keyboard::Key::F3] = "F3";
    returnValue[lug::Window::Keyboard::Key::F4] = "F4";
    returnValue[lug::Window::Keyboard::Key::F5] = "F5";
    returnValue[lug::Window::Keyboard::Key::F6] = "F6";
    returnValue[lug::Window::Keyboard::Key::F7] = "F7";
    returnValue[lug::Window::Keyboard::Key::F8] = "F8";
    returnValue[lug::Window::Keyboard::Key::F9] = "F9";
    returnValue[lug::Window::Keyboard::Key::F10] = "F10";
    returnValue[lug::Window::Keyboard::Key::F11] = "F11";
    returnValue[lug::Window::Keyboard::Key::F12] = "F12";
    returnValue[lug::Window::Keyboard::Key::F13] = "F13";
    returnValue[lug::Window::Keyboard::Key::F14] = "F14";
    returnValue[lug::Window::Keyboard::Key::F15] = "F15";

    return returnValue;
}

auto createButtonEnumMap() {
    std::unordered_map<lug::Window::Mouse::Button, std::string> returnValue;

    // Function keys
    returnValue[lug::Window::Mouse::Button::Left] = "Left";
    returnValue[lug::Window::Mouse::Button::Right] = "Right";
    returnValue[lug::Window::Mouse::Button::Middle] = "Middle";
    returnValue[lug::Window::Mouse::Button::XButton1] = "XButton1";
    returnValue[lug::Window::Mouse::Button::XButton2] = "XButton2";

    return returnValue;
}

int main() {
    auto window = lug::Window::Window::create({
        800,                        // width
        600,                        // height
        "Default Window",           // title
        lug::Window::Style::Default // style
    });

    auto logger = lug::System::Logger::makeLogger("LugdunumSamples");

    auto keyEnumMap = createKeyEnumMap();

    auto buttonEnumMap = createButtonEnumMap();

#if defined(LUG_SYSTEM_ANDROID)
    auto handler = lug::System::Logger::makeHandler<lug::System::Logger::LogCatHandler>("LogCat");
#else
    auto handler = lug::System::Logger::makeHandler<lug::System::Logger::StdoutHandler>("Stdout");
#endif

    logger->addHandler(handler);
    LUG_LOG.addHandler(handler);

    if (!window) {
        logger->fatal("Window was not created");
        return 1;
    }

    logger->info("Window was created successfully");

    window->setKeyRepeat(true);

    // While window is open execute the following
    while (window->isOpen()) {
        lug::Window::Event event;
        while (window->pollEvent(event)) {
            /*
             * AVOID MOUSE MOVE SPAM !!!
             */
            if (event.type != lug::Window::Event::Type::MouseMoved) {
                logger->info("Event received: {}", static_cast<uint32_t>(event.type));
            }

            if (event.type == lug::Window::Event::Type::Close) {
                logger->info("Closing the app");
                window->close();
            }

            if (event.type == lug::Window::Event::Type::KeyPressed && event.key.code == lug::Window::Keyboard::Key::Escape) {
                logger->info("Closing the app (escape)");
                window->close();
            }

            if (event.type == lug::Window::Event::Type::KeyPressed
                && event.key.code == lug::Window::Keyboard::Key::Q && event.key.ctrl) {
                logger->info("Closing the app (ctrl+Q)");
                window->close();
            }

//            // Checking to see if mouse position is stored correctly
//            {
//                uint32_t x;
//                uint32_t y;
//
//                window->getMousePos(x, y);
//                logger->info("Mouse is at position {} {}", x, y);
//            }

            // Checking KeyPressed events
            if (event.type == lug::Window::Event::Type::KeyPressed) {
                logger->info(keyEnumMap[event.key.code] + " is pressed");
            }

            // Checking KeyReleased events
            if (event.type == lug::Window::Event::Type::KeyReleased) {
                logger->info(keyEnumMap[event.key.code] + " is released");
            }

            // Checking ButtonPressed events
            if (event.type == lug::Window::Event::Type::ButtonPressed) {
                logger->info(buttonEnumMap[event.button.code] + " is pressed at position {} {}", event.button.coord.x, event.button.coord.y);
            }

            // Checking ButtonReleased events
            if (event.type == lug::Window::Event::Type::ButtonReleased) {
                logger->info(buttonEnumMap[event.button.code] + " is released at position {} {}", event.button.coord.x, event.button.coord.y);
            }

            // Checking MouseWheel events
            if (event.type == lug::Window::Event::Type::MouseWheel) {
                logger->info("Mouse scroll wheel offset {} {}", event.button.scrollOffset.xOffset, event.button.scrollOffset.yOffset);
                if (event.button.ctrl) {
                    logger->info("MODIFIER CTRL");
                }
                if (event.button.shift) {
                    logger->info("MODIFIER SHIFT");
                }
            }

            if (event.type == lug::Window::Event::Type::MouseLeave) {
                logger->info("Mouse just left the window.");
                if (event.button.ctrl) {
                    logger->info("MODIFIER CTRL");
                }
                if (event.button.shift) {
                    logger->info("MODIFIER SHIFT");
                }
            }
            if (event.type == lug::Window::Event::Type::MouseEnter) {
                logger->info("Mouse just entered the window.");
                if (event.button.ctrl) {
                    logger->info("MODIFIER CTRL");
                }
                if (event.button.shift) {
                    logger->info("MODIFIER SHIFT");
                }
            }

//            // Checking MouseMoved events
//            if (event.type == lug::Window::Event::Type::MouseMoved) {
//                logger->info("Mouse moved at position {} {}", event.button.coord.x, event.button.coord.y);
//            }

            if (event.type == lug::Window::Event::Type::CharEntered) {
                logger->info("Char event received: {}", static_cast<char>(event.character.val));
            }

            // Checking to see if any special keys are pressed
            if (event.type == lug::Window::Event::Type::KeyPressed ||
                event.type == lug::Window::Event::Type::KeyReleased) {
                if (event.key.ctrl) {
                    logger->info("MODIFIER CTRL");
                }
                if (event.key.alt) {
                    logger->info("MODIFIER ALT");
                }
                if (event.key.shift) {
                    logger->info("MODIFIER SHIFT");
                }
                if (event.key.system) {
                    logger->info("MODIFIER SYSTEM");
                }
            }

        }

//        // Checking that keys states are correctly set
//        for (auto it = keyEnumMap.begin(); it != keyEnumMap.end(); ++it) {
//            if (window->isKeyPressed(it->first)) {
//                logger->info(it->second + " is set to pressed");
//            }
//        }

//        // Checking that mouse button states are correctly set
//        for (auto it = buttonEnumMap.begin(); it != buttonEnumMap.end(); ++it) {
//            if (window->isMousePressed(it->first)) {
//                logger->info(it->second + " is set to pressed");
//            }
//        }
    }

    return 0;
}
<|endoftext|>