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

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

template<typename T, size_t S>
struct circular_buffer {
private:
    static constexpr const size_t Size = S + 1;

    T buffer[Size];

    volatile size_t start;
    volatile size_t end;

public:
    circular_buffer() : start(0), end(0) {
        //Nothing to init
    }

    bool full() const {
        return (end + 1) % Size == start;
    }

    bool empty() const {
        return end == start;
    }

    bool push(T value){
        if(full()){
            return false;
        } else {
            buffer[end] = value;
            end = (end + 1) % Size;

            return true;
        }
    }

    T pop(){
        auto value = buffer[start];
        start = (start + 1) % Size;
        return value;
    }
};

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

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

template<typename T, size_t S>
struct circular_buffer {
private:
    static constexpr const size_t Size = S + 1;

    T buffer[Size];

    volatile size_t start;
    volatile size_t end;

public:
    circular_buffer() : start(0), end(0) {
        //Nothing to init
    }

    bool full() const {
        return (end + 1) % Size == start;
    }

    bool empty() const {
        return end == start;
    }

    bool push(T value){
        if(full()){
            return false;
        } else {
            buffer[end] = value;
            end = (end + 1) % Size;

            return true;
        }
    }

    T top() const {
        return buffer[start];
    }

    T pop(){
        auto value = buffer[start];
        start = (start + 1) % Size;
        return value;
    }
};

#endif
<|endoftext|>


<commit_before>#ifndef KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
#define KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP

#include "function_traits.hpp"
#include "utils.hpp"

#include <type_traits>
#include <tuple>

namespace kgr {
namespace detail {

// void_t implementation
template<typename...>
struct voider { using type = void; };

template<typename... Ts> using void_t = typename voider<Ts...>::type;

// things missing from c++11 (to be removed when switching to c++14)
template <bool b, typename T = void>
using enable_if_t = typename std::enable_if<b, T>::type;

template<typename T>
using decay_t = typename std::decay<T>::type;

template<std::size_t S, typename T>
using tuple_element_t = typename std::tuple_element<S, T>::type;

template<std::size_t ...>
struct seq {};

template<std::size_t n, std::size_t ...S>
struct seq_gen : seq_gen<n-1, n-1, S...> {};

template<std::size_t ...S>
struct seq_gen<0, S...> {
	using type = seq<S...>;
};

template<typename Tuple>
struct TupleSeqGen : seq_gen<std::tuple_size<Tuple>::value> {};

template<>
struct TupleSeqGen<std::tuple<>> : seq_gen<0> {};

template<typename Tuple>
using tuple_seq = typename TupleSeqGen<Tuple>::type;

template<typename Tuple, int n>
using tuple_seq_minus = typename detail::seq_gen<std::tuple_size<Tuple>::value - (n > std::tuple_size<Tuple>::value ? std::tuple_size<Tuple>::value : n)>::type;

// SFINAE utilities
template<typename T, typename = void>
struct has_invoke : std::false_type {};

template<typename T>
struct has_invoke<T, void_t<typename T::invoke>> : std::true_type {};

template<typename T, typename = void>
struct has_next : std::false_type {};

template<typename T>
struct has_next<T, void_t<typename T::Next>> : std::true_type {};

template<typename T, typename = void>
struct has_forward : std::false_type {};

template<typename T>
struct has_forward<T, void_t<decltype(std::declval<T>().forward())>> : std::true_type {};

template<typename T, typename = void>
struct is_service : std::false_type {};

template<typename T>
struct is_service<T, enable_if_t<(!std::is_polymorphic<T>::value || std::is_abstract<T>::value) && has_forward<T>::value>> : std::true_type {};

template<typename T, typename = void>
struct has_construct : std::false_type {};

template<typename T>
struct has_construct<T, void_t<decltype(&T::construct)>> : std::true_type {};

template<typename T, typename = void>
struct is_invoke_call : std::false_type {};

template<typename T>
struct is_invoke_call<T, void_t<typename T::Params>> : std::true_type {};

template<template<typename> class Map, typename T, typename = void>
struct is_complete_map : std::false_type {};

template<template<typename> class Map, typename T>
struct is_complete_map<Map, T, void_t<typename Map<T>::Service>> : std::true_type {};

template<typename T, typename... Args>
struct is_brace_constructible_helper {
private:
	template<typename U, typename... As>
	static decltype(static_cast<void>(U{std::declval<As>()...}), std::true_type{}) test(int);

	template<typename...>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<typename T, typename... Args>
struct has_template_construct_helper {
private:
	template<typename U, typename... As>
	static std::true_type test(decltype(&U::template construct<As...>)* = nullptr);

	template<typename...>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(nullptr));
};

template<typename T, typename... Args>
struct has_emplace_helper {
private:
	template<typename U, typename... As>
	static decltype(static_cast<void>(std::declval<U>().emplace(std::declval<As>()...)), std::true_type{}) test(int);

	template<typename U, typename... As>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<template<typename> class, typename, typename, typename...>
struct is_invokable_helper;

template<template<typename> class Map, typename T, typename... Args, std::size_t... S>
struct is_invokable_helper<Map, T, seq<S...>, Args...> {
private:
	template<typename U, typename... As>
	static decltype(
		static_cast<void>(std::declval<U>()(std::declval<ServiceType<service_map_t<Map, function_argument_t<S, decay_t<U>>>>>()..., std::declval<As>()...)),
		std::true_type{}
	) test(int);

