<commit_before>#ifndef SRC_NODES_BASE_NODE_HPP_
#define SRC_NODES_BASE_NODE_HPP_

#include <flexcore/extended/node_fwd.hpp>
#include <flexcore/ports.hpp>
#include <adobe/forest.hpp>

#include <cassert>
#include <string>
#include <memory>

namespace fc
{
class graph_node : public node
{
public:
	graph_node(graph::connection_graph& graph, const std::string& name)
	    : graph_node(graph, {}, name)
	{
	}
	graph_node(graph::connection_graph& graph, std::shared_ptr<parallel_region> r,
	           const std::string& name)
	    : region_(std::move(r)), props_(name), graph_(&graph)
	{
		assert(graph_);
	}
	graph::graph_node_properties graph_info() const override
	{
		return props_;
	}
	graph::connection_graph& get_graph() override { return *graph_; }
	std::shared_ptr<parallel_region> region() override { return region_; }
private:
	std::shared_ptr<parallel_region> region_;
	graph::graph_node_properties props_;
	graph::connection_graph* graph_;
};

class tree_node : public node
{
public:
	virtual std::string name() const = 0;
};

using forest_t = adobe::forest<std::unique_ptr<tree_node>>;

struct forest_graph
{
	forest_graph(graph::connection_graph& graph) : graph(graph) {}
	forest_t forest;
	graph::connection_graph& graph;
};

/**
 * \brief base class for nodes contained in forest
 *
 *  Nodes are neither copy_constructyble nor copy_assignable.
 *
 *
 * \invariant region_ != null_ptr
 */
class tree_base_node : public tree_node
{
public:
	template<class data_t> using event_source = ::fc::event_source<data_t>;
	template<class data_t> using event_sink = ::fc::event_sink<data_t>;
	template<class data_t> using state_source = ::fc::state_source<data_t>;
	template<class data_t> using state_sink = ::fc::state_sink<data_t>;

	tree_base_node(forest_graph* fg, std::shared_ptr<parallel_region> r, std::string name);
	std::shared_ptr<parallel_region> region() override { return region_; }
	std::string name() const override;

	graph::graph_node_properties graph_info() const override;
	graph::connection_graph& get_graph() override;

protected:
	forest_graph* fg_;
private:
	/* Information about which region the node belongs to */
	std::shared_ptr<parallel_region> region_;
	/* Information for abstract graph */
	//stores the metainformation of the node used by the abstract graph
	graph::graph_node_properties graph_info_;

};

class owning_base_node;

class owner_holder : public tree_node
{
public:
	void set_owner(std::unique_ptr<owning_base_node> node) { owner_ = std::move(node); }
	std::shared_ptr<parallel_region> region() override;
	graph::graph_node_properties graph_info() const override;
	graph::connection_graph& get_graph() override;
	std::string name() const override;

private:
	std::unique_ptr<owning_base_node> owner_;
};

/**
 * \brief base class for nodes which own other nodes, aka nested nodes.
 *
 * \invariant forest_ != nullptr
 *
 * Nodes of this type may have children nodes.
 *
 * Use make_child/make_child_named to create a node already inserted into
 * the ownership tree.
 *
 * Node creation examples:
 * \code{cpp}
 * owning_base_node root;
 * // simple creation
 * root.make_child<node>();
 * root.make_child<node_tmpl<int>>();
 * root.make_child_named<node_tmpl<int>>("name");
 * \endcode
 */
class owning_base_node : public tree_base_node
{
public:
	owning_base_node(forest_graph* fg, std::shared_ptr<parallel_region> r, std::string name)
	    : tree_base_node(fg, r, name)
	{
	}
	owning_base_node(const tree_base_node& node)
		: tree_base_node(node)
	{
	}

	tree_base_node* new_node(std::string name)
	{
		return static_cast<tree_base_node*>(
		    add_child(std::make_unique<tree_base_node>(fg_, region(), name)));
	}
	tree_base_node* new_node(std::shared_ptr<parallel_region> r, std::string name)
	{
		return static_cast<tree_base_node*>(
		    add_child(std::make_unique<tree_base_node>(fg_, r, name)));
	}
	/**
	 * \brief creates child node of type node_t with constructor arguments args.
	 *
	 * Inserts new child into tree.
	 * Child node is attached to region of parent node.
	 *
	 * \tparam node_t type of node to be created
	 * \param args constructor arguments passed to node_t
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, region(), node_t::default_name})));
	}

	/**
	 * \brief  Overload of make_child with region.
	 *
	 * @param r Child is attached to this region
	 * @param args onstructor arguments passed to node_t
	 * @return pointer to child node
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(std::shared_ptr<parallel_region> r, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, r, node_t::default_name})));
	}

	/**
	 * \brief Creates a new child node of type node_t from args
	 *
	 * Sets name of child to n and inserts child into tree.
	 * Sets forest of of child to forest of parent.
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child_named(std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, region(), name})));
	}

	template<class node_t, class ... args_t>
	node_t* make_child_named(std::shared_ptr<parallel_region> r, std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, std::move(r), std::move(name)})));
	}

protected:
	forest_t::iterator self() const;
	// stores the access to the forest this node is contained in.
private:
	/**
	 * Takes ownership of child node and inserts into tree.
	 * \return pointer to child node
	 * \pre child != nullptr
	 */
	tree_node* add_child(std::unique_ptr<tree_node> child);

};

/**
 * \brief Root node for building node trees.
 *
 * Has ownership of the forest and thus serves
 * as the root node for all other nodes in the forest.
 */
class forest_owner
{
public:
	forest_owner(graph::connection_graph& graph, std::string n, std::shared_ptr<parallel_region> r);
	owning_base_node& nodes() { return *tree_root; }

private:
	std::unique_ptr<forest_graph> fg_;
	/// non_owning access to first node in tree, ownership is in forest.
	owning_base_node* tree_root;
};

/**
 * \brief Erases node and recursively erases all children.
 *
 * \param forest, forest to delete node from.
 * \position iterator of forest pointing to node.
 * \pre position must be in forest.
 * \returns todo
 *
 * invalidates iterators pointing to deleted node.
 */
inline forest_t::iterator
erase_with_subtree(
		forest_t& forest,
		forest_t::iterator position)
{
	return forest.erase(
			adobe::child_begin(position).base(),
			adobe::child_end(position).base());
}

/**
 * \brief Returns the full name of a node.
 *
 * The full name consists of the chained name of the nodes parent, grandparent etc.
 * and the name of the node itself.
 * The names are separated by a separation token.
 */
std::string full_name(forest_t& forest, const tree_node* node);

} // namespace fc

#endif /* SRC_NODES_BASE_NODE_HPP_ */
<commit_msg>nodes: Fix erase_with_subtree<commit_after>#ifndef SRC_NODES_BASE_NODE_HPP_
#define SRC_NODES_BASE_NODE_HPP_

#include <flexcore/extended/node_fwd.hpp>
#include <flexcore/ports.hpp>
#include <adobe/forest.hpp>

#include <cassert>
#include <string>
#include <memory>

namespace fc
{
class graph_node : public node
{
public:
	graph_node(graph::connection_graph& graph, const std::string& name)
	    : graph_node(graph, {}, name)
	{
	}
	graph_node(graph::connection_graph& graph, std::shared_ptr<parallel_region> r,
	           const std::string& name)
	    : region_(std::move(r)), props_(name), graph_(&graph)
	{
		assert(graph_);
	}
	graph::graph_node_properties graph_info() const override
	{
		return props_;
	}
	graph::connection_graph& get_graph() override { return *graph_; }
	std::shared_ptr<parallel_region> region() override { return region_; }
private:
	std::shared_ptr<parallel_region> region_;
	graph::graph_node_properties props_;
	graph::connection_graph* graph_;
};

class tree_node : public node
{
public:
	virtual std::string name() const = 0;
};

using forest_t = adobe::forest<std::unique_ptr<tree_node>>;

struct forest_graph
{
	forest_graph(graph::connection_graph& graph) : graph(graph) {}
	forest_t forest;
	graph::connection_graph& graph;
};

/**
 * \brief base class for nodes contained in forest
 *
 *  Nodes are neither copy_constructyble nor copy_assignable.
 *
 *
 * \invariant region_ != null_ptr
 */
class tree_base_node : public tree_node
{
public:
	template<class data_t> using event_source = ::fc::event_source<data_t>;
	template<class data_t> using event_sink = ::fc::event_sink<data_t>;
	template<class data_t> using state_source = ::fc::state_source<data_t>;
	template<class data_t> using state_sink = ::fc::state_sink<data_t>;

	tree_base_node(forest_graph* fg, std::shared_ptr<parallel_region> r, std::string name);
	std::shared_ptr<parallel_region> region() override { return region_; }
	std::string name() const override;

	graph::graph_node_properties graph_info() const override;
	graph::connection_graph& get_graph() override;

protected:
	forest_graph* fg_;
private:
	/* Information about which region the node belongs to */
	std::shared_ptr<parallel_region> region_;
	/* Information for abstract graph */
	//stores the metainformation of the node used by the abstract graph
	graph::graph_node_properties graph_info_;

};

class owning_base_node;

class owner_holder : public tree_node
{
public:
	void set_owner(std::unique_ptr<owning_base_node> node) { owner_ = std::move(node); }
	std::shared_ptr<parallel_region> region() override;
	graph::graph_node_properties graph_info() const override;
	graph::connection_graph& get_graph() override;
	std::string name() const override;

private:
	std::unique_ptr<owning_base_node> owner_;
};

/**
 * \brief base class for nodes which own other nodes, aka nested nodes.
 *
 * \invariant forest_ != nullptr
 *
 * Nodes of this type may have children nodes.
 *
 * Use make_child/make_child_named to create a node already inserted into
 * the ownership tree.
 *
 * Node creation examples:
 * \code{cpp}
 * owning_base_node root;
 * // simple creation
 * root.make_child<node>();
 * root.make_child<node_tmpl<int>>();
 * root.make_child_named<node_tmpl<int>>("name");
 * \endcode
 */
class owning_base_node : public tree_base_node
{
public:
	owning_base_node(forest_graph* fg, std::shared_ptr<parallel_region> r, std::string name)
	    : tree_base_node(fg, r, name)
	{
	}
	owning_base_node(const tree_base_node& node)
		: tree_base_node(node)
	{
	}

	tree_base_node* new_node(std::string name)
	{
		return static_cast<tree_base_node*>(
		    add_child(std::make_unique<tree_base_node>(fg_, region(), name)));
	}
	tree_base_node* new_node(std::shared_ptr<parallel_region> r, std::string name)
	{
		return static_cast<tree_base_node*>(
		    add_child(std::make_unique<tree_base_node>(fg_, r, name)));
	}
	/**
	 * \brief creates child node of type node_t with constructor arguments args.
	 *
	 * Inserts new child into tree.
	 * Child node is attached to region of parent node.
	 *
	 * \tparam node_t type of node to be created
	 * \param args constructor arguments passed to node_t
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, region(), node_t::default_name})));
	}

	/**
	 * \brief  Overload of make_child with region.
	 *
	 * @param r Child is attached to this region
	 * @param args onstructor arguments passed to node_t
	 * @return pointer to child node
	 */
	template<class node_t, class ... args_t>
	node_t* make_child(std::shared_ptr<parallel_region> r, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, r, node_t::default_name})));
	}

	/**
	 * \brief Creates a new child node of type node_t from args
	 *
	 * Sets name of child to n and inserts child into tree.
	 * Sets forest of of child to forest of parent.
	 * \return pointer to child node
	 * \post nr of children > 0
	 */
	template<class node_t, class ... args_t>
	node_t* make_child_named(std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, region(), name})));
	}

	template<class node_t, class ... args_t>
	node_t* make_child_named(std::shared_ptr<parallel_region> r, std::string name, args_t&&... args)
	{
		return static_cast<node_t*>(add_child(std::make_unique<node_t>(
				std::forward<args_t>(args)...,
				tree_base_node{fg_, std::move(r), std::move(name)})));
	}

