<commit_before>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_CREATE_TORRENT_HPP_INCLUDED
#define TORRENT_CREATE_TORRENT_HPP_INCLUDED

#include "libtorrent/bencode.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/file_storage.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/utf8.hpp"
#include "libtorrent/allocator.hpp"

#include <vector>
#include <string>
#include <utility>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

namespace libtorrent
{
	namespace fs = boost::filesystem;
	namespace pt = boost::posix_time;
	class torrent_info;

	struct TORRENT_EXPORT create_torrent
	{
		create_torrent(file_storage& fs, int piece_size = 0, int pad_file_limit = -1);
		create_torrent(torrent_info const& ti);
		entry generate() const;

		file_storage const& files() const { return m_files; }

		void set_comment(char const* str);
		void set_creator(char const* str);
		void set_hash(int index, sha1_hash const& h);
		void add_url_seed(std::string const& url);
		void add_node(std::pair<std::string, int> const& node);
		void add_tracker(std::string const& url, int tier = 0);
		void set_priv(bool p) { m_private = p; }

		int num_pieces() const { return m_files.num_pieces(); }
		int piece_length() const { return m_files.piece_length(); }
		int piece_size(int i) const { return m_files.piece_size(i); }
		bool priv() const { return m_private; }

	private:

		file_storage& m_files;
		// if m_info_dict is initialized, it is
		// used instead of m_files to generate
		// the info dictionary
		entry m_info_dict;

		// the urls to the trackers
		typedef std::pair<std::string, int> announce_entry;
		std::vector<announce_entry> m_urls;

		std::vector<std::string> m_url_seeds;

		std::vector<sha1_hash> m_piece_hash;

		// dht nodes to add to the routing table/bootstrap from
		typedef std::vector<std::pair<std::string, int> > nodes_t;
		nodes_t m_nodes;

		// the hash that identifies this torrent
		// is mutable because it's calculated
		// lazily
		mutable sha1_hash m_info_hash;

		// if a creation date is found in the torrent file
		// this will be set to that, otherwise it'll be
		// 1970, Jan 1
		pt::ptime m_creation_date;

		// if a comment is found in the torrent file
		// this will be set to that comment
		std::string m_comment;

		// an optional string naming the software used
		// to create the torrent file
		std::string m_created_by;

		// this is used when creating a torrent. If there's
		// only one file there are cases where it's impossible
		// to know if it should be written as a multifile torrent
		// or not. e.g. test/test  there's one file and one directory
		// and they have the same name.
		bool m_multifile;

		// this is true if the torrent is private. i.e., is should not
		// be announced on the dht
		bool m_private;
	};

	namespace detail
	{
		inline bool default_pred(boost::filesystem::path const&) { return true; }
		inline bool wdefault_pred(boost::filesystem::wpath const&) { return true; }

		inline bool ignore_subdir(std::string const& leaf)
		{ return leaf == ".." || leaf == "."; }

		inline bool ignore_subdir(std::wstring const& leaf)
		{ return leaf == L".." || leaf == L"."; }

		inline void nop(int i) {}

		int get_file_attributes(boost::filesystem::path const& p);
		int get_file_attributes(boost::filesystem::wpath const& p);

		template <class Pred, class Str, class PathTraits>
		void add_files_impl(file_storage& fs, boost::filesystem::basic_path<Str, PathTraits> const& p
			, boost::filesystem::basic_path<Str, PathTraits> const& l, Pred pred)
		{
			using boost::filesystem::basic_path;
			using boost::filesystem::basic_directory_iterator;
#if BOOST_VERSION < 103600
			Str const& leaf = l.leaf();
#else
			Str const& leaf = l.filename();
#endif
			if (ignore_subdir(leaf)) return;
			if (!pred(l)) return;
			basic_path<Str, PathTraits> f(p / l);
			if (is_directory(f))
			{
				for (basic_directory_iterator<basic_path<Str, PathTraits> > i(f), end; i != end; ++i)
#if BOOST_VERSION < 103600
					add_files_impl(fs, p, l / i->path().leaf(), pred);
#else
					add_files_impl(fs, p, l / i->path().filename(), pred);
#endif
			}
			else
			{
				int file_flags = get_file_attributes(f);
				fs.add_file(l, file_size(f), file_flags);
			}
		}
	}

	// path versions

	template <class Pred>
	void add_files(file_storage& fs, boost::filesystem::path const& file, Pred p)
	{
		using boost::filesystem::path;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), path(file.leaf()), p);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), path(file.filename()), p);
#endif
	}

	inline void add_files(file_storage& fs, boost::filesystem::path const& file)
	{
		using boost::filesystem::path;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), path(file.leaf()), detail::default_pred);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), path(file.filename()), detail::default_pred);
#endif
	}

	struct piece_holder
	{
		piece_holder(int bytes): m_piece(page_aligned_allocator::malloc(bytes)) {}
		~piece_holder() { page_aligned_allocator::free(m_piece); }
		char* bytes() { return m_piece; }
	private:
		char* m_piece;
	};

	template <class Fun>
	void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f)
	{
		file_pool fp;
		boost::scoped_ptr<storage_interface> st(
			default_storage_constructor(const_cast<file_storage&>(t.files()), p, fp));

		// calculate the hash for all pieces
		int num = t.num_pieces();
		piece_holder buf(t.piece_length());
		for (int i = 0; i < num; ++i)
		{
			// read hits the disk and will block. Progress should
			// be updated in between reads
			st->read(buf.bytes(), i, 0, t.piece_size(i));
			hasher h(buf.bytes(), t.piece_size(i));
			t.set_hash(i, h.final());
			f(i);
		}
	}

	inline void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p)
	{
		set_piece_hashes(t, p, detail::nop);
	}

	// wpath versions

	template <class Pred>
	void add_files(file_storage& fs, boost::filesystem::wpath const& file, Pred p)
	{
		using boost::filesystem::wpath;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), wpath(file.leaf()), p);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), wpath(file.filename()), p);
#endif
	}

	inline void add_files(file_storage& fs, boost::filesystem::wpath const& file)
	{
		using boost::filesystem::wpath;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), wpath(file.leaf()), detail::wdefault_pred);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), wpath(file.filename()), detail::wdefault_pred);
#endif
	}

	template <class Fun>
	void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f)
	{
		file_pool fp;
		std::string utf8;
		wchar_utf8(p.string(), utf8);
		boost::scoped_ptr<storage_interface> st(
			default_storage_constructor(const_cast<file_storage&>(t.files()), utf8, fp));

		// calculate the hash for all pieces
		int num = t.num_pieces();
		std::vector<char> buf(t.piece_length());
		for (int i = 0; i < num; ++i)
		{
			// read hits the disk and will block. Progress should
			// be updated in between reads
			st->read(&buf[0], i, 0, t.piece_size(i));
			hasher h(&buf[0], t.piece_size(i));
			t.set_hash(i, h.final());
			f(i);
		}
	}

	inline void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p)
	{
		set_piece_hashes(t, p, detail::nop);
	}
}

#endif