	template<typename U, typename... As>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<typename T, typename... Args>
struct has_emplace : has_emplace_helper<T, Args...>::type {};

template<typename T, typename... Args>
struct is_brace_constructible : is_brace_constructible_helper<T, Args...>::type {};

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

template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;

template<typename... Ts>
struct has_template_construct : has_template_construct_helper<Ts...>::type {};

template<typename... Ts>
using is_someway_constructible = std::integral_constant<bool, is_brace_constructible<Ts...>::value || std::is_constructible<Ts...>::value>;

template<typename T, typename... Args>
using is_emplaceable = typename std::conditional<std::is_default_constructible<T>::value && has_emplace<T, Args...>::value, std::true_type, std::false_type>::type;

template<template<typename> class Map, typename U, typename... Args>
struct is_invokable : is_invokable_helper<Map, U, tuple_seq_minus<function_arguments_t<U>, sizeof...(Args)>, Args...>::type {};

} // namespace detail
} // namespace kgr

#endif // KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
<commit_msg>removed useless decay<commit_after>#ifndef KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
#define KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP

#include "function_traits.hpp"
#include "utils.hpp"

#include <type_traits>
#include <tuple>

namespace kgr {
namespace detail {

// void_t implementation
template<typename...>
struct voider { using type = void; };

template<typename... Ts> using void_t = typename voider<Ts...>::type;

// things missing from c++11 (to be removed when switching to c++14)
template <bool b, typename T = void>
using enable_if_t = typename std::enable_if<b, T>::type;

template<typename T>
using decay_t = typename std::decay<T>::type;

template<std::size_t S, typename T>
using tuple_element_t = typename std::tuple_element<S, T>::type;

template<std::size_t ...>
struct seq {};

template<std::size_t n, std::size_t ...S>
struct seq_gen : seq_gen<n-1, n-1, S...> {};

template<std::size_t ...S>
struct seq_gen<0, S...> {
	using type = seq<S...>;
};

template<typename Tuple>
struct TupleSeqGen : seq_gen<std::tuple_size<Tuple>::value> {};

template<>
struct TupleSeqGen<std::tuple<>> : seq_gen<0> {};

template<typename Tuple>
using tuple_seq = typename TupleSeqGen<Tuple>::type;

template<typename Tuple, int n>
using tuple_seq_minus = typename detail::seq_gen<std::tuple_size<Tuple>::value - (n > std::tuple_size<Tuple>::value ? std::tuple_size<Tuple>::value : n)>::type;

// SFINAE utilities
template<typename T, typename = void>
struct has_invoke : std::false_type {};

template<typename T>
struct has_invoke<T, void_t<typename T::invoke>> : std::true_type {};

template<typename T, typename = void>
struct has_next : std::false_type {};

template<typename T>
struct has_next<T, void_t<typename T::Next>> : std::true_type {};

template<typename T, typename = void>
struct has_forward : std::false_type {};

template<typename T>
struct has_forward<T, void_t<decltype(std::declval<T>().forward())>> : std::true_type {};

template<typename T, typename = void>
struct is_service : std::false_type {};

template<typename T>
struct is_service<T, enable_if_t<(!std::is_polymorphic<T>::value || std::is_abstract<T>::value) && has_forward<T>::value>> : std::true_type {};

template<typename T, typename = void>
struct has_construct : std::false_type {};

template<typename T>
struct has_construct<T, void_t<decltype(&T::construct)>> : std::true_type {};

template<typename T, typename = void>
struct is_invoke_call : std::false_type {};

template<typename T>
struct is_invoke_call<T, void_t<typename T::Params>> : std::true_type {};

template<template<typename> class Map, typename T, typename = void>
struct is_complete_map : std::false_type {};

template<template<typename> class Map, typename T>
struct is_complete_map<Map, T, void_t<typename Map<T>::Service>> : std::true_type {};

template<typename T, typename... Args>
struct is_brace_constructible_helper {
private:
	template<typename U, typename... As>
	static decltype(static_cast<void>(U{std::declval<As>()...}), std::true_type{}) test(int);

	template<typename...>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<typename T, typename... Args>
struct has_template_construct_helper {
private:
	template<typename U, typename... As>
	static std::true_type test(decltype(&U::template construct<As...>)* = nullptr);

	template<typename...>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(nullptr));
};

template<typename T, typename... Args>
struct has_emplace_helper {
private:
	template<typename U, typename... As>
	static decltype(static_cast<void>(std::declval<U>().emplace(std::declval<As>()...)), std::true_type{}) test(int);

	template<typename U, typename... As>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<template<typename> class, typename, typename, typename...>
struct is_invokable_helper;

template<template<typename> class Map, typename T, typename... Args, std::size_t... S>
struct is_invokable_helper<Map, T, seq<S...>, Args...> {
private:
	template<typename U, typename... As>
	static decltype(
		static_cast<void>(std::declval<U>()(std::declval<ServiceType<service_map_t<Map, function_argument_t<S, U>>>>()..., std::declval<As>()...)),
		std::true_type{}
	) test(int);

	template<typename U, typename... As>
	static std::false_type test(...);

public:
	using type = decltype(test<T, Args...>(0));
};

template<typename T, typename... Args>
struct has_emplace : has_emplace_helper<T, Args...>::type {};

template<typename T, typename... Args>
struct is_brace_constructible : is_brace_constructible_helper<T, Args...>::type {};

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

template<typename T> using remove_rvalue_reference_t = typename remove_rvalue_reference<T>::type;

template<typename... Ts>
struct has_template_construct : has_template_construct_helper<Ts...>::type {};

template<typename... Ts>
using is_someway_constructible = std::integral_constant<bool, is_brace_constructible<Ts...>::value || std::is_constructible<Ts...>::value>;

template<typename T, typename... Args>
using is_emplaceable = typename std::conditional<std::is_default_constructible<T>::value && has_emplace<T, Args...>::value, std::true_type, std::false_type>::type;

template<template<typename> class Map, typename U, typename... Args>
struct is_invokable : is_invokable_helper<Map, U, tuple_seq_minus<function_arguments_t<U>, sizeof...(Args)>, Args...>::type {};

} // namespace detail
} // namespace kgr

#endif // KGR_INCLUDE_KANGARU_DETAIL_TRAITS_HPP
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_MARKER_HELPERS_HPP
#define MAPNIK_MARKER_HELPERS_HPP

#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_type.hpp>
#include <mapnik/geometry_centroid.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/expression_node.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/marker.hpp> // for svg_storage_type
#include <mapnik/markers_placement.hpp>
#include <mapnik/attribute.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/vertex_processor.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>

// agg
#include "agg_trans_affine.h"

// boost
#include <boost/optional.hpp>
// stl
#include <memory>
#include <type_traits> // remove_reference
#include <cmath>

namespace mapnik {

struct clip_poly_tag;

using svg_attribute_type = agg::pod_bvector<svg::path_attributes>;

template <typename Detector>
struct vector_markers_dispatch : util::noncopyable
{
    vector_markers_dispatch(svg_path_ptr const& src,
                            agg::trans_affine const& marker_trans,
                            symbolizer_base const& sym,
                            Detector & detector,
                            double scale_factor,
                            feature_impl & feature,
                            attributes const& vars)
        : src_(src),
          marker_trans_(marker_trans),
          sym_(sym),
          detector_(detector),
          feature_(feature),
          vars_(vars),
          scale_factor_(scale_factor)
    {}

    virtual ~vector_markers_dispatch() {}