protected:
	forest_t::iterator self() const;
	// stores the access to the forest this node is contained in.
private:
	/**
	 * Takes ownership of child node and inserts into tree.
	 * \return pointer to child node
	 * \pre child != nullptr
	 */
	tree_node* add_child(std::unique_ptr<tree_node> child);

};

/**
 * \brief Root node for building node trees.
 *
 * Has ownership of the forest and thus serves
 * as the root node for all other nodes in the forest.
 */
class forest_owner
{
public:
	forest_owner(graph::connection_graph& graph, std::string n, std::shared_ptr<parallel_region> r);
	owning_base_node& nodes() { return *tree_root; }

private:
	std::unique_ptr<forest_graph> fg_;
	/// non_owning access to first node in tree, ownership is in forest.
	owning_base_node* tree_root;
};

/**
 * \brief Erases node and recursively erases all children.
 *
 * \param forest, forest to delete node from.
 * \position iterator of forest pointing to node.
 * \pre position must be in forest.
 * \returns todo
 *
 * invalidates iterators pointing to deleted node.
 */
inline forest_t::iterator
erase_with_subtree(
		forest_t& forest,
		forest_t::iterator position)
{
	return forest.erase(
			adobe::leading_of(position),
			++adobe::trailing_of(position));
}

/**
 * \brief Returns the full name of a node.
 *
 * The full name consists of the chained name of the nodes parent, grandparent etc.
 * and the name of the node itself.
 * The names are separated by a separation token.
 */
std::string full_name(forest_t& forest, const tree_node* node);

} // namespace fc

#endif /* SRC_NODES_BASE_NODE_HPP_ */
<|endoftext|>


<commit_before>#define SP_USE_DEBUG 1

#include "simplesp.h"
#include "spex/spgl.h"

using namespace sp;

class KinectFusionGUI : public BaseWindow {

private:

    // Kinect Fusion
    KinectFusion m_kfusion;

    CamParam m_cam;

    // image
    Mem2<Col3> m_img;

    // object mesh model
    Mem1<Mesh3> m_model;

    // object to cam pose
    Pose m_pose;

private:

    void help() {
        printf("'r' key : reset kinect fusion\n");
        printf("'ESC' key : exit\n");
        printf("\n");
    }

    virtual void keyFun(int key, int scancode, int action, int mods) {
        if (m_keyAction[GLFW_KEY_R] == 1) {
            m_kfusion.reset();
        }
    }

    virtual void init() {

        help();

        if (0) {
            m_cam = getCamParam(640, 480);
        }
        else {
            m_cam = getCamParam(320, 240);
            m_viewScale *= 1.5;
        }

        m_img.resize(m_cam.dsize);
        m_img.zero();

        if (loadBunny(SP_DATA_DIR "/stanford/bun_zipper.ply", m_model) == false) {

            // if could not find stanford bunny, load dummy model
            loadGeodesicDorm(m_model, 100.0, 1);
        }

        m_pose = getPose(getVec(0.0, 0.0, getModelDistance(m_model, m_cam)));
    }

    virtual void display() {
        Mem2<double> depth;

        // render depth
        {
            static double s = 0.0;
            m_pose *= getRotAngle(getVec(+0.0, +::sin(s), +::cos(s)), 0.02);
            s += 0.01;

            renderDepth(depth, m_cam, m_pose, m_model);
        }

        // kinect fusion
        {

            if (m_kfusion.valid() == false) {
                const double radius = getModelRadius(m_model) * 1.1;

                // voxel size
                const int vsize = 200;

                // voxel unit length [mm]
                const double unit = (2 * radius) / vsize;

                m_kfusion.init(vsize, unit, m_cam, m_pose);
            }

            m_kfusion.execute(depth);
        }

        // render
        {
            glLoadView2D(m_kfusion.getCam(), m_viewPos, m_viewScale);
            glRenderNormal<Byte>(*m_kfusion.getCast());

            glLoadView3D(m_kfusion.getCam(), m_viewPos, m_viewScale);
            glLoadMatrix(*m_kfusion.getPose());

            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glBegin(GL_QUADS);
            glCube(m_kfusion.getMap()->dsize[0] * m_kfusion.getMap()->unit);
            glEnd();
        }

        // render depth
        if(0){
            const double scale = 0.3;

            const Vec2 offset = getVec(m_cam.dsize[0], m_cam.dsize[1]) * (0.5 - scale * 0.5);
            glLoadView2D(m_cam, m_viewPos - offset * m_viewScale, m_viewScale * scale);

            glRenderDepth(depth, m_pose.trn.z - 500.0, m_pose.trn.z + 500.0);
        }
        else {
            Mem2<Col3> img;
            cnvDepthToImg(img, depth, m_pose.trn.z - 500.0, m_pose.trn.z + 500.0);

            glShowImg(this, "input depth map", img);
        }

    }
};

int main(){

    KinectFusionGUI win;
    win.execute("kinectfusion", 800, 600);

    return 0;
}<commit_msg>refactoring<commit_after>#define SP_USE_DEBUG 1

#include "simplesp.h"
#include "spex/spgl.h"

using namespace sp;

class KinectFusionGUI : public BaseWindow {

private:

    // Kinect Fusion
    KinectFusion m_kfusion;

    CamParam m_cam;

    // image
    Mem2<Col3> m_img;

    // object mesh model
    Mem1<Mesh3> m_model;

    // object to cam pose
    Pose m_pose;

private:

    void help() {
        printf("'r' key : reset kinect fusion\n");
        printf("'ESC' key : exit\n");
        printf("\n");
    }

    virtual void init() {

        help();

        if (0) {
            m_cam = getCamParam(640, 480);
        }
        else {
            m_cam = getCamParam(320, 240);
            m_viewScale *= 1.5;
        }

        m_img.resize(m_cam.dsize);
        m_img.zero();

        if (loadBunny(SP_DATA_DIR "/stanford/bun_zipper.ply", m_model) == false) {

            // if could not find stanford bunny, load dummy model
            loadGeodesicDorm(m_model, 100.0, 1);
        }

        m_pose = getPose(getVec(0.0, 0.0, getModelDistance(m_model, m_cam)));
    }

    virtual void keyFun(int key, int scancode, int action, int mods) {
        if (m_keyAction[GLFW_KEY_R] == 1) {
            m_kfusion.reset();
        }
    }

    virtual void display() {
        Mem2<double> depth;

        // render depth
        {
            static double s = 0.0;
            m_pose *= getRotAngle(getVec(+0.0, +::sin(s), +::cos(s)), 0.02);
            s += 0.01;

            renderDepth(depth, m_cam, m_pose, m_model);
        }

        // kinect fusion
        {

            if (m_kfusion.valid() == false) {
                const double radius = getModelRadius(m_model) * 1.1;

                // voxel size
                const int vsize = 200;

                // voxel unit length [mm]
                const double unit = (2 * radius) / vsize;

                // voxel pose
                const Pose pose = getPose(getVec(0.0, 0.0, getModelDistance(m_model, m_cam)));

                m_kfusion.init(vsize, unit, m_cam, pose);
            }

            m_kfusion.execute(depth);
        }

        // render
        {
            glLoadView2D(m_kfusion.getCam(), m_viewPos, m_viewScale);
            glRenderNormal<Byte>(*m_kfusion.getCast());

            glLoadView3D(m_kfusion.getCam(), m_viewPos, m_viewScale);
            glLoadMatrix(*m_kfusion.getPose());

            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glBegin(GL_QUADS);
            glCube(m_kfusion.getMap()->dsize[0] * m_kfusion.getMap()->unit);
            glEnd();
        }

        // render depth
        if(0){
            const double scale = 0.3;

            const Vec2 offset = getVec(m_cam.dsize[0], m_cam.dsize[1]) * (0.5 - scale * 0.5);
            glLoadView2D(m_cam, m_viewPos - offset * m_viewScale, m_viewScale * scale);

            glRenderDepth(depth, m_pose.trn.z - 500.0, m_pose.trn.z + 500.0);
        }
        else {
            Mem2<Col3> img;
            cnvDepthToImg(img, depth, m_pose.trn.z - 500.0, m_pose.trn.z + 500.0);

            glShowImg(this, "input depth map", img);
        }

    }
};

int main(){

    KinectFusionGUI win;
    win.execute("kinectfusion", 800, 600);

    return 0;
}<|endoftext|>


<commit_before>/// @file
/// @author uentity
/// @date 22.11.2017
/// @brief BS tree-related functions impl
/// @copyright
/// 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 https://mozilla.org/MPL/2.0/

#include <bs/tree/tree.h>
#include "tree_impl.h"

#include <set>
#include <boost/uuid/uuid_io.hpp>
#include <boost/algorithm/string.hpp>

NAMESPACE_BEGIN(blue_sky::tree)

template<Key K> using Key_type = typename node::Key_type<K>;

/*-----------------------------------------------------------------------------
 *  impl details
 *-----------------------------------------------------------------------------*/
NAMESPACE_BEGIN()

void walk_impl(
	const std::list<sp_link>& nodes, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links,
	std::set<Key_type<Key::ID>> active_symlinks = {}
) {
	using detail::can_call_dnode;
	sp_node cur_node;
	std::list<sp_link> next_nodes;
	std::vector<sp_link> next_leafs;
	// for each node
	for(const auto& N : nodes) {
		if(!N) continue;
		// remember symlink
		const auto is_symlink = follow_symlinks && N->type_id() == "sym_link";
		if(is_symlink && !active_symlinks.insert(N->id()).second)
			continue;

		// obtain node from link honoring LazyLoad flag
		cur_node = (follow_lazy_links || can_call_dnode(*N)) ? N->data_node() : nullptr;

		next_nodes.clear();
		next_leafs.clear();

		if(cur_node) {
			// for each link in node
			for(const auto& l : *cur_node) {
				// collect nodes
				if((follow_lazy_links || can_call_dnode(*l)) && l->data_node()) {
					next_nodes.push_back(l);
				}
				else
					next_leafs.push_back(l);
			}
		}

		// if `topdown` == true, process this node BEFORE leafs processing
		if(topdown)
			step_f(N, next_nodes, next_leafs);
		// process list of next nodes
		if(!next_nodes.empty())
			walk_impl(next_nodes, step_f, topdown, follow_symlinks, follow_lazy_links, active_symlinks);
		// if walking from most deep subdir, process current node after all subtree
		if(!topdown)
			step_f(N, next_nodes, next_leafs);

		// forget symlink
		if(is_symlink)
			active_symlinks.erase(N->id());
	}
}