<commit_msg>added missing export directives<commit_after>/*

Copyright (c) 2008, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#ifndef TORRENT_CREATE_TORRENT_HPP_INCLUDED
#define TORRENT_CREATE_TORRENT_HPP_INCLUDED

#include "libtorrent/bencode.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/file_storage.hpp"
#include "libtorrent/file_pool.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/storage.hpp"
#include "libtorrent/hasher.hpp"
#include "libtorrent/utf8.hpp"
#include "libtorrent/allocator.hpp"

#include <vector>
#include <string>
#include <utility>

#ifdef _MSC_VER
#pragma warning(push, 1)
#endif

#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/optional.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/scoped_ptr.hpp>

#ifdef _MSC_VER
#pragma warning(pop)
#endif

namespace libtorrent
{
	namespace fs = boost::filesystem;
	namespace pt = boost::posix_time;
	class torrent_info;

	struct TORRENT_EXPORT create_torrent
	{
		create_torrent(file_storage& fs, int piece_size = 0, int pad_file_limit = -1);
		create_torrent(torrent_info const& ti);
		entry generate() const;

		file_storage const& files() const { return m_files; }

		void set_comment(char const* str);
		void set_creator(char const* str);
		void set_hash(int index, sha1_hash const& h);
		void add_url_seed(std::string const& url);
		void add_node(std::pair<std::string, int> const& node);
		void add_tracker(std::string const& url, int tier = 0);
		void set_priv(bool p) { m_private = p; }

		int num_pieces() const { return m_files.num_pieces(); }
		int piece_length() const { return m_files.piece_length(); }
		int piece_size(int i) const { return m_files.piece_size(i); }
		bool priv() const { return m_private; }

	private:

		file_storage& m_files;
		// if m_info_dict is initialized, it is
		// used instead of m_files to generate
		// the info dictionary
		entry m_info_dict;

		// the urls to the trackers
		typedef std::pair<std::string, int> announce_entry;
		std::vector<announce_entry> m_urls;

		std::vector<std::string> m_url_seeds;

		std::vector<sha1_hash> m_piece_hash;

		// dht nodes to add to the routing table/bootstrap from
		typedef std::vector<std::pair<std::string, int> > nodes_t;
		nodes_t m_nodes;

		// the hash that identifies this torrent
		// is mutable because it's calculated
		// lazily
		mutable sha1_hash m_info_hash;

		// if a creation date is found in the torrent file
		// this will be set to that, otherwise it'll be
		// 1970, Jan 1
		pt::ptime m_creation_date;

		// if a comment is found in the torrent file
		// this will be set to that comment
		std::string m_comment;

		// an optional string naming the software used
		// to create the torrent file
		std::string m_created_by;

		// this is used when creating a torrent. If there's
		// only one file there are cases where it's impossible
		// to know if it should be written as a multifile torrent
		// or not. e.g. test/test  there's one file and one directory
		// and they have the same name.
		bool m_multifile;

		// this is true if the torrent is private. i.e., is should not
		// be announced on the dht
		bool m_private;
	};

	namespace detail
	{
		inline bool default_pred(boost::filesystem::path const&) { return true; }
		inline bool wdefault_pred(boost::filesystem::wpath const&) { return true; }

		inline bool ignore_subdir(std::string const& leaf)
		{ return leaf == ".." || leaf == "."; }

		inline bool ignore_subdir(std::wstring const& leaf)
		{ return leaf == L".." || leaf == L"."; }

		inline void nop(int i) {}

		int TORRENT_EXPORT get_file_attributes(boost::filesystem::path const& p);
		int TORRENT_EXPORT get_file_attributes(boost::filesystem::wpath const& p);

		template <class Pred, class Str, class PathTraits>
		void add_files_impl(file_storage& fs, boost::filesystem::basic_path<Str, PathTraits> const& p
			, boost::filesystem::basic_path<Str, PathTraits> const& l, Pred pred)
		{
			using boost::filesystem::basic_path;
			using boost::filesystem::basic_directory_iterator;
#if BOOST_VERSION < 103600
			Str const& leaf = l.leaf();
#else
			Str const& leaf = l.filename();
#endif
			if (ignore_subdir(leaf)) return;
			if (!pred(l)) return;
			basic_path<Str, PathTraits> f(p / l);
			if (is_directory(f))
			{
				for (basic_directory_iterator<basic_path<Str, PathTraits> > i(f), end; i != end; ++i)
#if BOOST_VERSION < 103600
					add_files_impl(fs, p, l / i->path().leaf(), pred);
#else
					add_files_impl(fs, p, l / i->path().filename(), pred);
#endif
			}
			else
			{
				int file_flags = get_file_attributes(f);
				fs.add_file(l, file_size(f), file_flags);
			}
		}
	}

	// path versions

	template <class Pred>
	void add_files(file_storage& fs, boost::filesystem::path const& file, Pred p)
	{
		using boost::filesystem::path;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), path(file.leaf()), p);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), path(file.filename()), p);
#endif
	}

	inline void add_files(file_storage& fs, boost::filesystem::path const& file)
	{
		using boost::filesystem::path;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), path(file.leaf()), detail::default_pred);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), path(file.filename()), detail::default_pred);
#endif
	}

	struct piece_holder
	{
		piece_holder(int bytes): m_piece(page_aligned_allocator::malloc(bytes)) {}
		~piece_holder() { page_aligned_allocator::free(m_piece); }
		char* bytes() { return m_piece; }
	private:
		char* m_piece;
	};

	template <class Fun>
	void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p, Fun f)
	{
		file_pool fp;
		boost::scoped_ptr<storage_interface> st(
			default_storage_constructor(const_cast<file_storage&>(t.files()), p, fp));

		// calculate the hash for all pieces
		int num = t.num_pieces();
		piece_holder buf(t.piece_length());
		for (int i = 0; i < num; ++i)
		{
			// read hits the disk and will block. Progress should
			// be updated in between reads
			st->read(buf.bytes(), i, 0, t.piece_size(i));
			hasher h(buf.bytes(), t.piece_size(i));
			t.set_hash(i, h.final());
			f(i);
		}
	}

	inline void set_piece_hashes(create_torrent& t, boost::filesystem::path const& p)
	{
		set_piece_hashes(t, p, detail::nop);
	}

	// wpath versions

	template <class Pred>
	void add_files(file_storage& fs, boost::filesystem::wpath const& file, Pred p)
	{
		using boost::filesystem::wpath;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), wpath(file.leaf()), p);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), wpath(file.filename()), p);
#endif
	}

	inline void add_files(file_storage& fs, boost::filesystem::wpath const& file)
	{
		using boost::filesystem::wpath;
#if BOOST_VERSION < 103600
		detail::add_files_impl(fs, complete(file).branch_path(), wpath(file.leaf()), detail::wdefault_pred);
#else
		detail::add_files_impl(fs, complete(file).parent_path(), wpath(file.filename()), detail::wdefault_pred);
#endif
	}

	template <class Fun>
	void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p, Fun f)
	{
		file_pool fp;
		std::string utf8;
		wchar_utf8(p.string(), utf8);
		boost::scoped_ptr<storage_interface> st(
			default_storage_constructor(const_cast<file_storage&>(t.files()), utf8, fp));

		// calculate the hash for all pieces
		int num = t.num_pieces();
		std::vector<char> buf(t.piece_length());
		for (int i = 0; i < num; ++i)
		{
			// read hits the disk and will block. Progress should
			// be updated in between reads
			st->read(&buf[0], i, 0, t.piece_size(i));
			hasher h(&buf[0], t.piece_size(i));
			t.set_hash(i, h.final());
			f(i);
		}
	}

	inline void set_piece_hashes(create_torrent& t, boost::filesystem::wpath const& p)
	{
		set_piece_hashes(t, p, detail::nop);
	}
}

#endif

<|endoftext|>


<commit_before>#include "json-to-value.hh"

#include <variant>
#include <nlohmann/json.hpp>
#include <nlohmann/detail/exceptions.hpp>

using json = nlohmann::json;
using std::unique_ptr;

namespace nix {

void JSONParseError::anchor() {}

// for more information, refer to
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
class JSONSax : nlohmann::json_sax<json> {
    class JSONState {
    protected:
        unique_ptr<JSONState> parent;
        Value * v;
    public:
        virtual unique_ptr<JSONState> resolve(EvalState &)
        {
            throw std::logic_error("tried to close toplevel json parser state");
        };
        explicit JSONState(unique_ptr<JSONState>&& p) : parent(std::move(p)), v(nullptr) {};
        explicit JSONState(Value* v) : v(v) {};
        JSONState(JSONState& p) = delete;
        Value& value(EvalState & state)
        {
            if (v == nullptr)
                v = state.allocValue();
            return *v;
        };
        virtual ~JSONState() {};
        virtual void add() {};
    };

    class JSONObjectState : public JSONState {
        using JSONState::JSONState;
        ValueMap attrs = ValueMap();
        virtual unique_ptr<JSONState> resolve(EvalState & state) override
        {
            Value& v = parent->value(state);
            state.mkAttrs(v, attrs.size());
            for (auto & i : attrs)
                v.attrs->push_back(Attr(i.first, i.second));
            return std::move(parent);
        }
        virtual void add() override { v = nullptr; };
    public:
        void key(string_t& name, EvalState & state)
        {
            attrs[state.symbols.create(name)] = &value(state);
        }
    };

    class JSONListState : public JSONState {
        ValueVector values = ValueVector();
        virtual unique_ptr<JSONState> resolve(EvalState & state) override
        {
            Value& v = parent->value(state);
            state.mkList(v, values.size());
            for (size_t n = 0; n < values.size(); ++n) {
                v.listElems()[n] = values[n];
            }
            return std::move(parent);
        }
        virtual void add() override {
            values.push_back(v);
            v = nullptr;
        };
    public:
        JSONListState(unique_ptr<JSONState>&& p, std::size_t reserve) : JSONState(std::move(p))
        {
            values.reserve(reserve);
        }
    };

    EvalState & state;
    unique_ptr<JSONState> rs;

    template<typename T, typename... Args> inline bool handle_value(T f, Args... args)
    {
        f(rs->value(state), args...);
        rs->add();
        return true;
    }

public:
    JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};

    bool null()
    {
        return handle_value(mkNull);
    }

    bool boolean(bool val)
    {
        return handle_value(mkBool, val);
    }

    bool number_integer(number_integer_t val)
    {
        return handle_value(mkInt, val);
    }

    bool number_unsigned(number_unsigned_t val)
    {
        return handle_value(mkInt, val);
    }

    bool number_float(number_float_t val, const string_t& s)
    {
        return handle_value(mkFloat, val);
    }

    bool string(string_t& val)
    {
        return handle_value<void(Value&, const char*)>(mkString, val.c_str());
    }

    bool start_object(std::size_t len)
    {
        rs = std::make_unique<JSONObjectState>(std::move(rs));
        return true;
    }

    bool key(string_t& name)
    {
        dynamic_cast<JSONObjectState*>(rs.get())->key(name, state);
        return true;
    }

    bool end_object() {
        rs = rs->resolve(state);
        rs->add();
        return true;
    }

    bool end_array() {
        return end_object();
    }

    bool start_array(size_t len) {
        rs = std::make_unique<JSONListState>(std::move(rs),
            len != std::numeric_limits<size_t>::max() ? len : 128);
        return true;
    }

    bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) {
        throw JSONParseError(ex.what());
    }
};

void parseJSON(EvalState & state, const string & s_, Value & v)
{
    JSONSax parser(state, v);
    bool res = json::sax_parse(s_, &parser);
    if (!res)
        throw JSONParseError("Invalid JSON Value");
}

}
<commit_msg>Fix build<commit_after>#include "json-to-value.hh"

#include <variant>
#include <nlohmann/json.hpp>

using json = nlohmann::json;
using std::unique_ptr;

namespace nix {

void JSONParseError::anchor() {}

// for more information, refer to
// https://github.com/nlohmann/json/blob/master/include/nlohmann/detail/input/json_sax.hpp
class JSONSax : nlohmann::json_sax<json> {
    class JSONState {
    protected:
        unique_ptr<JSONState> parent;
        Value * v;
    public:
        virtual unique_ptr<JSONState> resolve(EvalState &)
        {
            throw std::logic_error("tried to close toplevel json parser state");
        };
        explicit JSONState(unique_ptr<JSONState>&& p) : parent(std::move(p)), v(nullptr) {};
        explicit JSONState(Value* v) : v(v) {};
        JSONState(JSONState& p) = delete;
        Value& value(EvalState & state)
        {
            if (v == nullptr)
                v = state.allocValue();
            return *v;
        };
        virtual ~JSONState() {};
        virtual void add() {};
    };

    class JSONObjectState : public JSONState {
        using JSONState::JSONState;
        ValueMap attrs = ValueMap();
        virtual unique_ptr<JSONState> resolve(EvalState & state) override
        {
            Value& v = parent->value(state);
            state.mkAttrs(v, attrs.size());
            for (auto & i : attrs)
                v.attrs->push_back(Attr(i.first, i.second));
            return std::move(parent);
        }
        virtual void add() override { v = nullptr; };
    public:
        void key(string_t& name, EvalState & state)
        {
            attrs[state.symbols.create(name)] = &value(state);
        }
    };

    class JSONListState : public JSONState {
        ValueVector values = ValueVector();
        virtual unique_ptr<JSONState> resolve(EvalState & state) override
        {
            Value& v = parent->value(state);
            state.mkList(v, values.size());
            for (size_t n = 0; n < values.size(); ++n) {
                v.listElems()[n] = values[n];
            }
            return std::move(parent);
        }
        virtual void add() override {
            values.push_back(v);
            v = nullptr;
        };
    public:
        JSONListState(unique_ptr<JSONState>&& p, std::size_t reserve) : JSONState(std::move(p))
        {
            values.reserve(reserve);
        }
    };

    EvalState & state;
    unique_ptr<JSONState> rs;

    template<typename T, typename... Args> inline bool handle_value(T f, Args... args)
    {
        f(rs->value(state), args...);
        rs->add();
        return true;
    }

public:
    JSONSax(EvalState & state, Value & v) : state(state), rs(new JSONState(&v)) {};

    bool null()
    {
        return handle_value(mkNull);
    }

    bool boolean(bool val)
    {
        return handle_value(mkBool, val);
    }

    bool number_integer(number_integer_t val)
    {
        return handle_value(mkInt, val);
    }

    bool number_unsigned(number_unsigned_t val)
    {
        return handle_value(mkInt, val);
    }

    bool number_float(number_float_t val, const string_t& s)
    {
        return handle_value(mkFloat, val);
    }

    bool string(string_t& val)
    {
        return handle_value<void(Value&, const char*)>(mkString, val.c_str());
    }

    bool start_object(std::size_t len)
    {
        rs = std::make_unique<JSONObjectState>(std::move(rs));
        return true;
    }

    bool key(string_t& name)
    {
        dynamic_cast<JSONObjectState*>(rs.get())->key(name, state);
        return true;
    }

    bool end_object() {
        rs = rs->resolve(state);
        rs->add();
        return true;
    }

    bool end_array() {
        return end_object();
    }

    bool start_array(size_t len) {
        rs = std::make_unique<JSONListState>(std::move(rs),
            len != std::numeric_limits<size_t>::max() ? len : 128);
        return true;
    }

    bool parse_error(std::size_t, const std::string&, const nlohmann::detail::exception& ex) {
        throw JSONParseError(ex.what());
    }
};

void parseJSON(EvalState & state, const string & s_, Value & v)
{
    JSONSax parser(state, v);
    bool res = json::sax_parse(s_, &parser);
    if (!res)
        throw JSONParseError("Invalid JSON Value");
}

}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 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_EXPRESSIONS_GRAMMAR_HPP
#define MAPNIK_EXPRESSIONS_GRAMMAR_HPP

// mapnik
#include <mapnik/value.hpp>
#include <mapnik/expression_node.hpp>

// boost
#include <boost/version.hpp>
#include <boost/variant.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/concept_check.hpp>

// spirit2
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_action.hpp>

// fusion
#include <boost/fusion/include/adapt_struct.hpp>

// phoenix
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>

namespace mapnik
{

using namespace boost;
namespace qi = boost::spirit::qi;
namespace standard_wide =  boost::spirit::standard_wide;
using standard_wide::space_type;

struct unicode_impl
{
    template <typename T>
    struct result
    {
        typedef UnicodeString type;
    };

    explicit unicode_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    UnicodeString operator()(std::string const& str) const
    {
        return tr_.transcode(str.c_str());
    }

    mapnik::transcoder const& tr_;
};

struct regex_match_impl
{
    template <typename T0, typename T1>
    struct result
    {
        typedef expr_node type;
    };

    explicit regex_match_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    template <typename T0,typename T1>
    expr_node operator() (T0 & node, T1 const& pattern) const
    {
#if defined(BOOST_REGEX_HAS_ICU)
        return regex_match_node(node,tr_.transcode(pattern.c_str()));
#else
        return regex_match_node(node,pattern);
#endif
    }

    mapnik::transcoder const& tr_;
};

struct regex_replace_impl
{
    template <typename T0, typename T1, typename T2>
    struct result
    {
        typedef expr_node type;
    };

    explicit regex_replace_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    template <typename T0,typename T1,typename T2>
    expr_node operator() (T0 & node, T1 const& pattern, T2 const& format) const
    {
#if defined(BOOST_REGEX_HAS_ICU)
        return regex_replace_node(node,tr_.transcode(pattern.c_str()),tr_.transcode(format.c_str()));
#else
        return regex_replace_node(node,pattern,format);
#endif
    }

    mapnik::transcoder const& tr_;
};

template <typename Iterator>
struct expression_grammar : qi::grammar<Iterator, expr_node(), space_type>
{
    typedef qi::rule<Iterator, expr_node(), space_type> rule_type;

    explicit expression_grammar(mapnik::transcoder const& tr)
        : expression_grammar::base_type(expr),
          unicode_(unicode_impl(tr)),
          regex_match_(regex_match_impl(tr)),
          regex_replace_(regex_replace_impl(tr))
    {
        using boost::phoenix::construct;
        using qi::_1;
        using qi::_a;
        using qi::_b;
        using qi::_r1;
#if BOOST_VERSION > 104200
        using qi::no_skip;
#else
        using qi::lexeme;
#endif
        using qi::_val;
        using qi::lit;
        using qi::int_;
        using qi::double_;
        using standard_wide::char_;

        expr = logical_expr.alias();

        logical_expr = not_expr [_val = _1]
            >>
            *(  (  ( lit("and") | lit("&&")) >> not_expr [_val && _1] )
                | (( lit("or") | lit("||")) >> not_expr [_val || _1])
                )
            ;

        not_expr =
            cond_expr [_val = _1 ]
            | ((lit("not") | lit('!')) >> cond_expr [ _val = !_1 ])
            ;

        cond_expr = equality_expr [_val = _1] | additive_expr [_val = _1]
            ;

        equality_expr =
            relational_expr [_val = _1]
            >> *(  ( (lit("=") | lit("eq") | lit("is")) >> relational_expr [_val == _1])
                   | (( lit("!=") | lit("<>") | lit("neq") ) >> relational_expr [_val != _1])
                )
            ;

        regex_match_expr = lit(".match")
            >> lit('(')
            >> ustring [_val = _1]
            >> lit(')')
            ;

        regex_replace_expr =
            lit(".replace")
            >> lit('(')
            >> ustring           [_a = _1]
            >> lit(',')
            >> ustring           [_b = _1]
            >> lit(')')          [_val = regex_replace_(_r1,_a,_b)]
            ;

        relational_expr = additive_expr[_val = _1]
            >>
            *(  (   (lit("<=") | lit("le") ) >> additive_expr [ _val <= _1 ])
                | ( (lit('<')  | lit("lt") ) >> additive_expr [ _val <  _1 ])
                | ( (lit(">=") | lit("ge") ) >> additive_expr [ _val >= _1 ])
                | ( (lit('>')  | lit("gt") ) >> additive_expr [ _val >  _1 ])
                )
            ;
        additive_expr = multiplicative_expr [_val = _1]
            >> * (   '+' >> multiplicative_expr[_val += _1]
                     | '-' >> multiplicative_expr[_val -= _1]
                )
            ;

        multiplicative_expr = primary_expr [_val = _1]
            >> *(     '*' >> primary_expr [_val *= _1]
                      | '/' >> primary_expr [_val /= _1]
                      | '%' >> primary_expr [_val %= _1]
                      |  regex_match_expr[_val = regex_match_(_val, _1)]
                      |  regex_replace_expr(_val) [_val = _1]
                )
            ;

        primary_expr = strict_double [_val = _1]
            | int_ [_val = _1]
            | lit("true") [_val = true]
            | lit("false") [_val = false]
            | lit("null") [_val = value_null() ]
            | ustring [_val = unicode_(_1) ]
            | attr [_val = construct<mapnik::attribute>( _1 ) ]
            | '(' >> expr [_val = _1 ] >> ')'
            ;

#if BOOST_VERSION > 104200
        ustring %= '\'' >> no_skip[*~char_('\'')] >> '\'';
        attr %= '[' >> no_skip[+~char_(']')] >> ']';
#else
        ustring %= '\'' >> lexeme[*(char_-'\'')] >> '\'';
        attr %= '[' >> lexeme[+(char_ - ']')] >> ']';
#endif
    }

    qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
    boost::phoenix::function<unicode_impl> unicode_;
    boost::phoenix::function<regex_match_impl> regex_match_;
    boost::phoenix::function<regex_replace_impl> regex_replace_;
    //
    rule_type expr;
    rule_type equality_expr;
    rule_type cond_expr;
    rule_type relational_expr;
    rule_type logical_expr;
    rule_type additive_expr;
    rule_type multiplicative_expr;
    rule_type not_expr;
    rule_type primary_expr;
    qi::rule<Iterator, std::string() > regex_match_expr;
    qi::rule<Iterator, expr_node(expr_node), qi::locals<std::string,std::string>, space_type> regex_replace_expr;
    qi::rule<Iterator, std::string() , space_type> attr;
    qi::rule<Iterator, std::string() > ustring;
};

} // namespace

#endif  // MAPNIK_EXPRESSIONS_GRAMMAR_HPP
<commit_msg>add support for escaped characters/hex notataion i.e \x20 allow both single and double quoted strings<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2011 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_EXPRESSIONS_GRAMMAR_HPP
#define MAPNIK_EXPRESSIONS_GRAMMAR_HPP

// mapnik
#include <mapnik/value.hpp>
#include <mapnik/expression_node.hpp>

// boost
#include <boost/version.hpp>
#include <boost/variant.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/concept_check.hpp>

// spirit2
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_action.hpp>

// fusion
#include <boost/fusion/include/adapt_struct.hpp>

// phoenix
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/home/phoenix/object/construct.hpp>

namespace mapnik
{

using namespace boost;
namespace qi = boost::spirit::qi;
namespace standard_wide =  boost::spirit::standard_wide;
using standard_wide::space_type;

struct unicode_impl
{
    template <typename T>
    struct result
    {
        typedef UnicodeString type;
    };

    explicit unicode_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    UnicodeString operator()(std::string const& str) const
    {
        return tr_.transcode(str.c_str());
    }

    mapnik::transcoder const& tr_;
};

struct regex_match_impl
{
    template <typename T0, typename T1>
    struct result
    {
        typedef expr_node type;
    };

    explicit regex_match_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    template <typename T0,typename T1>
    expr_node operator() (T0 & node, T1 const& pattern) const
    {
#if defined(BOOST_REGEX_HAS_ICU)
        return regex_match_node(node,tr_.transcode(pattern.c_str()));
#else
        return regex_match_node(node,pattern);
#endif
    }

    mapnik::transcoder const& tr_;
};

struct regex_replace_impl
{
    template <typename T0, typename T1, typename T2>
    struct result
    {
        typedef expr_node type;
    };

    explicit regex_replace_impl(mapnik::transcoder const& tr)
        : tr_(tr) {}

    template <typename T0,typename T1,typename T2>
    expr_node operator() (T0 & node, T1 const& pattern, T2 const& format) const
    {
#if defined(BOOST_REGEX_HAS_ICU)
        return regex_replace_node(node,tr_.transcode(pattern.c_str()),tr_.transcode(format.c_str()));
#else
        return regex_replace_node(node,pattern,format);
#endif
    }

    mapnik::transcoder const& tr_;
};

template <typename Iterator>
struct expression_grammar : qi::grammar<Iterator, expr_node(), space_type>
{
    typedef qi::rule<Iterator, expr_node(), space_type> rule_type;

    explicit expression_grammar(mapnik::transcoder const& tr)
        : expression_grammar::base_type(expr),
          unicode_(unicode_impl(tr)),
          regex_match_(regex_match_impl(tr)),
          regex_replace_(regex_replace_impl(tr))
    {
        using boost::phoenix::construct;
        using qi::_1;
        using qi::_a;
        using qi::_b;
        using qi::_r1;
#if BOOST_VERSION > 104200
        using qi::no_skip;
#else
        using qi::lexeme;
#endif
        using qi::_val;
        using qi::lit;
        using qi::int_;
        using qi::double_;
        using qi::hex;
        using qi::omit;
        using standard_wide::char_;

        expr = logical_expr.alias();

        logical_expr = not_expr [_val = _1]
            >>
            *(  (  ( lit("and") | lit("&&")) >> not_expr [_val && _1] )
                | (( lit("or") | lit("||")) >> not_expr [_val || _1])
                )
            ;

        not_expr =
            cond_expr [_val = _1 ]
            | ((lit("not") | lit('!')) >> cond_expr [ _val = !_1 ])
            ;

        cond_expr = equality_expr [_val = _1] | additive_expr [_val = _1]
            ;

        equality_expr =
            relational_expr [_val = _1]
            >> *(  ( (lit("=") | lit("eq") | lit("is")) >> relational_expr [_val == _1])
                   | (( lit("!=") | lit("<>") | lit("neq") ) >> relational_expr [_val != _1])
                )
            ;

        regex_match_expr = lit(".match")
            >> lit('(')
            >> ustring [_val = _1]
            >> lit(')')
            ;

        regex_replace_expr =
            lit(".replace")
            >> lit('(')
            >> ustring           [_a = _1]
            >> lit(',')
            >> ustring           [_b = _1]
            >> lit(')')          [_val = regex_replace_(_r1,_a,_b)]
            ;

        relational_expr = additive_expr[_val = _1]
            >>
            *(  (   (lit("<=") | lit("le") ) >> additive_expr [ _val <= _1 ])
                | ( (lit('<')  | lit("lt") ) >> additive_expr [ _val <  _1 ])
                | ( (lit(">=") | lit("ge") ) >> additive_expr [ _val >= _1 ])
                | ( (lit('>')  | lit("gt") ) >> additive_expr [ _val >  _1 ])
                )
            ;
        additive_expr = multiplicative_expr [_val = _1]
            >> * (   '+' >> multiplicative_expr[_val += _1]
                     | '-' >> multiplicative_expr[_val -= _1]
                )
            ;

        multiplicative_expr = primary_expr [_val = _1]
            >> *(     '*' >> primary_expr [_val *= _1]
                      | '/' >> primary_expr [_val /= _1]
                      | '%' >> primary_expr [_val %= _1]
                      |  regex_match_expr[_val = regex_match_(_val, _1)]
                      |  regex_replace_expr(_val) [_val = _1]
                )
            ;

        primary_expr = strict_double [_val = _1]
            | int_ [_val = _1]
            | lit("true") [_val = true]
            | lit("false") [_val = false]
            | lit("null") [_val = value_null() ]
            | ustring [_val = unicode_(_1) ]
            | attr [_val = construct<mapnik::attribute>( _1 ) ]
            | '(' >> expr [_val = _1 ] >> ')'
            ;

        unesc_char.add("\\a", '\a')("\\b", '\b')("\\f", '\f')("\\n", '\n')
            ("\\r", '\r')("\\t", '\t')("\\v", '\v')("\\\\", '\\')
            ("\\\'", '\'')("\\\"", '\"')
            ;

        ustring %= omit[quote_char[_a = _1]]
            >> *(unesc_char | "\\x" >> hex | (char_ - lit(_a)))
            >> lit(_a);

        quote_char %= char_('\'') | char_('"');

#if BOOST_VERSION > 104200
        attr %= '[' >> no_skip[+~char_(']')] >> ']';
#else
        attr %= '[' >> lexeme[+(char_ - ']')] >> ']';
#endif

    }

    qi::real_parser<double, qi::strict_real_policies<double> > strict_double;
    boost::phoenix::function<unicode_impl> unicode_;
    boost::phoenix::function<regex_match_impl> regex_match_;
    boost::phoenix::function<regex_replace_impl> regex_replace_;
    //
    rule_type expr;
    rule_type equality_expr;
    rule_type cond_expr;
    rule_type relational_expr;
    rule_type logical_expr;
    rule_type additive_expr;
    rule_type multiplicative_expr;
    rule_type not_expr;
    rule_type primary_expr;
    qi::rule<Iterator, std::string() > regex_match_expr;
    qi::rule<Iterator, expr_node(expr_node), qi::locals<std::string,std::string>, space_type> regex_replace_expr;
    qi::rule<Iterator, std::string() , space_type> attr;
    qi::rule<Iterator, std::string(), qi::locals<char> > ustring;
    qi::symbols<char const, char const> unesc_char;
    qi::rule<Iterator, char() > quote_char;
};

} // namespace

#endif  // MAPNIK_EXPRESSIONS_GRAMMAR_HPP
<|endoftext|>


<commit_before>#pragma once
#ifndef OPENGM_INDEXING_HXX
#define OPENGM_INDEXING_HXX

#include "opengm/opengm.hxx"
#include "opengm/datastructures/fast_sequence.hxx"

namespace opengm {
   /// \cond HIDDEN_SYMBOLS
   template<class VECTOR>
   bool isEqualValueVector(const VECTOR vector) {
      for(size_t i=0;i<vector.size();++i) {
         if(vector[0]!=vector[i]) {
            return false;
         }
      }
      return true;
   }

   /// walk over a factor / function , and some variables can be  fixed
   template<class Iterator,class FIXED_COORDINATE_INDEX_CONTAINER,class FIXED_COORDINATE_VALUE_CONTAINER>
   class SubShapeWalker{
   public:
      /// constructor
      /// \param shapeBegin begin of the shape of the function
      /// \param dimension size of the function / shape
      /// \param fixedCoordinateIndex container/ random access iterator which contains the position of the variables to fix
      /// \param fixedCoordinateValue container/ random access iterator which contains the values of the variables which are fixed
      SubShapeWalker
      (
         Iterator shapeBegin,
         const size_t dimension,
         const FIXED_COORDINATE_INDEX_CONTAINER & fixedCoordinateIndex,
         const FIXED_COORDINATE_VALUE_CONTAINER & fixedCoordinateValue
      )
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension,0),
      fixedCoordinateValue_(fixedCoordinateValue),
      fixedCoordinateIndex_(fixedCoordinateIndex),
      dimension_(dimension) {
         for(size_t d = 0; d < fixedCoordinateIndex_.size(); ++d) {
            coordinateTuple_[fixedCoordinateIndex_[d]] = fixedCoordinateValue_[d];
         }
      };

      /// reset coordinate to zeros
      void  resetCoordinate() {
         for(size_t i = 0; i < dimension_; ++i) {
            coordinateTuple_[i] = static_cast<size_t>(0);
         }
         for(size_t i = 0; i < fixedCoordinateIndex_.size(); ++i) {
            coordinateTuple_[fixedCoordinateIndex_[i]] = fixedCoordinateValue_[i];
         }
      };

      /// increment coordinate in last - coordinate major order  (therefore the first coordinate is incremented first)
      inline SubShapeWalker & operator++() {
         size_t counter = 0;
         for(size_t d = 0; d < dimension_; ++d) {
            bool atFixedValue = false;
            for(size_t i = counter; i < fixedCoordinateIndex_.size(); ++i) {
               if(d == fixedCoordinateIndex_[i]) {
                  atFixedValue = true;
                  ++counter;
               }
            }
            if(atFixedValue == false) {
               if(coordinateTuple_[d] != shapeBegin_[d] - 1) {
                  coordinateTuple_[d]++;
                  break;
               }
               else {
                  if(d != dimension_ - 1) {
                     coordinateTuple_[d] = 0;
                  }
                  else {
                     coordinateTuple_[d]++;
                     break;
                  }
               }
            }
         }
         return *this;
      };

      /// get the coordinate tuple
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

      /// get the number of elements over which one wants to walk
      size_t subSize() {
         size_t result = 1;
         size_t counter = 0;
         for(size_t d = 0; d < dimension_; ++d) {
            bool fixedVal = false;
            for(size_t i = counter; i < fixedCoordinateIndex_.size(); ++i) {
               if(d == fixedCoordinateIndex_[i]) ///??? replace i with counter for old version
               {
                  fixedVal = true;
                  counter++;
                  break;
               }
            }
            if(fixedVal == false) {
               result *= shapeBegin_[d];
            }
         }
         return result;
      }

   private:
      Iterator  shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const FIXED_COORDINATE_VALUE_CONTAINER & fixedCoordinateValue_;
      const FIXED_COORDINATE_INDEX_CONTAINER & fixedCoordinateIndex_;
      const size_t dimension_;
      //size_t subSize_;
   };

   template<class Iterator>
   class ShapeWalker
   {
   public:
      ShapeWalker(Iterator shapeBegin,size_t dimension)
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension, 0),
      dimension_(dimension) { }
      ShapeWalker & operator++() {
         for(size_t d = 0; d < dimension_; ++d) {
            if( size_t(coordinateTuple_[d]) != (size_t(shapeBegin_[d]) - size_t(1)) ) {
               ++coordinateTuple_[d];
               OPENGM_ASSERT(coordinateTuple_[d]<shapeBegin_[d]);
               break;
            }
            else {
               if(d != dimension_ - 1) {
                  coordinateTuple_[d] = 0;
               }
               else {
                  coordinateTuple_[d]++;
                  break;
               }
            }
         }
         return *this;
      };
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

   private:
      Iterator shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const size_t dimension_;
   };

   template<class Iterator>
   class ShapeWalkerSwitchedOrder
   {
   public:
      ShapeWalkerSwitchedOrder(Iterator shapeBegin,size_t dimension)
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension, 0),
      dimension_(dimension) { }
      ShapeWalkerSwitchedOrder & operator++() {
         for(size_t d = dimension_-1; true; --d) {
            if( size_t(coordinateTuple_[d]) != (size_t(shapeBegin_[d]) - size_t(1)) ) {
               ++coordinateTuple_[d];
               OPENGM_ASSERT(coordinateTuple_[d]<shapeBegin_[d]);
               break;
            }
            else {
               if(d != 0) {
                  coordinateTuple_[d] = 0;
               }
               else {
                  coordinateTuple_[d]++;
                  break;
               }
            }
            if(d==0){
               break;
            }
         }
         return *this;
      };
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

   private:
      Iterator shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const size_t dimension_;
   };

   template<class SHAPE_AB_ITERATOR>
   class TripleShapeWalker{
   public:
      template<class VI_AB,class VI_A,class VI_B>
      TripleShapeWalker
      (
         SHAPE_AB_ITERATOR  shapeABBegin,
         const size_t dimAB,
         const VI_AB & viAB,
         const VI_A & viA,
         const VI_B & viB
      ): shapeABBegin_(shapeABBegin),
      dimensionAB_(dimAB),
      coordinateTupleAB_(viAB.size(), 0),
      coordinateTupleA_(viA.size(), 0),
      coordinateTupleB_(viB.size(), 0),
      viMatchA_(viAB.size(), false),
      viMatchB_(viAB.size(), false),
      viMatchIndexA_(viAB.size()),
      viMatchIndexB_(viAB.size()) {
         OPENGM_ASSERT(dimAB == viAB.size());
         OPENGM_ASSERT( viA.size() != 0);
         OPENGM_ASSERT( viB.size() != 0);
         //vi matching:
         size_t counterA = 0;
         size_t counterB = 0;
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if(counterA<viA.size()) {
               if(viAB[d] == viA[counterA]) {
                  viMatchA_[d] = true;
                  viMatchIndexA_[d] = counterA;
                  counterA++;
               }
            }
            if(counterB<viB.size()) {
               if(viAB[d] == viB[counterB]) {
                  viMatchB_[d] = true;
                  viMatchIndexB_[d] = counterB;
                  counterB++;
               }
            }
         }
      }

      TripleShapeWalker & operator++() {
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if( int (coordinateTupleAB_[d]) != int( int(shapeABBegin_[d]) - int(1))) {
               coordinateTupleAB_[d]++;
               if(viMatchA_[d]) {
                  coordinateTupleA_[viMatchIndexA_[d]]++;
               }
               if(viMatchB_[d]) {
                  coordinateTupleB_[viMatchIndexB_[d]]++;
               }
               break;
            }
            else {
               coordinateTupleAB_[d] = 0;
               if(viMatchA_[d]) {
                  coordinateTupleA_[viMatchIndexA_[d]] = 0;
               }
               if(viMatchB_[d]) {
                  coordinateTupleB_[viMatchIndexB_[d]] = 0;
               }
            }
         }
         return *this;
      };

      const opengm::FastSequence<size_t> &coordinateTupleA()const {
         return coordinateTupleA_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleB()const {
         return coordinateTupleB_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleAB()const {
         return coordinateTupleAB_;
      };
   private:
      SHAPE_AB_ITERATOR shapeABBegin_;
      const size_t dimensionAB_;
      opengm::FastSequence<size_t> coordinateTupleAB_;
      opengm::FastSequence<size_t> coordinateTupleA_;
      opengm::FastSequence<size_t> coordinateTupleB_;
      opengm::FastSequence<bool> viMatchA_;
      opengm::FastSequence<bool> viMatchB_;
      opengm::FastSequence<size_t> viMatchIndexA_;
      opengm::FastSequence<size_t> viMatchIndexB_;
   };

   template<class SHAPE_AB_ITERATOR>
   class DoubleShapeWalker {
   public:
      template<class VI_A,class VI_B>
      DoubleShapeWalker
      (
         SHAPE_AB_ITERATOR shapeABbegin,
         const size_t dimAb,
         const VI_A & viAB,
         const VI_B & viA
      )
      :shapeABbegin_(shapeABbegin),
      dimensionAB_(dimAb),
      coordinateTupleAB_(dimensionAB_, 0),
      coordinateTupleA_(viA.size(), 0),
      viMatchA_(dimensionAB_, false),
      viMatchIndexA_(dimensionAB_) {
         //vi matching:
         size_t counterA = 0;
         for(size_t d = 0; d < dimensionAB_; ++d) {
            for(size_t i = counterA; i < viA.size(); ++i) {
               if(viAB[d] == viA[i]) {
                  viMatchA_[d] = true;
                  viMatchIndexA_[d] = i;
                  ++counterA;
               }
            }
         }
      }

      DoubleShapeWalker & operator++() {
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if(coordinateTupleAB_[d] != shapeABbegin_[d] - 1) {
               coordinateTupleAB_[d]++;
               if(viMatchA_[d] == true) {
                  coordinateTupleA_[viMatchIndexA_[d]]++;
               }
               break;
            }
            else {
               coordinateTupleAB_[d] = 0;
               if(viMatchA_[d] == true) {
                  coordinateTupleA_[viMatchIndexA_[d]] = 0;
               }
            }
         }
         return *this;
      };

      const opengm::FastSequence<size_t> & coordinateTupleA()const {
         return coordinateTupleA_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleAB()const {
            return coordinateTupleAB_;
      };

   private:
      SHAPE_AB_ITERATOR shapeABbegin_;
      const size_t dimensionAB_;
      opengm::FastSequence<size_t> coordinateTupleAB_;
      opengm::FastSequence<size_t> coordinateTupleA_;
      opengm::FastSequence<bool> viMatchA_;
      opengm::FastSequence<size_t> viMatchIndexA_;
   };

} // namespace opengm

#endif // #ifndef OPENGM_INDEXING_HXX
<commit_msg>removed useless instructions<commit_after>#pragma once
#ifndef OPENGM_INDEXING_HXX
#define OPENGM_INDEXING_HXX

#include "opengm/opengm.hxx"
#include "opengm/datastructures/fast_sequence.hxx"

namespace opengm {
   /// \cond HIDDEN_SYMBOLS
   template<class VECTOR>
   bool isEqualValueVector(const VECTOR vector) {
      for(size_t i=0;i<vector.size();++i) {
         if(vector[0]!=vector[i]) {
            return false;
         }
      }
      return true;
   }

   /// walk over a factor / function , and some variables can be  fixed
   template<class Iterator,class FIXED_COORDINATE_INDEX_CONTAINER,class FIXED_COORDINATE_VALUE_CONTAINER>
   class SubShapeWalker{
   public:
      /// constructor
      /// \param shapeBegin begin of the shape of the function
      /// \param dimension size of the function / shape
      /// \param fixedCoordinateIndex container/ random access iterator which contains the position of the variables to fix
      /// \param fixedCoordinateValue container/ random access iterator which contains the values of the variables which are fixed
      SubShapeWalker
      (
         Iterator shapeBegin,
         const size_t dimension,
         const FIXED_COORDINATE_INDEX_CONTAINER & fixedCoordinateIndex,
         const FIXED_COORDINATE_VALUE_CONTAINER & fixedCoordinateValue
      )
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension,0),
      fixedCoordinateValue_(fixedCoordinateValue),
      fixedCoordinateIndex_(fixedCoordinateIndex),
      dimension_(dimension) {
         for(size_t d = 0; d < fixedCoordinateIndex_.size(); ++d) {
            coordinateTuple_[fixedCoordinateIndex_[d]] = fixedCoordinateValue_[d];
         }
      };

      /// reset coordinate to zeros
      void  resetCoordinate() {
         for(size_t i = 0; i < dimension_; ++i) {
            coordinateTuple_[i] = static_cast<size_t>(0);
         }
         for(size_t i = 0; i < fixedCoordinateIndex_.size(); ++i) {
            coordinateTuple_[fixedCoordinateIndex_[i]] = fixedCoordinateValue_[i];
         }
      };

      /// increment coordinate in last - coordinate major order  (therefore the first coordinate is incremented first)
      inline SubShapeWalker & operator++() {
         size_t counter = 0;
         for(size_t d = 0; d < dimension_; ++d) {
            bool atFixedValue = false;
            for(size_t i = counter; i < fixedCoordinateIndex_.size(); ++i) {
               if(d == fixedCoordinateIndex_[i]) {
                  atFixedValue = true;
                  ++counter;
               }
            }
            if(atFixedValue == false) {
               if(coordinateTuple_[d] != shapeBegin_[d] - 1) {
                  coordinateTuple_[d]++;
                  break;
               }
               else {
                  if(d != dimension_ - 1) {
                     coordinateTuple_[d] = 0;
                  }
                  else {
                     coordinateTuple_[d]++;
                     break;
                  }
               }
            }
         }
         return *this;
      };

      /// get the coordinate tuple
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

      /// get the number of elements over which one wants to walk
      size_t subSize() {
         size_t result = 1;
         size_t counter = 0;
         for(size_t d = 0; d < dimension_; ++d) {
            bool fixedVal = false;
            for(size_t i = counter; i < fixedCoordinateIndex_.size(); ++i) {
               if(d == fixedCoordinateIndex_[i]) ///??? replace i with counter for old version
               {
                  fixedVal = true;
                  counter++;
                  break;
               }
            }
            if(fixedVal == false) {
               result *= shapeBegin_[d];
            }
         }
         return result;
      }

   private:
      Iterator  shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const FIXED_COORDINATE_VALUE_CONTAINER & fixedCoordinateValue_;
      const FIXED_COORDINATE_INDEX_CONTAINER & fixedCoordinateIndex_;
      const size_t dimension_;
      //size_t subSize_;
   };

   template<class Iterator>
   class ShapeWalker
   {
   public:
      ShapeWalker(Iterator shapeBegin,size_t dimension)
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension, 0),
      dimension_(dimension) { }
      ShapeWalker & operator++() {
         for(size_t d = 0; d < dimension_; ++d) {
            if( size_t(coordinateTuple_[d]) != (size_t(shapeBegin_[d]) - size_t(1)) ) {
               ++coordinateTuple_[d];
               OPENGM_ASSERT(coordinateTuple_[d]<shapeBegin_[d]);
               break;
            }
            else {
               if(d != dimension_ - 1) {
                  coordinateTuple_[d] = 0;
               }
               else {
                  coordinateTuple_[d]++;
                  break;
               }
            }
         }
         return *this;
      };
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

   private:
      Iterator shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const size_t dimension_;
   };

   template<class Iterator>
   class ShapeWalkerSwitchedOrder
   {
   public:
      ShapeWalkerSwitchedOrder(Iterator shapeBegin,size_t dimension)
      : shapeBegin_(shapeBegin),
      coordinateTuple_(dimension, 0),
      dimension_(dimension) { }
      ShapeWalkerSwitchedOrder & operator++() {
         for(size_t d = dimension_-1; true; --d) {
            if( size_t(coordinateTuple_[d]) != (size_t(shapeBegin_[d]) - size_t(1)) ) {
               ++coordinateTuple_[d];
               OPENGM_ASSERT(coordinateTuple_[d]<shapeBegin_[d]);
               break;
            }
            else {
               if(d != 0) {
                  coordinateTuple_[d] = 0;
               }
               else {
                  coordinateTuple_[d]++;
                  break;
               }
            }
            //if(d==0){
            //   break;
            //}
         }
         return *this;
      };
      const opengm::FastSequence<size_t> & coordinateTuple()const {
            return coordinateTuple_;
      };

   private:
      Iterator shapeBegin_;
      opengm::FastSequence<size_t> coordinateTuple_;
      const size_t dimension_;
   };

   template<class SHAPE_AB_ITERATOR>
   class TripleShapeWalker{
   public:
      template<class VI_AB,class VI_A,class VI_B>
      TripleShapeWalker
      (
         SHAPE_AB_ITERATOR  shapeABBegin,
         const size_t dimAB,
         const VI_AB & viAB,
         const VI_A & viA,
         const VI_B & viB
      ): shapeABBegin_(shapeABBegin),
      dimensionAB_(dimAB),
      coordinateTupleAB_(viAB.size(), 0),
      coordinateTupleA_(viA.size(), 0),
      coordinateTupleB_(viB.size(), 0),
      viMatchA_(viAB.size(), false),
      viMatchB_(viAB.size(), false),
      viMatchIndexA_(viAB.size()),
      viMatchIndexB_(viAB.size()) {
         OPENGM_ASSERT(dimAB == viAB.size());
         OPENGM_ASSERT( viA.size() != 0);
         OPENGM_ASSERT( viB.size() != 0);
         //vi matching:
         size_t counterA = 0;
         size_t counterB = 0;
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if(counterA<viA.size()) {
               if(viAB[d] == viA[counterA]) {
                  viMatchA_[d] = true;
                  viMatchIndexA_[d] = counterA;
                  counterA++;
               }
            }
            if(counterB<viB.size()) {
               if(viAB[d] == viB[counterB]) {
                  viMatchB_[d] = true;
                  viMatchIndexB_[d] = counterB;
                  counterB++;
               }
            }
         }
      }

      TripleShapeWalker & operator++() {
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if( int (coordinateTupleAB_[d]) != int( int(shapeABBegin_[d]) - int(1))) {
               coordinateTupleAB_[d]++;
               if(viMatchA_[d]) {
                  coordinateTupleA_[viMatchIndexA_[d]]++;
               }
               if(viMatchB_[d]) {
                  coordinateTupleB_[viMatchIndexB_[d]]++;
               }
               break;
            }
            else {
               coordinateTupleAB_[d] = 0;
               if(viMatchA_[d]) {
                  coordinateTupleA_[viMatchIndexA_[d]] = 0;
               }
               if(viMatchB_[d]) {
                  coordinateTupleB_[viMatchIndexB_[d]] = 0;
               }
            }
         }
         return *this;
      };

      const opengm::FastSequence<size_t> &coordinateTupleA()const {
         return coordinateTupleA_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleB()const {
         return coordinateTupleB_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleAB()const {
         return coordinateTupleAB_;
      };
   private:
      SHAPE_AB_ITERATOR shapeABBegin_;
      const size_t dimensionAB_;
      opengm::FastSequence<size_t> coordinateTupleAB_;
      opengm::FastSequence<size_t> coordinateTupleA_;
      opengm::FastSequence<size_t> coordinateTupleB_;
      opengm::FastSequence<bool> viMatchA_;
      opengm::FastSequence<bool> viMatchB_;
      opengm::FastSequence<size_t> viMatchIndexA_;
      opengm::FastSequence<size_t> viMatchIndexB_;
   };

   template<class SHAPE_AB_ITERATOR>
   class DoubleShapeWalker {
   public:
      template<class VI_A,class VI_B>
      DoubleShapeWalker
      (
         SHAPE_AB_ITERATOR shapeABbegin,
         const size_t dimAb,
         const VI_A & viAB,
         const VI_B & viA
      )
      :shapeABbegin_(shapeABbegin),
      dimensionAB_(dimAb),
      coordinateTupleAB_(dimensionAB_, 0),
      coordinateTupleA_(viA.size(), 0),
      viMatchA_(dimensionAB_, false),
      viMatchIndexA_(dimensionAB_) {
         //vi matching:
         size_t counterA = 0;
         for(size_t d = 0; d < dimensionAB_; ++d) {
            for(size_t i = counterA; i < viA.size(); ++i) {
               if(viAB[d] == viA[i]) {
                  viMatchA_[d] = true;
                  viMatchIndexA_[d] = i;
                  ++counterA;
               }
            }
         }
      }

      DoubleShapeWalker & operator++() {
         for(size_t d = 0; d < dimensionAB_; ++d) {
            if(coordinateTupleAB_[d] != shapeABbegin_[d] - 1) {
               coordinateTupleAB_[d]++;
               if(viMatchA_[d] == true) {
                  coordinateTupleA_[viMatchIndexA_[d]]++;
               }
               break;
            }
            else {
               coordinateTupleAB_[d] = 0;
               if(viMatchA_[d] == true) {
                  coordinateTupleA_[viMatchIndexA_[d]] = 0;
               }
            }
         }
         return *this;
      };

      const opengm::FastSequence<size_t> & coordinateTupleA()const {
         return coordinateTupleA_;
      };

      const opengm::FastSequence<size_t> & coordinateTupleAB()const {
            return coordinateTupleAB_;
      };

   private:
      SHAPE_AB_ITERATOR shapeABbegin_;
      const size_t dimensionAB_;
      opengm::FastSequence<size_t> coordinateTupleAB_;
      opengm::FastSequence<size_t> coordinateTupleA_;
      opengm::FastSequence<bool> viMatchA_;
      opengm::FastSequence<size_t> viMatchIndexA_;
   };

} // namespace opengm

#endif // #ifndef OPENGM_INDEXING_HXX
<|endoftext|>


<commit_before>#include "fetchers.hh"
#include "cache.hh"
#include "globals.hh"
#include "tarfile.hh"
#include "store-api.hh"

#include <sys/time.h>

using namespace std::string_literals;

namespace nix::fetchers {

struct MercurialInputScheme : InputScheme
{
    std::optional<Input> inputFromURL(const ParsedURL & url) override
    {
        if (url.scheme != "hg+http" &&
            url.scheme != "hg+https" &&
            url.scheme != "hg+ssh" &&
            url.scheme != "hg+file") return {};

        auto url2(url);
        url2.scheme = std::string(url2.scheme, 3);
        url2.query.clear();

        Attrs attrs;
        attrs.emplace("type", "hg");

        for (auto &[name, value] : url.query) {
            if (name == "rev" || name == "ref")
                attrs.emplace(name, value);
            else
                url2.query.emplace(name, value);
        }

        attrs.emplace("url", url2.to_string());

        return inputFromAttrs(attrs);
    }

    std::optional<Input> inputFromAttrs(const Attrs & attrs) override
    {
        if (maybeGetStrAttr(attrs, "type") != "hg") return {};

        for (auto & [name, value] : attrs)
            if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash")
                throw Error("unsupported Mercurial input attribute '%s'", name);

        parseURL(getStrAttr(attrs, "url"));

        if (auto ref = maybeGetStrAttr(attrs, "ref")) {
            if (!std::regex_match(*ref, refRegex))
                throw BadURL("invalid Mercurial branch/tag name '%s'", *ref);
        }

        Input input;
        input.attrs = attrs;
        return input;
    }

    ParsedURL toURL(const Input & input) override
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        url.scheme = "hg+" + url.scheme;
        if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev());
        if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref);
        return url;
    }

    bool hasAllInfo(const Input & input) override
    {
        // FIXME: ugly, need to distinguish between dirty and clean
        // default trees.
        return input.getRef() == "default" || maybeGetIntAttr(input.attrs, "revCount");
    }

    Input applyOverrides(
        const Input & input,
        std::optional<std::string> ref,
        std::optional<Hash> rev) override
    {
        auto res(input);
        if (rev) res.attrs.insert_or_assign("rev", rev->gitRev());
        if (ref) res.attrs.insert_or_assign("ref", *ref);
        return res;
    }

    std::optional<Path> getSourcePath(const Input & input) override
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        if (url.scheme == "file" && !input.getRef() && !input.getRev())
            return url.path;
        return {};
    }

    void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
    {
        auto sourcePath = getSourcePath(input);
        assert(sourcePath);

        // FIXME: shut up if file is already tracked.
        runProgram("hg", true,
            { "add", *sourcePath + "/" + std::string(file) });

        if (commitMsg)
            runProgram("hg", true,
                { "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg });
    }

    std::pair<bool, std::string> getActualUrl(const Input & input) const
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        bool isLocal = url.scheme == "file";
        return {isLocal, isLocal ? url.path : url.base};
    }

    std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
    {
        auto name = "source";

        Input input(_input);

        auto [isLocal, actualUrl_] = getActualUrl(input);
        auto actualUrl = actualUrl_; // work around clang bug

        // FIXME: return lastModified.

        // FIXME: don't clone local repositories.

        if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {

            bool clean = runProgram("hg", true, { "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == "";

            if (!clean) {

                /* This is an unclean working tree. So copy all tracked
                   files. */

                if (!settings.allowDirty)
                    throw Error("Mercurial tree '%s' is unclean", actualUrl);

                if (settings.warnDirty)
                    warn("Mercurial tree '%s' is unclean", actualUrl);

                input.attrs.insert_or_assign("ref", chomp(runProgram("hg", true, { "branch", "-R", actualUrl })));

                auto files = tokenizeString<std::set<std::string>>(
                    runProgram("hg", true, { "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);

                PathFilter filter = [&](const Path & p) -> bool {
                    assert(hasPrefix(p, actualUrl));
                    std::string file(p, actualUrl.size() + 1);

                    auto st = lstat(p);

                    if (S_ISDIR(st.st_mode)) {
                        auto prefix = file + "/";
                        auto i = files.lower_bound(prefix);
                        return i != files.end() && hasPrefix(*i, prefix);
                    }

                    return files.count(file);
                };

                auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);

                return {
                    Tree(store->printStorePath(storePath), std::move(storePath)),
                    input
                };
            }
        }

        if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");

        auto getImmutableAttrs = [&]()
        {
            return Attrs({
                {"type", "hg"},
                {"name", name},
                {"rev", input.getRev()->gitRev()},
            });
        };

        auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
            -> std::pair<Tree, Input>
        {
            assert(input.getRev());
            assert(!_input.getRev() || _input.getRev() == input.getRev());
            input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
            return {
                Tree(store->toRealPath(storePath), std::move(storePath)),
                input
            };
        };

        if (input.getRev()) {
            if (auto res = getCache()->lookup(store, getImmutableAttrs()))
                return makeResult(res->first, std::move(res->second));
        }

        auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef();

        Attrs mutableAttrs({
            {"type", "hg"},
            {"name", name},
            {"url", actualUrl},
            {"ref", *input.getRef()},
        });

        if (auto res = getCache()->lookup(store, mutableAttrs)) {
                    auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
            if (!input.getRev() || input.getRev() == rev2) {
                input.attrs.insert_or_assign("rev", rev2.gitRev());
                return makeResult(res->first, std::move(res->second));
            }
        }

        Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, actualUrl).to_string(Base32, false));

        /* If this is a commit hash that we already have, we don't
           have to pull again. */
        if (!(input.getRev()
                && pathExists(cacheDir)
                && runProgram(
                    RunOptions("hg", { "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })
                    .killStderr(true)).second == "1"))
        {
            Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));

            if (pathExists(cacheDir)) {
                try {
                    runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
                }
                catch (ExecError & e) {
                    string transJournal = cacheDir + "/.hg/store/journal";
                    /* hg throws "abandoned transaction" error only if this file exists */
                    if (pathExists(transJournal)) {
                        runProgram("hg", true, { "recover", "-R", cacheDir });
                        runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
                    } else {
                        throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
                    }
                }
            } else {
                createDirs(dirOf(cacheDir));
                runProgram("hg", true, { "clone", "--noupdate", "--", actualUrl, cacheDir });
            }
        }

        auto tokens = tokenizeString<std::vector<std::string>>(
            runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
        assert(tokens.size() == 3);

        input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], htSHA1).gitRev());
        auto revCount = std::stoull(tokens[1]);
        input.attrs.insert_or_assign("ref", tokens[2]);

        if (auto res = getCache()->lookup(store, getImmutableAttrs()))
            return makeResult(res->first, std::move(res->second));

        Path tmpDir = createTempDir();
        AutoDelete delTmpDir(tmpDir, true);

        runProgram("hg", true, { "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir });

        deletePath(tmpDir + "/.hg_archival.txt");

        auto storePath = store->addToStore(name, tmpDir);

        Attrs infoAttrs({
            {"rev", input.getRev()->gitRev()},
            {"revCount", (int64_t) revCount},
        });

        if (!_input.getRev())
            getCache()->add(
                store,
                mutableAttrs,
                infoAttrs,
                storePath,
                false);

        getCache()->add(
            store,
            getImmutableAttrs(),
            infoAttrs,
            storePath,
            true);

        return makeResult(infoAttrs, std::move(storePath));
    }
};