    template <typename T>
    void add_path(T & path)
    {
        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
        value_double opacity = get<value_double,keys::opacity>(sym_, feature_, vars_);
        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
        coord2d center = src_->bounding_box().center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine tr = recenter * marker_trans_;
        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
        markers_placement_params params { src_->bounding_box(), tr, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
        markers_placement_finder<T, Detector> placement_finder(
            placement_method, path, detector_, params);
        double x, y, angle = .0;
        while (placement_finder.get_point(x, y, angle, ignore_placement))
        {
            agg::trans_affine matrix = tr;
            matrix.rotate(angle);
            matrix.translate(x, y);
            render_marker(matrix, opacity);
        }
    }

    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;

protected:
    svg_path_ptr const& src_;
    agg::trans_affine const& marker_trans_;
    symbolizer_base const& sym_;
    Detector & detector_;
    feature_impl & feature_;
    attributes const& vars_;
    double scale_factor_;
};

template <typename Detector>
struct raster_markers_dispatch : util::noncopyable
{
    raster_markers_dispatch(image_rgba8 const& src,
                            agg::trans_affine const& marker_trans,
                            symbolizer_base const& sym,
                            Detector & detector,
                            double scale_factor,
                            feature_impl & feature,
                            attributes const& vars)
    : src_(src),
        marker_trans_(marker_trans),
        sym_(sym),
        detector_(detector),
        feature_(feature),
        vars_(vars),
        scale_factor_(scale_factor)
    {}

    virtual ~raster_markers_dispatch() {}

    template <typename T>
    void add_path(T & path)
    {
        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
        value_double opacity = get<value_double, keys::opacity>(sym_, feature_, vars_);
        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
        box2d<double> bbox(0,0, src_.width(),src_.height());
        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
        markers_placement_params params { bbox, marker_trans_, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
        markers_placement_finder<T, label_collision_detector4> placement_finder(
            placement_method, path, detector_, params);
        double x, y, angle = .0;
        while (placement_finder.get_point(x, y, angle, ignore_placement))
        {
            agg::trans_affine matrix = marker_trans_;
            matrix.rotate(angle);
            matrix.translate(x, y);
            render_marker(matrix, opacity);
        }
    }

    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;

protected:
    image_rgba8 const& src_;
    agg::trans_affine const& marker_trans_;
    symbolizer_base const& sym_;
    Detector & detector_;
    feature_impl & feature_;
    attributes const& vars_;
    double scale_factor_;
};

void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, attributes const& vars,
                   svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path);

bool push_explicit_style(svg_attribute_type const& src,
                         svg_attribute_type & dst,
                         symbolizer_base const& sym,
                         feature_impl & feature,
                         attributes const& vars);

void setup_transform_scaling(agg::trans_affine & tr,
                             double svg_width,
                             double svg_height,
                             mapnik::feature_impl & feature,
                             attributes const& vars,
                             symbolizer_base const& sym);

// Apply markers to a feature with multiple geometries
template <typename Converter>
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, symbolizer_base const& sym)
{
    using vertex_converter_type = Converter;
    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type>;
    using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;

    auto const& geom = feature.get_geometry();
    geometry::geometry_types type = geometry::geometry_type(geom);

    if (type == geometry::geometry_types::Point
        || type == geometry::geometry_types::LineString
        || type == geometry::geometry_types::Polygon)
    {
        apply_vertex_converter_type apply(converter);
        mapnik::util::apply_visitor(vertex_processor_type(apply), geom);
    }
    else
    {

        marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum, keys::markers_multipolicy>(sym, feature, vars);
        marker_placement_enum placement = get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars);

        if (placement == MARKER_POINT_PLACEMENT &&
            multi_policy == MARKER_WHOLE_MULTI)
        {
            geometry::point pt;
            if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y))
            {
                // unset any clipping since we're now dealing with a point
                converter.template unset<clip_poly_tag>();
                geometry::point_vertex_adapter va(pt);
                converter.apply(va);
            }
        }
        else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) &&
                 multi_policy == MARKER_LARGEST_MULTI)
        {
            // Only apply to path with largest envelope area
            // TODO: consider using true area for polygon types
            if (type == geometry::geometry_types::MultiPolygon)
            {
                geometry::multi_polygon const& multi_poly = mapnik::util::get<geometry::multi_polygon>(geom);
                double maxarea = 0;
                geometry::polygon const* largest = 0;
                for (geometry::polygon const& poly : multi_poly)
                {
                    box2d<double> bbox = geometry::envelope(poly);
                    geometry::polygon_vertex_adapter va(poly);
                    double area = bbox.width() * bbox.height();
                    if (area > maxarea)
                    {
                        maxarea = area;
                        largest = &poly;
                    }
                }
                if (largest)
                {
                    geometry::polygon_vertex_adapter va(*largest);
                    converter.apply(va);
                }
            }
            else
            {
                MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue";
            }
        }
        else
        {
            if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT)
            {
                MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'";
            }
            apply_vertex_converter_type apply(converter);
            mapnik::util::apply_visitor(vertex_processor_type(apply), geom);
        }
    }
}

}

#endif //MAPNIK_MARKER_HELPERS_HPP
<commit_msg>ref #2755  test if centroid is within bounding box<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2014 Artem Pavlenko
 *
 * This library 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 2.1 of the License, or (at your option) any later version.
 *
 * This library 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 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_MARKER_HELPERS_HPP
#define MAPNIK_MARKER_HELPERS_HPP

#include <mapnik/color.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/geometry_type.hpp>
#include <mapnik/geometry_centroid.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/expression_node.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/svg/svg_path_attributes.hpp>
#include <mapnik/svg/svg_converter.hpp>
#include <mapnik/marker.hpp> // for svg_storage_type
#include <mapnik/markers_placement.hpp>
#include <mapnik/attribute.hpp>
#include <mapnik/box2d.hpp>
#include <mapnik/vertex_converters.hpp>
#include <mapnik/vertex_processor.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/renderer_common/apply_vertex_converter.hpp>

// agg
#include "agg_trans_affine.h"

// boost
#include <boost/optional.hpp>
// stl
#include <memory>
#include <type_traits> // remove_reference
#include <cmath>

namespace mapnik {

struct clip_poly_tag;

using svg_attribute_type = agg::pod_bvector<svg::path_attributes>;

template <typename Detector>
struct vector_markers_dispatch : util::noncopyable
{
    vector_markers_dispatch(svg_path_ptr const& src,
                            agg::trans_affine const& marker_trans,
                            symbolizer_base const& sym,
                            Detector & detector,
                            double scale_factor,
                            feature_impl & feature,
                            attributes const& vars)
        : src_(src),
          marker_trans_(marker_trans),
          sym_(sym),
          detector_(detector),
          feature_(feature),
          vars_(vars),
          scale_factor_(scale_factor)
    {}

    virtual ~vector_markers_dispatch() {}