void walk_impl(
	const std::list<sp_node>& nodes, walk_nodes_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links,
	std::set<Key_type<Key::ID>> active_symlinks = {}
) {
	using detail::can_call_dnode;
	std::list<sp_node> next_nodes;
	std::vector<sp_link> next_leafs;

	// for each node
	for(const auto& cur_node : nodes) {
		if(!cur_node) continue;

		next_nodes.clear();
		next_leafs.clear();

		// symlinks among cur_node
		std::vector<Key_type<Key::ID>> cur_symlinks;

		// for each link in node
		for(const auto& l : *cur_node) {
			// remember symlinks
			if(follow_symlinks && l->type_id() == "sym_link") {
				if(active_symlinks.insert(l->id()).second)
					cur_symlinks.push_back(l->id());
				else continue;
			}

			// collect nodes
			auto sub_node = follow_lazy_links || can_call_dnode(*l) ? l->data_node() : nullptr;
			if(sub_node)
				next_nodes.push_back(sub_node);
			else
				next_leafs.push_back(l);
		}

		// if `topdown` == true, process this node BEFORE leafs processing
		if(topdown)
			step_f(cur_node, next_nodes, next_leafs);
		// process list of next nodes
		if(!next_nodes.empty())
			walk_impl(next_nodes, step_f, topdown, follow_symlinks, follow_lazy_links, active_symlinks);
		// if walking from most deep subdir, process current node after all subtree
		if(!topdown)
			step_f(cur_node, next_nodes, next_leafs);

		// forget symlinks
		for(const auto& sym_id : cur_symlinks)
			active_symlinks.erase(sym_id);
	}
}

inline std::string link2path_unit(const link& l, Key path_unit) {
	switch(path_unit) {
	default:
	case Key::ID : return boost::uuids::to_string(l.id());
	case Key::OID : return l.oid();
	case Key::Name : return l.name();
	case Key::Type : return l.type_id();
	}
}

NAMESPACE_END()

NAMESPACE_BEGIN(detail)

sp_link walk_down_tree(const std::string& cur_lid, const sp_node& level, Key path_unit) {
	if(!level) return nullptr;
	const auto next = level->find(cur_lid, path_unit);
	return next != level->end() ? *next : nullptr;
}

NAMESPACE_END(detail)

/*-----------------------------------------------------------------------------
 *  public API
 *-----------------------------------------------------------------------------*/
///////////////////////////////////////////////////////////////////////////////
//  abspath
//
auto abspath(const link& L, Key path_unit) -> std::string {
	// [NOTE] root ID is irrelevant => abs path always starts with '/'
	auto pL = &L;
	auto parent = L.owner();
	sp_clink parent_h;
	std::deque<std::string> res;
	while(true) {
		if(parent)
			res.push_front(link2path_unit(*pL, path_unit));
		else {
			res.emplace_front("");
			break;
		}

		if(( parent_h = parent->handle() )) {
			parent = parent_h->owner();
			pL = parent_h.get();
		}
		else parent.reset();
	}
	if(res.size() == 1) res.emplace_front("");
	return boost::join(res, "/");
}

auto abspath(const sp_clink& L, Key path_unit) -> std::string {
	return L ? abspath(*L, path_unit) : "";
}

///////////////////////////////////////////////////////////////////////////////
//  find_root & find_root_handle
//
auto find_root(link& L) -> sp_node {
	if(auto root = L.owner()) {
		while(const auto root_h = root->handle()) {
			if(auto parent = root_h->owner())
				root = std::move(parent);
			else return root;
		}
	}
	// root handle is passed
	return L.data_node();
}

auto find_root(const sp_link& L) -> sp_node {
	return L ? find_root(*L) : nullptr;
}

// [NOTE] avoid `data_node()` call
auto find_root(sp_node N) -> sp_node {
	if(N) {
		while(const auto root_h = N->handle()) {
			if(auto parent = root_h->owner())
				N = std::move(parent);
			else break;
		}
	}
	return N;
}

NAMESPACE_BEGIN()

const auto find_root_handle_impl = [](auto L) {
	if(L) {
		while(const auto root = L->owner()) {
			if(auto root_h = root->handle())
				L = std::move(root_h);
			else break;
		}
	}
	return L;
};

NAMESPACE_END()

auto find_root_handle(sp_link L) -> sp_link {
	return find_root_handle_impl(std::move(L));
}
auto find_root_handle(sp_clink L) -> sp_clink {
	return find_root_handle_impl(std::move(L));
}
auto find_root_handle(const sp_node& N) -> sp_link {
	return find_root_handle_impl(N->handle());
}

///////////////////////////////////////////////////////////////////////////////
//  convert_path
//
std::string convert_path(
	std::string src_path, sp_link start,
	Key src_path_unit, Key dst_path_unit,
	bool follow_lazy_links
) {
	std::vector<std::string> res_path;
	const auto converter = [&res_path, src_path_unit, dst_path_unit](
		std::string next_level, const sp_node& cur_level
	) {
		sp_link res;
		if(next_level.empty() || next_level == "." || next_level == "..")
			res_path.emplace_back(std::move(next_level));
		else {
			const auto next = cur_level->find(next_level, src_path_unit);
			if(next != cur_level->end()) {
				res = *next;
				res_path.emplace_back(link2path_unit(*res, dst_path_unit));
			}
		}
		return res;
	};

	// do conversion
	boost::trim(src_path);
	if(detail::deref_path_impl<true>(src_path, std::move(start), nullptr, follow_lazy_links, converter)) {
		return boost::join(res_path, "/");
	}
	return "";
}

///////////////////////////////////////////////////////////////////////////////
//  deref_path
//
sp_link deref_path(
	const std::string& path, sp_link start, Key path_unit, bool follow_lazy_links
) {
	return detail::deref_path_impl(
		path, std::move(start), nullptr, follow_lazy_links, detail::gen_walk_down_tree(path_unit)
	);
}
sp_link deref_path(
	const std::string& path, sp_node start, Key path_unit, bool follow_lazy_links
) {
	return detail::deref_path_impl(
		path, nullptr, std::move(start), follow_lazy_links, detail::gen_walk_down_tree(path_unit)
	);
}