static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<MercurialInputScheme>()); });

}
<commit_msg>Fix indentation<commit_after>#include "fetchers.hh"
#include "cache.hh"
#include "globals.hh"
#include "tarfile.hh"
#include "store-api.hh"

#include <sys/time.h>

using namespace std::string_literals;

namespace nix::fetchers {

struct MercurialInputScheme : InputScheme
{
    std::optional<Input> inputFromURL(const ParsedURL & url) override
    {
        if (url.scheme != "hg+http" &&
            url.scheme != "hg+https" &&
            url.scheme != "hg+ssh" &&
            url.scheme != "hg+file") return {};

        auto url2(url);
        url2.scheme = std::string(url2.scheme, 3);
        url2.query.clear();

        Attrs attrs;
        attrs.emplace("type", "hg");

        for (auto &[name, value] : url.query) {
            if (name == "rev" || name == "ref")
                attrs.emplace(name, value);
            else
                url2.query.emplace(name, value);
        }

        attrs.emplace("url", url2.to_string());

        return inputFromAttrs(attrs);
    }

    std::optional<Input> inputFromAttrs(const Attrs & attrs) override
    {
        if (maybeGetStrAttr(attrs, "type") != "hg") return {};

        for (auto & [name, value] : attrs)
            if (name != "type" && name != "url" && name != "ref" && name != "rev" && name != "revCount" && name != "narHash")
                throw Error("unsupported Mercurial input attribute '%s'", name);

        parseURL(getStrAttr(attrs, "url"));

        if (auto ref = maybeGetStrAttr(attrs, "ref")) {
            if (!std::regex_match(*ref, refRegex))
                throw BadURL("invalid Mercurial branch/tag name '%s'", *ref);
        }

        Input input;
        input.attrs = attrs;
        return input;
    }