    template <typename T>
    void add_path(T & path)
    {
        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
        value_double opacity = get<value_double,keys::opacity>(sym_, feature_, vars_);
        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
        coord2d center = src_->bounding_box().center();
        agg::trans_affine_translation recenter(-center.x, -center.y);
        agg::trans_affine tr = recenter * marker_trans_;
        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
        markers_placement_params params { src_->bounding_box(), tr, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
        markers_placement_finder<T, Detector> placement_finder(
            placement_method, path, detector_, params);
        double x, y, angle = .0;
        while (placement_finder.get_point(x, y, angle, ignore_placement))
        {
            agg::trans_affine matrix = tr;
            matrix.rotate(angle);
            matrix.translate(x, y);
            render_marker(matrix, opacity);
        }
    }

    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;

protected:
    svg_path_ptr const& src_;
    agg::trans_affine const& marker_trans_;
    symbolizer_base const& sym_;
    Detector & detector_;
    feature_impl & feature_;
    attributes const& vars_;
    double scale_factor_;
};

template <typename Detector>
struct raster_markers_dispatch : util::noncopyable
{
    raster_markers_dispatch(image_rgba8 const& src,
                            agg::trans_affine const& marker_trans,
                            symbolizer_base const& sym,
                            Detector & detector,
                            double scale_factor,
                            feature_impl & feature,
                            attributes const& vars)
    : src_(src),
        marker_trans_(marker_trans),
        sym_(sym),
        detector_(detector),
        feature_(feature),
        vars_(vars),
        scale_factor_(scale_factor)
    {}

    virtual ~raster_markers_dispatch() {}

    template <typename T>
    void add_path(T & path)
    {
        marker_placement_enum placement_method = get<marker_placement_enum, keys::markers_placement_type>(sym_, feature_, vars_);
        value_bool allow_overlap = get<value_bool, keys::allow_overlap>(sym_, feature_, vars_);
        value_bool avoid_edges = get<value_bool, keys::avoid_edges>(sym_, feature_, vars_);
        value_double opacity = get<value_double, keys::opacity>(sym_, feature_, vars_);
        value_bool ignore_placement = get<value_bool, keys::ignore_placement>(sym_, feature_, vars_);
        value_double spacing = get<value_double, keys::spacing>(sym_, feature_, vars_);
        value_double max_error = get<value_double, keys::max_error>(sym_, feature_, vars_);
        box2d<double> bbox(0,0, src_.width(),src_.height());
        direction_enum direction = get<direction_enum, keys::direction>(sym_, feature_, vars_);
        markers_placement_params params { bbox, marker_trans_, spacing * scale_factor_, max_error, allow_overlap, avoid_edges, direction };
        markers_placement_finder<T, label_collision_detector4> placement_finder(
            placement_method, path, detector_, params);
        double x, y, angle = .0;
        while (placement_finder.get_point(x, y, angle, ignore_placement))
        {
            agg::trans_affine matrix = marker_trans_;
            matrix.rotate(angle);
            matrix.translate(x, y);
            render_marker(matrix, opacity);
        }
    }

    virtual void render_marker(agg::trans_affine const& marker_tr, double opacity) = 0;

protected:
    image_rgba8 const& src_;
    agg::trans_affine const& marker_trans_;
    symbolizer_base const& sym_;
    Detector & detector_;
    feature_impl & feature_;
    attributes const& vars_;
    double scale_factor_;
};

void build_ellipse(symbolizer_base const& sym, mapnik::feature_impl & feature, attributes const& vars,
                   svg_storage_type & marker_ellipse, svg::svg_path_adapter & svg_path);

bool push_explicit_style(svg_attribute_type const& src,
                         svg_attribute_type & dst,
                         symbolizer_base const& sym,
                         feature_impl & feature,
                         attributes const& vars);

void setup_transform_scaling(agg::trans_affine & tr,
                             double svg_width,
                             double svg_height,
                             mapnik::feature_impl & feature,
                             attributes const& vars,
                             symbolizer_base const& sym);

// Apply markers to a feature with multiple geometries
template <typename Converter>
void apply_markers_multi(feature_impl const& feature, attributes const& vars, Converter & converter, symbolizer_base const& sym)
{
    using vertex_converter_type = Converter;
    using apply_vertex_converter_type = detail::apply_vertex_converter<vertex_converter_type>;
    using vertex_processor_type = geometry::vertex_processor<apply_vertex_converter_type>;

    auto const& geom = feature.get_geometry();
    geometry::geometry_types type = geometry::geometry_type(geom);

    if (type == geometry::geometry_types::Point
        || type == geometry::geometry_types::LineString
        || type == geometry::geometry_types::Polygon)
    {
        apply_vertex_converter_type apply(converter);
        mapnik::util::apply_visitor(vertex_processor_type(apply), geom);
    }
    else
    {

        marker_multi_policy_enum multi_policy = get<marker_multi_policy_enum, keys::markers_multipolicy>(sym, feature, vars);
        marker_placement_enum placement = get<marker_placement_enum, keys::markers_placement_type>(sym, feature, vars);

        if (placement == MARKER_POINT_PLACEMENT &&
            multi_policy == MARKER_WHOLE_MULTI)
        {
            geometry::point pt;
            // test if centroid is contained by bounding box
            if (geometry::centroid(geom, pt) && converter.disp_.args_.bbox.contains(pt.x, pt.y))
            {
                // unset any clipping since we're now dealing with a point
                converter.template unset<clip_poly_tag>();
                geometry::point_vertex_adapter va(pt);
                converter.apply(va);
            }
        }
        else if ((placement == MARKER_POINT_PLACEMENT || placement == MARKER_INTERIOR_PLACEMENT) &&
                 multi_policy == MARKER_LARGEST_MULTI)
        {
            // Only apply to path with largest envelope area
            // TODO: consider using true area for polygon types
            if (type == geometry::geometry_types::MultiPolygon)
            {
                geometry::multi_polygon const& multi_poly = mapnik::util::get<geometry::multi_polygon>(geom);
                double maxarea = 0;
                geometry::polygon const* largest = 0;
                for (geometry::polygon const& poly : multi_poly)
                {
                    box2d<double> bbox = geometry::envelope(poly);
                    geometry::polygon_vertex_adapter va(poly);
                    double area = bbox.width() * bbox.height();
                    if (area > maxarea)
                    {
                        maxarea = area;
                        largest = &poly;
                    }
                }
                if (largest)
                {
                    geometry::polygon_vertex_adapter va(*largest);
                    converter.apply(va);
                }
            }
            else
            {
                MAPNIK_LOG_WARN(marker_symbolizer) << "TODO: if you get here -> open an issue";
            }
        }
        else
        {
            if (multi_policy != MARKER_EACH_MULTI && placement != MARKER_POINT_PLACEMENT)
            {
                MAPNIK_LOG_WARN(marker_symbolizer) << "marker_multi_policy != 'each' has no effect with marker_placement != 'point'";
            }
            apply_vertex_converter_type apply(converter);
            mapnik::util::apply_visitor(vertex_processor_type(apply), geom);
        }
    }
}

}

#endif //MAPNIK_MARKER_HELPERS_HPP
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef CRYPT_TOOLS_HXX
#define CRYPT_TOOLS_HXX

#include <config_oox.h>

#include <rtl/ustring.hxx>

#if USE_TLS_OPENSSL
#include <openssl/evp.h>
#include <openssl/sha.h>
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
#include <nss.h>
#include <pk11pub.h>
#endif // USE_TLS_NSS

#include <rtl/digest.h>
#include <vector>

namespace oox {
namespace core {

class Crypto
{
public:
    enum CryptoType
    {
        UNKNOWN,
        AES_128_ECB,
        AES_128_CBC,
        AES_256_CBC,
    };

protected:
#if USE_TLS_OPENSSL
    EVP_CIPHER_CTX mContext;
#endif
#if USE_TLS_NSS
    PK11Context* mContext;
    SECItem*     mSecParam;
    PK11SymKey*  mSymKey;
#endif
    CryptoType mType;

#if USE_TLS_OPENSSL
    const EVP_CIPHER* getCipher(CryptoType type);
#endif
#if USE_TLS_NSS
    void setupContext(
            std::vector<sal_uInt8>& key,
            std::vector<sal_uInt8>& iv,
            CryptoType type,
            CK_ATTRIBUTE_TYPE operation);
#endif

public:
    Crypto(CryptoType type);