///////////////////////////////////////////////////////////////////////////////
//  walk
//
auto walk(
	const sp_link& root, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	walk_impl({root}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

auto walk(
	const sp_node& root, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	if(!root) return;
	auto hr = root->handle();
	if(!hr) hr = std::make_shared<hard_link>("/", root);
	walk_impl({hr}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

auto walk(
	const sp_node& root, walk_nodes_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	walk_impl({root}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

///////////////////////////////////////////////////////////////////////////////
//  misc
//
auto make_root_link(
	const std::string& link_type, std::string name, sp_node root_node
) -> sp_link {
	if(!root_node) root_node = std::make_shared<node>();
	// create link depending on type
	if(link_type == "fusion_link")
		return link::make_root<fusion_link>(std::move(name), std::move(root_node));
	else
		return link::make_root<hard_link>(std::move(name), std::move(root_node));
}

NAMESPACE_END(blue_sky::tree)
<commit_msg>tree: use safe node::leafs() to iterate over node in walk() impl<commit_after>/// @file
/// @author uentity
/// @date 22.11.2017
/// @brief BS tree-related functions impl
/// @copyright
/// 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 https://mozilla.org/MPL/2.0/

#include <bs/tree/tree.h>
#include "tree_impl.h"

#include <set>
#include <boost/uuid/uuid_io.hpp>
#include <boost/algorithm/string.hpp>

NAMESPACE_BEGIN(blue_sky::tree)

template<Key K> using Key_type = typename node::Key_type<K>;

/*-----------------------------------------------------------------------------
 *  impl details
 *-----------------------------------------------------------------------------*/
NAMESPACE_BEGIN()

void walk_impl(
	const std::list<sp_link>& nodes, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links,
	std::set<Key_type<Key::ID>> active_symlinks = {}
) {
	using detail::can_call_dnode;
	sp_node cur_node;
	std::list<sp_link> next_nodes;
	std::vector<sp_link> next_leafs;
	// for each node
	for(const auto& N : nodes) {
		if(!N) continue;
		// remember symlink
		const auto is_symlink = follow_symlinks && N->type_id() == "sym_link";
		if(is_symlink && !active_symlinks.insert(N->id()).second)
			continue;

		// obtain node from link honoring LazyLoad flag
		cur_node = (follow_lazy_links || can_call_dnode(*N)) ? N->data_node() : nullptr;

		next_nodes.clear();
		next_leafs.clear();

		if(cur_node) {
			// for each link in node
			for(const auto& l : cur_node->leafs()) {
				// collect nodes
				if((follow_lazy_links || can_call_dnode(*l)) && l->data_node()) {
					next_nodes.push_back(l);
				}
				else
					next_leafs.push_back(l);
			}
		}

		// if `topdown` == true, process this node BEFORE leafs processing
		if(topdown)
			step_f(N, next_nodes, next_leafs);
		// process list of next nodes
		if(!next_nodes.empty())
			walk_impl(next_nodes, step_f, topdown, follow_symlinks, follow_lazy_links, active_symlinks);
		// if walking from most deep subdir, process current node after all subtree
		if(!topdown)
			step_f(N, next_nodes, next_leafs);

		// forget symlink
		if(is_symlink)
			active_symlinks.erase(N->id());
	}
}

void walk_impl(
	const std::list<sp_node>& nodes, walk_nodes_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links,
	std::set<Key_type<Key::ID>> active_symlinks = {}
) {
	using detail::can_call_dnode;
	std::list<sp_node> next_nodes;
	std::vector<sp_link> next_leafs;

	// for each node
	for(const auto& cur_node : nodes) {
		if(!cur_node) continue;

		next_nodes.clear();
		next_leafs.clear();

		// symlinks among cur_node
		std::vector<Key_type<Key::ID>> cur_symlinks;

		// for each link in node
		for(const auto& l : cur_node->leafs()) {
			// remember symlinks
			if(follow_symlinks && l->type_id() == "sym_link") {
				if(active_symlinks.insert(l->id()).second)
					cur_symlinks.push_back(l->id());
				else continue;
			}

			// collect nodes
			auto sub_node = follow_lazy_links || can_call_dnode(*l) ? l->data_node() : nullptr;
			if(sub_node)
				next_nodes.push_back(sub_node);
			else
				next_leafs.push_back(l);
		}

		// if `topdown` == true, process this node BEFORE leafs processing
		if(topdown)
			step_f(cur_node, next_nodes, next_leafs);
		// process list of next nodes
		if(!next_nodes.empty())
			walk_impl(next_nodes, step_f, topdown, follow_symlinks, follow_lazy_links, active_symlinks);
		// if walking from most deep subdir, process current node after all subtree
		if(!topdown)
			step_f(cur_node, next_nodes, next_leafs);

		// forget symlinks
		for(const auto& sym_id : cur_symlinks)
			active_symlinks.erase(sym_id);
	}
}

inline std::string link2path_unit(const link& l, Key path_unit) {
	switch(path_unit) {
	default:
	case Key::ID : return boost::uuids::to_string(l.id());
	case Key::OID : return l.oid();
	case Key::Name : return l.name();
	case Key::Type : return l.type_id();
	}
}

NAMESPACE_END()

NAMESPACE_BEGIN(detail)

sp_link walk_down_tree(const std::string& cur_lid, const sp_node& level, Key path_unit) {
	if(!level) return nullptr;
	const auto next = level->find(cur_lid, path_unit);
	return next != level->end() ? *next : nullptr;
}

NAMESPACE_END(detail)

/*-----------------------------------------------------------------------------
 *  public API
 *-----------------------------------------------------------------------------*/
///////////////////////////////////////////////////////////////////////////////
//  abspath
//
auto abspath(const link& L, Key path_unit) -> std::string {
	// [NOTE] root ID is irrelevant => abs path always starts with '/'
	auto pL = &L;
	auto parent = L.owner();
	sp_clink parent_h;
	std::deque<std::string> res;
	while(true) {
		if(parent)
			res.push_front(link2path_unit(*pL, path_unit));
		else {
			res.emplace_front("");
			break;
		}

		if(( parent_h = parent->handle() )) {
			parent = parent_h->owner();
			pL = parent_h.get();
		}
		else parent.reset();
	}
	if(res.size() == 1) res.emplace_front("");
	return boost::join(res, "/");
}

auto abspath(const sp_clink& L, Key path_unit) -> std::string {
	return L ? abspath(*L, path_unit) : "";
}

///////////////////////////////////////////////////////////////////////////////
//  find_root & find_root_handle
//
auto find_root(link& L) -> sp_node {
	if(auto root = L.owner()) {
		while(const auto root_h = root->handle()) {
			if(auto parent = root_h->owner())
				root = std::move(parent);
			else return root;
		}
	}
	// root handle is passed
	return L.data_node();
}

auto find_root(const sp_link& L) -> sp_node {
	return L ? find_root(*L) : nullptr;
}

// [NOTE] avoid `data_node()` call
auto find_root(sp_node N) -> sp_node {
	if(N) {
		while(const auto root_h = N->handle()) {
			if(auto parent = root_h->owner())
				N = std::move(parent);
			else break;
		}
	}
	return N;
}

NAMESPACE_BEGIN()

const auto find_root_handle_impl = [](auto L) {
	if(L) {
		while(const auto root = L->owner()) {
			if(auto root_h = root->handle())
				L = std::move(root_h);
			else break;
		}
	}
	return L;
};

NAMESPACE_END()

auto find_root_handle(sp_link L) -> sp_link {
	return find_root_handle_impl(std::move(L));
}
auto find_root_handle(sp_clink L) -> sp_clink {
	return find_root_handle_impl(std::move(L));
}
auto find_root_handle(const sp_node& N) -> sp_link {
	return find_root_handle_impl(N->handle());
}

///////////////////////////////////////////////////////////////////////////////
//  convert_path
//
std::string convert_path(
	std::string src_path, sp_link start,
	Key src_path_unit, Key dst_path_unit,
	bool follow_lazy_links
) {
	std::vector<std::string> res_path;
	const auto converter = [&res_path, src_path_unit, dst_path_unit](
		std::string next_level, const sp_node& cur_level
	) {
		sp_link res;
		if(next_level.empty() || next_level == "." || next_level == "..")
			res_path.emplace_back(std::move(next_level));
		else {
			const auto next = cur_level->find(next_level, src_path_unit);
			if(next != cur_level->end()) {
				res = *next;
				res_path.emplace_back(link2path_unit(*res, dst_path_unit));
			}
		}
		return res;
	};

	// do conversion
	boost::trim(src_path);
	if(detail::deref_path_impl<true>(src_path, std::move(start), nullptr, follow_lazy_links, converter)) {
		return boost::join(res_path, "/");
	}
	return "";
}

///////////////////////////////////////////////////////////////////////////////
//  deref_path
//
sp_link deref_path(
	const std::string& path, sp_link start, Key path_unit, bool follow_lazy_links
) {
	return detail::deref_path_impl(
		path, std::move(start), nullptr, follow_lazy_links, detail::gen_walk_down_tree(path_unit)
	);
}
sp_link deref_path(
	const std::string& path, sp_node start, Key path_unit, bool follow_lazy_links
) {
	return detail::deref_path_impl(
		path, nullptr, std::move(start), follow_lazy_links, detail::gen_walk_down_tree(path_unit)
	);
}

///////////////////////////////////////////////////////////////////////////////
//  walk
//
auto walk(
	const sp_link& root, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	walk_impl({root}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

auto walk(
	const sp_node& root, walk_links_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	if(!root) return;
	auto hr = root->handle();
	if(!hr) hr = std::make_shared<hard_link>("/", root);
	walk_impl({hr}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

auto walk(
	const sp_node& root, walk_nodes_fv step_f,
	bool topdown, bool follow_symlinks, bool follow_lazy_links
) -> void {
	walk_impl({root}, step_f, topdown, follow_symlinks, follow_lazy_links);
}

///////////////////////////////////////////////////////////////////////////////
//  misc
//
auto make_root_link(
	const std::string& link_type, std::string name, sp_node root_node
) -> sp_link {
	if(!root_node) root_node = std::make_shared<node>();
	// create link depending on type
	if(link_type == "fusion_link")
		return link::make_root<fusion_link>(std::move(name), std::move(root_node));
	else
		return link::make_root<hard_link>(std::move(name), std::move(root_node));
}

NAMESPACE_END(blue_sky::tree)
<|endoftext|>


<commit_before>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/gui/WebTileGenerator.h>
#include <vw/FileIO/MemoryImageResource.h>

using namespace vw::platefile;

namespace vw { namespace gui {

HttpDownloadThread::HttpDownloadThread(const Url& u)
  : m_base_url(u), m_http(new QHttp(QString::fromStdString(m_base_url.hostname()), m_base_url.port() != 0 ? m_base_url.port() : 80, NULL))
{
  connect(m_http.get(), SIGNAL(requestFinished(int, bool)),this, SLOT(request_finished(int, bool)));
}

void HttpDownloadThread::run() {
  QThread::exec();
}

HttpDownloadThread::~HttpDownloadThread() { }

int HttpDownloadThread::get(const std::vector<std::string>& path, int transaction_id,
                            bool exact_transaction_id_match) {
  Url req_url(m_base_url);
  Url::split_t req_path = req_url.path_split();
  req_path.insert(req_path.end(), path.begin(), path.end());
  req_url.path_join(req_path);

  std::cout << "URL STRING: " << req_url << std::endl;

  // Set up request buffer
  RequestBufferPtr buf(new RequestBuffer(req_url));

  int request_id;
  req_url.query().set("nocache", "1");
  req_url.query().set("transaction_id", vw::stringify(transaction_id));

  if (exact_transaction_id_match)
    req_url.query().set("exact", "1");

  vw_out() << "\t --> Fetching " << req_url << "\n";

  request_id = m_http->get(QString::fromStdString(req_url.string()), &buf->buffer);

  Mutex::Lock lock(m_mutex);
  m_requests[request_id] = buf;

  return request_id;
}

bool HttpDownloadThread::result_available(int request_id) {
  Mutex::Lock lock(m_mutex);
  return m_requests[request_id]->ready;
}

vw::ImageView<vw::PixelRGBA<float> > HttpDownloadThread::pop_result(int request_id) {
  Mutex::Lock lock(m_mutex);
  map_t::iterator i = m_requests.find(request_id);
  VW_ASSERT(i != m_requests.end(), LogicErr() << "Asked for a request that doesn't exist!");
  RequestBufferPtr r = i->second;
  m_requests.erase(i);
  return r->result;
}

void HttpDownloadThread::request_finished(int request_id, bool error) {
  Mutex::Lock lock(m_mutex);

  map_t::iterator i = m_requests.find(request_id);
  // setHost and things count as requests
  if (i == m_requests.end())
    return;

  QHttpResponseHeader hdr = m_http->lastResponse();
  RequestBufferPtr buf = i->second;

  typedef PixelRGBA<float> pixel_t;

  switch (hdr.statusCode()) {
    case 200:
      // handled below
      break;
    case 404:
      buf->result.set_size(1,1);
      buf->result(0,0) = pixel_t(0.0f,0.1f,0.0f,1.0f);
      buf->ready = true;
      return;
    default:
      error = true;
      vw_out(WarningMessage, "console") <<
        "Request " << request_id << " failed with status " << hdr.statusCode() << " for URL: " << buf->url << "\n";
      break;
    }

    std::string filetype;
    if (hdr.contentType() == "image/png")
      filetype = "png";
    else if (hdr.contentType() == "image/jpeg" || hdr.contentType() == "image/jpg")
      filetype = "jpg";
    else if (hdr.contentType() == "image/tiff" || hdr.contentType() == "image/tif")
      filetype = "tif";
    else {
      vw_out(WarningMessage, "console") << "unrecognized content-type: " << hdr.contentType().toStdString() << "\n";
      error = true;
    }

    if (error) {
      buf->result.set_size(1,1);
      buf->result(0,0) = pixel_t(1.0f,0.0f,0.0f,1.0f);
      buf->ready = true;
      return;
    }

    const QByteArray& bytes = buf->buffer.data();

    boost::scoped_ptr<SrcImageResource> rsrc(
        SrcMemoryImageResource::open(
          filetype, reinterpret_cast<const uint8*>(bytes.constData()), bytes.size()));

    try {
      if (rsrc->channel_type() == VW_CHANNEL_UINT8) {
        buf->result = channel_cast_rescale<float>(ImageView<PixelRGBA<uint8> >(*rsrc));
      } else if (rsrc->channel_type() == VW_CHANNEL_UINT16) {
        buf->result = channel_cast_rescale<float>(ImageView<PixelRGBA<int16> >(*rsrc));
      } else {
        vw_out() << "WARNING: Image contains unsupported channel type: " << rsrc->channel_type() << "\n";
      }
      buf->ready = true;
    } catch (IOErr &e) {
      vw_out(WarningMessage) << "Could not parse network tile: " << buf->url << std::endl;
      buf->ready = true;
    }
}

WebTileGenerator::WebTileGenerator(const Url& url, int levels) :
  m_tile_size(256), m_levels(levels), m_download_thread(url) {
  m_download_thread.start();
}

boost::shared_ptr<ViewImageResource> WebTileGenerator::generate_tile(TileLocator const& tile_info) {

  std::vector<std::string> path(3);
  path.push_back(vw::stringify(tile_info.level));
  path.push_back(vw::stringify(tile_info.col));
  path.push_back(vw::stringify(tile_info.row) + ".png");

  int request_id = m_download_thread.get(path,
                                         tile_info.transaction_id,
                                         tile_info.exact_transaction_id_match);
  // TODO: busywait :(
  while(!m_download_thread.result_available(request_id))
    usleep(100);

  vw::ImageView<vw::PixelRGBA<float> > result = m_download_thread.pop_result(request_id);
  return boost::shared_ptr<ViewImageResource>( new ViewImageResource( result ) );
}

Vector2 WebTileGenerator::minmax() { return Vector2(0.0, 1.0); }

PixelRGBA<float32> WebTileGenerator::sample(int /*x*/, int /*y*/, int /*level*/, int /*transaction_id*/) {
  PixelRGBA<float32> result;
  return result;
}

int WebTileGenerator::cols() const { return m_tile_size * (1 << (m_levels-1)); }
int WebTileGenerator::rows() const { return m_tile_size * (1 << (m_levels-1)); }
PixelFormatEnum WebTileGenerator::pixel_format() const { return VW_PIXEL_RGBA; }
ChannelTypeEnum WebTileGenerator::channel_type() const { return VW_CHANNEL_UINT8; }
Vector2i WebTileGenerator::tile_size() const {
  return Vector2i(m_tile_size, m_tile_size);
}
int32 WebTileGenerator::num_levels() const {
  return m_levels;
}

}}
<commit_msg>vwv: make sure connection errors are caught<commit_after>// __BEGIN_LICENSE__
// Copyright (C) 2006-2010 United States Government as represented by
// the Administrator of the National Aeronautics and Space Administration.
// All Rights Reserved.
// __END_LICENSE__

#include <vw/gui/WebTileGenerator.h>
#include <vw/FileIO/MemoryImageResource.h>

using namespace vw::platefile;

namespace vw { namespace gui {

HttpDownloadThread::HttpDownloadThread(const Url& u)
  : m_base_url(u), m_http(new QHttp(QString::fromStdString(m_base_url.hostname()), m_base_url.port() != 0 ? m_base_url.port() : 80, NULL))
{
  connect(m_http.get(), SIGNAL(requestFinished(int, bool)),this, SLOT(request_finished(int, bool)));
}

void HttpDownloadThread::run() {
  QThread::exec();
}

HttpDownloadThread::~HttpDownloadThread() { }

int HttpDownloadThread::get(const std::vector<std::string>& path, int transaction_id,
                            bool exact_transaction_id_match) {
  Url req_url(m_base_url);
  Url::split_t req_path = req_url.path_split();
  req_path.insert(req_path.end(), path.begin(), path.end());
  req_url.path_join(req_path);

  // Set up request buffer
  RequestBufferPtr buf(new RequestBuffer(req_url));

  int request_id;
  req_url.query().set("nocache", "1");
  req_url.query().set("transaction_id", vw::stringify(transaction_id));

  if (exact_transaction_id_match)
    req_url.query().set("exact", "1");

  vw_out() << "\t --> Fetching " << req_url << "\n";

  request_id = m_http->get(QString::fromStdString(req_url.string()), &buf->buffer);

  Mutex::Lock lock(m_mutex);
  m_requests[request_id] = buf;

  return request_id;
}

bool HttpDownloadThread::result_available(int request_id) {
  Mutex::Lock lock(m_mutex);
  return m_requests[request_id]->ready;
}

vw::ImageView<vw::PixelRGBA<float> > HttpDownloadThread::pop_result(int request_id) {
  Mutex::Lock lock(m_mutex);
  map_t::iterator i = m_requests.find(request_id);
  VW_ASSERT(i != m_requests.end(), LogicErr() << "Asked for a request that doesn't exist!");
  RequestBufferPtr r = i->second;
  m_requests.erase(i);
  return r->result;
}

void HttpDownloadThread::request_finished(int request_id, bool error) {
  Mutex::Lock lock(m_mutex);

  map_t::iterator i = m_requests.find(request_id);
  // setHost and things count as requests
  if (i == m_requests.end())
    return;

  QHttpResponseHeader hdr = m_http->lastResponse();
  RequestBufferPtr buf = i->second;

  typedef PixelRGBA<float> pixel_t;

  std::string filetype;
  if (error) {
    vw_out(WarningMessage, "console") << "Connection Error: " << m_http->errorString().toStdString() << "\n";
  } else {
    switch (hdr.statusCode()) {
      case 200:
        // handled below
        break;
      case 404:
        buf->result.set_size(1,1);
        buf->result(0,0) = pixel_t(0.0f,0.1f,0.0f,1.0f);
        buf->ready = true;
        return;
      default:
        error = true;
        vw_out(WarningMessage, "console") <<
          "Request " << request_id << " failed with status " << hdr.statusCode() << " for URL: " << buf->url << "\n";
        break;
      }

      if (hdr.contentType() == "image/png")
        filetype = "png";
      else if (hdr.contentType() == "image/jpeg" || hdr.contentType() == "image/jpg")
        filetype = "jpg";
      else if (hdr.contentType() == "image/tiff" || hdr.contentType() == "image/tif")
        filetype = "tif";
      else {
        vw_out(WarningMessage, "console") << "unrecognized content-type: " << hdr.contentType().toStdString() << "\n";
        error = true;
      }
  }

  if (error) {
    buf->result.set_size(1,1);
    buf->result(0,0) = pixel_t(1.0f,0.0f,0.0f,1.0f);
    buf->ready = true;
    return;
  }

  const QByteArray& bytes = buf->buffer.data();

  boost::scoped_ptr<SrcImageResource> rsrc(
      SrcMemoryImageResource::open(
        filetype, reinterpret_cast<const uint8*>(bytes.constData()), bytes.size()));

  try {
    if (rsrc->channel_type() == VW_CHANNEL_UINT8) {
      buf->result = channel_cast_rescale<float>(ImageView<PixelRGBA<uint8> >(*rsrc));
    } else if (rsrc->channel_type() == VW_CHANNEL_UINT16) {
      buf->result = channel_cast_rescale<float>(ImageView<PixelRGBA<int16> >(*rsrc));
    } else {
      vw_out() << "WARNING: Image contains unsupported channel type: " << rsrc->channel_type() << "\n";
    }
    buf->ready = true;
  } catch (IOErr &e) {
    vw_out(WarningMessage) << "Could not parse network tile: " << buf->url << std::endl;
    buf->ready = true;
  }
}

WebTileGenerator::WebTileGenerator(const Url& url, int levels) :
  m_tile_size(256), m_levels(levels), m_download_thread(url) {
  m_download_thread.start();
}

boost::shared_ptr<ViewImageResource> WebTileGenerator::generate_tile(TileLocator const& tile_info) {

  std::vector<std::string> path(3);
  path[0] = vw::stringify(tile_info.level);
  path[1] = vw::stringify(tile_info.col);
  path[2] = vw::stringify(tile_info.row) + ".png";

  int request_id = m_download_thread.get(path,
                                         tile_info.transaction_id,
                                         tile_info.exact_transaction_id_match);
  // TODO: busywait :(
  while(!m_download_thread.result_available(request_id))
    usleep(100);

  vw::ImageView<vw::PixelRGBA<float> > result = m_download_thread.pop_result(request_id);
  return boost::shared_ptr<ViewImageResource>( new ViewImageResource( result ) );
}

Vector2 WebTileGenerator::minmax() { return Vector2(0.0, 1.0); }

PixelRGBA<float32> WebTileGenerator::sample(int /*x*/, int /*y*/, int /*level*/, int /*transaction_id*/) {
  PixelRGBA<float32> result;
  return result;
}

int WebTileGenerator::cols() const { return m_tile_size * (1 << (m_levels-1)); }
int WebTileGenerator::rows() const { return m_tile_size * (1 << (m_levels-1)); }
PixelFormatEnum WebTileGenerator::pixel_format() const { return VW_PIXEL_RGBA; }
ChannelTypeEnum WebTileGenerator::channel_type() const { return VW_CHANNEL_UINT8; }
Vector2i WebTileGenerator::tile_size() const {
  return Vector2i(m_tile_size, m_tile_size);
}
int32 WebTileGenerator::num_levels() const {
  return m_levels;
}

}}
<|endoftext|>


<commit_before>#include "sim.h"

#include <sim/file.h>
#include <simlib/filesystem.h>

Sim::FilePermissions Sim::files_get_permissions(ContestPermissions cperms) noexcept {
	STACK_UNWINDING_MARK;
	using PERM = FilePermissions;
	using CPERM = ContestPermissions;

	if (uint(cperms & CPERM::ADMIN)) {
		return PERM::ADD | PERM::VIEW | PERM::DOWNLOAD | PERM::EDIT |
			PERM::DELETE | PERM::VIEW_CREATOR;
	}

	if (uint(cperms & CPERM::VIEW))
		return PERM::VIEW | PERM::DOWNLOAD;

	return PERM::NONE;
}

Optional<Sim::FilePermissions> Sim::files_get_permissions(StringView file_id) {
	STACK_UNWINDING_MARK;

	auto stmt = mysql.prepare("SELECT c.is_public, cu.mode"
		" FROM files f"
		" LEFT JOIN contests c ON c.id=f.contest_id"
		" LEFT JOIN contest_users cu ON cu.contest_id=f.contest_id AND cu.user_id=?"
		" WHERE f.id=?");
	if (session_is_open)
		stmt.bindAndExecute(session_user_id, file_id);
	else
		stmt.bindAndExecute(nullptr, file_id);

	bool is_public;
	MySQL::Optional<EnumVal<ContestUserMode>> cu_mode;

	stmt.res_bind_all(is_public, cu_mode);
	if (not stmt.next())
		return std::nullopt;

	return files_get_permissions(contests_get_permissions(is_public, cu_mode));
}

void Sim::api_files() {
	STACK_UNWINDING_MARK;

	bool allow_access = false; // Either contest or specific id condition must occur

	InplaceBuff<512> query;
	query.append("SELECT f.id, f.name, f.description, f.file_size, f.modified,"
			" f.contest_id, f.creator, u.username"
		" FROM files f"
		" LEFT JOIN users u ON u.id=f.creator"
		" WHERE 1=1");

	enum ColumnIdx {
		FID, FNAME, DESCRIPTION, FSIZE, MODIFIED, CONTEST_ID, CREATOR, CUSERNAME
	};

	auto append_column_names = [&] {
		append("[{\"fields\":["
				"\"overall_actions\","
				"{\"name\":\"rows\", \"columns\":["
					"\"id\","
					"\"name\","
					"\"description\","
					"\"file_size\","
					"\"modified\","
					"\"contest_id\","
					"\"creator_id\","
					"\"creator_username\","
					"\"actions\""
				"]}"
			"]}");
	};

	auto set_empty_response = [&] {
		resp.content.clear();
		append_column_names();
		append(",\n\"\",[\n]]"); // Empty overall actions
	};

	FilePermissions perms = FilePermissions::NONE;
	{
		bool id_condition_occurred = false;
		bool contest_id_condition_occurred = false;

		for (StringView next_arg = url_args.extractNextArg();
			not next_arg.empty(); next_arg = url_args.extractNextArg())
		{
			auto arg = decodeURI(next_arg);
			char cond_c = arg[0];
			StringView arg_id = StringView(arg).substr(1);

			// File ID
			if (isOneOf(cond_c, '<', '>', '=')) {
				if (not isAlnum(arg_id))
					return api_error400("Invalid File ID");

				if (id_condition_occurred)
					return api_error400("File ID condition specified more"
						" than once");

				id_condition_occurred = true;

				if (cond_c == '=' and not allow_access) {
					perms = files_get_permissions(arg_id).value_or(FilePermissions::NONE);
					allow_access = uint(perms & FilePermissions::VIEW);
				}

				query.append(" AND f.id", cond_c, '\'', arg_id, '\'');

			// Next conditions require arg_id to be a valid (digital) ID
			} else if (not isDigit(arg_id)) {
				return api_error400();

			// Contest id
			} else if (cond_c == 'c') {
				if (contest_id_condition_occurred)
					return api_error400("Contest ID condition specified more"
						" than once");

				contest_id_condition_occurred = true;
				query.append(" AND f.contest_id=", arg_id);

				auto cperms = contests_get_permissions(arg_id);
				if (not cperms.has_value())
					return set_empty_response(); // Do allow to query for contest existence (not api_error404())

				perms = files_get_permissions(cperms.value());
				if (uint(perms & FilePermissions::VIEW))
					allow_access = true;

			} else
				return api_error400();
		}
	}

	if (not allow_access)
		return set_empty_response();

	constexpr uint FILES_LIMIT_PER_QUERY = 50;
	#define FILES_LIMIT_PER_QUERY_STR "50"
	static_assert(meta::equal(FILES_LIMIT_PER_QUERY_STR,
		meta::ToString<FILES_LIMIT_PER_QUERY>::value),
		"Update the above #define");

	// Execute query
	auto res = mysql.query(intentionalUnsafeStringView(concat(query,
		" ORDER BY f.id DESC LIMIT " FILES_LIMIT_PER_QUERY_STR)));

	append_column_names();

	// Overall actions
	append(",\n\"");
	if (uint(perms & FilePermissions::ADD))
		append('A');
	if (uint(perms & FilePermissions::VIEW_CREATOR))
		append('C');
	append("\",[");

	for (bool first = true; res.next(); ) {
		if (first)
			first = false;
		else
			append(',');

		append("\n[", jsonStringify(res[FID]), ',',
			jsonStringify(res[FNAME]), ',',
			jsonStringify(res[DESCRIPTION]), ',',
			res[FSIZE], ","
			"\"", res[MODIFIED], "\",",
			res[CONTEST_ID], ',');

		// Permissions are already loaded
		if (uint(perms & FilePermissions::VIEW_CREATOR) and not res.is_null(CREATOR))
			append(res[CREATOR], ',', jsonStringify(res[CUSERNAME]), ',');
		else
			append("null,null,");

		// Actions
		append("\"");
		if (uint(perms & FilePermissions::DOWNLOAD))
			append("O");
		if (uint(perms & FilePermissions::EDIT))
			append("E");
		if (uint(perms & FilePermissions::DELETE))
			append("D");
		append("\"]");
	}

	append("\n]]");
}

void Sim::api_file() {
	STACK_UNWINDING_MARK;

	files_id = url_args.extractNextArg();
	if (files_id == "add") {
		return api_file_add();
	} else if (files_id.empty())
		return api_error404();

	{
		auto file_perms_opt = files_get_permissions(files_id);
		if (not file_perms_opt.has_value())
			return api_error404();

		files_perms = file_perms_opt.value();
	}

	StringView next_arg = url_args.extractNextArg();
	if (next_arg == "download")
		return api_file_download();
	else if (next_arg == "edit")
		return api_file_edit();
	else if (next_arg == "delete")
		return api_file_delete();
	else if (not next_arg.empty())
		return api_error400();
}

void Sim::api_file_download() {
	if (uint(~files_perms & FilePermissions::DOWNLOAD))
		return api_error403();

	InplaceBuff<FILE_NAME_MAX_LEN> filename;
	auto stmt = mysql.prepare("SELECT name FROM files WHERE id=?");
	stmt.bindAndExecute(files_id);
	stmt.res_bind_all(filename);
	if (not stmt.next())
		return error404(); // Should not happen as the file existed a moment ago

	resp.headers["Content-Disposition"] =
		concat_tostr("attachment; filename=", http::quote(filename));
	resp.content_type = server::HttpResponse::FILE;
	resp.content = concat("files/", files_id);
}

void Sim::api_file_add() {
	StringView contest_id = url_args.extractNextArg();
	if (contest_id.empty() or contest_id[0] != 'c' or
		not isDigit(contest_id = contest_id.substr(1)))
	{
		return api_error400();
	}

	files_perms = files_get_permissions(
		contests_get_permissions(contest_id).value_or(ContestPermissions::NONE));
	if (uint(~files_perms & FilePermissions::ADD))
		return api_error403();

	CStringView name, description, file_tmp_path, user_filename;
	form_validate(name, "name", "File name", FILE_NAME_MAX_LEN);
	form_validate(description, "description", "Description", FILE_DESCRIPTION_MAX_LEN);
	bool file_exists = form_validate_file_path_not_blank(file_tmp_path, "file", "File");
	form_validate_not_blank(user_filename, "file", "Filename of the selected file");

	if (name.empty()) {
		if (user_filename.size() > FILE_NAME_MAX_LEN)
			add_notification("error", "Filename of the provided file is longer than ",
				FILE_NAME_MAX_LEN, ". You have to provide a shorter name.");
		else
			name = user_filename;
	}

	// Check the file size
	auto file_size = (file_exists ? get_file_size(file_tmp_path) : 0);
	if (file_size > FILE_MAX_SIZE) {
		add_notification("error", "Solution is too big (maximum allowed size: ", FILE_MAX_SIZE, " bytes = ", humanizeFileSize(FILE_MAX_SIZE), ')');
		return api_error400(notifications);
	}

	if (notifications.size)
		return api_error400(notifications);

	// Insert file
	auto stmt = mysql.prepare("INSERT IGNORE files (id, contest_id, name,"
			" description, file_size, modified, creator)"
		" VALUES(?, ?, ?, ?, ?, ?, ?)");

	InplaceBuff<FILE_ID_LEN> file_id;
	auto curr_date = mysql_date();
	throw_assert(session_is_open);
	stmt.bind_all(file_id, contest_id, name, description, file_size, curr_date,
		session_user_id);

	do {
		file_id = generate_random_token(FILE_ID_LEN);
		stmt.bind(0, file_id);
		stmt.fixAndExecute();
	} while (stmt.affected_rows() == 0);

	// Move file
	if (move(file_tmp_path, concat("files/", file_id)))
		THROW("move()", errmsg());

	append(file_id);
}

void Sim::api_file_edit() {
	if (uint(~files_perms & FilePermissions::EDIT))
		return api_error403();

	CStringView name, description, file_tmp_path;
	form_validate(name, "name", "File name", FILE_NAME_MAX_LEN);
	form_validate(description, "description", "Description", FILE_DESCRIPTION_MAX_LEN);
	bool file_exists = form_validate_file_path_not_blank(file_tmp_path, "file", "File");

	CStringView user_filename = request.form_data.get("file");
	file_exists &= not user_filename.empty();

	if (name.empty()) {
		if (user_filename.size() > FILE_NAME_MAX_LEN)
			add_notification("error", "Filename of the provided file is longer than ",
				FILE_NAME_MAX_LEN, ". You have to provide a shorter name.");
		else if (user_filename.empty())
			add_notification("error", "File cannot have an empty name - no name"
				" and file (to infer name) has been provided");
		else
			name = user_filename;
	}

	// Check the file size
	auto file_size = (file_exists ? get_file_size(file_tmp_path) : 0);
	if (file_size > FILE_MAX_SIZE) {
		add_notification("error", "Solution is too big (maximum allowed size: ", FILE_MAX_SIZE, " bytes = ", humanizeFileSize(FILE_MAX_SIZE), ')');
		return api_error400(notifications);
	}

	if (notifications.size)
		return api_error400(notifications);

	// Update file
	auto stmt = mysql.prepare("UPDATE files SET name=?, description=?,"
			" file_size=IF(?, ?, file_size), modified=?"
		" WHERE id=?");
	stmt.bindAndExecute(name, description, file_exists, file_size, mysql_date(), files_id);

	// Move file
	if (file_exists and move(file_tmp_path, concat("files/", files_id)))
		THROW("move()", errmsg());
}

void Sim::api_file_delete() {
	if (uint(~files_perms & FilePermissions::DELETE))
		return api_error403();

	file::delete_file(mysql, files_id);
}
<commit_msg>Fixed typo<commit_after>#include "sim.h"

#include <sim/file.h>
#include <simlib/filesystem.h>

Sim::FilePermissions Sim::files_get_permissions(ContestPermissions cperms) noexcept {
	STACK_UNWINDING_MARK;
	using PERM = FilePermissions;
	using CPERM = ContestPermissions;

	if (uint(cperms & CPERM::ADMIN)) {
		return PERM::ADD | PERM::VIEW | PERM::DOWNLOAD | PERM::EDIT |
			PERM::DELETE | PERM::VIEW_CREATOR;
	}

	if (uint(cperms & CPERM::VIEW))
		return PERM::VIEW | PERM::DOWNLOAD;

	return PERM::NONE;
}

Optional<Sim::FilePermissions> Sim::files_get_permissions(StringView file_id) {
	STACK_UNWINDING_MARK;

	auto stmt = mysql.prepare("SELECT c.is_public, cu.mode"
		" FROM files f"
		" LEFT JOIN contests c ON c.id=f.contest_id"
		" LEFT JOIN contest_users cu ON cu.contest_id=f.contest_id AND cu.user_id=?"
		" WHERE f.id=?");
	if (session_is_open)
		stmt.bindAndExecute(session_user_id, file_id);
	else
		stmt.bindAndExecute(nullptr, file_id);

	bool is_public;
	MySQL::Optional<EnumVal<ContestUserMode>> cu_mode;

	stmt.res_bind_all(is_public, cu_mode);
	if (not stmt.next())
		return std::nullopt;

	return files_get_permissions(contests_get_permissions(is_public, cu_mode));
}

void Sim::api_files() {
	STACK_UNWINDING_MARK;

	bool allow_access = false; // Either contest or specific id condition must occur

	InplaceBuff<512> query;
	query.append("SELECT f.id, f.name, f.description, f.file_size, f.modified,"
			" f.contest_id, f.creator, u.username"
		" FROM files f"
		" LEFT JOIN users u ON u.id=f.creator"
		" WHERE 1=1");

	enum ColumnIdx {
		FID, FNAME, DESCRIPTION, FSIZE, MODIFIED, CONTEST_ID, CREATOR, CUSERNAME
	};

	auto append_column_names = [&] {
		append("[{\"fields\":["
				"\"overall_actions\","
				"{\"name\":\"rows\", \"columns\":["
					"\"id\","
					"\"name\","
					"\"description\","
					"\"file_size\","
					"\"modified\","
					"\"contest_id\","
					"\"creator_id\","
					"\"creator_username\","
					"\"actions\""
				"]}"
			"]}");
	};

	auto set_empty_response = [&] {
		resp.content.clear();
		append_column_names();
		append(",\n\"\",[\n]]"); // Empty overall actions
	};

	FilePermissions perms = FilePermissions::NONE;
	{
		bool id_condition_occurred = false;
		bool contest_id_condition_occurred = false;

		for (StringView next_arg = url_args.extractNextArg();
			not next_arg.empty(); next_arg = url_args.extractNextArg())
		{
			auto arg = decodeURI(next_arg);
			char cond_c = arg[0];
			StringView arg_id = StringView(arg).substr(1);

			// File ID
			if (isOneOf(cond_c, '<', '>', '=')) {
				if (not isAlnum(arg_id))
					return api_error400("Invalid File ID");

				if (id_condition_occurred)
					return api_error400("File ID condition specified more"
						" than once");

				id_condition_occurred = true;

				if (cond_c == '=' and not allow_access) {
					perms = files_get_permissions(arg_id).value_or(FilePermissions::NONE);
					allow_access = uint(perms & FilePermissions::VIEW);
				}

				query.append(" AND f.id", cond_c, '\'', arg_id, '\'');

			// Next conditions require arg_id to be a valid (digital) ID
			} else if (not isDigit(arg_id)) {
				return api_error400();

			// Contest id
			} else if (cond_c == 'c') {
				if (contest_id_condition_occurred)
					return api_error400("Contest ID condition specified more"
						" than once");

				contest_id_condition_occurred = true;
				query.append(" AND f.contest_id=", arg_id);

				auto cperms = contests_get_permissions(arg_id);
				if (not cperms.has_value())
					return set_empty_response(); // Do allow to query for contest existence (not api_error404())

				perms = files_get_permissions(cperms.value());
				if (uint(perms & FilePermissions::VIEW))
					allow_access = true;

			} else
				return api_error400();
		}
	}

	if (not allow_access)
		return set_empty_response();

	constexpr uint FILES_LIMIT_PER_QUERY = 50;
	#define FILES_LIMIT_PER_QUERY_STR "50"
	static_assert(meta::equal(FILES_LIMIT_PER_QUERY_STR,
		meta::ToString<FILES_LIMIT_PER_QUERY>::value),
		"Update the above #define");

	// Execute query
	auto res = mysql.query(intentionalUnsafeStringView(concat(query,
		" ORDER BY f.id DESC LIMIT " FILES_LIMIT_PER_QUERY_STR)));

	append_column_names();

	// Overall actions
	append(",\n\"");
	if (uint(perms & FilePermissions::ADD))
		append('A');
	if (uint(perms & FilePermissions::VIEW_CREATOR))
		append('C');
	append("\",[");

	for (bool first = true; res.next(); ) {
		if (first)
			first = false;
		else
			append(',');

		append("\n[", jsonStringify(res[FID]), ',',
			jsonStringify(res[FNAME]), ',',
			jsonStringify(res[DESCRIPTION]), ',',
			res[FSIZE], ","
			"\"", res[MODIFIED], "\",",
			res[CONTEST_ID], ',');

		// Permissions are already loaded
		if (uint(perms & FilePermissions::VIEW_CREATOR) and not res.is_null(CREATOR))
			append(res[CREATOR], ',', jsonStringify(res[CUSERNAME]), ',');
		else
			append("null,null,");

		// Actions
		append("\"");
		if (uint(perms & FilePermissions::DOWNLOAD))
			append("O");
		if (uint(perms & FilePermissions::EDIT))
			append("E");
		if (uint(perms & FilePermissions::DELETE))
			append("D");
		append("\"]");
	}

	append("\n]]");
}

void Sim::api_file() {
	STACK_UNWINDING_MARK;

	files_id = url_args.extractNextArg();
	if (files_id == "add") {
		return api_file_add();
	} else if (files_id.empty())
		return api_error404();

	{
		auto file_perms_opt = files_get_permissions(files_id);
		if (not file_perms_opt.has_value())
			return api_error404();

		files_perms = file_perms_opt.value();
	}

	StringView next_arg = url_args.extractNextArg();
	if (next_arg == "download")
		return api_file_download();
	else if (next_arg == "edit")
		return api_file_edit();
	else if (next_arg == "delete")
		return api_file_delete();
	else if (not next_arg.empty())
		return api_error400();
}

void Sim::api_file_download() {
	if (uint(~files_perms & FilePermissions::DOWNLOAD))
		return api_error403();

	InplaceBuff<FILE_NAME_MAX_LEN> filename;
	auto stmt = mysql.prepare("SELECT name FROM files WHERE id=?");
	stmt.bindAndExecute(files_id);
	stmt.res_bind_all(filename);
	if (not stmt.next())
		return error404(); // Should not happen as the file existed a moment ago

	resp.headers["Content-Disposition"] =
		concat_tostr("attachment; filename=", http::quote(filename));
	resp.content_type = server::HttpResponse::FILE;
	resp.content = concat("files/", files_id);
}

void Sim::api_file_add() {
	StringView contest_id = url_args.extractNextArg();
	if (contest_id.empty() or contest_id[0] != 'c' or
		not isDigit(contest_id = contest_id.substr(1)))
	{
		return api_error400();
	}

	files_perms = files_get_permissions(
		contests_get_permissions(contest_id).value_or(ContestPermissions::NONE));
	if (uint(~files_perms & FilePermissions::ADD))
		return api_error403();

	CStringView name, description, file_tmp_path, user_filename;
	form_validate(name, "name", "File name", FILE_NAME_MAX_LEN);
	form_validate(description, "description", "Description", FILE_DESCRIPTION_MAX_LEN);
	bool file_exists = form_validate_file_path_not_blank(file_tmp_path, "file", "File");
	form_validate_not_blank(user_filename, "file", "Filename of the selected file");

	if (name.empty()) {
		if (user_filename.size() > FILE_NAME_MAX_LEN)
			add_notification("error", "Filename of the provided file is longer than ",
				FILE_NAME_MAX_LEN, ". You have to provide a shorter name.");
		else
			name = user_filename;
	}

	// Check the file size
	auto file_size = (file_exists ? get_file_size(file_tmp_path) : 0);
	if (file_size > FILE_MAX_SIZE) {
		add_notification("error", "File is too big (maximum allowed size: ", FILE_MAX_SIZE, " bytes = ", humanizeFileSize(FILE_MAX_SIZE), ')');
		return api_error400(notifications);
	}

	if (notifications.size)
		return api_error400(notifications);

	// Insert file
	auto stmt = mysql.prepare("INSERT IGNORE files (id, contest_id, name,"
			" description, file_size, modified, creator)"
		" VALUES(?, ?, ?, ?, ?, ?, ?)");

	InplaceBuff<FILE_ID_LEN> file_id;
	auto curr_date = mysql_date();
	throw_assert(session_is_open);
	stmt.bind_all(file_id, contest_id, name, description, file_size, curr_date,
		session_user_id);

	do {
		file_id = generate_random_token(FILE_ID_LEN);
		stmt.bind(0, file_id);
		stmt.fixAndExecute();
	} while (stmt.affected_rows() == 0);

	// Move file
	if (move(file_tmp_path, concat("files/", file_id)))
		THROW("move()", errmsg());

	append(file_id);
}

void Sim::api_file_edit() {
	if (uint(~files_perms & FilePermissions::EDIT))
		return api_error403();

	CStringView name, description, file_tmp_path;
	form_validate(name, "name", "File name", FILE_NAME_MAX_LEN);
	form_validate(description, "description", "Description", FILE_DESCRIPTION_MAX_LEN);
	bool file_exists = form_validate_file_path_not_blank(file_tmp_path, "file", "File");

	CStringView user_filename = request.form_data.get("file");
	file_exists &= not user_filename.empty();

	if (name.empty()) {
		if (user_filename.size() > FILE_NAME_MAX_LEN)
			add_notification("error", "Filename of the provided file is longer than ",
				FILE_NAME_MAX_LEN, ". You have to provide a shorter name.");
		else if (user_filename.empty())
			add_notification("error", "File cannot have an empty name - no name"
				" and file (to infer name) has been provided");
		else
			name = user_filename;
	}

	// Check the file size
	auto file_size = (file_exists ? get_file_size(file_tmp_path) : 0);
	if (file_size > FILE_MAX_SIZE) {
		add_notification("error", "File is too big (maximum allowed size: ", FILE_MAX_SIZE, " bytes = ", humanizeFileSize(FILE_MAX_SIZE), ')');
		return api_error400(notifications);
	}

	if (notifications.size)
		return api_error400(notifications);

	// Update file
	auto stmt = mysql.prepare("UPDATE files SET name=?, description=?,"
			" file_size=IF(?, ?, file_size), modified=?"
		" WHERE id=?");
	stmt.bindAndExecute(name, description, file_exists, file_size, mysql_date(), files_id);

	// Move file
	if (file_exists and move(file_tmp_path, concat("files/", files_id)))
		THROW("move()", errmsg());
}

void Sim::api_file_delete() {
	if (uint(~files_perms & FilePermissions::DELETE))
		return api_error403();

	file::delete_file(mysql, files_id);
}
<|endoftext|>


<commit_before>/**
 *	@file strings.hpp
 *  @brief Elementary algorithms from e-maxx.ru library
 *  @author Ivan Deviatkin <devyatkin.ivan@gmail.com>, great thanks to Maksim Ivanov
**/

#pragma once

#include <vector>
#include <string>

namespace Fudzi
{
/*
 * Class stores algorithms related to strings operations
 */
class Strings
{
private:
protected:
public:
	//! @brief Trivial algorithm for getting zet function for string
	//! @param s string
	//! @return vector of zet function for given string
	static std::vector<int> z_function_trivial(std::string s);
	//! @brief Efficiency algorithm for getting zet function for string
	//! @param s string
	//! @return vector of zet function for given string
	static std::vector<int> z_function(std::string s);
	//! @brief Trivial algorithm for getting prefix function for string
	//! @param s string
	//! @return vector of prefix function for given string
	static std::vector<int> prefix_function_trivial(std::string s);
	//! @brief Trivial algorithm for getting prefix function for string
	//! @param s string
	//! @return vector of prefix function for given string
	static std::vector<int> prefix_function(std::string s);
	//! @brief Calculates hash for string with using provided weight coefficient
	//! @param s string
	//! @param weightCoef coefficient for calculating hash, default value 31
	//! @return hash for provided string
	static long long hash_strung(std::string s, long long weightCoef = 31);
	//! @brief Searches and returns all substrings in given string(text)
	//! @param substring string for looking
	//! @param text given text in which we are looking for a substring
	//! @param divider symbol which is not present in text/substring
	//! @return number of substrings in given string
	static unsigned SearchSubstringInText(std::string substring, std::string text, std::string divider);
	//! @brief Non-recursive algorithm for unordered generation of permutations.
	//! For the string s of length n, for any k from 0 to n! - 1 inclusive, the following
	//! modifies s to provide a unique permutation (that is, different from those generated for
	//! any other k value on that range). To generate all permutations, run it for all n! k
	//! values on the original value of s.Searches and returns all substrings in given string(text)
	//! @param k value for permutation alg
	//! @param s string for generating new permutation
	static void permutationUnordered(int k, std::string &s);
	//! @brief Reverse string with reverse iterator
	//! @param input string for revrse
	//! @return reversed string
	static std::string reverseWithSTL(std::string &input);

};

} //! namespace Fudzi
<commit_msg>1. Removed static methods from strings.hpp<commit_after>/**
 *	@file strings.hpp
 *  @brief Elementary algorithms from e-maxx.ru library
 *  @author Ivan Deviatkin <devyatkin.ivan@gmail.com>, great thanks to Maksim Ivanov
**/

#pragma once

#include <vector>
#include <string>

namespace Fudzi
{
/*
 * Class stores algorithms related to strings operations
 */
class Strings
{
private:
protected:
public:
	//! @brief Trivial algorithm for getting zet function for string
	//! @param s string
	//! @return vector of zet function for given string
	static std::vector<int> z_function_trivial(std::string s);
	//! @brief Efficiency algorithm for getting zet function for string
	//! @param s string
	//! @return vector of zet function for given string
	std::vector<int> z_function(std::string s);
	//! @brief Trivial algorithm for getting prefix function for string
	//! @param s string
	//! @return vector of prefix function for given string
	std::vector<int> prefix_function_trivial(std::string s);
	//! @brief Trivial algorithm for getting prefix function for string
	//! @param s string
	//! @return vector of prefix function for given string
	std::vector<int> prefix_function(std::string s);
	//! @brief Calculates hash for string with using provided weight coefficient
	//! @param s string
	//! @param weightCoef coefficient for calculating hash, default value 31
	//! @return hash for provided string
	long long hash_strung(std::string s, long long weightCoef = 31);
	//! @brief Searches and returns all substrings in given string(text)
	//! @param substring string for looking
	//! @param text given text in which we are looking for a substring
	//! @param divider symbol which is not present in text/substring
	//! @return number of substrings in given string
	unsigned SearchSubstringInText(std::string substring, std::string text, std::string divider);
	//! @brief Non-recursive algorithm for unordered generation of permutations.
	//! For the string s of length n, for any k from 0 to n! - 1 inclusive, the following
	//! modifies s to provide a unique permutation (that is, different from those generated for
	//! any other k value on that range). To generate all permutations, run it for all n! k
	//! values on the original value of s.Searches and returns all substrings in given string(text)
	//! @param k value for permutation alg
	//! @param s string for generating new permutation
	void permutationUnordered(int k, std::string &s);
	//! @brief Reverse string with reverse iterator
	//! @param input string for revrse
	//! @return reversed string
	std::string reverseWithSTL(std::string &input);

};

} //! namespace Fudzi
<|endoftext|>


<commit_before>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/SSTableScan.h"
#include "fnord-base/io/fileutil.h"

namespace fnord {
namespace sstable {

SSTableServlet::SSTableServlet(
    const String& base_path,
    VFS* vfs) :
    base_path_(base_path),
    vfs_(vfs) {}

void SSTableServlet::handleHTTPRequest(
    fnord::http::HTTPRequest* req,
    fnord::http::HTTPResponse* res) {
  URI uri(req->uri());

  res->addHeader("Access-Control-Allow-Origin", "*");

  if (uri.path() == base_path_ + "/scan") {
    try{
      scan(req, res, uri);
    } catch (const Exception& e) {
      res->setStatus(http::kStatusInternalServerError);
      res->addBody(StringUtil::format("error: $0", e.getMessage()));
    }
    return;
  }

  res->setStatus(fnord::http::kStatusNotFound);
  res->addBody("not found");
}

void SSTableServlet::scan(
    fnord::http::HTTPRequest* req,
    fnord::http::HTTPResponse* res,
    const URI& uri) {
  fnord::URI::ParamList params = uri.queryParams();

  auto format = ResponseFormat::CSV;
  std::string format_param;
  if (fnord::URI::getParam(params, "format", &format_param)) {
    format = formatFromString(format_param);
  }

  std::string file_path;
  if (!fnord::URI::getParam(params, "file", &file_path)) {
    res->addBody("error: missing ?file=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  sstable::SSTableReader reader(vfs_->openFile(file_path));
  if (reader.bodySize() == 0) {
    res->addBody("sstable is unfinished (body_size == 0)");
    res->setStatus(http::kStatusInternalServerError);
    return;
  }

  sstable::SSTableColumnSchema schema;
  schema.loadIndex(&reader);

  sstable::SSTableScan sstable_scan(&schema);

  String limit_str;
  if (fnord::URI::getParam(params, "limit", &limit_str)) {
    sstable_scan.setLimit(std::stoul(limit_str));
  }

  String offset_str;
  if (fnord::URI::getParam(params, "offset", &offset_str)) {
    sstable_scan.setOffset(std::stoul(offset_str));
  }

  String order_by;
  String order_fn = "STRASC";
  if (fnord::URI::getParam(params, "order_by", &order_by)) {
    fnord::URI::getParam(params, "order_fn", &order_fn);
    sstable_scan.setOrderBy(order_by, order_fn);
  }

  Buffer buf;
  json::JSONOutputStream json(
      static_cast<std::shared_ptr<OutputStream>>(
          BufferOutputStream::fromBuffer(&buf)));

  auto headers = sstable_scan.columnNames();

  switch (format) {
    case ResponseFormat::CSV:
      buf.append(StringUtil::join(headers, ";"));
      buf.append("\n");
      break;
    case ResponseFormat::JSON:
      json.beginObject();
      json.addString("headers");
      json.addColon();
      json.beginArray();
      for (int i = 0; i < headers.size(); ++i) {
        if (i > 0) json.addComma();
        json.addString(headers[i]);
      }
      json.endArray();
      json.addComma();
      json.addString("rows");
      json.addColon();
      json.beginArray();
      break;
  }

  auto cursor = reader.getCursor();
  int n = 0;
  sstable_scan.execute(
      cursor.get(),
      [&buf, format, &n, &json] (const Vector<String> row) {
    switch (format) {
      case ResponseFormat::CSV:
        buf.append(StringUtil::join(row, ";"));
        buf.append("\n");
        break;
      case ResponseFormat::JSON:
        if (n++ > 0) json.addComma();
        json.beginArray();
        for (int i = 0; i < row.size(); ++i) {
          if (i > 0) json.addComma();
          json.addString(row[i]);
        }
        json.endArray();
        break;
    }
  });

  switch (format) {
    case ResponseFormat::CSV:
      res->addHeader("Content-Type", "text/csv; charset=utf-8");
      break;
    case ResponseFormat::JSON:
      json.endArray();
      json.endObject();
      res->addHeader("Content-Type", "application/json; charset=utf-8");
      break;
  }

  res->setStatus(fnord::http::kStatusOK);
  res->addBody(buf);
}

SSTableServlet::ResponseFormat SSTableServlet::formatFromString(
    const String& format) {
  if (format == "csv") {
    return ResponseFormat::CSV;
  }

  if (format == "json") {
    return ResponseFormat::JSON;
  }

  RAISEF(kIllegalArgumentError, "invalid format: $0", format);
}

}
}
<commit_msg>better JSON response format in SSTableServlet<commit_after>/**
 * This file is part of the "FnordMetric" project
 *   Copyright (c) 2014 Paul Asmuth, Google Inc.
 *
 * FnordMetric is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License v3.0. You should have received a
 * copy of the GNU General Public License along with this program. If not, see
 * <http://www.gnu.org/licenses/>.
 */
#include "fnord-sstable/SSTableServlet.h"
#include "fnord-sstable/sstablereader.h"
#include "fnord-sstable/SSTableScan.h"
#include "fnord-base/io/fileutil.h"

namespace fnord {
namespace sstable {

SSTableServlet::SSTableServlet(
    const String& base_path,
    VFS* vfs) :
    base_path_(base_path),
    vfs_(vfs) {}

void SSTableServlet::handleHTTPRequest(
    fnord::http::HTTPRequest* req,
    fnord::http::HTTPResponse* res) {
  URI uri(req->uri());

  res->addHeader("Access-Control-Allow-Origin", "*");

  if (uri.path() == base_path_ + "/scan") {
    try{
      scan(req, res, uri);
    } catch (const Exception& e) {
      res->setStatus(http::kStatusInternalServerError);
      res->addBody(StringUtil::format("error: $0", e.getMessage()));
    }
    return;
  }

  res->setStatus(fnord::http::kStatusNotFound);
  res->addBody("not found");
}

void SSTableServlet::scan(
    fnord::http::HTTPRequest* req,
    fnord::http::HTTPResponse* res,
    const URI& uri) {
  fnord::URI::ParamList params = uri.queryParams();

  auto format = ResponseFormat::CSV;
  std::string format_param;
  if (fnord::URI::getParam(params, "format", &format_param)) {
    format = formatFromString(format_param);
  }

  std::string file_path;
  if (!fnord::URI::getParam(params, "file", &file_path)) {
    res->addBody("error: missing ?file=... parameter");
    res->setStatus(http::kStatusBadRequest);
    return;
  }

  sstable::SSTableReader reader(vfs_->openFile(file_path));
  if (reader.bodySize() == 0) {
    res->addBody("sstable is unfinished (body_size == 0)");
    res->setStatus(http::kStatusInternalServerError);
    return;
  }

  sstable::SSTableColumnSchema schema;
  schema.loadIndex(&reader);

  sstable::SSTableScan sstable_scan(&schema);

  String limit_str;
  if (fnord::URI::getParam(params, "limit", &limit_str)) {
    sstable_scan.setLimit(std::stoul(limit_str));
  }

  String offset_str;
  if (fnord::URI::getParam(params, "offset", &offset_str)) {
    sstable_scan.setOffset(std::stoul(offset_str));
  }

  String order_by;
  String order_fn = "STRASC";
  if (fnord::URI::getParam(params, "order_by", &order_by)) {
    fnord::URI::getParam(params, "order_fn", &order_fn);
    sstable_scan.setOrderBy(order_by, order_fn);
  }

  Buffer buf;
  json::JSONOutputStream json(
      static_cast<std::shared_ptr<OutputStream>>(
          BufferOutputStream::fromBuffer(&buf)));

  auto headers = sstable_scan.columnNames();

  switch (format) {
    case ResponseFormat::CSV:
      buf.append(StringUtil::join(headers, ";"));
      buf.append("\n");
      break;
    case ResponseFormat::JSON:
      json.beginArray();
      break;
  }

  auto cursor = reader.getCursor();
  int n = 0;
  sstable_scan.execute(
      cursor.get(),
      [&buf, format, &n, &json, &headers] (const Vector<String> row) {
    switch (format) {
      case ResponseFormat::CSV:
        buf.append(StringUtil::join(row, ";"));
        buf.append("\n");
        break;
      case ResponseFormat::JSON:
        if (n++ > 0) json.addComma();
        json.beginObject();
        for (int i = 0; i < row.size(); ++i) {
          if (i > 0) json.addComma();
          json.addString(headers[i]);
          json.addColon();
          json.addString(row[i]);
        }
        json.endObject();
        break;
    }
  });

  switch (format) {
    case ResponseFormat::CSV:
      res->addHeader("Content-Type", "text/csv; charset=utf-8");
      break;
    case ResponseFormat::JSON:
      json.endArray();
      res->addHeader("Content-Type", "application/json; charset=utf-8");
      break;
  }

  res->setStatus(fnord::http::kStatusOK);
  res->addBody(buf);
}

SSTableServlet::ResponseFormat SSTableServlet::formatFromString(
    const String& format) {
  if (format == "csv") {
    return ResponseFormat::CSV;
  }

  if (format == "json") {
    return ResponseFormat::JSON;
  }

  RAISEF(kIllegalArgumentError, "invalid format: $0", format);
}

}
}
<|endoftext|>