    ParsedURL toURL(const Input & input) override
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        url.scheme = "hg+" + url.scheme;
        if (auto rev = input.getRev()) url.query.insert_or_assign("rev", rev->gitRev());
        if (auto ref = input.getRef()) url.query.insert_or_assign("ref", *ref);
        return url;
    }

    bool hasAllInfo(const Input & input) override
    {
        // FIXME: ugly, need to distinguish between dirty and clean
        // default trees.
        return input.getRef() == "default" || maybeGetIntAttr(input.attrs, "revCount");
    }

    Input applyOverrides(
        const Input & input,
        std::optional<std::string> ref,
        std::optional<Hash> rev) override
    {
        auto res(input);
        if (rev) res.attrs.insert_or_assign("rev", rev->gitRev());
        if (ref) res.attrs.insert_or_assign("ref", *ref);
        return res;
    }

    std::optional<Path> getSourcePath(const Input & input) override
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        if (url.scheme == "file" && !input.getRef() && !input.getRev())
            return url.path;
        return {};
    }

    void markChangedFile(const Input & input, std::string_view file, std::optional<std::string> commitMsg) override
    {
        auto sourcePath = getSourcePath(input);
        assert(sourcePath);

        // FIXME: shut up if file is already tracked.
        runProgram("hg", true,
            { "add", *sourcePath + "/" + std::string(file) });

        if (commitMsg)
            runProgram("hg", true,
                { "commit", *sourcePath + "/" + std::string(file), "-m", *commitMsg });
    }

    std::pair<bool, std::string> getActualUrl(const Input & input) const
    {
        auto url = parseURL(getStrAttr(input.attrs, "url"));
        bool isLocal = url.scheme == "file";
        return {isLocal, isLocal ? url.path : url.base};
    }

    std::pair<Tree, Input> fetch(ref<Store> store, const Input & _input) override
    {
        auto name = "source";

        Input input(_input);

        auto [isLocal, actualUrl_] = getActualUrl(input);
        auto actualUrl = actualUrl_; // work around clang bug

        // FIXME: return lastModified.

        // FIXME: don't clone local repositories.

        if (!input.getRef() && !input.getRev() && isLocal && pathExists(actualUrl + "/.hg")) {

            bool clean = runProgram("hg", true, { "status", "-R", actualUrl, "--modified", "--added", "--removed" }) == "";

            if (!clean) {

                /* This is an unclean working tree. So copy all tracked
                   files. */

                if (!settings.allowDirty)
                    throw Error("Mercurial tree '%s' is unclean", actualUrl);

                if (settings.warnDirty)
                    warn("Mercurial tree '%s' is unclean", actualUrl);

                input.attrs.insert_or_assign("ref", chomp(runProgram("hg", true, { "branch", "-R", actualUrl })));

                auto files = tokenizeString<std::set<std::string>>(
                    runProgram("hg", true, { "status", "-R", actualUrl, "--clean", "--modified", "--added", "--no-status", "--print0" }), "\0"s);

                PathFilter filter = [&](const Path & p) -> bool {
                    assert(hasPrefix(p, actualUrl));
                    std::string file(p, actualUrl.size() + 1);

                    auto st = lstat(p);

                    if (S_ISDIR(st.st_mode)) {
                        auto prefix = file + "/";
                        auto i = files.lower_bound(prefix);
                        return i != files.end() && hasPrefix(*i, prefix);
                    }

                    return files.count(file);
                };

                auto storePath = store->addToStore("source", actualUrl, FileIngestionMethod::Recursive, htSHA256, filter);

                return {
                    Tree(store->printStorePath(storePath), std::move(storePath)),
                    input
                };
            }
        }

        if (!input.getRef()) input.attrs.insert_or_assign("ref", "default");

        auto getImmutableAttrs = [&]()
        {
            return Attrs({
                {"type", "hg"},
                {"name", name},
                {"rev", input.getRev()->gitRev()},
            });
        };

        auto makeResult = [&](const Attrs & infoAttrs, StorePath && storePath)
            -> std::pair<Tree, Input>
        {
            assert(input.getRev());
            assert(!_input.getRev() || _input.getRev() == input.getRev());
            input.attrs.insert_or_assign("revCount", getIntAttr(infoAttrs, "revCount"));
            return {
                Tree(store->toRealPath(storePath), std::move(storePath)),
                input
            };
        };

        if (input.getRev()) {
            if (auto res = getCache()->lookup(store, getImmutableAttrs()))
                return makeResult(res->first, std::move(res->second));
        }

        auto revOrRef = input.getRev() ? input.getRev()->gitRev() : *input.getRef();

        Attrs mutableAttrs({
            {"type", "hg"},
            {"name", name},
            {"url", actualUrl},
            {"ref", *input.getRef()},
        });

        if (auto res = getCache()->lookup(store, mutableAttrs)) {
            auto rev2 = Hash::parseAny(getStrAttr(res->first, "rev"), htSHA1);
            if (!input.getRev() || input.getRev() == rev2) {
                input.attrs.insert_or_assign("rev", rev2.gitRev());
                return makeResult(res->first, std::move(res->second));
            }
        }

        Path cacheDir = fmt("%s/nix/hg/%s", getCacheDir(), hashString(htSHA256, actualUrl).to_string(Base32, false));

        /* If this is a commit hash that we already have, we don't
           have to pull again. */
        if (!(input.getRev()
                && pathExists(cacheDir)
                && runProgram(
                    RunOptions("hg", { "log", "-R", cacheDir, "-r", input.getRev()->gitRev(), "--template", "1" })
                    .killStderr(true)).second == "1"))
        {
            Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching Mercurial repository '%s'", actualUrl));

            if (pathExists(cacheDir)) {
                try {
                    runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
                }
                catch (ExecError & e) {
                    string transJournal = cacheDir + "/.hg/store/journal";
                    /* hg throws "abandoned transaction" error only if this file exists */
                    if (pathExists(transJournal)) {
                        runProgram("hg", true, { "recover", "-R", cacheDir });
                        runProgram("hg", true, { "pull", "-R", cacheDir, "--", actualUrl });
                    } else {
                        throw ExecError(e.status, fmt("'hg pull' %s", statusToString(e.status)));
                    }
                }
            } else {
                createDirs(dirOf(cacheDir));
                runProgram("hg", true, { "clone", "--noupdate", "--", actualUrl, cacheDir });
            }
        }

        auto tokens = tokenizeString<std::vector<std::string>>(
            runProgram("hg", true, { "log", "-R", cacheDir, "-r", revOrRef, "--template", "{node} {rev} {branch}" }));
        assert(tokens.size() == 3);

        input.attrs.insert_or_assign("rev", Hash::parseAny(tokens[0], htSHA1).gitRev());
        auto revCount = std::stoull(tokens[1]);
        input.attrs.insert_or_assign("ref", tokens[2]);

        if (auto res = getCache()->lookup(store, getImmutableAttrs()))
            return makeResult(res->first, std::move(res->second));

        Path tmpDir = createTempDir();
        AutoDelete delTmpDir(tmpDir, true);

        runProgram("hg", true, { "archive", "-R", cacheDir, "-r", input.getRev()->gitRev(), tmpDir });

        deletePath(tmpDir + "/.hg_archival.txt");

        auto storePath = store->addToStore(name, tmpDir);

        Attrs infoAttrs({
            {"rev", input.getRev()->gitRev()},
            {"revCount", (int64_t) revCount},
        });

        if (!_input.getRev())
            getCache()->add(
                store,
                mutableAttrs,
                infoAttrs,
                storePath,
                false);

        getCache()->add(
            store,
            getImmutableAttrs(),
            infoAttrs,
            storePath,
            true);

        return makeResult(infoAttrs, std::move(storePath));
    }
};

static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<MercurialInputScheme>()); });

}
<|endoftext|>


<commit_before><commit_msg>ui > level_widget: added final<commit_after><|endoftext|>