    virtual ~Crypto();

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0) = 0;
};

class Decrypt : public Crypto
{
public:
    Decrypt(std::vector<sal_uInt8>& key, CryptoType type);
    Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0);

    static sal_uInt32 aes128ecb(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    std::vector<sal_uInt8>& key );

    static sal_uInt32 aes128cbc(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    std::vector<sal_uInt8>& key,
                    std::vector<sal_uInt8>& iv );
};

class Encrypt : public Crypto
{
public:
    Encrypt(std::vector<sal_uInt8>& key, CryptoType type);
    Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0);
};

const sal_uInt32 SHA1_LENGTH    = 20;
const sal_uInt32 SHA512_LENGTH  = 64;

bool sha1( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input );

bool sha512( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input );

} // namespace core
} // namespace oox

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>CID#1079343 mContext.cipher is not initialized...<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef CRYPT_TOOLS_HXX
#define CRYPT_TOOLS_HXX

#include <config_oox.h>

#include <rtl/ustring.hxx>

#if USE_TLS_OPENSSL
#include <openssl/evp.h>
#include <openssl/sha.h>
#endif // USE_TLS_OPENSSL
#if USE_TLS_NSS
#include <nss.h>
#include <pk11pub.h>
#endif // USE_TLS_NSS

#include <rtl/digest.h>
#include <vector>

namespace oox {
namespace core {

class Crypto
{
public:
    enum CryptoType
    {
        UNKNOWN,
        AES_128_ECB,
        AES_128_CBC,
        AES_256_CBC,
    };

protected:
#if USE_TLS_OPENSSL
    EVP_CIPHER_CTX mContext;
#endif
#if USE_TLS_NSS
    PK11Context* mContext;
    SECItem*     mSecParam;
    PK11SymKey*  mSymKey;
#endif
    CryptoType mType;

#if USE_TLS_OPENSSL
    const EVP_CIPHER* getCipher(CryptoType type);
#endif
#if USE_TLS_NSS
    void setupContext(
            std::vector<sal_uInt8>& key,
            std::vector<sal_uInt8>& iv,
            CryptoType type,
            CK_ATTRIBUTE_TYPE operation);
#endif

protected:
    Crypto(CryptoType type);

public:
    virtual ~Crypto();

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0) = 0;
};

class Decrypt : public Crypto
{
public:
    Decrypt(std::vector<sal_uInt8>& key, CryptoType type);
    Decrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0);

    static sal_uInt32 aes128ecb(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    std::vector<sal_uInt8>& key );

    static sal_uInt32 aes128cbc(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    std::vector<sal_uInt8>& key,
                    std::vector<sal_uInt8>& iv );
};

class Encrypt : public Crypto
{
public:
    Encrypt(std::vector<sal_uInt8>& key, CryptoType type);
    Encrypt(std::vector<sal_uInt8>& key, std::vector<sal_uInt8>& iv, CryptoType type);

    virtual sal_uInt32 update(
                    std::vector<sal_uInt8>& output,
                    std::vector<sal_uInt8>& input,
                    sal_uInt32 inputLength = 0);
};

const sal_uInt32 SHA1_LENGTH    = 20;
const sal_uInt32 SHA512_LENGTH  = 64;

bool sha1( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input );

bool sha512( std::vector<sal_uInt8>& output, std::vector<sal_uInt8>& input );

} // namespace core
} // namespace oox

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>#ifndef SERENITY_HTTP_REQUEST_HPP_
#define SERENITY_HTTP_REQUEST_HPP_
#include <string>
#include <map>
#include <iostream>

#include "response.hpp"

namespace serenity { namespace http {

    const unsigned int nothing = 0;
    const unsigned int crlf_01_start = 1;
    const unsigned int crlf_01_end = 2;
    const unsigned int crlf_02_start = 3;
    const unsigned int crlf_02_end = 4;
    const unsigned int post_end = 5;
    const uint32_t initial_data_sz = 128000;

    template <typename Enumeration>
    auto as_integer(Enumeration const value) -> typename std::underlying_type<Enumeration>::type
    {
        return static_cast<typename std::underlying_type<Enumeration>::type>(value);
    }

    class request {
        public:
            request() { data_ = (char *)malloc(initial_data_sz); }
            ~request() { if (data_) free(data_); }
            /** \brief Method used for request: GET, PUT, DELETE, etc. */
            std::string method = "";

            /** \brief The requested URI, including parameters */
            std::string uri = ""; // Convert to uri class from cpp-net

            /** \brief The requested function name */
            std::string function = ""; // First token after the service resolution info.

            /** \brief Extra URI arguments, path elements after the first token. */
            std::string extra_path = "";

            /** \brief All of the HTTP headers provided by the client. */
            std::map<std::string, std::string> headers;

            /** \brief All of the parameters supplied for the request. */
            std::map<std::string, std::string> parameters;

            /** \brief Major HTTP version (ie 1) */
            uint8_t version_major = 0;

            /** \brief Minor HTTP version (ie 0) */
            uint8_t version_minor = 0;

            /** \brief Raw bytes of the extra data provided by the client in the request */
            std::string post_data = "";

            /** \brief Add data to the request for parsing. */
            void add_data(const char *request_data, std::size_t bytes) {
                //std::cerr << "Data (" << bytes << "):" << std::endl
                //    << request_data << std::endl;
                if ((data_end_ + bytes) > data_sz_) {
                    data_ = (char *)realloc(data_, data_end_ + bytes + 1);
                }
                memcpy(data_ + data_end_, request_data, bytes);
                data_end_ += bytes;
                data_ptr_ = (data_ + data_end_) - bytes;

                if (headers_complete_)  {
                    // If we're done with the headers, go straight to parse()
                    parse();
                    return;
                }

                int i = 0;
                for (; (i < bytes) && (parse_state_ < post_end); ++i) {
                    switch (parse_state_) {
                        case nothing:
                        case crlf_01_end:
                            if (request_data[i] == '\r') ++parse_state_;
                            else parse_state_ = nothing;
                            break;
                        case crlf_01_start:
                        case crlf_02_start:
                            if (request_data[i] == '\n') ++parse_state_;
                            else parse_state_ = nothing;
                            break;
                        case crlf_02_end:
                            ++parse_state_;
                            break;
                    }
                }
                headers_complete_ = (parse_state_ >= crlf_02_end);
                if (headers_complete_) {
                    parse();
                }
            }

            bool is_complete() {
                return is_complete_;
            }

            bool is_error() {
                return is_error_;
            }

            /** \brief Parses the provided data as an HTTP request, and populates the current object. */
            bool parse() {
                parser_state next_state = parser_state::start;
                char const *token_start = nullptr;
                std::string variable;
                std::string value;
                for (const char *p = data_ptr_; (p - data_) < data_end_; ++p) {
                    switch (parser_state_) {
                        case parser_state::start:
                            if (!set_error( (*p < 'A' || *p > 'Z') )) {
                                parser_state_ = parser_state::method;
                                token_start = p;
                            }
                            break;
                        case parser_state::method:
                            if (!set_error( (*p < 'A' || *p > 'Z') && *p != ' ' )) {
                                if (*p == ' ') {
                                    method = std::string(token_start, p - token_start);
                                    parser_state_ = parser_state::uri;
                                    token_start = p + 1; // +1 to move past ' '.
                                    //std::cerr << "[parse] Method: " << method << std::endl;
                                }
                            }
                            break;
                        case parser_state::uri:
                            if (!set_error( (*p < 32 || *p > 126) )) { // Accept all printables.
                                if (*p == '?') { // End of URI, start of params
                                    uri = decode_url(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::uri_parameters;
                                    token_start = p + 1; // Move past '?'
                                    //std::cerr << "[parse] URI: " << uri << std::endl;
                                }
                                else if (*p == ' ') { // End of URI, start of version
                                    uri = decode_url(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::http_version_HTTP;
                                    //std::cerr << "[parse] URI: " << uri << std::endl;
                                }
                            }
                            break;
                        case parser_state::uri_parameters:
                            if (*p == '=') {
                                variable = decode_url(std::string(token_start, p - token_start));
                                token_start = p + 1; // Move beyond '='
                            }
                            else if (*p == '&' || *p == ' ') {
                                value = decode_url(std::string(token_start, p - token_start));
                                if (variable.size() > 0)
                                    parameters[variable] = value;
                                else if (value.size() > 0)
                                    parameters[value] = "";
                                //std::cerr << "[parse] param: " << variable << " = " << value << std::endl;

                                if (*p == ' ')
                                    parser_state_ = parser_state::http_version_HTTP;

                                token_start = p + 1; // Move beyond '&'
                            }
                            else if (*p == '\r' || *p == '\n')
                                parser_state_ = parser_state::error;
                            break;
                        case parser_state::http_version_HTTP:
                            //std::cerr << "[parse] parsing HTTP: " << *p << std::endl;
                            // TODO: Proper HTTP sequence should be determined.
                            if (!set_error( (*p != 'H' && *p != 'T' && *p != 'P') && *p != '/')) {
                                if (*p == '/') {
                                    parser_state_ = parser_state::http_version_major;
                                    token_start = p + 1; // +1 to move past '/'
                                }
                            }
                            break;
                        case parser_state::http_version_major:
                            //std::cerr << "[parse] parsing HTTP major: " << *p << std::endl;
                            if (!set_error( (*p < '0' || *p > '9') && (*p != '.') )) {
                                if ('.' == *p) {
                                    version_major = std::stol(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::http_version_minor;
                                    token_start = p + 1; // +1 to move past '.'
                                }
                            }
                            break;
                        case parser_state::http_version_minor:
                            //std::cerr << "[parse] parsing HTTP minor: " << *p << std::endl;
                            if (!set_error( (*p < '0' || *p > '9') && (*p != '\r') )) {
                                if ('\r' == *p) {
                                    version_minor = std::stol(std::string(token_start, p - token_start));
                                    token_start = p + 2; // Move past "\r\n"

                                    next_state = parser_state::header_name;
                                    parser_state_ = parser_state::end_of_line;
                                }
                            }
                            break;
                        case parser_state::header_name:
                            if (*p == '\r') {
                                parser_state_ = parser_state::end_of_line;
                                next_state = parser_state::post_data;
                                //post_data_start_ptr_ = p + 2; // move past \r\n
                                post_data_start_offset_ = (p - data_) + 2;
                                post_data.clear();
                                header_length_ = &data_[post_data_start_offset_] - data_ptr_;
                            }
                            else if (!set_error(!identifier_char(*p))) {
                                if (*p == ':') {
                                    variable = std::string(token_start, p - token_start);
                                    token_start = p + 2; // Move past ": "
                                    parser_state_ = parser_state::header_value;
                                }
                            }
                            break;
                        case parser_state::header_value:
                            if (*p == '\r') {
                                value = std::string(token_start, p - token_start);
                                next_state = parser_state::header_name;
                                parser_state_ = parser_state::end_of_line;

                                token_start = p + 2; // Move past "\r\n"
                                headers[variable] = value;
                                //std::cerr << "[parse] " << variable << " = " << value << std::endl;
                            }
                            break;
                        case parser_state::end_of_line:
                            //std::cerr << "[parser] parsing end of line: " << *p << std::endl;
                            if (!set_error( *p != '\n' )) {
                                //std::cerr << "[parser] EOL parsed" << std::endl;
                                parser_state_ = next_state;
                                token_start = p + 1; // Move past '\n'
                            }
                            break;
                        default:
                            break;
                    }
                    if (is_error_) {
                        //std::cerr << "[parse] ERROR - previous state: " << as_integer(state) << std::endl;
                        parser_state_ = parser_state::error;
                        break;
                    }
                }
                if (!is_complete_ && parser_state_ == parser_state::post_data &&
                        headers.find("Content-Length") != headers.end()) {
                    // This variable is for debugging puposes.
                    uint32_t content_length =
                        strtoul(headers["Content-Length"].c_str(), NULL, 0);
                    if (data_end_ - header_length_ >= content_length) {
                        post_data = std::string(&data_[post_data_start_offset_], content_length);
                        is_complete_ = true;
                    }
                }
                else {
                    is_complete_ = true;
                    data_end_ = 0;
                }

                return !is_error_;
            }

        private:
            enum class parser_state {
                start,
                method,
                uri,
                uri_parameters,
                http_version_HTTP,
                http_version_major,
                http_version_minor,
                header_name,
                header_value,
                end_of_request_maybe,
                post_data,
                error,
                end_of_line
            };

            // TODO:  Make this dynamic without the potential for uploading
            // huge files.
            //std::array<char, 2000000> data_;
            char *data_ = nullptr;
            char *data_ptr_ = data_;
            //const char *post_data_start_ptr_ = nullptr;
            std::size_t post_data_start_offset_ = 0;
            std::size_t data_end_ = 0;
            std::size_t header_length_ = 0;
            std::size_t data_sz_ = initial_data_sz;
            unsigned int parse_state_ = 0;
            bool is_complete_ = false;
            bool headers_complete_ = false;
            bool is_error_ = false;
            parser_state parser_state_ = parser_state::start;

            inline bool set_error(bool is_error) { return (is_error_ = is_error); }
            bool identifier_char(char c) {
                std::string valid_chars = "abcdefghijklmnopqrstuvwxyz0123456789-:";
                auto p = valid_chars.find_first_of(::tolower(c));
                return (p != std::string::npos);
            }

            std::string decode_url(const std::string &str) {
                char hex[3] = { 0, 0, 0 };
                bool in_hex = false;
                std::string decoded = "";
                for (int i=0; i < str.size(); ++i) {
                    if (in_hex) {
                        if (hex[0] == '\0')
                            hex[0] = str[i];
                        else {
                            hex[1] = str[i];
                            in_hex = false;
                            char c = std::strtol(hex, nullptr, 16);
                            decoded += c;
                        }
                    }
                    else if (str[i] != '%')
                        decoded += str[i];
                    else
                        in_hex = true;
                }
                return decoded;
            }
    };

} /* http */ } /* serenity */

#endif /* end of include guard: SERENITY_HTTP_REQUEST_HPP_ */
<commit_msg>Added a proper copy constructor for the request class, since the default was doing the wrong thing with the new data_ char * and causing a sefgault.<commit_after>#ifndef SERENITY_HTTP_REQUEST_HPP_
#define SERENITY_HTTP_REQUEST_HPP_
#include <string>
#include <map>
#include <iostream>

#include "response.hpp"

namespace serenity { namespace http {

    const unsigned int nothing = 0;
    const unsigned int crlf_01_start = 1;
    const unsigned int crlf_01_end = 2;
    const unsigned int crlf_02_start = 3;
    const unsigned int crlf_02_end = 4;
    const unsigned int post_end = 5;
    const uint32_t initial_data_sz = 128000;

    template <typename Enumeration>
    auto as_integer(Enumeration const value) -> typename std::underlying_type<Enumeration>::type
    {
        return static_cast<typename std::underlying_type<Enumeration>::type>(value);
    }

    class request {
        public:
            request() { data_ = (char *)malloc(initial_data_sz); }
            ~request() { if (data_) { free(data_); data_ = nullptr; } }

            request(const request &r) {
                data_ = nullptr;
                data_ptr_ = data_;
                post_data_start_offset_ = 0;
                data_end_ = 0;
                header_length_ = 0;
                data_sz_ = initial_data_sz;
                parse_state_ = 0;
                is_complete_ = false;
                headers_complete_ = false;
                is_error_ = false;
                parser_state_ = parser_state::start;

                method = r.method;
                uri = r.uri;
                function = r.function;
                extra_path = r.extra_path;
                headers = r.headers;
                parameters = r.parameters;
                version_major = r.version_major;
                version_minor = r.version_minor;
                post_data = r.post_data;
            }

            /** \brief Method used for request: GET, PUT, DELETE, etc. */
            std::string method = "";

            /** \brief The requested URI, including parameters */
            std::string uri = ""; // Convert to uri class from cpp-net

            /** \brief The requested function name */
            std::string function = ""; // First token after the service resolution info.

            /** \brief Extra URI arguments, path elements after the first token. */
            std::string extra_path = "";

            /** \brief All of the HTTP headers provided by the client. */
            std::map<std::string, std::string> headers;

            /** \brief All of the parameters supplied for the request. */
            std::map<std::string, std::string> parameters;

            /** \brief Major HTTP version (ie 1) */
            uint8_t version_major = 0;

            /** \brief Minor HTTP version (ie 0) */
            uint8_t version_minor = 0;

            /** \brief Raw bytes of the extra data provided by the client in the request */
            std::string post_data = "";

            /** \brief Add data to the request for parsing. */
            void add_data(const char *request_data, std::size_t bytes) {
                //std::cerr << "Data (" << bytes << "):" << std::endl
                //    << request_data << std::endl;
                if ((data_end_ + bytes) > data_sz_) {
                    data_ = (char *)realloc(data_, data_end_ + bytes + 1);
                }
                memcpy(data_ + data_end_, request_data, bytes);
                data_end_ += bytes;
                data_ptr_ = (data_ + data_end_) - bytes;

                if (headers_complete_)  {
                    // If we're done with the headers, go straight to parse()
                    parse();
                    return;
                }

                int i = 0;
                for (; (i < bytes) && (parse_state_ < post_end); ++i) {
                    switch (parse_state_) {
                        case nothing:
                        case crlf_01_end:
                            if (request_data[i] == '\r') ++parse_state_;
                            else parse_state_ = nothing;
                            break;
                        case crlf_01_start:
                        case crlf_02_start:
                            if (request_data[i] == '\n') ++parse_state_;
                            else parse_state_ = nothing;
                            break;
                        case crlf_02_end:
                            ++parse_state_;
                            break;
                    }
                }
                headers_complete_ = (parse_state_ >= crlf_02_end);
                if (headers_complete_) {
                    parse();
                }
            }

            bool is_complete() {
                return is_complete_;
            }

            bool is_error() {
                return is_error_;
            }

            /** \brief Parses the provided data as an HTTP request, and populates the current object. */
            bool parse() {
                parser_state next_state = parser_state::start;
                char const *token_start = nullptr;
                std::string variable;
                std::string value;
                for (const char *p = data_ptr_; (p - data_) < data_end_; ++p) {
                    switch (parser_state_) {
                        case parser_state::start:
                            if (!set_error( (*p < 'A' || *p > 'Z') )) {
                                parser_state_ = parser_state::method;
                                token_start = p;
                            }
                            break;
                        case parser_state::method:
                            if (!set_error( (*p < 'A' || *p > 'Z') && *p != ' ' )) {
                                if (*p == ' ') {
                                    method = std::string(token_start, p - token_start);
                                    parser_state_ = parser_state::uri;
                                    token_start = p + 1; // +1 to move past ' '.
                                    //std::cerr << "[parse] Method: " << method << std::endl;
                                }
                            }
                            break;
                        case parser_state::uri:
                            if (!set_error( (*p < 32 || *p > 126) )) { // Accept all printables.
                                if (*p == '?') { // End of URI, start of params
                                    uri = decode_url(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::uri_parameters;
                                    token_start = p + 1; // Move past '?'
                                    //std::cerr << "[parse] URI: " << uri << std::endl;
                                }
                                else if (*p == ' ') { // End of URI, start of version
                                    uri = decode_url(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::http_version_HTTP;
                                    //std::cerr << "[parse] URI: " << uri << std::endl;
                                }
                            }
                            break;
                        case parser_state::uri_parameters:
                            if (*p == '=') {
                                variable = decode_url(std::string(token_start, p - token_start));
                                token_start = p + 1; // Move beyond '='
                            }
                            else if (*p == '&' || *p == ' ') {
                                value = decode_url(std::string(token_start, p - token_start));
                                if (variable.size() > 0)
                                    parameters[variable] = value;
                                else if (value.size() > 0)
                                    parameters[value] = "";
                                //std::cerr << "[parse] param: " << variable << " = " << value << std::endl;

                                if (*p == ' ')
                                    parser_state_ = parser_state::http_version_HTTP;

                                token_start = p + 1; // Move beyond '&'
                            }
                            else if (*p == '\r' || *p == '\n')
                                parser_state_ = parser_state::error;
                            break;
                        case parser_state::http_version_HTTP:
                            //std::cerr << "[parse] parsing HTTP: " << *p << std::endl;
                            // TODO: Proper HTTP sequence should be determined.
                            if (!set_error( (*p != 'H' && *p != 'T' && *p != 'P') && *p != '/')) {
                                if (*p == '/') {
                                    parser_state_ = parser_state::http_version_major;
                                    token_start = p + 1; // +1 to move past '/'
                                }
                            }
                            break;
                        case parser_state::http_version_major:
                            //std::cerr << "[parse] parsing HTTP major: " << *p << std::endl;
                            if (!set_error( (*p < '0' || *p > '9') && (*p != '.') )) {
                                if ('.' == *p) {
                                    version_major = std::stol(std::string(token_start, p - token_start));
                                    parser_state_ = parser_state::http_version_minor;
                                    token_start = p + 1; // +1 to move past '.'
                                }
                            }
                            break;
                        case parser_state::http_version_minor:
                            //std::cerr << "[parse] parsing HTTP minor: " << *p << std::endl;
                            if (!set_error( (*p < '0' || *p > '9') && (*p != '\r') )) {
                                if ('\r' == *p) {
                                    version_minor = std::stol(std::string(token_start, p - token_start));
                                    token_start = p + 2; // Move past "\r\n"

                                    next_state = parser_state::header_name;
                                    parser_state_ = parser_state::end_of_line;
                                }
                            }
                            break;
                        case parser_state::header_name:
                            if (*p == '\r') {
                                parser_state_ = parser_state::end_of_line;
                                next_state = parser_state::post_data;
                                //post_data_start_ptr_ = p + 2; // move past \r\n
                                post_data_start_offset_ = (p - data_) + 2;
                                post_data.clear();
                                header_length_ = &data_[post_data_start_offset_] - data_ptr_;
                            }
                            else if (!set_error(!identifier_char(*p))) {
                                if (*p == ':') {
                                    variable = std::string(token_start, p - token_start);
                                    token_start = p + 2; // Move past ": "
                                    parser_state_ = parser_state::header_value;
                                }
                            }
                            break;
                        case parser_state::header_value:
                            if (*p == '\r') {
                                value = std::string(token_start, p - token_start);
                                next_state = parser_state::header_name;
                                parser_state_ = parser_state::end_of_line;

                                token_start = p + 2; // Move past "\r\n"
                                headers[variable] = value;
                                //std::cerr << "[parse] " << variable << " = " << value << std::endl;
                            }
                            break;
                        case parser_state::end_of_line:
                            //std::cerr << "[parser] parsing end of line: " << *p << std::endl;
                            if (!set_error( *p != '\n' )) {
                                //std::cerr << "[parser] EOL parsed" << std::endl;
                                parser_state_ = next_state;
                                token_start = p + 1; // Move past '\n'
                            }
                            break;
                        default:
                            break;
                    }
                    if (is_error_) {
                        //std::cerr << "[parse] ERROR - previous state: " << as_integer(state) << std::endl;
                        parser_state_ = parser_state::error;
                        break;
                    }
                }
                if (!is_complete_ && parser_state_ == parser_state::post_data &&
                        headers.find("Content-Length") != headers.end()) {
                    // This variable is for debugging puposes.
                    uint32_t content_length =
                        strtoul(headers["Content-Length"].c_str(), NULL, 0);
                    if (data_end_ - header_length_ >= content_length) {
                        post_data = std::string(&data_[post_data_start_offset_], content_length);
                        is_complete_ = true;
                    }
                }
                else {
                    is_complete_ = true;
                    data_end_ = 0;
                }

                return !is_error_;
            }

        private:
            enum class parser_state {
                start,
                method,
                uri,
                uri_parameters,
                http_version_HTTP,
                http_version_major,
                http_version_minor,
                header_name,
                header_value,
                end_of_request_maybe,
                post_data,
                error,
                end_of_line
            };

            // TODO:  Make this dynamic without the potential for uploading
            // huge files.
            //std::array<char, 2000000> data_;
            char *data_ = nullptr;
            char *data_ptr_ = data_;
            //const char *post_data_start_ptr_ = nullptr;
            std::size_t post_data_start_offset_ = 0;
            std::size_t data_end_ = 0;
            std::size_t header_length_ = 0;
            std::size_t data_sz_ = initial_data_sz;
            unsigned int parse_state_ = 0;
            bool is_complete_ = false;
            bool headers_complete_ = false;
            bool is_error_ = false;
            parser_state parser_state_ = parser_state::start;

            inline bool set_error(bool is_error) { return (is_error_ = is_error); }
            bool identifier_char(char c) {
                std::string valid_chars = "abcdefghijklmnopqrstuvwxyz0123456789-:";
                auto p = valid_chars.find_first_of(::tolower(c));
                return (p != std::string::npos);
            }

            std::string decode_url(const std::string &str) {
                char hex[3] = { 0, 0, 0 };
                bool in_hex = false;
                std::string decoded = "";
                for (int i=0; i < str.size(); ++i) {
                    if (in_hex) {
                        if (hex[0] == '\0')
                            hex[0] = str[i];
                        else {
                            hex[1] = str[i];
                            in_hex = false;
                            char c = std::strtol(hex, nullptr, 16);
                            decoded += c;
                        }
                    }
                    else if (str[i] != '%')
                        decoded += str[i];
                    else
                        in_hex = true;
                }
                return decoded;
            }

            void operator =(const request &o) = delete;
    };

} /* http */ } /* serenity */

#endif /* end of include guard: SERENITY_HTTP_REQUEST_HPP_ */
<|endoftext|>